Browse Source

Remove network connection from local games

This removes need for TCP network connection in single-player games.

Instead, game will now create internal pseudo-connection that performs
client<->server communication by posting sent messages to client/server
asio::io_service'a.

This should fix gameplay aborting on switching to another app on iOS (and
apparently, on Android in some cases)
Ivan Savenko 8 months ago
parent
commit
d9244cf061

+ 7 - 3
client/CServerHandler.cpp

@@ -206,9 +206,13 @@ void CServerHandler::connectToServer(const std::string & addr, const ui16 port)
 
 		Settings remotePort = settings.write["server"]["remotePort"];
 		remotePort->Integer() = port;
-	}
 
-	networkHandler->connectToRemote(*this, addr, port);
+		networkHandler->connectToRemote(*this, addr, port);
+	}
+	else
+	{
+		serverRunner->connect(*networkHandler, *this, addr, port);
+	}
 }
 
 void CServerHandler::onConnectionFailed(const std::string & errorMessage)
@@ -245,7 +249,7 @@ void CServerHandler::onTimer()
 	}
 
 	assert(isServerLocal());
-	networkHandler->connectToRemote(*this, getLocalHostname(), getLocalPort());
+	serverRunner->connect(*networkHandler, *this, getLocalHostname(), getLocalPort());
 }
 
 void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)

+ 11 - 0
client/ServerRunner.cpp

@@ -13,6 +13,7 @@
 
 #include "../lib/VCMIDirs.h"
 #include "../lib/CThreadHelper.h"
+#include "../lib/network/NetworkInterface.h"
 #include "../server/CVCMIServer.h"
 
 #ifdef ENABLE_SERVER_PROCESS
@@ -74,6 +75,11 @@ int ServerThreadRunner::exitCode()
 	return 0;
 }
 
+void ServerThreadRunner::connect(INetworkHandler & network, INetworkClientListener & listener, const std::string & host, uint16_t port)
+{
+	network.createInternalConnection(listener, server->getNetworkServer());
+}
+
 #ifdef ENABLE_SERVER_PROCESS
 
 ServerProcessRunner::ServerProcessRunner() = default;
@@ -113,4 +119,9 @@ uint16_t ServerProcessRunner::start(uint16_t port, bool connectToLobby, std::sha
 	return port;
 }
 
+void ServerProcessRunner::connect(INetworkHandler & network, INetworkClientListener & listener, const std::string & host, uint16_t port)
+{
+	network.connectToRemote(listener, host, port);
+}
+
 #endif

+ 10 - 2
client/ServerRunner.h

@@ -12,6 +12,8 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 struct StartInfo;
+class INetworkHandler;
+class INetworkClientListener;
 
 VCMI_LIB_NAMESPACE_END
 
@@ -25,11 +27,13 @@ public:
 	virtual void wait() = 0;
 	virtual int exitCode() = 0;
 
+	virtual void connect(INetworkHandler & network, INetworkClientListener & listener, const std::string & host, uint16_t port) = 0;
+
 	virtual ~IServerRunner() = default;
 };
 
 /// Class that runs server instance as a thread of client process
-class ServerThreadRunner : public IServerRunner, boost::noncopyable
+class ServerThreadRunner final : public IServerRunner, boost::noncopyable
 {
 	std::unique_ptr<CVCMIServer> server;
 	boost::thread threadRunLocalServer;
@@ -39,6 +43,8 @@ public:
 	void wait() override;
 	int exitCode() override;
 
+	void connect(INetworkHandler & network, INetworkClientListener & listener, const std::string & host, uint16_t port) override;
+
 	ServerThreadRunner();
 	~ServerThreadRunner();
 };
@@ -64,7 +70,7 @@ class child;
 
 /// Class that runs server instance as a child process
 /// Available only on desktop systems where process management is allowed
