1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-10 22:31:40 +02:00

Restore save compatibility with 1.6

This commit is contained in:
Ivan Savenko
2025-04-16 17:05:52 +03:00
parent 77845f74bc
commit a43c3fcb31
27 changed files with 429 additions and 140 deletions

View File

@@ -14,10 +14,7 @@
#include "bonuses/Bonus.h" #include "bonuses/Bonus.h"
#include "bonuses/CBonusSystemNode.h" #include "bonuses/CBonusSystemNode.h"
#include "GameConstants.h"
#include "GameCallbackHolder.h"
#include "IHandlerBase.h" #include "IHandlerBase.h"
#include "serializer/Serializeable.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@@ -180,82 +177,4 @@ private:
void loadComponents(CArtifact * art, const JsonNode & node); 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 <typename Handler> void serialize(Handler & h)
{
h & artifactID;
h & locked;
}
};
class DLL_LINKAGE CArtifactSet : public virtual Serializeable
{
public:
using ArtPlacementMap = std::map<const CArtifactInstance*, ArtifactPosition>;
std::vector<ArtSlotInfo> artifactsInBackpack; //hero's artifacts from bag
std::map<ArtifactPosition, ArtSlotInfo> artifactsWorn; //map<position,artifact_id>; 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 <typename Handler> 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 VCMI_LIB_NAMESPACE_END

View File

@@ -11,6 +11,7 @@
#include "StdInc.h" #include "StdInc.h"
#include "CArtifactInstance.h" #include "CArtifactInstance.h"
#include "CArtifactSet.h"
#include "ArtifactUtils.h" #include "ArtifactUtils.h"
#include "CArtHandler.h" #include "CArtHandler.h"
#include "IGameCallback.h" #include "IGameCallback.h"
@@ -189,4 +190,11 @@ void CArtifactInstance::attachToBonusSystem(CGameState * gs)
attachToSource(*gs->getArtInstance(part.artifactID)); attachToSource(*gs->getArtInstance(part.artifactID));
} }
void CArtifactInstance::saveCompatibilityFixArtifactID(std::shared_ptr<CArtifactInstance> 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 VCMI_LIB_NAMESPACE_END

View File

@@ -10,41 +10,40 @@
#pragma once #pragma once
#include "bonuses/CBonusSystemNode.h" #include "bonuses/CBonusSystemNode.h"
#include "CArtHandler.h" #include "GameCallbackHolder.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct ArtifactLocation; struct ArtifactLocation;
class CGameState; class CGameState;
class CArtifactSet;
class DLL_LINKAGE CCombinedArtifactInstance : public GameCallbackHolder class DLL_LINKAGE CCombinedArtifactInstance : public GameCallbackHolder
{ {
protected: protected:
using GameCallbackHolder::GameCallbackHolder; using GameCallbackHolder::GameCallbackHolder;
public: public:
using ArtPlacementMap = std::map<const CArtifactInstance*, ArtifactPosition>;
struct PartInfo : public GameCallbackHolder struct PartInfo : public GameCallbackHolder
{ {
explicit PartInfo(IGameCallback * cb); explicit PartInfo(IGameCallback * cb);
PartInfo(const CArtifactInstance * artifact, ArtifactPosition slot); PartInfo(const CArtifactInstance * artifact, ArtifactPosition slot);
ArtifactInstanceID artifactID; ArtifactInstanceID artifactID;
ArtifactPosition slot; ArtifactPosition slot;
const CArtifactInstance * getArtifact() const; const CArtifactInstance * getArtifact() const;
template <typename Handler> void serialize(Handler & h) template <typename Handler>
{ void serialize(Handler & h);
h & artifactID;
h & slot;
}
}; };
void addPart(const CArtifactInstance * art, const ArtifactPosition & slot); void addPart(const CArtifactInstance * art, const ArtifactPosition & slot);
// Checks if supposed part inst is part of this combined art inst // Checks if supposed part inst is part of this combined art inst
bool isPart(const CArtifactInstance * supposedPart) const; bool isPart(const CArtifactInstance * supposedPart) const;
bool hasParts() const; bool hasParts() const;
const std::vector<PartInfo> & getPartsInfo() const; const std::vector<PartInfo> & getPartsInfo() const;
void addPlacementMap(const CArtifactSet::ArtPlacementMap & placementMap); void addPlacementMap(const ArtPlacementMap & placementMap);
template <typename Handler> void serialize(Handler & h) template <typename Handler> void serialize(Handler & h)
{ {
@@ -86,6 +85,8 @@ public:
ArtifactInstanceID getId() const; ArtifactInstanceID getId() const;
void setId(ArtifactInstanceID id); void setId(ArtifactInstanceID id);
static void saveCompatibilityFixArtifactID(std::shared_ptr<CArtifactInstance> self);
bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE, bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE,
bool assumeDestRemoved = false) const; bool assumeDestRemoved = false) const;
bool isCombined() const; bool isCombined() const;
@@ -106,4 +107,22 @@ public:
} }
}; };
template <typename Handler>
void CCombinedArtifactInstance::PartInfo::serialize(Handler & h)
{
if (h.saving || h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & artifactID;
}
else
{
std::shared_ptr<CArtifactInstance> pointer;
h & pointer;
if (pointer->getId() == ArtifactInstanceID())
CArtifactInstance::saveCompatibilityFixArtifactID(pointer);
artifactID = pointer->getId();
}
h & slot;
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

109
lib/CArtifactSet.h Normal file
View File

@@ -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 <typename Handler> void serialize(Handler & h)
{
if (h.saving || h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & artifactID;
}
else
{
std::shared_ptr<CArtifactInstance> 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<const CArtifactInstance*, ArtifactPosition>;
std::vector<ArtSlotInfo> artifactsInBackpack; //hero's artifacts from bag
std::map<ArtifactPosition, ArtSlotInfo> artifactsWorn; //map<position,artifact_id>; 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 <typename Handler> 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

View File

@@ -9,7 +9,7 @@
*/ */
#pragma once #pragma once
#include "CArtHandler.h" #include "CArtifactSet.h"
#include "CArtifactInstance.h" #include "CArtifactInstance.h"
#include "CCreatureHandler.h" #include "CCreatureHandler.h"
#include "GameCallbackHolder.h" #include "GameCallbackHolder.h"
@@ -18,6 +18,7 @@
#include "bonuses/BonusCache.h" #include "bonuses/BonusCache.h"
#include "bonuses/CBonusSystemNode.h" #include "bonuses/CBonusSystemNode.h"
#include "serializer/Serializeable.h" #include "serializer/Serializeable.h"
#include "mapObjects/CGObjectInstance.h"
#include <vcmi/Entity.h> #include <vcmi/Entity.h>
@@ -98,7 +99,17 @@ public:
h & static_cast<CBonusSystemNode&>(*this); h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CStackBasicDescriptor&>(*this); h & static_cast<CStackBasicDescriptor&>(*this);
h & static_cast<CArtifactSet&>(*this); h & static_cast<CArtifactSet&>(*this);
h & armyInstanceID;
if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & armyInstanceID;
}
else
{
std::shared_ptr<CGObjectInstance> army;
h & army;
armyInstanceID = army->id;
}
h & experience; h & experience;
} }

