1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Started working on objects serialization

This commit is contained in:
AlexVinS 2015-11-13 17:47:47 +03:00
parent a5b72ce593
commit 9cc3dae5fe
9 changed files with 319 additions and 66 deletions

View File

@ -136,6 +136,19 @@ CGObjectInstance::~CGObjectInstance()
{
}
const std::string & CGObjectInstance::getStringId() const
{
//todo: getStringId use real object type
if(stringId == "")
{
boost::format fmt("%s_%d");
fmt % "object" % id.getNum();
stringId = fmt.str();
}
return stringId;
}
void CGObjectInstance::setOwner(PlayerColor ow)
{
tempOwner = ow;
@ -311,6 +324,50 @@ bool CGObjectInstance::passableFor(PlayerColor color) const
return false;
}
void CGObjectInstance::writeJson(JsonNode & json, bool withState) const
{
json.setType(JsonNode::DATA_STRUCT);
appearance.writeJson(json["template"]);
writeJsonOptions(json["options"]);
if(withState)
writeJsonState(json["state"]);
}
void CGObjectInstance::readJson(const JsonNode & json, bool withState)
{
if(json.getType() != JsonNode::DATA_STRUCT)
{
logGlobal->error("Invalid object instance data");
return;
}
appearance.readJson(json["template"]);
readJsonOptions(json["options"]);
if(withState)
readJsonState(json["state"]);
}
void CGObjectInstance::writeJsonOptions(JsonNode & json) const
{
}
void CGObjectInstance::readJsonOptions(const JsonNode & json)
{
}
void CGObjectInstance::writeJsonState(JsonNode & json) const
{
}
void CGObjectInstance::readJsonState(const JsonNode & json)
{
}
CGObjectInstanceBySubIdFinder::CGObjectInstanceBySubIdFinder(CGObjectInstance * obj) : obj(obj)
{

View File

@ -35,7 +35,7 @@ public:
virtual void newTurn() const;
virtual void initObj(); //synchr
virtual void setProperty(ui8 what, ui32 val);//synchr
//Called when queries created DURING HERO VISIT are resolved
//First parameter is always hero that visited object and triggered the query
virtual void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const;
@ -116,6 +116,8 @@ public:
CGObjectInstance();
~CGObjectInstance();
const std::string & getStringId() const;
/// "center" tile from which the sight distance is calculated
int3 getSightCenter() const;
@ -164,17 +166,44 @@ public:
//friend class CGameHandler;
///Entry point of binary (de-)serialization
template <typename Handler> void serialize(Handler &h, const int version)
{
h & pos & ID & subID & id & tempOwner & blockVisit & appearance;
//definfo is handled by map serializer
}
///Entry point of Json serialization
void writeJson(JsonNode & json, bool withState = false) const;
///Entry point of Json de-serialization
void readJson(const JsonNode & json, bool withState = false);
protected:
/// virtual method that allows synchronously update object state on server and all clients
virtual void setPropertyDer(ui8 what, ui32 val);
/// Gives dummy bonus from this object to hero. Can be used to track visited state
void giveDummyBonus(ObjectInstanceID heroID, ui8 duration = Bonus::ONE_DAY) const;
///Saves object-type specific options
///(!) do not forget to call inherited method first when overriding
virtual void writeJsonOptions(JsonNode & json) const;
///Loads object-type specific options
///(!) do not forget to call inherited method first when overriding
virtual void readJsonOptions(const JsonNode & json);
///Saves object-type specific state
///(!) do not forget to call inherited method first when overriding
virtual void writeJsonState(JsonNode & json) const;
///Loads object-type specific state
///(!) do not forget to call inherited method first when overriding
virtual void readJsonState(const JsonNode & json);
private:
mutable std::string stringId;///<alternate id, dynamically generated, do not serialize
};
/// function object which can be used to find an object with an specific sub ID

View File

