aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Instances
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2020-01-03 21:55:45 +0100
committerShauren <shauren.trinity@gmail.com>2022-10-04 00:19:38 +0200
commit76be303351ae398b7f9e69e4c472cb5b05fce45e (patch)
treeb7139aa70f1e9492fe8b94224fee28bbbe1ec34c /src/server/game/Instances
parent9b924522d0549dd67b10e2cbdfc20297dd21e182 (diff)
Core/Scripts: Save instance data in JSON format
Diffstat (limited to 'src/server/game/Instances')
-rw-r--r--src/server/game/Instances/InstanceScript.cpp142
-rw-r--r--src/server/game/Instances/InstanceScript.h112
-rw-r--r--src/server/game/Instances/InstanceScriptData.cpp270
-rw-r--r--src/server/game/Instances/InstanceScriptData.h86
4 files changed, 492 insertions, 118 deletions
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 159eb82b89e..9c1956c4dfb 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -27,6 +27,7 @@
#include "Group.h"
#include "InstancePackets.h"
#include "InstanceScenario.h"
+#include "InstanceScriptData.h"
#include "LFGMgr.h"
#include "Log.h"
#include "Map.h"
@@ -35,11 +36,11 @@
#include "Player.h"
#include "RBAC.h"
#include "ScriptReloadMgr.h"
+#include "SmartEnum.h"
#include "SpellMgr.h"
#include "World.h"
#include "WorldSession.h"
#include "WorldStateMgr.h"
-#include <sstream>
#include <cstdarg>
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
@@ -83,17 +84,6 @@ void InstanceScript::SaveToDB()
{
if (InstanceScenario* scenario = instance->GetInstanceScenario())
scenario->SaveToDB();
-
- std::string data = GetSaveData();
- if (data.empty())
- return;
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_DATA);
- stmt->setUInt32(0, GetCompletedEncounterMask());
- stmt->setString(1, data);
- stmt->setUInt32(2, _entranceId);
- stmt->setUInt32(3, instance->GetInstanceId());
- CharacterDatabase.Execute(stmt);
}
bool InstanceScript::IsEncounterInProgress() const
@@ -171,9 +161,7 @@ GameObject* InstanceScript::GetGameObject(uint32 type)
void InstanceScript::SetHeaders(std::string const& dataHeaders)
{
- for (char header : dataHeaders)
- if (isalpha(header))
- headers.push_back(header);
+ headers = dataHeaders;
}
void InstanceScript::LoadBossBoundaries(BossBoundaryData const& data)
@@ -451,8 +439,8 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
bossInfo->state = state;
SaveToDB();
- if (state == DONE && dungeonEncounter)
- instance->UpdateInstanceLock(dungeonEncounter, { id, state });
+ if (dungeonEncounter)
+ instance->UpdateInstanceLock({ dungeonEncounter, id, state });
}
for (uint32 type = 0; type < MAX_DOOR_TYPES; ++type)
@@ -493,12 +481,11 @@ void InstanceScript::Load(char const* data)
OUT_LOAD_INST_DATA(data);
- std::istringstream loadStream(data);
-
- if (ReadSaveDataHeaders(loadStream))
+ InstanceScriptDataReader reader(*this);
+ if (reader.Load(data) == InstanceScriptDataReader::Result::Ok)
{
- ReadSaveDataBossStates(loadStream);
- ReadSaveDataMore(loadStream);
+ UpdateSpawnGroups();
+ AfterDataLoad();
}
else
OUT_LOAD_INST_DATA_FAIL;
@@ -506,86 +493,39 @@ void InstanceScript::Load(char const* data)
OUT_LOAD_INST_DATA_COMPLETE;
}
-bool InstanceScript::ReadSaveDataHeaders(std::istringstream& data)
-{
- for (char header : headers)
- {
- char buff;
- data >> buff;
-
- if (header != buff)
- return false;
- }
-
- return true;
-}
-
-void InstanceScript::ReadSaveDataBossStates(std::istringstream& data)
-{
- uint32 bossId = 0;
- for (; bossId < bosses.size(); ++bossId)
- {
- uint32 buff;
- data >> buff;
- if (buff == IN_PROGRESS || buff == FAIL || buff == SPECIAL)
- buff = NOT_STARTED;
-
- if (buff < TO_BE_DECIDED)
- SetBossState(bossId, EncounterState(buff));
- }
- UpdateSpawnGroups();
-}
-
std::string InstanceScript::GetSaveData()
{
OUT_SAVE_INST_DATA;
- std::ostringstream saveStream;
+ InstanceScriptDataWriter writer(*this);
- WriteSaveDataHeaders(saveStream);
- WriteSaveDataBossStates(saveStream);
- WriteSaveDataMore(saveStream);
+ writer.FillData();
OUT_SAVE_INST_DATA_COMPLETE;
- return saveStream.str();
+ return writer.GetString();
}
-std::string InstanceScript::UpdateSaveData(std::string const& oldData, UpdateSaveDataEvent const& event)
+std::string InstanceScript::UpdateBossStateSaveData(std::string const& oldData, UpdateBossStateSaveDataEvent const& event)
{
if (!instance->GetMapDifficulty()->IsUsingEncounterLocks())
return GetSaveData();
- std::size_t position = (headers.size() + event.BossId) * 2;
- std::string newData = oldData;
- if (position >= oldData.length())
- {
- // Initialize blank data
- std::ostringstream saveStream;
- WriteSaveDataHeaders(saveStream);
- for (std::size_t i = 0; i < bosses.size(); ++i)
- saveStream << uint32(NOT_STARTED) << ' ';
-
- WriteSaveDataMore(saveStream);
-
- newData = saveStream.str();
- }
-
- newData[position] = uint32(event.NewState) + '0';
-
- return newData;
+ InstanceScriptDataWriter writer(*this);
+ writer.FillDataFrom(oldData);
+ writer.SetBossState(event);
+ return writer.GetString();
}
-void InstanceScript::WriteSaveDataHeaders(std::ostringstream& data)
+std::string InstanceScript::UpdateAdditionalSaveData(std::string const& oldData, UpdateAdditionalSaveDataEvent const& event)
{
- for (char header : headers)
- data << header << ' ';
-}
+ if (!instance->GetMapDifficulty()->IsUsingEncounterLocks())
+ return GetSaveData();
-void InstanceScript::WriteSaveDataBossStates(std::ostringstream& data)
-{
- for (BossInfo const& bossInfo : bosses)
- data << uint32(bossInfo.state) << ' ';
+ InstanceScriptDataWriter writer(*this);
+ writer.FillDataFrom(oldData);
+ writer.SetAdditionalData(event);
+ return writer.GetString();
}
void InstanceScript::HandleGameObject(ObjectGuid guid, bool open, GameObject* go /*= nullptr*/)
@@ -935,26 +875,9 @@ void InstanceScript::UpdatePhasing()
});
}
-/*static*/ char const* InstanceScript::GetBossStateName(uint8 state)
+char const* InstanceScript::GetBossStateName(uint8 state)
{
- // See enum EncounterState in InstanceScript.h
- switch (state)
- {
- case NOT_STARTED:
- return "NOT_STARTED";
- case IN_PROGRESS:
- return "IN_PROGRESS";
- case FAIL:
- return "FAIL";
- case DONE:
- return "DONE";
- case SPECIAL:
- return "SPECIAL";
- case TO_BE_DECIDED:
- return "TO_BE_DECIDED";
- default:
- return "INVALID";
- }
+ return EnumUtils::ToConstant(EncounterState(state));
}
void InstanceScript::UpdateCombatResurrection(uint32 diff)
@@ -1012,6 +935,19 @@ uint32 InstanceScript::GetCombatResurrectionChargeInterval() const
return interval;
}
+PersistentInstanceScriptValueBase::PersistentInstanceScriptValueBase(InstanceScript& instance, char const* name, std::variant<int64, double> value)
+ : _instance(instance), _name(name), _value(std::move(value))
+{
+ _instance.RegisterPersistentScriptValue(this);
+}
+
+PersistentInstanceScriptValueBase::~PersistentInstanceScriptValueBase() = default;
+
+void PersistentInstanceScriptValueBase::NotifyValueChanged()
+{
+ _instance.instance->UpdateInstanceLock(CreateEvent());
+}
+
bool InstanceHasScript(WorldObject const* obj, char const* scriptName)
{
if (InstanceMap* instance = obj->GetMap()->ToInstanceMap())
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index 77c8886628d..a296877d123 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -22,9 +22,9 @@
#include "Common.h"
#include "Duration.h"
#include <array>
-#include <iosfwd>
#include <map>
#include <set>
+#include <variant>
#define OUT_SAVE_INST_DATA TC_LOG_DEBUG("scripts", "Saving Instance Data for Instance %s (Map %d, Instance Id %d)", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
#define OUT_SAVE_INST_DATA_COMPLETE TC_LOG_DEBUG("scripts", "Saving Instance Data for Instance %s (Map %d, Instance Id %d) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
@@ -37,6 +37,7 @@ class Creature;
class GameObject;
class InstanceMap;
class ModuleReference;
+class PersistentInstanceScriptValueBase;
class Player;
class Unit;
struct DungeonEncounterEntry;
@@ -154,12 +155,21 @@ struct MinionInfo
BossInfo* bossInfo;
};
-struct UpdateSaveDataEvent
+struct UpdateBossStateSaveDataEvent
{
+ DungeonEncounterEntry const* DungeonEncounter;
uint32 BossId;
EncounterState NewState;
};
+struct UpdateAdditionalSaveDataEvent
+{
+ explicit UpdateAdditionalSaveDataEvent(char const* key, std::variant<int64, double> value) : Key(key), Value(value) { }
+
+ char const* Key;
+ std::variant<int64, double> Value;
+};
+
typedef std::multimap<uint32 /*entry*/, DoorInfo> DoorInfoMap;
typedef std::pair<DoorInfoMap::const_iterator, DoorInfoMap::const_iterator> DoorInfoMapBounds;
@@ -180,14 +190,13 @@ class TC_GAME_API InstanceScript : public ZoneScript
// if we're starting without any saved instance data
virtual void Create();
// if we're loading existing instance save data
- virtual void Load(char const* data);
+ void Load(char const* data);
// When save is needed, this function generates the data
- virtual std::string GetSaveData();
-
- virtual std::string UpdateSaveData(std::string const& oldData, UpdateSaveDataEvent const& event);
+ std::string GetSaveData();
- void SaveToDB();
+ std::string UpdateBossStateSaveData(std::string const& oldData, UpdateBossStateSaveDataEvent const& event);
+ std::string UpdateAdditionalSaveData(std::string const& oldData, UpdateAdditionalSaveDataEvent const& event);
virtual void Update(uint32 /*diff*/) { }
void UpdateCombatResurrection(uint32 diff);
@@ -307,6 +316,10 @@ class TC_GAME_API InstanceScript : public ZoneScript
uint8 GetCombatResurrectionCharges() const { return _combatResurrectionCharges; }
uint32 GetCombatResurrectionChargeInterval() const;
+ void RegisterPersistentScriptValue(PersistentInstanceScriptValueBase* value) { _persistentScriptValues.push_back(value); }
+ std::string const& GetHeader() const { return headers; }
+ std::vector<PersistentInstanceScriptValueBase*>& GetPersistentScriptValues() { return _persistentScriptValues; }
+
protected:
void SetHeaders(std::string const& dataHeaders);
void SetBossNumber(uint32 number) { bosses.resize(number); }
@@ -337,13 +350,8 @@ class TC_GAME_API InstanceScript : public ZoneScript
// Pay very much attention at how the returned BossInfo data is modified to avoid issues.
BossInfo* GetBossInfo(uint32 id);
- // Instance Load and Save
- bool ReadSaveDataHeaders(std::istringstream& data);
- void ReadSaveDataBossStates(std::istringstream& data);
- virtual void ReadSaveDataMore(std::istringstream& /*data*/) { }
- void WriteSaveDataHeaders(std::ostringstream& data);
- void WriteSaveDataBossStates(std::ostringstream& data);
- virtual void WriteSaveDataMore(std::ostringstream& /*data*/) { }
+ // Override this function to validate all additional data loads
+ virtual void AfterDataLoad() { }
bool _SkipCheckRequiredBosses(Player const* player = nullptr) const;
@@ -352,8 +360,11 @@ class TC_GAME_API InstanceScript : public ZoneScript
void LoadDungeonEncounterData(uint32 bossId, std::array<uint32, MAX_DUNGEON_ENCOUNTERS_PER_BOSS> const& dungeonEncounterIds);
void UpdateEncounterState(EncounterCreditType type, uint32 creditEntry, Unit* source);
- std::vector<char> headers;
+ void SaveToDB();
+
+ std::string headers;
std::vector<BossInfo> bosses;
+ std::vector<PersistentInstanceScriptValueBase*> _persistentScriptValues;
DoorInfoMap doors;
MinionInfoMap minions;
ObjectInfoMap _creatureInfo;
@@ -376,4 +387,75 @@ class TC_GAME_API InstanceScript : public ZoneScript
friend class debug_commandscript;
};
+class TC_GAME_API PersistentInstanceScriptValueBase
+{
+protected:
+ PersistentInstanceScriptValueBase(InstanceScript& instance, char const* name, std::variant<int64, double> value);
+
+public:
+ virtual ~PersistentInstanceScriptValueBase();
+
+ char const* GetName() const { return _name; }
+
+ UpdateAdditionalSaveDataEvent CreateEvent() const
+ {
+ return UpdateAdditionalSaveDataEvent(_name, _value);
+ }
+
+ void LoadValue(int64 value)
+ {
+ _value.emplace<int64>(value);
+ }
+
+ void LoadValue(double value)
+ {
+ _value.emplace<double>(value);
+ }
+
+protected:
+ void NotifyValueChanged();
+
+ InstanceScript& _instance;
+ char const* _name;
+ std::variant<int64, double> _value;
+};
+
+template<typename T>
+class PersistentInstanceScriptValue : public PersistentInstanceScriptValueBase
+{
+public:
+ PersistentInstanceScriptValue(InstanceScript& instance, char const* name, T value = {})
+ : PersistentInstanceScriptValueBase(instance, name, WrapValue(value))
+ {
+ }
+
+ operator T() const
+ {
+ return std::visit([](auto v) { return static_cast<T>(v); }, _value);
+ }
+
+ PersistentInstanceScriptValue& operator=(T value)
+ {
+ _value = WrapValue(value);
+ NotifyValueChanged();
+ return *this;
+ }
+
+ void LoadValue(T value)
+ {
+ _value = WrapValue(value);
+ }
+
+private:
+ static std::variant<int64, double> WrapValue(T value)
+ {
+ if constexpr (std::is_integral_v<T> || std::is_enum_v<T>)
+ return int64(value);
+ else if constexpr (std::is_floating_point_v<T>)
+ return double(value);
+ else
+ return {};
+ }
+};
+
#endif // TRINITY_INSTANCE_DATA_H
diff --git a/src/server/game/Instances/InstanceScriptData.cpp b/src/server/game/Instances/InstanceScriptData.cpp
new file mode 100644
index 00000000000..ee19a912742
--- /dev/null
+++ b/src/server/game/Instances/InstanceScriptData.cpp
@@ -0,0 +1,270 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "InstanceScriptData.h"
+#include "DB2Stores.h"
+#include "InstanceScript.h"
+#include "Log.h"
+#include "Map.h"
+#include "World.h"
+#include <rapidjson/pointer.h>
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
+#include <rapidjson/error/en.h>
+
+namespace
+{
+ std::string const HeadersKey = "Header";
+
+ std::string const BossStatesSaveDataKey = "BossStates";
+
+ std::string const MoreSaveDataKey = "AdditionalData";
+}
+
+InstanceScriptDataReader::Result InstanceScriptDataReader::Load(char const* data)
+{
+ /*
+ Expected JSON
+
+ {
+ "Header": "HEADER_STRING_SET_BY_SCRIPT",
+ "BossStates": [0,2,0,...] // indexes are boss ids, values are EncounterState
+ "AdditionalData: { // optional
+ "ExtraKey1": 123
+ "AnotherExtraKey": 2.0
+ }
+ }
+ */
+ if (_doc.Parse(data).HasParseError())
+ {
+ TC_LOG_ERROR("scripts.data.load", "JSON parser error %s at " SZFMTD " while loading data for instance %u [%u-%s | %u-%s]",
+ rapidjson::GetParseError_En(_doc.GetParseError()), _doc.GetErrorOffset(),
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::MalformedJson;
+ }
+
+ if (!_doc.IsObject())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Root JSON value is not an object for instance %u [%u-%s | %u-%s]",
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::RootIsNotAnObject;
+ }
+
+ Result result = ParseHeader();
+ if (result != Result::Ok)
+ return result;
+
+ result = ParseBossStates();
+ if (result != Result::Ok)
+ return result;
+
+ result = ParseAdditionalData();
+ if (result != Result::Ok)
+ return result;
+
+ return Result::Ok;
+}
+
+InstanceScriptDataReader::Result InstanceScriptDataReader::ParseHeader()
+{
+ auto headerItr = _doc.FindMember(HeadersKey);
+ if (headerItr == _doc.MemberEnd())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Missing data header for instance %u [%u-%s | %u-%s]",
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::MissingHeader;
+ }
+
+ if (headerItr->value != _instance.GetHeader())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Incorrect data header for instance %u [%u-%s | %u-%s], expected \"%s\" got \"%s\"",
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName(),
+ _instance.GetHeader().c_str(), headerItr->value.IsString() ? headerItr->value.GetString() : "");
+ return Result::UnexpectedHeader;
+ }
+
+ return Result::Ok;
+}
+
+InstanceScriptDataReader::Result InstanceScriptDataReader::ParseBossStates()
+{
+ auto bossStatesItr = _doc.FindMember(BossStatesSaveDataKey);
+ if (bossStatesItr == _doc.MemberEnd())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Missing boss states for instance %u [%u-%s | %u-%s]",
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::MissingBossStates;
+ }
+
+ if (!bossStatesItr->value.IsArray())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Boss states is not an array for instance %u [%u-%s | %u-%s]",
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::BossStatesIsNotAnObject;
+ }
+
+ for (uint32 bossId = 0; bossId < bossStatesItr->value.Size(); ++bossId)
+ {
+ if (bossId >= _instance.GetEncounterCount())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Boss states has entry for boss with higher id (%u) than number of bosses (%u) for instance %u [%u-%s | %u-%s]",
+ bossId, _instance.GetEncounterCount(), GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::UnknownBoss;
+ }
+
+ auto& bossState = bossStatesItr->value[bossId];
+ if (!bossState.IsNumber())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Boss state for boss (%u) is not a number for instance %u [%u-%s | %u-%s]",
+ bossId, GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::BossStateIsNotAnObject;
+ }
+
+ EncounterState state = EncounterState(bossState.GetInt());
+ if (state == IN_PROGRESS || state == FAIL || state == SPECIAL)
+ state = NOT_STARTED;
+
+ if (state < TO_BE_DECIDED)
+ _instance.SetBossState(bossId, state);
+ }
+
+ return Result::Ok;
+}
+
+InstanceScriptDataReader::Result InstanceScriptDataReader::ParseAdditionalData()
+{
+ auto moreDataItr = _doc.FindMember(MoreSaveDataKey);
+ if (moreDataItr == _doc.MemberEnd())
+ return Result::Ok;
+
+ if (!moreDataItr->value.IsObject())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Additional data is not an object for instance %u [%u-%s | %u-%s]",
+ GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::AdditionalDataIsNotAnObject;
+ }
+
+ for (PersistentInstanceScriptValueBase* value : _instance.GetPersistentScriptValues())
+ {
+ auto valueItr = moreDataItr->value.FindMember(value->GetName());
+ if (valueItr != moreDataItr->value.MemberEnd() && !valueItr->value.IsNull())
+ {
+ if (!valueItr->value.IsNumber())
+ {
+ TC_LOG_ERROR("scripts.data.load", "Additional data value for key %s is not a number for instance %u [%u-%s | %u-%s]",
+ value->GetName(), GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
+ return Result::AdditionalDataUnexpectedValueType;
+ }
+
+ if (valueItr->value.IsDouble())
+ value->LoadValue(valueItr->value.GetDouble());
+ else
+ value->LoadValue(valueItr->value.GetInt64());
+ }
+ }
+
+ return Result::Ok;
+}
+
+uint32 InstanceScriptDataReader::GetInstanceId() const { return _instance.instance->GetInstanceId(); }
+uint32 InstanceScriptDataReader::GetMapId() const { return _instance.instance->GetId(); }
+char const* InstanceScriptDataReader::GetMapName() const { return _instance.instance->GetMapName(); }
+uint32 InstanceScriptDataReader::GetDifficultyId() const { return uint32(_instance.instance->GetDifficultyID()); }
+char const* InstanceScriptDataReader::GetDifficultyName() const { return sDifficultyStore.AssertEntry(_instance.instance->GetDifficultyID())->Name[sWorld->GetDefaultDbcLocale()]; }
+
+std::string InstanceScriptDataWriter::GetString()
+{
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ _doc.Accept(writer);
+
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+void InstanceScriptDataWriter::FillData(bool withValues)
+{
+ _doc.SetObject();
+ _doc.AddMember(rapidjson::StringRef(HeadersKey), _instance.GetHeader(), _doc.GetAllocator());
+
+ rapidjson::Value bossStates(rapidjson::kArrayType);
+ for (uint32 bossId = 0; bossId < _instance.GetEncounterCount(); ++bossId)
+ {
+ rapidjson::Value bossStateValue(rapidjson::kNumberType);
+ bossStateValue.SetInt(withValues ? _instance.GetBossState(bossId) : NOT_STARTED);
+ bossStates.PushBack(bossStateValue.Move(), _doc.GetAllocator());
+ }
+ _doc.AddMember(rapidjson::StringRef(BossStatesSaveDataKey), bossStates.Move(), _doc.GetAllocator());
+
+ if (!_instance.GetPersistentScriptValues().empty())
+ {
+ rapidjson::Value moreData(rapidjson::kObjectType);
+ for (PersistentInstanceScriptValueBase* additionalValue : _instance.GetPersistentScriptValues())
+ {
+ if (withValues)
+ {
+ UpdateAdditionalSaveDataEvent data = additionalValue->CreateEvent();
+ std::visit([&](auto v)
+ {
+ moreData.AddMember(rapidjson::StringRef(data.Key), rapidjson::Value(v), _doc.GetAllocator());
+ }, data.Value);
+ }
+ else
+ moreData.AddMember(rapidjson::StringRef(additionalValue->GetName()), rapidjson::Value(), _doc.GetAllocator());
+ }
+
+ _doc.AddMember(rapidjson::StringRef(MoreSaveDataKey), moreData.Move(), _doc.GetAllocator());
+ }
+}
+
+void InstanceScriptDataWriter::FillDataFrom(std::string const& data)
+{
+ if (_doc.Parse(data).HasParseError())
+ FillData(false);
+}
+
+void InstanceScriptDataWriter::SetBossState(UpdateBossStateSaveDataEvent const& data)
+{
+ std::string bossIdKey = Trinity::StringFormat("%u", data.BossId);
+
+ rapidjson::Pointer::Token tokens[] =
+ {
+ { BossStatesSaveDataKey.c_str(), uint32(BossStatesSaveDataKey.length()), rapidjson::kPointerInvalidIndex },
+ { bossIdKey.c_str(), uint32(bossIdKey.length()), rapidjson::kPointerInvalidIndex }
+ };
+ rapidjson::Pointer ptr(tokens, std::size(tokens));
+
+ // jsonptr("/BossStates/BossId")
+ rapidjson::Value stateValue(rapidjson::kNumberType);
+ stateValue.SetInt(data.NewState);
+ rapidjson::SetValueByPointer(_doc, ptr, stateValue.Move());
+}
+
+void InstanceScriptDataWriter::SetAdditionalData(UpdateAdditionalSaveDataEvent const& data)
+{
+ rapidjson::Pointer::Token tokens[] =
+ {
+ { MoreSaveDataKey.c_str(), uint32(MoreSaveDataKey.length()), rapidjson::kPointerInvalidIndex },
+ { data.Key, uint32(strlen(data.Key)), rapidjson::kPointerInvalidIndex }
+ };
+ rapidjson::Pointer ptr(tokens, std::size(tokens));
+
+ // jsonptr("/AdditionalData/CustomValueName")
+ std::visit([&](auto v)
+ {
+ rapidjson::SetValueByPointer(_doc, ptr, v);
+ }, data.Value);
+}
diff --git a/src/server/game/Instances/InstanceScriptData.h b/src/server/game/Instances/InstanceScriptData.h
new file mode 100644
index 00000000000..c489123d625
--- /dev/null
+++ b/src/server/game/Instances/InstanceScriptData.h
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef InstanceScriptData_h__
+#define InstanceScriptData_h__
+
+#include "Errors.h" // rapidjson depends on WPAssert
+#include <rapidjson/document.h>
+#include <string>
+
+class InstanceScript;
+struct UpdateBossStateSaveDataEvent;
+struct UpdateAdditionalSaveDataEvent;
+
+class InstanceScriptDataReader
+{
+public:
+ enum class Result
+ {
+ Ok,
+ MalformedJson,
+ RootIsNotAnObject,
+ MissingHeader,
+ UnexpectedHeader,
+ MissingBossStates,
+ BossStatesIsNotAnObject,
+ UnknownBoss,
+ BossStateIsNotAnObject,
+ MissingBossState,
+ BossStateValueIsNotANumber,
+ AdditionalDataIsNotAnObject,
+ AdditionalDataUnexpectedValueType
+ };
+
+ InstanceScriptDataReader(InstanceScript& instance) : _instance(instance) { }
+
+ Result Load(char const* data);
+
+private:
+ Result ParseHeader();
+ Result ParseBossStates();
+ Result ParseAdditionalData();
+
+ // logging helpers
+ uint32 GetInstanceId() const;
+ uint32 GetMapId() const;
+ char const* GetMapName() const;
+ uint32 GetDifficultyId() const;
+ char const* GetDifficultyName() const;
+
+ InstanceScript& _instance;
+ rapidjson::Document _doc;
+};
+
+class InstanceScriptDataWriter
+{
+public:
+ InstanceScriptDataWriter(InstanceScript& instance) : _instance(instance) { }
+
+ std::string GetString();
+ void FillData(bool withValues = true);
+ void FillDataFrom(std::string const& data);
+
+ void SetBossState(UpdateBossStateSaveDataEvent const& data);
+ void SetAdditionalData(UpdateAdditionalSaveDataEvent const& data);
+
+private:
+ InstanceScript& _instance;
+ rapidjson::Document _doc;
+};
+
+#endif // InstanceScriptData_h__