From 1fb7ac5cda3398d348976029f1964c047bbcc524 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Mon, 16 Nov 2020 01:04:56 -0800 Subject: [PATCH] Converts equipment packets (#314) ### Change to how DisplayEquipInfo works * Fixes a bug where raw name wasn't used * Fixes another bug where an empty, blank, or null string was still sending an empty crafted by property. Bumps release version --- .../Packets/Outgoing/EquipmentPacketTests.cs | 84 ++++-------- .../Packets/Outgoing}/EquipmentPackets.cs | 34 +---- Projects/Server/Items/Containers.cs | 3 +- Projects/Server/Items/Item.cs | 17 +-- Projects/Server/Mobiles/Mobile.cs | 2 +- .../Packets/OutgoingEquipmentPackets.cs | 129 ++++++++++++++++++ Projects/Server/Utilities/Utility.cs | 2 +- Projects/UOContent/Items/Armor/BaseArmor.cs | 4 +- .../UOContent/Items/Clothing/BaseClothing.cs | 4 +- .../Items/Skill Items/Magical/Spellbook.cs | 2 +- .../Musical Instruments/BaseInstrument.cs | 9 +- Projects/UOContent/Items/Wands/BaseWand.cs | 9 +- .../UOContent/Items/Weapons/BaseWeapon.cs | 4 +- .../UOContent/Mobiles/Vendors/BaseVendor.cs | 6 +- 14 files changed, 177 insertions(+), 132 deletions(-) rename Projects/{Server/Network/Packets => Server.Tests/Tests/Network/Packets/Outgoing}/EquipmentPackets.cs (81%) create mode 100644 Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs index 34b4e1d63..526795be0 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using System.Collections.Generic; using Server.Network; using Xunit; @@ -7,18 +8,22 @@ namespace Server.Tests.Network { public class EquipmentPacketTests : IClassFixture { - [Fact] - public void TestDisplayEquipmentInfo() + [Theory] + [InlineData(null, false)] + [InlineData("Some Crafter", false)] + [InlineData("", true)] + public void TestDisplayEquipmentInfo(string name, bool unidentified) { var m = new Mobile(0x1); m.DefaultMobileInit(); + m.RawName = name; var item = new Item(World.NewItem); var info = new EquipmentInfo( 500000, m, - false, + unidentified, new[] { new EquipInfoAttribute(500001, 1), @@ -27,48 +32,19 @@ namespace Server.Tests.Network } ); - var data = new DisplayEquipmentInfo(item, info).Compile(); + var expected = new DisplayEquipmentInfo(item, info).Compile(); - var attrs = info.Attributes; + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayEquipmentInfo( + item.Serial, + info.Number, + info.Crafter?.RawName, + info.Unidentified, + new List(info.Attributes) + ); - var length = 17 + (info.Unidentified ? 4 : 0) + attrs.Length * 6; - if (info.Crafter != null) - { - length += 6 + (info.Crafter.Name?.Length ?? 0); - } - - Span expectedData = stackalloc byte[length]; - - var pos = 0; - - expectedData.Write(ref pos, (byte)0xBF); // Packet ID - expectedData.Write(ref pos, (ushort)length); // Length - expectedData.Write(ref pos, (ushort)0x10); // Subcommand - expectedData.Write(ref pos, item.Serial); - expectedData.Write(ref pos, info.Number); - if (info.Crafter != null) - { - var name = info.Crafter.Name ?? ""; - expectedData.Write(ref pos, -3); - expectedData.Write(ref pos, (ushort)name.Length); - expectedData.WriteAscii(ref pos, name); - } - - if (info.Unidentified) - { - expectedData.Write(ref pos, -4); - } - - for (var i = 0; i < attrs.Length; i++) - { - var attr = attrs[i]; - expectedData.Write(ref pos, attr.Number); - expectedData.Write(ref pos, (ushort)attr.Charges); - } - - expectedData.Write(ref pos, -1); - - AssertThat.Equal(data, expectedData); + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); } [Fact] @@ -79,25 +55,13 @@ namespace Server.Tests.Network var item = new Item(World.NewItem) { Parent = m }; - var data = new EquipUpdate(item).Compile(); + var expected = new EquipUpdate(item).Compile(); - Span expectedData = stackalloc byte[15]; - var pos = 0; - expectedData.Write(ref pos, (byte)0x2E); // Packet ID - expectedData.Write(ref pos, item.Serial); - expectedData.Write(ref pos, (ushort)item.ItemID); + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendEquipUpdate(item); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (byte)0); -#else - pos++; -#endif - - expectedData.Write(ref pos, (byte)item.Layer); - expectedData.Write(ref pos, item.Parent.Serial); - expectedData.Write(ref pos, (ushort)item.Hue); - - AssertThat.Equal(data, expectedData); + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); } } } diff --git a/Projects/Server/Network/Packets/EquipmentPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs similarity index 81% rename from Projects/Server/Network/Packets/EquipmentPackets.cs rename to Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs index cbd483260..8d81f694d 100644 --- a/Projects/Server/Network/Packets/EquipmentPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs @@ -17,19 +17,6 @@ using System; namespace Server.Network { - public class EquipInfoAttribute - { - public EquipInfoAttribute(int number, int charges = -1) - { - Number = number; - Charges = charges; - } - - public int Number { get; } - - public int Charges { get; } - } - public class EquipmentInfo { public EquipmentInfo(int number, Mobile crafter, bool unidentified, EquipInfoAttribute[] attributes) @@ -56,7 +43,7 @@ namespace Server.Network var attrs = info.Attributes; EnsureCapacity( - 17 + (info.Crafter?.Name?.Length ?? 0) + + 17 + (info.Crafter?.RawName?.Length ?? 0) + (info.Unidentified ? 4 : 0) + attrs.Length * 6 ); @@ -65,22 +52,15 @@ namespace Server.Network Stream.Write(info.Number); - if (info.Crafter != null) - { - var name = info.Crafter.Name; + var name = info.Crafter?.RawName?.Trim() ?? ""; + if (name.Length > 0) + { Stream.Write(-3); - if (name == null) - { - Stream.Write((ushort)0); - } - else - { - var length = name.Length; - Stream.Write((ushort)length); - Stream.WriteAsciiFixed(name, length); - } + var length = name.Length; + Stream.Write((ushort)length); + Stream.WriteAsciiFixed(name, length); } if (info.Unidentified) diff --git a/Projects/Server/Items/Containers.cs b/Projects/Server/Items/Containers.cs index e327e3258..7dcea24e8 100644 --- a/Projects/Server/Items/Containers.cs +++ b/Projects/Server/Items/Containers.cs @@ -39,7 +39,8 @@ namespace Server.Items $"Bank container has {TotalItems} items, {TotalWeight} stones", Owner.NetState ); - Owner.Send(new EquipUpdate(this)); + + Owner.NetState?.SendEquipUpdate(this); DisplayTo(Owner); } } diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index acf8cf81e..950bfdf58 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -1450,8 +1450,6 @@ namespace Server return; } - Packet p = null; - var eable = map.GetClientsInRange(worldLoc, GetMaxUpdateRange()); foreach (var state in eable) @@ -1471,11 +1469,7 @@ namespace Server } else { - if (p != null) - { - state.Send(p); - } - else if (m_Parent is Item) + if (m_Parent is Item) { if (state.ContainerGridLines) { @@ -1488,10 +1482,8 @@ namespace Server } else if (m_Parent is Mobile) { - p = new EquipUpdate(this); - p.Acquire(); - - state.Send(p); + // TODO: Optimize by writing once? + state.SendEquipUpdate(this); } if (ObjectPropertyList.Enabled) @@ -1502,7 +1494,7 @@ namespace Server } else if ((flags & ItemDelta.EquipOnly) != 0 && m_Parent is Mobile) { - state.Send(p ??= Packet.Acquire(new EquipUpdate(this))); + state.SendEquipUpdate(this); if (ObjectPropertyList.Enabled) { @@ -1515,7 +1507,6 @@ namespace Server } } - Packet.Release(p); eable.Free(); } diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 02021956c..8c4edd256 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -5699,7 +5699,7 @@ namespace Server } else if (item.Parent is Mobile) { - state.Send(new EquipUpdate(item)); + state.SendEquipUpdate(item); } else { diff --git a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs new file mode 100644 index 000000000..0a73bd30c --- /dev/null +++ b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs @@ -0,0 +1,129 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: OutgoingEquipmentPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Buffers; +using System.Collections.Generic; + +namespace Server.Network +{ + public class EquipInfoAttribute + { + public EquipInfoAttribute(int number, int charges = -1) + { + Number = number; + Charges = charges; + } + + public int Number { get; } + + public int Charges { get; } + } + + public static class OutgoingEquipmentPackets + { + public static void SendDisplayEquipmentInfo( + this NetState ns, + Serial serial, + int number, + string crafterName, + bool unidentified, + List attrs + ) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + crafterName = crafterName.DefaultIfNullOrEmpty(""); + + var length = 17 + + (crafterName.Length > 0 ? 6 + crafterName.Length : 0) + + (unidentified ? 4 : 0) + + attrs.Count * 6; + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0x10); // Subpacket + writer.Write(serial); + writer.Write(number); + + if (crafterName.Length > 0) + { + writer.Write(-3); // crafted by + + writer.Write((ushort)crafterName.Length); + writer.WriteAscii(crafterName); + } + + if (unidentified) + { + writer.Write(-4); + } + + for (var i = 0; i < attrs.Count; ++i) + { + var attr = attrs[i]; + writer.Write(attr.Number); + writer.Write((short)attr.Charges); + } + + writer.Write(-1); + + ns.Send(ref buffer, writer.Position); + } + + public static void SendEquipUpdate(this NetState ns, Item item) + { + if (ns == null || !ns.GetSendBuffer(out var buffer)) + { + return; + } + + Serial parentSerial; + + var parent = item.Parent as Mobile; + var hue = item.Hue; + + if (parent != null) + { + parentSerial = parent.Serial; + + if (parent.SolidHueOverride >= 0) + { + hue = parent.SolidHueOverride; + } + } + else + { + Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); + parentSerial = Serial.Zero; + } + + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0x2E); // Packet ID + writer.Write(item.Serial); + writer.Write((short)item.ItemID); + writer.Write((ushort)item.Layer); + writer.Write(parentSerial); + writer.Write((short)hue); + + ns.Send(ref buffer, writer.Position); + } + } +} diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 31eab98a0..ed7fdd0b1 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -174,7 +174,7 @@ namespace Server } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string DefaultIfNullOrEmpty(this string value, string def) => value?.Length > 0 ? value : def; + public static string DefaultIfNullOrEmpty(this string value, string def) => value?.Trim().Length > 0 ? value : def; public static IPAddress Intern(IPAddress ipAddress) { diff --git a/Projects/UOContent/Items/Armor/BaseArmor.cs b/Projects/UOContent/Items/Armor/BaseArmor.cs index 0dffbc5f3..58752d49d 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.cs @@ -2036,9 +2036,7 @@ namespace Server.Items return; } - var eqInfo = new EquipmentInfo(number, m_Crafter, false, attrs.ToArray()); - - from.Send(new DisplayEquipmentInfo(this, eqInfo)); + from.NetState?.SendDisplayEquipmentInfo(Serial, number, m_Crafter?.RawName, false, attrs); } [Flags] diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.cs b/Projects/UOContent/Items/Clothing/BaseClothing.cs index cac92efd4..8e7bdf614 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.cs @@ -880,9 +880,7 @@ namespace Server.Items return; } - var eqInfo = new EquipmentInfo(number, m_Crafter, false, attrs.ToArray()); - - from.Send(new DisplayEquipmentInfo(this, eqInfo)); + from.NetState?.SendDisplayEquipmentInfo(Serial, number, m_Crafter?.RawName, false, attrs); } public virtual void AddEquipInfoAttributes(Mobile from, List attrs) diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index 5d04fa844..bc415b404 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -696,7 +696,7 @@ namespace Server.Items else if (Parent is Mobile) { // What will happen if the client doesn't know about our parent? - to.Send(new EquipUpdate(this)); + to.NetState?.SendEquipUpdate(this); } if (ns.HighSeas) diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs index 7b285197f..3b150ae58 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -469,14 +469,7 @@ namespace Server.Items return; } - var eqInfo = new EquipmentInfo( - number, - m_Crafter, - false, - attrs.ToArray() - ); - - from.Send(new DisplayEquipmentInfo(this, eqInfo)); + from.NetState?.SendDisplayEquipmentInfo(Serial, number, m_Crafter?.RawName, false, attrs); } public override void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Items/Wands/BaseWand.cs b/Projects/UOContent/Items/Wands/BaseWand.cs index 59d4ed1e3..022282a72 100644 --- a/Projects/UOContent/Items/Wands/BaseWand.cs +++ b/Projects/UOContent/Items/Wands/BaseWand.cs @@ -268,14 +268,7 @@ namespace Server.Items return; } - var eqInfo = new EquipmentInfo( - number, - Crafter, - false, - attrs.ToArray() - ); - - from.Send(new DisplayEquipmentInfo(this, eqInfo)); + from.NetState?.SendDisplayEquipmentInfo(Serial, number, Crafter?.RawName, false, attrs); } public void Cast(Spell spell) diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 8aaedd0df..aa5849f84 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -3413,9 +3413,7 @@ namespace Server.Items return; } - var eqInfo = new EquipmentInfo(number, m_Crafter, false, attrs.ToArray()); - - from.Send(new DisplayEquipmentInfo(this, eqInfo)); + from.NetState?.SendDisplayEquipmentInfo(Serial, number, m_Crafter?.RawName, false, attrs); } public virtual int GetHitAttackSound(Mobile attacker, Mobile defender) diff --git a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs index e92fc642f..fa0533f85 100644 --- a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs @@ -1021,13 +1021,13 @@ namespace Server.Mobiles AddItem(pack); } - from.Send(new EquipUpdate(pack)); + from.NetState?.SendEquipUpdate(pack); pack = FindItemOnLayer(Layer.ShopSell); if (pack != null) { - from.Send(new EquipUpdate(pack)); + from.NetState?.SendEquipUpdate(pack); } pack = FindItemOnLayer(Layer.ShopResale); @@ -1038,7 +1038,7 @@ namespace Server.Mobiles AddItem(pack); } - from.Send(new EquipUpdate(pack)); + from.NetState?.SendEquipUpdate(pack); } public virtual void VendorSell(Mobile from)