View File

@@ -692,6 +692,7 @@ set(lib_MAIN_HEADERS
CAndroidVMHelper.h CAndroidVMHelper.h
CArtHandler.h CArtHandler.h
CArtifactInstance.h CArtifactInstance.h
CArtifactSet.h
CBonusTypeHandler.h CBonusTypeHandler.h
CCreatureHandler.h CCreatureHandler.h
CCreatureSet.h CCreatureSet.h

View File

@@ -17,6 +17,7 @@
#include "TurnTimerInfo.h" #include "TurnTimerInfo.h"
#include "bonuses/Bonus.h" #include "bonuses/Bonus.h"
#include "bonuses/CBonusSystemNode.h" #include "bonuses/CBonusSystemNode.h"
#include "mapObjects/CGObjectInstance.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@@ -117,7 +118,16 @@ public:
h & status; h & status;
h & turnTimer; h & turnTimer;
h & *playerLocalSettings; h & *playerLocalSettings;
h & ownedObjects; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
h & ownedObjects;
else
{
std::vector<std::shared_ptr<CGObjectInstance>> objectPtrs;
h & objectPtrs;
for (const auto & ptr : objectPtrs)
ownedObjects.push_back(ptr->id);
}
h & quests; h & quests;
h & visitedObjects; h & visitedObjects;
h & visitedObjectsGlobal; h & visitedObjectsGlobal;

View File

@@ -1571,6 +1571,8 @@ void CGameState::buildBonusSystemTree()
void CGameState::restoreBonusSystemTree() void CGameState::restoreBonusSystemTree()
{ {
heroesPool->setGameState(this);
buildGlobalTeamPlayerTree(); buildGlobalTeamPlayerTree();
for(auto & armed : map->getObjects<CArmedInstance>()) for(auto & armed : map->getObjects<CArmedInstance>())
armed->restoreBonusSystem(this); armed->restoreBonusSystem(this);
@@ -1759,13 +1761,35 @@ void CGameState::loadGame(CLoadFile & file)
logGlobal->info("Loading game state..."); logGlobal->info("Loading game state...");
CMapHeader dummyHeader; CMapHeader dummyHeader;
StartInfo dummyStartInfo; auto startInfo = std::make_shared<StartInfo>();
ActiveModsInSaveList dummyActiveMods; ActiveModsInSaveList dummyActiveMods;
file.load(dummyHeader); file.load(dummyHeader);
file.load(dummyStartInfo); if (file.hasFeature(ESerializationVersion::NO_RAW_POINTERS_IN_SERIALIZER))
file.load(dummyActiveMods); {
file.load(*this); 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 VCMI_LIB_NAMESPACE_END

View File

@@ -49,10 +49,15 @@ class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeabl
{ {
friend class CGameStateCampaign; friend class CGameStateCampaign;
std::unique_ptr<StartInfo> initialOpts; //copy of settings received from pregame (not randomized) std::shared_ptr<StartInfo> initialOpts; //copy of settings received from pregame (not randomized)
std::unique_ptr<StartInfo> scenarioOps; std::shared_ptr<StartInfo> scenarioOps;
std::unique_ptr<CMap> map; std::unique_ptr<CMap> map;
void saveCompatibilityRegisterMissingArtifacts();
public: public:
ArtifactInstanceID saveCompatibilityLastAllocatedArtifactID;
std::vector<std::shared_ptr<CArtifactInstance>> saveCompatibilityUnregisteredArtifacts;
/// Stores number of times each artifact was placed on map via randomization /// Stores number of times each artifact was placed on map via randomization
std::map<ArtifactID, int> allocatedArtifacts; std::map<ArtifactID, int> allocatedArtifacts;
@@ -180,9 +185,14 @@ public:
h & actingPlayers; h & actingPlayers;
h & day; h & day;
h & map; h & map;
if (!h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
saveCompatibilityRegisterMissingArtifacts();
h & players; h & players;
h & teams; h & teams;
h & *heroesPool; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
h & *heroesPool;
else
h & heroesPool;
h & globalEffects; h & globalEffects;
h & currentRumor; h & currentRumor;
h & campaign; h & campaign;

View File

@@ -21,6 +21,11 @@ TavernHeroesPool::TavernHeroesPool(CGameState * owner)
: owner(owner) : owner(owner)
{} {}
void TavernHeroesPool::setGameState(CGameState * newOwner)
{
owner = newOwner;
}
std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() const std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() const
{ {
std::map<HeroTypeID, CGHeroInstance*> pool; std::map<HeroTypeID, CGHeroInstance*> pool;

View File

@@ -11,6 +11,7 @@
#include "../GameConstants.h" #include "../GameConstants.h"
#include "TavernSlot.h" #include "TavernSlot.h"
#include "../mapObjects/CGObjectInstance.h"
#include "../serializer/Serializeable.h" #include "../serializer/Serializeable.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@@ -34,7 +35,17 @@ class DLL_LINKAGE TavernHeroesPool : public Serializeable
template <typename Handler> void serialize(Handler &h) template <typename Handler> void serialize(Handler &h)
{ {
h & hero; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & hero;
}
else
{
std::shared_ptr<CGObjectInstance> pointer;
h & pointer;
hero = HeroTypeID(pointer->subID);
}
h & slot; h & slot;
h & role; h & role;
h & player; h & player;
@@ -52,8 +63,11 @@ class DLL_LINKAGE TavernHeroesPool : public Serializeable
std::vector<TavernSlot> currentTavern; std::vector<TavernSlot> currentTavern;
public: public:
TavernHeroesPool() = default;
TavernHeroesPool(CGameState * owner); TavernHeroesPool(CGameState * owner);
void setGameState(CGameState * owner);
/// Returns heroes currently available in tavern of a specific player /// Returns heroes currently available in tavern of a specific player
std::vector<const CGHeroInstance *> getHeroesFor(PlayerColor color) const; std::vector<const CGHeroInstance *> getHeroesFor(PlayerColor color) const;
@@ -80,7 +94,15 @@ public:
template <typename Handler> void serialize(Handler &h) template <typename Handler> void serialize(Handler &h)
{ {
h & heroesPool; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
h & heroesPool;
else
{
std::map<HeroTypeID, std::shared_ptr<CGObjectInstance>> objectPtrs;
h & objectPtrs;
for (const auto & ptr : objectPtrs)
heroesPool.push_back(ptr.first);
}
h & perPlayerAvailability; h & perPlayerAvailability;
h & currentTavern; h & currentTavern;
} }

View File

@@ -378,8 +378,23 @@ public:
h & patrol; h & patrol;
h & moveDir; h & moveDir;
h & skillsInfo; h & skillsInfo;
h & visitedTown;
h & boardedBoat; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & visitedTown;
h & boardedBoat;
}
else
{
std::shared_ptr<CGObjectInstance> ptrTown;
std::shared_ptr<CGObjectInstance> ptrBoat;
h & ptrTown;
h & ptrBoat;
visitedTown = ptrTown ? ptrTown->id : ObjectInstanceID();
boardedBoat = ptrBoat ? ptrBoat->id : ObjectInstanceID();
}
h & commander; h & commander;
h & visitedObjects; h & visitedObjects;
} }

View File

@@ -84,8 +84,23 @@ public:
h & built; h & built;
h & destroyed; h & destroyed;
h & identifier; h & identifier;
h & garrisonHero;
h & visitingHero; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & garrisonHero;
h & visitingHero;
}
else
{
std::shared_ptr<CGObjectInstance> ptrGarr;
std::shared_ptr<CGObjectInstance> ptrVisit;
h & ptrGarr;
h & ptrVisit;
garrisonHero = ptrGarr ? ptrGarr->id : ObjectInstanceID();
visitingHero = ptrVisit ? ptrVisit->id : ObjectInstanceID();
}
h & alignmentToPlayer; h & alignmentToPlayer;
h & forbiddenBuildings; h & forbiddenBuildings;
h & builtBuildings; h & builtBuildings;

View File

@@ -416,7 +416,7 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
} }
IQuestObject::IQuestObject() IQuestObject::IQuestObject()
:quest(std::make_unique<CQuest>()) :quest(std::make_shared<CQuest>())
{} {}
IQuestObject::~IQuestObject() = default; IQuestObject::~IQuestObject() = default;

View File

@@ -118,7 +118,7 @@ public:
class DLL_LINKAGE IQuestObject class DLL_LINKAGE IQuestObject
{ {
std::unique_ptr<CQuest> quest; std::shared_ptr<CQuest> quest; // TODO: not actually shared, replace with unique_ptr once 1.6 save compat is not needed
public: public:
IQuestObject(); IQuestObject();
virtual ~IQuestObject(); virtual ~IQuestObject();

View File

@@ -11,7 +11,7 @@
#include "../networkPacks/TradeItem.h" #include "../networkPacks/TradeItem.h"
#include "../constants/Enumerations.h" #include "../constants/Enumerations.h"
#include "../CArtHandler.h" #include "../CArtifactSet.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN

View File

@@ -119,7 +119,18 @@ public:
{ {
h & static_cast<CArmedInstance&>(*this); h & static_cast<CArmedInstance&>(*this);
h & message; h & message;
h & storedArtifact; if (h.saving || h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & storedArtifact;
}
else
{
std::shared_ptr<CArtifactInstance> pointer;
h & pointer;
if (pointer->getId() == ArtifactInstanceID())
CArtifactInstance::saveCompatibilityFixArtifactID(pointer);
storedArtifact = pointer->getId();
}
} }
protected: protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override; void serializeJsonOptions(JsonSerializeFormat & handler) override;
@@ -312,7 +323,17 @@ public:
h & static_cast<CGObjectInstance&>(*this); h & static_cast<CGObjectInstance&>(*this);
h & static_cast<CBonusSystemNode&>(*this); h & static_cast<CBonusSystemNode&>(*this);
h & direction; h & direction;
h & boardedHeroID; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & boardedHeroID;
}
else
{
std::shared_ptr<CGObjectInstance> ptr;
h & ptr;
boardedHeroID = ptr ? ptr->id : ObjectInstanceID();
}
h & layer; h & layer;
h & onboardAssaultAllowed; h & onboardAssaultAllowed;
h & onboardVisitAllowed; h & onboardVisitAllowed;

View File

@@ -10,24 +10,28 @@
#include "StdInc.h" #include "StdInc.h"
#include "CMap.h" #include "CMap.h"
#include "CMapEditManager.h"
#include "CMapOperation.h"
#include "../CArtHandler.h" #include "../CArtHandler.h"
#include "../GameLibrary.h"
#include "../CCreatureHandler.h" #include "../CCreatureHandler.h"
#include "../CSkillHandler.h"
#include "../GameLibrary.h"
#include "../GameSettings.h" #include "../GameSettings.h"
#include "../IGameCallback.h"
#include "../RiverHandler.h" #include "../RiverHandler.h"
#include "../RoadHandler.h" #include "../RoadHandler.h"
#include "../TerrainHandler.h" #include "../TerrainHandler.h"
#include "../entities/hero/CHeroHandler.h" #include "../entities/hero/CHeroHandler.h"
#include "../gameState/CGameState.h"
#include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CGTownInstance.h"
#include "../mapObjects/CQuest.h" #include "../mapObjects/CQuest.h"
#include "../mapObjects/ObjectTemplate.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 "../serializer/JsonSerializeFormat.h"
#include "../spells/CSpellHandler.h"
#include "../texts/CGeneralTextHandler.h"
#include <vstd/RNG.h> #include <vstd/RNG.h>
@@ -930,4 +934,16 @@ const CGObjectInstance * CMap::getObject(ObjectInstanceID obj) const
return objects.at(obj).get(); return objects.at(obj).get();
} }
void CMap::saveCompatibilityStoreAllocatedArtifactID()
{
if (!artInstances.empty())
cb->gameState()->saveCompatibilityLastAllocatedArtifactID = artInstances.back()->getId();
}
void CMap::saveCompatibilityAddMissingArtifact(std::shared_ptr<CArtifactInstance> artifact)
{
assert(artifact->getId().getNum() == artInstances.size());
artInstances.push_back(artifact);
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@@ -13,6 +13,7 @@
#include "CMapDefines.h" #include "CMapDefines.h"
#include "CMapHeader.h" #include "CMapHeader.h"
#include "../mapObjects/CGObjectInstance.h"
#include "../GameCallbackHolder.h" #include "../GameCallbackHolder.h"
#include "../networkPacks/TradeItem.h" #include "../networkPacks/TradeItem.h"
@@ -95,6 +96,7 @@ public:
void calculateGuardingGreaturePositions(); void calculateGuardingGreaturePositions();
void saveCompatibilityAddMissingArtifact(std::shared_ptr<CArtifactInstance> artifact);
CArtifactInstance * createScroll(const SpellID & spellId); CArtifactInstance * createScroll(const SpellID & spellId);
CArtifactInstance * createArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE); CArtifactInstance * createArtifact(const ArtifactID & artId, const SpellID & spellId = SpellID::NONE);
CArtifactInstance * createSingleArtifact(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; const IGameSettings & getSettings() const;
void postInitialize(); void postInitialize();
void saveCompatibilityStoreAllocatedArtifactID();
private: private:
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground /// 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 & grailPos;
h & artInstances; h & artInstances;
if (!h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
saveCompatibilityStoreAllocatedArtifactID();
std::vector< std::shared_ptr<CQuest> > quests;
h & quests;
}
h & heroesPool;
//TODO: viccondetails //TODO: viccondetails
h & terrain; h & terrain;
h & guardingCreaturePositions; h & guardingCreaturePositions;
h & objects; h & objects;
h & heroesOnMap; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
h & heroesPool; h & heroesOnMap;
else
{
std::vector<std::shared_ptr<CGObjectInstance>> 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 & teleportChannels;
h & towns; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
h & towns;
else
{
std::vector<std::shared_ptr<CGObjectInstance>> objectPtrs;
h & objectPtrs;
for (const auto & ptr : objectPtrs)
towns.push_back(ptr->id);
}
h & artInstances; h & artInstances;
// static members // static members

View File

@@ -14,6 +14,7 @@
#include "../texts/MetaString.h" #include "../texts/MetaString.h"
#include "../GameLibrary.h" #include "../GameLibrary.h"
#include "../TerrainHandler.h" #include "../TerrainHandler.h"
#include "../mapObjects/CGObjectInstance.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@@ -139,8 +140,24 @@ struct DLL_LINKAGE TerrainTile
h & roadType; h & roadType;
h & roadDir; h & roadDir;
h & extTileFlags; h & extTileFlags;
h & visitableObjects;
h & blockingObjects; if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
{
h & visitableObjects;
h & blockingObjects;
}
else
{
std::vector<std::shared_ptr<CGObjectInstance>> 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);
}
} }
}; };