-class ServerProcessRunner : public IServerRunner, boost::noncopyable
+class ServerProcessRunner final : public IServerRunner, boost::noncopyable
 {
 	std::unique_ptr<boost::process::child> child;
 
@@ -74,6 +80,8 @@ public:
 	void wait() override;
 	int exitCode() override;
 
+	void connect(INetworkHandler & network, INetworkClientListener & listener, const std::string & host, uint16_t port) override;
+
 	ServerProcessRunner();
 	~ServerProcessRunner();
 };

+ 51 - 0
lib/network/NetworkConnection.cpp

@@ -208,4 +208,55 @@ void NetworkConnection::close()
 	//NOTE: ignoring error code, intended
 }
 
+InternalConnection::InternalConnection(INetworkConnectionListener & listener, const std::shared_ptr<NetworkContext> & context)
+	: listener(listener)
+	, io(context)
+{
+}
+
+void InternalConnection::receivePacket(const std::vector<std::byte> & message)
+{
+	io->post([self = shared_from_this(), message](){
+		self->listener.onPacketReceived(self, message);
+	});
+}
+
+void InternalConnection::disconnect()
+{
+	io->post([self = shared_from_this()](){
+		self->listener.onDisconnected(self, "Internal connection has been terminated");
+		self->otherSideWeak.reset();
+	});
+}
+
+void InternalConnection::connectTo(std::shared_ptr<IInternalConnection> connection)
+{
+	otherSideWeak = connection;
+}
+
+void InternalConnection::sendPacket(const std::vector<std::byte> & message)
+{
+	auto otherSide = otherSideWeak.lock();
+
+	if (otherSide)
+		otherSide->receivePacket(message);
+	else
+		throw std::runtime_error("Failed to send packet! Connection has been deleted!");
+}
+
+void InternalConnection::setAsyncWritesEnabled(bool on)
+{
+	// no-op
+}
+
+void InternalConnection::close()
+{
+	auto otherSide = otherSideWeak.lock();
+
+	if (otherSide)
+		otherSide->disconnect();
+
+	otherSideWeak.reset();
+}
+
 VCMI_LIB_NAMESPACE_END

+ 16 - 0
lib/network/NetworkConnection.h

@@ -46,4 +46,20 @@ public:
 	void setAsyncWritesEnabled(bool on) override;
 };
 
+class InternalConnection final : public IInternalConnection, public std::enable_shared_from_this<InternalConnection>
+{
+	std::weak_ptr<IInternalConnection> otherSideWeak;
+	std::shared_ptr<NetworkContext> io;
+	INetworkConnectionListener & listener;
+public:
+	InternalConnection(INetworkConnectionListener & listener, const std::shared_ptr<NetworkContext> & context);
+
+	void receivePacket(const std::vector<std::byte> & message) override;
+	void disconnect() override;
+	void connectTo(std::shared_ptr<IInternalConnection> connection) override;
+	void sendPacket(const std::vector<std::byte> & message) override;
+	void setAsyncWritesEnabled(bool on) override;
+	void close() override;
+};
+
 VCMI_LIB_NAMESPACE_END

+ 11 - 0
lib/network/NetworkHandler.cpp

@@ -73,6 +73,17 @@ void NetworkHandler::createTimer(INetworkTimerListener & listener, std::chrono::
 	});
 }
 
+void NetworkHandler::createInternalConnection(INetworkClientListener & listener, INetworkServer & server)
+{
+	auto localConnection = std::make_shared<InternalConnection>(listener, io);
+
+	server.receiveInternalConnection(localConnection);
+
+	io->post([&listener, localConnection](){
+		listener.onConnectionEstablished(localConnection);
+	});
+}
+
 void NetworkHandler::stop()
 {
 	io->stop();

+ 1 - 0
lib/network/NetworkHandler.h

@@ -22,6 +22,7 @@ public:
 
 	std::unique_ptr<INetworkServer> createServerTCP(INetworkServerListener & listener) override;
 	void connectToRemote(INetworkClientListener & listener, const std::string & host, uint16_t port) override;
+	void createInternalConnection(INetworkClientListener & listener, INetworkServer & server) override;
 	void createTimer(INetworkTimerListener & listener, std::chrono::milliseconds duration) override;
 
 	void run() override;

+ 14 - 0
lib/network/NetworkInterface.h

@@ -21,6 +21,15 @@ public:
 	virtual void close() = 0;
 };
 
