Browse Source

Refactor enet-related code

nordsoft 3 years ago
parent
commit
ccef596cc0

+ 0 - 6
client/CMT.cpp

@@ -11,7 +11,6 @@
 //
 //
 #include "StdInc.h"
 #include "StdInc.h"
 
 
-#include <enet/enet.h>
 #include <boost/program_options.hpp>
 #include <boost/program_options.hpp>
 
 
 #include <vcmi/scripting/Service.h>
 #include <vcmi/scripting/Service.h>
@@ -251,11 +250,6 @@ int main(int argc, char * argv[])
 	*console->cb = processCommand;
 	*console->cb = processCommand;
 	console->start();
 	console->start();
 #endif
 #endif
-	
-	if(enet_initialize() != 0)
-	{
-		return EXIT_FAILURE;
-	}
 
 
 	const bfs::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
 	const bfs::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
 	logConfig = new CBasicLogConfigurator(logPath, console);
 	logConfig = new CBasicLogConfigurator(logPath, console);

+ 15 - 67
client/CServerHandler.cpp

@@ -55,7 +55,6 @@
 #include "../lib/serializer/Cast.h"
 #include "../lib/serializer/Cast.h"
 
 
 #include <vcmi/events/EventBus.h>
 #include <vcmi/events/EventBus.h>
-#include <enet/enet.h>
 
 
 #ifdef VCMI_WINDOWS
 #ifdef VCMI_WINDOWS
 #include <windows.h>
 #include <windows.h>
@@ -121,21 +120,27 @@ extern std::string NAME;
 CServerHandler::CServerHandler()
 CServerHandler::CServerHandler()
 	: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
 	: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
 {
 {
-	enetClient = enet_host_create(NULL, 1, 2, 0, 0);
 	uuid = boost::uuids::to_string(boost::uuids::random_generator()());
 	uuid = boost::uuids::to_string(boost::uuids::random_generator()());
 	//read from file to restore last session
 	//read from file to restore last session
 	if(!settings["server"]["uuid"].isNull() && !settings["server"]["uuid"].String().empty())
 	if(!settings["server"]["uuid"].isNull() && !settings["server"]["uuid"].String().empty())
 		uuid = settings["server"]["uuid"].String();
 		uuid = settings["server"]["uuid"].String();
 	applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
 	applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
 	registerTypesLobbyPacks(*applier);
 	registerTypesLobbyPacks(*applier);
-	
-	threadPollClient = std::make_shared<boost::thread>(&CServerHandler::threadPoll, this);
-	threadPollClient->detach();
 }
 }
 
 
 CServerHandler::~CServerHandler()
 CServerHandler::~CServerHandler()
 {
 {
-	enet_host_destroy(enetClient);
+}
+
+void CServerHandler::handleConnection(std::shared_ptr<EnetConnection> _c)
+{
+	c = std::make_shared<CConnection>(_c, NAME, uuid);
+	c->handler = std::make_shared<boost::thread>(&CServerHandler::threadHandleConnection, this);
+}
+
+void CServerHandler::handleDisconnection(std::shared_ptr<EnetConnection> _c)
+{
+	state = EClientState::DISCONNECTING;
 }
 }
 
 
 void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names)
 void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names)
@@ -179,50 +184,6 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::
 #endif
 #endif
 }
 }
 
 
