2
0

AntilagServer.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * AntilagServer.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "AntilagServer.h"
  12. #include "CServerHandler.h"
  13. #include "Client.h"
  14. #include "GameEngine.h"
  15. #include "../server/CGameHandler.h"
  16. #include "../lib/gameState/CGameState.h"
  17. #include "../lib/mapObjects/CGHeroInstance.h"
  18. #include "../lib/serializer/GameConnection.h"
  19. #include "GameInstance.h"
  20. class AntilagRollbackNotSupportedException : public std::runtime_error
  21. {
  22. public:
  23. using std::runtime_error::runtime_error;
  24. };
  25. int ConnectionPackWriter::write(const std::byte * data, unsigned size)
  26. {
  27. buffer.insert(buffer.end(), data, data + size);
  28. return size;
  29. }
  30. void AntilagRollbackGeneratorVisitor::visitPackageReceived(PackageReceived & pack)
  31. {
  32. success = true;
  33. // no-op rollback?
  34. }
  35. void AntilagRollbackGeneratorVisitor::visitPackageApplied(PackageApplied & pack)
  36. {
  37. success = true;
  38. // no-op rollback?
  39. }
  40. void AntilagRollbackGeneratorVisitor::visitPlayerBlocked(PlayerBlocked & pack)
  41. {
  42. success = true;
  43. // no-op rollback?
  44. }
  45. void AntilagRollbackGeneratorVisitor::visitSwapStacks(SwapStacks & pack)
  46. {
  47. auto rollbackSwap = std::make_unique<SwapStacks>();
  48. rollbackSwap->srcArmy = pack.dstArmy;
  49. rollbackSwap->dstArmy = pack.srcArmy;
  50. rollbackSwap->srcSlot = pack.dstSlot;
  51. rollbackSwap->dstSlot = pack.srcSlot;
  52. rollbackPacks.push_back(std::move(rollbackSwap));
  53. success = true;
  54. }
  55. void AntilagRollbackGeneratorVisitor::visitRebalanceStacks(RebalanceStacks & pack)
  56. {
  57. const auto * srcObject = gs.getObjInstance(pack.srcArmy);
  58. const auto * dstObject = gs.getObjInstance(pack.dstArmy);
  59. const auto * srcArmy = dynamic_cast<const CArmedInstance*>(srcObject);
  60. const auto * dstArmy = dynamic_cast<const CArmedInstance*>(dstObject);
  61. if (srcArmy->getStack(pack.srcSlot).getTotalExperience() != 0 ||
  62. dstArmy->getStack(pack.srcSlot).getTotalExperience() != 0 ||
  63. srcArmy->getStack(pack.srcSlot).getSlot(ArtifactPosition::CREATURE_SLOT)->artifactID.hasValue())
  64. {
  65. // TODO: rollback creature artifacts & stack experience
  66. return;
  67. }
  68. auto rollbackRebalance = std::make_unique<RebalanceStacks>();
  69. rollbackRebalance->srcArmy = pack.dstArmy;
  70. rollbackRebalance->dstArmy = pack.srcArmy;
  71. rollbackRebalance->srcSlot = pack.dstSlot;
  72. rollbackRebalance->dstSlot = pack.srcSlot;
  73. rollbackRebalance->count = pack.count;
  74. rollbackPacks.push_back(std::move(rollbackRebalance));
  75. success = true;
  76. }
  77. void AntilagRollbackGeneratorVisitor::visitBulkRebalanceStacks(BulkRebalanceStacks & pack)
  78. {
  79. for (auto & subpack : pack.moves)
  80. visitRebalanceStacks(subpack);
  81. success = true;
  82. }
  83. void AntilagRollbackGeneratorVisitor::visitHeroVisitCastle(HeroVisitCastle & pack)
  84. {
  85. auto rollbackVisit = std::make_unique<HeroVisitCastle>();
  86. rollbackVisit->startVisit = !pack.startVisit;
  87. rollbackVisit->tid = pack.tid;
  88. rollbackVisit->hid = pack.hid;
  89. rollbackPacks.push_back(std::move(rollbackVisit));
  90. success = true;
  91. }
  92. void AntilagRollbackGeneratorVisitor::visitTryMoveHero(TryMoveHero & pack)
  93. {
  94. auto rollbackMove = std::make_unique<TryMoveHero>();
  95. auto rollbackFow = std::make_unique<FoWChange>();
  96. const auto * movedHero = gs.getHero(pack.id);
  97. rollbackMove->id = pack.id;
  98. rollbackMove->movePoints = movedHero->movementPointsRemaining();
  99. rollbackMove->result = pack.result;
  100. if (pack.result == TryMoveHero::EMBARK)
  101. rollbackMove->result = TryMoveHero::DISEMBARK;
  102. if (pack.result == TryMoveHero::DISEMBARK)
  103. rollbackMove->result = TryMoveHero::EMBARK;
  104. rollbackMove->start = pack.end;
  105. rollbackMove->end = pack.start;
  106. rollbackFow->mode = ETileVisibility::HIDDEN;
  107. rollbackFow->player = movedHero->getOwner();
  108. rollbackFow->tiles = pack.fowRevealed;
  109. rollbackPacks.push_back(std::move(rollbackMove));
  110. rollbackPacks.push_back(std::move(rollbackFow));
  111. success = true;
  112. }
  113. bool AntilagRollbackGeneratorVisitor::canBeRolledBack() const
  114. {
  115. return success;
  116. }
  117. std::vector<std::unique_ptr<CPackForClient>> AntilagRollbackGeneratorVisitor::getRollbackPacks()
  118. {
  119. return std::move(rollbackPacks);
  120. }
  121. AntilagReplyPredictionVisitor::AntilagReplyPredictionVisitor() = default;
  122. void AntilagReplyPredictionVisitor::visitMoveHero(MoveHero & pack)
  123. {
  124. canBeAppliedValue = true;
  125. }
  126. void AntilagReplyPredictionVisitor::visitArrangeStacks(ArrangeStacks & pack)
  127. {
  128. canBeAppliedValue = true;
  129. }
  130. bool AntilagReplyPredictionVisitor::canBeApplied() const
  131. {
  132. return canBeAppliedValue;
  133. }
  134. AntilagServer::AntilagServer(INetworkHandler & network, const std::shared_ptr<CGameState> & gs)
  135. : gameState(gs)
  136. {
  137. antilagNetConnection = network.createAsyncConnection(*this);
  138. antilagGameConnection = std::make_shared<GameConnection>(antilagNetConnection);
  139. }
  140. AntilagServer::~AntilagServer() = default;
  141. void AntilagServer::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
  142. {
  143. // should never be called
  144. throw std::runtime_error("AntilagServer::onDisconnected called!");
  145. }
  146. void AntilagServer::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<std::byte> & message)
  147. {
  148. std::scoped_lock interfaceLock(ENGINE->interfaceMutex);
  149. auto basePack = antilagGameConnection->retrievePack(message);
  150. auto * serverPack = dynamic_cast<CPackForServer*>(basePack.get());
  151. AntilagReplyPredictionVisitor packVisitor;
  152. serverPack->visit(packVisitor);
  153. if (!packVisitor.canBeApplied())
  154. return;
  155. logGlobal->info("Predicting effects of pack '%s'", typeid(*serverPack).name());
  156. AntilagReplyPrediction newPrediction;
  157. newPrediction.requestID = serverPack->requestID;
  158. newPrediction.senderID = serverPack->player;
  159. predictedReplies.push_back(std::move(newPrediction));
  160. try
  161. {
  162. CGameHandler gameHandler(*this, gameState);
  163. gameHandler.handleReceivedPack(GameConnectionID::FIRST_CONNECTION, *serverPack);
  164. }
  165. catch (const AntilagRollbackNotSupportedException & )
  166. {
  167. return;
  168. }
  169. }
  170. void AntilagServer::tryPredictReply(const CPackForServer & request)
  171. {
  172. antilagGameConnection->sendPack(request);
  173. logGlobal->info("Scheduled prediction of effects of pack '%s'", typeid(request).name());
  174. }
  175. bool AntilagServer::verifyReply(const CPackForClient & pack)
  176. {
  177. logGlobal->info("Verifying reply: received pack '%s'", typeid(pack).name());
  178. const auto * packageReceived = dynamic_cast<const PackageReceived*>(&pack);
  179. const auto * packageApplied = dynamic_cast<const PackageApplied*>(&pack);
  180. if (packageReceived)
  181. {
  182. assert(currentPackageID == invalidPackageID);
  183. if (!predictedReplies.empty() && predictedReplies.front().requestID == packageReceived->requestID)
  184. {
  185. [[maybe_unused]] const auto & nextPrediction = predictedReplies.front();
  186. assert(nextPrediction.senderID == packageReceived->player);
  187. assert(nextPrediction.requestID == packageReceived->requestID);
  188. currentPackageID = packageReceived->requestID;
  189. }
  190. }
  191. if (currentPackageID == invalidPackageID)
  192. {
  193. // this is system package or reply to actions of another player
  194. // TODO: consider reapplying all our predictions, in case if this event invalidated our prediction
  195. return false;
  196. }
  197. ConnectionPackWriter packWriter;
  198. BinarySerializer serializer(&packWriter);
  199. serializer & &pack;
  200. if (packWriter.buffer == predictedReplies.front().writtenPacks.front().buffer)
  201. {
  202. // Our prediction was sucessful - drop rollback information for this pack
  203. predictedReplies.front().writtenPacks.erase(predictedReplies.front().writtenPacks.begin());
  204. if (predictedReplies.front().writtenPacks.empty())
  205. {
  206. predictedReplies.erase(predictedReplies.begin());
  207. currentPackageID = invalidPackageID;
  208. return true;
  209. }
  210. }
  211. else
  212. {
  213. // Prediction was incorrect - rollback everything that is left in this prediction and use real server packs
  214. for (auto & prediction : boost::adaptors::reverse(predictedReplies.front().writtenPacks))
  215. {
  216. for (auto & pack : prediction.rollbackPacks)
  217. GAME->server().client->handlePack(*pack);
  218. }
  219. predictedReplies.erase(predictedReplies.begin());
  220. currentPackageID = invalidPackageID;
  221. return false;
  222. }
  223. if (packageApplied)
  224. {
  225. assert(currentPackageID == packageApplied->requestID);
  226. assert(!predictedReplies.empty());
  227. assert(currentPackageID == predictedReplies.front().requestID);
  228. assert(predictedReplies.front().writtenPacks.empty());
  229. predictedReplies.erase(predictedReplies.begin());
  230. currentPackageID = invalidPackageID;
  231. }
  232. return true;
  233. }
  234. void AntilagServer::setState(EServerState value)
  235. {
  236. // no-op
  237. }
  238. EServerState AntilagServer::getState() const
  239. {
  240. return EServerState::GAMEPLAY;
  241. }
  242. bool AntilagServer::isPlayerHost(const PlayerColor & color) const
  243. {
  244. return false; // TODO?
  245. }
  246. bool AntilagServer::hasPlayerAt(PlayerColor player, GameConnectionID c) const
  247. {
  248. return true; // TODO?
  249. }
  250. bool AntilagServer::hasBothPlayersAtSameConnection(PlayerColor left, PlayerColor right) const
  251. {
  252. return false; // TODO?
  253. }
  254. void AntilagServer::applyPack(CPackForClient & pack)
  255. {
  256. AntilagRollbackGeneratorVisitor visitor(*gameState);
  257. pack.visit(visitor);
  258. if (!visitor.canBeRolledBack())
  259. {
  260. logGlobal->info("Prediction not possible: pack '%s'", typeid(pack).name());
  261. throw AntilagRollbackNotSupportedException(std::string("Prediction not possible ") + typeid(pack).name());
  262. }
  263. logGlobal->info("Prediction: pack '%s'", typeid(pack).name());
  264. ConnectionPackWriter packWriter;
  265. BinarySerializer serializer(&packWriter);
  266. serializer & &pack;
  267. packWriter.rollbackPacks = visitor.getRollbackPacks();
  268. predictedReplies.back().writtenPacks.push_back(std::move(packWriter));
  269. GAME->server().client->handlePack(pack);
  270. }
  271. void AntilagServer::sendPack(CPackForClient & pack, GameConnectionID connectionID)
  272. {
  273. applyPack(pack);
  274. }