1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00
vcmi/lib/serializer/JsonSerializeFormat.h
AlexVinS 0b70baa95e Spells configuration version 2 (effect-based)
* Indirect spell effects loading
* Json serializer improvements
* spell->canBeCastAt do not allow useless cast for any spell
* Added proxy caster class for spell-created obstacles
* Handle damage from spell-created obstacles inside mechanics
* Experimental GameState integration/regression tests
* Ignore mod settings and load only "vcmi" mod when running tests
* fixed https://bugs.vcmi.eu/view.php?id=2765 (with tests)
* Huge improvements of BattleAI regarding spell casts
* AI can cast almost any combat spell except TELEPORT, SACRIFICE and obstacle placement spells.
* Possible fix for https://bugs.vcmi.eu/view.php?id=1811
* CStack factored out to several classes
* [Battle] Allowed RETURN_AFTER_STRIKE effect on server side to be optional
* [Battle] Allowed BattleAction have multiple destinations
* [Spells] Converted limit|immunity to target condition
* [Spells] Use partial configuration reload for backward compatibility handling
* [Tests] Started tests for CUnitState
* Partial fixes of fire shield effect
* [Battle] Do HP calculations in 64 bits
* [BattleAI] Use threading for spell cast evaluation
* [BattleAI] Made AI be able to evaluate modified turn order (on hypothetical battle state)
* Implemented https://bugs.vcmi.eu/view.php?id=2811
* plug rare freeze when hypnotized unit shots vertically
* Correctly apply ONLY_MELEE_FIGHT / ONLY_DISTANCE_FIGHT for unit damage, attack & defense
* [BattleAI] Try to not waste a cast if battle is actually won already
* Extended JsonSerializeFormat API
* fixed https://bugs.vcmi.eu/view.php?id=2847
* Any unit effect can be now chained (not only damage like Chain Lightning)
** only damage effect for now actually uses "chainFactor"
* Possible quick fix for https://bugs.vcmi.eu/view.php?id=2860
2018-02-08 11:37:21 +03:00

445 lines
13 KiB
C++

