1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-04-25 12:14:46 +02:00

Merge pull request #5369 from IvanSavenko/internal_connection

[1.6.6?] Remove network connection from local games
This commit is contained in:
Ivan Savenko 2025-02-04 00:50:16 +02:00 committed by GitHub
commit a3797e4f76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 187 additions and 40 deletions

View File

@ -186,9 +186,9 @@ void CServerHandler::startLocalServerAndConnect(bool connectToLobby)
si->difficulty = lastDifficulty.Integer(); si->difficulty = lastDifficulty.Integer();
logNetwork->trace("\tStarting local server"); logNetwork->trace("\tStarting local server");
uint16_t srvport = serverRunner->start(getLocalPort(), connectToLobby, si); serverRunner->start(loadMode == ELoadMode::MULTI, connectToLobby, si);
logNetwork->trace("\tConnecting to local server"); logNetwork->trace("\tConnecting to local server");
connectToServer(getLocalHostname(), srvport); connectToServer(getLocalHostname(), getLocalPort());
logNetwork->trace("\tWaiting for connection"); logNetwork->trace("\tWaiting for connection");
} }
@ -206,10 +206,14 @@ void CServerHandler::connectToServer(const std::string & addr, const ui16 port)
Settings remotePort = settings.write["server"]["remotePort"]; Settings remotePort = settings.write["server"]["remotePort"];
remotePort->Integer() = port; remotePort->Integer() = port;
}
networkHandler->connectToRemote(*this, addr, port); networkHandler->connectToRemote(*this, addr, port);
} }
else
{
serverRunner->connect(*networkHandler, *this);
}
}
void CServerHandler::onConnectionFailed(const std::string & errorMessage) void CServerHandler::onConnectionFailed(const std::string & errorMessage)
{ {
@ -245,7 +249,7 @@ void CServerHandler::onTimer()
} }
assert(isServerLocal()); assert(isServerLocal());
networkHandler->connectToRemote(*this, getLocalHostname(), getLocalPort()); serverRunner->connect(*networkHandler, *this);
} }
void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection) void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)

View File

