-
Notifications
You must be signed in to change notification settings - Fork 431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prototype reload + other enhancements #5371
base: master
Are you sure you want to change the base?
Changes from all commits
ed55f3b
0a460b7
a793774
37fccca
1e3d149
6b7a8b2
83c2f78
6f65e38
c20dfe1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using Robust.Shared.GameObjects; | ||
using Robust.Shared.IoC; | ||
using Robust.Shared.Localization; | ||
|
@@ -21,7 +22,7 @@ namespace Robust.Shared.Prototypes | |
/// Prototype that represents game entities. | ||
/// </summary> | ||
[Prototype("entity", -1)] | ||
public sealed partial class EntityPrototype : IPrototype, IInheritingPrototype, ISerializationHooks | ||
public sealed partial class EntityPrototype : IPrototype, IInheritingPrototype, ISerializationHooks, IEquatable<EntityPrototype> | ||
{ | ||
private ILocalizationManager _loc = default!; | ||
|
||
|
@@ -288,7 +289,7 @@ public override string ToString() | |
public record ComponentRegistryEntry(IComponent Component, MappingDataNode Mapping); | ||
|
||
[DataDefinition] | ||
public sealed partial class EntityPlacementProperties | ||
public sealed partial class EntityPlacementProperties : IEquatable<EntityPlacementProperties> | ||
{ | ||
public bool PlacementOverriden { get; private set; } | ||
public bool SnapOverriden { get; private set; } | ||
|
@@ -332,6 +333,27 @@ public HashSet<string> SnapFlags | |
_snapFlags = value; | ||
} | ||
} | ||
|
||
public bool Equals(EntityPlacementProperties? other) | ||
{ | ||
if (ReferenceEquals(null, other)) return false; | ||
if (ReferenceEquals(this, other)) return true; | ||
return _placementMode == other._placementMode && | ||
_placementOffset.Equals(other._placementOffset) && | ||
Equals(MountingPoints, other.MountingPoints) && | ||
PlacementRange == other.PlacementRange && | ||
_snapFlags.SetEquals(other._snapFlags); | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
{ | ||
return ReferenceEquals(this, obj) || obj is EntityPlacementProperties other && Equals(other); | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
return HashCode.Combine(_placementMode, _placementOffset, MountingPoints, PlacementRange, _snapFlags); | ||
} | ||
} | ||
/*private class PrototypeSerializationContext : YamlObjectSerializer.Context | ||
{ | ||
|
@@ -400,9 +422,71 @@ public override bool TryGetDataCache(string field, out object? value) | |
return prototype.DataCache.TryGetValue(field, out value); | ||
} | ||
}*/ | ||
public bool Equals(EntityPrototype? other) | ||
{ | ||
if (ReferenceEquals(null, other)) return false; | ||
if (ReferenceEquals(this, other)) return true; | ||
var result = ID == other.ID && | ||
Equals(_locPropertiesSet, other._locPropertiesSet) && | ||
Equals(CategoriesInternal, other.CategoriesInternal) && | ||
PlacementProperties.Equals(other.PlacementProperties) && | ||
SetName == other.SetName && | ||
SetDesc == other.SetDesc && | ||
SetSuffix == other.SetSuffix && | ||
CustomLocalizationID == other.CustomLocalizationID && | ||
HideSpawnMenu == other.HideSpawnMenu && | ||
MapSavable == other.MapSavable && | ||
Abstract == other.Abstract; | ||
|
||
if (!result) | ||
return false; | ||
|
||
if ((Parents == null && other.Parents != null) || | ||
(Parents != null && other.Parents == null)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (Parents != null && other.Parents != null) | ||
{ | ||
for (var i = 0; i < Parents.Length; i++) | ||
{ | ||
if (Parents[i] != other.Parents[i]) | ||
return false; | ||
} | ||
} | ||
|
||
return Components.Equals(other.Components); | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
{ | ||
return ReferenceEquals(this, obj) || obj is EntityPrototype other && Equals(other); | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
var hashCode = new HashCode(); | ||
hashCode.Add(_loc); | ||
hashCode.Add(_locPropertiesSet); | ||
hashCode.Add(CategoriesInternal); | ||
hashCode.Add(PlacementProperties); | ||
hashCode.Add(ID); | ||
hashCode.Add(SetName); | ||
hashCode.Add(SetDesc); | ||
hashCode.Add(SetSuffix); | ||
hashCode.Add(Categories); | ||
hashCode.Add(CustomLocalizationID); | ||
hashCode.Add(HideSpawnMenu); | ||
hashCode.Add(MapSavable); | ||
hashCode.Add(Parents); | ||
hashCode.Add(Abstract); | ||
hashCode.Add(Components); | ||
return hashCode.ToHashCode(); | ||
} | ||
} | ||
|
||
public sealed class ComponentRegistry : Dictionary<string, EntityPrototype.ComponentRegistryEntry>, IEntityLoadContext, ISerializationContext | ||
public sealed class ComponentRegistry : Dictionary<string, EntityPrototype.ComponentRegistryEntry>, IEntityLoadContext, ISerializationContext, IEquatable<ComponentRegistry> | ||
{ | ||
public ComponentRegistry() | ||
{ | ||
|
@@ -432,5 +516,37 @@ public bool ShouldSkipComponent(string compName) | |
|
||
public SerializationManager.SerializerProvider SerializerProvider { get; } = new(); | ||
public bool WritingReadingPrototypes { get; } = true; | ||
|
||
public bool Equals(ComponentRegistry? other) | ||
{ | ||
Comment on lines
+520
to
+521
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This equality override, and thus also the Assuming these were just added specifically to do the equivalence check for prototype reloading, I'd rather that just be a separate method that has to be intentionally called. Removing the hashcode overrides would also remove all the |
||
if (ReferenceEquals(null, other)) return false; | ||
if (ReferenceEquals(this, other)) return true; | ||
|
||
if (other.Count != Count) | ||
return false; | ||
|
||
foreach (var (id, component) in this) | ||
{ | ||
if (!other.TryGetValue(id, out var otherComponent)) | ||
continue; | ||
|
||
if (!component.Mapping.Equals(otherComponent.Mapping)) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
{ | ||
return ReferenceEquals(this, obj) || obj is ComponentRegistry other && Equals(other); | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
return HashCode.Combine(SerializerProvider, WritingReadingPrototypes); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assumes that the data on an entity's component matches the data on the prototype's component, which isn't necessarily true, especially when reloading prototypes in a post map-init map.
What this would have to do instead is closer to map loading. I.e., using methods added in #5571, I guess it'd look something like this (though this is kinda pseudo code):
Or alternatively, instead of creating a new instance, use serialization manager to load directly onto the entity's existing comp instance, though you'd probably still want to raise the comp remove & add events to ensure that its fully refreshed and changes get networked.