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/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 <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

View File

@@ -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<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

View File

@@ -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<const CArtifactInstance*, ArtifactPosition>;
struct PartInfo : public GameCallbackHolder
{
explicit PartInfo(IGameCallback * cb);
PartInfo(const CArtifactInstance * artifact, ArtifactPosition slot);
ArtifactInstanceID artifactID;
ArtifactPosition slot;
const CArtifactInstance * getArtifact() const;
template <typename Handler> void serialize(Handler & h)
{
h & artifactID;
h & slot;
}
template <typename Handler>
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<PartInfo> & getPartsInfo() const;
void addPlacementMap(const CArtifactSet::ArtPlacementMap & placementMap);
void addPlacementMap(const ArtPlacementMap & placementMap);
template <typename Handler> void serialize(Handler & h)
{
@@ -86,6 +85,8 @@ public:
ArtifactInstanceID getId() const;
void setId(ArtifactInstanceID id);
static void saveCompatibilityFixArtifactID(std::shared_ptr<CArtifactInstance> self);
bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE,
bool assumeDestRemoved = false) 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

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
#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 <vcmi/Entity.h>
@@ -98,7 +99,17 @@ public:
h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CStackBasicDescriptor&>(*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;
}

View File

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

View File

@@ -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<std::shared_ptr<CGObjectInstance>> objectPtrs;
h & objectPtrs;
for (const auto & ptr : objectPtrs)
ownedObjects.push_back(ptr->id);
}
h & quests;
h & visitedObjects;
h & visitedObjectsGlobal;

View File

@@ -1571,6 +1571,8 @@ void CGameState::buildBonusSystemTree()
void CGameState::restoreBonusSystemTree()
{
heroesPool->setGameState(this);
buildGlobalTeamPlayerTree();
for(auto & armed : map->getObjects<CArmedInstance>())
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<StartInfo>();
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

View File

@@ -49,10 +49,15 @@ class DLL_LINKAGE CGameState : public CNonConstInfoCallback, public Serializeabl
{
friend class CGameStateCampaign;
std::unique_ptr<StartInfo> initialOpts; //copy of settings received from pregame (not randomized)
std::unique_ptr<StartInfo> scenarioOps;
std::shared_ptr<StartInfo> initialOpts; //copy of settings received from pregame (not randomized)
std::shared_ptr<StartInfo> scenarioOps;
std::unique_ptr<CMap> map;
void saveCompatibilityRegisterMissingArtifacts();
public:
ArtifactInstanceID saveCompatibilityLastAllocatedArtifactID;
std::vector<std::shared_ptr<CArtifactInstance>> saveCompatibilityUnregisteredArtifacts;
/// Stores number of times each artifact was placed on map via randomization
std::map<ArtifactID, int> 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;

View File

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

View File

@@ -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 <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 & role;
h & player;
@@ -52,8 +63,11 @@ class DLL_LINKAGE TavernHeroesPool : public Serializeable
std::vector<TavernSlot> currentTavern;
public:
TavernHeroesPool() = default;
TavernHeroesPool(CGameState * owner);
void setGameState(CGameState * owner);
/// Returns heroes currently available in tavern of a specific player
std::vector<const CGHeroInstance *> getHeroesFor(PlayerColor color) const;
@@ -80,7 +94,15 @@ public:
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 & currentTavern;
}

View File

@@ -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<CGObjectInstance> ptrTown;
std::shared_ptr<CGObjectInstance> ptrBoat;
h & ptrTown;
h & ptrBoat;
visitedTown = ptrTown ? ptrTown->id : ObjectInstanceID();
boardedBoat = ptrBoat ? ptrBoat->id : ObjectInstanceID();
}
h & commander;
h & visitedObjects;
}

View File

@@ -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<CGObjectInstance> ptrGarr;
std::shared_ptr<CGObjectInstance> ptrVisit;
h & ptrGarr;
h & ptrVisit;
garrisonHero = ptrGarr ? ptrGarr->id : ObjectInstanceID();
visitingHero = ptrVisit ? ptrVisit->id : ObjectInstanceID();
}
h & alignmentToPlayer;
h & forbiddenBuildings;
h & builtBuildings;

View File

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

View File

@@ -118,7 +118,7 @@ public:
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:
IQuestObject();
virtual ~IQuestObject();

View File

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

View File

@@ -119,7 +119,18 @@ public:
{
h & static_cast<CArmedInstance&>(*this);
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:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
@@ -312,7 +323,17 @@ public:
h & static_cast<CGObjectInstance&>(*this);
h & static_cast<CBonusSystemNode&>(*this);
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 & onboardAssaultAllowed;
h & onboardVisitAllowed;

View File

@@ -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 <vstd/RNG.h>
@@ -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<CArtifactInstance> artifact)
{
assert(artifact->getId().getNum() == artInstances.size());
artInstances.push_back(artifact);
}
VCMI_LIB_NAMESPACE_END

View File

@@ -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<CArtifactInstance> 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<CQuest> > 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<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 & 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;
// static members

View File

@@ -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<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>();
scenarioOptionsOfSave = std::make_unique<StartInfo>();
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);

View File

@@ -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;
}

View File

@@ -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<std::string> loadedStrings;
std::map<uint32_t, Serializeable *> loadedPointers;
std::set<Serializeable *> loadedUniquePointers;
std::map<const Serializeable *, std::shared_ptr<Serializeable>> loadedSharedPointers;
IBinaryReader * reader;
@@ -156,6 +163,7 @@ private:
{
uint8_t read;
load(read);
assert(read == 0 || read == 1);
data = static_cast<bool>(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<length;i++)
load( data[i]);
}
template<typename T, size_t N>
void load(boost::container::small_vector<T, N> & data)
template <typename T, size_t N>
void load(boost::container::small_vector<T, N>& 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<T>(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<ncpT *>(app->createPtr(*this, cb));
auto createdPtr = app->createPtr(*this, cb);
auto dataNonConst = dynamic_cast<ncpT *>(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<typename T, size_t N>
@@ -321,9 +337,8 @@ private:
data.insert(ins);
}
}
template<typename T, typename U>
void load(std::unordered_set<T, U> & data)
template <typename T, typename U>
void load(std::unordered_set<T, U> &data)
{
uint32_t length = readAndCheckLength();
data.clear();

View File

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

View File

@@ -12,6 +12,13 @@
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)
: 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<char*>(&serializer.version), sizeof(serializer.version));
if(serializer.version < ESerializationVersion::MINIMAL)
throw std::runtime_error("Error: too old file format detected in '" + fName + "'!");

View File

@@ -28,9 +28,14 @@ public:
template<class T>
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;
}
bool hasFeature(BinaryDeserializer::Version v) const
{
return serializer.version >= v;
}
};
VCMI_LIB_NAMESPACE_END

View File

@@ -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!");