Skip to content

Commit

Permalink
fix: Fixes dupe property copying. Adds IgnoreDupe (#1811)
Browse files Browse the repository at this point in the history
## Summary

### Changes
- Adds `[IgnoreDupe]` and `[SerializedIgnoreDupe]`
- Updates all _known_ classes that need the attribute. Some might be missing, please helps us find them!
- Adds `Item.Dupe()` command and encapsulates `CopyProperties` and `OnAfterDuped`. This is also overridable.
- Updates Dupe command to use the new logic.
- Fixes duping multiple kinds of objects that used to be outright broken.

### Bug Fixes
- Fixes issue with durability after duping
- Fixes issue with hue after duping

> [!Note]
> **Developer Note**
> Customizing how duping an item works now requires two steps:
> 1. Add `[IgnoreDupe]` or `[SerializedIgnoreDupe]` to the property/field
> 2. Add custom logic in an `OnAfterDuped` override
>
> When do you need to do this?
> *When the property being copied is not a primitive, and you need to manually deep-clone the contents of the property such as with Lists, Dictionaries, or sub classes.*
  • Loading branch information
kamronbatman committed Jun 2, 2024
1 parent a8d3d27 commit 9c7cb5d
Show file tree
Hide file tree
Showing 34 changed files with 490 additions and 236 deletions.
18 changes: 9 additions & 9 deletions Projects/Server/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,13 @@
namespace Server;

[AttributeUsage(AttributeTargets.Property)]
public class HueAttribute : Attribute
{
}
public class HueAttribute : Attribute;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class PropertyObjectAttribute : Attribute
{
}
public class PropertyObjectAttribute : Attribute;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class NoSortAttribute : Attribute
{
}
public class NoSortAttribute : Attribute;

[AttributeUsage(AttributeTargets.Method)]
public class CallPriorityAttribute : Attribute
Expand Down Expand Up @@ -193,3 +187,9 @@ public SerializedCommandPropertyAttribute(AccessLevel readLevel, AccessLevel wri
public bool ReadOnly { get; }
public bool CanModify { get; }
}

[AttributeUsage(AttributeTargets.Property)]
public class IgnoreDupeAttribute : Attribute;

[AttributeUsage(AttributeTargets.Field)]
public class SerializedIgnoreDupeAttribute : SerializedPropertyAttrAttribute<IgnoreDupeAttribute>;
4 changes: 4 additions & 0 deletions Projects/Server/Guild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ protected BaseGuild()

public bool Deleted => Disbanded;

[IgnoreDupe]
[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }

[IgnoreDupe]
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

[IgnoreDupe]
public long SavePosition { get; set; } = -1;

[IgnoreDupe]
public BufferWriter SaveBuffer { get; set; }

public abstract void Serialize(IGenericWriter writer);
Expand Down
53 changes: 43 additions & 10 deletions Projects/Server/Items/Item.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Server.Collections;
using Server.ContextMenus;
Expand Down Expand Up @@ -254,8 +255,13 @@ public int SavedFlags
}

// Sectors
[IgnoreDupe]
public Item Next { get; set; }

[IgnoreDupe]
public Item Previous { get; set; }

[IgnoreDupe]
public bool OnLinkList { get; set; }

/// <summary>
Expand Down Expand Up @@ -586,6 +592,7 @@ public string Name
}

// Note: Setting the parent via command/props causes problems.
[IgnoreDupe]
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public IEntity Parent
{
Expand Down Expand Up @@ -754,6 +761,7 @@ public Mobile BlessedFor
public int CompareTo(Item other) => other == null ? -1 : Serial.CompareTo(other.Serial);

public virtual int HuedItemID => m_ItemID;

public ObjectPropertyList PropertyList => m_PropertyList ??= InitializePropertyList(new ObjectPropertyList(this));

/// <summary>
Expand All @@ -768,13 +776,17 @@ public virtual void GetProperties(IPropertyList list)
AddNameProperties(list);
}

[IgnoreDupe]
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

[IgnoreDupe]
public long SavePosition { get; set; } = -1;

[IgnoreDupe]
public BufferWriter SaveBuffer { get; set; }

[IgnoreDupe]
[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }

Expand Down Expand Up @@ -1445,6 +1457,7 @@ public virtual void Delete()
ClearProperties();
}

[IgnoreDupe]
public ISpawner Spawner
{
get => LookupCompactInfo()?.m_Spawner;
Expand Down Expand Up @@ -3341,19 +3354,39 @@ public virtual void RemoveItem(Item item)
}
}

private static readonly HashSet<string> _excludedProperties =
[
"SaveBuffer",
"Parent",
"Next",
"Previous",
"OnLinkList"
];
public virtual void OnAfterDuped(Item newItem)
{
}

public virtual bool DupeExcludedProperty(string propertyName) => _excludedProperties.Contains(propertyName);
// Warning: This uses reflection and is slow!
public virtual void Dupe(Item newItem)
{
CopyProperties(this, newItem);
OnAfterDuped(newItem);
}

public virtual void OnAfterDuped(Item newItem)
// Warning: This uses reflection and is slow!
public static void CopyProperties(Item src, Item dest)
{
var props = src.GetType().GetProperties();

for (var i = 0; i < props.Length; i++)
{
var p = props[i];
if (p.GetCustomAttribute(typeof(IgnoreDupeAttribute), true) != null || !p.CanRead || !p.CanWrite)
{
continue;
}

try
{
p.SetValue(dest, p.GetValue(src, null), null);
}
catch
{
// ignored
}
}
}

public virtual bool OnDragLift(Mobile from) => true;
Expand Down
4 changes: 4 additions & 0 deletions Projects/Server/Mobiles/Mobile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2256,13 +2256,17 @@ public virtual void GetProperties(IPropertyList list)
AddNameProperties(list);
}

[IgnoreDupe]
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

[IgnoreDupe]
public long SavePosition { get; set; } = -1;

[IgnoreDupe]
public BufferWriter SaveBuffer { get; set; }

[IgnoreDupe]
[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }

Expand Down
4 changes: 2 additions & 2 deletions Projects/UOContent.Tests/Tests/Items/Books/BookPacketTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class TestBook : BaseBook
{
public TestBook(int itemID, int pageCount = 20, bool writable = true) : base(itemID, pageCount, writable)
{
}
}

public TestBook(int itemID, string title, string author, int pageCount, bool writable) : base(itemID, title, author, pageCount, writable)
{
Expand Down Expand Up @@ -89,4 +89,4 @@ public void TestBookContent()
var result = ns.SendPipe.Reader.AvailableToRead();
AssertThat.Equal(result, expected);
}
}
}

0 comments on commit 9c7cb5d

Please sign in to comment.