-void CServerHandler::threadPoll()
-{
-	ENetEvent event;
-	while(true)
-	{
-		if(enet_host_service(enetClient, &event, 2) > 0)
-		{
-			switch(event.type)
-			{
-				case ENET_EVENT_TYPE_CONNECT: {
-					enet_packet_destroy(event.packet);
-					if(c && c->getPeer() == event.peer)
-					{
-						state = EClientState::CONNECTING;
-					}
-					break;
-				}
-					
-				case ENET_EVENT_TYPE_RECEIVE: {
-					if(c && c->getPeer() == event.peer)
-					{
-						c->dispatch(event.packet);
-					}
-					else
-					{
-						enet_packet_destroy(event.packet);
-					}
-					break;
-				}
-					
-				case ENET_EVENT_TYPE_DISCONNECT:
-				{
-					enet_packet_destroy(event.packet);
-					if(c && c->getPeer() == event.peer)
-					{
-						c.reset();
-					}
-					break;
-				}
-			}
-		}
-	}
-}
-
 void CServerHandler::startLocalServerAndConnect()
 void CServerHandler::startLocalServerAndConnect()
 {
 {
 	/*auto errorMsg = CGI->generaltexth->localizedTexts["server"]["errors"]["existingProcess"].String();
 	/*auto errorMsg = CGI->generaltexth->localizedTexts["server"]["errors"]["existingProcess"].String();
@@ -320,21 +281,11 @@ void CServerHandler::startLocalServerAndConnect()
 
 
 void CServerHandler::justConnectToServer(const std::string & addr, const ui16 port)
 void CServerHandler::justConnectToServer(const std::string & addr, const ui16 port)
 {
 {
-	while(!c && state != EClientState::CONNECTION_CANCELLED)
+	while(!valid() && state != EClientState::CONNECTION_CANCELLED)
 	{
 	{
-		try
-		{
-			logNetwork->info("Establishing connection...");
-			c = std::make_shared<CConnection>(enetClient,
-					addr.size() ? addr : getHostAddress(),
-					port ? port : getHostPort(),
-					NAME, uuid);
-		}
-		catch(...)
-		{
-			logNetwork->error("\nCannot establish connection! Retrying within 1 second");
-			boost::this_thread::sleep(boost::posix_time::seconds(1));
-		}
+		logNetwork->info("Establishing connection...");
+		init(port ? port : getHostPort(), addr.size() ? addr : getHostAddress());
+		boost::this_thread::sleep(boost::posix_time::seconds(1));
 	}
 	}
 	
 	
 	while(state != EClientState::CONNECTING)
 	while(state != EClientState::CONNECTING)
@@ -346,9 +297,6 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po
 		}
 		}
 		boost::this_thread::sleep(boost::posix_time::milliseconds(100));
 		boost::this_thread::sleep(boost::posix_time::milliseconds(100));
 	}
 	}
-	
-	c->init();
-	c->handler = std::make_shared<boost::thread>(&CServerHandler::threadHandleConnection, this);
 
 
 	if(!addr.empty() && addr != getHostAddress())
 	if(!addr.empty() && addr != getHostAddress())
 	{
 	{

+ 6 - 7
client/CServerHandler.h

@@ -9,12 +9,11 @@
  */
  */
 #pragma once
 #pragma once
 
 
-#include <enet/enet.h>
-
 #include "../lib/CStopWatch.h"
 #include "../lib/CStopWatch.h"
 
 
 #include "../lib/StartInfo.h"
 #include "../lib/StartInfo.h"
 #include "../lib/CondSh.h"
 #include "../lib/CondSh.h"
+#include "../lib/EnetService.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
@@ -74,7 +73,7 @@ public:
 };
 };
 
 
 /// structure to handle running server and connecting to it
 /// structure to handle running server and connecting to it