View File

@@ -64,7 +64,11 @@ void CMapInfo::saveInit(const ResourcePath & file)
mapHeader = std::make_unique<CMapHeader>(); mapHeader = std::make_unique<CMapHeader>();
scenarioOptionsOfSave = std::make_unique<StartInfo>(); scenarioOptionsOfSave = std::make_unique<StartInfo>();
lf.load(*mapHeader); 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(); fileURI = file.getName();
originalFileURI = file.getOriginalName(); originalFileURI = file.getOriginalName();
fullFileURI = getFullFileURI(file); fullFileURI = getFullFileURI(file);

View File

@@ -2459,15 +2459,13 @@ ArtSlotInfo::ArtSlotInfo(const CArtifactInstance * artifact, bool locked)
const CArtifactInstance * ArtSlotInfo::getArt() const const CArtifactInstance * ArtSlotInfo::getArt() const
{ {
if(locked || !artifactID.hasValue()) if(!artifactID.hasValue())
return nullptr; return nullptr;
return cb->getArtInstance(artifactID); return cb->getArtInstance(artifactID);
} }
ArtifactInstanceID ArtSlotInfo::getID() const ArtifactInstanceID ArtSlotInfo::getID() const
{ {
if(locked)
return {};
return artifactID; return artifactID;
} }

