From a43c3fcb31f990e25edf860c70ea778e91068474 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 16 Apr 2025 17:05:52 +0300 Subject: [PATCH] Restore save compatibility with 1.6 --- lib/CArtHandler.h | 81 ------------------ lib/CArtifactInstance.cpp | 8 ++ lib/CArtifactInstance.h | 35 ++++++-- lib/CArtifactSet.h | 109 +++++++++++++++++++++++++ lib/CCreatureSet.h | 15 +++- lib/CMakeLists.txt | 1 + lib/CPlayerState.h | 12 ++- lib/gameState/CGameState.cpp | 32 +++++++- lib/gameState/CGameState.h | 16 +++- lib/gameState/TavernHeroesPool.cpp | 5 ++ lib/gameState/TavernHeroesPool.h | 26 +++++- lib/mapObjects/CGHeroInstance.h | 19 ++++- lib/mapObjects/CGTownInstance.h | 19 ++++- lib/mapObjects/CQuest.cpp | 2 +- lib/mapObjects/CQuest.h | 2 +- lib/mapObjects/IMarket.h | 2 +- lib/mapObjects/MiscObjects.h | 25 +++++- lib/mapping/CMap.cpp | 28 +++++-- lib/mapping/CMap.h | 37 ++++++++- lib/mapping/CMapDefines.h | 21 ++++- lib/mapping/CMapInfo.cpp | 6 +- lib/networkPacks/NetPacksLib.cpp | 4 +- lib/serializer/BinaryDeserializer.h | 33 ++++++-- lib/serializer/BinarySerializer.h | 6 ++ lib/serializer/CLoadFile.cpp | 16 +++- lib/serializer/CLoadFile.h | 7 +- lib/serializer/ESerializationVersion.h | 2 +- 27 files changed, 429 insertions(+), 140 deletions(-) create mode 100644 lib/CArtifactSet.h diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 522e81c18..08ef69f46 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -14,10 +14,7 @@ #include "bonuses/Bonus.h" #include "bonuses/CBonusSystemNode.h" -#include "GameConstants.h" -#include "GameCallbackHolder.h" #include "IHandlerBase.h" -#include "serializer/Serializeable.h" VCMI_LIB_NAMESPACE_BEGIN @@ -180,82 +177,4 @@ private: void loadComponents(CArtifact * art, const JsonNode & node); }; -struct DLL_LINKAGE ArtSlotInfo : public GameCallbackHolder -{ - ArtifactInstanceID artifactID; - bool locked = false; //if locked, then artifact points to the combined artifact - - explicit ArtSlotInfo(IGameCallback * cb); - ArtSlotInfo(const CArtifactInstance * artifact, bool locked); - - const CArtifactInstance * getArt() const; - ArtifactInstanceID getID() const; - - template void serialize(Handler & h) - { - h & artifactID; - h & locked; - } -}; - -class DLL_LINKAGE CArtifactSet : public virtual Serializeable -{ - -public: - using ArtPlacementMap = std::map; - - std::vector artifactsInBackpack; //hero's artifacts from bag - std::map artifactsWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 - ArtSlotInfo artifactsTransitionPos; // Used as transition position for dragAndDrop artifact exchange - - const ArtSlotInfo * getSlot(const ArtifactPosition & pos) const; - void lockSlot(const ArtifactPosition & pos); - const CArtifactInstance * getArt(const ArtifactPosition & pos, bool excludeLocked = true) const; - ArtifactInstanceID getArtID(const ArtifactPosition & pos, bool excludeLocked = true) const; - /// Looks for first artifact with given ID - ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const; - ArtifactPosition getArtPos(const CArtifactInstance * art) const; - const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const; - bool hasArt(const ArtifactID & aid, bool onlyWorn = false, bool searchCombinedParts = false) const; - bool isPositionFree(const ArtifactPosition & pos, bool onlyLockCheck = false) const; - - virtual IGameCallback * getCallback() const = 0; - virtual ArtBearer::ArtBearer bearerType() const = 0; - virtual ArtPlacementMap putArtifact(const ArtifactPosition & slot, const CArtifactInstance * art); - virtual void removeArtifact(const ArtifactPosition & slot); - CArtifactSet(IGameCallback * cb); - virtual ~CArtifactSet() = default; - - template void serialize(Handler &h) - { - h & artifactsInBackpack; - h & artifactsWorn; - } - - void artDeserializationFix(CGameState * gs, CBonusSystemNode *node); - - void serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map); - const CArtifactInstance * getCombinedArtWithPart(const ArtifactID & partId) const; - -private: - void serializeJsonHero(JsonSerializeFormat & handler, CMap * map); - void serializeJsonCreature(JsonSerializeFormat & handler); - void serializeJsonCommander(JsonSerializeFormat & handler); - - void serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map);//normal slots -}; - -// Used to try on artifacts before the claimed changes have been applied -class DLL_LINKAGE CArtifactFittingSet : public CArtifactSet, public GameCallbackHolder -{ - IGameCallback * getCallback() const final { return cb; } -public: - CArtifactFittingSet(IGameCallback *cb, ArtBearer::ArtBearer Bearer); - explicit CArtifactFittingSet(const CArtifactSet & artSet); - ArtBearer::ArtBearer bearerType() const override; - -protected: - ArtBearer::ArtBearer bearer; -}; - VCMI_LIB_NAMESPACE_END diff --git a/lib/CArtifactInstance.cpp b/lib/CArtifactInstance.cpp index cb1c2699a..0673ec83d 100644 --- a/lib/CArtifactInstance.cpp +++ b/lib/CArtifactInstance.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "CArtifactInstance.h" +#include "CArtifactSet.h" #include "ArtifactUtils.h" #include "CArtHandler.h" #include "IGameCallback.h" @@ -189,4 +190,11 @@ void CArtifactInstance::attachToBonusSystem(CGameState * gs) attachToSource(*gs->getArtInstance(part.artifactID)); } +void CArtifactInstance::saveCompatibilityFixArtifactID(std::shared_ptr self) +{ + self->cb->gameState()->saveCompatibilityLastAllocatedArtifactID = ArtifactInstanceID(self->cb->gameState()->saveCompatibilityLastAllocatedArtifactID.getNum()+1); + self->id = self->cb->gameState()->saveCompatibilityLastAllocatedArtifactID; + self->cb->gameState()->saveCompatibilityUnregisteredArtifacts.push_back(self); +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/CArtifactInstance.h b/lib/CArtifactInstance.h index 934d951cb..b070aeaca 100644 --- a/lib/CArtifactInstance.h +++ b/lib/CArtifactInstance.h @@ -10,41 +10,40 @@ #pragma once #include "bonuses/CBonusSystemNode.h" -#include "CArtHandler.h" +#include "GameCallbackHolder.h" VCMI_LIB_NAMESPACE_BEGIN struct ArtifactLocation; class CGameState; +class CArtifactSet; class DLL_LINKAGE CCombinedArtifactInstance : public GameCallbackHolder { protected: using GameCallbackHolder::GameCallbackHolder; public: + using ArtPlacementMap = std::map; + struct PartInfo : public GameCallbackHolder { explicit PartInfo(IGameCallback * cb); PartInfo(const CArtifactInstance * artifact, ArtifactPosition slot); - ArtifactInstanceID artifactID; ArtifactPosition slot; const CArtifactInstance * getArtifact() const; - template void serialize(Handler & h) - { - h & artifactID; - h & slot; - } + template + void serialize(Handler & h); }; void addPart(const CArtifactInstance * art, const ArtifactPosition & slot); // Checks if supposed part inst is part of this combined art inst bool isPart(const CArtifactInstance * supposedPart) const; bool hasParts() const; const std::vector & getPartsInfo() const; - void addPlacementMap(const CArtifactSet::ArtPlacementMap & placementMap); + void addPlacementMap(const ArtPlacementMap & placementMap); template void serialize(Handler & h) { @@ -86,6 +85,8 @@ public: ArtifactInstanceID getId() const; void setId(ArtifactInstanceID id); + static void saveCompatibilityFixArtifactID(std::shared_ptr self); + bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE, bool assumeDestRemoved = false) const; bool isCombined() const; @@ -106,4 +107,22 @@ public: } }; +template +void CCombinedArtifactInstance::PartInfo::serialize(Handler & h) +{ + if (h.saving || h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & artifactID; + } + else + { + std::shared_ptr pointer; + h & pointer; + if (pointer->getId() == ArtifactInstanceID()) + CArtifactInstance::saveCompatibilityFixArtifactID(pointer); + artifactID = pointer->getId(); + } + h & slot; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/CArtifactSet.h b/lib/CArtifactSet.h new file mode 100644 index 000000000..c9b2a8077 --- /dev/null +++ b/lib/CArtifactSet.h @@ -0,0 +1,109 @@ +/* + * CArtHandler.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "CArtHandler.h" +#include "GameCallbackHolder.h" +#include "constants/EntityIdentifiers.h" + +#include "CArtifactInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +struct DLL_LINKAGE ArtSlotInfo : public GameCallbackHolder +{ + ArtifactInstanceID artifactID; + bool locked = false; //if locked, then artifact points to the combined artifact + + explicit ArtSlotInfo(IGameCallback * cb); + ArtSlotInfo(const CArtifactInstance * artifact, bool locked); + + const CArtifactInstance * getArt() const; + ArtifactInstanceID getID() const; + + template void serialize(Handler & h) + { + if (h.saving || h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & artifactID; + } + else + { + std::shared_ptr pointer; + h & pointer; + if (pointer->getId() == ArtifactInstanceID()) + CArtifactInstance::saveCompatibilityFixArtifactID(pointer); + artifactID = pointer->getId(); + } + h & locked; + } +}; + +class DLL_LINKAGE CArtifactSet : public virtual Serializeable +{ + +public: + using ArtPlacementMap = std::map; + + std::vector artifactsInBackpack; //hero's artifacts from bag + std::map artifactsWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 + ArtSlotInfo artifactsTransitionPos; // Used as transition position for dragAndDrop artifact exchange + + const ArtSlotInfo * getSlot(const ArtifactPosition & pos) const; + void lockSlot(const ArtifactPosition & pos); + const CArtifactInstance * getArt(const ArtifactPosition & pos, bool excludeLocked = true) const; + ArtifactInstanceID getArtID(const ArtifactPosition & pos, bool excludeLocked = true) const; + /// Looks for first artifact with given ID + ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const; + ArtifactPosition getArtPos(const CArtifactInstance * art) const; + const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const; + bool hasArt(const ArtifactID & aid, bool onlyWorn = false, bool searchCombinedParts = false) const; + bool isPositionFree(const ArtifactPosition & pos, bool onlyLockCheck = false) const; + + virtual IGameCallback * getCallback() const = 0; + virtual ArtBearer::ArtBearer bearerType() const = 0; + virtual ArtPlacementMap putArtifact(const ArtifactPosition & slot, const CArtifactInstance * art); + virtual void removeArtifact(const ArtifactPosition & slot); + CArtifactSet(IGameCallback * cb); + virtual ~CArtifactSet() = default; + + template void serialize(Handler &h) + { + h & artifactsInBackpack; + h & artifactsWorn; + } + + void artDeserializationFix(CGameState * gs, CBonusSystemNode *node); + + void serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map); + const CArtifactInstance * getCombinedArtWithPart(const ArtifactID & partId) const; + +private: + void serializeJsonHero(JsonSerializeFormat & handler, CMap * map); + void serializeJsonCreature(JsonSerializeFormat & handler); + void serializeJsonCommander(JsonSerializeFormat & handler); + + void serializeJsonSlot(JsonSerializeFormat & handler, const ArtifactPosition & slot, CMap * map);//normal slots +}; + +// Used to try on artifacts before the claimed changes have been applied +class DLL_LINKAGE CArtifactFittingSet : public CArtifactSet, public GameCallbackHolder +{ + IGameCallback * getCallback() const final { return cb; } +public: + CArtifactFittingSet(IGameCallback *cb, ArtBearer::ArtBearer Bearer); + explicit CArtifactFittingSet(const CArtifactSet & artSet); + ArtBearer::ArtBearer bearerType() const override; + +protected: + ArtBearer::ArtBearer bearer; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 0f6afadb0..393c4b196 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CArtHandler.h" +#include "CArtifactSet.h" #include "CArtifactInstance.h" #include "CCreatureHandler.h" #include "GameCallbackHolder.h" @@ -18,6 +18,7 @@ #include "bonuses/BonusCache.h" #include "bonuses/CBonusSystemNode.h" #include "serializer/Serializeable.h" +#include "mapObjects/CGObjectInstance.h" #include @@ -98,7 +99,17 @@ public: h & static_cast(*this); h & static_cast(*this); h & static_cast(*this); - h & armyInstanceID; + + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & armyInstanceID; + } + else + { + std::shared_ptr army; + h & army; + armyInstanceID = army->id; + } h & experience; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 125bb9d2d..0a7fbd2a1 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -692,6 +692,7 @@ set(lib_MAIN_HEADERS CAndroidVMHelper.h CArtHandler.h CArtifactInstance.h + CArtifactSet.h CBonusTypeHandler.h CCreatureHandler.h CCreatureSet.h diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index 45724cc84..fb1c073f8 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -17,6 +17,7 @@ #include "TurnTimerInfo.h" #include "bonuses/Bonus.h" #include "bonuses/CBonusSystemNode.h" +#include "mapObjects/CGObjectInstance.h" VCMI_LIB_NAMESPACE_BEGIN @@ -117,7 +118,16 @@ public: h & status; h & turnTimer; h & *playerLocalSettings; - h & ownedObjects; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + h & ownedObjects; + else + { + std::vector> objectPtrs; + h & objectPtrs; + for (const auto & ptr : objectPtrs) + ownedObjects.push_back(ptr->id); + } + h & quests; h & visitedObjects; h & visitedObjectsGlobal; diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index d4d64ed93..97da1311b 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -1571,6 +1571,8 @@ void CGameState::buildBonusSystemTree() void CGameState::restoreBonusSystemTree() { + heroesPool->setGameState(this); + buildGlobalTeamPlayerTree(); for(auto & armed : map->getObjects()) armed->restoreBonusSystem(this); @@ -1759,13 +1761,35 @@ void CGameState::loadGame(CLoadFile & file) logGlobal->info("Loading game state..."); CMapHeader dummyHeader; - StartInfo dummyStartInfo; + auto startInfo = std::make_shared(); ActiveModsInSaveList dummyActiveMods; file.load(dummyHeader); - file.load(dummyStartInfo); - file.load(dummyActiveMods); - file.load(*this); + if (file.hasFeature(ESerializationVersion::NO_RAW_POINTERS_IN_SERIALIZER)) + { + file.load(startInfo); + file.load(dummyActiveMods); + file.load(*this); + } + else + { + bool dummyA = false; + uint32_t dummyB = 0; + uint16_t dummyC = 0; + file.load(startInfo); + file.load(dummyActiveMods); + file.load(dummyA); + file.load(dummyB); + file.load(dummyC); + file.load(*this); + } +} + +void CGameState::saveCompatibilityRegisterMissingArtifacts() +{ + for( const auto & newArtifact : saveCompatibilityUnregisteredArtifacts) + map->saveCompatibilityAddMissingArtifact(newArtifact); + saveCompatibilityUnregisteredArtifacts.clear(); } VCMI_LIB_NAMESPACE_END diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index b0d50f9f3..e967e08ad 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -49,10 +49,15 @@ class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeabl { friend class CGameStateCampaign; - std::unique_ptr initialOpts; //copy of settings received from pregame (not randomized) - std::unique_ptr scenarioOps; + std::shared_ptr initialOpts; //copy of settings received from pregame (not randomized) + std::shared_ptr scenarioOps; std::unique_ptr map; + + void saveCompatibilityRegisterMissingArtifacts(); public: + ArtifactInstanceID saveCompatibilityLastAllocatedArtifactID; + std::vector> saveCompatibilityUnregisteredArtifacts; + /// Stores number of times each artifact was placed on map via randomization std::map allocatedArtifacts; @@ -180,9 +185,14 @@ public: h & actingPlayers; h & day; h & map; + if (!h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + saveCompatibilityRegisterMissingArtifacts(); h & players; h & teams; - h & *heroesPool; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + h & *heroesPool; + else + h & heroesPool; h & globalEffects; h & currentRumor; h & campaign; diff --git a/lib/gameState/TavernHeroesPool.cpp b/lib/gameState/TavernHeroesPool.cpp index 0102baa87..877c5c5bd 100644 --- a/lib/gameState/TavernHeroesPool.cpp +++ b/lib/gameState/TavernHeroesPool.cpp @@ -21,6 +21,11 @@ TavernHeroesPool::TavernHeroesPool(CGameState * owner) : owner(owner) {} +void TavernHeroesPool::setGameState(CGameState * newOwner) +{ + owner = newOwner; +} + std::map TavernHeroesPool::unusedHeroesFromPool() const { std::map pool; diff --git a/lib/gameState/TavernHeroesPool.h b/lib/gameState/TavernHeroesPool.h index e7ab6881b..2affbc52a 100644 --- a/lib/gameState/TavernHeroesPool.h +++ b/lib/gameState/TavernHeroesPool.h @@ -11,6 +11,7 @@ #include "../GameConstants.h" #include "TavernSlot.h" +#include "../mapObjects/CGObjectInstance.h" #include "../serializer/Serializeable.h" VCMI_LIB_NAMESPACE_BEGIN @@ -34,7 +35,17 @@ class DLL_LINKAGE TavernHeroesPool : public Serializeable template void serialize(Handler &h) { - h & hero; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & hero; + } + else + { + std::shared_ptr pointer; + h & pointer; + hero = HeroTypeID(pointer->subID); + } + h & slot; h & role; h & player; @@ -52,8 +63,11 @@ class DLL_LINKAGE TavernHeroesPool : public Serializeable std::vector currentTavern; public: + TavernHeroesPool() = default; TavernHeroesPool(CGameState * owner); + void setGameState(CGameState * owner); + /// Returns heroes currently available in tavern of a specific player std::vector getHeroesFor(PlayerColor color) const; @@ -80,7 +94,15 @@ public: template void serialize(Handler &h) { - h & heroesPool; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + h & heroesPool; + else + { + std::map> objectPtrs; + h & objectPtrs; + for (const auto & ptr : objectPtrs) + heroesPool.push_back(ptr.first); + } h & perPlayerAvailability; h & currentTavern; } diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 6f607adaf..69d6468de 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -378,8 +378,23 @@ public: h & patrol; h & moveDir; h & skillsInfo; - h & visitedTown; - h & boardedBoat; + + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & visitedTown; + h & boardedBoat; + } + else + { + std::shared_ptr ptrTown; + std::shared_ptr ptrBoat; + h & ptrTown; + h & ptrBoat; + + visitedTown = ptrTown ? ptrTown->id : ObjectInstanceID(); + boardedBoat = ptrBoat ? ptrBoat->id : ObjectInstanceID(); + } + h & commander; h & visitedObjects; } diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 4aadc11d7..7b9c09fb2 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -84,8 +84,23 @@ public: h & built; h & destroyed; h & identifier; - h & garrisonHero; - h & visitingHero; + + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & garrisonHero; + h & visitingHero; + } + else + { + std::shared_ptr ptrGarr; + std::shared_ptr ptrVisit; + h & ptrGarr; + h & ptrVisit; + + garrisonHero = ptrGarr ? ptrGarr->id : ObjectInstanceID(); + visitingHero = ptrVisit ? ptrVisit->id : ObjectInstanceID(); + } + h & alignmentToPlayer; h & forbiddenBuildings; h & builtBuildings; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index c3d9c556f..c3e60e2a4 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -416,7 +416,7 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi } IQuestObject::IQuestObject() - :quest(std::make_unique()) + :quest(std::make_shared()) {} IQuestObject::~IQuestObject() = default; diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 4fad99f58..bc70959eb 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -118,7 +118,7 @@ public: class DLL_LINKAGE IQuestObject { - std::unique_ptr quest; + std::shared_ptr quest; // TODO: not actually shared, replace with unique_ptr once 1.6 save compat is not needed public: IQuestObject(); virtual ~IQuestObject(); diff --git a/lib/mapObjects/IMarket.h b/lib/mapObjects/IMarket.h index cea5c833c..a04e15b52 100644 --- a/lib/mapObjects/IMarket.h +++ b/lib/mapObjects/IMarket.h @@ -11,7 +11,7 @@ #include "../networkPacks/TradeItem.h" #include "../constants/Enumerations.h" -#include "../CArtHandler.h" +#include "../CArtifactSet.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 3ed210d17..08cf5c324 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -119,7 +119,18 @@ public: { h & static_cast(*this); h & message; - h & storedArtifact; + if (h.saving || h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & storedArtifact; + } + else + { + std::shared_ptr pointer; + h & pointer; + if (pointer->getId() == ArtifactInstanceID()) + CArtifactInstance::saveCompatibilityFixArtifactID(pointer); + storedArtifact = pointer->getId(); + } } protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; @@ -312,7 +323,17 @@ public: h & static_cast(*this); h & static_cast(*this); h & direction; - h & boardedHeroID; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & boardedHeroID; + } + else + { + std::shared_ptr ptr; + h & ptr; + boardedHeroID = ptr ? ptr->id : ObjectInstanceID(); + } + h & layer; h & onboardAssaultAllowed; h & onboardVisitAllowed; diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 3dda3e36a..892127ab0 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -10,24 +10,28 @@ #include "StdInc.h" #include "CMap.h" +#include "CMapEditManager.h" +#include "CMapOperation.h" + #include "../CArtHandler.h" -#include "../GameLibrary.h" #include "../CCreatureHandler.h" +#include "../CSkillHandler.h" +#include "../GameLibrary.h" #include "../GameSettings.h" +#include "../IGameCallback.h" #include "../RiverHandler.h" #include "../RoadHandler.h" #include "../TerrainHandler.h" + #include "../entities/hero/CHeroHandler.h" +#include "../gameState/CGameState.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/ObjectTemplate.h" -#include "../texts/CGeneralTextHandler.h" -#include "../spells/CSpellHandler.h" -#include "../CSkillHandler.h" -#include "CMapEditManager.h" -#include "CMapOperation.h" #include "../serializer/JsonSerializeFormat.h" +#include "../spells/CSpellHandler.h" +#include "../texts/CGeneralTextHandler.h" #include @@ -930,4 +934,16 @@ const CGObjectInstance * CMap::getObject(ObjectInstanceID obj) const return objects.at(obj).get(); } +void CMap::saveCompatibilityStoreAllocatedArtifactID() +{ + if (!artInstances.empty()) + cb->gameState()->saveCompatibilityLastAllocatedArtifactID = artInstances.back()->getId(); +} + +void CMap::saveCompatibilityAddMissingArtifact(std::shared_ptr artifact) +{ + assert(artifact->getId().getNum() == artInstances.size()); + artInstances.push_back(artifact); +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 42b661600..dc92ae611 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -13,6 +13,7 @@ #include "CMapDefines.h" #include "CMapHeader.h" +#include "../mapObjects/CGObjectInstance.h" #include "../GameCallbackHolder.h" #include "../networkPacks/TradeItem.h" @@ -95,6 +96,7 @@ public: void calculateGuardingGreaturePositions(); + void saveCompatibilityAddMissingArtifact(std::shared_ptr artifact); CArtifactInstance * createScroll(const SpellID & spellId); CArtifactInstance * createArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE); CArtifactInstance * createSingleArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE); @@ -240,6 +242,7 @@ public: const IGameSettings & getSettings() const; void postInitialize(); + void saveCompatibilityStoreAllocatedArtifactID(); private: /// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground @@ -261,15 +264,43 @@ public: h & grailPos; h & artInstances; + if (!h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + saveCompatibilityStoreAllocatedArtifactID(); + std::vector< std::shared_ptr > quests; + h & quests; + } + h & heroesPool; + //TODO: viccondetails h & terrain; h & guardingCreaturePositions; h & objects; - h & heroesOnMap; - h & heroesPool; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + h & heroesOnMap; + else + { + std::vector> objectPtrs; + h & objectPtrs; + for (const auto & ptr : objectPtrs) + heroesOnMap.push_back(ptr->id); + + for (auto & ptr : heroesPool) + if (vstd::contains(objects, ptr)) + ptr = nullptr; + } + h & teleportChannels; - h & towns; + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + h & towns; + else + { + std::vector> objectPtrs; + h & objectPtrs; + for (const auto & ptr : objectPtrs) + towns.push_back(ptr->id); + } h & artInstances; // static members diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/CMapDefines.h index b67db4231..d2df9203c 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/CMapDefines.h @@ -14,6 +14,7 @@ #include "../texts/MetaString.h" #include "../GameLibrary.h" #include "../TerrainHandler.h" +#include "../mapObjects/CGObjectInstance.h" VCMI_LIB_NAMESPACE_BEGIN @@ -139,8 +140,24 @@ struct DLL_LINKAGE TerrainTile h & roadType; h & roadDir; h & extTileFlags; - h & visitableObjects; - h & blockingObjects; + + if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + h & visitableObjects; + h & blockingObjects; + } + else + { + std::vector> objectPtrs; + h & objectPtrs; + for (const auto & ptr : objectPtrs) + visitableObjects.push_back(ptr->id); + h & objectPtrs; + for (const auto & ptr : objectPtrs) + blockingObjects.push_back(ptr->id); + } + + } }; diff --git a/lib/mapping/CMapInfo.cpp b/lib/mapping/CMapInfo.cpp index 2324d626b..3bc1b42ab 100644 --- a/lib/mapping/CMapInfo.cpp +++ b/lib/mapping/CMapInfo.cpp @@ -64,7 +64,11 @@ void CMapInfo::saveInit(const ResourcePath & file) mapHeader = std::make_unique(); scenarioOptionsOfSave = std::make_unique(); lf.load(*mapHeader); - lf.load(*scenarioOptionsOfSave); + if (lf.hasFeature(ESerializationVersion::NO_RAW_POINTERS_IN_SERIALIZER)) + lf.load(*scenarioOptionsOfSave); + else + lf.load(scenarioOptionsOfSave); + fileURI = file.getName(); originalFileURI = file.getOriginalName(); fullFileURI = getFullFileURI(file); diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 1aa8456d5..d1358590e 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -2459,15 +2459,13 @@ ArtSlotInfo::ArtSlotInfo(const CArtifactInstance * artifact, bool locked) const CArtifactInstance * ArtSlotInfo::getArt() const { - if(locked || !artifactID.hasValue()) + if(!artifactID.hasValue()) return nullptr; return cb->getArtInstance(artifactID); } ArtifactInstanceID ArtSlotInfo::getID() const { - if(locked) - return {}; return artifactID; } diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index 92001c375..2db96e42e 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -44,6 +44,12 @@ public: { loadedPointers.clear(); loadedSharedPointers.clear(); + loadedUniquePointers.clear(); + } + + bool hasFeature(Version v) const + { + return version >= v; } private: @@ -51,6 +57,7 @@ private: std::vector loadedStrings; std::map loadedPointers; + std::set loadedUniquePointers; std::map> loadedSharedPointers; IBinaryReader * reader; @@ -156,6 +163,7 @@ private: { uint8_t read; load(read); + assert(read == 0 || read == 1); data = static_cast(read); } @@ -168,16 +176,17 @@ private: data.resize(length, T(cb)); else data.resize(length); - for(uint32_t i = 0; i < length; i++) - load(data[i]); + + for(uint32_t i=0;i - void load(boost::container::small_vector & data) + template + void load(boost::container::small_vector& data) { uint32_t length = readAndCheckLength(); data.resize(length); - for(uint32_t i = 0; i < length; i++) + for (uint32_t i = 0; i < length; i++) load(data[i]); } @@ -212,6 +221,9 @@ private: // We already got this pointer // Cast it in case we are loading it to a non-first base pointer data = dynamic_cast(i->second); + if (vstd::contains(loadedUniquePointers, data)) + throw std::runtime_error("Attempt to deserialize duplicated unique_ptr!"); + return; } } @@ -236,7 +248,10 @@ private: data = nullptr; return; } - auto dataNonConst = dynamic_cast(app->createPtr(*this, cb)); + auto createdPtr = app->createPtr(*this, cb); + auto dataNonConst = dynamic_cast(createdPtr); + assert(createdPtr); + assert(dataNonConst); data = dataNonConst; ptrAllocated(data, pid); app->loadPtr(*this, cb, dataNonConst); @@ -300,6 +315,7 @@ private: T * internalPtr; loadRawPointer(internalPtr); data.reset(internalPtr); + loadedUniquePointers.insert(internalPtr); } template @@ -321,9 +337,8 @@ private: data.insert(ins); } } - - template - void load(std::unordered_set & data) + template + void load(std::unordered_set &data) { uint32_t length = readAndCheckLength(); data.clear(); diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h index 8790cdc16..c18f36b4b 100644 --- a/lib/serializer/BinarySerializer.h +++ b/lib/serializer/BinarySerializer.h @@ -47,6 +47,11 @@ public: savedPointers.clear(); } + bool hasFeature(Version v) const + { + return version >= v; + } + private: std::map savedStrings; std::map savedPointers; @@ -97,6 +102,7 @@ private: void save(const T & data) { uint8_t writ = static_cast(data); + assert(writ == 0 || writ == 1); save(writ); } diff --git a/lib/serializer/CLoadFile.cpp b/lib/serializer/CLoadFile.cpp index fa3bc9e3b..3a2972c99 100644 --- a/lib/serializer/CLoadFile.cpp +++ b/lib/serializer/CLoadFile.cpp @@ -12,6 +12,13 @@ VCMI_LIB_NAMESPACE_BEGIN +template +struct static_caster +{ + To operator()(From p) {return static_cast(p);} +}; + + CLoadFile::CLoadFile(const boost::filesystem::path & fname, IGameCallback * cb) : serializer(this) , fName(fname.string()) @@ -28,12 +35,13 @@ CLoadFile::CLoadFile(const boost::filesystem::path & fname, IGameCallback * cb) throw std::runtime_error("Error: cannot open file '" + fName + "' for reading!"); //we can read - char buffer[4]; - sfile.read(buffer, 4); - if(std::memcmp(buffer, "VCMI", 4) != 0) + char magic[4]; + sfile.read(magic, 4); + if(std::memcmp(magic, "VCMI", 4) != 0) throw std::runtime_error("Error: '" + fName + "' is not a VCMI file!"); - serializer & serializer.version; + sfile.read(reinterpret_cast(&serializer.version), sizeof(serializer.version)); + if(serializer.version < ESerializationVersion::MINIMAL) throw std::runtime_error("Error: too old file format detected in '" + fName + "'!"); diff --git a/lib/serializer/CLoadFile.h b/lib/serializer/CLoadFile.h index 10a0067a3..b3c7bfb6c 100644 --- a/lib/serializer/CLoadFile.h +++ b/lib/serializer/CLoadFile.h @@ -28,9 +28,14 @@ public: template void load(T & data) { - static_assert(is_serializeable::value, "This class can't be deserialized (possible pointer?)"); + //static_assert(is_serializeable::value, "This class can't be deserialized (possible pointer?)"); serializer & data; } + + bool hasFeature(BinaryDeserializer::Version v) const + { + return serializer.version >= v; + } }; VCMI_LIB_NAMESPACE_END diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index cec0d6d2e..043af7a5a 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -32,12 +32,12 @@ enum class ESerializationVersion : int32_t NONE = 0, RELEASE_160 = 873, + MINIMAL = RELEASE_160, MAP_HEADER_DISPOSED_HEROES, // map header contains disposed heroes list NO_RAW_POINTERS_IN_SERIALIZER, // large rework that removed all non-owning pointers from serializer CURRENT = NO_RAW_POINTERS_IN_SERIALIZER, - MINIMAL = CURRENT, }; static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");