-class CServerHandler : public IServerAPI, public LobbyInfo
+class CServerHandler : public IServerAPI, public LobbyInfo, public EnetService
 {
 {
 	std::shared_ptr<CApplier<CBaseForLobbyApply>> applier;
 	std::shared_ptr<CApplier<CBaseForLobbyApply>> applier;
 
 
@@ -82,15 +81,17 @@ class CServerHandler : public IServerAPI, public LobbyInfo
 	std::list<CPackForLobby *> packsForLobbyScreen; //protected by mx
 	std::list<CPackForLobby *> packsForLobbyScreen; //protected by mx
 
 
 	std::vector<std::string> myNames;
 	std::vector<std::string> myNames;
-	ENetHost * enetClient;
 
 
 	void threadHandleConnection();
 	void threadHandleConnection();
 	void threadRunServer();
 	void threadRunServer();
-	void threadPoll();
+
 	void onServerFinished();
 	void onServerFinished();
 	void sendLobbyPack(const CPackForLobby & pack) const override;
 	void sendLobbyPack(const CPackForLobby & pack) const override;
 
 
 public:
 public:
+	void handleDisconnection(std::shared_ptr<EnetConnection>) override;
+	void handleConnection(std::shared_ptr<EnetConnection>) override;
+	
 	std::atomic<EClientState> state;
 	std::atomic<EClientState> state;
 	////////////////////
 	////////////////////
 	// FIXME: Bunch of crutches to glue it all together
 	// FIXME: Bunch of crutches to glue it all together
@@ -104,8 +105,6 @@ public:
 
 
 	std::unique_ptr<CStopWatch> th;
 	std::unique_ptr<CStopWatch> th;
 	std::shared_ptr<boost::thread> threadRunLocalServer;
 	std::shared_ptr<boost::thread> threadRunLocalServer;
-	std::shared_ptr<boost::thread> threadPollClient;
-
 	std::shared_ptr<CConnection> c;
 	std::shared_ptr<CConnection> c;
 	CClient * client;
 	CClient * client;
 
 

+ 2 - 0
cmake_modules/VCMI_lib.cmake

@@ -179,6 +179,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/CStack.cpp
 		${MAIN_LIB_DIR}/CStack.cpp
 		${MAIN_LIB_DIR}/CThreadHelper.cpp
 		${MAIN_LIB_DIR}/CThreadHelper.cpp
 		${MAIN_LIB_DIR}/CTownHandler.cpp
 		${MAIN_LIB_DIR}/CTownHandler.cpp
+		${MAIN_LIB_DIR}/EnetService.cpp
 		${MAIN_LIB_DIR}/GameConstants.cpp
 		${MAIN_LIB_DIR}/GameConstants.cpp
 		${MAIN_LIB_DIR}/HeroBonus.cpp
 		${MAIN_LIB_DIR}/HeroBonus.cpp
 		${MAIN_LIB_DIR}/IGameCallback.cpp
 		${MAIN_LIB_DIR}/IGameCallback.cpp
@@ -420,6 +421,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/CStopWatch.h
 		${MAIN_LIB_DIR}/CStopWatch.h
 		${MAIN_LIB_DIR}/CThreadHelper.h
 		${MAIN_LIB_DIR}/CThreadHelper.h
 		${MAIN_LIB_DIR}/CTownHandler.h
 		${MAIN_LIB_DIR}/CTownHandler.h
+		${MAIN_LIB_DIR}/EnetService.h
 		${MAIN_LIB_DIR}/FunctionList.h
 		${MAIN_LIB_DIR}/FunctionList.h
 		${MAIN_LIB_DIR}/GameConstants.h
 		${MAIN_LIB_DIR}/GameConstants.h
 		${MAIN_LIB_DIR}/HeroBonus.h
 		${MAIN_LIB_DIR}/HeroBonus.h

+ 270 - 0
lib/EnetService.cpp

@@ -0,0 +1,270 @@
+//
+//  EnetService.cpp
+//  vcmi
+//
+//  Created by nordsoft on 17.01.2023.
+//
+
+#include "EnetService.h"
+#include "StdInc.h"
+#include "CThreadHelper.h"
+#include <thread>
+
+EnetConnection::EnetConnection(ENetPeer * _peer):
+	peer(_peer)
+{
+	connected = false;
+}
+
+EnetConnection::EnetConnection(ENetHost * client, const std::string & host, ui16 port)
+{
+	connected = false;
+	ENetAddress address;
+	enet_address_set_host(&address, host.c_str());
+	address.port = port;
+	
+	peer = enet_host_connect(client, &address, 2, 0);
+	if(!peer)
+	{
+		throw std::runtime_error("Can't establish connection :(");
+	}
+}
+
+EnetConnection::~EnetConnection()
+{
+	close();
+	kill();
+}
+
+bool EnetConnection::isOpen() const
+{
+	return connected;
+}
+
+void EnetConnection::open()
+{
+	connected = true;
+}
+
+void EnetConnection::close()
+{
+	std::lock_guard<std::mutex> guard(mutexWrite);
+	connected = false;
+	enet_peer_disconnect(peer, 0);
+}
+
+void EnetConnection::kill()
+{
+	connected = false;
+	if(peer)
+		enet_peer_reset(peer);
+	peer = nullptr;
+}
+
+const ENetPeer * EnetConnection::getPeer() const
+{
+	return peer;
+}
+
+void EnetConnection::dispatch(ENetPacket * packet)
+{
+	std::lock_guard<std::mutex> guard(mutexRead);
+	packets.push_back(packet);
+}
+
+void EnetConnection::write(const void * data, unsigned size)
+{
+	if(size == 0 || !isOpen())
+		return;
+	
+	std::lock_guard<std::mutex> guard(mutexWrite);
+	ENetPacket * packet = enet_packet_create(data, size, ENET_PACKET_FLAG_RELIABLE);
+	enet_peer_send(peer, channel, packet);
+}
+
+void EnetConnection::read(void * data, unsigned size)
+{
+	if(!size)
+		return;
+	
+	while(packets.empty())
+	{
+		if(!isOpen())
+			return;
+		
+		std::this_thread::sleep_for(std::chrono::milliseconds(READ_REFRESH));
+	}
+	
+	std::lock_guard<std::mutex> guard(mutexRead);
+	auto * packet = packets.front();
+	packets.pop_front();
+	
+	if(packet->dataLength > 0)
+		memcpy(data, packet->data, packet->dataLength);
+	
+	assert(size == packet->dataLength);
+	enet_packet_destroy(packet);
+}
+
+EnetService::EnetService():
+	service(nullptr), flagValid(false)
+{
+	if(enet_initialize() != 0)
+		throw std::runtime_error("Cannot initialize enet");
+	
+	doMonitoring = true;
+	threadMonitoring = std::make_unique<std::thread>(&EnetService::monitor, this);
+	threadMonitoring->detach();
+}
+
+EnetService::~EnetService()
+{
+	stop();
+	doMonitoring = false;
+	if(threadMonitoring)
+		threadMonitoring->join();
+	
+	enet_deinitialize();
+}
+
+void EnetService::init(short port, const std::string & host)
+{
+	init(0);
+	active.insert(std::make_shared<EnetConnection>(service, host, port));
+}
+
+void EnetService::init(short port)
+{
+	ENetAddress address;
+	address.host = ENET_HOST_ANY;
+	address.port = port;
+	
+	service = enet_host_create(port ? &address : nullptr, port ? CONNECTIONS : 1, CHANNELS, 0, 0);
+	if(service)
+		flagValid = true;
+	
+	start();
+}
+
+void EnetService::start()
+{
+	stop();
+	doPolling = true;
+	
+	if(service)
+	{
+		threadPolling = std::make_unique<std::thread>(&EnetService::poll, this);
+		threadPolling->detach();
+	}
+}
+
+void EnetService::monitor()
+{
+	setThreadName("EnetService::monitor");
+	while(doMonitoring)
+	{
+		while(!disconnecting.empty())
+		{
+			std::lock_guard<std::mutex> guard(mutex);
+			if(disconnecting.front()->isOpen())
+				disconnecting.front()->kill();
+			handleDisconnection(disconnecting.front());
+			disconnecting.pop_front();
+		}
+		while(!connecting.empty())
+		{
+			std::lock_guard<std::mutex> guard(mutex);
+			connecting.front()->open();
+			handleConnection(connecting.front());
+			connecting.pop_front();
+		}
+		if(service)
+			enet_host_flush(service);
+		std::this_thread::sleep_for(std::chrono::milliseconds(MONITOR_INTERVAL));
+	}
+}
+
+void EnetService::stop()
+{
+	doPolling = false;
+	if(threadPolling)
+		threadPolling->join();
+	threadPolling.reset();
+}
+
+bool EnetService::valid() const
+{
+	return flagValid;
+}
+
+void EnetService::poll()
+{
+	setThreadName("EnetService::poll");
+	ENetEvent event;
+	while(doPolling)
+	{
+		if(enet_host_service(service, &event, POLL_INTERVAL) > 0)
+		{
+			switch(event.type)
+			{
+				case ENET_EVENT_TYPE_CONNECT: {
+					bool receiverFound = false;
+					for(auto & c : active)
+					{
+						if(c->getPeer() == event.peer)
+						{
+							std::lock_guard<std::mutex> guard(mutex);
+							connecting.push_back(c);
+							receiverFound = true;
+							break;
+						}
+					}
+					
+					if(!receiverFound)
+					{
+						auto c = std::make_shared<EnetConnection>(event.peer);
+						active.insert(c);
+						std::lock_guard<std::mutex> guard(mutex);
+						connecting.push_back(c);
+					}
+					
+					enet_packet_destroy(event.packet);
+					break;
+				}
+					
+				case ENET_EVENT_TYPE_RECEIVE: {
+					bool receiverFound = false;
+					for(auto & c : active)
+					{
+						if(c->getPeer() == event.peer)
+						{
+							c->dispatch(event.packet);
+							receiverFound = true;
+							break;
+						}
+					}
+			
+					if(!receiverFound)
+						enet_packet_destroy(event.packet);
+					
+					break;
+				}
+					
+				case ENET_EVENT_TYPE_DISCONNECT: {
+					for(auto c : active)
+					{
+						if(c->getPeer() == event.peer)
+						{
+							std::lock_guard<std::mutex> guard(mutex);
+							disconnecting.push_back(c);
+							active.erase(c);
+							break;
+						}
+					}
+					enet_packet_destroy(event.packet);
+					break;
+				}
+			}
+		}
+	}
+}

+ 82 - 0
lib/EnetService.h

@@ -0,0 +1,82 @@
+//
+//  EnetService.hpp
+//  vcmi
+//
+//  Created by nordsoft on 17.01.2023.
+//
+
+#pragma once
+#include <enet/enet.h>
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE EnetConnection
+{
+private:
+	
+	const unsigned int READ_REFRESH = 20; //ms
+	
+	std::list<ENetPacket*> packets;
+	int channel = 0;
+	ENetPeer * peer = nullptr;
+	std::atomic<bool> connected;
+	
+	std::mutex mutexRead, mutexWrite;
+	
+public:
+	EnetConnection(ENetPeer * peer);
+	EnetConnection(ENetHost * client, const std::string & host, ui16 port);
+	virtual ~EnetConnection();
+	
+	bool isOpen() const;
+	void open();
+	void close();
+	void kill();
+	const ENetPeer * getPeer() const;
+	void dispatch(ENetPacket * packet);
+	
+	void write(const void * data, unsigned size);
+	void read(void * data, unsigned size);
+};
+
+class DLL_LINKAGE EnetService
+{
+private:
+	
+	const unsigned int CHANNELS = 2;
+	const unsigned int CONNECTIONS = 16;
+	const unsigned int POLL_INTERVAL = 4; //ms
+	const unsigned int MONITOR_INTERVAL = 50; //ms
+	
+	ENetHost * service;
+	
+	std::set<std::shared_ptr<EnetConnection>> active;
+	std::list<std::shared_ptr<EnetConnection>> connecting, disconnecting;
+	
+	std::atomic<bool> doPolling, doMonitoring;
+	bool flagValid;
+	
+	std::unique_ptr<std::thread> threadPolling, threadMonitoring;
+	std::mutex mutex;
+	
+	void poll();
+	void monitor();
+	void start();
+	
+public:
+	
+	EnetService();
+	virtual ~EnetService();
+	
+	void init(short port);
+	void init(short port, const std::string & host);
+	
+	void stop();
+	
+	bool valid() const;
+	
+	virtual void handleDisconnection(std::shared_ptr<EnetConnection>) = 0;
+	virtual void handleConnection(std::shared_ptr<EnetConnection>) = 0;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 18 - 59
lib/serializer/Connection.cpp

@@ -51,76 +51,30 @@ void CConnection::init()
 	iser.fileVersion = SERIALIZATION_VERSION;
 	iser.fileVersion = SERIALIZATION_VERSION;
 }
 }
 
 
-CConnection::CConnection(ENetHost * _client, ENetPeer * _peer, std::string Name, std::string UUID)
-	: client(_client), peer(_peer), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0), connected(false), channel(1)
+CConnection::CConnection(std::shared_ptr<EnetConnection> _c, std::string Name, std::string UUID)
+	: enetConnection(_c), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0), connected(false)
 {	
 {	
-	//init();
-}
-
-CConnection::CConnection(ENetHost * _client, std::string host, ui16 port, std::string Name, std::string UUID)
-	: client(_client), iser(this), oser(this), name(Name), uuid(UUID), connectionID(0), connected(false), channel(0)
-{
-	ENetAddress address;
-	enet_address_set_host(&address, host.c_str());
-	address.port = port;
-	
-	peer = enet_host_connect(client, &address, 2, 0);
-	if(peer == NULL)
-	{
-		throw std::runtime_error("Can't establish connection :(");
-	}
-}
-
-void CConnection::dispatch(ENetPacket * packet)
-{
-	boost::unique_lock<boost::mutex> lock(mutexRead);
-	packets.push_back(packet);
-}
-
-const ENetPeer * CConnection::getPeer() const
-{
-	return peer;
+	init();
 }
 }
 
 
 int CConnection::write(const void * data, unsigned size)
 int CConnection::write(const void * data, unsigned size)
 {
 {
-	if(size == 0)
-		return 0;
-	boost::unique_lock<boost::mutex> lock(mutexWrite);
-	ENetPacket * packet = enet_packet_create(data, size, ENET_PACKET_FLAG_RELIABLE);
-	enet_peer_send(peer, channel, packet);
+	if(connected)
+		enetConnection->write(data, size);
 	return size;
 	return size;
 }
 }
 
 
 int CConnection::read(void * data, unsigned size)
 int CConnection::read(void * data, unsigned size)
 {
 {
-	const int timout = 1000;
-	if(size == 0)
-		return 0;
-	for(int i = 0; packets.empty() && (i < timout || connected); ++i)
-	{
-		boost::this_thread::sleep(boost::posix_time::milliseconds(10));
-	}
-	boost::unique_lock<boost::mutex> lock(mutexRead);
-	auto * packet = packets.front();
-	packets.pop_front();
-	
-	if(packet->dataLength > 0)
-		memcpy(data, packet->data, packet->dataLength);
-	int ret = packet->dataLength;
-	assert(ret == size);
-	enet_packet_destroy(packet);
-
-	return ret;
+	if(connected)
+		enetConnection->read(data, size);
+	return size;
 }
 }
 
 
 CConnection::~CConnection()
 CConnection::~CConnection()
 {
 {
 	if(handler)
 	if(handler)
 		handler->join();
 		handler->join();
-	
-	for(auto * packet : packets)
-		enet_packet_destroy(packet);
 
 
 	close();
 	close();
 }
 }
