diff options
author | Shauren <shauren.trinity@gmail.com> | 2020-01-03 21:55:45 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-10-04 00:19:38 +0200 |
commit | 76be303351ae398b7f9e69e4c472cb5b05fce45e (patch) | |
tree | b7139aa70f1e9492fe8b94224fee28bbbe1ec34c /src/server/game/Instances | |
parent | 9b924522d0549dd67b10e2cbdfc20297dd21e182 (diff) |
Core/Scripts: Save instance data in JSON format
Diffstat (limited to 'src/server/game/Instances')
-rw-r--r-- | src/server/game/Instances/InstanceScript.cpp | 142 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceScript.h | 112 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceScriptData.cpp | 270 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceScriptData.h | 86 |
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__ |