@ -256,6 +256,12 @@ void ObjectTemplate::readJson(const JsonNode &node)
printPriority = 0; //default value
}
void ObjectTemplate::writeJson(JsonNode & node) const
{
//todo: ObjectTemplate::writeJson
}
ui32 ObjectTemplate::getWidth() const
{
return usedTiles.empty() ? 0 : usedTiles.front().size();

View File

@ -74,6 +74,7 @@ public:
void readMsk();
void readMap(CBinaryReader & reader);
void readJson(const JsonNode & node);
void writeJson(JsonNode & node) const;
bool operator==(const ObjectTemplate& ot) const { return (id == ot.id && subid == ot.subid); }

View File

@ -16,6 +16,9 @@
#include "CMap.h"
#include "../CModHandler.h"
#include "../VCMI_Lib.h"
#include "../mapObjects/ObjectTemplate.h"
#include "../mapObjects/CObjectHandler.h"
#include "../mapObjects/CObjectClassesHandler.h"
#include "../StringConstants.h"
namespace TriggeredEventsDetail
@ -117,11 +120,27 @@ namespace MapHeaderDetail
};
}
static std::string tailString(const std::string & input, char separator)
{
std::string ret;
size_t splitPos = input.find(separator);
if (splitPos != std::string::npos)
ret = input.substr(splitPos + 1);
return ret;
}
static si32 extractNumber(const std::string & input, char separator)
{
std::string tmp = tailString(input, separator);
return atoi(tmp.c_str());
}
///CMapFormatJson
const int CMapFormatJson::VERSION_MAJOR = 1;
const int CMapFormatJson::VERSION_MINOR = 0;
const std::string CMapFormatJson::HEADER_FILE_NAME = "header.json";
const std::string CMapFormatJson::OBJECTS_FILE_NAME = "objects.json";
void CMapFormatJson::readTriggeredEvents(const JsonNode & input)
{
@ -264,7 +283,7 @@ void CMapLoaderJson::readMap()
readHeader();
map->initTerrain();
readTerrain();
//TODO:readMap
readObjects();
}
void CMapLoaderJson::readHeader()
@ -542,6 +561,65 @@ void CMapLoaderJson::readTerrain()
}
CMapLoaderJson::MapObjectLoader::MapObjectLoader(CMapLoaderJson * _owner, const JsonMap::value_type& json):
owner(_owner), instance(nullptr),handler(nullptr),id(-1), jsonKey(json.first), configuration(json.second), internalId(extractNumber(jsonKey, '_'))
{
}
void CMapLoaderJson::MapObjectLoader::construct()
{
//find type handler
std::string typeName = configuration["type"].String(), subTypeName = configuration["subType"].String();
if(typeName.empty())
{
logGlobal->errorStream() << "Object type missing";
return;
}
if(subTypeName.empty())
{
logGlobal->errorStream() << "Object subType missing";
return;
}
si32 type = owner->getIdentifier("object", typeName);
// VLC->objtypeh->getHandlerFor()
//TODO:MapObjectLoader::construct()
}
void CMapLoaderJson::MapObjectLoader::configure()
{
//TODO:MapObjectLoader::configure()
}
void CMapLoaderJson::readObjects()
{
LOG_TRACE(logGlobal);
std::vector<std::unique_ptr<MapObjectLoader>> loaders;//todo: optimize MapObjectLoader memory layout
const JsonNode data = readJson(OBJECTS_FILE_NAME);
//get raw data
for(const auto & p : data.Struct())
loaders.push_back(vstd::make_unique<MapObjectLoader>(this, p));
auto sortInfos = [](const std::unique_ptr<MapObjectLoader> & lhs, const std::unique_ptr<MapObjectLoader> & rhs) -> bool
{
return lhs->internalId < rhs->internalId;
};
boost::sort(loaders, sortInfos);//this makes instance ids consistent after save-load, needed for testing
//todo: use internalId in CMap
for(auto & ptr : loaders)
ptr->construct();
//configure objects after all objects are constructed so we may resolve internal IDs even to actual pointers OTF
for(auto & ptr : loaders)
ptr->configure();
}
///CMapSaverJson
CMapSaverJson::CMapSaverJson(CInputOutputStream * stream):
CMapFormatZip(stream),
@ -576,6 +654,7 @@ void CMapSaverJson::saveMap(const std::unique_ptr<CMap>& map)
this->map = map.get();
writeHeader();
writeTerrain();
writeObjects();
}
void CMapSaverJson::writeHeader()
@ -751,3 +830,14 @@ void CMapSaverJson::writeTerrain()
addToArchive(underground, "underground_terrain.json");
}
}
void CMapSaverJson::writeObjects()
{
JsonNode data(JsonNode::DATA_STRUCT);
for(const CGObjectInstance * obj : map->objects)
obj->writeJson(data[obj->getStringId()], false);
addToArchive(data, OBJECTS_FILE_NAME);
}

View File

