Skip to content

Commit

Permalink
Synchronise placed markers
Browse files Browse the repository at this point in the history
  • Loading branch information
Extremelyd1 committed Aug 1, 2024
1 parent 18c1efd commit 78e0a6e
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 11 deletions.
6 changes: 6 additions & 0 deletions HKMP/Game/Client/Save/SaveDataMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ public static SaveDataMapping Instance {
[JsonProperty("bossStatueCompletionVariables")]
public readonly List<string> BossStatueCompletionVariables;

/// <summary>
/// Deserialized list of strings that represent variable names with the type of a vector3 list.
/// </summary>
[JsonProperty("vectorListVariables")]
public readonly List<string> VectorListVariables;

/// <summary>
/// Initializes the class by converting the deserialized data fields into the various dictionaries and lookups.
/// </summary>
Expand Down
105 changes: 94 additions & 11 deletions HKMP/Game/Client/Save/SaveManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,16 @@ internal class SaveManager {
/// Dictionary of BossStatue.Completion structs in the PlayerData for comparing changes against.
/// </summary>
private readonly Dictionary<string, BossStatue.Completion> _bsCompHashes;

/// <summary>
/// Dictionary of hash codes for vector list variables in the PlayerData for comparing changes against.
/// </summary>
private readonly Dictionary<string, int> _vectorListHashes;

/// <summary>
/// List of FieldInfo for fields in PlayerData that are simple values that should be synced. Used for looping
/// over to check for changes and network those changes.
/// </summary>
private readonly List<FieldInfo> _playerDataSyncFields;

/// <summary>
Expand All @@ -89,6 +98,7 @@ public SaveManager(NetClient netClient, PacketManager packetManager, EntityManag
_stringListHashes = new Dictionary<string, int>();
_bsdCompHashes = new Dictionary<string, BossSequenceDoor.Completion>();
_bsCompHashes = new Dictionary<string, BossStatue.Completion>();
_vectorListHashes = new Dictionary<string, int>();
_playerDataSyncFields = new List<FieldInfo>();
}

Expand All @@ -115,6 +125,15 @@ public void Initialize() {

foreach (var field in typeof(PlayerData).GetFields()) {
var fieldName = field.Name;
if (
SaveDataMapping.StringListVariables.Contains(fieldName) ||
SaveDataMapping.BossSequenceDoorCompletionVariables.Contains(fieldName) ||
SaveDataMapping.BossStatueCompletionVariables.Contains(fieldName) ||
SaveDataMapping.VectorListVariables.Contains(fieldName)
) {
continue;
}

if (SaveDataMapping.PlayerDataSyncProperties.TryGetValue(fieldName, out var syncProps) && syncProps.Sync) {
_playerDataSyncFields.Add(field);
}
Expand Down Expand Up @@ -199,6 +218,13 @@ byte[] EncodeString(string stringValue) {
return BitConverter.GetBytes(index);
}

byte[] EncodeVector3(Vector3 vec3Value) {
return BitConverter.GetBytes(vec3Value.x)
.Concat(BitConverter.GetBytes(vec3Value.y))
.Concat(BitConverter.GetBytes(vec3Value.z))
.ToArray();
}

if (value is bool bValue) {
return [(byte) (bValue ? 1 : 0)];
}
Expand All @@ -216,10 +242,7 @@ byte[] EncodeString(string stringValue) {
}

if (value is Vector3 vecValue) {
return BitConverter.GetBytes(vecValue.x)
.Concat(BitConverter.GetBytes(vecValue.y))
.Concat(BitConverter.GetBytes(vecValue.z))
.ToArray();
return EncodeVector3(vecValue);
}

if (value is List<string> listValue) {
Expand Down Expand Up @@ -263,11 +286,29 @@ byte[] EncodeString(string stringValue) {
return [EncodeUtil.GetByte(bools)];
}

if (value is List<Vector3> vecListValue) {
if (vecListValue.Count > ushort.MaxValue) {
throw new ArgumentOutOfRangeException($"Could not encode vector list length: {vecListValue.Count}");
}

var length = (ushort) vecListValue.Count;

IEnumerable<byte> byteArray = BitConverter.GetBytes(length);

for (var i = 0; i < length; i++) {
var encoded = EncodeVector3(vecListValue[i]);

byteArray = byteArray.Concat(encoded);
}

return byteArray.ToArray();
}

if (value is MapZone mapZone) {
return [(byte) mapZone];
}

throw new NotImplementedException($"No encoding implementation for type: {value.GetType()}");
throw new ArgumentException($"No encoding implementation for type: {value.GetType()}");
}

/// <summary>
Expand Down Expand Up @@ -588,7 +629,7 @@ Func<TCheck, TCheck, bool> changeFunc
Logger.Info($"Compound variable ({varName}) changed value");

checkDict[varName] = newCheck;

if (!SaveDataMapping.PlayerDataSyncProperties.TryGetValue(varName, out var syncProps)) {
continue;
}
Expand Down Expand Up @@ -655,6 +696,13 @@ Func<TCheck, TCheck, bool> changeFunc
b1.seenTier3Unlock != b2.seenTier3Unlock ||
b1.usingAltVersion != b2.usingAltVersion
);

CheckUpdates<List<Vector3>, int>(
SaveDataMapping.VectorListVariables,
_vectorListHashes,
GetVectorListHashCode,
(hash1, hash2) => hash1 != hash2
);
}

/// <summary>
Expand Down Expand Up @@ -766,7 +814,7 @@ private void UpdateSaveWithData(ushort index, byte[] encodedValue) {
list.Add(sceneName);
}

// First set the new string list hash so we don't trigger an update and subsequently a feedback loop
// First set the new string list hash, so we don't trigger an update and subsequently a feedback loop
_stringListHashes[name] = GetStringListHashCode(list);

pd.SetVariableInternal(name, list);
Expand All @@ -788,7 +836,7 @@ private void UpdateSaveWithData(ushort index, byte[] encodedValue) {
boundSoul = byte2 == 1
};

// First set the new bsdComp obj in the dict so we don't trigger an update and subsequently a
// First set the new bsdComp obj in the dict, so we don't trigger an update and subsequently a
// feedback loop
_bsdCompHashes[name] = bsdComp;

Expand All @@ -806,19 +854,39 @@ private void UpdateSaveWithData(ushort index, byte[] encodedValue) {
usingAltVersion = bools[6]
};

// First set the new bsComp obj in the dict so we don't trigger an update and subsequently a
// First set the new bsComp obj in the dict, so we don't trigger an update and subsequently a
// feedback loop
_bsCompHashes[name] = bsComp;

pd.SetVariableInternal(name, bsComp);
} else if (type == typeof(List<Vector3>)) {
var length = BitConverter.ToUInt16(encodedValue, 0);

var list = new List<Vector3>();
for (var i = 0; i < length; i++) {
// Decode the floats of the vector with offset indices 2, 6, and 10 because we already read 2
// bytes as the length. The index is multiplied by 12 as this is the length of a single float
var value = new Vector3(
BitConverter.ToSingle(encodedValue, 2 + i * 12),
BitConverter.ToSingle(encodedValue, 6 + i * 12),
BitConverter.ToSingle(encodedValue, 10 + i * 12)
);

list.Add(value);
}

// First set the new string list hash, so we don't trigger an update and subsequently a feedback loop
_vectorListHashes[name] = GetVectorListHashCode(list);

pd.SetVariableInternal(name, list);
} else if (type == typeof(MapZone)) {
if (valueLength != 1) {
Logger.Warn($"Received save update with incorrect value length for MapZone: {valueLength}");
}

pd.SetVariableInternal(name, (MapZone) encodedValue[0]);
} else {
throw new NotImplementedException($"Could not decode type: {type}");
throw new ArgumentException($"Could not decode type: {type}");
}

_saveChanges.ApplyPlayerDataSaveChange(name);
Expand Down Expand Up @@ -883,7 +951,6 @@ private void UpdateSaveWithData(ushort index, byte[] encodedValue) {

Logger.Info($"Received persistent int save update: {itemData.Id}, {itemData.SceneName}, {value}");

// TODO: make the _persistentFsmData a dictionary for quicker lookups
foreach (var persistentFsmData in _persistentFsmData) {
var existingItemData = persistentFsmData.PersistentItemData;

Expand Down Expand Up @@ -1039,4 +1106,20 @@ private static int GetStringListHashCode(List<string> list) {
.Select(item => item.GetHashCode())
.Aggregate((total, nextCode) => total ^ nextCode);
}

/// <summary>
/// Get the hash code of the combined values in a Vector3 list.
/// </summary>
/// <param name="list">The list of Vector3 to calculate the hash code for.</param>
/// <returns>0 if the list is empty, otherwise a hash code matching the specific order of Vector3 in the list.
/// </returns>
private static int GetVectorListHashCode(List<Vector3> list) {
if (list.Count == 0) {
return 0;
}

return list
.Select(item => item.GetHashCode())
.Aggregate((total, nextCode) => total ^ nextCode);
}
}
26 changes: 26 additions & 0 deletions HKMP/Resource/save-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -4747,6 +4747,26 @@
"SyncType": "Player",
"IgnoreSceneHost": true
},
"placedMarkers_r": {
"Sync": true,
"SyncType": "Player",
"IgnoreSceneHost": true
},
"placedMarkers_b": {
"Sync": true,
"SyncType": "Player",
"IgnoreSceneHost": true
},
"placedMarkers_y": {
"Sync": true,
"SyncType": "Player",
"IgnoreSceneHost": true
},
"placedMarkers_w": {
"Sync": true,
"SyncType": "Player",
"IgnoreSceneHost": true
},
"openedTramLower": {
"Sync": true,
"SyncType": "Player",
Expand Down Expand Up @@ -31465,5 +31485,11 @@
"statueStateZote",
"statueStateNoskHornet",
"statueStateMantisLordsExtra"
],
"vectorListVariables": [
"placedMarkers_r",
"placedMarkers_b",
"placedMarkers_y",
"placedMarkers_w"
]
}

0 comments on commit 78e0a6e

Please sign in to comment.