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:
parent
39ad8c7da1
commit
ccef596cc0
@ -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);
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
270
lib/EnetService.cpp
Normal 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
82
lib/EnetService.h
Normal 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
|
@ -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()
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -129,7 +129,6 @@ bool LobbyClientDisconnected::checkClientPermissions(CVCMIServer * srv) const
|
||||
|
||||
bool LobbyClientDisconnected::applyOnServer(CVCMIServer * srv)
|
||||
{
|
||||
srv->clientDisconnected(c);
|
||||
c->close();
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user