@@ -134,11 +88,18 @@ CConnection & CConnection::operator&(const T &t) {
 	return *this;
 	return *this;
 }
 }
 
 
+const std::shared_ptr<EnetConnection> CConnection::getEnetConnection() const
+{
+	return enetConnection;
+}
+
 void CConnection::close()
 void CConnection::close()
 {
 {
-	boost::unique_lock<boost::mutex> lock(mutexWrite);
-	enet_peer_disconnect(peer, 0);
-	enet_peer_reset(peer);
+	connected = false;
+	if(enetConnection->isOpen())
+		enetConnection->close();
+	else
+		enetConnection->kill();
 }
 }
 
 
 bool CConnection::isOpen() const
 bool CConnection::isOpen() const
@@ -179,9 +140,7 @@ void CConnection::sendPack(const CPack * pack)
 {
 {
 	logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
 	logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
 
 
-
 	oser & pack;
 	oser & pack;
-
 }
 }
 
 
 void CConnection::disableStackSendingByID()
 void CConnection::disableStackSendingByID()

+ 7 - 12
lib/serializer/Connection.h

@@ -9,10 +9,9 @@
  */
  */
 #pragma once
 #pragma once
 
 
-#include <enet/enet.h>
-
 #include "BinaryDeserializer.h"
 #include "BinaryDeserializer.h"
 #include "BinarySerializer.h"
 #include "BinarySerializer.h"
