Core/Scripts: Save instance data in JSON format
Port From (https://github.com/TrinityCore/TrinityCore/commit/76be303351ae398b7f9e69e4c472cb5b05fce45e)
This commit is contained in:
@@ -116,9 +116,9 @@ namespace Game.Chat
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Command("savedata", RBACPermissions.CommandInstanceSavedata)]
|
[Command("savedata", RBACPermissions.CommandInstanceSavedata)]
|
||||||
static bool HandleInstanceSaveData(CommandHandler handler)
|
static bool HandleInstanceSaveDataCommand(CommandHandler handler)
|
||||||
{
|
{
|
||||||
Player player = handler.GetSession().GetPlayer();
|
/*Player player = handler.GetSession().GetPlayer();
|
||||||
InstanceMap map = player.GetMap().ToInstanceMap();
|
InstanceMap map = player.GetMap().ToInstanceMap();
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
@@ -132,7 +132,7 @@ namespace Game.Chat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
map.GetInstanceScript().SaveToDB();
|
map.GetInstanceScript().SaveToDB();*/
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -460,13 +460,13 @@ namespace Game.Maps
|
|||||||
_sharedData = sharedData;
|
_sharedData = sharedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInstanceId(uint instanceId)
|
public override void SetInstanceId(uint instanceId)
|
||||||
{
|
{
|
||||||
base.SetInstanceId(instanceId);
|
base.SetInstanceId(instanceId);
|
||||||
_sharedData.InstanceId = instanceId;
|
_sharedData.InstanceId = instanceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceLockData GetInstanceInitializationData() { return _sharedData; }
|
public override InstanceLockData GetInstanceInitializationData() { return _sharedData; }
|
||||||
|
|
||||||
public SharedInstanceLockData GetSharedData() { return _sharedData; }
|
public SharedInstanceLockData GetSharedData() { return _sharedData; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,17 +44,6 @@ namespace Game.Maps
|
|||||||
InstanceScenario scenario = instance.GetInstanceScenario();
|
InstanceScenario scenario = instance.GetInstanceScenario();
|
||||||
if (scenario != null)
|
if (scenario != null)
|
||||||
scenario.SaveToDB();
|
scenario.SaveToDB();
|
||||||
|
|
||||||
string data = GetSaveData();
|
|
||||||
if (string.IsNullOrEmpty(data))
|
|
||||||
return;
|
|
||||||
|
|
||||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_INSTANCE_DATA);
|
|
||||||
stmt.AddValue(0, GetCompletedEncounterMask());
|
|
||||||
stmt.AddValue(1, data);
|
|
||||||
stmt.AddValue(2, _entranceId);
|
|
||||||
stmt.AddValue(3, instance.GetInstanceId());
|
|
||||||
DB.Characters.Execute(stmt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool IsEncounterInProgress()
|
public virtual bool IsEncounterInProgress()
|
||||||
@@ -104,9 +93,7 @@ namespace Game.Maps
|
|||||||
|
|
||||||
public void SetHeaders(string dataHeaders)
|
public void SetHeaders(string dataHeaders)
|
||||||
{
|
{
|
||||||
foreach (char header in dataHeaders)
|
headers = dataHeaders;
|
||||||
if (char.IsLetter(header))
|
|
||||||
headers.Add(header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadBossBoundaries(BossBoundaryEntry[] data)
|
public void LoadBossBoundaries(BossBoundaryEntry[] data)
|
||||||
@@ -430,8 +417,8 @@ namespace Game.Maps
|
|||||||
|
|
||||||
bossInfo.state = state;
|
bossInfo.state = state;
|
||||||
SaveToDB();
|
SaveToDB();
|
||||||
if (state == EncounterState.Done && dungeonEncounter != null)
|
if (dungeonEncounter != null)
|
||||||
instance.UpdateInstanceLock(dungeonEncounter, new(id, state));
|
instance.UpdateInstanceLock(new UpdateBossStateSaveDataEvent(dungeonEncounter, id, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint type = 0; type < (int)DoorType.Max; ++type)
|
for (uint type = 0; type < (int)DoorType.Max; ++type)
|
||||||
@@ -470,7 +457,7 @@ namespace Game.Maps
|
|||||||
UpdateSpawnGroups();
|
UpdateSpawnGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Load(string data)
|
public void Load(string data)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(data))
|
if (string.IsNullOrEmpty(data))
|
||||||
{
|
{
|
||||||
@@ -480,12 +467,11 @@ namespace Game.Maps
|
|||||||
|
|
||||||
OutLoadInstData(data);
|
OutLoadInstData(data);
|
||||||
|
|
||||||
var loadStream = new StringArguments(data);
|
InstanceScriptDataReader reader = new(this);
|
||||||
|
if (reader.Load(data) == InstanceScriptDataReader.Result.Ok)
|
||||||
if (ReadSaveDataHeaders(loadStream))
|
|
||||||
{
|
{
|
||||||
ReadSaveDataBossStates(loadStream);
|
UpdateSpawnGroups();
|
||||||
ReadSaveDataMore(loadStream);
|
AfterDataLoad();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
OutLoadInstDataFail();
|
OutLoadInstDataFail();
|
||||||
@@ -493,84 +479,39 @@ namespace Game.Maps
|
|||||||
OutLoadInstDataComplete();
|
OutLoadInstDataComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadSaveDataHeaders(StringArguments data)
|
public string GetSaveData()
|
||||||
{
|
|
||||||
foreach (char header in headers)
|
|
||||||
{
|
|
||||||
char buff = data.NextChar();
|
|
||||||
|
|
||||||
if (header != buff)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadSaveDataBossStates(StringArguments data)
|
|
||||||
{
|
|
||||||
foreach (var pair in bosses)
|
|
||||||
{
|
|
||||||
EncounterState buff = (EncounterState)data.NextUInt32();
|
|
||||||
if (buff == EncounterState.InProgress || buff == EncounterState.Fail || buff == EncounterState.Special)
|
|
||||||
buff = EncounterState.NotStarted;
|
|
||||||
|
|
||||||
if (buff < EncounterState.ToBeDecided)
|
|
||||||
SetBossState(pair.Key, buff);
|
|
||||||
}
|
|
||||||
UpdateSpawnGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string GetSaveData()
|
|
||||||
{
|
{
|
||||||
OutSaveInstData();
|
OutSaveInstData();
|
||||||
|
|
||||||
StringBuilder saveStream = new();
|
InstanceScriptDataWriter writer = new(this);
|
||||||
|
|
||||||
WriteSaveDataHeaders(saveStream);
|
writer.FillData();
|
||||||
WriteSaveDataBossStates(saveStream);
|
|
||||||
WriteSaveDataMore(saveStream);
|
|
||||||
|
|
||||||
OutSaveInstDataComplete();
|
OutSaveInstDataComplete();
|
||||||
|
|
||||||
return saveStream.ToString();
|
return writer.GetString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string UpdateSaveData(string oldData, UpdateSaveDataEvent saveEvent)
|
public string UpdateBossStateSaveData(string oldData, UpdateBossStateSaveDataEvent saveEvent)
|
||||||
{
|
{
|
||||||
if (!instance.GetMapDifficulty().IsUsingEncounterLocks())
|
if (!instance.GetMapDifficulty().IsUsingEncounterLocks())
|
||||||
return GetSaveData();
|
return GetSaveData();
|
||||||
|
|
||||||
int position = (int)(headers.Count + saveEvent.BossId) * 2;
|
InstanceScriptDataWriter writer = new(this);
|
||||||
StringBuilder newData = new(oldData);
|
writer.FillDataFrom(oldData);
|
||||||
if (position >= oldData.Length)
|
writer.SetBossState(saveEvent);
|
||||||
{
|
return writer.GetString();
|
||||||
// Initialize blank data
|
|
||||||
StringBuilder saveStream = new();
|
|
||||||
WriteSaveDataHeaders(saveStream);
|
|
||||||
for (var i = 0; i < bosses.Count; ++i)
|
|
||||||
saveStream.Append($"{(uint)EncounterState.NotStarted} ");
|
|
||||||
|
|
||||||
WriteSaveDataMore(saveStream);
|
|
||||||
|
|
||||||
newData = saveStream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newData.Remove(position, 2);
|
public string UpdateAdditionalSaveData(string oldData, UpdateAdditionalSaveDataEvent saveEvent)
|
||||||
newData.Insert(position, $"{(uint)saveEvent.NewState}0");
|
|
||||||
|
|
||||||
return newData.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteSaveDataHeaders(StringBuilder data)
|
|
||||||
{
|
{
|
||||||
foreach (char header in headers)
|
if (!instance.GetMapDifficulty().IsUsingEncounterLocks())
|
||||||
data.AppendFormat("{0} ", header);
|
return GetSaveData();
|
||||||
}
|
|
||||||
|
|
||||||
void WriteSaveDataBossStates(StringBuilder data)
|
InstanceScriptDataWriter writer = new(this);
|
||||||
{
|
writer.FillDataFrom(oldData);
|
||||||
foreach (BossInfo bossInfo in bosses.Values)
|
writer.SetAdditionalData(saveEvent);
|
||||||
data.AppendFormat("{0} ", (uint)bossInfo.state);
|
return writer.GetString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleGameObject(ObjectGuid guid, bool open, GameObject go = null)
|
public void HandleGameObject(ObjectGuid guid, bool open, GameObject go = null)
|
||||||
@@ -998,6 +939,12 @@ namespace Game.Maps
|
|||||||
|
|
||||||
public byte GetCombatResurrectionCharges() { return _combatResurrectionCharges; }
|
public byte GetCombatResurrectionCharges() { return _combatResurrectionCharges; }
|
||||||
|
|
||||||
|
public void RegisterPersistentScriptValue(PersistentInstanceScriptValueBase value) { _persistentScriptValues.Add(value); }
|
||||||
|
|
||||||
|
public string GetHeader() { return headers; }
|
||||||
|
|
||||||
|
public List<PersistentInstanceScriptValueBase> GetPersistentScriptValues() { return _persistentScriptValues; }
|
||||||
|
|
||||||
public void SetBossNumber(uint number)
|
public void SetBossNumber(uint number)
|
||||||
{
|
{
|
||||||
for (uint i = 0; i < number; ++i)
|
for (uint i = 0; i < number; ++i)
|
||||||
@@ -1010,15 +957,15 @@ namespace Game.Maps
|
|||||||
public void OutLoadInstDataComplete() { Log.outDebug(LogFilter.Scripts, "Instance Data Load for Instance {0} (Map {1}, Instance Id: {2}) is complete.", instance.GetMapName(), instance.GetId(), instance.GetInstanceId()); }
|
public void OutLoadInstDataComplete() { Log.outDebug(LogFilter.Scripts, "Instance Data Load for Instance {0} (Map {1}, Instance Id: {2}) is complete.", instance.GetMapName(), instance.GetId(), instance.GetInstanceId()); }
|
||||||
public void OutLoadInstDataFail() { Log.outDebug(LogFilter.Scripts, "Unable to load Instance Data for Instance {0} (Map {1}, Instance Id: {2}).", instance.GetMapName(), instance.GetId(), instance.GetInstanceId()); }
|
public void OutLoadInstDataFail() { Log.outDebug(LogFilter.Scripts, "Unable to load Instance Data for Instance {0} (Map {1}, Instance Id: {2}).", instance.GetMapName(), instance.GetId(), instance.GetInstanceId()); }
|
||||||
|
|
||||||
public virtual void ReadSaveDataMore(StringArguments data) { }
|
|
||||||
|
|
||||||
public virtual void WriteSaveDataMore(StringBuilder data) { }
|
|
||||||
|
|
||||||
public List<InstanceSpawnGroupInfo> GetInstanceSpawnGroups() { return _instanceSpawnGroups; }
|
public List<InstanceSpawnGroupInfo> GetInstanceSpawnGroups() { return _instanceSpawnGroups; }
|
||||||
|
|
||||||
|
// Override this function to validate all additional data loads
|
||||||
|
public virtual void AfterDataLoad() { }
|
||||||
|
|
||||||
public InstanceMap instance;
|
public InstanceMap instance;
|
||||||
List<char> headers = new();
|
string headers;
|
||||||
Dictionary<uint, BossInfo> bosses = new();
|
Dictionary<uint, BossInfo> bosses = new();
|
||||||
|
List<PersistentInstanceScriptValueBase> _persistentScriptValues = new();
|
||||||
MultiMap<uint, DoorInfo> doors = new();
|
MultiMap<uint, DoorInfo> doors = new();
|
||||||
Dictionary<uint, MinionInfo> minions = new();
|
Dictionary<uint, MinionInfo> minions = new();
|
||||||
Dictionary<uint, uint> _creatureInfo = new();
|
Dictionary<uint, uint> _creatureInfo = new();
|
||||||
@@ -1131,15 +1078,84 @@ namespace Game.Maps
|
|||||||
public BossInfo bossInfo;
|
public BossInfo bossInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UpdateSaveDataEvent
|
public struct UpdateBossStateSaveDataEvent
|
||||||
{
|
{
|
||||||
|
public DungeonEncounterRecord DungeonEncounter;
|
||||||
public uint BossId;
|
public uint BossId;
|
||||||
public EncounterState NewState;
|
public EncounterState NewState;
|
||||||
|
|
||||||
public UpdateSaveDataEvent(uint bossId, EncounterState state)
|
public UpdateBossStateSaveDataEvent(DungeonEncounterRecord dungeonEncounter, uint bossId, EncounterState state)
|
||||||
{
|
{
|
||||||
|
DungeonEncounter = dungeonEncounter;
|
||||||
BossId = bossId;
|
BossId = bossId;
|
||||||
NewState = state;
|
NewState = state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct UpdateAdditionalSaveDataEvent
|
||||||
|
{
|
||||||
|
public string Key;
|
||||||
|
public object Value;
|
||||||
|
|
||||||
|
public UpdateAdditionalSaveDataEvent(string key, object value)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PersistentInstanceScriptValueBase
|
||||||
|
{
|
||||||
|
protected InstanceScript _instance;
|
||||||
|
protected string _name;
|
||||||
|
protected object _value;
|
||||||
|
|
||||||
|
protected PersistentInstanceScriptValueBase(InstanceScript instance, string name, object value)
|
||||||
|
{
|
||||||
|
_instance = instance;
|
||||||
|
_name = name;
|
||||||
|
_value = value;
|
||||||
|
|
||||||
|
_instance.RegisterPersistentScriptValue(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetName() { return _name; }
|
||||||
|
|
||||||
|
public UpdateAdditionalSaveDataEvent CreateEvent()
|
||||||
|
{
|
||||||
|
return new UpdateAdditionalSaveDataEvent(_name, _value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadValue(long value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadValue(double value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PersistentInstanceScriptValue<T> : PersistentInstanceScriptValueBase
|
||||||
|
{
|
||||||
|
public PersistentInstanceScriptValue(InstanceScript instance, string name, T value) : base(instance, name, value) { }
|
||||||
|
|
||||||
|
public PersistentInstanceScriptValue<T> SetValue(T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
NotifyValueChanged();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyValueChanged()
|
||||||
|
{
|
||||||
|
_instance.instance.UpdateInstanceLock(CreateEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadValue(T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||||
|
*
|
||||||
|
* 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 3 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Framework.Constants;
|
||||||
|
using Game.DataStorage;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
|
namespace Game.Maps
|
||||||
|
{
|
||||||
|
class InstanceScriptDataReader
|
||||||
|
{
|
||||||
|
public enum Result
|
||||||
|
{
|
||||||
|
Ok,
|
||||||
|
MalformedJson,
|
||||||
|
RootIsNotAnObject,
|
||||||
|
MissingHeader,
|
||||||
|
UnexpectedHeader,
|
||||||
|
MissingBossStates,
|
||||||
|
BossStatesIsNotAnObject,
|
||||||
|
UnknownBoss,
|
||||||
|
BossStateIsNotAnObject,
|
||||||
|
MissingBossState,
|
||||||
|
BossStateValueIsNotANumber,
|
||||||
|
AdditionalDataIsNotAnObject,
|
||||||
|
AdditionalDataUnexpectedValueType
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceScript _instance;
|
||||||
|
JsonDocument _doc;
|
||||||
|
|
||||||
|
public InstanceScriptDataReader(InstanceScript instance)
|
||||||
|
{
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Load(string 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_doc = JsonDocument.Parse(data);
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"JSON parser error {ex.Message} at {ex.LineNumber} while loading data for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.MalformedJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_doc.RootElement.ValueKind != JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ParseHeader()
|
||||||
|
{
|
||||||
|
if (!_doc.RootElement.TryGetProperty("Header", out JsonElement header))
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Missing data header for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.MissingHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.GetString() != _instance.GetHeader())
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Incorrect data header for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}], expected \"{_instance.GetHeader()}\" got \"{header.GetString()}\"");
|
||||||
|
return Result.UnexpectedHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ParseBossStates()
|
||||||
|
{
|
||||||
|
if (!_doc.RootElement.TryGetProperty("BossStates", out JsonElement bossStates))
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Missing boss states for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.MissingBossStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bossStates.ValueKind != JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Boss states is not an array for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.BossStatesIsNotAnObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int bossId = 0; bossId < bossStates.GetArrayLength(); ++bossId)
|
||||||
|
{
|
||||||
|
if (bossId >= _instance.GetEncounterCount())
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Boss states has entry for boss with higher id ({bossId}) than number of bosses ({_instance.GetEncounterCount()}) for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.UnknownBoss;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bossState = bossStates[bossId];
|
||||||
|
if (bossState.ValueKind != JsonValueKind.Number)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Boss state for boss ({bossId}) is not a number for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.BossStateIsNotAnObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncounterState state = (EncounterState)bossState.GetInt32();
|
||||||
|
if (state == EncounterState.InProgress || state == EncounterState.Fail || state == EncounterState.Special)
|
||||||
|
state = EncounterState.NotStarted;
|
||||||
|
|
||||||
|
if (state < EncounterState.ToBeDecided)
|
||||||
|
_instance.SetBossState((uint)bossId, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ParseAdditionalData()
|
||||||
|
{
|
||||||
|
if (!_doc.RootElement.TryGetProperty("AdditionalData", out JsonElement moreData))
|
||||||
|
return Result.Ok;
|
||||||
|
|
||||||
|
if (moreData.ValueKind != JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Additional data is not an object for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.AdditionalDataIsNotAnObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (PersistentInstanceScriptValueBase valueBase in _instance.GetPersistentScriptValues())
|
||||||
|
{
|
||||||
|
if (moreData.TryGetProperty(valueBase.GetName(), out JsonElement value) && value.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
|
if (value.ValueKind != JsonValueKind.Number)
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Scripts, $"Additional data value for key {valueBase.GetName()} is not a number for instance {GetInstanceId()} [{GetMapId()}-{GetMapName()} | {GetDifficultyId()}-{GetDifficultyName()}]");
|
||||||
|
return Result.AdditionalDataUnexpectedValueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.TryGetDouble(out double doubleValue))
|
||||||
|
valueBase.LoadValue(doubleValue);
|
||||||
|
else
|
||||||
|
valueBase.LoadValue(value.GetInt64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint GetInstanceId() { return _instance.instance.GetInstanceId(); }
|
||||||
|
|
||||||
|
uint GetMapId() { return _instance.instance.GetId(); }
|
||||||
|
|
||||||
|
string GetMapName() { return _instance.instance.GetMapName(); }
|
||||||
|
|
||||||
|
uint GetDifficultyId() { return (uint)_instance.instance.GetDifficultyID(); }
|
||||||
|
|
||||||
|
string GetDifficultyName() { return CliDB.DifficultyStorage.LookupByKey(_instance.instance.GetDifficultyID()).Name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstanceScriptDataWriter
|
||||||
|
{
|
||||||
|
InstanceScript _instance;
|
||||||
|
JsonObject _doc = new();
|
||||||
|
|
||||||
|
public InstanceScriptDataWriter(InstanceScript instance)
|
||||||
|
{
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetString()
|
||||||
|
{
|
||||||
|
using var stream = new MemoryStream();
|
||||||
|
using (var writer = new Utf8JsonWriter(stream))
|
||||||
|
{
|
||||||
|
_doc.WriteTo(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(stream.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FillData(bool withValues = true)
|
||||||
|
{
|
||||||
|
_doc.Add("Header", _instance.GetHeader());
|
||||||
|
|
||||||
|
JsonArray bossStates = new();
|
||||||
|
for (uint bossId = 0; bossId < _instance.GetEncounterCount(); ++bossId)
|
||||||
|
bossStates.Add(JsonValue.Create((int)(withValues ? _instance.GetBossState(bossId) : EncounterState.NotStarted)));
|
||||||
|
|
||||||
|
_doc.Add("BossStates", bossStates);
|
||||||
|
|
||||||
|
if (!_instance.GetPersistentScriptValues().Empty())
|
||||||
|
{
|
||||||
|
JsonObject moreData = new();
|
||||||
|
foreach (PersistentInstanceScriptValueBase additionalValue in _instance.GetPersistentScriptValues())
|
||||||
|
{
|
||||||
|
if (withValues)
|
||||||
|
{
|
||||||
|
UpdateAdditionalSaveDataEvent data = additionalValue.CreateEvent();
|
||||||
|
if (data.Value is double)
|
||||||
|
moreData.Add(data.Key, (double)data.Value);
|
||||||
|
else
|
||||||
|
moreData.Add(data.Key, (long)data.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
moreData.Add(additionalValue.GetName(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
_doc.Add("AdditionalData", moreData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FillDataFrom(string data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_doc = JsonNode.Parse(data).AsObject();
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
FillData(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBossState(UpdateBossStateSaveDataEvent data)
|
||||||
|
{
|
||||||
|
var array = _doc["BossStates"].AsArray();
|
||||||
|
array[(int)data.BossId] = (int)data.NewState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAdditionalData(UpdateAdditionalSaveDataEvent data)
|
||||||
|
{
|
||||||
|
var jObject = _doc["AdditionalData"].AsObject();
|
||||||
|
if (data.Value is double)
|
||||||
|
jObject[data.Key] = (double)data.Value;
|
||||||
|
else
|
||||||
|
jObject[data.Key] = (long)data.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
-6
@@ -4990,20 +4990,18 @@ namespace Game.Maps
|
|||||||
return Global.ObjectMgr.GetScriptName(i_script_id);
|
return Global.ObjectMgr.GetScriptName(i_script_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateInstanceLock(DungeonEncounterRecord dungeonEncounter, UpdateSaveDataEvent updateSaveDataEvent)
|
public void UpdateInstanceLock(UpdateBossStateSaveDataEvent updateSaveDataEvent)
|
||||||
{
|
{
|
||||||
if (i_instanceLock != null)
|
if (i_instanceLock != null)
|
||||||
{
|
{
|
||||||
uint instanceCompletedEncounters = i_instanceLock.GetData().CompletedEncountersMask;
|
uint instanceCompletedEncounters = i_instanceLock.GetData().CompletedEncountersMask | (1u << updateSaveDataEvent.DungeonEncounter.Bit);
|
||||||
if (dungeonEncounter != null)
|
|
||||||
instanceCompletedEncounters |= 1u << dungeonEncounter.Bit;
|
|
||||||
|
|
||||||
MapDb2Entries entries = new(GetEntry(), GetMapDifficulty());
|
MapDb2Entries entries = new(GetEntry(), GetMapDifficulty());
|
||||||
|
|
||||||
SQLTransaction trans = new();
|
SQLTransaction trans = new();
|
||||||
|
|
||||||
if (entries.IsInstanceIdBound())
|
if (entries.IsInstanceIdBound())
|
||||||
Global.InstanceLockMgr.UpdateSharedInstanceLock(trans, new InstanceLockUpdateEvent(GetInstanceId(), i_data.GetSaveData(), instanceCompletedEncounters, dungeonEncounter));
|
Global.InstanceLockMgr.UpdateSharedInstanceLock(trans, new InstanceLockUpdateEvent(GetInstanceId(), i_data.GetSaveData(), instanceCompletedEncounters, updateSaveDataEvent.DungeonEncounter));
|
||||||
|
|
||||||
foreach (var player in GetPlayers())
|
foreach (var player in GetPlayers())
|
||||||
{
|
{
|
||||||
@@ -5018,7 +5016,49 @@ namespace Game.Maps
|
|||||||
|
|
||||||
bool isNewLock = playerLock == null || playerLock.GetData().CompletedEncountersMask == 0 || playerLock.IsExpired();
|
bool isNewLock = playerLock == null || playerLock.GetData().CompletedEncountersMask == 0 || playerLock.IsExpired();
|
||||||
|
|
||||||
InstanceLock newLock = Global.InstanceLockMgr.UpdateInstanceLockForPlayer(trans, player.GetGUID(), entries, new InstanceLockUpdateEvent(GetInstanceId(), i_data.UpdateSaveData(oldData, updateSaveDataEvent), instanceCompletedEncounters, dungeonEncounter));
|
InstanceLock newLock = Global.InstanceLockMgr.UpdateInstanceLockForPlayer(trans, player.GetGUID(), entries, new InstanceLockUpdateEvent(GetInstanceId(), i_data.UpdateBossStateSaveData(oldData, updateSaveDataEvent), instanceCompletedEncounters, updateSaveDataEvent.DungeonEncounter));
|
||||||
|
|
||||||
|
if (isNewLock)
|
||||||
|
{
|
||||||
|
InstanceSaveCreated data = new();
|
||||||
|
data.Gm = player.IsGameMaster();
|
||||||
|
player.SendPacket(data);
|
||||||
|
|
||||||
|
player.GetSession().SendCalendarRaidLockoutAdded(newLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DB.Characters.CommitTransaction(trans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateInstanceLock(UpdateAdditionalSaveDataEvent updateSaveDataEvent)
|
||||||
|
{
|
||||||
|
if (i_instanceLock != null)
|
||||||
|
{
|
||||||
|
uint instanceCompletedEncounters = i_instanceLock.GetData().CompletedEncountersMask;
|
||||||
|
|
||||||
|
MapDb2Entries entries = new(GetEntry(), GetMapDifficulty());
|
||||||
|
|
||||||
|
SQLTransaction trans = new();
|
||||||
|
|
||||||
|
if (entries.IsInstanceIdBound())
|
||||||
|
Global.InstanceLockMgr.UpdateSharedInstanceLock(trans, new InstanceLockUpdateEvent(GetInstanceId(), i_data.GetSaveData(), instanceCompletedEncounters, null));
|
||||||
|
|
||||||
|
foreach (var player in GetPlayers())
|
||||||
|
{
|
||||||
|
// never instance bind GMs with GM mode enabled
|
||||||
|
if (player.IsGameMaster())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
InstanceLock playerLock = Global.InstanceLockMgr.FindActiveInstanceLock(player.GetGUID(), entries);
|
||||||
|
string oldData = "";
|
||||||
|
if (playerLock != null)
|
||||||
|
oldData = playerLock.GetData().Data;
|
||||||
|
|
||||||
|
bool isNewLock = playerLock == null || playerLock.GetData().CompletedEncountersMask == 0 || playerLock.IsExpired();
|
||||||
|
|
||||||
|
InstanceLock newLock = Global.InstanceLockMgr.UpdateInstanceLockForPlayer(trans, player.GetGUID(), entries, new InstanceLockUpdateEvent(GetInstanceId(), i_data.UpdateAdditionalSaveData(oldData, updateSaveDataEvent), instanceCompletedEncounters, null));
|
||||||
|
|
||||||
if (isNewLock)
|
if (isNewLock)
|
||||||
{
|
{
|
||||||
|
|||||||
+14
-61
@@ -114,9 +114,6 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths
|
|||||||
|
|
||||||
class instance_blackrock_depths_InstanceMapScript : InstanceScript
|
class instance_blackrock_depths_InstanceMapScript : InstanceScript
|
||||||
{
|
{
|
||||||
uint[] encounter = new uint[MiscConst.MaxEncounter];
|
|
||||||
string str_data;
|
|
||||||
|
|
||||||
ObjectGuid EmperorGUID;
|
ObjectGuid EmperorGUID;
|
||||||
ObjectGuid PhalanxGUID;
|
ObjectGuid PhalanxGUID;
|
||||||
ObjectGuid MagmusGUID;
|
ObjectGuid MagmusGUID;
|
||||||
@@ -155,6 +152,7 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths
|
|||||||
public instance_blackrock_depths_InstanceMapScript(InstanceMap map) : base(map)
|
public instance_blackrock_depths_InstanceMapScript(InstanceMap map) : base(map)
|
||||||
{
|
{
|
||||||
SetHeaders("BRD");
|
SetHeaders("BRD");
|
||||||
|
SetBossNumber(MiscConst.MaxEncounter);
|
||||||
|
|
||||||
BarAleCount = 0;
|
BarAleCount = 0;
|
||||||
GhostKillCount = 0;
|
GhostKillCount = 0;
|
||||||
@@ -238,40 +236,30 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case DataTypes.TypeRingOfLaw:
|
case DataTypes.TypeRingOfLaw:
|
||||||
encounter[0] = data;
|
SetBossState(0, (EncounterState)data);
|
||||||
break;
|
break;
|
||||||
case DataTypes.TypeVault:
|
case DataTypes.TypeVault:
|
||||||
encounter[1] = data;
|
SetBossState(1, (EncounterState)data);
|
||||||
break;
|
break;
|
||||||
case DataTypes.TypeBar:
|
case DataTypes.TypeBar:
|
||||||
if (data == (uint)EncounterState.Special)
|
if (data == (uint)EncounterState.Special)
|
||||||
++BarAleCount;
|
++BarAleCount;
|
||||||
else
|
else
|
||||||
encounter[2] = data;
|
SetBossState(2, (EncounterState)data);
|
||||||
break;
|
break;
|
||||||
case DataTypes.TypeTombOfSeven:
|
case DataTypes.TypeTombOfSeven:
|
||||||
encounter[3] = data;
|
SetBossState(3, (EncounterState)data);
|
||||||
break;
|
break;
|
||||||
case DataTypes.TypeLyceum:
|
case DataTypes.TypeLyceum:
|
||||||
encounter[4] = data;
|
SetBossState(4, (EncounterState)data);
|
||||||
break;
|
break;
|
||||||
case DataTypes.TypeIronHall:
|
case DataTypes.TypeIronHall:
|
||||||
encounter[5] = data;
|
SetBossState(5, (EncounterState)data);
|
||||||
break;
|
break;
|
||||||
case DataTypes.DataGhostkill:
|
case DataTypes.DataGhostkill:
|
||||||
GhostKillCount += data;
|
GhostKillCount += data;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data == (uint)EncounterState.Done || GhostKillCount >= MiscConst.TombOfSevenBossNum)
|
|
||||||
{
|
|
||||||
OutSaveInstData();
|
|
||||||
|
|
||||||
str_data = $"{encounter[0]} {encounter[1]} {encounter[2]} {encounter[3]} {encounter[4]} {encounter[5]} {GhostKillCount}";
|
|
||||||
|
|
||||||
SaveToDB();
|
|
||||||
OutSaveInstDataComplete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override uint GetData(uint type)
|
public override uint GetData(uint type)
|
||||||
@@ -279,20 +267,20 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case DataTypes.TypeRingOfLaw:
|
case DataTypes.TypeRingOfLaw:
|
||||||
return encounter[0];
|
return (uint)GetBossState(0);
|
||||||
case DataTypes.TypeVault:
|
case DataTypes.TypeVault:
|
||||||
return encounter[1];
|
return (uint)GetBossState(1);
|
||||||
case DataTypes.TypeBar:
|
case DataTypes.TypeBar:
|
||||||
if (encounter[2] == (uint)EncounterState.InProgress && BarAleCount == 3)
|
if (GetBossState(2) == EncounterState.InProgress && BarAleCount == 3)
|
||||||
return (uint)EncounterState.Special;
|
return (uint)EncounterState.Special;
|
||||||
else
|
else
|
||||||
return encounter[2];
|
return (uint)GetBossState(2);
|
||||||
case DataTypes.TypeTombOfSeven:
|
case DataTypes.TypeTombOfSeven:
|
||||||
return encounter[3];
|
return (uint)GetBossState(3);
|
||||||
case DataTypes.TypeLyceum:
|
case DataTypes.TypeLyceum:
|
||||||
return encounter[4];
|
return (uint)GetBossState(4);
|
||||||
case DataTypes.TypeIronHall:
|
case DataTypes.TypeIronHall:
|
||||||
return encounter[5];
|
return (uint)GetBossState(5);
|
||||||
case DataTypes.DataGhostkill:
|
case DataTypes.DataGhostkill:
|
||||||
return GhostKillCount;
|
return GhostKillCount;
|
||||||
}
|
}
|
||||||
@@ -343,41 +331,6 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths
|
|||||||
return ObjectGuid.Empty;
|
return ObjectGuid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetSaveData()
|
|
||||||
{
|
|
||||||
return str_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Load(string str)
|
|
||||||
{
|
|
||||||
if (str.IsEmpty())
|
|
||||||
{
|
|
||||||
|
|
||||||
OutLoadInstDataFail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OutLoadInstData(str);
|
|
||||||
|
|
||||||
StringArguments loadStream = new(str);
|
|
||||||
|
|
||||||
for (var i = 0; i < encounter.Length; ++i)
|
|
||||||
encounter[i] = loadStream.NextUInt32();
|
|
||||||
|
|
||||||
GhostKillCount = loadStream.NextUInt32();
|
|
||||||
|
|
||||||
for (byte i = 0; i < encounter.Length; ++i)
|
|
||||||
if (encounter[i] == (uint)EncounterState.InProgress)
|
|
||||||
encounter[i] = (uint)EncounterState.NotStarted;
|
|
||||||
|
|
||||||
if (GhostKillCount > 0 && GhostKillCount < MiscConst.TombOfSevenBossNum)
|
|
||||||
GhostKillCount = 0;//reset tomb of seven event
|
|
||||||
if (GhostKillCount >= MiscConst.TombOfSevenBossNum)
|
|
||||||
GhostKillCount = MiscConst.TombOfSevenBossNum;
|
|
||||||
|
|
||||||
OutLoadInstDataComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TombOfSevenEvent()
|
void TombOfSevenEvent()
|
||||||
{
|
{
|
||||||
if (GhostKillCount < MiscConst.TombOfSevenBossNum && !TombBossGUIDs[TombEventCounter].IsEmpty())
|
if (GhostKillCount < MiscConst.TombOfSevenBossNum && !TombBossGUIDs[TombEventCounter].IsEmpty())
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.MoltenCore
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSaveDataMore(string data)
|
public override void AfterDataLoad()
|
||||||
{
|
{
|
||||||
if (CheckMajordomoExecutus())
|
if (CheckMajordomoExecutus())
|
||||||
_executusSchedule = true;
|
_executusSchedule = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user