/* * 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 . */ #include "InstanceScriptData.h" #include "DB2Stores.h" #include "InstanceScript.h" #include "Log.h" #include "Map.h" #include "World.h" #include #include #include #include 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, strlen(data)).HasParseError()) { TC_LOG_ERROR("scripts.data.load", "JSON parser error {} at {} while loading data for instance {} [{}-{} | {}-{}]", 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 {} [{}-{} | {}-{}]", 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 {} [{}-{} | {}-{}]", GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName()); return Result::MissingHeader; } if (headerItr->value != _instance.GetHeader()) { TC_LOG_ERROR("scripts.data.load", "Incorrect data header for instance {} [{}-{} | {}-{}], expected \"{}\" got \"{}\"", GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName(), _instance.GetHeader(), 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 {} [{}-{} | {}-{}]", 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 {} [{}-{} | {}-{}]", 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 ({}) than number of bosses ({}) for instance {} [{}-{} | {}-{}]", 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 ({}) is not a number for instance {} [{}-{} | {}-{}]", 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 {} [{}-{} | {}-{}]", 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 {} is not a number for instance {} [{}-{} | {}-{}]", 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 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("{}", 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); }