+#include "../EnetService.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
@@ -24,16 +23,13 @@ struct ConnectionBuffers;
 class DLL_LINKAGE CConnection
 class DLL_LINKAGE CConnection
 	: public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this<CConnection>
 	: public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this<CConnection>
 {
 {
+	std::shared_ptr<EnetConnection> enetConnection;
+	
 	void reportState(vstd::CLoggerBase * out) override;
 	void reportState(vstd::CLoggerBase * out) override;
 
 
 	int write(const void * data, unsigned size) override;
 	int write(const void * data, unsigned size) override;
 	int read(void * data, unsigned size) override;
 	int read(void * data, unsigned size) override;
 	
 	
-	std::list<ENetPacket*> packets;
-	int channel;
-	ENetPeer * peer = nullptr;
-	ENetHost * client = nullptr;
-
 	BinaryDeserializer iser;
 	BinaryDeserializer iser;
 	BinarySerializer oser;
 	BinarySerializer oser;
 	
 	
@@ -51,12 +47,11 @@ public:
 	int connectionID;
 	int connectionID;
 	std::shared_ptr<boost::thread> handler;
 	std::shared_ptr<boost::thread> handler;
 
 
-	CConnection(ENetHost * client, ENetPeer * peer, std::string Name, std::string UUID);
-	CConnection(ENetHost * client, std::string host, ui16 port, std::string Name, std::string UUID);
-	void init(); //must be called from outside after connection message received
-	void dispatch(ENetPacket * packet);
-	const ENetPeer * getPeer() const;
+	CConnection(std::shared_ptr<EnetConnection>, std::string Name, std::string UUID);
+	
+	const std::shared_ptr<EnetConnection> getEnetConnection() const;
 
 
+	void init();
 	void close();
 	void close();
 	bool isOpen() const;
 	bool isOpen() const;
 	template<class T>
 	template<class T>

+ 65 - 134
server/CVCMIServer.cpp

@@ -127,18 +127,9 @@ CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts)
 		port = cmdLineOptions["port"].as<ui16>();
 		port = cmdLineOptions["port"].as<ui16>();
 	logNetwork->info("Port %d will be used", port);
 	logNetwork->info("Port %d will be used", port);
 	
 	
-	const int maxConnections = 8;
-	const int maxChannels = 2;
-	
-	ENetAddress address;
-	address.host = ENET_HOST_ANY;
-	address.port = port;
-	server = enet_host_create(&address, maxConnections, maxChannels, 0, 0);
-	if(!server)
-	{
-		logNetwork->error("Can't create host at port %d", port);
-		exit(0);
-	}
+	init(port);
+	if(!valid())
+		state = EServerState::SHUTDOWN;
 	
 	
 	logNetwork->info("Listening for connections at port %d", port);
 	logNetwork->info("Listening for connections at port %d", port);
 }
 }
