mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-10 22:31:40 +02:00
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)
This commit is contained in:
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -1077,3 +1077,8 @@ INetworkHandler & CVCMIServer::getNetworkHandler()
|
||||
{
|
||||
return *networkHandler;
|
||||
}
|
||||
|
||||
INetworkServer & CVCMIServer::getNetworkServer()
|
||||
{
|
||||
return *networkServer;
|
||||
}
|
||||
|
@@ -107,6 +107,7 @@ public:
|
||||
void updateAndPropagateLobbyState();
|
||||
|
||||
INetworkHandler & getNetworkHandler();
|
||||
INetworkServer & getNetworkServer();
|
||||
|
||||
void setState(EServerState value);
|
||||
EServerState getState() const;
|
||||
|
Reference in New Issue
Block a user