diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 04f60c3ff..6dfb0a3f0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -44,8 +44,8 @@ jobs: # Use (compiler) default C++ 14 standard # Use system cmake # Use system boost (min version) - - { compiler: gcc-9, os: ubuntu-20.04, type: Debug, cmake: 3.16.3, boost: 1.69.0 } - - { compiler: clang-10, os: ubuntu-20.04, type: Debug, cmake: 3.16.3, boost: 1.69.0, externalSanitizer: true } + - { compiler: gcc-9, os: ubuntu-20.04, type: Debug, cmake: 3.16.3, boost: 1.71.0 } + - { compiler: clang-10, os: ubuntu-20.04, type: Debug, cmake: 3.16.3, boost: 1.71.0, externalSanitizer: true } # # Default compiler for Ubuntu 20.04 # Use (compiler) default C++ 14 standard diff --git a/CMakeLists.txt b/CMakeLists.txt index 4583f4889..71e556bba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2005 - 2021 Settlers Freaks +# Copyright (C) 2005 - 2024 Settlers Freaks # # SPDX-License-Identifier: GPL-2.0-or-later @@ -306,21 +306,13 @@ if(BUILD_TESTING) list(APPEND BoostPackages unit_test_framework) endif() -find_package(Boost 1.69 COMPONENTS ${BoostPackages}) +find_package(Boost 1.71 COMPONENTS ${BoostPackages}) if(NOT Boost_FOUND) - message(FATAL_ERROR "You have to install boost (>=1.69) into contrib/boost or set (as CMake or environment variable) " + message(FATAL_ERROR "You have to install boost (>=1.71) into contrib/boost or set (as CMake or environment variable) " "BOOST_ROOT (currently: '${BOOST_ROOT}', Environment: '$ENV{BOOST_ROOT}'), " "BOOST_INCLUDEDIR (currently: '${BOOST_INCLUDEDIR}', Environment: '$ENV{BOOST_INCLUDEDIR}') " "since cmake was unable to find boost!") endif() -if(Boost_VERSION VERSION_EQUAL 1.70) - # Bug in Boost 1.70: https://github.com/boostorg/boost_install/issues/5 - if(Boost_USE_STATIC_LIBS) - set(BUILD_SHARED_LIBS OFF) - else() - set(BUILD_SHARED_LIBS ON) - endif() -endif() option(RTTR_USE_SYSTEM_BOOST_NOWIDE "Use system installed Boost.Nowide. Fails if not found!" "${RTTR_USE_SYSTEM_LIBS}") diff --git a/appveyor.yml b/appveyor.yml index 1985b2c47..1a1f730d5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,8 +9,8 @@ branches: - master image: - - Visual Studio 2017 - Visual Studio 2019 + - Visual Studio 2022 configuration: - Debug @@ -35,10 +35,16 @@ matrix: for: - matrix: only: - - image: Visual Studio 2017 + - image: Visual Studio 2019 environment: - BOOST_ROOT: C:\Libraries\boost_1_69_0 - GENERATOR: Visual Studio 15 2017 + GENERATOR: Visual Studio 16 2019 + BOOST_ROOT: C:\Libraries\boost_1_83_0 + - matrix: + only: + - image: Visual Studio 2022 + environment: + GENERATOR: Visual Studio 17 2022 + BOOST_ROOT: C:\Libraries\boost_1_84_0 install: - dir C:\Libraries @@ -50,7 +56,6 @@ before_build: - if exist %INSTALL_DIR%\ (rmdir /S /Q %INSTALL_DIR%) - mkdir build - cd build - - if %platform% == x64 (if "%GENERATOR%" == "Visual Studio 2017" (set "GENERATOR=%GENERATOR% Win64")) # Enable LTCG for release builds (speeds up linking as /GL compiled modules are used) - if %configuration% == Release (set "cmakeFlags=-DCMAKE_EXE_LINKER_FLAGS=/LTCG -DCMAKE_SHARED_LINKER_FLAGS=/LTCG") - echo "Configuring %GENERATOR% for %configuration% on %platform% with boost=%BOOST_ROOT%" diff --git a/external/libutil b/external/libutil index 98d6ab5b2..a40c2337e 160000 --- a/external/libutil +++ b/external/libutil @@ -1 +1 @@ -Subproject commit 98d6ab5b21bf7f35a2d5ee76d6ea43c7d416f8a1 +Subproject commit a40c2337e87bec26ae495d7186e002d6cb0bb8bf diff --git a/external/s25edit b/external/s25edit index 4ca9689a6..e39cf96fe 160000 --- a/external/s25edit +++ b/external/s25edit @@ -1 +1 @@ -Subproject commit 4ca9689a6700b4a75eeb6e5bbd509b0eaeec4522 +Subproject commit e39cf96fe9da374fde88501c209aba39c4df83e5 diff --git a/external/s25update b/external/s25update index 343dc0c46..bbf970bf9 160000 --- a/external/s25update +++ b/external/s25update @@ -1 +1 @@ -Subproject commit 343dc0c46439d2cafa206bee2ddf3d87a73162a2 +Subproject commit bbf970bf93cc02df5a207e167ccd1750add4086d diff --git a/extras/ai-battle/HeadlessGame.cpp b/extras/ai-battle/HeadlessGame.cpp index d8cf7f95c..e4aedbdbd 100644 --- a/extras/ai-battle/HeadlessGame.cpp +++ b/extras/ai-battle/HeadlessGame.cpp @@ -138,11 +138,10 @@ void HeadlessGame::RecordReplay(const bfs::path& path, unsigned random_init) mapInfo.mapData.CompressFromFile(mapInfo.filepath, &mapInfo.mapChecksum); mapInfo.type = MapType::OldMap; - replay_.random_init = random_init; for(unsigned playerId = 0; playerId < world_.GetNumPlayers(); ++playerId) replay_.AddPlayer(world_.GetPlayer(playerId)); replay_.ggs = game_.ggs_; - if(!replay_.StartRecording(path, mapInfo)) + if(!replay_.StartRecording(path, mapInfo, random_init)) throw std::runtime_error("Replayfile could not be opened!"); } diff --git a/extras/videoDrivers/SDL2/VideoSDL2.cpp b/extras/videoDrivers/SDL2/VideoSDL2.cpp index 28b535d6a..4fa0b49aa 100644 --- a/extras/videoDrivers/SDL2/VideoSDL2.cpp +++ b/extras/videoDrivers/SDL2/VideoSDL2.cpp @@ -19,6 +19,10 @@ #ifdef _WIN32 # include +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include // Avoid "Windows headers require the default packing option" due to SDL2 # include #endif // _WIN32 diff --git a/libs/common/include/helpers/OptionalIO.h b/libs/common/include/helpers/OptionalIO.h index 95397a325..f33651121 100644 --- a/libs/common/include/helpers/OptionalIO.h +++ b/libs/common/include/helpers/OptionalIO.h @@ -5,6 +5,7 @@ #pragma once #include "helpers/OptionalEnum.h" +#include #include namespace helpers { @@ -14,3 +15,13 @@ std::ostream& operator<<(std::ostream& os, OptionalEnum const& v) return (v) ? os << *v : os << "[empty]"; } } // namespace helpers + +namespace std { +// LCOV_EXCL_START +template +static std::ostream& boost_test_print_type(std::ostream& os, std::optional const& v) +{ + return (v) ? os << *v : os << "[empty]"; +} +// LCOV_EXCL_STOP +} // namespace std diff --git a/libs/common/include/variant.h b/libs/common/include/variant.h index 9b9009415..96f186c17 100644 --- a/libs/common/include/variant.h +++ b/libs/common/include/variant.h @@ -1,22 +1,20 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include +#include -namespace detail { -template -struct indexOf; -template -struct indexOf : std::integral_constant -{}; -template -struct indexOf : std::integral_constant::value> -{}; +/// Shortcuts to avoid typing out boost::variant2 +template +using boost_variant2 = boost::variant2::variant; + +using boost::variant2::get; +using boost::variant2::get_if; +using boost::variant2::holds_alternative; +namespace detail { template struct lambda_visitor; @@ -36,12 +34,6 @@ struct lambda_visitor : public Lambda1 }; } // namespace detail -template -bool holds_alternative(const boost::variant& v) noexcept -{ - return v.which() == detail::indexOf::value; -} - template auto composeVisitor(Fs&&... fs) { diff --git a/libs/s25main/CMakeLists.txt b/libs/s25main/CMakeLists.txt index 6a136b4ec..273b85bbe 100644 --- a/libs/s25main/CMakeLists.txt +++ b/libs/s25main/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2005 - 2021 Settlers Freaks +# Copyright (C) 2005 - 2024 Settlers Freaks # # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/libs/s25main/Debug.cpp b/libs/s25main/Debug.cpp index 0c7a02058..9d1cfa745 100644 --- a/libs/s25main/Debug.cpp +++ b/libs/s25main/Debug.cpp @@ -296,7 +296,7 @@ bool DebugInfo::SendReplay() rpl->Close(); BinaryFile f; - if(f.Open(replayPath, OpenFileMode::OFM_READ)) + if(f.Open(replayPath, OpenFileMode::Read)) { if(!SendString("Replay")) return false; @@ -317,7 +317,7 @@ bool DebugInfo::SendReplay() bool DebugInfo::SendAsyncLog(const boost::filesystem::path& asyncLogFilepath) { BinaryFile file; - if(!file.Open(asyncLogFilepath, OFM_READ)) + if(!file.Open(asyncLogFilepath, OpenFileMode::Read)) return false; if(!SendString("AsyncLog")) diff --git a/libs/s25main/GameCommand.cpp b/libs/s25main/GameCommand.cpp index 490cc470c..468d34697 100644 --- a/libs/s25main/GameCommand.cpp +++ b/libs/s25main/GameCommand.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,7 +9,13 @@ namespace gc { -GameCommandPtr GameCommand::Deserialize(Serializer& ser) +/// Current version of the game commands, see VersionedDeserializer. +unsigned Deserializer::getCurrentVersion() +{ + return 0; +} + +GameCommandPtr GameCommand::Deserialize(Deserializer& ser) { auto gcType = helpers::popEnum(ser); GameCommand* gc; diff --git a/libs/s25main/GameCommand.h b/libs/s25main/GameCommand.h index 28a66c76a..b9afbc90a 100644 --- a/libs/s25main/GameCommand.h +++ b/libs/s25main/GameCommand.h @@ -1,9 +1,10 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "s25util/VersionedDeserializer.h" #include #include #include @@ -14,6 +15,12 @@ class GameCommandFactory; namespace gc { +struct Deserializer : s25util::VersionedDeserializer +{ + using s25util::VersionedDeserializer::VersionedDeserializer; + static unsigned getCurrentVersion(); +}; + class GameCommand; // Use this for safely using Pointers to GameCommands using GameCommandPtr = boost::intrusive_ptr; @@ -82,7 +89,7 @@ class GameCommand } /// Builds a GameCommand depending on Type - static GameCommandPtr Deserialize(Serializer& ser); + static GameCommandPtr Deserialize(Deserializer& ser); /// Serializes this GameCommand virtual void Serialize(Serializer& ser) const; diff --git a/libs/s25main/GameCommands.h b/libs/s25main/GameCommands.h index c46bd2ee3..e91fd63bf 100644 --- a/libs/s25main/GameCommands.h +++ b/libs/s25main/GameCommands.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -441,11 +441,11 @@ class NotifyAlliesOfLocation : public Coords class SetInventorySetting : public Coords { GC_FRIEND_DECL; - boost::variant what; + boost_variant2 what; InventorySetting state; protected: - SetInventorySetting(const MapPoint pt, boost::variant what, const InventorySetting state) + SetInventorySetting(const MapPoint pt, boost_variant2 what, const InventorySetting state) : Coords(GCType::SetInventorySetting, pt), what(std::move(what)), state(state) {} SetInventorySetting(Serializer& ser) : Coords(GCType::SetInventorySetting, ser) @@ -464,7 +464,7 @@ class SetInventorySetting : public Coords Coords::Serialize(ser); ser.PushBool(holds_alternative(what)); - boost::apply_visitor([&ser](auto type) { helpers::pushEnum(ser, type); }, what); + visit([&ser](auto type) { helpers::pushEnum(ser, type); }, what); ser.PushUnsignedChar(static_cast(state)); } @@ -775,13 +775,13 @@ class ExpeditionCommand : public GameCommand class TradeOverLand : public Coords { GC_FRIEND_DECL; - boost::variant what; + boost_variant2 what; /// Number of wares/figures we want to trade uint32_t count; protected: /// Note: Can only trade wares or figures! - TradeOverLand(const MapPoint pt, boost::variant what, const uint32_t count) + TradeOverLand(const MapPoint pt, boost_variant2 what, const uint32_t count) : Coords(GCType::Trade, pt), what(std::move(what)), count(count) {} TradeOverLand(Serializer& ser) : Coords(GCType::Trade, ser) @@ -799,7 +799,7 @@ class TradeOverLand : public Coords Coords::Serialize(ser); ser.PushBool(holds_alternative(what)); - boost::apply_visitor([&ser](auto type) { helpers::pushEnum(ser, type); }, what); + visit([&ser](auto type) { helpers::pushEnum(ser, type); }, what); ser.PushUnsignedInt(count); } diff --git a/libs/s25main/GamePlayer.cpp b/libs/s25main/GamePlayer.cpp index 50b08acde..65fa08f29 100644 --- a/libs/s25main/GamePlayer.cpp +++ b/libs/s25main/GamePlayer.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,7 +26,6 @@ #include "postSystem/DiplomacyPostQuestion.h" #include "postSystem/PostManager.h" #include "random/Random.h" -#include "variant.h" #include "world/GameWorld.h" #include "world/TradeRoute.h" #include "nodeObjs/noFlag.h" @@ -2170,7 +2169,7 @@ struct WarehouseDistanceComparator }; /// Send wares to warehouse wh -void GamePlayer::Trade(nobBaseWarehouse* goalWh, const boost::variant& what, unsigned count) const +void GamePlayer::Trade(nobBaseWarehouse* goalWh, const boost_variant2& what, unsigned count) const { if(!world.GetGGS().isEnabled(AddonId::TRADE)) return; @@ -2195,9 +2194,9 @@ void GamePlayer::Trade(nobBaseWarehouse* goalWh, const boost::variantGetAvailableWaresForTrading(gt); }, - [wh](Job job) { return wh->GetAvailableFiguresForTrading(job); }), - what); + boost::variant2::visit(composeVisitor([wh](GoodType gt) { return wh->GetAvailableWaresForTrading(gt); }, + [wh](Job job) { return wh->GetAvailableFiguresForTrading(job); }), + what); if(available == 0) continue; diff --git a/libs/s25main/GamePlayer.h b/libs/s25main/GamePlayer.h index 3f9dccf57..59ee84347 100644 --- a/libs/s25main/GamePlayer.h +++ b/libs/s25main/GamePlayer.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,6 +8,7 @@ #include "GamePlayerInfo.h" #include "helpers/EnumArray.h" #include "helpers/MultiArray.h" +#include "variant.h" #include "gameTypes/BuildingType.h" #include "gameTypes/Inventory.h" #include "gameTypes/MapCoordinates.h" @@ -15,7 +16,6 @@ #include "gameTypes/SettingsTypes.h" #include "gameTypes/StatisticTypes.h" #include "gameData/MaxPlayers.h" -#include #include #include #include @@ -281,7 +281,7 @@ class GamePlayer : public GamePlayerInfo /// IMPORTANT: Warehouses can be destroyed. So check them first before using! std::vector GetWarehousesForTrading(const nobBaseWarehouse& goalWh) const; /// Send wares to warehouse wh - void Trade(nobBaseWarehouse* goalWh, const boost::variant& what, unsigned count) const; + void Trade(nobBaseWarehouse* goalWh, const boost_variant2& what, unsigned count) const; void EnableBuilding(BuildingType type) { building_enabled[type] = true; } void DisableBuilding(BuildingType type) { building_enabled[type] = false; } diff --git a/libs/s25main/Replay.cpp b/libs/s25main/Replay.cpp index 2442bcc7e..17db28f78 100644 --- a/libs/s25main/Replay.cpp +++ b/libs/s25main/Replay.cpp @@ -1,9 +1,10 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #include "Replay.h" #include "Savegame.h" +#include "enum_cast.hpp" #include "network/PlayerGameCommands.h" #include "gameTypes/MapInfo.h" #include @@ -16,17 +17,33 @@ std::string Replay::GetSignature() const return "RTTRRP2"; } +// clang-format off +/// (Sub)-Version of the current replay file +/// Usage: +//// - Always save for the most current version +//// - Loading code may cope with file format changes +/// If the format changes (e.g. new enum values, types, ... increase this version and handle it in the loading code. +/// If the change cannot be handled: +/// - Remove all code handling this version. +/// - Reset this version to 0 +/// - Increase the version in GetVersion +/// +/// Changelog: +/// 1: Unused first CommandType (End) removed, GameCommand version added +static const uint8_t currentReplayDataVersion = 1; +// clang-format on + +/// Format version of replay files uint16_t Replay::GetVersion() const { - /// Version des Replay-Formates - /// Search for "TODO(Replay)" when increasing this (breaking Replay compatibility) + // Search for "TODO(Replay)" when increasing this (breaking Replay compatibility) + // and handle/remove the relevant code return 8; } ////////////////////////////////////////////////////////////////////////// -Replay::Replay() : random_init(0), isRecording_(false), lastGF_(0), lastGfFilePos_(0), mapType_(MapType::OldMap) {} - +Replay::Replay() = default; Replay::~Replay() = default; void Replay::Close() @@ -47,7 +64,7 @@ bool Replay::StopRecording() file_.Close(); BinaryFile file; - if(!file.Open(filepath_, OpenFileMode::OFM_READ)) + if(!file.Open(filepath_, OpenFileMode::Read)) return false; try { @@ -55,7 +72,7 @@ bool Replay::StopRecording() file.Seek(0, SEEK_SET); tmpReplayFile.close(); BinaryFile compressedReplay; - compressedReplay.Open(tmpReplayFile.filePath, OpenFileMode::OFM_WRITE); + compressedReplay.Open(tmpReplayFile.filePath, OpenFileMode::Write); // Copy header data uncompressed std::vector data(lastGfFilePos_); @@ -92,30 +109,33 @@ bool Replay::StopRecording() } } -bool Replay::StartRecording(const boost::filesystem::path& filepath, const MapInfo& mapInfo) +bool Replay::StartRecording(const boost::filesystem::path& filepath, const MapInfo& mapInfo, const unsigned randomSeed) { // Deny overwrite, also avoids double-opening by different processes if(boost::filesystem::exists(filepath)) return false; - // Datei öffnen - if(!file_.Open(filepath, OFM_WRITE)) + if(!file_.Open(filepath, OpenFileMode::Write)) return false; filepath_ = filepath; isRecording_ = true; - /// End-GF (erstmal nur 0, wird dann im Spiel immer geupdatet) + /// End-GF (will be updated during the game) lastGF_ = 0; mapType_ = mapInfo.type; + randomSeed_ = randomSeed; - // Write header WriteAllHeaderData(file_, mapInfo.title); + file_.WriteUnsignedChar(rttr::enum_cast(mapType_)); + // TODO(Replay): Move before mapType + file_.WriteUnsignedChar(subVersion_ = currentReplayDataVersion); + RTTR_Assert(gc::Deserializer::getCurrentVersion() <= std::numeric_limits::max()); + file_.WriteUnsignedChar(gcVersion_ = gc::Deserializer::getCurrentVersion()); - file_.WriteUnsignedShort(static_cast(mapType_)); - // For validation purposes + // For (savegame) format validation if(mapType_ == MapType::Savegame) mapInfo.savegame->WriteFileHeader(file_); - // Position merken für End-GF + // store position to update it later lastGfFilePos_ = file_.Tell(); file_.WriteUnsignedInt(lastGF_); file_.WriteUnsignedChar(0); // Compressed flag @@ -124,7 +144,7 @@ bool Replay::StartRecording(const boost::filesystem::path& filepath, const MapIn WriteGGS(file_); // Game data - file_.WriteUnsignedInt(random_init); + file_.WriteUnsignedInt(randomSeed_); file_.WriteLongString(mapInfo.filepath.string()); switch(mapType_) @@ -132,7 +152,6 @@ bool Replay::StartRecording(const boost::filesystem::path& filepath, const MapIn default: return false; case MapType::OldMap: RTTR_Assert(!mapInfo.savegame); - // Map-Daten file_.WriteUnsignedInt(mapInfo.mapData.uncompressedLength); file_.WriteUnsignedInt(mapInfo.mapData.data.size()); file_.WriteRawData(&mapInfo.mapData.data[0], mapInfo.mapData.data.size()); @@ -143,7 +162,7 @@ bool Replay::StartRecording(const boost::filesystem::path& filepath, const MapIn break; case MapType::Savegame: mapInfo.savegame->Save(file_, GetMapName()); break; } - // Alles sofort reinschreiben + // Flush now to not loose any information file_.Flush(); return true; @@ -151,14 +170,14 @@ bool Replay::StartRecording(const boost::filesystem::path& filepath, const MapIn const boost::filesystem::path& Replay::GetPath() const { - RTTR_Assert(IsValid()); + RTTR_Assert(file_.IsOpen()); return filepath_; } bool Replay::LoadHeader(const boost::filesystem::path& filepath) { Close(); - if(!file_.Open(filepath, OFM_READ)) + if(!file_.Open(filepath, OpenFileMode::Read)) { lastErrorMsg = _("File could not be opened."); return false; @@ -171,7 +190,15 @@ bool Replay::LoadHeader(const boost::filesystem::path& filepath) if(!ReadAllHeaderData(file_)) return false; - mapType_ = static_cast(file_.ReadUnsignedShort()); + mapType_ = static_cast(file_.ReadUnsignedChar()); + // TODO(Replay): Move before mapType to have it as early as possible. + // Previously mapType was an unsigned short, i.e. in little endian the 2nd byte was always unused/zero + subVersion_ = file_.ReadUnsignedChar(); + if(subVersion_ >= 1) + gcVersion_ = file_.ReadUnsignedChar(); + else + gcVersion_ = 0; + if(mapType_ == MapType::Savegame) { // Validate savegame @@ -209,12 +236,12 @@ bool Replay::LoadGameData(MapInfo& mapInfo) uncompressedDataFile_->close(); compressedData.DecompressToFile(uncompressedDataFile_->filePath); file_.Close(); - file_.Open(uncompressedDataFile_->filePath, OpenFileMode::OFM_READ); + file_.Open(uncompressedDataFile_->filePath, OpenFileMode::Read); } ReadPlayerData(file_); ReadGGS(file_); - random_init = file_.ReadUnsignedInt(); + randomSeed_ = file_.ReadUnsignedInt(); mapInfo.Clear(); mapInfo.type = mapType_; @@ -224,7 +251,6 @@ bool Replay::LoadGameData(MapInfo& mapInfo) { default: return false; case MapType::OldMap: - // Map-Daten mapInfo.mapData.uncompressedLength = file_.ReadUnsignedInt(); mapInfo.mapData.data.resize(file_.ReadUnsignedInt()); file_.ReadRawData(&mapInfo.mapData.data[0], mapInfo.mapData.data.size()); @@ -234,7 +260,6 @@ bool Replay::LoadGameData(MapInfo& mapInfo) file_.ReadRawData(&mapInfo.luaData.data[0], mapInfo.luaData.data.size()); break; case MapType::Savegame: - // Load savegame mapInfo.savegame = std::make_unique(); if(!mapInfo.savegame->Load(file_, SaveGameDataToLoad::All)) { @@ -254,89 +279,85 @@ bool Replay::LoadGameData(MapInfo& mapInfo) void Replay::AddChatCommand(unsigned gf, uint8_t player, ChatDestination dest, const std::string& str) { RTTR_Assert(IsRecording()); - if(!file_.IsValid()) + if(!file_.IsOpen()) return; file_.WriteUnsignedInt(gf); - file_.WriteUnsignedChar(static_cast(ReplayCommand::Chat)); + file_.WriteUnsignedChar(rttr::enum_cast(CommandType::Chat)); file_.WriteUnsignedChar(player); - file_.WriteUnsignedChar(static_cast(dest)); + file_.WriteUnsignedChar(rttr::enum_cast(dest)); file_.WriteLongString(str); - // Sofort rein damit + // Prevent loss in case of crash file_.Flush(); } void Replay::AddGameCommand(unsigned gf, uint8_t player, const PlayerGameCommands& cmds) { RTTR_Assert(IsRecording()); - if(!file_.IsValid()) + if(!file_.IsOpen()) return; file_.WriteUnsignedInt(gf); - file_.WriteUnsignedChar(static_cast(ReplayCommand::Game)); + file_.WriteUnsignedChar(rttr::enum_cast(CommandType::Game)); Serializer ser; ser.PushUnsignedChar(player); cmds.Serialize(ser); ser.WriteToFile(file_); - // Sofort rein damit + // Prevent loss in case of crash file_.Flush(); } -bool Replay::ReadGF(unsigned* gf) +std::optional Replay::ReadGF() { RTTR_Assert(IsReplaying()); try { - *gf = file_.ReadUnsignedInt(); + return file_.ReadUnsignedInt(); } catch(std::runtime_error&) { - *gf = 0xFFFFFFFF; - if(file_.EndOfFile()) - return false; + if(file_.IsEndOfFile()) + return std::nullopt; throw; } - return true; -} - -ReplayCommand Replay::ReadRCType() -{ - RTTR_Assert(IsReplaying()); - // Type auslesen - return ReplayCommand(file_.ReadUnsignedChar()); -} - -void Replay::ReadChatCommand(uint8_t& player, uint8_t& dest, std::string& str) -{ - RTTR_Assert(IsReplaying()); - player = file_.ReadUnsignedChar(); - dest = file_.ReadUnsignedChar(); - str = file_.ReadLongString(); } -void Replay::ReadGameCommand(uint8_t& player, PlayerGameCommands& cmds) +boost_variant2 Replay::ReadCommand() { RTTR_Assert(IsReplaying()); - Serializer ser; - ser.ReadFromFile(file_); - player = ser.PopUnsignedChar(); - cmds.Deserialize(ser); + const auto type = static_cast(file_.ReadUnsignedChar() - (subVersion_ == 0 ? 1 : 0)); + switch(type) + { + case CommandType::Chat: return ChatCommand(file_); + case CommandType::Game: return GameCommand(file_, gcVersion_); + default: throw std::invalid_argument("Invalid command type: " + std::to_string(rttr::enum_cast(type))); + } } void Replay::UpdateLastGF(unsigned last_gf) { RTTR_Assert(IsRecording()); - if(!file_.IsValid()) + if(!file_.IsOpen()) return; - // An die Stelle springen file_.Seek(lastGfFilePos_, SEEK_SET); - // Dorthin schreiben file_.WriteUnsignedInt(last_gf); - // Wieder ans Ende springen file_.Seek(0, SEEK_END); lastGF_ = last_gf; } + +Replay::ChatCommand::ChatCommand(BinaryFile& file) + : player(file.ReadUnsignedChar()), dest(static_cast(file.ReadUnsignedChar())), + msg(file.ReadLongString()) +{} + +Replay::GameCommand::GameCommand(BinaryFile& file, const unsigned version) +{ + gc::Deserializer ser{version}; + ser.ReadFromFile(file); + player = ser.PopUnsignedChar(); + cmds.Deserialize(ser); +} diff --git a/libs/s25main/Replay.h b/libs/s25main/Replay.h index 6c004d35d..9192740cb 100644 --- a/libs/s25main/Replay.h +++ b/libs/s25main/Replay.h @@ -1,28 +1,23 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "SavedFile.h" +#include "network/PlayerGameCommands.h" +#include "variant.h" #include "gameTypes/ChatDestination.h" #include "gameTypes/MapType.h" #include "s25util/BinaryFile.h" #include +#include #include class MapInfo; struct PlayerGameCommands; class TmpFile; -/// Replay-Command-Art -enum class ReplayCommand -{ - End, - Chat, - Game -}; - /// Holds a replay that is being recorded or was recorded and loaded /// It has a header that holds minimal information: /// File header (version etc.), record time, map name, player names, length (last GF), savegame header (if @@ -31,6 +26,25 @@ enum class ReplayCommand class Replay : public SavedFile { public: + enum class CommandType + { + Chat, + Game, + }; + struct ChatCommand + { + ChatCommand(BinaryFile& file); + uint8_t player; + ChatDestination dest; + std::string msg; + }; + struct GameCommand + { + GameCommand(BinaryFile& file, unsigned version); + uint8_t player; + PlayerGameCommands cmds; + }; + Replay(); ~Replay() override; @@ -39,51 +53,52 @@ class Replay : public SavedFile std::string GetSignature() const override; uint16_t GetVersion() const override; - /// Beginnt die Save-Datei und schreibt den Header - bool StartRecording(const boost::filesystem::path& filepath, const MapInfo& mapInfo); + /// Opens the replay for recording + bool StartRecording(const boost::filesystem::path& filepath, const MapInfo& mapInfo, unsigned randomSeed); /// Stop recording. Will compress the data and return true if that succeeded. The file will be closed in any case bool StopRecording(); - /// Replaydatei gültig? - bool IsValid() const { return file_.IsValid(); } - bool IsRecording() const { return isRecording_ && file_.IsValid(); } - bool IsReplaying() const { return !isRecording_ && file_.IsValid(); } + /// Is the replay in the state for accepting commands to record + bool IsRecording() const { return isRecording_ && file_.IsOpen(); } + /// Is the replay open for reading commands + bool IsReplaying() const { return !isRecording_ && file_.IsOpen(); } const boost::filesystem::path& GetPath() const; - /// Loads the header and optionally the mapInfo (former "extended header") + /// Load the header (containing map and player names) bool LoadHeader(const boost::filesystem::path& filepath); + /// Load the remaining data into the mapInfo bool LoadGameData(MapInfo& mapInfo); - /// Fügt ein Chat-Kommando hinzu (schreibt) + /// Record a chat message void AddChatCommand(unsigned gf, uint8_t player, ChatDestination dest, const std::string& str); - /// Fügt ein Spiel-Kommando hinzu (schreibt) + /// Record game commands (player actions) void AddGameCommand(unsigned gf, uint8_t player, const PlayerGameCommands& cmds); - /// Liest RC-Type aus, liefert false, wenn das Replay zu Ende ist - bool ReadGF(unsigned* gf); - /// RC-Type aus, liefert false - ReplayCommand ReadRCType(); - /// Liest ein Chat-Command aus - void ReadChatCommand(uint8_t& player, uint8_t& dest, std::string& str); - void ReadGameCommand(uint8_t& player, PlayerGameCommands& cmds); + /// Read the next GameFrame to which the following replay command applies if there are any left + std::optional ReadGF(); + boost_variant2 ReadCommand(); - /// Aktualisiert den End-GF, schreibt ihn in die Replaydatei (nur beim Spielen bzw. Schreiben verwenden!) + /// Update the (currently) last GameFrame in the file void UpdateLastGF(unsigned last_gf); + unsigned getSeed() const { return randomSeed_; } unsigned GetLastGF() const { return lastGF_; } - /// Zufallsgeneratorinitialisierung - unsigned random_init; - protected: BinaryFile file_; std::unique_ptr uncompressedDataFile_; /// Used when reading a compressed replay boost::filesystem::path filepath_; /// Path to current file - bool isRecording_; + bool isRecording_ = false; + /// Seed for the random number generator + unsigned randomSeed_ = 0; /// End-GF - unsigned lastGF_; - /// Position des End-GF in der Datei - unsigned lastGfFilePos_; - MapType mapType_; + unsigned lastGF_ = 0; + /// Position of the last GF value in the file + unsigned lastGfFilePos_ = 0; + MapType mapType_ = MapType(0); + + /// Sub version for backwards compatibility (i.e. allow loading older files with same file version) + uint8_t subVersion_ = 0; + uint8_t gcVersion_ = 0; }; diff --git a/libs/s25main/ReplayInfo.h b/libs/s25main/ReplayInfo.h index 16f7b954c..201729e5c 100644 --- a/libs/s25main/ReplayInfo.h +++ b/libs/s25main/ReplayInfo.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,20 +6,18 @@ #include "Replay.h" #include +#include #include struct ReplayInfo { - ReplayInfo() : async(0), end(false), next_gf(0), all_visible(false) {} - - /// Replaydatei + /// Replay file Replay replay; boost::filesystem::path filename; - /// Replay asynchron (Meldung nur einmal ausgeben!) - int async; - bool end; - // Nächster Replay-Command-Zeitpunkt (in GF) - unsigned next_gf; - /// Alles sichtbar (FoW deaktiviert) + /// Number of async GFs + int async = 0; + // GF for the next replay command if any + std::optional next_gf; + /// FoW deactivated? bool all_visible; }; diff --git a/libs/s25main/SavedFile.cpp b/libs/s25main/SavedFile.cpp index 870931c55..d3af6585b 100644 --- a/libs/s25main/SavedFile.cpp +++ b/libs/s25main/SavedFile.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/libs/s25main/SavedFile.h b/libs/s25main/SavedFile.h index 8a0193813..6a11a838b 100644 --- a/libs/s25main/SavedFile.h +++ b/libs/s25main/SavedFile.h @@ -52,10 +52,10 @@ class SavedFile void AddPlayer(const BasePlayerInfo& player); void ClearPlayers(); - std::string GetLastErrorMsg() const { return lastErrorMsg; } + const std::string& GetLastErrorMsg() const { return lastErrorMsg; } std::string GetRevision() const; - std::string GetMapName() const { return mapName_; } + const std::string& GetMapName() const { return mapName_; } s25util::time64_t GetSaveTime() const { return saveTime_; } const std::vector& GetPlayerNames() const { return playerNames_; } diff --git a/libs/s25main/Savegame.cpp b/libs/s25main/Savegame.cpp index 629fcf780..ec1a1bad8 100644 --- a/libs/s25main/Savegame.cpp +++ b/libs/s25main/Savegame.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,7 +30,7 @@ bool Savegame::Save(const boost::filesystem::path& filepath, const std::string& { BinaryFile file; - return file.Open(filepath, OFM_WRITE) && Save(file, mapName); + return file.Open(filepath, OpenFileMode::Write) && Save(file, mapName); } bool Savegame::Save(BinaryFile& file, const std::string& mapName) @@ -47,7 +47,7 @@ bool Savegame::Load(const boost::filesystem::path& filePath, const SaveGameDataT { BinaryFile file; - return file.Open(filePath, OFM_READ) && Load(file, what); + return file.Open(filePath, OpenFileMode::Read) && Load(file, what); } bool Savegame::Load(BinaryFile& file, const SaveGameDataToLoad what) diff --git a/libs/s25main/SerializedGameData.cpp b/libs/s25main/SerializedGameData.cpp index bd304f8a7..3d2a79771 100644 --- a/libs/s25main/SerializedGameData.cpp +++ b/libs/s25main/SerializedGameData.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -83,7 +83,7 @@ /// Usage: Always save for the most current version but include loading code that can cope with file format changes /// If a format change occurred that can still be handled increase this version and handle it in the loading code. /// If the change is to big to handle increase the version in Savegame.cpp and remove all code referencing GetGameDataVersion. -/// Then reset this number to 1. +/// Then reset this number to 0. /// TODO: Let GO_Type start at 0 again when resetting this /// Changelog: /// 2: All player buildings together, variable width size for containers and ship names diff --git a/libs/s25main/buildings/nobBaseWarehouse.cpp b/libs/s25main/buildings/nobBaseWarehouse.cpp index f44ba1cc0..8da8aeba7 100644 --- a/libs/s25main/buildings/nobBaseWarehouse.cpp +++ b/libs/s25main/buildings/nobBaseWarehouse.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -367,7 +367,7 @@ void nobBaseWarehouse::HandleSendoutEvent() return; } - std::vector> possibleTypes; + std::vector> possibleTypes; // Waren und Figuren zum Auslagern zusammensuchen // Wenn keine Platz an Flagge, dann keine Waren raus if(GetFlag()->HasSpaceForWare()) @@ -397,7 +397,7 @@ void nobBaseWarehouse::HandleSendoutEvent() if(holds_alternative(selectedId)) { // Ware - const auto goodType = boost::get(selectedId); + const auto goodType = get(selectedId); auto ware = std::make_unique(goodType, nullptr, this); noBaseBuilding* wareGoal = world->GetPlayer(player).FindClientForWare(*ware); if(wareGoal != this) @@ -418,7 +418,7 @@ void nobBaseWarehouse::HandleSendoutEvent() world->GetPlayer(player).RemoveWare(*ware); } else { - const auto jobType = boost::get(selectedId); + const auto jobType = get(selectedId); nobBaseWarehouse* wh = world->GetPlayer(player).FindWarehouse(*this, FW::AcceptsFigureButNoSend(jobType), true, false); if(wh != this) @@ -1174,20 +1174,20 @@ InventorySetting nobBaseWarehouse::GetInventorySetting(const GoodType ware) cons } /// Verändert Ein/Auslagerungseinstellungen (visuell) -void nobBaseWarehouse::SetInventorySettingVisual(const boost::variant& what, InventorySetting state) +void nobBaseWarehouse::SetInventorySettingVisual(const boost_variant2& what, InventorySetting state) { state.MakeValid(); - boost::apply_visitor([this, state](auto type) { inventorySettingsVisual[type] = state; }, what); + visit([this, state](auto type) { inventorySettingsVisual[type] = state; }, what); NotifyListeners(1); } /// Verändert Ein/Auslagerungseinstellungen (real) -void nobBaseWarehouse::SetInventorySetting(const boost::variant& what, InventorySetting state) +void nobBaseWarehouse::SetInventorySetting(const boost_variant2& what, InventorySetting state) { state.MakeValid(); InventorySetting& selectedSetting = - boost::apply_visitor([this](auto type) -> InventorySetting& { return inventorySettings[type]; }, what); + visit([this](auto type) -> InventorySetting& { return inventorySettings[type]; }, what); InventorySetting oldState = selectedSetting; selectedSetting = state; @@ -1208,7 +1208,7 @@ void nobBaseWarehouse::SetInventorySetting(const boost::variant& // Sind Waren vorhanden, die ausgelagert werden müssen und ist noch kein Auslagerungsevent vorhanden --> neues // anmelden auto getWaresOrJobs = [this](auto type) { return inventory[type]; }; - if(!empty_event && boost::apply_visitor(getWaresOrJobs, what)) + if(!empty_event && visit(getWaresOrJobs, what)) empty_event = GetEvMgr().AddEvent(this, empty_INTERVAL, 3); } else if(!oldState.IsSet(EInventorySetting::Collect) && state.IsSet(EInventorySetting::Collect)) { @@ -1340,7 +1340,7 @@ void nobBaseWarehouse::RefreshReserve(unsigned rank) // ansonsten ists gleich und alles ist in Ordnung! } -void nobBaseWarehouse::CheckOuthousing(const boost::variant& what) +void nobBaseWarehouse::CheckOuthousing(const boost_variant2& what) { // Check if we need to send this ware or figure and register an event for this // If we already have an event, we don't need to do anything @@ -1348,12 +1348,12 @@ void nobBaseWarehouse::CheckOuthousing(const boost::variant& what return; const InventorySetting setting = - boost::apply_visitor(composeVisitor( - [this](Job job) { // Bootsträger in Träger umwandeln, der evtl dann raus soll - return GetInventorySetting((job == Job::BoatCarrier) ? Job::Helper : job); - }, - [this](GoodType good) { return GetInventorySetting(good); }), - what); + visit(composeVisitor( + [this](Job job) { // Bootsträger in Träger umwandeln, der evtl dann raus soll + return GetInventorySetting((job == Job::BoatCarrier) ? Job::Helper : job); + }, + [this](GoodType good) { return GetInventorySetting(good); }), + what); if(setting.IsSet(EInventorySetting::Send)) empty_event = GetEvMgr().AddEvent(this, empty_INTERVAL, 3); @@ -1389,7 +1389,7 @@ unsigned nobBaseWarehouse::GetAvailableFiguresForTrading(const Job job) const } /// Starts a trade caravane from this warehouse -void nobBaseWarehouse::StartTradeCaravane(const boost::variant& what, const unsigned count, +void nobBaseWarehouse::StartTradeCaravane(const boost_variant2& what, const unsigned count, const TradeRoute& tr, nobBaseWarehouse* goal) { auto tlOwned = std::make_unique(pos, player, tr, this->GetPos(), goal->GetPos()); @@ -1417,19 +1417,19 @@ void nobBaseWarehouse::StartTradeCaravane(const boost::variant& w owner.DecreaseInventoryJob(Job::Helper, 1); // Also diminish the count of donkeys - boost::apply_visitor(composeVisitor( - [&](const Job job) { - // remove the jobs - inventory.real.Remove(job, count); - owner.DecreaseInventoryJob(job, count); - }, - [&](const GoodType gt) { - // Diminish the goods in the warehouse - inventory.real.Remove(gt, count); - owner.DecreaseInventoryWare(gt, count); - // now that we have removed the goods lets remove the donkeys - inventory.real.Remove(Job::PackDonkey, count); - owner.DecreaseInventoryJob(Job::PackDonkey, count); - }), - what); + visit(composeVisitor( + [&](const Job job) { + // remove the jobs + inventory.real.Remove(job, count); + owner.DecreaseInventoryJob(job, count); + }, + [&](const GoodType gt) { + // Diminish the goods in the warehouse + inventory.real.Remove(gt, count); + owner.DecreaseInventoryWare(gt, count); + // now that we have removed the goods lets remove the donkeys + inventory.real.Remove(Job::PackDonkey, count); + owner.DecreaseInventoryJob(Job::PackDonkey, count); + }), + what); } diff --git a/libs/s25main/buildings/nobBaseWarehouse.h b/libs/s25main/buildings/nobBaseWarehouse.h index 39d06e21a..eb9afcf97 100644 --- a/libs/s25main/buildings/nobBaseWarehouse.h +++ b/libs/s25main/buildings/nobBaseWarehouse.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,10 +6,10 @@ #include "DataChangedObservable.h" #include "nobBaseMilitary.h" +#include "variant.h" #include "gameTypes/GoodsAndPeopleArray.h" #include "gameTypes/InventorySetting.h" #include "gameTypes/VirtualInventory.h" -#include #include #include #include @@ -89,13 +89,13 @@ class nobBaseWarehouse : public nobBaseMilitary, public DataChangedObservable friend class gc::SetInventorySetting; friend class gc::SetAllInventorySettings; /// Verändert Ein/Auslagerungseinstellungen - void SetInventorySetting(const boost::variant& what, InventorySetting state); + void SetInventorySetting(const boost_variant2& what, InventorySetting state); /// Verändert alle Ein/Auslagerungseinstellungen einer Kategorie (also Waren oder Figuren)(real) void SetAllInventorySettings(bool isJob, const std::vector& states); /// Lässt einen bestimmten Waren/Job-Typ ggf auslagern - void CheckOuthousing(const boost::variant& what); + void CheckOuthousing(const boost_variant2& what); void HandleCollectEvent(); void HandleSendoutEvent(); void HandleRecrutingEvent(); @@ -169,7 +169,7 @@ class nobBaseWarehouse : public nobBaseMilitary, public DataChangedObservable return GetInventorySetting(ware).IsSet(setting); } - void SetInventorySettingVisual(const boost::variant& what, InventorySetting state); + void SetInventorySettingVisual(const boost_variant2& what, InventorySetting state); /// Bestellt einen Träger void OrderCarrier(noRoadNode& goal, RoadSegment& workplace); @@ -278,7 +278,7 @@ class nobBaseWarehouse : public nobBaseMilitary, public DataChangedObservable /// Available figures of a specific type that can be used for trading unsigned GetAvailableFiguresForTrading(Job job) const; /// Starts a trade caravane from this warehouse - void StartTradeCaravane(const boost::variant& what, unsigned count, const TradeRoute& tr, + void StartTradeCaravane(const boost_variant2& what, unsigned count, const TradeRoute& tr, nobBaseWarehouse* goal); /// For debug only diff --git a/libs/s25main/controls/ctrlChat.cpp b/libs/s25main/controls/ctrlChat.cpp index 64991ba65..7078d63ab 100644 --- a/libs/s25main/controls/ctrlChat.cpp +++ b/libs/s25main/controls/ctrlChat.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +8,6 @@ #include "ctrlScrollBar.h" #include "driver/MouseCoords.h" #include "ogl/glFont.h" -#include "variant.h" #include "s25util/Log.h" /// Breite der Scrollbar @@ -118,7 +117,7 @@ void ctrlChat::Draw_() for(unsigned i = 0; i < show_lines; ++i) { DrawPoint curTextPos = textPos; - if(PrimaryChatLine* line = boost::get(&chat_lines[i + pos])) + if(PrimaryChatLine* line = get_if(&chat_lines[i + pos])) { // Zeit, Spieler und danach Textnachricht if(!line->time_string.empty()) @@ -140,7 +139,7 @@ void ctrlChat::Draw_() curTextPos.x += bracket2_size; } } - boost::apply_visitor( + visit( [this, curTextPos](const auto& line) { // Draw msg this->font->Draw(curTextPos, line.msg, FontStyle{}, line.msg_color); }, diff --git a/libs/s25main/controls/ctrlChat.h b/libs/s25main/controls/ctrlChat.h index 27b3ec6de..26c5bb0a1 100644 --- a/libs/s25main/controls/ctrlChat.h +++ b/libs/s25main/controls/ctrlChat.h @@ -1,11 +1,11 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "Window.h" -#include +#include "variant.h" #include class MouseCoords; class glFont; @@ -64,7 +64,7 @@ class ctrlChat : public Window /// Farbe der Chatnachricht unsigned msg_color; }; - using ChatLine = boost::variant; + using ChatLine = boost_variant2; TextureColor tc; /// Hintergrundtextur. const glFont* font; /// Schriftart. diff --git a/libs/s25main/desktops/dskGameLoader.cpp b/libs/s25main/desktops/dskGameLoader.cpp index 6c1536561..2fe877130 100644 --- a/libs/s25main/desktops/dskGameLoader.cpp +++ b/libs/s25main/desktops/dskGameLoader.cpp @@ -132,8 +132,11 @@ void dskGameLoader::Msg_Timer(const unsigned /*ctrl_id*/) void dskGameLoader::ShowErrorMsg(const std::string& error) { - WINDOWMANAGER.Show( - std::make_unique(_("Error"), error, this, MsgboxButton::Ok, MsgboxIcon::ExclamationRed, 0)); + auto wnd = std::make_unique(_("Error"), error, this, MsgboxButton::Ok, MsgboxIcon::ExclamationRed, 0); + if(IsActive()) + WINDOWMANAGER.Show(std::move(wnd)); + else + WINDOWMANAGER.ShowAfterSwitch(std::move(wnd)); GetCtrl(1)->Stop(); } diff --git a/libs/s25main/factories/GameCommandFactory.cpp b/libs/s25main/factories/GameCommandFactory.cpp index 8df5ad451..98aa7da1a 100644 --- a/libs/s25main/factories/GameCommandFactory.cpp +++ b/libs/s25main/factories/GameCommandFactory.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -102,7 +102,7 @@ bool GameCommandFactory::NotifyAlliesOfLocation(const MapPoint pt) return AddGC(new gc::NotifyAlliesOfLocation(pt)); } -bool GameCommandFactory::SetInventorySetting(const MapPoint pt, const boost::variant& what, +bool GameCommandFactory::SetInventorySetting(const MapPoint pt, const boost_variant2& what, InventorySetting state) { return AddGC(new gc::SetInventorySetting(pt, what, state)); @@ -189,7 +189,7 @@ bool GameCommandFactory::StartStopExplorationExpedition(const MapPoint pt, bool return AddGC(new gc::StartStopExplorationExpedition(pt, start)); } -bool GameCommandFactory::TradeOverLand(const MapPoint pt, const boost::variant& what, unsigned count) +bool GameCommandFactory::TradeOverLand(const MapPoint pt, const boost_variant2& what, unsigned count) { return AddGC(new gc::TradeOverLand(pt, what, count)); } diff --git a/libs/s25main/factories/GameCommandFactory.h b/libs/s25main/factories/GameCommandFactory.h index 302a1d031..73fa0391c 100644 --- a/libs/s25main/factories/GameCommandFactory.h +++ b/libs/s25main/factories/GameCommandFactory.h @@ -1,10 +1,11 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "GameCommand.h" +#include "variant.h" #include "gameTypes/BuildingType.h" #include "gameTypes/Direction.h" #include "gameTypes/GoodTypes.h" @@ -13,7 +14,6 @@ #include "gameTypes/PactTypes.h" #include "gameTypes/SettingsTypes.h" #include "gameTypes/ShipDirection.h" -#include #include struct InventorySetting; @@ -57,7 +57,7 @@ class GameCommandFactory bool SetProductionEnabled(MapPoint pt, bool enabled); bool NotifyAlliesOfLocation(MapPoint pt); /// Sets inventory settings for a warehouse - bool SetInventorySetting(MapPoint pt, const boost::variant& what, InventorySetting state); + bool SetInventorySetting(MapPoint pt, const boost_variant2& what, InventorySetting state); bool SetAllInventorySettings(MapPoint pt, bool isJob, const std::vector& states); bool ChangeReserve(MapPoint pt, unsigned char rank, unsigned count); bool CheatArmageddon(); @@ -78,7 +78,7 @@ class GameCommandFactory /// Cancels an expedition bool CancelExpedition(unsigned shipID); bool StartStopExplorationExpedition(MapPoint pt, bool start); - bool TradeOverLand(MapPoint pt, const boost::variant& what, unsigned count); + bool TradeOverLand(MapPoint pt, const boost_variant2& what, unsigned count); protected: virtual ~GameCommandFactory() = default; diff --git a/libs/s25main/figures/nofTradeDonkey.cpp b/libs/s25main/figures/nofTradeDonkey.cpp index b56e80023..fd6255097 100644 --- a/libs/s25main/figures/nofTradeDonkey.cpp +++ b/libs/s25main/figures/nofTradeDonkey.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,18 +9,17 @@ #include "buildings/nobBaseWarehouse.h" #include "network/GameClient.h" #include "ogl/glArchivItem_Bitmap.h" -#include "variant.h" #include "world/GameWorld.h" #include "gameData/BuildingProperties.h" #include "gameData/GameConsts.h" #include "gameData/JobConsts.h" nofTradeDonkey::nofTradeDonkey(const MapPoint pos, const unsigned char player, - const boost::variant& what) - : noFigure(holds_alternative(what) ? boost::get(what) : Job::PackDonkey, pos, player), successor(nullptr) + const boost_variant2& what) + : noFigure(holds_alternative(what) ? get(what) : Job::PackDonkey, pos, player), successor(nullptr) { if(holds_alternative(what)) - gt = boost::get(what); + gt = get(what); } nofTradeDonkey::nofTradeDonkey(SerializedGameData& sgd, const unsigned obj_id) diff --git a/libs/s25main/figures/nofTradeDonkey.h b/libs/s25main/figures/nofTradeDonkey.h index ac4a180ba..24f5330eb 100644 --- a/libs/s25main/figures/nofTradeDonkey.h +++ b/libs/s25main/figures/nofTradeDonkey.h @@ -1,13 +1,13 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "figures/noFigure.h" +#include "variant.h" #include "gameTypes/GoodTypes.h" #include "gameTypes/TradeDirection.h" -#include #include class SerializedGameData; @@ -38,7 +38,7 @@ class nofTradeDonkey : public noFigure } public: - nofTradeDonkey(MapPoint pos, unsigned char player, const boost::variant& what); + nofTradeDonkey(MapPoint pos, unsigned char player, const boost_variant2& what); nofTradeDonkey(SerializedGameData& sgd, unsigned obj_id); void Destroy() override diff --git a/libs/s25main/ingameWindows/iwAction.cpp b/libs/s25main/ingameWindows/iwAction.cpp index a7c96ae37..071b506ca 100644 --- a/libs/s25main/ingameWindows/iwAction.cpp +++ b/libs/s25main/ingameWindows/iwAction.cpp @@ -185,7 +185,7 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP { ctrlGroup* group = main_tab->AddTab(LOADER.GetImageN("io", 70), _("Erect flag"), TAB_FLAG); - switch(boost::get(params)) + switch(get(params)) { case FlagType::Normal: { @@ -236,7 +236,7 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP ctrlGroup* group = main_tab->AddTab(LOADER.GetImageN("io", 45), _("Erect flag"), TAB_SETFLAG); unsigned nr = 70; - if(boost::get(params) == FlagType::WaterFlag) + if(get(params) == FlagType::WaterFlag) nr = 94; // Straße aufwerten ggf anzeigen @@ -266,7 +266,7 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP if(tabs.attack) { ctrlGroup* group = main_tab->AddTab(LOADER.GetImageN("io", 98), _("Attack options"), TAB_ATTACK); - available_soldiers_count = boost::get(params); + available_soldiers_count = get(params); AddAttackControls(group, available_soldiers_count); selected_soldiers_count = 1; } diff --git a/libs/s25main/ingameWindows/iwAction.h b/libs/s25main/ingameWindows/iwAction.h index 52bd6a731..8544ee22c 100644 --- a/libs/s25main/ingameWindows/iwAction.h +++ b/libs/s25main/ingameWindows/iwAction.h @@ -1,12 +1,12 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "IngameWindow.h" +#include "variant.h" #include "gameTypes/MapCoordinates.h" -#include #include class GameInterface; @@ -26,7 +26,7 @@ class iwAction : public IngameWindow /// werden WaterFlag /// Flagge mit Anker drauf (Wasserstraße kann gebaut werden) }; - using Params = boost::variant; + using Params = boost_variant2; enum class BuildTab { diff --git a/libs/s25main/ingameWindows/iwTrade.cpp b/libs/s25main/ingameWindows/iwTrade.cpp index c796ca45b..51a8d2c57 100644 --- a/libs/s25main/ingameWindows/iwTrade.cpp +++ b/libs/s25main/ingameWindows/iwTrade.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,13 +15,13 @@ #include "helpers/toString.h" #include "ogl/FontStyle.h" #include "ogl/glArchivItem_Bitmap.h" +#include "variant.h" #include "world/GameWorldBase.h" #include "world/GameWorldViewer.h" #include "gameData/GoodConsts.h" #include "gameData/JobConsts.h" #include "gameData/ShieldConsts.h" #include "gameData/const_gui_ids.h" -#include iwTrade::iwTrade(const nobBaseWarehouse& wh, const GameWorldViewer& gwv, GameCommandFactory& gcFactory) : IngameWindow(CGI_BUILDING + MapBase::CreateGUIID(wh.GetPos()), IngameWindow::posAtMouse, Extent(400, 194), @@ -79,7 +79,7 @@ void iwTrade::Msg_ButtonClick(const unsigned /*ctrl_id*/) // pressed the send button const unsigned short ware_figure_selection = GetCtrl(4)->GetSelection().get(); const bool isJob = this->GetCtrl(2)->GetSelection() == 1u; - boost::variant what; + boost_variant2 what; if(isJob) what = jobs[ware_figure_selection]; else diff --git a/libs/s25main/network/GameClient.cpp b/libs/s25main/network/GameClient.cpp index 7886cf78f..fe27e4277 100644 --- a/libs/s25main/network/GameClient.cpp +++ b/libs/s25main/network/GameClient.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -1442,13 +1442,12 @@ void GameClient::StartReplayRecording(const unsigned random_init) { replayinfo = std::make_unique(); replayinfo->filename = s25util::Time::FormatTime("%Y-%m-%d_%H-%i-%s") + ".rpl"; - replayinfo->replay.random_init = random_init; WritePlayerInfo(replayinfo->replay); replayinfo->replay.ggs = game->ggs_; - // Datei speichern - if(!replayinfo->replay.StartRecording(RTTRCONFIG.ExpandPath(s25::folders::replays) / replayinfo->filename, mapinfo)) + if(!replayinfo->replay.StartRecording(RTTRCONFIG.ExpandPath(s25::folders::replays) / replayinfo->filename, mapinfo, + random_init)) { LOG.write(_("Replayfile couldn't be opened. No replay will be recorded\n")); replayinfo.reset(); @@ -1534,12 +1533,10 @@ bool GameClient::StartReplay(const boost::filesystem::path& path) } replayMode = true; - replayinfo->async = 0; - replayinfo->end = false; try { - StartGame(replayinfo->replay.random_init); + StartGame(replayinfo->replay.getSeed()); } catch(SerializedGameData::Error& error) { LOG.write(_("Error when loading game from replay: %s\n")) % error.what(); @@ -1547,7 +1544,7 @@ bool GameClient::StartReplay(const boost::filesystem::path& path) return false; } - replayinfo->replay.ReadGF(&replayinfo->next_gf); + replayinfo->next_gf = replayinfo->replay.ReadGF(); return true; } diff --git a/libs/s25main/network/GameClientGF_Replay.cpp b/libs/s25main/network/GameClientGF_Replay.cpp index eb05faeb2..9f4a758f8 100644 --- a/libs/s25main/network/GameClientGF_Replay.cpp +++ b/libs/s25main/network/GameClientGF_Replay.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,6 +8,7 @@ #include "helpers/format.hpp" #include "network/ClientInterface.h" #include "network/GameClient.h" +#include "variant.h" #include "s25util/Log.h" void GameClient::ExecuteGameFrame_Replay() @@ -15,62 +16,54 @@ void GameClient::ExecuteGameFrame_Replay() AsyncChecksum checksum = AsyncChecksum::create(*game); const unsigned curGF = GetGFNumber(); - RTTR_Assert(replayinfo->next_gf >= curGF || curGF > replayinfo->replay.GetLastGF()); //-V807 + RTTR_Assert(replayinfo->next_gf.value_or(curGF) >= curGF || curGF > replayinfo->replay.GetLastGF()); //-V807 bool cmdsExecuted = false; + auto& replay = replayinfo->replay; // Execute all commands from the replay for the current GF - while(replayinfo->next_gf == curGF) + while(replayinfo->next_gf && replayinfo->next_gf == curGF) { - // What type of command follows? - ReplayCommand rc = replayinfo->replay.ReadRCType(); - - if(rc == ReplayCommand::Chat) - { - uint8_t player, dest; - std::string message; - replayinfo->replay.ReadChatCommand(player, dest, message); - - if(ci) - ci->CI_Chat(player, ChatDestination(dest), message); - } else if(rc == ReplayCommand::Game) - { - cmdsExecuted = true; - - PlayerGameCommands msg; - uint8_t gcPlayer; - replayinfo->replay.ReadGameCommand(gcPlayer, msg); - - // Execute them - ExecuteAllGCs(gcPlayer, msg); - AsyncChecksum& msgChecksum = msg.checksum; - - // Check for async if checksum data is valid - if(msgChecksum.randChecksum != 0 && msgChecksum != checksum) - { - // Show message if this is the first async GF - if(replayinfo->async == 0) + const auto cmd = replay.ReadCommand(); + visit( + composeVisitor( + [this](const Replay::ChatCommand& cmd) { + if(ci) + ci->CI_Chat(cmd.player, cmd.dest, cmd.msg); + }, + [this, &cmdsExecuted, &checksum, curGF](const Replay::GameCommand& cmd) { + cmdsExecuted = true; + + ExecuteAllGCs(cmd.player, cmd.cmds); + const AsyncChecksum& msgChecksum = cmd.cmds.checksum; + + // Check for async if checksum data is valid + if(msgChecksum.randChecksum != 0 && msgChecksum != checksum) { - if(ci) + // Show message if this is the first async GF + if(replayinfo->async == 0) { - ci->CI_ReplayAsync(helpers::format( - _("Warning: The played replay is not in sync with the original match. (GF: %u)"), curGF)); + if(ci) + { + ci->CI_ReplayAsync(helpers::format( + _("Warning: The played replay is not in sync with the original match. (GF: %u)"), curGF)); + } + + LOG.write("Async at GF %u: Checksum %i:%i ObjCt %u:%u ObjIdCt %u:%u\n") % curGF + % msgChecksum.randChecksum % checksum.randChecksum % msgChecksum.objCt % checksum.objCt + % msgChecksum.objIdCt % checksum.objIdCt; + + // and pause the game for further investigation + framesinfo.isPaused = true; + if(skiptogf) + skiptogf = 0; } - LOG.write("Async at GF %u: Checksum %i:%i ObjCt %u:%u ObjIdCt %u:%u\n") % curGF - % msgChecksum.randChecksum % checksum.randChecksum % msgChecksum.objCt % checksum.objCt - % msgChecksum.objIdCt % checksum.objIdCt; - - // and pause the game for further investigation - framesinfo.isPaused = true; - if(skiptogf) - skiptogf = 0; + replayinfo->async++; } - - replayinfo->async++; - } - } + }), + cmd); // Read GF of next command - replayinfo->replay.ReadGF(&replayinfo->next_gf); + replayinfo->next_gf = replayinfo->replay.ReadGF(); } // Run game simulation @@ -97,15 +90,14 @@ void GameClient::ExecuteGameFrame_Replay() if(replayinfo->async != 0) { - // Messenger im Game + // in-game messenger if(ci) { ci->CI_ReplayEndReached( helpers::format(_("Notice: Overall asynchronous frame count: %u"), replayinfo->async)); } } - - replayinfo->end = true; + replayinfo->next_gf.reset(); framesinfo.isPaused = true; if(skiptogf) skiptogf = GetGFNumber(); diff --git a/libs/s25main/network/GameMessage_GameCommand.cpp b/libs/s25main/network/GameMessage_GameCommand.cpp index 30644ee0b..391fe9f31 100644 --- a/libs/s25main/network/GameMessage_GameCommand.cpp +++ b/libs/s25main/network/GameMessage_GameCommand.cpp @@ -24,7 +24,8 @@ void GameMessage_GameCommand::Serialize(Serializer& ser) const void GameMessage_GameCommand::Deserialize(Serializer& ser) { GameMessageWithPlayer::Deserialize(ser); - cmds.Deserialize(ser); + gc::Deserializer deser(ser); + cmds.Deserialize(deser); } bool GameMessage_GameCommand::Run(GameMessageInterface* callback) const diff --git a/libs/s25main/network/GameServerPlayer.cpp b/libs/s25main/network/GameServerPlayer.cpp index d0001fa09..775b4927a 100644 --- a/libs/s25main/network/GameServerPlayer.cpp +++ b/libs/s25main/network/GameServerPlayer.cpp @@ -21,7 +21,7 @@ int durationToInt(const T_Duration& duration) GameServerPlayer::GameServerPlayer(unsigned id, const Socket& socket) //-V818 : NetworkPlayer(id), state_(JustConnectedState()) { - boost::get(state_).timer.start(); + get(state_).timer.start(); this->socket = socket; } @@ -42,7 +42,7 @@ void GameServerPlayer::setActive() void GameServerPlayer::doPing() { - ActiveState* state = boost::get(&state_); + ActiveState* state = get_if(&state_); if(state && !state->isPinging && (!state->pingTimer.isRunning() || state->pingTimer.getElapsed() >= seconds(PING_RATE))) { @@ -54,7 +54,7 @@ void GameServerPlayer::doPing() unsigned GameServerPlayer::calcPingTime() { - auto& state = boost::get(state_); + auto& state = get(state_); if(!state.isPinging) return 0u; int result = durationToInt(std::chrono::duration_cast(state.pingTimer.getElapsed())); @@ -67,7 +67,7 @@ unsigned GameServerPlayer::calcPingTime() bool GameServerPlayer::hasTimedOut() const { - return boost::apply_visitor( + return visit( composeVisitor( [](const JustConnectedState& s) { return s.timer.getElapsed() > seconds(CONNECT_TIMEOUT); }, [](const MapSendingState& s) { return s.timer.getElapsed() > seconds(CONNECT_TIMEOUT) + s.estimatedSendTime; }, @@ -77,7 +77,7 @@ bool GameServerPlayer::hasTimedOut() const unsigned GameServerPlayer::getLagTimeOut() const { - const ActiveState& state = boost::get(state_); + const auto& state = get(state_); if(!state.lagTimer.isRunning()) return LAG_TIMEOUT; int timeout = @@ -87,10 +87,10 @@ unsigned GameServerPlayer::getLagTimeOut() const void GameServerPlayer::setLagging() { - boost::get(state_).lagTimer.restart(); + get(state_).lagTimer.restart(); } void GameServerPlayer::setNotLagging() { - boost::get(state_).lagTimer.stop(); + get(state_).lagTimer.stop(); } diff --git a/libs/s25main/network/GameServerPlayer.h b/libs/s25main/network/GameServerPlayer.h index 31830b1d5..5c1cf6248 100644 --- a/libs/s25main/network/GameServerPlayer.h +++ b/libs/s25main/network/GameServerPlayer.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,7 +7,7 @@ #include "NetworkPlayer.h" #include "Timer.h" #include "helpers/SmoothedValue.hpp" -#include +#include "variant.h" /// Player connected to the server class GameServerPlayer : public NetworkPlayer @@ -58,8 +58,8 @@ class GameServerPlayer : public NetworkPlayer /// Set player not lagging (anymore) void setNotLagging(); - auto& getPendingSwaps() { return boost::get(state_).pendingSwaps; } + auto& getPendingSwaps() { return get(state_).pendingSwaps; } private: - boost::variant state_; + boost_variant2 state_; }; diff --git a/libs/s25main/network/PlayerGameCommands.cpp b/libs/s25main/network/PlayerGameCommands.cpp index d7f855336..a2fc86979 100644 --- a/libs/s25main/network/PlayerGameCommands.cpp +++ b/libs/s25main/network/PlayerGameCommands.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,7 +14,7 @@ void PlayerGameCommands::Serialize(Serializer& ser) const gc->Serialize(ser); } -void PlayerGameCommands::Deserialize(Serializer& ser) +void PlayerGameCommands::Deserialize(gc::Deserializer& ser) { checksum.Deserialize(ser); diff --git a/libs/s25main/network/PlayerGameCommands.h b/libs/s25main/network/PlayerGameCommands.h index 76825be86..90fddf8b8 100644 --- a/libs/s25main/network/PlayerGameCommands.h +++ b/libs/s25main/network/PlayerGameCommands.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,7 +14,7 @@ struct PlayerGameCommands { /// Checksum for this NWF AsyncChecksum checksum; - /// The game gammands for this NWF + /// The game commands for this NWF std::vector gcs; PlayerGameCommands() = default; @@ -22,5 +22,5 @@ struct PlayerGameCommands : checksum(checksum), gcs(std::move(gcs)) {} void Serialize(Serializer& ser) const; - void Deserialize(Serializer& ser); + void Deserialize(gc::Deserializer& ser); }; diff --git a/libs/s25main/world/GameWorld.cpp b/libs/s25main/world/GameWorld.cpp index c7ddd3908..b02b01c3e 100644 --- a/libs/s25main/world/GameWorld.cpp +++ b/libs/s25main/world/GameWorld.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/libs/s25main/world/MapBase.cpp b/libs/s25main/world/MapBase.cpp index 44f31e181..3057780dc 100644 --- a/libs/s25main/world/MapBase.cpp +++ b/libs/s25main/world/MapBase.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/libs/s25main/world/MapSerializer.cpp b/libs/s25main/world/MapSerializer.cpp index ba73383c5..548655a86 100644 --- a/libs/s25main/world/MapSerializer.cpp +++ b/libs/s25main/world/MapSerializer.cpp @@ -179,11 +179,9 @@ void MapSerializer::Deserialize(GameWorldBase& world, SerializedGameData& sgd, G { if(sgd.PopUnsignedInt() != 0xC0DEBA5E) throw SerializedGameData::Error(_("Invalid id for lua data")); - // If there is a script, there is also save data. Pop that first - unsigned luaSaveSize = sgd.PopUnsignedInt(); - Serializer luaSaveState; - sgd.PopRawData(luaSaveState.GetDataWritable(luaSaveSize), luaSaveSize); - luaSaveState.SetLength(luaSaveSize); + // If there is a script, there is also save data. Store reference to that + const auto luaSaveSize = sgd.PopUnsignedInt(); + Serializer luaSaveState(sgd.PopAndDiscard(luaSaveSize), luaSaveSize); if(sgd.PopUnsignedInt() != 0xC001C0DE) throw SerializedGameData::Error(_("Invalid end-id for lua data")); diff --git a/tests/s25Main/autoplay/main.cpp b/tests/s25Main/autoplay/main.cpp index 27574121b..47810e264 100644 --- a/tests/s25Main/autoplay/main.cpp +++ b/tests/s25Main/autoplay/main.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -12,6 +12,8 @@ #include "network/PlayerGameCommands.h" #include "ogl/glAllocator.h" #include "random/Random.h" +#include "random/randomIO.h" +#include "variant.h" #include "world/GameWorld.h" #include "world/MapLoader.h" #include "gameTypes/MapInfo.h" @@ -31,6 +33,20 @@ struct Fixture : rttr::test::Fixture }; BOOST_GLOBAL_FIXTURE(Fixture); +static boost::test_tools::predicate_result verifyChecksum(const AsyncChecksum& actual, const AsyncChecksum& expected, + const bool fail = false) +{ + if(!fail && (expected.randChecksum == 0 || actual == expected)) + return true; + // LCOV_EXCL_START + boost::test_tools::predicate_result result(false); + result.message() << '\n' << actual << " != \n" << expected << '\n'; + for(const auto& entry : RANDOM.GetAsyncLog()) + result.message() << entry << '\n'; + return result; + // LCOV_EXCL_STOP +} + static void playReplay(const boost::filesystem::path& replayPath) { Replay replay; @@ -46,7 +62,7 @@ static void playReplay(const boost::filesystem::path& replayPath) for(unsigned i = 0; i < replay.GetNumPlayers(); i++) players.emplace_back(replay.GetPlayer(i)); Game game(replay.ggs, /*startGF*/ 0, players); - RANDOM.Init(replay.random_init); + RANDOM.Init(replay.getSeed()); GameWorld& gameWorld = game.world_; for(unsigned i = 0; i < gameWorld.GetNumPlayers(); ++i) @@ -58,43 +74,34 @@ static void playReplay(const boost::filesystem::path& replayPath) gameWorld.InitAfterLoad(); bool endOfReplay = false; - unsigned nextGF; - BOOST_TEST_REQUIRE(replay.ReadGF(&nextGF)); + auto nextGF = replay.ReadGF(); + BOOST_TEST_REQUIRE(nextGF.has_value()); const Timer timer(true); do { - unsigned curGF = game.em_->GetCurrentGF(); + const unsigned curGF = game.em_->GetCurrentGF(); AsyncChecksum checksum; - if(nextGF == curGF) + if(*nextGF == curGF) checksum = AsyncChecksum::create(game); - while(nextGF == curGF) + while(*nextGF == curGF) { BOOST_TEST_INFO("Current GF: " << curGF); - const ReplayCommand rc = replay.ReadRCType(); - - if(rc == ReplayCommand::Chat) - { - uint8_t player, dest; - std::string message; - replay.ReadChatCommand(player, dest, message); - } else if(rc == ReplayCommand::Game) - { - PlayerGameCommands msg; - uint8_t gcPlayer; - replay.ReadGameCommand(gcPlayer, msg); - for(const gc::GameCommandPtr& gc : msg.gcs) - gc->Execute(game.world_, gcPlayer); - AsyncChecksum& msgChecksum = msg.checksum; - if(msgChecksum.randChecksum != 0) - BOOST_TEST_REQUIRE(msgChecksum == checksum); - } - if(!replay.ReadGF(&nextGF)) + const auto cmd = replay.ReadCommand(); + visit(composeVisitor([](const Replay::ChatCommand&) {}, + [&](const Replay::GameCommand& cmd) { + for(const gc::GameCommandPtr& gc : cmd.cmds.gcs) + gc->Execute(game.world_, cmd.player); + BOOST_TEST_REQUIRE(verifyChecksum(checksum, cmd.cmds.checksum)); + }), + cmd); + nextGF = replay.ReadGF(); + if(!nextGF) { endOfReplay = true; break; } else - BOOST_TEST_REQUIRE(nextGF <= replay.GetLastGF()); + BOOST_TEST_REQUIRE(*nextGF <= replay.GetLastGF()); } game.RunGF(); } while(!endOfReplay); diff --git a/tests/s25Main/integration/testEconomyMode.cpp b/tests/s25Main/integration/testEconomyMode.cpp index f04d114cd..db5bd5cb4 100644 --- a/tests/s25Main/integration/testEconomyMode.cpp +++ b/tests/s25Main/integration/testEconomyMode.cpp @@ -148,8 +148,8 @@ BOOST_FIXTURE_TEST_CASE(EconomyModeSerialization, EconModeFixture) newWorld.getEconHandler()->UpdateAmounts(); for(const auto& teamPair : boost::combine(econTeams, econTeamsAfter)) { - const EconomyModeHandler::EconTeam& before = boost::get<0>(teamPair); - const EconomyModeHandler::EconTeam& after = boost::get<1>(teamPair); + const EconomyModeHandler::EconTeam& before = get<0>(teamPair); + const EconomyModeHandler::EconTeam& after = get<1>(teamPair); BOOST_TEST_REQUIRE(before.playersInTeam == after.playersInTeam); BOOST_TEST_REQUIRE(before.amountsTheTeamCollected == after.amountsTheTeamCollected); BOOST_TEST_REQUIRE(before.goodTypeWins == after.goodTypeWins); diff --git a/tests/s25Main/integration/testSerialization.cpp b/tests/s25Main/integration/testSerialization.cpp index 701eb3b70..46323d8cb 100644 --- a/tests/s25Main/integration/testSerialization.cpp +++ b/tests/s25Main/integration/testSerialization.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -17,6 +17,7 @@ #include "factories/BuildingFactory.h" #include "factories/GameCommandFactory.h" #include "figures/nofHunter.h" +#include "helpers/OptionalIO.h" #include "helpers/format.hpp" #include "network/GameMessage_Chat.h" #include "network/PlayerGameCommands.h" @@ -41,15 +42,6 @@ BOOST_TEST_DONT_PRINT_LOG_VALUE(Resource) BOOST_TEST_DONT_PRINT_LOG_VALUE(AddonId) BOOST_TEST_DONT_PRINT_LOG_VALUE(nofBuildingWorker::State) -namespace boost::test_tools::tt_detail { -template<> -struct print_log_value -{ - void operator()(std::ostream& os, ReplayCommand const& rc) { os << static_cast(rc); } -}; -} // namespace boost::test_tools::tt_detail -// LCOV_EXCL_STOP - namespace { using EmptyWorldFixture1P = WorldFixture; struct RandWorldFixture : public WorldFixture @@ -115,54 +107,56 @@ void AddReplayCmds(Replay& replay, const PlayerGameCommands& cmds) void CheckReplayCmds(Replay& loadReplay, const PlayerGameCommands& recordedCmds) { BOOST_TEST_REQUIRE(loadReplay.IsReplaying()); - unsigned gf; - BOOST_TEST_REQUIRE(loadReplay.ReadGF(&gf)); - BOOST_TEST_REQUIRE(gf == 1u); - BOOST_TEST_REQUIRE(loadReplay.ReadRCType() == ReplayCommand::Chat); - uint8_t player, dst; - std::string txt; - loadReplay.ReadChatCommand(player, dst, txt); - BOOST_TEST_REQUIRE(player == 2); - BOOST_TEST_REQUIRE(dst == 3); - BOOST_TEST_REQUIRE(txt == "Hello"); - - BOOST_TEST_REQUIRE(loadReplay.ReadGF(&gf)); - BOOST_TEST_REQUIRE(gf == 1u); - BOOST_TEST_REQUIRE(loadReplay.ReadRCType() == ReplayCommand::Chat); - loadReplay.ReadChatCommand(player, dst, txt); - BOOST_TEST_REQUIRE(player == 3); - BOOST_TEST_REQUIRE(dst == 1); - BOOST_TEST_REQUIRE(txt == "Hello2"); - - BOOST_TEST_REQUIRE(loadReplay.ReadGF(&gf)); - BOOST_TEST_REQUIRE(gf == 2u); - BOOST_TEST_REQUIRE(loadReplay.ReadRCType() == ReplayCommand::Chat); - loadReplay.ReadChatCommand(player, dst, txt); - BOOST_TEST_REQUIRE(player == 2); - BOOST_TEST_REQUIRE(dst == 2); - BOOST_TEST_REQUIRE(txt == "Hello3"); - - BOOST_TEST_REQUIRE(loadReplay.ReadGF(&gf)); - BOOST_TEST_REQUIRE(gf == 2u); - BOOST_TEST_REQUIRE(loadReplay.ReadRCType() == ReplayCommand::Game); - PlayerGameCommands cmds; - loadReplay.ReadGameCommand(player, cmds); - BOOST_TEST_REQUIRE(player == 0u); - BOOST_TEST_REQUIRE(cmds.checksum == recordedCmds.checksum); - BOOST_TEST_REQUIRE(cmds.gcs.size() == recordedCmds.gcs.size()); - BOOST_TEST_REQUIRE(dynamic_cast(cmds.gcs[0].get())); - BOOST_TEST_REQUIRE(dynamic_cast(cmds.gcs[1].get())); - - BOOST_TEST_REQUIRE(loadReplay.ReadGF(&gf)); - BOOST_TEST_REQUIRE(gf == 2u); - BOOST_TEST_REQUIRE(loadReplay.ReadRCType() == ReplayCommand::Chat); - loadReplay.ReadChatCommand(player, dst, txt); - BOOST_TEST_REQUIRE(player == 2); - BOOST_TEST_REQUIRE(dst == 3); - BOOST_TEST_REQUIRE(txt == "Hello4"); - - BOOST_TEST_REQUIRE(!loadReplay.ReadGF(&gf)); - BOOST_TEST_REQUIRE(gf == 0xFFFFFFFF); + + auto gf = loadReplay.ReadGF(); + BOOST_TEST(gf == 1u); + { + const auto cmd = get(loadReplay.ReadCommand()); + BOOST_TEST(cmd.player == 2); + BOOST_TEST(cmd.dest == ChatDestination::Enemies); + BOOST_TEST(cmd.msg == "Hello"); + } + + gf = loadReplay.ReadGF(); + BOOST_TEST(gf == 1u); + { + const auto cmd = get(loadReplay.ReadCommand()); + BOOST_TEST(cmd.player == 3); + BOOST_TEST(cmd.dest == ChatDestination::All); + BOOST_TEST(cmd.msg == "Hello2"); + } + + gf = loadReplay.ReadGF(); + BOOST_TEST(gf == 2u); + { + const auto cmd = get(loadReplay.ReadCommand()); + BOOST_TEST(cmd.player == 2); + BOOST_TEST(cmd.dest == ChatDestination::Allies); + BOOST_TEST(cmd.msg == "Hello3"); + } + + gf = loadReplay.ReadGF(); + BOOST_TEST(gf == 2u); + { + const auto cmd = get(loadReplay.ReadCommand()); + BOOST_TEST(cmd.player == 0); + BOOST_TEST(cmd.cmds.checksum == recordedCmds.checksum); + BOOST_TEST(cmd.cmds.gcs.size() == recordedCmds.gcs.size()); + BOOST_TEST(dynamic_cast(cmd.cmds.gcs[0].get())); + BOOST_TEST(dynamic_cast(cmd.cmds.gcs[1].get())); + } + + gf = loadReplay.ReadGF(); + BOOST_TEST(gf == 2u); + { + const auto cmd = get(loadReplay.ReadCommand()); + BOOST_TEST(cmd.player == 2); + BOOST_TEST(cmd.dest == ChatDestination::Enemies); + BOOST_TEST(cmd.msg == "Hello4"); + } + + gf = loadReplay.ReadGF(); + BOOST_TEST(!gf); } } // namespace @@ -268,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(BaseSaveLoad, RandWorldFixture) s25util::time64_t saveTime = s25util::Time::CurrentTime(); BOOST_TEST_REQUIRE(save.Save(tmpFile.filePath, "MapTitle")); - BOOST_TEST_REQUIRE(save.GetSaveTime() - saveTime <= 20); // 20s difference max + BOOST_TEST(save.GetSaveTime() - saveTime <= 20); // 20s difference max const unsigned origObjNum = GameObject::GetNumObjs(); const unsigned origObjIdNum = GameObject::GetObjIDCounter(); @@ -276,17 +270,17 @@ BOOST_FIXTURE_TEST_CASE(BaseSaveLoad, RandWorldFixture) { Savegame loadSave; BOOST_TEST_REQUIRE(loadSave.Load(tmpFile.filePath, what)); - BOOST_TEST_REQUIRE(loadSave.GetSaveTime() == save.GetSaveTime()); - BOOST_TEST_REQUIRE(loadSave.GetMapName() == "MapTitle"); - BOOST_TEST_REQUIRE(loadSave.GetPlayerNames().size() == 3u); - BOOST_TEST_REQUIRE(loadSave.GetPlayerNames()[0] == "Human"); - BOOST_TEST_REQUIRE(loadSave.GetPlayerNames()[1] == "PlAI"); - BOOST_TEST_REQUIRE(loadSave.GetPlayerNames()[2] == "PlAI2"); - BOOST_TEST_REQUIRE(loadSave.start_gf == em.GetCurrentGF()); + BOOST_TEST(loadSave.GetSaveTime() == save.GetSaveTime()); + BOOST_TEST(loadSave.GetMapName() == "MapTitle"); + BOOST_TEST(loadSave.GetPlayerNames().size() == 3u); + BOOST_TEST(loadSave.GetPlayerNames()[0] == "Human"); + BOOST_TEST(loadSave.GetPlayerNames()[1] == "PlAI"); + BOOST_TEST(loadSave.GetPlayerNames()[2] == "PlAI2"); + BOOST_TEST(loadSave.start_gf == em.GetCurrentGF()); // Players are loaded with the settings if(what == SaveGameDataToLoad::Header) { - BOOST_TEST_REQUIRE(loadSave.GetNumPlayers() == 0u); + BOOST_TEST(loadSave.GetNumPlayers() == 0u); } else { BOOST_TEST_REQUIRE(loadSave.GetNumPlayers() == 4u); @@ -294,20 +288,20 @@ BOOST_FIXTURE_TEST_CASE(BaseSaveLoad, RandWorldFixture) { const BasePlayerInfo& loadPlayer = loadSave.GetPlayer(j); const BasePlayerInfo& worldPlayer = world.GetPlayer(j); - BOOST_TEST_REQUIRE(loadPlayer.ps == worldPlayer.ps); + BOOST_TEST(loadPlayer.ps == worldPlayer.ps); if(!loadPlayer.isUsed()) continue; - BOOST_TEST_REQUIRE(loadPlayer.name == worldPlayer.name); + BOOST_TEST(loadPlayer.name == worldPlayer.name); if(!loadPlayer.isHuman()) { - BOOST_TEST_REQUIRE(loadPlayer.aiInfo.type == worldPlayer.aiInfo.type); - BOOST_TEST_REQUIRE(loadPlayer.aiInfo.level == worldPlayer.aiInfo.level); + BOOST_TEST(loadPlayer.aiInfo.type == worldPlayer.aiInfo.type); + BOOST_TEST(loadPlayer.aiInfo.level == worldPlayer.aiInfo.level); } } - BOOST_TEST_REQUIRE(loadSave.ggs.speed == ggs.speed); + BOOST_TEST(loadSave.ggs.speed == ggs.speed); } if(what != SaveGameDataToLoad::All) - BOOST_TEST_REQUIRE(loadSave.sgd.GetLength() == 0u); + BOOST_TEST(loadSave.sgd.GetLength() == 0u); else { std::vector players; @@ -320,43 +314,43 @@ BOOST_FIXTURE_TEST_CASE(BaseSaveLoad, RandWorldFixture) const World& newWorld = game.world_; auto& newEm = static_cast(game.world_.GetEvMgr()); - BOOST_TEST_REQUIRE(newWorld.GetSize() == world.GetSize()); - BOOST_TEST_REQUIRE(newEm.GetCurrentGF() == em.GetCurrentGF()); - BOOST_TEST_REQUIRE(GameObject::GetNumObjs() == origObjNum); - BOOST_TEST_REQUIRE(GameObject::GetObjIDCounter() == origObjIdNum); + BOOST_TEST(newWorld.GetSize() == world.GetSize()); + BOOST_TEST(newEm.GetCurrentGF() == em.GetCurrentGF()); + BOOST_TEST(GameObject::GetNumObjs() == origObjNum); + BOOST_TEST(GameObject::GetObjIDCounter() == origObjIdNum); std::vector worldEvs = em.GetEvents(); std::vector loadEvs = newEm.GetEvents(); - BOOST_TEST_REQUIRE(worldEvs.size() == loadEvs.size()); + BOOST_TEST(worldEvs.size() == loadEvs.size()); for(unsigned j = 0; j < worldEvs.size(); ++j) { - BOOST_TEST_REQUIRE(worldEvs[j]->GetInstanceId() == loadEvs[j]->GetInstanceId()); - BOOST_TEST_REQUIRE(worldEvs[j]->startGF == loadEvs[j]->startGF); - BOOST_TEST_REQUIRE(worldEvs[j]->length == loadEvs[j]->length); - BOOST_TEST_REQUIRE(worldEvs[j]->id == loadEvs[j]->id); + BOOST_TEST(worldEvs[j]->GetInstanceId() == loadEvs[j]->GetInstanceId()); + BOOST_TEST(worldEvs[j]->startGF == loadEvs[j]->startGF); + BOOST_TEST(worldEvs[j]->length == loadEvs[j]->length); + BOOST_TEST(worldEvs[j]->id == loadEvs[j]->id); } RTTR_FOREACH_PT(MapPoint, world.GetSize()) BOOST_TEST_CONTEXT("Point " << pt) { const MapNode& worldNode = world.GetNode(pt); const MapNode& loadNode = newWorld.GetNode(pt); - BOOST_TEST_REQUIRE(loadNode.roads == worldNode.roads, boost::test_tools::per_element()); - BOOST_TEST_REQUIRE(loadNode.altitude == worldNode.altitude); - BOOST_TEST_REQUIRE(loadNode.shadow == worldNode.shadow); - BOOST_TEST_REQUIRE(loadNode.t1 == worldNode.t1); - BOOST_TEST_REQUIRE(loadNode.t2 == worldNode.t2); - BOOST_TEST_REQUIRE(loadNode.resources == worldNode.resources); - BOOST_TEST_REQUIRE(loadNode.reserved == worldNode.reserved); - BOOST_TEST_REQUIRE(loadNode.owner == worldNode.owner); - BOOST_TEST_REQUIRE(loadNode.bq == worldNode.bq); - BOOST_TEST_REQUIRE(loadNode.seaId == worldNode.seaId); - BOOST_TEST_REQUIRE(loadNode.harborId == worldNode.harborId); - BOOST_TEST_REQUIRE((loadNode.obj != nullptr) == (worldNode.obj != nullptr)); + BOOST_TEST(loadNode.roads == worldNode.roads, boost::test_tools::per_element()); + BOOST_TEST(loadNode.altitude == worldNode.altitude); + BOOST_TEST(loadNode.shadow == worldNode.shadow); + BOOST_TEST(loadNode.t1 == worldNode.t1); + BOOST_TEST(loadNode.t2 == worldNode.t2); + BOOST_TEST(loadNode.resources == worldNode.resources); + BOOST_TEST(loadNode.reserved == worldNode.reserved); + BOOST_TEST(loadNode.owner == worldNode.owner); + BOOST_TEST(loadNode.bq == worldNode.bq); + BOOST_TEST(loadNode.seaId == worldNode.seaId); + BOOST_TEST(loadNode.harborId == worldNode.harborId); + BOOST_TEST((loadNode.obj != nullptr) == (worldNode.obj != nullptr)); } const auto* newUsual = newWorld.GetSpecObj(usualBldPos); BOOST_TEST_REQUIRE(newUsual); - BOOST_TEST_REQUIRE(newUsual->is_working == usualBld->is_working); - BOOST_TEST_REQUIRE(newUsual->HasWorker() == usualBld->HasWorker()); - BOOST_TEST_REQUIRE(newUsual->GetProductivity() == usualBld->GetProductivity()); + BOOST_TEST(newUsual->is_working == usualBld->is_working); + BOOST_TEST(newUsual->HasWorker() == usualBld->HasWorker()); + BOOST_TEST(newUsual->GetProductivity() == usualBld->GetProductivity()); hq = world.GetSpecObj(hqPos); BOOST_TEST_REQUIRE(hq); @@ -406,30 +400,26 @@ struct ReplayMapFixture BOOST_FIXTURE_TEST_CASE(ReplayWithMap, ReplayMapFixture) { Replay replay; - BOOST_TEST_REQUIRE(!replay.IsValid()); BOOST_TEST_REQUIRE(!replay.IsRecording()); BOOST_TEST_REQUIRE(!replay.IsReplaying()); for(const BasePlayerInfo& player : players) replay.AddPlayer(player); replay.ggs.speed = GameSpeed::VeryFast; - replay.random_init = 815; TmpFile tmpFile; BOOST_TEST_REQUIRE(tmpFile.isValid()); tmpFile.close(); // No overwrite - BOOST_TEST_REQUIRE(!replay.StartRecording(tmpFile.filePath, map)); - BOOST_TEST_REQUIRE(!replay.IsValid()); + BOOST_TEST_REQUIRE(!replay.StartRecording(tmpFile.filePath, map, 815)); BOOST_TEST_REQUIRE(!replay.IsRecording()); - BOOST_TEST_REQUIRE(!replay.IsReplaying()); + BOOST_TEST(!replay.IsReplaying()); bfs::remove(tmpFile.filePath); s25util::time64_t saveTime = s25util::Time::CurrentTime(); - BOOST_TEST_REQUIRE(replay.StartRecording(tmpFile.filePath, map)); + BOOST_TEST_REQUIRE(replay.StartRecording(tmpFile.filePath, map, 42)); BOOST_TEST(replay.GetSaveTime() - saveTime <= 20); // 20s difference max - BOOST_TEST_REQUIRE(replay.IsValid()); BOOST_TEST_REQUIRE(replay.IsRecording()); - BOOST_TEST_REQUIRE(!replay.IsReplaying()); + BOOST_TEST(!replay.IsReplaying()); GlobalGameSettings ggs = replay.ggs; Game game(ggs, 0u, players); @@ -437,8 +427,8 @@ BOOST_FIXTURE_TEST_CASE(ReplayWithMap, ReplayMapFixture) AddReplayCmds(replay, cmds); BOOST_TEST(replay.GetLastGF() == 5u); BOOST_TEST_REQUIRE(replay.StopRecording()); - BOOST_TEST_REQUIRE(!replay.IsValid()); BOOST_TEST_REQUIRE(!replay.IsRecording()); + BOOST_TEST(!replay.IsReplaying()); for(const bool loadSettings : {false, true}) { @@ -450,7 +440,7 @@ BOOST_FIXTURE_TEST_CASE(ReplayWithMap, ReplayMapFixture) BOOST_TEST(loadReplay.GetPlayerNames()[0] == "Human"); BOOST_TEST(loadReplay.GetPlayerNames()[1] == "PlAI"); BOOST_TEST(loadReplay.GetPlayerNames()[2] == "PlAI2"); - BOOST_TEST_REQUIRE(loadReplay.GetLastGF() == 5u); + BOOST_TEST(loadReplay.GetLastGF() == 5u); if(!loadSettings) { // Not loaded @@ -464,24 +454,23 @@ BOOST_FIXTURE_TEST_CASE(ReplayWithMap, ReplayMapFixture) { const BasePlayerInfo& loadPlayer = loadReplay.GetPlayer(j); const BasePlayerInfo& worldPlayer = players[j]; - BOOST_TEST_REQUIRE(loadPlayer.ps == worldPlayer.ps); + BOOST_TEST(loadPlayer.ps == worldPlayer.ps); if(!loadPlayer.isUsed()) continue; - BOOST_TEST_REQUIRE(loadPlayer.name == worldPlayer.name); + BOOST_TEST(loadPlayer.name == worldPlayer.name); if(!loadPlayer.isHuman()) { - BOOST_TEST_REQUIRE(loadPlayer.aiInfo.type == worldPlayer.aiInfo.type); - BOOST_TEST_REQUIRE(loadPlayer.aiInfo.level == worldPlayer.aiInfo.level); + BOOST_TEST(loadPlayer.aiInfo.type == worldPlayer.aiInfo.type); + BOOST_TEST(loadPlayer.aiInfo.level == worldPlayer.aiInfo.level); } } BOOST_TEST(loadReplay.ggs.speed == replay.ggs.speed); - BOOST_TEST(loadReplay.random_init == replay.random_init); + BOOST_TEST(loadReplay.getSeed() == replay.getSeed()); BOOST_TEST(newMap.type == map.type); BOOST_TEST(newMap.title == map.title); BOOST_TEST(newMap.filepath == map.filepath); BOOST_TEST(newMap.mapData.data == map.mapData.data, boost::test_tools::per_element()); BOOST_TEST(newMap.luaData.data == map.luaData.data, boost::test_tools::per_element()); - BOOST_TEST_REQUIRE(loadReplay.IsReplaying()); CheckReplayCmds(loadReplay, cmds); } @@ -505,9 +494,8 @@ BOOST_FIXTURE_TEST_CASE(BrokenReplayWithMap, ReplayMapFixture) for(const BasePlayerInfo& player : players) replay.AddPlayer(player); replay.ggs.speed = GameSpeed::VeryFast; - replay.random_init = randomInit; - BOOST_TEST_REQUIRE(replay.StartRecording(tmpFile.filePath, map)); + BOOST_TEST_REQUIRE(replay.StartRecording(tmpFile.filePath, map, randomInit)); saveTime = replay.GetSaveTime(); BOOST_TEST_REQUIRE(replay.IsRecording()); AddReplayCmds(replay, cmds); @@ -536,13 +524,12 @@ BOOST_FIXTURE_TEST_CASE(BrokenReplayWithMap, ReplayMapFixture) BOOST_TEST_REQUIRE(loadPlayer.name == worldPlayer.name); } BOOST_TEST(loadReplay.ggs.speed == ggs.speed); - BOOST_TEST(loadReplay.random_init == randomInit); + BOOST_TEST(loadReplay.getSeed() == randomInit); BOOST_TEST(newMap.type == map.type); BOOST_TEST(newMap.title == map.title); BOOST_TEST(newMap.filepath == map.filepath); BOOST_TEST(newMap.mapData.data == map.mapData.data, boost::test_tools::per_element()); BOOST_TEST(newMap.luaData.data == map.luaData.data, boost::test_tools::per_element()); - BOOST_TEST(loadReplay.IsReplaying()); CheckReplayCmds(loadReplay, cmds); } @@ -574,31 +561,28 @@ BOOST_FIXTURE_TEST_CASE(ReplayWithSavegame, RandWorldFixture) map.savegame->sgd.MakeSnapshot(*game); Replay replay; - BOOST_TEST_REQUIRE(!replay.IsValid()); BOOST_TEST_REQUIRE(!replay.IsRecording()); BOOST_TEST_REQUIRE(!replay.IsReplaying()); for(const BasePlayerInfo& player : players) replay.AddPlayer(player); replay.ggs.speed = GameSpeed::VeryFast; - replay.random_init = 815; TmpFile tmpFile; BOOST_TEST_REQUIRE(tmpFile.isValid()); tmpFile.close(); bfs::remove(tmpFile.filePath); s25util::time64_t saveTime = s25util::Time::CurrentTime(); - BOOST_TEST_REQUIRE(replay.StartRecording(tmpFile.filePath, map)); + BOOST_TEST_REQUIRE(replay.StartRecording(tmpFile.filePath, map, 815)); BOOST_TEST_REQUIRE(replay.GetSaveTime() - saveTime <= 20); // 20s difference max - BOOST_TEST_REQUIRE(replay.IsValid()); BOOST_TEST_REQUIRE(replay.IsRecording()); - BOOST_TEST_REQUIRE(!replay.IsReplaying()); + BOOST_TEST(!replay.IsReplaying()); PlayerGameCommands cmds = GetTestCommands().create(*game).result; AddReplayCmds(replay, cmds); BOOST_TEST(replay.GetLastGF() == 5u); BOOST_TEST_REQUIRE(replay.StopRecording()); - BOOST_TEST_REQUIRE(!replay.IsValid()); - BOOST_TEST_REQUIRE(!replay.IsRecording()); + BOOST_TEST(!replay.IsRecording()); + BOOST_TEST_REQUIRE(!replay.IsReplaying()); for(const bool loadSettings : {false, true}) { @@ -635,7 +619,7 @@ BOOST_FIXTURE_TEST_CASE(ReplayWithSavegame, RandWorldFixture) } } BOOST_TEST(loadReplay.ggs.speed == replay.ggs.speed); - BOOST_TEST(loadReplay.random_init == replay.random_init); + BOOST_TEST(loadReplay.getSeed() == replay.getSeed()); BOOST_TEST(newMap.type == map.type); BOOST_TEST(newMap.title == map.title); BOOST_TEST(newMap.filepath == map.filepath); @@ -688,7 +672,7 @@ BOOST_FIXTURE_TEST_CASE(SerializeHunter, EmptyWorldFixture1P) BOOST_AUTO_TEST_CASE(SerializeGameMessageChat) { - GameMessage_Chat msg(rttr::test::randomValue(0u, 10u), rttr::test::randomEnum(), "Hello"); + const GameMessage_Chat msg(rttr::test::randomValue(0u, 10u), rttr::test::randomEnum(), "Hello"); Serializer ser; msg.Serialize(ser); std::unique_ptr newMsg(GameMessage::create_game(msg.getId())); diff --git a/tests/s25Main/integration/testTrading.cpp b/tests/s25Main/integration/testTrading.cpp index 4c1569414..483f19f7f 100644 --- a/tests/s25Main/integration/testTrading.cpp +++ b/tests/s25Main/integration/testTrading.cpp @@ -14,7 +14,7 @@ #include "gameData/JobConsts.h" #include #include -#include +#include struct TradeFixture : public WorldWithGCExecution3P { diff --git a/tests/s25Main/worldFixtures/GCExecutor.h b/tests/s25Main/worldFixtures/GCExecutor.h index 77f5ced3a..75534ef0b 100644 --- a/tests/s25Main/worldFixtures/GCExecutor.h +++ b/tests/s25Main/worldFixtures/GCExecutor.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +6,6 @@ #include "GameCommand.h" #include "factories/GameCommandFactory.h" -#include "s25util/Serializer.h" #include class GCExecutor : public GameCommandFactory @@ -19,7 +18,7 @@ class GCExecutor : public GameCommandFactory bool AddGC(gc::GameCommandPtr gc) override { // Go through serialization to check if that works too - Serializer ser; + gc::Deserializer ser; gc->Serialize(ser); gc.reset(); gc = gc::GameCommand::Deserialize(ser);