@ -15,21 +15,24 @@
#include "../filesystem/CZipSaver.h"
#include "../filesystem/CZipLoader.h"
#include "../GameConstants.h"
class TriggeredEvent;
struct TerrainTile;
struct PlayerInfo;
class CGObjectInstance;
class AObjectTypeHandler;
class DLL_LINKAGE CMapFormatJson
{
public:
public:
static const int VERSION_MAJOR;
static const int VERSION_MINOR;
static const std::string HEADER_FILE_NAME;
static const int VERSION_MINOR;
static const std::string HEADER_FILE_NAME;
static const std::string OBJECTS_FILE_NAME;
protected:
/** ptr to the map object which gets filled by data from the buffer or written to buffer */
CMap * map;
@ -37,8 +40,8 @@ protected:
* ptr to the map header object which gets filled by data from the buffer.
* (when loading map and mapHeader point to the same object)
*/
std::unique_ptr<CMapHeader> mapHeader;
std::unique_ptr<CMapHeader> mapHeader;
/**
* Reads triggered events, including victory/loss conditions
*/
@ -53,11 +56,11 @@ protected:
* Reads one of triggered events
*/
void readTriggeredEvent(TriggeredEvent & event, const JsonNode & source);
/**
* Writes one of triggered events
*/
void writeTriggeredEvent(const TriggeredEvent & event, JsonNode & dest);
void writeTriggeredEvent(const TriggeredEvent & event, JsonNode & dest);
};
class DLL_LINKAGE CMapPatcher : public CMapFormatJson, public IMapPatcher
@ -69,14 +72,14 @@ public:
* @param stream. A stream containing the map data.
*/
CMapPatcher(JsonNode stream);
public: //IMapPatcher
/**
* Modifies supplied map header using Json data
*
*/
void patchMapHeader(std::unique_ptr<CMapHeader> & header) override;
private:
/**
* Reads subset of header that can be replaced by patching.
@ -84,7 +87,7 @@ private:
void readPatchData();
const JsonNode input;
const JsonNode input;
};
class DLL_LINKAGE CMapFormatZip : public CMapFormatJson
@ -100,7 +103,7 @@ class DLL_LINKAGE CMapLoaderJson : public CMapFormatZip, public IMapLoader
{
public:
/**
* Default constructor.
* Constructor.
*
* @param stream a stream containing the map data
*/
@ -120,9 +123,28 @@ public:
*/
std::unique_ptr<CMapHeader> loadMapHeader() override;
private:
private:
struct MapObjectLoader
{
MapObjectLoader(CMapLoaderJson * _owner, const JsonMap::value_type & json);
CMapLoaderJson * owner;
CGObjectInstance * instance;
std::shared_ptr<AObjectTypeHandler> handler;
ObjectInstanceID id;
std::string jsonKey;//full id defined by map creator
const JsonNode & configuration;
si32 internalId;//unique part of id defined by map creator (also = quest identifier)
///constructs object (without configuration)
void construct();
///configures object
void configure();
};
si32 getIdentifier(const std::string & type, const std::string & name);
/**
* Reads complete map.
*/
@ -137,69 +159,101 @@ private:
* Reads player information.
*/
void readPlayerInfo(const JsonNode & input);
/**
* Reads one player information.
*/
void readPlayerInfo(PlayerInfo & info, const JsonNode & input);
/**
* Reads team settings to header
* @param input serialized header
*/
void readTeams(const JsonNode & input);
void readTerrainTile(const std::string & src, TerrainTile & tile);
void readTerrainLevel(const JsonNode & src, const int index);
void readTerrainLevel(const JsonNode & src, const int index);
void readTerrain();
/**
* Loads all map objects from zip archive
*/
void readObjects();
const JsonNode readJson(const std::string & archiveFilename);
CZipLoader loader;
CZipLoader loader;///< object to handle zip archive operations
};
class DLL_LINKAGE CMapSaverJson : public CMapFormatZip, public IMapSaver
{
public:
/**
* Default constructor.
* Constructor.
*
* @param stream a stream to save the map to
* @param stream a stream to save the map to, will contain zip archive
*/
CMapSaverJson(CInputOutputStream * stream);
CMapSaverJson(CInputOutputStream * stream);
~CMapSaverJson();
/**
* Actually saves the VCMI/Json map into stream.
*
*/
void saveMap(const std::unique_ptr<CMap> & map) override;
*/
void saveMap(const std::unique_ptr<CMap> & map) override;
private:
/**
* Save @data as json file with specified @filename
*
*/
* Saves @data as json file with specified @filename
*/
void addToArchive(const JsonNode & data, const std::string & filename);
/**
* Saves header to zip archive
*/
void writeHeader();
/**
* Save all players info to header
* @param output serialized header
*/
* Saves all players info to header
* @param output serialized header
*/
void writePlayerInfo(JsonNode & output);
/**
* Save one player info
* @param output empty object
*/
void writePlayerInfo(const PlayerInfo & info, JsonNode & output);
void writeTeams(JsonNode & output);
/**
* Saves one player info
* @param output empty object
*/
void writePlayerInfo(const PlayerInfo & info, JsonNode & output);
/**
* Saves team settings to header
* @param output serialized header
*/
void writeTeams(JsonNode & output);
/**
* Encodes one tile into string
* @param tile tile to serialize
*/
const std::string writeTerrainTile(const TerrainTile & tile);
/**
* Saves map level into json
* @param index z coordinate
*/
JsonNode writeTerrainLevel(const int index);
/**
* Saves all terrain into zip archive
*/
void writeTerrain();
CZipSaver saver;
/**
* Saves all map objects into zip archive
*/
void writeObjects();
CZipSaver saver;///< object to handle zip archive operations
};

