From 0b802dbe1b3d9574d9ff5085c0c2b7766c077ad0 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:44:56 -0700 Subject: [PATCH] fix: Fixes duping containers and removes copying private setter properties (#1816) ### Summary - Removes copying private setters - Fixes duping containers - Adds public `Dupe.DoDupe` functions for external scripts to hook into the existing logic. --- Projects/Server/Items/Item.cs | 8 +- Projects/Server/Mobiles/Mobile.cs | 2 + .../Server/Utilities/ActivatorExtensions.cs | 4 +- Projects/UOContent/Commands/Dupe.cs | 162 +++++++++++------- Projects/UOContent/Items/Aquarium/Aquarium.cs | 5 +- 5 files changed, 116 insertions(+), 65 deletions(-) diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 6ce65cd36..66412b8cf 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -3381,9 +3381,15 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt continue; } + var setMethod = p.GetSetMethod(false); + try { - p.SetValue(dest, p.GetValue(src, null), null); + // Do not copy private properties + if (setMethod != null) + { + p.SetValue(dest, p.GetValue(src, null), null); + } } catch { diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 11dbbd635..1580a3b3d 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -865,10 +865,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } } + [IgnoreDupe] public bool Pushing { get; set; } public virtual bool IsDeadBondedPet => false; + [IgnoreDupe] public ISpell Spell { get => m_Spell; diff --git a/Projects/Server/Utilities/ActivatorExtensions.cs b/Projects/Server/Utilities/ActivatorExtensions.cs index 724fd7d72..96c116304 100644 --- a/Projects/Server/Utilities/ActivatorExtensions.cs +++ b/Projects/Server/Utilities/ActivatorExtensions.cs @@ -123,7 +123,7 @@ public static class ActivatorExtensions { var argLength = args?.Length ?? 0; - var types = argLength > 0 ? new Type[argLength] : Array.Empty(); + var types = argLength > 0 ? new Type[argLength] : []; for (int i = 0; i < types.Length; i++) { types[i] = args![i]?.GetType(); @@ -139,7 +139,7 @@ public static class ActivatorExtensions object[] paramArgs; if (paramCount == 0) { - paramArgs = Array.Empty(); + paramArgs = []; } else if (argLength == paramCount) { diff --git a/Projects/UOContent/Commands/Dupe.cs b/Projects/UOContent/Commands/Dupe.cs index 2ab656651..e38a114f3 100644 --- a/Projects/UOContent/Commands/Dupe.cs +++ b/Projects/UOContent/Commands/Dupe.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Server.Items; using Server.Targeting; @@ -40,21 +41,110 @@ public static class Dupe e.Mobile.SendMessage("What do you wish to dupe?"); } + public static bool DoDupe(Item src, int amount, Mobile from = null, Container pack = null) + { + from?.SendMessage($"Duping {amount}..."); + var c = src.GetType().GetConstructor(out var paramCount); + if (c == null) + { + from?.SendMessage("Unable to dupe. Item must have a constructor with zero required parameters."); + return false; + } + + var args = paramCount == 0 ? null : new object[paramCount]; + if (args != null) + { + Array.Fill(args, Type.Missing); + } + + try + { + + for (var i = 0; i < amount; i++) + { + var newItem = DoDupe(src, c, args, from); + if (newItem != null) + { + if (pack != null) + { + pack.DropItem(newItem); + } + else if (from != null) + { + newItem.MoveToWorld(from.Location, from.Map); + } + } + } + + return true; + } + catch + { + return false; + } + } + + public static Item DoDupe(Item src, ConstructorInfo c, object[] args, Mobile from = null) + { + try + { + if (c.Invoke(args) is not Item newItem) + { + return null; + } + + src.Dupe(newItem); + + newItem.UpdateTotals(); + newItem.InvalidateProperties(); + newItem.Delta(ItemDelta.Update); + + if (from != null) + { + CommandLogging.WriteLine( + from, + $"{from.AccessLevel} {CommandLogging.Format(from)} duped {CommandLogging.Format(src)} creating {CommandLogging.Format(newItem)}" + ); + } + + // Recurse for items that have items + if (newItem.Items.Count > 0) + { + for (var j = newItem.Items.Count - 1; j >= 0; j--) + { + var itemToDelete = newItem.Items[j]; + newItem.RemoveItem(itemToDelete); + itemToDelete.Delete(); + } + } + + for (var j = 0; j < src.Items.Count; j++) + { + var subItem = DoDupe(src.Items[j], c, args, from); + newItem.AddItem(subItem); + } + + return newItem; + } + catch + { + return null; + } + } + private class DupeTarget : Target { - private readonly int m_Amount; - private readonly bool m_InBag; + private readonly int _amount; + private readonly bool _inBag; - public DupeTarget(bool inbag, int amount) - : base(15, false, TargetFlags.None) + public DupeTarget(bool inbag, int amount) : base(15, false, TargetFlags.None) { - m_InBag = inbag; - m_Amount = amount; + _inBag = inbag; + _amount = amount; } protected override void OnTarget(Mobile from, object targ) { - var done = false; if (targ is not Item copy) { from.SendMessage("You can only dupe items."); @@ -63,12 +153,12 @@ public static class Dupe CommandLogging.WriteLine( from, - $"{from.AccessLevel} {CommandLogging.Format(from)} duping {CommandLogging.Format(copy)} (inBag={m_InBag}; amount={m_Amount})" + $"{from.AccessLevel} {CommandLogging.Format(from)} duping {CommandLogging.Format(copy)} (inBag={_inBag}; amount={_amount})" ); - Container pack = null; + Container pack; - if (m_InBag) + if (_inBag) { pack = copy.Parent switch { @@ -82,57 +172,13 @@ public static class Dupe pack = from.Backpack; } - var c = copy.GetType().GetConstructor(out var paramCount); - if (c != null) + if (DoDupe(copy, _amount, from, pack)) { - var args = paramCount == 0 ? null : new object[paramCount]; - if (args != null) - { - Array.Fill(args, Type.Missing); - } - - try - { - from.SendMessage($"Duping {m_Amount}..."); - for (var i = 0; i < m_Amount; i++) - { - if (c.Invoke(args) is Item newItem) - { - copy.Dupe(newItem); - - if (pack != null) - { - pack.DropItem(newItem); - } - else - { - newItem.MoveToWorld(from.Location, from.Map); - } - - newItem.UpdateTotals(); - newItem.InvalidateProperties(); - newItem.Delta(ItemDelta.Update); - - CommandLogging.WriteLine( - from, - $"{from.AccessLevel} {CommandLogging.Format(from)} duped {CommandLogging.Format(copy)} creating {CommandLogging.Format(newItem)}" - ); - } - } - - from.SendMessage("Done"); - done = true; - } - catch - { - from.SendMessage("Error!"); - return; - } + from.SendMessage("Duping done."); } - - if (!done) + else { - from.SendMessage("Unable to dupe. Item must have a constructor with zero required parameters."); + from.SendMessage("Duping Error!"); } } } diff --git a/Projects/UOContent/Items/Aquarium/Aquarium.cs b/Projects/UOContent/Items/Aquarium/Aquarium.cs index dd1baf0b1..8ffeb3fe7 100644 --- a/Projects/UOContent/Items/Aquarium/Aquarium.cs +++ b/Projects/UOContent/Items/Aquarium/Aquarium.cs @@ -492,10 +492,7 @@ namespace Server.Items aquarium.Water.Maintain = Water.Maintain; aquarium.Water.State = Water.State; - for (var i = 0; i < _events.Count; i++) - { - aquarium.AddToEvents(_events[i]); - } + aquarium.Events = [..Events]; } private void Deserialize(IGenericReader reader, int version)