@ -13,6 +13,8 @@
#include "../lib/VCMIDirs.h" #include "../lib/VCMIDirs.h"
#include "../lib/CThreadHelper.h" #include "../lib/CThreadHelper.h"
#include "../lib/network/NetworkInterface.h"
#include "../lib/CConfigHandler.h"
#include "../server/CVCMIServer.h" #include "../server/CVCMIServer.h"
#ifdef ENABLE_SERVER_PROCESS #ifdef ENABLE_SERVER_PROCESS
@ -33,10 +35,11 @@
ServerThreadRunner::ServerThreadRunner() = default; ServerThreadRunner::ServerThreadRunner() = default;
ServerThreadRunner::~ServerThreadRunner() = default; ServerThreadRunner::~ServerThreadRunner() = default;
uint16_t ServerThreadRunner::start(uint16_t cfgport, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) void ServerThreadRunner::start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo)
{ {
// cfgport may be 0 -- the real port is returned after calling prepare() // cfgport may be 0 -- the real port is returned after calling prepare()
server = std::make_unique<CVCMIServer>(cfgport, true); uint16_t port = settings["server"]["localPort"].Integer();
server = std::make_unique<CVCMIServer>(port, true);
if (startingInfo) if (startingInfo)
{ {
@ -45,18 +48,16 @@ uint16_t ServerThreadRunner::start(uint16_t cfgport, bool connectToLobby, std::s
std::promise<uint16_t> promise; std::promise<uint16_t> promise;
threadRunLocalServer = boost::thread([this, connectToLobby, &promise]{ threadRunLocalServer = boost::thread([this, connectToLobby, listenForConnections, &promise]{
setThreadName("runServer"); setThreadName("runServer");
uint16_t port = server->prepare(connectToLobby); uint16_t port = server->prepare(connectToLobby, listenForConnections);
promise.set_value(port); promise.set_value(port);
server->run(); server->run();
}); });
logNetwork->trace("Waiting for server port..."); logNetwork->trace("Waiting for server port...");
auto srvport = promise.get_future().get(); serverPort = promise.get_future().get();
logNetwork->debug("Server port: %d", srvport); logNetwork->debug("Server port: %d", serverPort);
return srvport;
} }
void ServerThreadRunner::shutdown() void ServerThreadRunner::shutdown()
@ -74,6 +75,11 @@ int ServerThreadRunner::exitCode()
return 0; return 0;
} }
void ServerThreadRunner::connect(INetworkHandler & network, INetworkClientListener & listener)
{
network.createInternalConnection(listener, server->getNetworkServer());
}
#ifdef ENABLE_SERVER_PROCESS #ifdef ENABLE_SERVER_PROCESS
ServerProcessRunner::ServerProcessRunner() = default; ServerProcessRunner::ServerProcessRunner() = default;
@ -94,8 +100,9 @@ int ServerProcessRunner::exitCode()
return child->exit_code(); return child->exit_code();
} }
uint16_t ServerProcessRunner::start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) void ServerProcessRunner::start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo)
{ {
uint16_t port = settings["server"]["localPort"].Integer();
boost::filesystem::path serverPath = VCMIDirs::get().serverPath(); boost::filesystem::path serverPath = VCMIDirs::get().serverPath();
boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "server_log.txt"; boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "server_log.txt";
std::vector<std::string> args; std::vector<std::string> args;
@ -109,8 +116,14 @@ uint16_t ServerProcessRunner::start(uint16_t port, bool connectToLobby, std::sha
if (ec) if (ec)
throw std::runtime_error("Failed to start server! Reason: " + ec.message()); throw std::runtime_error("Failed to start server! Reason: " + ec.message());
}
return port; void ServerProcessRunner::connect(INetworkHandler & network, INetworkClientListener & listener)
{
std::string host = settings["server"]["localHostname"].String();
uint16_t port = settings["server"]["localPort"].Integer();
network.connectToRemote(listener, host, port);
} }
#endif #endif

View File

@ -12,6 +12,8 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct StartInfo; struct StartInfo;
class INetworkHandler;
class INetworkClientListener;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
@ -20,25 +22,31 @@ class CVCMIServer;
class IServerRunner class IServerRunner
{ {
public: public:
virtual uint16_t start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) = 0; virtual void start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) = 0;
virtual void shutdown() = 0; virtual void shutdown() = 0;
virtual void wait() = 0; virtual void wait() = 0;
virtual int exitCode() = 0; virtual int exitCode() = 0;
virtual void connect(INetworkHandler & network, INetworkClientListener & listener) = 0;
virtual ~IServerRunner() = default; virtual ~IServerRunner() = default;
}; };
/// Class that runs server instance as a thread of client process /// 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; std::unique_ptr<CVCMIServer> server;
boost::thread threadRunLocalServer; boost::thread threadRunLocalServer;
uint16_t serverPort = 0;
public: public:
uint16_t start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override; void start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override;
void shutdown() override; void shutdown() override;
void wait() override; void wait() override;
int exitCode() override; int exitCode() override;
void connect(INetworkHandler & network, INetworkClientListener & listener) override;
ServerThreadRunner(); ServerThreadRunner();
~ServerThreadRunner(); ~ServerThreadRunner();
}; };
@ -64,16 +72,18 @@ class child;
/// Class that runs server instance as a child process /// Class that runs server instance as a child process
/// Available only on desktop systems where process management is allowed /// 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; std::unique_ptr<boost::process::child> child;
public: public:
uint16_t start(uint16_t port, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override; void start(bool listenForConnections, bool connectToLobby, std::shared_ptr<StartInfo> startingInfo) override;
void shutdown() override; void shutdown() override;
void wait() override; void wait() override;
int exitCode() override; int exitCode() override;
void connect(INetworkHandler & network, INetworkClientListener & listener) override;
ServerProcessRunner(); ServerProcessRunner();
~ServerProcessRunner(); ~ServerProcessRunner();
}; };

View File

@ -208,4 +208,55 @@ void NetworkConnection::close()
//NOTE: ignoring error code, intended //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 VCMI_LIB_NAMESPACE_END

View File

@ -46,4 +46,20 @@ public:
void setAsyncWritesEnabled(bool on) override; 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 VCMI_LIB_NAMESPACE_END

View File