View File

@ -83,8 +83,8 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
BOOST_CHECK(map->getTile(int3(5, 6, 1)).terType == ETerrainType::ROCK || map->getTile(int3(7, 8, 1)).terType == ETerrainType::ROCK);
//todo: add checks here and enable, also use smaller size
#if 0
#if 0
// next check
auto map2 = make_unique<CMap>();
map2->width = 128;
@ -105,15 +105,15 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
}
catch(const std::exception & e)
{
logGlobal->errorStream() << "CMapEditManager_DrawTerrain_Type crash" << "\n" << e.what();
logGlobal->errorStream() << "CMapEditManager_DrawTerrain_Type crash" << "\n" << e.what();
throw;
}
}
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_Type finish";
}
BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
{
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_View start";
logGlobal->infoStream() << "CMapEditManager_DrawTerrain_View start";
try
{
// Load maps and json config
@ -151,8 +151,10 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
const auto & posVector = posNode.Vector();
if(posVector.size() != 3) throw std::runtime_error("A position should consist of three values x,y,z. Continue with next position.");
int3 pos(posVector[0].Float(), posVector[1].Float(), posVector[2].Float());
logGlobal->infoStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z;
#if 0
logGlobal->traceStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z;
CTerrainViewPatternUtils::printDebuggingInfoAboutTile(map.get(), pos);
#endif // 0
const auto & originalTile = originalMap->getTile(pos);
editManager->getTerrainSelection().selectRange(MapRect(pos, 1, 1));
editManager->drawTerrain(originalTile.terType, &gen);

View File

@ -148,9 +148,22 @@ void MapComparer::compareOptions()
BOOST_ERROR("Not implemented compareOptions()");
}
void MapComparer::compareObject(const CGObjectInstance * actual, const CGObjectInstance * expected)
{
BOOST_CHECK_EQUAL(actual->getStringId(), expected->getStringId());
BOOST_CHECK_EQUAL(typeid(actual).name(), typeid(expected).name());//todo: remove and use just comparison
}
void MapComparer::compareObjects()
{
BOOST_ERROR("Not implemented compareObjects()");
BOOST_CHECK_EQUAL(actual->objects.size(), expected->objects.size());
for(size_t idx = 0; idx < std::min(actual->objects.size(), expected->objects.size()); idx++)
{
BOOST_REQUIRE_EQUAL(idx, expected->objects[idx]->id.getNum());
BOOST_CHECK_EQUAL(idx, actual->objects[idx]->id.getNum());
compareObject(actual->objects[idx], expected->objects[idx]);
}
}
void MapComparer::compareTerrain()

View File

@ -7,23 +7,24 @@
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../lib/mapping/CMap.h"
struct MapComparer
{
const CMap * actual;
const CMap * expected;
void compareHeader();
void compareOptions();
void compareObject(const CGObjectInstance * actual, const CGObjectInstance * expected);
void compareObjects();
void compareTerrain();
void compare();
void compareTerrain();
void compare();
void operator() (const std::unique_ptr<CMap>& actual, const std::unique_ptr<CMap>& expected);
};