Core/Scripts: Save instance data in JSON format

Port From (https://github.com/TrinityCore/TrinityCore/commit/76be303351ae398b7f9e69e4c472cb5b05fce45e)
This commit is contained in:
hondacrx
2022-10-04 14:04:16 -04:00
parent 66c7047a29
commit 67d6fa48eb
7 changed files with 454 additions and 166 deletions
+109 -93
View File
@@ -44,17 +44,6 @@ namespace Game.Maps
InstanceScenario scenario = instance.GetInstanceScenario();
if (scenario != null)
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()
@@ -104,9 +93,7 @@ namespace Game.Maps
public void SetHeaders(string dataHeaders)
{
foreach (char header in dataHeaders)
if (char.IsLetter(header))
headers.Add(header);
headers = dataHeaders;
}
public void LoadBossBoundaries(BossBoundaryEntry[] data)
@@ -178,7 +165,7 @@ namespace Game.Maps
for (int i = 0; i < MapConst.MaxDungeonEncountersPerBoss; ++i)
bosses[bossId].DungeonEncounters[i] = CliDB.DungeonEncounterStorage.LookupByKey(dungeonEncounterIds[i]);
}
public virtual void UpdateDoorState(GameObject door)
{
var range = doors.LookupByKey(door.GetEntry());
@@ -430,8 +417,8 @@ namespace Game.Maps
bossInfo.state = state;
SaveToDB();
if (state == EncounterState.Done && dungeonEncounter != null)
instance.UpdateInstanceLock(dungeonEncounter, new(id, state));
if (dungeonEncounter != null)
instance.UpdateInstanceLock(new UpdateBossStateSaveDataEvent(dungeonEncounter, id, state));
}
for (uint type = 0; type < (int)DoorType.Max; ++type)
@@ -470,7 +457,7 @@ namespace Game.Maps
UpdateSpawnGroups();
}
public virtual void Load(string data)
public void Load(string data)
{
if (string.IsNullOrEmpty(data))
{
@@ -480,12 +467,11 @@ namespace Game.Maps
OutLoadInstData(data);
var loadStream = new StringArguments(data);
if (ReadSaveDataHeaders(loadStream))
InstanceScriptDataReader reader = new(this);
if (reader.Load(data) == InstanceScriptDataReader.Result.Ok)
{
ReadSaveDataBossStates(loadStream);
ReadSaveDataMore(loadStream);
UpdateSpawnGroups();
AfterDataLoad();
}
else
OutLoadInstDataFail();
@@ -493,84 +479,39 @@ namespace Game.Maps
OutLoadInstDataComplete();
}
bool ReadSaveDataHeaders(StringArguments data)
{
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()
public string GetSaveData()
{
OutSaveInstData();
StringBuilder saveStream = new();
InstanceScriptDataWriter writer = new(this);
WriteSaveDataHeaders(saveStream);
WriteSaveDataBossStates(saveStream);
WriteSaveDataMore(saveStream);
writer.FillData();
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())
return GetSaveData();
int position = (int)(headers.Count + saveEvent.BossId) * 2;
StringBuilder newData = new(oldData);
if (position >= oldData.Length)
{
// 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);
newData.Insert(position, $"{(uint)saveEvent.NewState}0");
return newData.ToString();
}
void WriteSaveDataHeaders(StringBuilder data)
{
foreach (char header in headers)
data.AppendFormat("{0} ", header);
InstanceScriptDataWriter writer = new(this);
writer.FillDataFrom(oldData);
writer.SetBossState(saveEvent);
return writer.GetString();
}
void WriteSaveDataBossStates(StringBuilder data)
public string UpdateAdditionalSaveData(string oldData, UpdateAdditionalSaveDataEvent saveEvent)
{
foreach (BossInfo bossInfo in bosses.Values)
data.AppendFormat("{0} ", (uint)bossInfo.state);
if (!instance.GetMapDifficulty().IsUsingEncounterLocks())
return GetSaveData();
InstanceScriptDataWriter writer = new(this);
writer.FillDataFrom(oldData);
writer.SetAdditionalData(saveEvent);
return writer.GetString();
}
public void HandleGameObject(ObjectGuid guid, bool open, GameObject go = null)
@@ -763,7 +704,7 @@ namespace Game.Maps
return false;
}
public void SetEntranceLocation(uint worldSafeLocationId)
{
_entranceId = worldSafeLocationId;
@@ -998,6 +939,12 @@ namespace Game.Maps
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)
{
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 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; }
// Override this function to validate all additional data loads
public virtual void AfterDataLoad() { }
public InstanceMap instance;
List<char> headers = new();
string headers;
Dictionary<uint, BossInfo> bosses = new();
List<PersistentInstanceScriptValueBase> _persistentScriptValues = new();
MultiMap<uint, DoorInfo> doors = new();
Dictionary<uint, MinionInfo> minions = new();
Dictionary<uint, uint> _creatureInfo = new();
@@ -1131,15 +1078,84 @@ namespace Game.Maps
public BossInfo bossInfo;
}
public struct UpdateSaveDataEvent
public struct UpdateBossStateSaveDataEvent
{
public DungeonEncounterRecord DungeonEncounter;
public uint BossId;
public EncounterState NewState;
public UpdateSaveDataEvent(uint bossId, EncounterState state)
public UpdateBossStateSaveDataEvent(DungeonEncounterRecord dungeonEncounter, uint bossId, EncounterState state)
{
DungeonEncounter = dungeonEncounter;
BossId = bossId;
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;
}
}
}