aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Instances/InstanceScriptData.cpp
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/InstanceScriptData.cpp
parent9b924522d0549dd67b10e2cbdfc20297dd21e182 (diff)
Core/Scripts: Save instance data in JSON format
Diffstat (limited to 'src/server/game/Instances/InstanceScriptData.cpp')
-rw-r--r--src/server/game/Instances/InstanceScriptData.cpp270
1 files changed, 270 insertions, 0 deletions
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);
+}