@@ -151,8 +142,68 @@ CVCMIServer::~CVCMIServer()
 		announceLobbyThread->join();
 		announceLobbyThread->join();
 	if(lobbyConnectionsThread)
 	if(lobbyConnectionsThread)
 		lobbyConnectionsThread->join();
 		lobbyConnectionsThread->join();
+}
+
+void CVCMIServer::handleConnection(std::shared_ptr<EnetConnection> _c)
+{
+	auto c = *connections.insert(std::make_shared<CConnection>(_c, SERVER_NAME, uuid)).first;
+	c->handler = std::make_shared<boost::thread>(&CVCMIServer::threadHandleClient, this, c);
+}
+
+void CVCMIServer::handleDisconnection(std::shared_ptr<EnetConnection> _c)
+{
+	std::shared_ptr<CConnection> c;
+	for(auto cc : connections)
+	{
+		if(cc->getEnetConnection() == _c)
+		{
+			c = cc;
+			break;
+		}
+	}
+	assert(c);
+	c->close();
+	connections -= c;
+	if(connections.empty() || hostClient == c)
+	{
+		state = EServerState::SHUTDOWN;
+		return;
+	}
+	
+	PlayerReinitInterface startAiPack;
+	startAiPack.playerConnectionId = PlayerSettings::PLAYER_AI;
+	
+	for(auto it = playerNames.begin(); it != playerNames.end();)
+	{
+		if(it->second.connection != c->connectionID)
+		{
+			++it;
+			continue;
+		}
+
+		int id = it->first;
+		std::string playerLeftMsgText = boost::str(boost::format("%s (pid %d cid %d) left the game") % id % playerNames[id].name % c->connectionID);
+		announceTxt(playerLeftMsgText); //send lobby text, it will be ignored for non-lobby clients
+		auto * playerSettings = si->getPlayersSettings(id);
+		if(!playerSettings)
+		{
+			++it;
+			continue;
+		}
+		
+		it = playerNames.erase(it);
+		setPlayerConnectedId(*playerSettings, PlayerSettings::PLAYER_AI);
+		
+		if(gh && si && state == EServerState::GAMEPLAY)
+		{
+			gh->playerMessage(playerSettings->color, playerLeftMsgText, ObjectInstanceID{});
+			gh->connections[playerSettings->color].insert(hostClient);
+			startAiPack.players.push_back(playerSettings->color);
+		}
+	}
 	
 	
-	enet_host_destroy(server);
+	if(!startAiPack.players.empty())
+		gh->sendAndApply(&startAiPack);
 }
 }
 
 
 void CVCMIServer::run()
 void CVCMIServer::run()
@@ -172,9 +223,6 @@ void CVCMIServer::run()
 		}
 		}
 #endif
 #endif
 		
 		
