TrinityCore
InstanceScriptData.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "InstanceScriptData.h"
19#include "DB2Stores.h"
20#include "InstanceScript.h"
21#include "Log.h"
22#include "Map.h"
23#include "World.h"
24#include <rapidjson/pointer.h>
25#include <rapidjson/stringbuffer.h>
26#include <rapidjson/writer.h>
27#include <rapidjson/error/en.h>
28
29namespace
30{
31 std::string const HeadersKey = "Header";
32
33 std::string const BossStatesSaveDataKey = "BossStates";
34
35 std::string const MoreSaveDataKey = "AdditionalData";
36}
37
39{
40 /*
41 Expected JSON
42
43 {
44 "Header": "HEADER_STRING_SET_BY_SCRIPT",
45 "BossStates": [0,2,0,...] // indexes are boss ids, values are EncounterState
46 "AdditionalData: { // optional
47 "ExtraKey1": 123
48 "AnotherExtraKey": 2.0
49 }
50 }
51 */
52 if (_doc.Parse(data).HasParseError())
53 {
54 TC_LOG_ERROR("scripts.data.load", "JSON parser error {} at {} while loading data for instance {} [{}-{} | {}-{}]",
55 rapidjson::GetParseError_En(_doc.GetParseError()), _doc.GetErrorOffset(),
58 }
59
60 if (!_doc.IsObject())
61 {
62 TC_LOG_ERROR("scripts.data.load", "Root JSON value is not an object for instance {} [{}-{} | {}-{}]",
65 }
66
67 Result result = ParseHeader();
68 if (result != Result::Ok)
69 return result;
70
71 result = ParseBossStates();
72 if (result != Result::Ok)
73 return result;
74
75 result = ParseAdditionalData();
76 if (result != Result::Ok)
77 return result;
78
79 return Result::Ok;
80}
81
83{
84 auto headerItr = _doc.FindMember(HeadersKey);
85 if (headerItr == _doc.MemberEnd())
86 {
87 TC_LOG_ERROR("scripts.data.load", "Missing data header for instance {} [{}-{} | {}-{}]",
90 }
91
92 if (headerItr->value != _instance.GetHeader())
93 {
94 TC_LOG_ERROR("scripts.data.load", "Incorrect data header for instance {} [{}-{} | {}-{}], expected \"{}\" got \"{}\"",
96 _instance.GetHeader(), headerItr->value.IsString() ? headerItr->value.GetString() : "");
98 }
99
100 return Result::Ok;
101}
102
104{
105 auto bossStatesItr = _doc.FindMember(BossStatesSaveDataKey);
106 if (bossStatesItr == _doc.MemberEnd())
107 {
108 TC_LOG_ERROR("scripts.data.load", "Missing boss states for instance {} [{}-{} | {}-{}]",
111 }
112
113 if (!bossStatesItr->value.IsArray())
114 {
115 TC_LOG_ERROR("scripts.data.load", "Boss states is not an array for instance {} [{}-{} | {}-{}]",
118 }
119
120 for (uint32 bossId = 0; bossId < bossStatesItr->value.Size(); ++bossId)
121 {
122 if (bossId >= _instance.GetEncounterCount())
123 {
124 TC_LOG_ERROR("scripts.data.load", "Boss states has entry for boss with higher id ({}) than number of bosses ({}) for instance {} [{}-{} | {}-{}]",
126 return Result::UnknownBoss;
127 }
128
129 auto& bossState = bossStatesItr->value[bossId];
130 if (!bossState.IsNumber())
131 {
132 TC_LOG_ERROR("scripts.data.load", "Boss state for boss ({}) is not a number for instance {} [{}-{} | {}-{}]",
135 }
136
137 EncounterState state = EncounterState(bossState.GetInt());
138 if (state == IN_PROGRESS || state == FAIL || state == SPECIAL)
139 state = NOT_STARTED;
140
141 if (state < TO_BE_DECIDED)
142 _instance.SetBossState(bossId, state);
143 }
144
145 return Result::Ok;
146}
147
149{
150 auto moreDataItr = _doc.FindMember(MoreSaveDataKey);
151 if (moreDataItr == _doc.MemberEnd())
152 return Result::Ok;
153
154 if (!moreDataItr->value.IsObject())
155 {
156 TC_LOG_ERROR("scripts.data.load", "Additional data is not an object for instance {} [{}-{} | {}-{}]",
159 }
160
162 {
163 auto valueItr = moreDataItr->value.FindMember(value->GetName());
164 if (valueItr != moreDataItr->value.MemberEnd() && !valueItr->value.IsNull())
165 {
166 if (!valueItr->value.IsNumber())
167 {
168 TC_LOG_ERROR("scripts.data.load", "Additional data value for key {} is not a number for instance {} [{}-{} | {}-{}]",
169 value->GetName(), GetInstanceId(), GetMapId(), GetMapName(), GetDifficultyId(), GetDifficultyName());
171 }
172
173 if (valueItr->value.IsDouble())
174 value->LoadValue(valueItr->value.GetDouble());
175 else
176 value->LoadValue(valueItr->value.GetInt64());
177 }
178 }
179
180 return Result::Ok;
181}
182
187char const* InstanceScriptDataReader::GetDifficultyName() const { return sDifficultyStore.AssertEntry(_instance.instance->GetDifficultyID())->Name[sWorld->GetDefaultDbcLocale()]; }
188
190{
191 rapidjson::StringBuffer buffer;
192 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
193 _doc.Accept(writer);
194
195 return std::string(buffer.GetString(), buffer.GetSize());
196}
197
199{
200 _doc.SetObject();
201 _doc.AddMember(rapidjson::StringRef(HeadersKey), _instance.GetHeader(), _doc.GetAllocator());
202
203 rapidjson::Value bossStates(rapidjson::kArrayType);
204 for (uint32 bossId = 0; bossId < _instance.GetEncounterCount(); ++bossId)
205 {
206 rapidjson::Value bossStateValue(rapidjson::kNumberType);
207 bossStateValue.SetInt(withValues ? _instance.GetBossState(bossId) : NOT_STARTED);
208 bossStates.PushBack(bossStateValue.Move(), _doc.GetAllocator());
209 }
210 _doc.AddMember(rapidjson::StringRef(BossStatesSaveDataKey), bossStates.Move(), _doc.GetAllocator());
211
213 {
214 rapidjson::Value moreData(rapidjson::kObjectType);
216 {
217 if (withValues)
218 {
219 UpdateAdditionalSaveDataEvent data = additionalValue->CreateEvent();
220 std::visit([&](auto v)
221 {
222 moreData.AddMember(rapidjson::StringRef(data.Key), rapidjson::Value(v), _doc.GetAllocator());
223 }, data.Value);
224 }
225 else
226 moreData.AddMember(rapidjson::StringRef(additionalValue->GetName()), rapidjson::Value(), _doc.GetAllocator());
227 }
228
229 _doc.AddMember(rapidjson::StringRef(MoreSaveDataKey), moreData.Move(), _doc.GetAllocator());
230 }
231}
232
233void InstanceScriptDataWriter::FillDataFrom(std::string const& data)
234{
235 if (_doc.Parse(data).HasParseError())
236 FillData(false);
237}
238
240{
241 std::string bossIdKey = Trinity::StringFormat("{}", data.BossId);
242
243 rapidjson::Pointer::Token tokens[] =
244 {
245 { BossStatesSaveDataKey.c_str(), uint32(BossStatesSaveDataKey.length()), rapidjson::kPointerInvalidIndex },
246 { bossIdKey.c_str(), uint32(bossIdKey.length()), rapidjson::kPointerInvalidIndex }
247 };
248 rapidjson::Pointer ptr(tokens, std::size(tokens));
249
250 // jsonptr("/BossStates/BossId")
251 rapidjson::Value stateValue(rapidjson::kNumberType);
252 stateValue.SetInt(data.NewState);
253 rapidjson::SetValueByPointer(_doc, ptr, stateValue.Move());
254}
255
257{
258 rapidjson::Pointer::Token tokens[] =
259 {
260 { MoreSaveDataKey.c_str(), uint32(MoreSaveDataKey.length()), rapidjson::kPointerInvalidIndex },
261 { data.Key, uint32(strlen(data.Key)), rapidjson::kPointerInvalidIndex }
262 };
263 rapidjson::Pointer ptr(tokens, std::size(tokens));
264
265 // jsonptr("/AdditionalData/CustomValueName")
266 std::visit([&](auto v)
267 {
268 rapidjson::SetValueByPointer(_doc, ptr, v);
269 }, data.Value);
270}
DB2Storage< DifficultyEntry > sDifficultyStore("Difficulty.db2", &DifficultyLoadInfo::Instance)
uint32_t uint32
Definition: Define.h:142
EncounterState
@ IN_PROGRESS
@ FAIL
@ SPECIAL
@ NOT_STARTED
@ TO_BE_DECIDED
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
char const * GetDifficultyName() const
rapidjson::Document _doc
Result Load(char const *data)
char const * GetMapName() const
void SetAdditionalData(UpdateAdditionalSaveDataEvent const &data)
void FillDataFrom(std::string const &data)
void FillData(bool withValues=true)
void SetBossState(UpdateBossStateSaveDataEvent const &data)
rapidjson::Document _doc
virtual bool SetBossState(uint32 id, EncounterState state)
std::vector< PersistentInstanceScriptValueBase * > & GetPersistentScriptValues()
InstanceMap * instance
std::string const & GetHeader() const
EncounterState GetBossState(uint32 id) const
uint32 GetEncounterCount() const
Difficulty GetDifficultyID() const
Definition: Map.h:324
uint32 GetId() const
Definition: Map.cpp:3228
char const * GetMapName() const
Definition: Map.cpp:1854
uint32 GetInstanceId() const
Definition: Map.h:314
#define sWorld
Definition: World.h:931
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
constexpr std::size_t size()
Definition: UpdateField.h:796
std::variant< int64, double > Value