@ -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() void NetworkHandler::stop()
{ {
io->stop(); io->stop();

View File

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

View File

@ -21,6 +21,15 @@ public:
virtual void close() = 0; 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 NetworkConnectionPtr = std::shared_ptr<INetworkConnection>;
using NetworkConnectionWeakPtr = std::weak_ptr<INetworkConnection>; using NetworkConnectionWeakPtr = std::weak_ptr<INetworkConnection>;
@ -41,6 +50,7 @@ public:
virtual ~INetworkServer() = default; virtual ~INetworkServer() = default;
virtual uint16_t start(uint16_t port) = 0; 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 /// 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 /// 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; 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 /// Creates a timer that will be called once, after specified interval has passed
/// On success: INetworkTimerListener::onTimer() will be called /// On success: INetworkTimerListener::onTimer() will be called
/// On failure: no-op /// On failure: no-op

View File

@ -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) void NetworkServer::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message)
{ {
listener.onPacketReceived(connection, message); listener.onPacketReceived(connection, message);

View File

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

View File

@ -80,27 +80,34 @@ CVCMIServer::CVCMIServer(uint16_t port, bool runByClient)
CVCMIServer::~CVCMIServer() = default; CVCMIServer::~CVCMIServer() = default;
uint16_t CVCMIServer::prepare(bool connectToLobby) { uint16_t CVCMIServer::prepare(bool connectToLobby, bool listenForConnections) {
if(connectToLobby) { if(connectToLobby) {
lobbyProcessor = std::make_unique<GlobalLobbyProcessor>(*this); lobbyProcessor = std::make_unique<GlobalLobbyProcessor>(*this);
return 0; return 0;
} else { } else {
return startAcceptingIncomingConnections(); return startAcceptingIncomingConnections(listenForConnections);
} }
} }
uint16_t CVCMIServer::startAcceptingIncomingConnections() uint16_t CVCMIServer::startAcceptingIncomingConnections(bool listenForConnections)
{ {
networkServer = networkHandler->createServerTCP(*this);
port port
? logNetwork->info("Port %d will be used", port) ? logNetwork->info("Port %d will be used", port)
: logNetwork->info("Randomly assigned port will be used"); : logNetwork->info("Randomly assigned port will be used");
// config port may be 0 => srvport will contain the OS-assigned port value // config port may be 0 => srvport will contain the OS-assigned port value
networkServer = networkHandler->createServerTCP(*this);
if (listenForConnections)
{
auto srvport = networkServer->start(port); auto srvport = networkServer->start(port);
logNetwork->info("Listening for connections at port %d", srvport); logNetwork->info("Listening for connections at port %d", srvport);
return srvport; return srvport;
} }
else
return 0;
}
void CVCMIServer::onNewConnection(const std::shared_ptr<INetworkConnection> & connection) void CVCMIServer::onNewConnection(const std::shared_ptr<INetworkConnection> & connection)
{ {
@ -1077,3 +1084,8 @@ INetworkHandler & CVCMIServer::getNetworkHandler()
{ {
return *networkHandler; return *networkHandler;
} }
INetworkServer & CVCMIServer::getNetworkServer()
{
return *networkServer;
}

View File

@ -63,7 +63,7 @@ public:
/// List of all active connections /// List of all active connections
std::vector<std::shared_ptr<CConnection>> activeConnections; std::vector<std::shared_ptr<CConnection>> activeConnections;
uint16_t prepare(bool connectToLobby); uint16_t prepare(bool connectToLobby, bool listenForConnections);
// INetworkListener impl // INetworkListener impl
void onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage) override; void onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage) override;
@ -82,7 +82,7 @@ public:
bool prepareToStartGame(); bool prepareToStartGame();
void prepareToRestart(); void prepareToRestart();
void startGameImmediately(); void startGameImmediately();
uint16_t startAcceptingIncomingConnections(); uint16_t startAcceptingIncomingConnections(bool listenForConnections);
void threadHandleClient(std::shared_ptr<CConnection> c); void threadHandleClient(std::shared_ptr<CConnection> c);
@ -107,6 +107,7 @@ public:
void updateAndPropagateLobbyState(); void updateAndPropagateLobbyState();
INetworkHandler & getNetworkHandler(); INetworkHandler & getNetworkHandler();
INetworkServer & getNetworkServer();
void setState(EServerState value); void setState(EServerState value);
EServerState getState() const; EServerState getState() const;

View File

@ -99,7 +99,7 @@ void GlobalLobbyProcessor::receiveServerLoginSuccess(const JsonNode & json)
{ {
// no-op, wait just for any new commands from lobby // no-op, wait just for any new commands from lobby
logGlobal->info("Lobby: Successfully connected to lobby server"); logGlobal->info("Lobby: Successfully connected to lobby server");
owner.startAcceptingIncomingConnections(); owner.startAcceptingIncomingConnections(true);
} }
void GlobalLobbyProcessor::receiveAccountJoinsRoom(const JsonNode & json) void GlobalLobbyProcessor::receiveAccountJoinsRoom(const JsonNode & json)

View File

@ -92,7 +92,7 @@ int main(int argc, const char * argv[])
port = opts["port"].as<uint16_t>(); port = opts["port"].as<uint16_t>();
CVCMIServer server(port, runByClient); CVCMIServer server(port, runByClient);
server.prepare(connectToLobby); server.prepare(connectToLobby, true);
server.run(); server.run();
// CVCMIServer destructor must be called here - before VLC cleanup // CVCMIServer destructor must be called here - before VLC cleanup