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:
213
client/AntilagServer.cpp
Normal file
213
client/AntilagServer.cpp
Normal 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
213
client/AntilagServer.h
Normal 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);
|
||||
};
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
};
|
||||
|
Reference in New Issue
Block a user