1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

Initial version of antilag support

This commit is contained in:
Ivan Savenko
2025-06-28 21:42:54 +03:00
parent 58de180083
commit 3576efc3f3
24 changed files with 490 additions and 23 deletions

213
client/AntilagServer.cpp Normal file
View File

@@ -0,0 +1,213 @@
/*
* AntilagServer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AntilagServer.h"
#include "GameEngine.h"
#include "../server/CGameHandler.h"
#include "../lib/gameState/CGameState.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/serializer/GameConnection.h"
int ConnectionPackWriter::write(const std::byte * data, unsigned size)
{
buffer.insert(buffer.end(), data, data + size);
return size;
}
void AntilagFakeConnection::sendPack(const CPack & pack)
{
logGlobal->info("Prediction: pack '%s'", typeid(pack).name());
ConnectionPackWriter packWriter;
BinarySerializer serializer(&packWriter);
serializer & &pack;
writtenPacks.push_back(std::move(packWriter));
}
std::unique_ptr<CPack> AntilagFakeConnection::retrievePack(const std::vector<std::byte> & data)
{
throw std::runtime_error("AntilagFakeConnection::retrievePack not implemented");
}
int AntilagFakeConnection::getConnectionID() const
{
return 0;
}
void AntilagRollbackGeneratorVisitor::visitTryMoveHero(TryMoveHero & pack)
{
auto rollbackMove = std::make_unique<TryMoveHero>();
auto rollbackFow = std::make_unique<FoWChange>();
const auto * movedHero = gs.getHero(pack.id);
rollbackMove->id = pack.id;
rollbackMove->movePoints = movedHero->movementPointsRemaining();
rollbackMove->result = pack.result;
if (pack.result == TryMoveHero::EMBARK)
rollbackMove->result = TryMoveHero::DISEMBARK;
if (pack.result == TryMoveHero::DISEMBARK)
rollbackMove->result = TryMoveHero::EMBARK;
rollbackMove->start = pack.end;
rollbackMove->end = pack.start;
rollbackFow->mode = ETileVisibility::HIDDEN;
rollbackFow->player = movedHero->getOwner();
rollbackFow->tiles = pack.fowRevealed;
rollbackPacks.push_back(std::move(rollbackMove));
rollbackPacks.push_back(std::move(rollbackFow));
success = true;
}
bool AntilagRollbackGeneratorVisitor::canBeRolledBack() const
{
return success;
}
std::vector<std::unique_ptr<CPackForClient>> AntilagRollbackGeneratorVisitor::getRollbackPacks()
{
return std::move(rollbackPacks);
}
AntilagReplyPredictionVisitor::AntilagReplyPredictionVisitor() = default;
void AntilagReplyPredictionVisitor::visitMoveHero(MoveHero & pack)
{
canBeAppliedValue = true;
}
bool AntilagReplyPredictionVisitor::canBeApplied() const
{
return canBeAppliedValue;
}
AntilagServer::AntilagServer(INetworkHandler & network, const std::shared_ptr<CGameState> & gs)
: gameHandler(std::make_unique<CGameHandler>(*this, gs))
{
antilagNetConnection = network.createAsyncConnection(*this);
antilagGameConnection = std::make_shared<GameConnection>(antilagNetConnection);
}
AntilagServer::~AntilagServer() = default;
void AntilagServer::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
{
// should never be called
throw std::runtime_error("AntilagServer::onDisconnected called!");
}
void AntilagServer::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message)
{
std::scoped_lock interfaceLock(ENGINE->interfaceMutex);
auto basePack = antilagGameConnection->retrievePack(message);
auto * serverPack = dynamic_cast<CPackForServer*>(basePack.get());
AntilagReplyPredictionVisitor packVisitor;
serverPack->visit(packVisitor);
if (!packVisitor.canBeApplied())
return;
logGlobal->info("Predicting effects of pack '%s'", typeid(*serverPack).name());
auto newConnection = std::make_shared<AntilagFakeConnection>();
newConnection->requestID = serverPack->requestID;
newConnection->senderID = serverPack->player;
predictedReplies.push_back(std::move(newConnection));
gameHandler->handleReceivedPack(predictedReplies.back(), *serverPack);
}
void AntilagServer::tryPredictReply(const CPackForServer & request)
{
antilagGameConnection->sendPack(request);
logGlobal->info("Scheduled prediction of effects of pack '%s'", typeid(request).name());
}
bool AntilagServer::verifyReply(const CPackForClient & pack)
{
logGlobal->info("Verifying reply: received pack '%s'", typeid(pack).name());
const auto * packageReceived = dynamic_cast<const PackageReceived*>(&pack);
const auto * packageApplied = dynamic_cast<const PackageApplied*>(&pack);
if (packageReceived)
{
assert(currentPackageID == invalidPackageID);
assert(!predictedReplies.empty());
const auto & nextPrediction = predictedReplies.front();
assert(nextPrediction->senderID == packageReceived->player);
assert(nextPrediction->requestID == packageReceived->requestID);
currentPackageID = packageReceived->requestID;
}
if (currentPackageID == invalidPackageID)
{
// this is system package or reply to actions of another player
// TODO: consider reapplying all our predictions, in case if this event invalidated our prediction
return false;
}
ConnectionPackWriter packWriter;
BinarySerializer serializer(&packWriter);
serializer & &pack;
if (packWriter.buffer == predictedReplies.front()->writtenPacks.front().buffer)
predictedReplies.front()->writtenPacks.erase(predictedReplies.front()->writtenPacks.begin());
else
throw std::runtime_error("TODO: IMPLEMENT PACK ROLLBACK");
if (packageApplied)
{
assert(currentPackageID == packageApplied->requestID);
assert(!predictedReplies.empty());
assert(currentPackageID == predictedReplies.front()->requestID);
assert(predictedReplies.front()->writtenPacks.empty());
predictedReplies.erase(predictedReplies.begin());
currentPackageID = invalidPackageID;
}
return true;
}
void AntilagServer::setState(EServerState value)
{
// no-op
}
EServerState AntilagServer::getState() const
{
return EServerState::GAMEPLAY;
}
bool AntilagServer::isPlayerHost(const PlayerColor & color) const
{
return false; // TODO?
}
bool AntilagServer::hasPlayerAt(PlayerColor player, const std::shared_ptr<IGameConnection> & c) const
{
return true; // TODO?
}
bool AntilagServer::hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor right) const
{
return false; // TODO?
}
void AntilagServer::broadcastPack(CPackForClient & pack)
{
AntilagReplyPredictionVisitor visitor;
pack.visit(visitor);
predictedReplies.back()->sendPack(pack);
}

213
client/AntilagServer.h Normal file
View File

@@ -0,0 +1,213 @@
/*
* AntilagServer.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../lib/networkPacks/NetPackVisitor.h"
#include "../server/IGameServer.h"
#include "../lib/network/NetworkInterface.h"
#include "../lib/serializer/IGameConnection.h"
#include "../lib/serializer/CSerializer.h"
#include "../lib/serializer/BinarySerializer.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CPackForServer;
class IGameConnection;
VCMI_LIB_NAMESPACE_END
class CGameHandler;
class ConnectionPackWriter final : public IBinaryWriter
{
public:
std::vector<std::byte> buffer;
int write(const std::byte * data, unsigned size) final;
};
class AntilagFakeConnection final : public IGameConnection
{
public:
void sendPack(const CPack & pack) override;
std::unique_ptr<CPack> retrievePack(const std::vector<std::byte> & data) override;
int getConnectionID() const override;
PlayerColor senderID;
uint32_t requestID;
std::vector<ConnectionPackWriter> writtenPacks;
};
class AntilagReplyPredictionVisitor final : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor)
{
bool canBeAppliedValue = false;
//void visitSaveGame(SaveGame & pack) override;
//void visitGamePause(GamePause & pack) override;
//void visitEndTurn(EndTurn & pack) override;
//void visitDismissHero(DismissHero & pack) override;
void visitMoveHero(MoveHero & pack) override;
//void visitCastleTeleportHero(CastleTeleportHero & pack) override;
//void visitArrangeStacks(ArrangeStacks & pack) override;
//void visitBulkMoveArmy(BulkMoveArmy & pack) override;
//void visitBulkSplitStack(BulkSplitStack & pack) override;
//void visitBulkMergeStacks(BulkMergeStacks & pack) override;
//void visitBulkSplitAndRebalanceStack(BulkSplitAndRebalanceStack & pack) override;
//void visitDisbandCreature(DisbandCreature & pack) override;
//void visitBuildStructure(BuildStructure & pack) override;
//void visitSpellResearch(SpellResearch & pack) override;
//void visitVisitTownBuilding(VisitTownBuilding & pack) override;
//void visitRecruitCreatures(RecruitCreatures & pack) override;
//void visitUpgradeCreature(UpgradeCreature & pack) override;
//void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) override;
//void visitExchangeArtifacts(ExchangeArtifacts & pack) override;
//void visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack) override;
//void visitManageBackpackArtifacts(ManageBackpackArtifacts & pack) override;
//void visitManageEquippedArtifacts(ManageEquippedArtifacts & pack) override;
//void visitAssembleArtifacts(AssembleArtifacts & pack) override;
//void visitEraseArtifactByClient(EraseArtifactByClient & pack) override;
//void visitBuyArtifact(BuyArtifact & pack) override;
//void visitTradeOnMarketplace(TradeOnMarketplace & pack) override;
//void visitSetFormation(SetFormation & pack) override;
//void visitHireHero(HireHero & pack) override;
//void visitBuildBoat(BuildBoat & pack) override;
//void visitQueryReply(QueryReply & pack) override;
//void visitMakeAction(MakeAction & pack) override;
//void visitDigWithHero(DigWithHero & pack) override;
//void visitCastAdvSpell(CastAdvSpell & pack) override;
//void visitPlayerMessage(PlayerMessage & pack) override;
//void visitSaveLocalState(SaveLocalState & pack) override;
public:
AntilagReplyPredictionVisitor();
bool canBeApplied() const;
};
class AntilagRollbackGeneratorVisitor final : public ICPackVisitor
{
private:
const CGameState & gs;
std::vector<std::unique_ptr<CPackForClient>> rollbackPacks;
bool success = false;
//void visitSetResources(SetResources & pack) override;
//void visitSetPrimarySkill(SetPrimarySkill & pack) override;
//void visitSetHeroExperience(SetHeroExperience & pack) override;
//void visitGiveStackExperience(GiveStackExperience & pack) override;
//void visitSetSecSkill(SetSecSkill & pack) override;
//void visitHeroVisitCastle(HeroVisitCastle & pack) override;
//void visitSetMana(SetMana & pack) override;
//void visitSetMovePoints(SetMovePoints & pack) override;
//void visitSetResearchedSpells(SetResearchedSpells & pack) override;
//void visitFoWChange(FoWChange & pack) override;
//void visitChangeStackCount(ChangeStackCount & pack) override;
//void visitSetStackType(SetStackType & pack) override;
//void visitEraseStack(EraseStack & pack) override;
//void visitSwapStacks(SwapStacks & pack) override;
//void visitInsertNewStack(InsertNewStack & pack) override;
//void visitRebalanceStacks(RebalanceStacks & pack) override;
//void visitBulkRebalanceStacks(BulkRebalanceStacks & pack) override;
//void visitGrowUpArtifact(GrowUpArtifact & pack) override;
//void visitPutArtifact(PutArtifact & pack) override;
//void visitBulkEraseArtifacts(BulkEraseArtifacts & pack) override;
//void visitBulkMoveArtifacts(BulkMoveArtifacts & pack) override;
//void visitAssembledArtifact(AssembledArtifact & pack) override;
//void visitDisassembledArtifact(DisassembledArtifact & pack) override;
//void visitDischargeArtifact(DischargeArtifact & pack) override;
//void visitHeroVisit(HeroVisit & pack) override;
//void visitNewTurn(NewTurn & pack) override;
//void visitGiveBonus(GiveBonus & pack) override;
//void visitChangeObjPos(ChangeObjPos & pack) override;
//void visitPlayerEndsTurn(PlayerEndsTurn & pack) override;
//void visitPlayerEndsGame(PlayerEndsGame & pack) override;
//void visitPlayerReinitInterface(PlayerReinitInterface & pack) override;
//void visitRemoveBonus(RemoveBonus & pack) override;
//void visitRemoveObject(RemoveObject & pack) override;
void visitTryMoveHero(TryMoveHero & pack) override;
//void visitNewStructures(NewStructures & pack) override;
//void visitRazeStructures(RazeStructures & pack) override;
//void visitSetAvailableCreatures(SetAvailableCreatures & pack) override;
//void visitSetHeroesInTown(SetHeroesInTown & pack) override;
//void visitHeroRecruited(HeroRecruited & pack) override;
//void visitGiveHero(GiveHero & pack) override;
//void visitSetObjectProperty(SetObjectProperty & pack) override;
//void visitHeroLevelUp(HeroLevelUp & pack) override;
//void visitCommanderLevelUp(CommanderLevelUp & pack) override;
//void visitBattleStart(BattleStart & pack) override;
//void visitBattleSetActiveStack(BattleSetActiveStack & pack) override;
//void visitBattleTriggerEffect(BattleTriggerEffect & pack) override;
//void visitBattleAttack(BattleAttack & pack) override;
//void visitBattleSpellCast(BattleSpellCast & pack) override;
//void visitSetStackEffect(SetStackEffect & pack) override;
//void visitStacksInjured(StacksInjured & pack) override;
//void visitBattleUnitsChanged(BattleUnitsChanged & pack) override;
//void visitBattleObstaclesChanged(BattleObstaclesChanged & pack) override;
//void visitBattleStackMoved(BattleStackMoved & pack) override;
//void visitCatapultAttack(CatapultAttack & pack) override;
//void visitPlayerStartsTurn(PlayerStartsTurn & pack) override;
//void visitNewObject(NewObject & pack) override;
//void visitSetAvailableArtifacts(SetAvailableArtifacts & pack) override;
//void visitEntitiesChanged(EntitiesChanged & pack) override;
//void visitSetCommanderProperty(SetCommanderProperty & pack) override;
//void visitAddQuest(AddQuest & pack) override;
//void visitChangeFormation(ChangeFormation & pack) override;
//void visitChangeSpells(ChangeSpells & pack) override;
//void visitSetAvailableHero(SetAvailableHero & pack) override;
//void visitChangeObjectVisitors(ChangeObjectVisitors & pack) override;
//void visitChangeArtifactsCostume(ChangeArtifactsCostume & pack) override;
//void visitNewArtifact(NewArtifact & pack) override;
//void visitBattleUpdateGateState(BattleUpdateGateState & pack) override;
//void visitPlayerCheated(PlayerCheated & pack) override;
//void visitDaysWithoutTown(DaysWithoutTown & pack) override;
//void visitStartAction(StartAction & pack) override;
//void visitSetRewardableConfiguration(SetRewardableConfiguration & pack) override;
//void visitBattleSetStackProperty(BattleSetStackProperty & pack) override;
//void visitBattleNextRound(BattleNextRound & pack) override;
//void visitBattleCancelled(BattleCancelled & pack) override;
//void visitBattleResultsApplied(BattleResultsApplied & pack) override;
//void visitBattleResultAccepted(BattleResultAccepted & pack) override;
//void visitTurnTimeUpdate(TurnTimeUpdate & pack) override;
public:
AntilagRollbackGeneratorVisitor(const CGameState & gs)
: gs(gs)
{}
bool canBeRolledBack() const;
std::vector<std::unique_ptr<CPackForClient>> getRollbackPacks();
};
// Fake server that is used by client to make a quick prediction on what real server would reply without waiting for network latency
class AntilagServer final : public IGameServer, public INetworkConnectionListener, boost::noncopyable
{
std::vector<std::shared_ptr<AntilagFakeConnection>> predictedReplies;
std::shared_ptr<INetworkConnection> antilagNetConnection;
std::shared_ptr<IGameConnection> antilagGameConnection;
std::unique_ptr<CGameHandler> gameHandler;
static constexpr uint32_t invalidPackageID = std::numeric_limits<uint32_t>::max();
uint32_t currentPackageID = invalidPackageID;
// IGameServer impl
void setState(EServerState value) override;
EServerState getState() const override;
bool isPlayerHost(const PlayerColor & color) const override;
bool hasPlayerAt(PlayerColor player, const std::shared_ptr<IGameConnection> & c) const override;
bool hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor right) const override;
void broadcastPack(CPackForClient & pack) override;
void onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage) override;
void onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message) override;
public:
AntilagServer(INetworkHandler & network, const std::shared_ptr<CGameState> & gs);
~AntilagServer();
void tryPredictReply(const CPackForServer & request);
bool verifyReply(const CPackForClient & reply);
};

View File

@@ -184,6 +184,7 @@ set(vcmiclientcommon_SRCS
xBRZ/xbrz.cpp
AntilagServer.cpp
ArtifactsUIController.cpp
GameEngine.cpp
GameInstance.cpp
@@ -407,6 +408,7 @@ set(vcmiclientcommon_HEADERS
xBRZ/xbrz.h
xBRZ/xbrz_tools.h
AntilagServer.h
ArtifactsUIController.h
CMT.h
CPlayerInterface.h

View File

@@ -8,8 +8,9 @@
*
*/
#include "StdInc.h"
#include "CServerHandler.h"
#include "AntilagServer.h"
#include "Client.h"
#include "ServerRunner.h"
#include "GameChatHandler.h"
@@ -616,6 +617,9 @@ void CServerHandler::startGameplay(std::shared_ptr<CGameState> gameState)
if(GAME->mainmenu())
GAME->mainmenu()->disable();
// if (isGuest())
antilagServer = std::make_unique<AntilagServer>(getNetworkHandler(), gameState);
switch(si->mode)
{
case EStartMode::NEW_GAME:
@@ -940,10 +944,12 @@ void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
void CServerHandler::visitForClient(CPackForClient & clientPack)
{
if (antilagServer && antilagServer->verifyReply(clientPack))
return;
client->handlePack(clientPack);
}
void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
{
if(getState() != EClientState::STARTING)
@@ -959,3 +965,11 @@ bool CServerHandler::inGame() const
{
return logicConnection != nullptr;
}
void CServerHandler::sendGamePack(const CPackForServer & pack) const
{
if (antilagServer)
antilagServer->tryPredictReply(pack);
logicConnection->sendPack(pack);
}

View File

@@ -26,12 +26,14 @@ class CMapInfo;
class CGameState;
struct ClientPlayer;
struct CPackForLobby;
struct CPackForServer;
struct CPackForClient;
class HighScoreParameter;
VCMI_LIB_NAMESPACE_END
class AntilagServer;
class CClient;
class CBaseForLobbyApply;
class GlobalLobbyClient;
@@ -100,6 +102,7 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
std::unique_ptr<GlobalLobbyClient> lobbyClient;
std::unique_ptr<GameChatHandler> gameChat;
std::unique_ptr<IServerRunner> serverRunner;
std::unique_ptr<AntilagServer> antilagServer;
std::shared_ptr<CMapInfo> mapToStart;
std::vector<std::string> localPlayerNames;
@@ -214,4 +217,6 @@ public:
void visitForLobby(CPackForLobby & lobbyPack);
void visitForClient(CPackForClient & clientPack);
void sendGamePack(const CPackForServer & pack) const;
};

View File

@@ -366,7 +366,7 @@ int CClient::sendRequest(const CPackForServer & request, PlayerColor player, boo
waitingRequest.pushBack(requestID);
request.requestID = requestID;
request.player = player;
GAME->server().logicConnection->sendPack(request);
GAME->server().sendGamePack(request);
if(vstd::contains(playerint, player))
playerint[player]->requestSent(&request, requestID);

View File

@@ -126,6 +126,7 @@ class CClient : public Environment, public IClient
{
std::shared_ptr<CGameState> gamestate;
int requestCounter = 1;
public:
std::map<PlayerColor, std::shared_ptr<CGameInterface>> playerint;
std::map<PlayerColor, std::shared_ptr<CBattleGameInterface>> battleints;

View File

@@ -208,15 +208,15 @@ void NetworkConnection::close()
//NOTE: ignoring error code, intended
}
InternalConnection::InternalConnection(INetworkConnectionListener & listener, NetworkContext & context)
: io(context)
InternalConnection::InternalConnection(INetworkConnectionListener & listener, NetworkStrand & strand)
: strand(strand)
, listener(listener)
{
}
void InternalConnection::receivePacket(const std::vector<std::byte> & message)
{
boost::asio::post(io, [self = std::static_pointer_cast<InternalConnection>(shared_from_this()), message](){
strand.post([self = std::static_pointer_cast<InternalConnection>(shared_from_this()), message](){
if (self->connectionActive)
self->listener.onPacketReceived(self, message);
});
@@ -224,7 +224,7 @@ void InternalConnection::receivePacket(const std::vector<std::byte> & message)
void InternalConnection::disconnect()
{
boost::asio::post(io, [self = std::static_pointer_cast<InternalConnection>(shared_from_this())](){
strand.post([self = std::static_pointer_cast<InternalConnection>(shared_from_this())](){
self->listener.onDisconnected(self, "Internal connection has been terminated");
self->otherSideWeak.reset();
self->connectionActive = false;

View File

@@ -49,11 +49,11 @@ public:
class InternalConnection final : public IInternalConnection, public std::enable_shared_from_this<InternalConnection>
{
std::weak_ptr<IInternalConnection> otherSideWeak;
NetworkContext & io;
NetworkStrand & strand;
INetworkConnectionListener & listener;
bool connectionActive = false;
public:
InternalConnection(INetworkConnectionListener & listener, NetworkContext & context);
InternalConnection(INetworkConnectionListener & listener, NetworkStrand & strand);
void receivePacket(const std::vector<std::byte> & message) override;
void disconnect() override;

View File

@@ -20,6 +20,7 @@ using NetworkContext = boost::asio::io_context;
#else
using NetworkContext = boost::asio::io_service;
#endif
using NetworkStrand = NetworkContext::strand;
using NetworkSocket = boost::asio::ip::tcp::socket;
using NetworkAcceptor = boost::asio::ip::tcp::acceptor;
using NetworkBuffer = boost::asio::streambuf;

View File

@@ -22,11 +22,19 @@ std::unique_ptr<INetworkHandler> INetworkHandler::createHandler()
NetworkHandler::NetworkHandler()
: io(std::make_unique<NetworkContext>())
, strand(std::make_unique<NetworkStrand>(*io))
{}
std::unique_ptr<INetworkServer> NetworkHandler::createServerTCP(INetworkServerListener & listener)
{
return std::make_unique<NetworkServer>(listener, *io);
return std::make_unique<NetworkServer>(listener, *io, *strand);
}
std::shared_ptr<INetworkConnection> NetworkHandler::createAsyncConnection(INetworkConnectionListener & listener)
{
auto loopbackConnection = std::make_shared<InternalConnection>(listener, *strand);
loopbackConnection->connectTo(loopbackConnection);
return loopbackConnection;
}
void NetworkHandler::connectToRemote(INetworkClientListener & listener, const std::string & host, uint16_t port)
@@ -75,7 +83,7 @@ void NetworkHandler::createTimer(INetworkTimerListener & listener, std::chrono::
void NetworkHandler::createInternalConnection(INetworkClientListener & listener, INetworkServer & server)
{
auto localConnection = std::make_shared<InternalConnection>(listener, *io);
auto localConnection = std::make_shared<InternalConnection>(listener, *strand);
server.receiveInternalConnection(localConnection);

View File

@@ -13,9 +13,10 @@
VCMI_LIB_NAMESPACE_BEGIN
class NetworkHandler : public INetworkHandler
class NetworkHandler final : public INetworkHandler
{
std::unique_ptr<NetworkContext> io;
std::unique_ptr<NetworkStrand> strand;
public:
NetworkHandler();
@@ -23,6 +24,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;
std::shared_ptr<INetworkConnection> createAsyncConnection(INetworkConnectionListener & listener) override;
void createTimer(INetworkTimerListener & listener, std::chrono::milliseconds duration) override;
void run() override;

View File

@@ -108,6 +108,9 @@ public:
/// On success INetworkTimerListener::onConnectionEstablished() will be called asynchronously, established connection provided as parameter
virtual void createInternalConnection(INetworkClientListener & listener, INetworkServer & server) = 0;
/// Creates one-way connection that allows sending messages to listener in async form
virtual std::shared_ptr<INetworkConnection> createAsyncConnection(INetworkConnectionListener & listener) = 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

View File

@@ -13,8 +13,9 @@
VCMI_LIB_NAMESPACE_BEGIN
NetworkServer::NetworkServer(INetworkServerListener & listener, NetworkContext & context)
NetworkServer::NetworkServer(INetworkServerListener & listener, NetworkContext & context, NetworkStrand & strand)
: io(context)
, strand(strand)
, listener(listener)
{
}
@@ -59,7 +60,7 @@ 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);
auto localConnection = std::make_shared<InternalConnection>(*this, strand);
connections.insert(localConnection);

View File

@@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
class NetworkServer : public INetworkConnectionListener, public INetworkServer
{
NetworkContext & io;
NetworkStrand & strand;
std::shared_ptr<NetworkAcceptor> acceptor;
std::set<std::shared_ptr<INetworkConnection>> connections;
@@ -27,7 +28,7 @@ class NetworkServer : public INetworkConnectionListener, public INetworkServer
void onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage) override;
void onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message) override;
public:
NetworkServer(INetworkServerListener & listener, NetworkContext & context);
NetworkServer(INetworkServerListener & listener, NetworkContext & context, NetworkStrand & strand);
void receiveInternalConnection(std::shared_ptr<IInternalConnection> remoteConnection) override;

View File

@@ -19,7 +19,7 @@
VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE ConnectionPackWriter final : public IBinaryWriter
class ConnectionPackWriter final : public IBinaryWriter
{
public:
std::vector<std::byte> buffer;
@@ -27,7 +27,7 @@ public:
int write(const std::byte * data, unsigned size) final;
};
class DLL_LINKAGE ConnectionPackReader final : public IBinaryReader
class ConnectionPackReader final : public IBinaryReader
{
public:
const std::vector<std::byte> * buffer;

View File

@@ -50,7 +50,7 @@ public:
void sendPack(const CPack & pack) override;
int getConnectionID() const override;
std::unique_ptr<CPack> retrievePack(const std::vector<std::byte> & data);
std::unique_ptr<CPack> retrievePack(const std::vector<std::byte> & data) override;
void enterLobbyConnectionMode();
void setCallback(IGameInfoCallback & cb);

View File

@@ -17,6 +17,7 @@ class DLL_LINKAGE IGameConnection : boost::noncopyable
{
public:
virtual void sendPack(const CPack & pack) = 0;
virtual std::unique_ptr<CPack> retrievePack(const std::vector<std::byte> & data) = 0;
virtual int getConnectionID() const = 0;
};

View File

@@ -291,6 +291,7 @@ void registerTypes(Serializer &s)
s.template registerType<GiveStackExperience>(248);
s.template registerType<TimesStackSizeUpdater>(249);
s.template registerType<TimesArmySizeUpdater>(250);
s.template registerType<PackageReceived>(251);
}
VCMI_LIB_NAMESPACE_END

View File

@@ -488,6 +488,7 @@ void CGameHandler::handleReceivedPack(std::shared_ptr<IGameConnection> connectio
connection->sendPack(applied);
};
std::this_thread::sleep_for(std::chrono::milliseconds(100));
PackageReceived received(
pack.player,
pack.requestID,
@@ -1505,7 +1506,7 @@ void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)
}
}
void CGameHandler::sendToAllClients(const CPackForClient & pack)
void CGameHandler::sendToAllClients(CPackForClient & pack)
{
logNetwork->trace("\tSending to all clients: %s", typeid(pack).name());
gameServer().broadcastPack(pack);

View File

@@ -276,7 +276,7 @@ public:
#endif
}
void sendToAllClients(const CPackForClient & pack);
void sendToAllClients(CPackForClient & pack);
void sendAndApply(CPackForClient & pack) override;
void sendAndApply(CGarrisonOperationPack & pack);
void sendAndApply(SetResources & pack);

View File

@@ -1158,7 +1158,7 @@ bool CVCMIServer::hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor r
return false;
}
void CVCMIServer::broadcastPack(const CPackForClient & pack)
void CVCMIServer::broadcastPack(CPackForClient & pack)
{
for (const auto & c : activeConnections)
c->sendPack(pack);

View File

@@ -64,7 +64,7 @@ public:
bool isPlayerHost(const PlayerColor & color) const override;
bool hasPlayerAt(PlayerColor player, const std::shared_ptr<IGameConnection> & c) const override;
bool hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor right) const override;
void broadcastPack(const CPackForClient & pack) override;
void broadcastPack(CPackForClient & pack) override;
/// List of all active connections
std::vector<std::shared_ptr<GameConnection>> activeConnections;

View File

@@ -33,5 +33,5 @@ public:
virtual bool isPlayerHost(const PlayerColor & color) const = 0;
virtual bool hasPlayerAt(PlayerColor player, const std::shared_ptr<IGameConnection> & c) const = 0;
virtual bool hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor right) const = 0;
virtual void broadcastPack(const CPackForClient & pack) = 0;
virtual void broadcastPack(CPackForClient & pack) = 0;
};