-		if(!lobbyConnectionsThread)
-			lobbyConnectionsThread = std::make_unique<boost::thread>(&CVCMIServer::startAsyncAccept, this);
-		
 		/*if(!remoteConnectionsThread && cmdLineOptions.count("lobby"))
 		/*if(!remoteConnectionsThread && cmdLineOptions.count("lobby"))
 		{
 		{
 			remoteConnectionsThread = vstd::make_unique<boost::thread>(&CVCMIServer::establishRemoteConnections, this);
 			remoteConnectionsThread = vstd::make_unique<boost::thread>(&CVCMIServer::establishRemoteConnections, this);
@@ -193,7 +241,6 @@ void CVCMIServer::run()
 
 
 	while(state == EServerState::LOBBY || state == EServerState::GAMEPLAY_STARTING)
 	while(state == EServerState::LOBBY || state == EServerState::GAMEPLAY_STARTING)
 	{
 	{
-		connectionAccepted();
 		boost::this_thread::sleep(boost::posix_time::milliseconds(50));
 		boost::this_thread::sleep(boost::posix_time::milliseconds(50));
 	}
 	}
 
 
@@ -224,7 +271,7 @@ void CVCMIServer::connectToRemote(const std::string & addr, int port)
 	try
 	try
 	{
 	{
 		logNetwork->info("Establishing connection...");
 		logNetwork->info("Establishing connection...");
-		c = std::make_shared<CConnection>(server, addr, port, SERVER_NAME, uuid);
+		//c = std::make_shared<CConnection>(server, addr, port, SERVER_NAME, uuid);
 	}
 	}
 	catch(...)
 	catch(...)
 	{
 	{
@@ -316,72 +363,6 @@ void CVCMIServer::startGameImmidiately()
 	state = EServerState::GAMEPLAY;
 	state = EServerState::GAMEPLAY;
 }
 }
 
 
-void CVCMIServer::startAsyncAccept()
-{
-	ENetEvent event;
-	while(state != EServerState::SHUTDOWN)
-	{
-		if(enet_host_service(server, &event, 2) > 0)
-		{
-			switch(event.type)
-			{
-				case ENET_EVENT_TYPE_CONNECT: {
-					if(state == EServerState::LOBBY)
-					{
-						upcomingConnection = std::make_shared<CConnection>(server, event.peer, SERVER_NAME, uuid);
-						connections.insert(upcomingConnection);
-					}
-					enet_packet_destroy(event.packet);
-					break;
-				}
-					
-				case ENET_EVENT_TYPE_RECEIVE: {
-					
-					bool receiverFound = false;
-					for(auto & c : connections)
-					{
-						if(c->getPeer() == event.peer)
-						{
-							c->dispatch(event.packet);
-							receiverFound = true;
-							break;
-						}
-					}
-					
-					if(!receiverFound)
-						enet_packet_destroy(event.packet);
-					
-					break;
-				}
-					
-				case ENET_EVENT_TYPE_DISCONNECT: {
-					enet_packet_destroy(event.packet);
-					for(auto & c : connections)
-					{
-						if(c->getPeer() == event.peer)
-						{
-							clientDisconnected(c);
-							break;
-						}
-					}
-					break;
-					
-				}
-			}
-		}
-	}
-}
-
-void CVCMIServer::connectionAccepted()
-{
-	if(upcomingConnection)
-	{
-		upcomingConnection->init();
-		upcomingConnection->handler = std::make_shared<boost::thread>(&CVCMIServer::threadHandleClient, this, upcomingConnection);
-		upcomingConnection.reset();
-	}
-}
-
 void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
 void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
 {
 {
 	setThreadName("CVCMIServer::handleConnection");
 	setThreadName("CVCMIServer::handleConnection");
@@ -555,51 +536,6 @@ void CVCMIServer::clientConnected(std::shared_ptr<CConnection> c, std::vector<st
 	}
 	}
 }
 }
 
 
-void CVCMIServer::clientDisconnected(std::shared_ptr<CConnection> c)
-{
-	connections -= c;
-	if(connections.empty() || hostClient == c)
-	{
-		state = EServerState::SHUTDOWN;
-		return;
-	}
-	
-	PlayerReinitInterface startAiPack;
-	startAiPack.playerConnectionId = PlayerSettings::PLAYER_AI;
-	
-	for(auto it = playerNames.begin(); it != playerNames.end();)
-	{
-		if(it->second.connection != c->connectionID)
-		{
-			++it;
-			continue;
-		}
-
-		int id = it->first;
-		std::string playerLeftMsgText = boost::str(boost::format("%s (pid %d cid %d) left the game") % id % playerNames[id].name % c->connectionID);
-		announceTxt(playerLeftMsgText); //send lobby text, it will be ignored for non-lobby clients
-		auto * playerSettings = si->getPlayersSettings(id);
-		if(!playerSettings)
-		{
-			++it;
-			continue;
-		}
-		
-		it = playerNames.erase(it);
-		setPlayerConnectedId(*playerSettings, PlayerSettings::PLAYER_AI);
-		
-		if(gh && si && state == EServerState::GAMEPLAY)
-		{
-			gh->playerMessage(playerSettings->color, playerLeftMsgText, ObjectInstanceID{});
-			gh->connections[playerSettings->color].insert(hostClient);
-			startAiPack.players.push_back(playerSettings->color);
-		}
-	}
-	
-	if(!startAiPack.players.empty())
-		gh->sendAndApply(&startAiPack);
-}
-
 void CVCMIServer::reconnectPlayer(int connId)
 void CVCMIServer::reconnectPlayer(int connId)
 {
 {
 	PlayerReinitInterface startAiPack;
 	PlayerReinitInterface startAiPack;
@@ -1073,10 +1009,6 @@ static void handleCommandOptions(int argc, char * argv[], boost::program_options
 int main(int argc, char * argv[])
 int main(int argc, char * argv[])
 {
 {
 #if !defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP)
 #if !defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP)
-	if(enet_initialize() != 0)
-	{
-		return EXIT_FAILURE;
-	}
 	// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
 	// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
 	boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
 	boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
 #endif
 #endif
@@ -1126,7 +1058,6 @@ int main(int argc, char * argv[])
 #endif
 #endif
 	logConfig.deconfigure();
 	logConfig.deconfigure();
 	vstd::clear_pointer(VLC);
 	vstd::clear_pointer(VLC);
-	enet_deinitialize();
 	return 0;
 	return 0;
 }
 }
 
 

+ 5 - 8
server/CVCMIServer.h

@@ -11,7 +11,7 @@
 
 
 #include "../lib/serializer/Connection.h"
 #include "../lib/serializer/Connection.h"
 #include "../lib/StartInfo.h"
 #include "../lib/StartInfo.h"
-#include <enet/enet.h>
+#include "../lib/EnetService.h"
 
 
 #include <boost/program_options.hpp>
 #include <boost/program_options.hpp>
 
 
@@ -44,7 +44,7 @@ enum class EServerState : ui8
 	SHUTDOWN
 	SHUTDOWN
 };
 };
 
 
-class CVCMIServer : public LobbyInfo
+class CVCMIServer : public LobbyInfo, public EnetService
 {
 {
 	std::atomic<bool> restartGameplay; // FIXME: this is just a hack
 	std::atomic<bool> restartGameplay; // FIXME: this is just a hack
 	
 	
@@ -53,9 +53,10 @@ class CVCMIServer : public LobbyInfo
 	std::shared_ptr<CApplier<CBaseForServerApply>> applier;
 	std::shared_ptr<CApplier<CBaseForServerApply>> applier;
 	std::unique_ptr<boost::thread> announceLobbyThread, lobbyConnectionsThread, remoteConnectionsThread;
 	std::unique_ptr<boost::thread> announceLobbyThread, lobbyConnectionsThread, remoteConnectionsThread;
 	
 	
-	ENetHost * server;
-
 public:
 public:
+	void handleDisconnection(std::shared_ptr<EnetConnection>) override;
+	void handleConnection(std::shared_ptr<EnetConnection>) override;
+	
 	std::shared_ptr<CGameHandler> gh;
 	std::shared_ptr<CGameHandler> gh;
 	std::atomic<EServerState> state;
 	std::atomic<EServerState> state;
 	ui16 port;
 	ui16 port;
@@ -67,7 +68,6 @@ public:
 	std::atomic<int> currentClientId;
 	std::atomic<int> currentClientId;
 	std::atomic<ui8> currentPlayerId;
 	std::atomic<ui8> currentPlayerId;
 	std::shared_ptr<CConnection> hostClient;
 	std::shared_ptr<CConnection> hostClient;
-	std::shared_ptr<CConnection> upcomingConnection;
 
 
 	CVCMIServer(boost::program_options::variables_map & opts);
 	CVCMIServer(boost::program_options::variables_map & opts);
 	~CVCMIServer();
 	~CVCMIServer();
@@ -78,8 +78,6 @@ public:
 
 
 	void establishRemoteConnections();
 	void establishRemoteConnections();
 	void connectToRemote(const std::string & addr, int port);
 	void connectToRemote(const std::string & addr, int port);
-	void startAsyncAccept();
-	void connectionAccepted();
 	void threadHandleClient(std::shared_ptr<CConnection> c);
 	void threadHandleClient(std::shared_ptr<CConnection> c);
 	void threadAnnounceLobby();
 	void threadAnnounceLobby();
 	void handleReceivedPack(std::unique_ptr<CPackForLobby> pack);
 	void handleReceivedPack(std::unique_ptr<CPackForLobby> pack);
@@ -95,7 +93,6 @@ public:
 	void updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo, std::shared_ptr<CMapGenOptions> mapGenOpt = {});
 	void updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo, std::shared_ptr<CMapGenOptions> mapGenOpt = {});
 
 
 	void clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, StartInfo::EMode mode);
 	void clientConnected(std::shared_ptr<CConnection> c, std::vector<std::string> & names, std::string uuid, StartInfo::EMode mode);
-	void clientDisconnected(std::shared_ptr<CConnection> c);
 	void reconnectPlayer(int connId);
 	void reconnectPlayer(int connId);
 
 
 	void updateAndPropagateLobbyState();
 	void updateAndPropagateLobbyState();

+ 0 - 1
server/NetPacksLobbyServer.cpp

@@ -129,7 +129,6 @@ bool LobbyClientDisconnected::checkClientPermissions(CVCMIServer * srv) const
 
 
 bool LobbyClientDisconnected::applyOnServer(CVCMIServer * srv)
 bool LobbyClientDisconnected::applyOnServer(CVCMIServer * srv)
 {
 {
-	srv->clientDisconnected(c);
 	c->close();
 	c->close();
 	return true;
 	return true;
 }
 }