/*
* JsonSerializeFormat.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 "../JsonNode.h"
class JsonSerializeFormat;
class JsonStructSerializer;
class JsonArraySerializer;
class DLL_LINKAGE IInstanceResolver
{
public:
virtual ~IInstanceResolver(){};
virtual si32 decode(const std::string & identifier) const = 0;
virtual std::string encode(si32 identifier) const = 0;
};
class DLL_LINKAGE JsonSerializeHelper: public boost::noncopyable
{
public:
JsonSerializeHelper(JsonSerializeHelper && other);
virtual ~JsonSerializeHelper();
JsonSerializeFormat * operator->();
protected:
JsonSerializeHelper(JsonSerializeFormat * owner_);
JsonSerializeFormat * owner;
private:
bool restoreState;
};
class DLL_LINKAGE JsonStructSerializer: public JsonSerializeHelper
{
public:
JsonStructSerializer(JsonStructSerializer && other);
~JsonStructSerializer();
protected:
JsonStructSerializer(JsonSerializeFormat * owner_);
friend class JsonSerializeFormat;
friend class JsonArraySerializer;
};
class DLL_LINKAGE JsonArraySerializer: public JsonSerializeHelper
{
public:
JsonArraySerializer(JsonArraySerializer && other);
JsonStructSerializer enterStruct(const size_t index);
JsonArraySerializer enterArray(const size_t index);
template <typename Container>
void syncSize(Container & c, JsonNode::JsonType type = JsonNode::JsonType::DATA_NULL);
///Anything int64-convertible <-> Json integer
template <typename T>
void serializeInt(const size_t index, T & value);
///String <-> Json string
void serializeString(const size_t index, std::string & value);
///vector of serializable <-> Json vector of structs
template <typename Element>
void serializeStruct(std::vector<Element> & value)
{
syncSize(value, JsonNode::JsonType::DATA_STRUCT);
for(size_t idx = 0; idx < size(); idx++)
{
auto s = enterStruct(idx);
value[idx].serializeJson(*owner);
}
}
void resize(const size_t newSize);
void resize(const size_t newSize, JsonNode::JsonType type);
size_t size() const;
protected:
JsonArraySerializer(JsonSerializeFormat * owner_);
friend class JsonSerializeFormat;
private:
const JsonNode * thisNode;
void serializeInt64(const size_t index, int64_t & value);
};
class DLL_LINKAGE JsonSerializeFormat: public boost::noncopyable
{
public:
///user-provided callback to resolve string identifier
///returns resolved identifier or -1 on error
typedef std::function<si32(const std::string &)> TDecoder;
///user-provided callback to get string identifier
///may assume that object index is valid
typedef std::function<std::string(si32)> TEncoder;
typedef std::function<void(JsonSerializeFormat &)> TSerialize;
struct LIC
{
LIC(const std::vector<bool> & Standard, const TDecoder Decoder, const TEncoder Encoder);
const std::vector<bool> & standard;
const TDecoder decoder;
const TEncoder encoder;
std::vector<bool> all, any, none;
};
struct LICSet
{
LICSet(const std::set<si32> & Standard, const TDecoder Decoder, const TEncoder Encoder);
const std::set<si32> & standard;
const TDecoder decoder;
const TEncoder encoder;
std::set<si32> all, any, none;
};
const bool saving;
JsonSerializeFormat() = delete;
virtual ~JsonSerializeFormat() = default;
virtual const JsonNode & getCurrent() = 0;
JsonStructSerializer enterStruct(const std::string & fieldName);
JsonArraySerializer enterArray(const std::string & fieldName);
///Anything comparable <-> Json bool
template <typename T>
void serializeBool(const std::string & fieldName, T & value, const T trueValue, const T falseValue, const T defaultValue)
{
boost::logic::tribool temp(boost::logic::indeterminate);
if(value == defaultValue)
;//leave as indeterminate
else if(value == trueValue)
temp = true;
else if(value == falseValue)
temp = false;
serializeInternal(fieldName, temp);
if(!saving)
{
if(boost::logic::indeterminate(temp))
value = defaultValue;
else
value = temp ? trueValue : falseValue;
}
}
///bool <-> Json bool
void serializeBool(const std::string & fieldName, bool & value);
void serializeBool(const std::string & fieldName, bool & value, const bool defaultValue);
///tribool <-> Json bool
void serializeBool(const std::string & fieldName, boost::logic::tribool & value)
{
serializeInternal(fieldName, value);
};
/** @brief Restrictive ("anyOf") simple serialization of Logical identifier condition, simple deserialization (allOf=anyOf)
*
* @param fieldName
* @param decoder resolve callback, should report errors itself and do not throw
* @param encoder encode callback, should report errors itself and do not throw
* @param value target value, must be resized properly
*
*/
virtual void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) = 0;
/** @brief Complete serialization of Logical identifier condition
*/
virtual void serializeLIC(const std::string & fieldName, LIC & value) = 0;
/** @brief Complete serialization of Logical identifier condition. (Special version)
* Assumes that all values are allowed by default, and standard contains them
*/
virtual void serializeLIC(const std::string & fieldName, LICSet & value) = 0;
///String <-> Json string
virtual void serializeString(const std::string & fieldName, std::string & value) = 0;
///si32-convertible enum <-> Json string enum
template <typename T>
void serializeEnum(const std::string & fieldName, T & value, const std::vector<std::string> & enumMap)
{
doSerializeInternal<T, T, si32>(fieldName, value, boost::none, enumMap);
};
///si32-convertible enum <-> Json string enum
template <typename T, typename U>
void serializeEnum(const std::string & fieldName, T & value, const U & defaultValue, const std::vector<std::string> & enumMap)
{
doSerializeInternal<T, U, si32>(fieldName, value, defaultValue, enumMap);
};
template <typename T, typename U, typename C>
void serializeEnum(const std::string & fieldName, T & value, const U & defaultValue, const C & enumMap)
{
std::vector<std::string> enumMapCopy;
std::copy(std::begin(enumMap), std::end(enumMap), std::back_inserter(enumMapCopy));
doSerializeInternal<T, U, si32>(fieldName, value, defaultValue, enumMapCopy);
};
///Anything double-convertible <-> Json double
template <typename T>
void serializeFloat(const std::string & fieldName, T & value)
{
doSerializeInternal<T, T, double>(fieldName, value, boost::none);
};
///Anything double-convertible <-> Json double
template <typename T, typename U>
void serializeFloat(const std::string & fieldName, T & value, const U & defaultValue)
{
doSerializeInternal<T, U, double>(fieldName, value, defaultValue);
};
///Anything int64-convertible <-> Json integer
template <typename T>
void serializeInt(const std::string & fieldName, T & value)
{
doSerializeInternal<T, T, si64>(fieldName, value, boost::none);
};
///Anything int64-convertible <-> Json integer
template <typename T, typename U>
void serializeInt(const std::string & fieldName, T & value, const U & defaultValue)
{
doSerializeInternal<T, U, si64>(fieldName, value, defaultValue);
};
///si32-convertible identifier <-> Json string
template <typename T, typename U>
void serializeId(const std::string & fieldName, T & value, const U & defaultValue, const TDecoder & decoder, const TEncoder & encoder)
{
doSerializeInternal<T, U, si32>(fieldName, value, defaultValue, decoder, encoder);
}
///si32-convertible identifier <-> Json string
template <typename T, typename U>
void serializeId(const std::string & fieldName, T & value, const U & defaultValue)
{
doSerializeInternal<T, U, si32>(fieldName, value, defaultValue, &T::decode, &T::encode);
}
///si32-convertible identifier vector <-> Json array of string
template <typename T, typename U = T>
void serializeIdArray(const std::string & fieldName, std::vector<T> & value)
{
std::vector<si32> temp;
if(saving)
{
temp.reserve(value.size());
for(const T & vitem : value)
{
si32 item = static_cast<si32>(vitem);
temp.push_back(item);
}
}
serializeInternal(fieldName, temp, &U::decode, &U::encode);
if(!saving)
{
value.clear();
value.reserve(temp.size());
for(const si32 item : temp)
{
T vitem = static_cast<T>(item);
value.push_back(vitem);
}
}
}
///si32-convertible identifier set <-> Json array of string
template <typename T, typename U = T>
void serializeIdArray(const std::string & fieldName, std::set<T> & value)
{
std::vector<si32> temp;
if(saving)
{
temp.reserve(value.size());
for(const T & vitem : value)
{
si32 item = static_cast<si32>(vitem);
temp.push_back(item);
}
}
serializeInternal(fieldName, temp, &U::decode, &U::encode);
if(!saving)
{
value.clear();
for(const si32 item : temp)
{
T vitem = static_cast<T>(item);
value.insert(vitem);
}
}
}
///bitmask <-> Json array of string
template <typename T, int Size>
void serializeIdArray(const std::string & fieldName, T & value, const T & defaultValue, const TDecoder & decoder, const TEncoder & encoder)
{
static_assert(8 * sizeof(T) >= Size, "Mask size too small");
std::vector<si32> temp;
temp.reserve(Size);
if(saving && value != defaultValue)
{
for(si32 i = 0; i < Size; i++)
if(value & (1 << i))
temp.push_back(i);
serializeInternal(fieldName, temp, decoder, encoder);
}
if(!saving)
{
serializeInternal(fieldName, temp, decoder, encoder);
if(temp.empty())
value = defaultValue;
else
{
value = 0;
for(auto i : temp)
value |= (1 << i);
}
}
}
///si32-convertible instance identifier <-> Json string
template <typename T>
void serializeInstance(const std::string & fieldName, T & value, const T & defaultValue)
{
const TDecoder decoder = std::bind(&IInstanceResolver::decode, instanceResolver, _1);
const TEncoder endoder = std::bind(&IInstanceResolver::encode, instanceResolver, _1);
serializeId<T>(fieldName, value, defaultValue, decoder, endoder);
}
///any serializable object <-> Json struct
template <typename T>
void serializeStruct(const std::string & fieldName, T & value)
{
auto guard = enterStruct(fieldName);
value.serializeJson(*this);
}
virtual void serializeRaw(const std::string & fieldName, JsonNode & value, const boost::optional<const JsonNode &> defaultValue) = 0;
protected:
JsonSerializeFormat(const IInstanceResolver * instanceResolver_, const bool saving_);
///bool <-> Json bool, indeterminate is default
virtual void serializeInternal(const std::string & fieldName, boost::logic::tribool & value) = 0;
///Numeric Id <-> String Id
virtual void serializeInternal(const std::string & fieldName, si32 & value, const boost::optional<si32> & defaultValue, const TDecoder & decoder, const TEncoder & encoder) = 0;
///Numeric Id vector <-> String Id vector
virtual void serializeInternal(const std::string & fieldName, std::vector<si32> & value, const TDecoder & decoder, const TEncoder & encoder) = 0;
///Numeric <-> Json double
virtual void serializeInternal(const std::string & fieldName, double & value, const boost::optional<double> & defaultValue) = 0;
///Numeric <-> Json integer
virtual void serializeInternal(const std::string & fieldName, si64 & value, const boost::optional<si64> & defaultValue) = 0;
///Enum/Numeric <-> Json string enum
virtual void serializeInternal(const std::string & fieldName, si32 & value, const boost::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) = 0;
virtual void pop() = 0;
virtual void pushStruct(const std::string & fieldName) = 0;
virtual void pushArray(const std::string & fieldName) = 0;
virtual void pushArrayElement(const size_t index) = 0;
virtual void resizeCurrent(const size_t newSize, JsonNode::JsonType type){};
virtual void serializeInternal(std::string & value) = 0;
virtual void serializeInternal(int64_t & value) = 0;
private:
const IInstanceResolver * instanceResolver;
template <typename VType, typename DVType, typename IType, typename... Args>
void doSerializeInternal(const std::string & fieldName, VType & value, const boost::optional<DVType> & defaultValue, Args ... args)
{
const boost::optional<IType> tempDefault = defaultValue ? boost::optional<IType>(static_cast<IType>(defaultValue.get())) : boost::none;
IType temp = static_cast<IType>(value);
serializeInternal(fieldName, temp, tempDefault, args...);
if(!saving)
value = static_cast<VType>(temp);
}
friend class JsonSerializeHelper;
friend class JsonStructSerializer;
friend class JsonArraySerializer;
};
template <typename Container>
void JsonArraySerializer::syncSize(Container & c, JsonNode::JsonType type)
{
if(owner->saving)
resize(c.size(), type);
else
c.resize(size());
}
template <typename T>
void JsonArraySerializer::serializeInt(const size_t index, T & value)
{
int64_t temp = static_cast<int64_t>(value);
serializeInt64(index, temp);
if(!owner->saving)
value = static_cast<T>(temp);
};