1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Refactor enet-related code

This commit is contained in:
nordsoft 2023-01-18 02:02:17 +04:00
parent 39ad8c7da1
commit ccef596cc0
11 changed files with 470 additions and 294 deletions

View File

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

View File

@ -55,7 +55,6 @@
#include "../lib/serializer/Cast.h"
#include <vcmi/events/EventBus.h>
#include <enet/enet.h>
#ifdef VCMI_WINDOWS
#include <windows.h>
@ -121,21 +120,27 @@ extern std::string NAME;
CServerHandler::CServerHandler()
: 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()());
//read from file to restore last session
if(!settings["server"]["uuid"].isNull() && !settings["server"]["uuid"].String().empty())
uuid = settings["server"]["uuid"].String();
applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
registerTypesLobbyPacks(*applier);
threadPollClient = std::make_shared<boost::thread>(&CServerHandler::threadPoll, this);
threadPollClient->detach();
}
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)
@ -179,50 +184,6 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::
#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()
{
/*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)
{
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)
@ -346,9 +297,6 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po
}
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())
{

View File

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

View File

@ -179,6 +179,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/CStack.cpp
${MAIN_LIB_DIR}/CThreadHelper.cpp
${MAIN_LIB_DIR}/CTownHandler.cpp
${MAIN_LIB_DIR}/EnetService.cpp
${MAIN_LIB_DIR}/GameConstants.cpp
${MAIN_LIB_DIR}/HeroBonus.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}/CThreadHelper.h
${MAIN_LIB_DIR}/CTownHandler.h
${MAIN_LIB_DIR}/EnetService.h
${MAIN_LIB_DIR}/FunctionList.h
${MAIN_LIB_DIR}/GameConstants.h
${MAIN_LIB_DIR}/HeroBonus.h

270
lib/EnetService.cpp Normal file
View File

@ -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
lib/EnetService.h Normal file
View File

@ -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

View File

@ -51,76 +51,30 @@ void CConnection::init()
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)
{
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;
}
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()
{
if(handler)
handler->join();
for(auto * packet : packets)
enet_packet_destroy(packet);
close();
}
@ -134,11 +88,18 @@ CConnection & CConnection::operator&(const T &t) {
return *this;
}
const std::shared_ptr<EnetConnection> CConnection::getEnetConnection() const
{
return enetConnection;
}
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
@ -179,9 +140,7 @@ void CConnection::sendPack(const CPack * pack)
{
logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
oser & pack;
}
void CConnection::disableStackSendingByID()

View File

@ -9,10 +9,9 @@
*/
#pragma once
#include <enet/enet.h>
#include "BinaryDeserializer.h"
#include "BinarySerializer.h"
#include "../EnetService.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -24,16 +23,13 @@ struct ConnectionBuffers;
class DLL_LINKAGE CConnection
: public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this<CConnection>
{
std::shared_ptr<EnetConnection> enetConnection;
void reportState(vstd::CLoggerBase * out) override;
int write(const 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;
BinarySerializer oser;
@ -51,12 +47,11 @@ public:
int connectionID;
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();
bool isOpen() const;
template<class T>

View File

@ -127,18 +127,9 @@ CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts)
port = cmdLineOptions["port"].as<ui16>();
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);
}
@ -151,8 +142,68 @@ CVCMIServer::~CVCMIServer()
announceLobbyThread->join();
if(lobbyConnectionsThread)
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;
}
enet_host_destroy(server);
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::run()
@ -172,9 +223,6 @@ void CVCMIServer::run()
}
#endif
if(!lobbyConnectionsThread)
lobbyConnectionsThread = std::make_unique<boost::thread>(&CVCMIServer::startAsyncAccept, this);
/*if(!remoteConnectionsThread && cmdLineOptions.count("lobby"))
{
remoteConnectionsThread = vstd::make_unique<boost::thread>(&CVCMIServer::establishRemoteConnections, this);
@ -193,7 +241,6 @@ void CVCMIServer::run()
while(state == EServerState::LOBBY || state == EServerState::GAMEPLAY_STARTING)
{
connectionAccepted();
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
}
@ -224,7 +271,7 @@ void CVCMIServer::connectToRemote(const std::string & addr, int port)
try
{
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(...)
{
@ -316,72 +363,6 @@ void CVCMIServer::startGameImmidiately()
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)
{
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)
{
PlayerReinitInterface startAiPack;
@ -1073,10 +1009,6 @@ static void handleCommandOptions(int argc, char * argv[], boost::program_options
int main(int argc, char * argv[])
{
#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
boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
#endif
@ -1126,7 +1058,6 @@ int main(int argc, char * argv[])
#endif
logConfig.deconfigure();
vstd::clear_pointer(VLC);
enet_deinitialize();
return 0;
}

View File

@ -11,7 +11,7 @@
#include "../lib/serializer/Connection.h"
#include "../lib/StartInfo.h"
#include <enet/enet.h>
#include "../lib/EnetService.h"
#include <boost/program_options.hpp>
@ -44,7 +44,7 @@ enum class EServerState : ui8
SHUTDOWN
};
class CVCMIServer : public LobbyInfo
class CVCMIServer : public LobbyInfo, public EnetService
{
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::unique_ptr<boost::thread> announceLobbyThread, lobbyConnectionsThread, remoteConnectionsThread;
ENetHost * server;
public:
void handleDisconnection(std::shared_ptr<EnetConnection>) override;
void handleConnection(std::shared_ptr<EnetConnection>) override;
std::shared_ptr<CGameHandler> gh;
std::atomic<EServerState> state;
ui16 port;
@ -67,7 +68,6 @@ public:
std::atomic<int> currentClientId;
std::atomic<ui8> currentPlayerId;
std::shared_ptr<CConnection> hostClient;
std::shared_ptr<CConnection> upcomingConnection;
CVCMIServer(boost::program_options::variables_map & opts);
~CVCMIServer();
@ -78,8 +78,6 @@ public:
void establishRemoteConnections();
void connectToRemote(const std::string & addr, int port);
void startAsyncAccept();
void connectionAccepted();
void threadHandleClient(std::shared_ptr<CConnection> c);
void threadAnnounceLobby();
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 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 updateAndPropagateLobbyState();

View File

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