Skip to content

Commit af0667a

Browse files
committed
Implement archive-based serialization for PropertyCollection
1 parent b1ccadf commit af0667a

File tree

3 files changed

+101
-37
lines changed

3 files changed

+101
-37
lines changed

src/MHServerEmu.Games/Common/Serializer.cs

-19
Original file line numberDiff line numberDiff line change
@@ -140,25 +140,6 @@ public static bool Transfer(Archive archive, ref PropertyId ioData)
140140
return success;
141141
}
142142

143-
public static bool Transfer(Archive archive, ref PropertyValue ioData)
144-
{
145-
bool success = true;
146-
147-
if (archive.IsPacking)
148-
{
149-
ulong value = ioData;
150-
success &= Transfer(archive, ref value);
151-
}
152-
else
153-
{
154-
ulong value = 0;
155-
success &= Transfer(archive, ref value);
156-
ioData = value;
157-
}
158-
159-
return success;
160-
}
161-
162143
// Other
163144

164145
public static bool Transfer(Archive archive, ref TimeSpan ioData)

src/MHServerEmu.Games/Properties/PropertyCollection.cs

+76-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
using Google.ProtocolBuffers;
33
using MHServerEmu.Core.Extensions;
44
using MHServerEmu.Core.Logging;
5+
using MHServerEmu.Core.Serialization;
56
using MHServerEmu.Core.VectorMath;
7+
using MHServerEmu.Games.Common;
68
using MHServerEmu.Games.GameData;
79
using MHServerEmu.Games.GameData.Calligraphy;
810
using MHServerEmu.Games.GameData.Prototypes;
@@ -12,7 +14,7 @@ namespace MHServerEmu.Games.Properties
1214
/// <summary>
1315
/// An aggregatable collection of key/value pairs of <see cref="PropertyId"/> and <see cref="PropertyValue"/>.
1416
/// </summary>
15-
public class PropertyCollection : IEnumerable<KeyValuePair<PropertyId, PropertyValue>>
17+
public class PropertyCollection : IEnumerable<KeyValuePair<PropertyId, PropertyValue>>, ISerialize
1618
{
1719
// TODO: Eval
1820
// TODO: PropertyChangeWatcher API: AttachWatcher(), RemoveWatcher(), RemoveAllWatchers()
@@ -25,7 +27,7 @@ public class PropertyCollection : IEnumerable<KeyValuePair<PropertyId, PropertyV
2527
private readonly Dictionary<PropertyId, CurveProperty> _curveList = new();
2628

2729
// Parent and child collections
28-
// NOTE: The client uses a tabletree structure to store those with PropertyCollection as key and an empty struct called EmptyDummyValue as value.
30+
// NOTE: The client uses a tabletree structure to store these with PropertyCollection as key and an empty struct called EmptyDummyValue as value.
2931
// I'm not sure what the intention there was, but it makes zero sense for us to do it the same way.
3032
private readonly HashSet<PropertyCollection> _parentCollections = new();
3133
private readonly HashSet<PropertyCollection> _childCollections = new();
@@ -399,7 +401,52 @@ public bool RemoveFromParent(PropertyCollection parentCollection)
399401

400402
#endregion
401403

402-
// TODO: PropertyCollection::serializeWithDefault
404+
public virtual bool Serialize(Archive archive)
405+
{
406+
return SerializeWithDefault(archive, null);
407+
}
408+
409+
public virtual bool SerializeWithDefault(Archive archive, PropertyCollection defaultCollection)
410+
{
411+
bool success = true;
412+
413+
// TODO: skip properties that match the default collection
414+
415+
if (archive.IsPacking)
416+
{
417+
// NOTE: PropertyCollection::serializeWithDefault() does a weird thing where it manipulates the archive buffer directly.
418+
// First it allocates 4 bytes for the number of properties, than it writes all the properties, and then it goes back
419+
// and updates the number. This is most likely a side effect of not all properties being saved to the database in the
420+
// original implementation.
421+
archive.WriteUnencodedStream((uint)_baseList.Count);
422+
423+
foreach (var kvp in _baseList)
424+
success &= SerializePropertyForPacking(kvp, archive, defaultCollection);
425+
}
426+
else
427+
{
428+
uint numProperties = 0;
429+
success &= archive.ReadUnencodedStream(ref numProperties);
430+
431+
for (uint i = 0; i < numProperties; i++)
432+
{
433+
PropertyId id = new();
434+
success &= Serializer.Transfer(archive, ref id);
435+
436+
PropertyInfo info = GameDatabase.PropertyInfoTable.LookupPropertyInfo(id.Enum);
437+
438+
ulong bits = 0;
439+
success &= Serializer.Transfer(archive, ref bits);
440+
441+
if (success)
442+
SetPropertyValue(id, ConvertBitsToValue(bits, info.DataType));
443+
}
444+
}
445+
446+
return success;
447+
}
448+
449+
#region REMOVEME: Old Serialization
403450

404451
/// <summary>
405452
/// Decodes <see cref="PropertyCollection"/> data from a <see cref="CodedInputStream"/>.
@@ -423,9 +470,24 @@ public virtual void Encode(CodedOutputStream stream)
423470
{
424471
stream.WriteRawUInt32((uint)_baseList.Count);
425472
foreach (var kvp in _baseList)
426-
SerializePropertyForPacking(kvp, stream);
473+
OLD_SerializePropertyForPacking(kvp, stream);
474+
}
475+
476+
/// <summary>
477+
/// Serializes a key/value pair of <see cref="PropertyId"/> and <see cref="PropertyValue"/> to a <see cref="CodedOutputStream"/>.
478+
/// </summary>
479+
protected static bool OLD_SerializePropertyForPacking(KeyValuePair<PropertyId, PropertyValue> kvp, CodedOutputStream stream)
480+
{
481+
// TODO: Serialize only properties that are different from the base collection for replication
482+
PropertyInfo info = GameDatabase.PropertyInfoTable.LookupPropertyInfo(kvp.Key.Enum);
483+
ulong valueBits = ConvertValueToBits(kvp.Value, info.DataType);
484+
stream.WriteRawVarint64(kvp.Key.Raw.ReverseBytes());
485+
stream.WriteRawVarint64(valueBits);
486+
return true;
427487
}
428488

489+
#endregion
490+
429491
/// <summary>
430492
/// Returns the <see cref="PropertyValue"/> with the specified <see cref="PropertyId"/>.
431493
/// Falls back to the default value for the property if this <see cref="PropertyCollection"/> does not contain it.
@@ -474,17 +536,19 @@ protected bool SetPropertyValue(PropertyId id, PropertyValue value, SetPropertyF
474536
return hasChanged || flags.HasFlag(SetPropertyFlags.Flag2); // Some kind of flag that forces property value update
475537
}
476538

477-
/// <summary>
478-
/// Serializes a key/value pair of <see cref="PropertyId"/> and <see cref="PropertyValue"/> to a <see cref="CodedOutputStream"/>.
479-
/// </summary>
480-
protected static bool SerializePropertyForPacking(KeyValuePair<PropertyId, PropertyValue> kvp, CodedOutputStream stream)
539+
protected static bool SerializePropertyForPacking(KeyValuePair<PropertyId, PropertyValue> kvp, Archive archive, PropertyCollection defaultCollection)
481540
{
541+
bool success = true;
542+
482543
// TODO: Serialize only properties that are different from the base collection for replication
483544
PropertyInfo info = GameDatabase.PropertyInfoTable.LookupPropertyInfo(kvp.Key.Enum);
484-
ulong valueBits = ConvertValueToBits(kvp.Value, info.DataType);
485-
stream.WriteRawVarint64(kvp.Key.Raw.ReverseBytes()); // Id is reversed so that it can be efficiently encoded into varint when all params are 0
486-
stream.WriteRawVarint64(valueBits);
487-
return true;
545+
546+
ulong id = kvp.Key.Raw.ReverseBytes(); // Id is reversed so that it can be efficiently encoded into varint when all params are 0
547+
ulong value = ConvertValueToBits(kvp.Value, info.DataType);
548+
success &= Serializer.Transfer(archive, ref id);
549+
success &= Serializer.Transfer(archive, ref value);
550+
551+
return success;
488552
}
489553

490554
// TODO: make value <-> bits conversion protected once we no longer need it for hacks

src/MHServerEmu.Games/Properties/ReplicatedPropertyCollection.cs

+25-6
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,53 @@
11
using System.Text;
22
using Google.ProtocolBuffers;
3+
using MHServerEmu.Core.Serialization;
4+
using MHServerEmu.Games.Common;
35
using MHServerEmu.Games.Network;
46

57
namespace MHServerEmu.Games.Properties
68
{
79
public class ReplicatedPropertyCollection : PropertyCollection, IArchiveMessageHandler
810
{
9-
public ulong ReplicationId { get; set; }
11+
private ulong _replicationId;
12+
13+
public ulong ReplicationId { get => _replicationId; set => _replicationId = value; }
14+
15+
public ReplicatedPropertyCollection(ulong replicationId = 0)
16+
{
17+
_replicationId = replicationId;
18+
}
1019

1120
public ReplicatedPropertyCollection(CodedInputStream stream)
1221
{
13-
ReplicationId = stream.ReadRawVarint64();
22+
_replicationId = stream.ReadRawVarint64();
1423
Decode(stream);
1524
}
1625

17-
public ReplicatedPropertyCollection(ulong replicationId = 0)
26+
public override bool SerializeWithDefault(Archive archive, PropertyCollection defaultCollection)
1827
{
19-
ReplicationId = replicationId;
28+
bool success = true;
29+
30+
// ArchiveMessageHandler::Serialize() -> move this to a common helper class?
31+
if (archive.IsReplication)
32+
{
33+
success &= Serializer.Transfer(archive, ref _replicationId);
34+
// TODO: register message dispatcher
35+
}
36+
37+
success &= base.SerializeWithDefault(archive, defaultCollection);
38+
return success;
2039
}
2140

2241
public override void Encode(CodedOutputStream stream)
2342
{
24-
stream.WriteRawVarint64(ReplicationId);
43+
stream.WriteRawVarint64(_replicationId);
2544
base.Encode(stream);
2645
}
2746

2847
public override string ToString()
2948
{
3049
StringBuilder sb = new();
31-
sb.AppendLine($"{nameof(ReplicationId)}: {ReplicationId}");
50+
sb.AppendLine($"{nameof(_replicationId)}: {_replicationId}");
3251
sb.Append(base.ToString());
3352
return sb.ToString();
3453
}

0 commit comments

Comments
 (0)