Skip to content

Commit 6b58d62

Browse files
committed
Clean up PrototypePropertyCollection
1 parent ae2df10 commit 6b58d62

File tree

1 file changed

+96
-56
lines changed

1 file changed

+96
-56
lines changed

src/MHServerEmu/Games/GameData/Calligraphy/PrototypePropertyCollection.cs

+96-56
Original file line numberDiff line numberDiff line change
@@ -4,146 +4,180 @@
44

55
namespace MHServerEmu.Games.GameData.Calligraphy
66
{
7+
/// <summary>
8+
/// A <see cref="PropertyCollection"/> that stores properties deserialized from Calligraphy prototype mixin field groups.
9+
/// </summary>
710
public class PrototypePropertyCollection : PropertyCollection
811
{
912
private static readonly Logger Logger = LogManager.CreateLogger();
1013

11-
private readonly Dictionary<ulong, PropertyId> _mixinPropertyLookup; // See SetKeyToPropertyId() for details on this
14+
// Child prototypes may override params of properties of their parents, so PrototypePropertyCollection has to
15+
// keep track of how contained property ids correspond to blueprints. For that purpose it uses a dictionary with
16+
// composite values made from blueprint copy number and property enum as keys.
17+
private readonly Dictionary<ulong, PropertyId> _mixinPropertyLookup;
1218

19+
/// <summary>
20+
/// Constructs a new blank <see cref="PrototypePropertyCollection"/> instance.
21+
/// </summary>
1322
public PrototypePropertyCollection()
1423
{
1524
_mixinPropertyLookup = new();
1625
}
1726

27+
/// <summary>
28+
/// Constructs a new <see cref="PrototypePropertyCollection"/> instance and copies mixin property lookups from another collection.
29+
/// </summary>
1830
public PrototypePropertyCollection(Dictionary<ulong, PropertyId> mixinPropertyLookup)
1931
{
2032
_mixinPropertyLookup = new(mixinPropertyLookup); // Copy mixin lookups
2133
}
2234

35+
/// <summary>
36+
/// Clones this <see cref="PrototypePropertyCollection"/>.
37+
/// </summary>
2338
public PrototypePropertyCollection ShallowCopy()
2439
{
2540
PrototypePropertyCollection newCollection = new(_mixinPropertyLookup);
2641
newCollection.FlattenCopyFrom(this, true);
2742
return newCollection;
2843
}
2944

30-
// The ugliness going on here is an attempt to imitate passing struct pointers C++ style.
31-
// TODO: Clean this mess up.
32-
45+
/// <summary>
46+
/// Sets a <see cref="PropertyValue"/> for a <see cref="PropertyId"/> from a mixin field group.
47+
/// </summary>
3348
public void SetPropertyFromMixin(PropertyValue value, PropertyId propertyId, byte blueprintCopyNum, byte paramsSetMask)
3449
{
3550
PropertyValue? existingValueRef = null;
36-
CurveProperty? curveProperty = null;
37-
PropertyId? curveIndex = null;
3851

39-
SetKeyToPropertyId(ref propertyId, blueprintCopyNum, paramsSetMask, ref existingValueRef, ref curveProperty, ref curveIndex);
52+
SetKeyToPropertyId(ref propertyId, blueprintCopyNum, paramsSetMask, ref existingValueRef);
4053
SetPropertyValue(propertyId, value);
4154
}
4255

56+
/// <summary>
57+
/// Updates the <see cref="PropertyId"/> of an existing <see cref="PropertyValue"/> set from a mixin field group.
58+
/// </summary>
4359
public void ReplacePropertyIdFromMixin(PropertyId newPropertyId, byte blueprintCopyNum, byte paramsSetMask)
4460
{
4561
PropertyValue? existingValueRef = new();
46-
CurveProperty? curveProperty = null;
47-
PropertyId? curveIndex = null;
4862

49-
if (SetKeyToPropertyId(ref newPropertyId, blueprintCopyNum, paramsSetMask, ref existingValueRef, ref curveProperty, ref curveIndex))
50-
SetPropertyValue(newPropertyId, (PropertyValue)existingValueRef); // Set property value only if there is something to replace
63+
// If the id got updated we need to reassign the existing value to the new id
64+
if (SetKeyToPropertyId(ref newPropertyId, blueprintCopyNum, paramsSetMask, ref existingValueRef))
65+
SetPropertyValue(newPropertyId, (PropertyValue)existingValueRef);
5166
}
5267

68+
/// <summary>
69+
/// Sets a curve property for a <see cref="PropertyId"/> from a mixin field group.
70+
/// </summary>
5371
public void SetCurvePropertyFromMixin(PropertyId propertyId, CurveId curveId, PropertyId indexProperty, PropertyInfo info, byte blueprintCopyNum)
5472
{
55-
PropertyValue? existingValueRef = null;
56-
CurveProperty? nullableOldCurve = new();
57-
PropertyId? nullableIndexProperty = indexProperty;
73+
CurveProperty oldCurve = new();
5874

59-
bool replaced = SetKeyToPropertyId(ref propertyId, blueprintCopyNum, 0xff, ref existingValueRef, ref nullableOldCurve, ref nullableIndexProperty);
60-
61-
var oldCurve = (CurveProperty)nullableOldCurve;
62-
indexProperty = (PropertyId)nullableIndexProperty;
75+
bool replaced = SetKeyToPropertyId(ref propertyId, blueprintCopyNum, 0xff, ref oldCurve, (PropertyId?)indexProperty);
6376

77+
// There must be a valid index property
6478
if (indexProperty == PropertyId.Invalid)
6579
{
66-
// If the prototype contained an invalid curve index, fall back to the old curve or default curve index from the property info
6780
if (replaced)
6881
{
6982
if (oldCurve.IndexPropertyId == PropertyId.Invalid)
7083
{
71-
Logger.Warn("Prototype property read error: trying to replace a curve property that has an invalid curve index");
84+
// Nothing to fall back on
85+
Logger.Error("Prototype property read error: trying to replace a curve property that has an invalid curve index");
7286
return;
7387
}
7488

89+
// If we are replacing a valid existing curve property, get the index property from it
7590
indexProperty = oldCurve.IndexPropertyId;
7691
}
7792
else
7893
{
94+
// If we are adding a new property fall back to the default curve defined in property info
7995
indexProperty = info.DefaultCurveIndex;
8096
}
8197
}
8298

8399
SetCurveProperty(propertyId, curveId, indexProperty, info, UInt32Flags.None, true);
84100
}
85101

102+
/// <summary>
103+
/// Updates the <see cref="PropertyId"/> and the curve index property for an existing curve property added from a mixin field group.
104+
/// </summary>
86105
public void ReplaceCurvePropertyIdFromMixin(PropertyId propertyId, PropertyId indexProperty, PropertyInfo info, byte blueprintCopyNum, byte paramsSetMask)
87106
{
88-
PropertyValue? existingValueRef = null;
89-
CurveProperty? nullableOldCurve = new();
90-
PropertyId? nullableIndexProperty = indexProperty;
107+
CurveProperty oldCurve = new();
91108

92-
if (SetKeyToPropertyId(ref propertyId, blueprintCopyNum, paramsSetMask, ref existingValueRef, ref nullableOldCurve, ref nullableIndexProperty))
93-
SetCurveProperty(propertyId, ((CurveProperty)nullableOldCurve).CurveId, (PropertyId)nullableIndexProperty, info, UInt32Flags.None, true);
109+
if (SetKeyToPropertyId(ref propertyId, blueprintCopyNum, paramsSetMask, ref oldCurve, (PropertyId?)indexProperty))
110+
SetCurveProperty(propertyId, oldCurve.CurveId, indexProperty, info, UInt32Flags.None, true);
94111
}
95112

113+
/// <summary>
114+
/// Updates the <see cref="PropertyId"/> for an existing curve property added from a mixin field group.
115+
/// </summary>
96116
public void ReplaceCurvePropertyIdFromMixin(PropertyId propertyId, PropertyInfo info, byte blueprintCopyNum, byte paramsSetMask)
97117
{
98-
PropertyValue? existingValueRef = null;
99-
CurveProperty? nullableOldCurve = new();
100-
PropertyId? nullableIndexProperty = null;
118+
CurveProperty oldCurve = new();
101119

102-
if (SetKeyToPropertyId(ref propertyId, blueprintCopyNum, paramsSetMask, ref existingValueRef, ref nullableOldCurve, ref nullableIndexProperty))
103-
{
104-
CurveProperty oldCurve = (CurveProperty)nullableOldCurve;
120+
if (SetKeyToPropertyId(ref propertyId, blueprintCopyNum, paramsSetMask, ref oldCurve, null))
105121
SetCurveProperty(propertyId, oldCurve.CurveId, oldCurve.IndexPropertyId, info, UInt32Flags.None, true);
106-
}
107122
}
108123

109-
private bool SetKeyToPropertyId(ref PropertyId propertyIdRef, byte blueprintCopyNum, byte paramsSetMask,
110-
ref PropertyValue? existingValueRef, ref CurveProperty? curveProp, ref PropertyId? curveIndex)
124+
/// <summary>
125+
/// Updates the <see cref="PropertyId"/> corresponding to a <see cref="PropertyEnum"/> / blueprint copy number pair. For use with non-curve properties.
126+
/// </summary>
127+
private bool SetKeyToPropertyId(ref PropertyId propertyIdRef, byte blueprintCopyNum, byte paramsSetMask, ref PropertyValue? existingValueRef)
111128
{
112-
// TODO: Make an overload of this method for handling curve properties
113129
bool valueIsReplaced = false;
114-
115-
// Child prototypes may override params of properties of their parents, so PrototypePropertyCollection has to
116-
// keep track of how contained property ids correspond to blueprints. For that purpose it uses a dictionary with
117-
// composite values made from blueprint copy number and property enum as keys.
118130
ulong key = ((ulong)blueprintCopyNum << 32) | (ulong)propertyIdRef.Enum;
119131

120132
// If the lookup dict already has this key it means we are modifying an existing property rather than adding a new one
121133
if (_mixinPropertyLookup.TryGetValue(key, out PropertyId existingPropertyId))
122134
{
123-
if (HasMatchingParams(propertyIdRef, existingPropertyId, 0xff) == false || curveIndex != null)
135+
if (HasMatchingParams(propertyIdRef, existingPropertyId, 0xff) == false)
124136
{
125137
valueIsReplaced = true;
126138

127-
// We need to cast null to one of the supported value types for this check because of all the implicit casting we are doing
139+
// If existingValueRef is not a null ref it means we are replacing an existing property id,
140+
// and we need to output existing PropertyValue to this ref so it can be reassigned to the new id.
141+
//
142+
// For null comparison we need to cast null to one of the supported PropertyValue types
143+
// for this check because of all the implicit casting we are doing.
128144
if (existingValueRef != (bool?)null)
129145
existingValueRef = GetPropertyValue(existingPropertyId);
130146

131-
if (curveProp != null)
147+
SetOverridenParams(ref propertyIdRef, existingPropertyId, paramsSetMask);
148+
RemoveProperty(existingPropertyId);
149+
}
150+
}
151+
152+
_mixinPropertyLookup[key] = propertyIdRef;
153+
return valueIsReplaced;
154+
}
155+
156+
/// <summary>
157+
/// Updates the <see cref="PropertyId"/> corresponding to a <see cref="PropertyEnum"/> / blueprint copy number pair. For use with curve properties.
158+
/// </summary>
159+
private bool SetKeyToPropertyId(ref PropertyId propertyIdRef, byte blueprintCopyNum, byte paramsSetMask, ref CurveProperty oldCurve, PropertyId? curveIndex)
160+
{
161+
bool valueIsReplaced = false;
162+
ulong key = ((ulong)blueprintCopyNum << 32) | (ulong)propertyIdRef.Enum;
163+
164+
// If the lookup dict already has this key it means we are modifying an existing property rather than adding a new one
165+
if (_mixinPropertyLookup.TryGetValue(key, out PropertyId existingPropertyId))
166+
{
167+
if (HasMatchingParams(propertyIdRef, existingPropertyId, 0xff) == false || curveIndex != null)
168+
{
169+
// If there is an existing CurveProperty assigned to this id and we are actually making changes,
170+
// we need to output this CurveProperty to be reassigned to the new id.
171+
CurveProperty? nullableExistingCurveProp = GetCurveProperty(existingPropertyId);
172+
if (nullableExistingCurveProp != null)
132173
{
133-
CurveProperty? nullableExistingCurveProp = GetCurveProperty(existingPropertyId);
134-
if (nullableExistingCurveProp != null)
135-
{
136-
var existingCurveProp = (CurveProperty)nullableExistingCurveProp;
137-
138-
if (curveIndex != null && propertyIdRef == existingPropertyId && existingCurveProp.IndexPropertyId == curveIndex)
139-
return false;
140-
141-
curveProp = existingCurveProp;
142-
}
143-
else
144-
{
145-
valueIsReplaced = false;
146-
}
174+
var existingCurveProp = (CurveProperty)nullableExistingCurveProp;
175+
176+
if (curveIndex != null && propertyIdRef == existingPropertyId && existingCurveProp.IndexPropertyId == curveIndex)
177+
return false; // No need to change anything
178+
179+
oldCurve = existingCurveProp;
180+
valueIsReplaced = true;
147181
}
148182

149183
SetOverridenParams(ref propertyIdRef, existingPropertyId, paramsSetMask);
@@ -155,6 +189,9 @@ private bool SetKeyToPropertyId(ref PropertyId propertyIdRef, byte blueprintCopy
155189
return valueIsReplaced;
156190
}
157191

192+
/// <summary>
193+
/// Compares parameters of two <see cref="PropertyId"/> values. The provided mask defines params that need to be compared.
194+
/// </summary>
158195
private bool HasMatchingParams(PropertyId left, PropertyId right, byte paramsSetMask)
159196
{
160197
if (paramsSetMask == 0xff) return left == right;
@@ -174,6 +211,9 @@ private bool HasMatchingParams(PropertyId left, PropertyId right, byte paramsSet
174211
return true;
175212
}
176213

214+
/// <summary>
215+
/// Overrides <see cref="PropertyParam"/> values of a <see cref="PropertyId"/>. The provided mask defines params that need to be overriden.
216+
/// </summary>
177217
private void SetOverridenParams(ref PropertyId destId, PropertyId sourceId, byte paramsSetMask)
178218
{
179219
if (paramsSetMask == 0xff) return;

0 commit comments

Comments
 (0)