View File

@@ -44,6 +44,12 @@ public:
{ {
loadedPointers.clear(); loadedPointers.clear();
loadedSharedPointers.clear(); loadedSharedPointers.clear();
loadedUniquePointers.clear();
}
bool hasFeature(Version v) const
{
return version >= v;
} }
private: private:
@@ -51,6 +57,7 @@ private:
std::vector<std::string> loadedStrings; std::vector<std::string> loadedStrings;
std::map<uint32_t, Serializeable *> loadedPointers; std::map<uint32_t, Serializeable *> loadedPointers;
std::set<Serializeable *> loadedUniquePointers;
std::map<const Serializeable *, std::shared_ptr<Serializeable>> loadedSharedPointers; std::map<const Serializeable *, std::shared_ptr<Serializeable>> loadedSharedPointers;
IBinaryReader * reader; IBinaryReader * reader;
@@ -156,6 +163,7 @@ private:
{ {
uint8_t read; uint8_t read;
load(read); load(read);
assert(read == 0 || read == 1);
data = static_cast<bool>(read); data = static_cast<bool>(read);
} }
@@ -168,16 +176,17 @@ private:
data.resize(length, T(cb)); data.resize(length, T(cb));
else else
data.resize(length); data.resize(length);
for(uint32_t i = 0; i < length; i++)
load(data[i]); for(uint32_t i=0;i<length;i++)
load( data[i]);
} }
template<typename T, size_t N> template <typename T, size_t N>
void load(boost::container::small_vector<T, N> & data) void load(boost::container::small_vector<T, N>& data)
{ {
uint32_t length = readAndCheckLength(); uint32_t length = readAndCheckLength();
data.resize(length); data.resize(length);
for(uint32_t i = 0; i < length; i++) for (uint32_t i = 0; i < length; i++)
load(data[i]); load(data[i]);
} }
@@ -212,6 +221,9 @@ private:
// We already got this pointer // We already got this pointer
// Cast it in case we are loading it to a non-first base pointer // Cast it in case we are loading it to a non-first base pointer
data = dynamic_cast<T>(i->second); data = dynamic_cast<T>(i->second);
if (vstd::contains(loadedUniquePointers, data))
throw std::runtime_error("Attempt to deserialize duplicated unique_ptr!");
return; return;
} }
} }
@@ -236,7 +248,10 @@ private:
data = nullptr; data = nullptr;
return; return;
} }
auto dataNonConst = dynamic_cast<ncpT *>(app->createPtr(*this, cb)); auto createdPtr = app->createPtr(*this, cb);
auto dataNonConst = dynamic_cast<ncpT *>(createdPtr);
assert(createdPtr);
assert(dataNonConst);
data = dataNonConst; data = dataNonConst;
ptrAllocated(data, pid); ptrAllocated(data, pid);
app->loadPtr(*this, cb, dataNonConst); app->loadPtr(*this, cb, dataNonConst);
@@ -300,6 +315,7 @@ private:
T * internalPtr; T * internalPtr;
loadRawPointer(internalPtr); loadRawPointer(internalPtr);
data.reset(internalPtr); data.reset(internalPtr);
loadedUniquePointers.insert(internalPtr);
} }
template<typename T, size_t N> template<typename T, size_t N>
@@ -321,9 +337,8 @@ private:
data.insert(ins); data.insert(ins);
} }
} }
template <typename T, typename U>
template<typename T, typename U> void load(std::unordered_set<T, U> &data)
void load(std::unordered_set<T, U> & data)
{ {
uint32_t length = readAndCheckLength(); uint32_t length = readAndCheckLength();
data.clear(); data.clear();

View File

@@ -47,6 +47,11 @@ public:
savedPointers.clear(); savedPointers.clear();
} }
bool hasFeature(Version v) const
{
return version >= v;
}
private: private:
std::map<std::string, uint32_t> savedStrings; std::map<std::string, uint32_t> savedStrings;
std::map<const Serializeable*, uint32_t> savedPointers; std::map<const Serializeable*, uint32_t> savedPointers;
@@ -97,6 +102,7 @@ private:
void save(const T & data) void save(const T & data)
{ {
uint8_t writ = static_cast<uint8_t>(data); uint8_t writ = static_cast<uint8_t>(data);
assert(writ == 0 || writ == 1);
save(writ); save(writ);
} }