+/// Class for internal connections within single process, for use when TCP is not possible or not desired
+class IInternalConnection : public INetworkConnection
+{
+public:
+	virtual void receivePacket(const std::vector<std::byte> & message) = 0;
+	virtual void disconnect() = 0;
+	virtual void connectTo(std::shared_ptr<IInternalConnection> connection) = 0;
+};
+
 using NetworkConnectionPtr = std::shared_ptr<INetworkConnection>;
 using NetworkConnectionWeakPtr = std::weak_ptr<INetworkConnection>;
 
@@ -41,6 +50,7 @@ public:
 	virtual ~INetworkServer() = default;
 
 	virtual uint16_t start(uint16_t port) = 0;
+	virtual void receiveInternalConnection(std::shared_ptr<IInternalConnection> remoteConnection) = 0;
 };
 
 /// Base interface that must be implemented by user of networking API to handle any connection callbacks
@@ -94,6 +104,10 @@ public:
 	/// On failure: INetworkTimerListener::onConnectionFailed will be called with human-readable error message
 	virtual void connectToRemote(INetworkClientListener & listener, const std::string & host, uint16_t port) = 0;
 
+	/// Creates an instance of internal connection that is connected to a network server, but uses intra-process communication instead of TCP
+	/// On success INetworkTimerListener::onConnectionEstablished() will be called asynchronously, established connection provided as parameter
+	virtual void createInternalConnection(INetworkClientListener & listener, INetworkServer & server) = 0;
+
 	/// Creates a timer that will be called once, after specified interval has passed
 	/// On success: INetworkTimerListener::onTimer() will be called
 	/// On failure: no-op

+ 12 - 0
lib/network/NetworkServer.cpp

@@ -57,6 +57,18 @@ void NetworkServer::onDisconnected(const std::shared_ptr<INetworkConnection> & c
 	}
 }
 
+void NetworkServer::receiveInternalConnection(std::shared_ptr<IInternalConnection> remoteConnection)
+{
+	auto localConnection = std::make_shared<InternalConnection>(*this, io);
+
+	connections.insert(localConnection);
+
+	localConnection->connectTo(remoteConnection);
+	remoteConnection->connectTo(localConnection);
+
+	listener.onNewConnection(localConnection);
+}
+
 void NetworkServer::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message)
 {
 	listener.onPacketReceived(connection, message);

+ 2 - 0
lib/network/NetworkServer.h

@@ -29,6 +29,8 @@ class NetworkServer : public INetworkConnectionListener, public INetworkServer
 public:
 	NetworkServer(INetworkServerListener & listener, const std::shared_ptr<NetworkContext> & context);
 
+	void receiveInternalConnection(std::shared_ptr<IInternalConnection> remoteConnection) override;
+
 	uint16_t start(uint16_t port) override;
 };
 

+ 5 - 0
server/CVCMIServer.cpp

@@ -1077,3 +1077,8 @@ INetworkHandler & CVCMIServer::getNetworkHandler()
 {
 	return *networkHandler;
 }
+
+INetworkServer & CVCMIServer::getNetworkServer()
+{
+	return *networkServer;
+}

+ 1 - 0
server/CVCMIServer.h

@@ -107,6 +107,7 @@ public:
 	void updateAndPropagateLobbyState();
 
 	INetworkHandler & getNetworkHandler();
+	INetworkServer & getNetworkServer();
 
 	void setState(EServerState value);
 	EServerState getState() const;