mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Fix connection to game lobby & map load
This commit is contained in:
		| @@ -131,7 +131,11 @@ public: | ||||
| static const std::string NAME_AFFIX = "client"; | ||||
| static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name | ||||
|  | ||||
| CServerHandler::~CServerHandler() = default; | ||||
| CServerHandler::~CServerHandler() | ||||
| { | ||||
| 	networkClient->stop(); | ||||
| 	threadNetwork->join(); | ||||
| } | ||||
|  | ||||
| CServerHandler::CServerHandler() | ||||
| 	: state(EClientState::NONE) | ||||
| @@ -148,6 +152,16 @@ CServerHandler::CServerHandler() | ||||
| 		uuid = settings["server"]["uuid"].String(); | ||||
| 	applier = std::make_shared<CApplier<CBaseForLobbyApply>>(); | ||||
| 	registerTypesLobbyPacks(*applier); | ||||
|  | ||||
| 	threadNetwork = std::make_unique<boost::thread>(&CServerHandler::threadRunNetwork, this); | ||||
| } | ||||
|  | ||||
| void CServerHandler::threadRunNetwork() | ||||
| { | ||||
| 	logGlobal->info("Starting network thread"); | ||||
| 	setThreadName("runNetwork"); | ||||
| 	networkClient->run(); | ||||
| 	logGlobal->info("Ending network thread"); | ||||
| } | ||||
|  | ||||
| void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names) | ||||
| @@ -178,12 +192,13 @@ void CServerHandler::startLocalServerAndConnect(const std::function<void()> & on | ||||
| 	 | ||||
| 	auto errorMsg = CGI->generaltexth->translate("vcmi.server.errors.existingProcess"); | ||||
|  | ||||
| 	if (!checkNetworkPortIsFree(localhostAddress, getDefaultPort())) | ||||
| 	{ | ||||
| 		logNetwork->error("Port is busy, check if another instance of vcmiserver is working"); | ||||
| 		CInfoWindow::showInfoDialog(errorMsg, {}); | ||||
| 		return; | ||||
| 	} | ||||
| // TODO: restore | ||||
| //	if (!checkNetworkPortIsFree(localhostAddress, getDefaultPort())) | ||||
| //	{ | ||||
| //		logNetwork->error("Port is busy, check if another instance of vcmiserver is working"); | ||||
| //		CInfoWindow::showInfoDialog(errorMsg, {}); | ||||
| //		return; | ||||
| //	} | ||||
| 	 | ||||
| #if defined(SINGLE_PROCESS_APP) | ||||
| 	boost::condition_variable cond; | ||||
| @@ -195,7 +210,7 @@ void CServerHandler::startLocalServerAndConnect(const std::function<void()> & on | ||||
| 		args.push_back("--lobby-port=" + std::to_string(settings["session"]["port"].Integer())); | ||||
| 		args.push_back("--lobby-uuid=" + settings["session"]["hostUuid"].String()); | ||||
| 	} | ||||
| 	threadRunLocalServer = std::make_shared<boost::thread>([&cond, args, this] { | ||||
| 	threadRunLocalServer = std::make_unique<boost::thread>([&cond, args, this] { | ||||
| 		setThreadName("CVCMIServer"); | ||||
| 		CVCMIServer::create(&cond, args); | ||||
| 		onServerFinished(); | ||||
| @@ -207,7 +222,7 @@ void CServerHandler::startLocalServerAndConnect(const std::function<void()> & on | ||||
| 		envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "startServer", true); | ||||
| 	} | ||||
| #else | ||||
| 	threadRunLocalServer = std::make_shared<boost::thread>(&CServerHandler::threadRunServer, this); //runs server executable; | ||||
| 	threadRunLocalServer = std::make_unique<boost::thread>(&CServerHandler::threadRunServer, this); //runs server executable; | ||||
| #endif | ||||
| 	logNetwork->trace("Setting up thread calling server: %d ms", th->getDiff()); | ||||
|  | ||||
| @@ -268,6 +283,10 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po | ||||
| 		serverPort->Integer() = port; | ||||
| 	} | ||||
|  | ||||
| 	if (onConnectedCallback) | ||||
| 		throw std::runtime_error("Attempt to connect while there is already a pending connection!"); | ||||
|  | ||||
| 	onConnectedCallback = onConnected; | ||||
| 	networkClient->start(addr.size() ? addr : getHostAddress(), port ? port : getHostPort()); | ||||
| } | ||||
|  | ||||
| @@ -288,14 +307,18 @@ void CServerHandler::onConnectionFailed(const std::string & errorMessage) | ||||
| 	networkClient->start(getHostAddress(), getHostPort()); | ||||
| } | ||||
|  | ||||
| void CServerHandler::onConnectionEstablished() | ||||
| void CServerHandler::onConnectionEstablished(const std::shared_ptr<NetworkConnection> & netConnection) | ||||
| { | ||||
| 	logNetwork->info("Connection established"); | ||||
| 	c = std::make_shared<CConnection>(netConnection); | ||||
| 	c->enterLobbyConnectionMode(); | ||||
| 	sendClientConnecting(); | ||||
|  | ||||
| 	//FIXME: call functor provided in CServerHandler::justConnectToServer | ||||
| 	assert(0); | ||||
| 	//onConnected(); | ||||
| 	if (onConnectedCallback) | ||||
| 	{ | ||||
| 		onConnectedCallback(); | ||||
| 		onConnectedCallback = {}; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CServerHandler::applyPacksOnLobbyScreen() | ||||
| @@ -620,7 +643,6 @@ void CServerHandler::sendStartGame(bool allowOnlyAI) const | ||||
| 	} | ||||
| 	sendLobbyPack(lsg); | ||||
| 	c->enterLobbyConnectionMode(); | ||||
| 	c->disableStackSendingByID(); | ||||
| } | ||||
|  | ||||
| void CServerHandler::startMapAfterConnection(std::shared_ptr<CMapInfo> to) | ||||
| @@ -699,10 +721,7 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart) | ||||
| 	} | ||||
| 	 | ||||
| 	if(c) | ||||
| 	{ | ||||
| 		c->enterLobbyConnectionMode(); | ||||
| 		c->disableStackSendingByID(); | ||||
| 	} | ||||
| 	 | ||||
| 	//reset settings | ||||
| 	Settings saveSession = settings.write["server"]["reconnect"]; | ||||
| @@ -898,7 +917,7 @@ public: | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| void CServerHandler::onPacketReceived(const std::vector<uint8_t> & message) | ||||
| void CServerHandler::onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message) | ||||
| { | ||||
| 	CPack * pack = c->retrievePack(message); | ||||
| 	if(state == EClientState::DISCONNECTING) | ||||
| @@ -914,7 +933,7 @@ void CServerHandler::onPacketReceived(const std::vector<uint8_t> & message) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CServerHandler::onDisconnected() | ||||
| void CServerHandler::onDisconnected(const std::shared_ptr<NetworkConnection> &) | ||||
| { | ||||
| 	if(state == EClientState::DISCONNECTING) | ||||
| 	{ | ||||
|   | ||||
| @@ -98,14 +98,17 @@ class CServerHandler : public IServerAPI, public LobbyInfo, public INetworkClien | ||||
|  | ||||
| 	std::shared_ptr<HighScoreCalculation> highScoreCalc; | ||||
|  | ||||
| 	std::function<void()> onConnectedCallback; | ||||
|  | ||||
| 	void threadRunNetwork(); | ||||
| 	void threadRunServer(); | ||||
| 	void onServerFinished(); | ||||
| 	void sendLobbyPack(const CPackForLobby & pack) const override; | ||||
|  | ||||
| 	void onPacketReceived(const std::vector<uint8_t> & message) override; | ||||
| 	void onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message) override; | ||||
| 	void onConnectionFailed(const std::string & errorMessage) override; | ||||
| 	void onConnectionEstablished() override; | ||||
| 	void onDisconnected() override; | ||||
| 	void onConnectionEstablished(const std::shared_ptr<NetworkConnection> &) override; | ||||
| 	void onDisconnected(const std::shared_ptr<NetworkConnection> &) override; | ||||
|  | ||||
| public: | ||||
| 	std::shared_ptr<CConnection> c; | ||||
| @@ -122,7 +125,8 @@ public: | ||||
| 	//////////////////// | ||||
|  | ||||
| 	std::unique_ptr<CStopWatch> th; | ||||
| 	std::shared_ptr<boost::thread> threadRunLocalServer; | ||||
| 	std::unique_ptr<boost::thread> threadRunLocalServer; | ||||
| 	std::unique_ptr<boost::thread> threadNetwork; | ||||
|  | ||||
| 	CClient * client; | ||||
|  | ||||
|   | ||||
| @@ -36,7 +36,8 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon | ||||
| 	result = false; | ||||
|  | ||||
| 	// Check if it's LobbyClientConnected for our client | ||||
| 	if(pack.uuid == handler.c->uuid) | ||||
| 	// TODO: restore | ||||
| 	//if(pack.uuid == handler.c->uuid) | ||||
| 	{ | ||||
| 		handler.c->connectionID = pack.clientId; | ||||
| 		if(handler.mapToStart) | ||||
|   | ||||
| @@ -615,6 +615,7 @@ void CSimpleJoinScreen::startConnection(const std::string & addr, ui16 port) | ||||
| 	{ | ||||
| 		// async call to prevent thread race | ||||
| 		GH.dispatchMainThread([this](){ | ||||
| 			// FIXME: this enum value is never set!!! | ||||
| 			if(CSH->state == EClientState::CONNECTION_FAILED) | ||||
| 			{ | ||||
| 				CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.mainMenu.serverConnectionFailed"), {}); | ||||
|   | ||||
| @@ -42,7 +42,7 @@ static std::string getCurrentTimeFormatted(int timeOffsetSeconds = 0) | ||||
| 	return timeFormatted.toString(); | ||||
| } | ||||
|  | ||||
| void LobbyClient::onPacketReceived(const std::vector<uint8_t> & message) | ||||
| void LobbyClient::onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message) | ||||
| { | ||||
| 	// FIXME: find better approach | ||||
| 	const char * payloadBegin = reinterpret_cast<const char*>(message.data()); | ||||
| @@ -69,7 +69,7 @@ void LobbyClient::onPacketReceived(const std::vector<uint8_t> & message) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void LobbyClient::onConnectionEstablished() | ||||
| void LobbyClient::onConnectionEstablished(const std::shared_ptr<NetworkConnection> &) | ||||
| { | ||||
| 	JsonNode toSend; | ||||
| 	toSend["type"].String() = "authentication"; | ||||
| @@ -84,7 +84,7 @@ void LobbyClient::onConnectionFailed(const std::string & errorMessage) | ||||
| 	CInfoWindow::showInfoDialog("Failed to connect to game lobby!\n" + errorMessage, {}); | ||||
| } | ||||
|  | ||||
| void LobbyClient::onDisconnected() | ||||
| void LobbyClient::onDisconnected(const std::shared_ptr<NetworkConnection> &) | ||||
| { | ||||
| 	GH.windows().popWindows(1); | ||||
| 	CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {}); | ||||
|   | ||||
| @@ -32,10 +32,10 @@ class LobbyClient : public INetworkClientListener | ||||
| 	std::unique_ptr<NetworkClient> networkClient; | ||||
| 	LobbyWindow * window; | ||||
|  | ||||
| 	void onPacketReceived(const std::vector<uint8_t> & message) override; | ||||
| 	void onPacketReceived(const std::shared_ptr<NetworkConnection> &, const std::vector<uint8_t> & message) override; | ||||
| 	void onConnectionFailed(const std::string & errorMessage) override; | ||||
| 	void onConnectionEstablished() override; | ||||
| 	void onDisconnected() override; | ||||
| 	void onConnectionEstablished(const std::shared_ptr<NetworkConnection> &) override; | ||||
| 	void onDisconnected(const std::shared_ptr<NetworkConnection> &) override; | ||||
|  | ||||
| public: | ||||
| 	explicit LobbyClient(LobbyWindow * window); | ||||
|   | ||||
| @@ -56,11 +56,12 @@ void NetworkClient::onConnected(const boost::system::error_code & ec) | ||||
| 	connection = std::make_shared<NetworkConnection>(socket, *this); | ||||
| 	connection->start(); | ||||
|  | ||||
| 	listener.onConnectionEstablished(); | ||||
| 	listener.onConnectionEstablished(connection); | ||||
| } | ||||
|  | ||||
| void NetworkClient::run() | ||||
| { | ||||
| 	boost::asio::executor_work_guard<decltype(io->get_executor())> work{io->get_executor()}; | ||||
| 	io->run(); | ||||
| } | ||||
|  | ||||
| @@ -69,6 +70,11 @@ void NetworkClient::poll() | ||||
| 	io->poll(); | ||||
| } | ||||
|  | ||||
| void NetworkClient::stop() | ||||
| { | ||||
| 	io->stop(); | ||||
| } | ||||
|  | ||||
| void NetworkClient::sendPacket(const std::vector<uint8_t> & message) | ||||
| { | ||||
| 	connection->sendPacket(message); | ||||
| @@ -76,12 +82,12 @@ void NetworkClient::sendPacket(const std::vector<uint8_t> & message) | ||||
|  | ||||
| void NetworkClient::onDisconnected(const std::shared_ptr<NetworkConnection> & connection) | ||||
| { | ||||
| 	listener.onDisconnected(); | ||||
| 	listener.onDisconnected(connection); | ||||
| } | ||||
|  | ||||
| void NetworkClient::onPacketReceived(const std::shared_ptr<NetworkConnection> & connection, const std::vector<uint8_t> & message) | ||||
| { | ||||
| 	listener.onPacketReceived(message); | ||||
| 	listener.onPacketReceived(connection, message); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -42,6 +42,7 @@ public: | ||||
| 	void start(const std::string & host, uint16_t port); | ||||
| 	void run(); | ||||
| 	void poll(); | ||||
| 	void stop(); | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -16,7 +16,9 @@ NetworkConnection::NetworkConnection(const std::shared_ptr<NetworkSocket> & sock | ||||
| 	: socket(socket) | ||||
| 	, listener(listener) | ||||
| { | ||||
|  | ||||
| 	socket->set_option(boost::asio::ip::tcp::no_delay(true)); | ||||
| 	socket->set_option(boost::asio::socket_base::send_buffer_size(4194304)); | ||||
| 	socket->set_option(boost::asio::socket_base::receive_buffer_size(4194304)); | ||||
| } | ||||
|  | ||||
| void NetworkConnection::start() | ||||
|   | ||||
| @@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN | ||||
| class DLL_LINKAGE NetworkConnection :public std::enable_shared_from_this<NetworkConnection>, boost::noncopyable | ||||
| { | ||||
| 	static const int messageHeaderSize = sizeof(uint32_t); | ||||
| 	static const int messageMaxSize = 65536; // arbitrary size to prevent potential massive allocation if we receive garbage input | ||||
| 	static const int messageMaxSize = 64 * 1024 * 1024; // arbitrary size to prevent potential massive allocation if we receive garbage input | ||||
|  | ||||
| 	std::shared_ptr<NetworkSocket> socket; | ||||
|  | ||||
|   | ||||
| @@ -34,14 +34,12 @@ protected: | ||||
| 	~INetworkServerListener() = default; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE INetworkClientListener | ||||
| class DLL_LINKAGE INetworkClientListener : public INetworkConnectionListener | ||||
| { | ||||
| 	friend class NetworkClient; | ||||
| protected: | ||||
| 	virtual void onPacketReceived(const std::vector<uint8_t> & message) = 0; | ||||
| 	virtual void onConnectionFailed(const std::string & errorMessage) = 0; | ||||
| 	virtual void onConnectionEstablished() = 0; | ||||
| 	virtual void onDisconnected() = 0; | ||||
| 	virtual void onConnectionEstablished(const std::shared_ptr<NetworkConnection> &) = 0; | ||||
|  | ||||
| 	~INetworkClientListener() = default; | ||||
| }; | ||||
|   | ||||
| @@ -13,397 +13,150 @@ | ||||
| #include "BinaryDeserializer.h" | ||||
| #include "BinarySerializer.h" | ||||
|  | ||||
| //#include "../networkPacks/NetPacksBase.h" | ||||
| #include "../networkPacks/NetPacksBase.h" | ||||
| #include "../network/NetworkConnection.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class DLL_LINKAGE ConnectionPackWriter final : public IBinaryWriter | ||||
| { | ||||
| public: | ||||
| 	std::vector<uint8_t> buffer; | ||||
|  | ||||
| 	int write(const void * data, unsigned size) final; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ConnectionPackReader final : public IBinaryReader | ||||
| { | ||||
| public: | ||||
| 	const std::vector<uint8_t> * buffer; | ||||
| 	size_t position; | ||||
|  | ||||
| 	int read(void * data, unsigned size) final; | ||||
| }; | ||||
|  | ||||
| int ConnectionPackWriter::write(const void * data, unsigned size) | ||||
| { | ||||
| 	const uint8_t * begin_ptr = static_cast<const uint8_t *>(data); | ||||
| 	const uint8_t * end_ptr = begin_ptr + size; | ||||
| 	buffer.insert(buffer.end(), begin_ptr, end_ptr); | ||||
| 	return size; | ||||
| } | ||||
|  | ||||
| int ConnectionPackReader::read(void * data, unsigned size) | ||||
| { | ||||
| 	if (position + size > buffer->size()) | ||||
| 		throw std::runtime_error("End of file reached when reading received network pack!"); | ||||
|  | ||||
| 	uint8_t * begin_ptr = static_cast<uint8_t *>(data); | ||||
|  | ||||
| 	std::copy_n(buffer->begin() + position, size, begin_ptr); | ||||
| 	position += size; | ||||
| 	return size; | ||||
| } | ||||
|  | ||||
| CConnection::CConnection(std::weak_ptr<NetworkConnection> networkConnection) | ||||
| 	: networkConnection(networkConnection) | ||||
| 	, packReader(std::make_unique<ConnectionPackReader>()) | ||||
| 	, packWriter(std::make_unique<ConnectionPackWriter>()) | ||||
| 	, deserializer(std::make_unique<BinaryDeserializer>(packReader.get())) | ||||
| 	, serializer(std::make_unique<BinarySerializer>(packWriter.get())) | ||||
| 	, connectionID(-1) | ||||
| { | ||||
| 	assert(networkConnection.lock() != nullptr); | ||||
|  | ||||
| 	enableSmartPointerSerialization(); | ||||
| 	disableStackSendingByID(); | ||||
| 	deserializer->fileVersion = SERIALIZATION_VERSION; | ||||
| } | ||||
|  | ||||
| CConnection::~CConnection() = default; | ||||
|  | ||||
| void CConnection::sendPack(const CPack * pack) | ||||
| { | ||||
| 	auto connectionPtr = networkConnection.lock(); | ||||
|  | ||||
| 	if (!connectionPtr) | ||||
| 		throw std::runtime_error("Attempt to send packet on a closed connection!"); | ||||
|  | ||||
| 	*serializer & pack; | ||||
|  | ||||
| 	logNetwork->trace("Sending a pack of type %s", typeid(*pack).name()); | ||||
|  | ||||
| 	connectionPtr->sendPacket(packWriter->buffer); | ||||
| 	packWriter->buffer.clear(); | ||||
| } | ||||
|  | ||||
| CPack * CConnection::retrievePack(const std::vector<uint8_t> & data) | ||||
| { | ||||
| 	return nullptr; | ||||
| 	CPack * result; | ||||
|  | ||||
| 	packReader->buffer = &data; | ||||
| 	packReader->position = 0; | ||||
|  | ||||
| 	*deserializer & result; | ||||
|  | ||||
| 	logNetwork->trace("Received CPack of type %s", (result ? typeid(*result).name() : "nullptr")); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| bool CConnection::isMyConnection(const std::shared_ptr<NetworkConnection> & otherConnection) const | ||||
| { | ||||
| 	return otherConnection != nullptr && networkConnection.lock() == otherConnection; | ||||
| } | ||||
|  | ||||
| void CConnection::disableStackSendingByID() | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CConnection::enterLobbyConnectionMode() | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CConnection::enterGameplayConnectionMode(CGameState * gs) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| int CConnection::write(const void * data, unsigned size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int CConnection::read(void * data, unsigned size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| using namespace boost; | ||||
| using namespace boost::asio::ip; | ||||
|  | ||||
| struct ConnectionBuffers | ||||
| { | ||||
| 	boost::asio::streambuf readBuffer; | ||||
| 	boost::asio::streambuf writeBuffer; | ||||
| }; | ||||
|  | ||||
| void CConnection::init() | ||||
| { | ||||
| 	enableBufferedWrite = false; | ||||
| 	enableBufferedRead = false; | ||||
| 	connectionBuffers = std::make_unique<ConnectionBuffers>(); | ||||
|  | ||||
| 	socket->set_option(boost::asio::ip::tcp::no_delay(true)); | ||||
|     try | ||||
|     { | ||||
|         socket->set_option(boost::asio::socket_base::send_buffer_size(4194304)); | ||||
|         socket->set_option(boost::asio::socket_base::receive_buffer_size(4194304)); | ||||
|     } | ||||
|     catch (const boost::system::system_error & e) | ||||
|     { | ||||
|         logNetwork->error("error setting socket option: %s", e.what()); | ||||
|     } | ||||
|  | ||||
| 	enableSmartPointerSerialization(); | ||||
| 	disableStackSendingByID(); | ||||
| #ifndef VCMI_ENDIAN_BIG | ||||
| 	myEndianess = true; | ||||
| #else | ||||
| 	myEndianess = false; | ||||
| #endif | ||||
| 	connected = true; | ||||
| 	std::string pom; | ||||
| 	//we got connection | ||||
| 	oser & std::string("Aiya!\n") & name & uuid & myEndianess; //identify ourselves | ||||
| 	iser & pom & pom & contactUuid & contactEndianess; | ||||
| 	logNetwork->info("Established connection with %s. UUID: %s", pom, contactUuid); | ||||
| 	mutexRead = std::make_shared<boost::mutex>(); | ||||
| 	mutexWrite = std::make_shared<boost::mutex>(); | ||||
|  | ||||
| 	iser.fileVersion = SERIALIZATION_VERSION; | ||||
| } | ||||
|  | ||||
| CConnection::CConnection(const std::string & host, ui16 port, std::string Name, std::string UUID): | ||||
| 	io_service(std::make_shared<asio::io_service>()), | ||||
| 	iser(this), | ||||
| 	oser(this), | ||||
| 	name(std::move(Name)), | ||||
| 	uuid(std::move(UUID)) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	boost::system::error_code error = asio::error::host_not_found; | ||||
| 	socket = std::make_shared<tcp::socket>(*io_service); | ||||
|  | ||||
| 	tcp::resolver resolver(*io_service); | ||||
| 	tcp::resolver::iterator end; | ||||
| 	tcp::resolver::iterator pom; | ||||
| 	tcp::resolver::iterator endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)), error); | ||||
| 	if(error) | ||||
| 	{ | ||||
| 		logNetwork->error("Problem with resolving: \n%s", error.message()); | ||||
| 		throw std::runtime_error("Problem with resolving"); | ||||
| 	} | ||||
| 	pom = endpoint_iterator; | ||||
| 	if(pom != end) | ||||
| 		logNetwork->info("Found endpoints:"); | ||||
| 	else | ||||
| 	{ | ||||
| 		logNetwork->error("Critical problem: No endpoints found!"); | ||||
| 		throw std::runtime_error("No endpoints found!"); | ||||
| 	} | ||||
| 	while(pom != end) | ||||
| 	{ | ||||
| 		logNetwork->info("\t%d:%s", i, (boost::asio::ip::tcp::endpoint&)*pom); | ||||
| 		pom++; | ||||
| 	} | ||||
| 	i=0; | ||||
| 	while(endpoint_iterator != end) | ||||
| 	{ | ||||
| 		logNetwork->info("Trying connection to %s(%d)", (boost::asio::ip::tcp::endpoint&)*endpoint_iterator, i++); | ||||
| 		socket->connect(*endpoint_iterator, error); | ||||
| 		if(!error) | ||||
| 		{ | ||||
| 			init(); | ||||
| 			return; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			throw std::runtime_error("Failed to connect!"); | ||||
| 		} | ||||
| 		endpoint_iterator++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CConnection::CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID): | ||||
| 	iser(this), | ||||
| 	oser(this), | ||||
| 	socket(std::move(Socket)), | ||||
| 	name(std::move(Name)), | ||||
| 	uuid(std::move(UUID)) | ||||
| { | ||||
| 	init(); | ||||
| } | ||||
| CConnection::CConnection(const std::shared_ptr<TAcceptor> & acceptor, | ||||
| 						 const std::shared_ptr<boost::asio::io_service> & io_service, | ||||
| 						 std::string Name, | ||||
| 						 std::string UUID): | ||||
| 	io_service(io_service), | ||||
| 	iser(this), | ||||
| 	oser(this), | ||||
| 	name(std::move(Name)), | ||||
| 	uuid(std::move(UUID)) | ||||
| { | ||||
| 	boost::system::error_code error = asio::error::host_not_found; | ||||
| 	socket = std::make_shared<tcp::socket>(*io_service); | ||||
| 	acceptor->accept(*socket,error); | ||||
| 	if (error) | ||||
| 	{ | ||||
| 		logNetwork->error("Error on accepting: %s", error.message()); | ||||
| 		socket.reset(); | ||||
| 		throw std::runtime_error("Can't establish connection :("); | ||||
| 	} | ||||
| 	init(); | ||||
| } | ||||
|  | ||||
| void CConnection::flushBuffers() | ||||
| { | ||||
| 	if(!enableBufferedWrite) | ||||
| 		return; | ||||
|  | ||||
| 	if (!socket) | ||||
| 		throw std::runtime_error("Can't write to closed socket!"); | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		asio::write(*socket, connectionBuffers->writeBuffer); | ||||
| 	} | ||||
| 	catch(...) | ||||
| 	{ | ||||
| 		//connection has been lost | ||||
| 		connected = false; | ||||
| 		throw; | ||||
| 	} | ||||
|  | ||||
| 	enableBufferedWrite = false; | ||||
| } | ||||
|  | ||||
| int CConnection::write(const void * data, unsigned size) | ||||
| { | ||||
| 	if (!socket) | ||||
| 		throw std::runtime_error("Can't write to closed socket!"); | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		if(enableBufferedWrite) | ||||
| 		{ | ||||
| 			std::ostream ostream(&connectionBuffers->writeBuffer); | ||||
| 		 | ||||
| 			ostream.write(static_cast<const char *>(data), size); | ||||
|  | ||||
| 			return size; | ||||
| 		} | ||||
|  | ||||
| 		int ret = static_cast<int>(asio::write(*socket, asio::const_buffers_1(asio::const_buffer(data, size)))); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	catch(...) | ||||
| 	{ | ||||
| 		//connection has been lost | ||||
| 		connected = false; | ||||
| 		throw; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int CConnection::read(void * data, unsigned size) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		if(enableBufferedRead) | ||||
| 		{ | ||||
| 			auto available = connectionBuffers->readBuffer.size(); | ||||
|  | ||||
| 			while(available < size) | ||||
| 			{ | ||||
| 				auto bytesRead = socket->read_some(connectionBuffers->readBuffer.prepare(1024)); | ||||
| 				connectionBuffers->readBuffer.commit(bytesRead); | ||||
| 				available = connectionBuffers->readBuffer.size(); | ||||
| 			} | ||||
|  | ||||
| 			std::istream istream(&connectionBuffers->readBuffer); | ||||
|  | ||||
| 			istream.read(static_cast<char *>(data), size); | ||||
|  | ||||
| 			return size; | ||||
| 		} | ||||
|  | ||||
| 		int ret = static_cast<int>(asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size)))); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	catch(...) | ||||
| 	{ | ||||
| 		//connection has been lost | ||||
| 		connected = false; | ||||
| 		throw; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CConnection::~CConnection() | ||||
| { | ||||
| 	close(); | ||||
|  | ||||
| 	if(handler) | ||||
| 	{ | ||||
| 		// ugly workaround to avoid self-join if last strong reference to shared_ptr that owns this class has been released in this very thread, e.g. on netpack processing | ||||
| 		if (boost::this_thread::get_id() != handler->get_id()) | ||||
| 			handler->join(); | ||||
| 		else | ||||
| 			handler->detach(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| template<class T> | ||||
| CConnection & CConnection::operator&(const T &t) { | ||||
| //	throw std::exception(); | ||||
| //XXX this is temporaly ? solution to fix gcc (4.3.3, other?) compilation | ||||
| //    problem for more details contact t0@czlug.icis.pcz.pl or impono@gmail.com | ||||
| //    do not remove this exception it shoudnt be called | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| void CConnection::close() | ||||
| { | ||||
| 	if(socket) | ||||
| 	{ | ||||
| 		try | ||||
| 		{ | ||||
| 			socket->shutdown(boost::asio::ip::tcp::socket::shutdown_receive); | ||||
| 		} | ||||
| 		catch (const boost::system::system_error & e) | ||||
| 		{ | ||||
| 			logNetwork->error("error closing socket: %s", e.what()); | ||||
| 		} | ||||
|  | ||||
| 		socket->close(); | ||||
| 		socket.reset(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool CConnection::isOpen() const | ||||
| { | ||||
| 	return socket && connected; | ||||
| } | ||||
|  | ||||
| void CConnection::reportState(vstd::CLoggerBase * out) | ||||
| { | ||||
| 	out->debug("CConnection"); | ||||
| 	if(socket && socket->is_open()) | ||||
| 	{ | ||||
| 		out->debug("\tWe have an open and valid socket"); | ||||
| 		out->debug("\t %d bytes awaiting", socket->available()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CPack * CConnection::retrievePack() | ||||
| { | ||||
| 	enableBufferedRead = true; | ||||
|  | ||||
| 	CPack * pack = nullptr; | ||||
| 	boost::unique_lock<boost::mutex> lock(*mutexRead); | ||||
| 	iser & pack; | ||||
| 	logNetwork->trace("Received CPack of type %s", (pack ? typeid(*pack).name() : "nullptr")); | ||||
| 	if(pack == nullptr) | ||||
| 		logNetwork->error("Received a nullptr CPack! You should check whether client and server ABI matches."); | ||||
|  | ||||
| 	enableBufferedRead = false; | ||||
|  | ||||
| 	return pack; | ||||
| } | ||||
|  | ||||
| void CConnection::sendPack(const CPack * pack) | ||||
| { | ||||
| 	boost::unique_lock<boost::mutex> lock(*mutexWrite); | ||||
| 	logNetwork->trace("Sending a pack of type %s", typeid(*pack).name()); | ||||
|  | ||||
| 	enableBufferedWrite = true; | ||||
|  | ||||
| 	oser & pack; | ||||
|  | ||||
| 	flushBuffers(); | ||||
| } | ||||
|  | ||||
| void CConnection::disableStackSendingByID() | ||||
| { | ||||
| 	CSerializer::sendStackInstanceByIds = false; | ||||
| 	packReader->sendStackInstanceByIds = false; | ||||
| 	packWriter->sendStackInstanceByIds = false; | ||||
| } | ||||
|  | ||||
| void CConnection::enableStackSendingByID() | ||||
| { | ||||
| 	CSerializer::sendStackInstanceByIds = true; | ||||
| } | ||||
|  | ||||
| void CConnection::disableSmartPointerSerialization() | ||||
| { | ||||
| 	iser.smartPointerSerialization = oser.smartPointerSerialization = false; | ||||
| } | ||||
|  | ||||
| void CConnection::enableSmartPointerSerialization() | ||||
| { | ||||
| 	iser.smartPointerSerialization = oser.smartPointerSerialization = true; | ||||
| 	packReader->sendStackInstanceByIds = true; | ||||
| 	packWriter->sendStackInstanceByIds = true; | ||||
| } | ||||
|  | ||||
| void CConnection::enterLobbyConnectionMode() | ||||
| { | ||||
| 	iser.loadedPointers.clear(); | ||||
| 	oser.savedPointers.clear(); | ||||
| 	deserializer->loadedPointers.clear(); | ||||
| 	serializer->savedPointers.clear(); | ||||
| 	disableSmartVectorMemberSerialization(); | ||||
| 	disableSmartPointerSerialization(); | ||||
| 	disableStackSendingByID(); | ||||
| } | ||||
|  | ||||
| void CConnection::enterGameplayConnectionMode(CGameState * gs) | ||||
| { | ||||
| 	enableStackSendingByID(); | ||||
| 	disableSmartPointerSerialization(); | ||||
| 	addStdVecItems(gs); | ||||
|  | ||||
| 	packReader->addStdVecItems(gs); | ||||
| 	packWriter->addStdVecItems(gs); | ||||
| } | ||||
|  | ||||
| void CConnection::disableSmartPointerSerialization() | ||||
| { | ||||
| 	deserializer->smartPointerSerialization = false; | ||||
| 	serializer->smartPointerSerialization = false; | ||||
| } | ||||
|  | ||||
| void CConnection::enableSmartPointerSerialization() | ||||
| { | ||||
| 	deserializer->smartPointerSerialization = true; | ||||
| 	serializer->smartPointerSerialization = true; | ||||
| } | ||||
|  | ||||
| void CConnection::disableSmartVectorMemberSerialization() | ||||
| { | ||||
| 	CSerializer::smartVectorMembersSerialization = false; | ||||
| 	packReader->smartVectorMembersSerialization = false; | ||||
| 	packWriter->smartVectorMembersSerialization = false; | ||||
| } | ||||
|  | ||||
| void CConnection::enableSmartVectorMemberSerializatoin() | ||||
| { | ||||
| 	CSerializer::smartVectorMembersSerialization = true; | ||||
| } | ||||
|  | ||||
| std::string CConnection::toString() const | ||||
| { | ||||
| 	boost::format fmt("Connection with %s (ID: %d UUID: %s)"); | ||||
| 	fmt % name % connectionID % uuid; | ||||
| 	return fmt.str(); | ||||
| 	packReader->smartVectorMembersSerialization = true; | ||||
| 	packWriter->smartVectorMembersSerialization = true; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -9,84 +9,49 @@ | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "CSerializer.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class BinaryDeserializer; | ||||
| class BinarySerializer; | ||||
| struct CPack; | ||||
| struct ConnectionBuffers; | ||||
| class NetworkConnection; | ||||
| class ConnectionPackReader; | ||||
| class ConnectionPackWriter; | ||||
| class CGameState; | ||||
|  | ||||
| /// Main class for network communication | ||||
| /// Allows establishing connection and bidirectional read-write | ||||
| class DLL_LINKAGE CConnection : public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this<CConnection> | ||||
| /// Wrapper class for game connection | ||||
| /// Handles serialization and deserialization of data received from network | ||||
| class DLL_LINKAGE CConnection : boost::noncopyable | ||||
| { | ||||
| 	/// Non-owning pointer to underlying connection | ||||
| 	std::weak_ptr<NetworkConnection> networkConnection; | ||||
|  | ||||
| //	void init(); | ||||
| //	void reportState(vstd::CLoggerBase * out) override; | ||||
| // | ||||
| 	int write(const void * data, unsigned size) override; | ||||
| 	int read(void * data, unsigned size) override; | ||||
| //	void flushBuffers(); | ||||
| // | ||||
| //	bool enableBufferedWrite; | ||||
| //	bool enableBufferedRead; | ||||
| //	std::unique_ptr<ConnectionBuffers> connectionBuffers; | ||||
| // | ||||
| 	std::unique_ptr<BinaryDeserializer> iser; | ||||
| 	std::unique_ptr<BinarySerializer> oser; | ||||
| // | ||||
| //	std::string contactUuid; | ||||
| //	std::string name; //who uses this connection | ||||
| 	std::unique_ptr<ConnectionPackReader> packReader; | ||||
| 	std::unique_ptr<ConnectionPackWriter> packWriter; | ||||
| 	std::unique_ptr<BinaryDeserializer> deserializer; | ||||
| 	std::unique_ptr<BinarySerializer> serializer; | ||||
|  | ||||
| 	void disableStackSendingByID(); | ||||
| 	void enableStackSendingByID(); | ||||
| 	void disableSmartPointerSerialization(); | ||||
| 	void enableSmartPointerSerialization(); | ||||
| 	void disableSmartVectorMemberSerialization(); | ||||
| 	void enableSmartVectorMemberSerializatoin(); | ||||
|  | ||||
| public: | ||||
| 	bool isMyConnection(const std::shared_ptr<NetworkConnection> & otherConnection) const; | ||||
|  | ||||
| 	std::string uuid; | ||||
| 	int connectionID; | ||||
|  | ||||
| 	CConnection(std::weak_ptr<NetworkConnection> networkConnection); | ||||
| //	CConnection(const std::string & host, ui16 port, std::string Name, std::string UUID); | ||||
| //	CConnection(const std::shared_ptr<TAcceptor> & acceptor, const std::shared_ptr<boost::asio::io_service> & Io_service, std::string Name, std::string UUID); | ||||
| //	CConnection(std::shared_ptr<TSocket> Socket, std::string Name, std::string UUID); //use immediately after accepting connection into socket | ||||
| //	virtual ~CConnection(); | ||||
| 	~CConnection(); | ||||
|  | ||||
| //	void close(); | ||||
| //	bool isOpen() const; | ||||
| // | ||||
| //	CPack * retrievePack(); | ||||
| 	void sendPack(const CPack * pack); | ||||
|  | ||||
| 	CPack * retrievePack(const std::vector<uint8_t> & data); | ||||
| //	std::vector<uint8_t> serializePack(const CPack * pack); | ||||
| // | ||||
| 	void disableStackSendingByID(); | ||||
| //	void enableStackSendingByID(); | ||||
| //	void disableSmartPointerSerialization(); | ||||
| //	void enableSmartPointerSerialization(); | ||||
| //	void disableSmartVectorMemberSerialization(); | ||||
| //	void enableSmartVectorMemberSerializatoin(); | ||||
| // | ||||
|  | ||||
| 	void enterLobbyConnectionMode(); | ||||
| 	void enterGameplayConnectionMode(CGameState * gs); | ||||
| // | ||||
| //	std::string toString() const; | ||||
| // | ||||
| //	template<class T> | ||||
| //	CConnection & operator>>(T &t) | ||||
| //	{ | ||||
| //		iser & t; | ||||
| //		return * this; | ||||
| //	} | ||||
| // | ||||
| //	template<class T> | ||||
| //	CConnection & operator<<(const T &t) | ||||
| //	{ | ||||
| //		oser & t; | ||||
| //		return * this; | ||||
| //	} | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -177,7 +177,10 @@ void CVCMIServer::onNewConnection(const std::shared_ptr<NetworkConnection> & con | ||||
| 		establishOutgoingConnection(); | ||||
|  | ||||
| 	if(state == EServerState::LOBBY) | ||||
| 	{ | ||||
| 		activeConnections.push_back(std::make_shared<CConnection>(connection));//, SERVER_NAME, uuid);) | ||||
| 		activeConnections.back()->enterLobbyConnectionMode(); | ||||
| 	} | ||||
| 	// TODO: else: deny connection | ||||
| 	// TODO: else: try to reconnect / send state to reconnected client | ||||
| } | ||||
| @@ -193,26 +196,16 @@ void CVCMIServer::onPacketReceived(const std::shared_ptr<NetworkConnection> & co | ||||
| 	//FIXME: delete pack? | ||||
| } | ||||
|  | ||||
| void CVCMIServer::onPacketReceived(const std::vector<uint8_t> & message) | ||||
| { | ||||
| 	//TODO: handle pack received from lobby | ||||
| } | ||||
|  | ||||
| void CVCMIServer::onConnectionFailed(const std::string & errorMessage) | ||||
| { | ||||
| 	//TODO: handle failure to connect to lobby | ||||
| } | ||||
|  | ||||
| void CVCMIServer::onConnectionEstablished() | ||||
| void CVCMIServer::onConnectionEstablished(const std::shared_ptr<NetworkConnection> &) | ||||
| { | ||||
| 	//TODO: handle connection to lobby - login? | ||||
| } | ||||
|  | ||||
| void CVCMIServer::onDisconnected() | ||||
| { | ||||
| 	//TODO: handle disconnection from lobby | ||||
| } | ||||
|  | ||||
| void CVCMIServer::setState(EServerState value) | ||||
| { | ||||
| 	state.store(value); | ||||
| @@ -225,9 +218,13 @@ EServerState CVCMIServer::getState() const | ||||
|  | ||||
| std::shared_ptr<CConnection> CVCMIServer::findConnection(const std::shared_ptr<NetworkConnection> & netConnection) | ||||
| { | ||||
| 	//TODO | ||||
| 	assert(0); | ||||
| 	return nullptr; | ||||
| 	for (auto const & gameConnection : activeConnections) | ||||
| 	{ | ||||
| 		if (gameConnection->isMyConnection(netConnection)) | ||||
| 			return gameConnection; | ||||
| 	} | ||||
|  | ||||
| 	throw std::runtime_error("Unknown connection received in CVCMIServer::findConnection"); | ||||
| } | ||||
|  | ||||
| void CVCMIServer::run() | ||||
| @@ -294,10 +291,8 @@ void CVCMIServer::prepareToRestart() | ||||
| 	} | ||||
| 	 | ||||
| 	for(auto c : activeConnections) | ||||
| 	{ | ||||
| 		c->enterLobbyConnectionMode(); | ||||
| 		c->disableStackSendingByID(); | ||||
| 	} | ||||
|  | ||||
| 	boost::unique_lock<boost::recursive_mutex> queueLock(mx); | ||||
| 	gh = nullptr; | ||||
| } | ||||
| @@ -417,8 +412,8 @@ void CVCMIServer::announcePack(std::unique_ptr<CPackForLobby> pack) | ||||
| 	{ | ||||
| 		// FIXME: we need to avoid sending something to client that not yet get answer for LobbyClientConnected | ||||
| 		// Until UUID set we only pass LobbyClientConnected to this client | ||||
| 		if(c->uuid == uuid && !dynamic_cast<LobbyClientConnected *>(pack.get())) | ||||
| 			continue; | ||||
| 		//if(c->uuid == uuid && !dynamic_cast<LobbyClientConnected *>(pack.get())) | ||||
| 		//	continue; | ||||
|  | ||||
| 		c->sendPack(pack.get()); | ||||
| 	} | ||||
|   | ||||
| @@ -70,16 +70,12 @@ private: | ||||
| 	std::unique_ptr<boost::thread> remoteConnectionsThread; | ||||
| 	std::atomic<EServerState> state; | ||||
|  | ||||
| 	// INetworkServerListener impl | ||||
| 	// INetworkListener impl | ||||
| 	void onDisconnected(const std::shared_ptr<NetworkConnection> & connection) override; | ||||
| 	void onPacketReceived(const std::shared_ptr<NetworkConnection> & connection, const std::vector<uint8_t> & message) override; | ||||
| 	void onNewConnection(const std::shared_ptr<NetworkConnection> &) override; | ||||
|  | ||||
| 	// INetworkClientListener impl | ||||
| 	void onPacketReceived(const std::vector<uint8_t> & message) override; | ||||
| 	void onConnectionFailed(const std::string & errorMessage) override; | ||||
| 	void onConnectionEstablished() override; | ||||
| 	void onDisconnected() override; | ||||
| 	void onConnectionEstablished(const std::shared_ptr<NetworkConnection> &) override; | ||||
|  | ||||
| 	void establishOutgoingConnection(); | ||||
|  | ||||
|   | ||||
| @@ -210,10 +210,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) | ||||
| void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) | ||||
| { | ||||
| 	for(auto & c : srv.activeConnections) | ||||
| 	{ | ||||
| 		c->enterLobbyConnectionMode(); | ||||
| 		c->disableStackSendingByID(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ClientPermissionsCheckerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user