View File

@@ -12,6 +12,13 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
template <typename From, typename To>
struct static_caster
{
To operator()(From p) {return static_cast<To>(p);}
};
CLoadFile::CLoadFile(const boost::filesystem::path & fname, IGameCallback * cb) CLoadFile::CLoadFile(const boost::filesystem::path & fname, IGameCallback * cb)
: serializer(this) : serializer(this)
, fName(fname.string()) , 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!"); throw std::runtime_error("Error: cannot open file '" + fName + "' for reading!");
//we can read //we can read
char buffer[4]; char magic[4];
sfile.read(buffer, 4); sfile.read(magic, 4);
if(std::memcmp(buffer, "VCMI", 4) != 0) if(std::memcmp(magic, "VCMI", 4) != 0)
throw std::runtime_error("Error: '" + fName + "' is not a VCMI file!"); throw std::runtime_error("Error: '" + fName + "' is not a VCMI file!");
serializer & serializer.version; sfile.read(reinterpret_cast<char*>(&serializer.version), sizeof(serializer.version));
if(serializer.version < ESerializationVersion::MINIMAL) if(serializer.version < ESerializationVersion::MINIMAL)
throw std::runtime_error("Error: too old file format detected in '" + fName + "'!"); throw std::runtime_error("Error: too old file format detected in '" + fName + "'!");

View File

@@ -28,9 +28,14 @@ public:
template<class T> template<class T>
void load(T & data) void load(T & data)
{ {
static_assert(is_serializeable<BinaryDeserializer, T>::value, "This class can't be deserialized (possible pointer?)"); //static_assert(is_serializeable<BinaryDeserializer, T>::value, "This class can't be deserialized (possible pointer?)");
serializer & data; serializer & data;
} }
bool hasFeature(BinaryDeserializer::Version v) const
{
return serializer.version >= v;
}
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@@ -32,12 +32,12 @@ enum class ESerializationVersion : int32_t
NONE = 0, NONE = 0,
RELEASE_160 = 873, RELEASE_160 = 873,
MINIMAL = RELEASE_160,
MAP_HEADER_DISPOSED_HEROES, // map header contains disposed heroes list 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 NO_RAW_POINTERS_IN_SERIALIZER, // large rework that removed all non-owning pointers from serializer
CURRENT = NO_RAW_POINTERS_IN_SERIALIZER, CURRENT = NO_RAW_POINTERS_IN_SERIALIZER,
MINIMAL = CURRENT,
}; };
static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!"); static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");