From 9d4bc7cda260181744f811b96d77edcc59df0983 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 5 Jul 2020 20:14:23 -0700 Subject: [PATCH] Adds more packet tests (#173) --- .../Packets/AttributeNormalizerUtilities.cs | 38 ++ .../Old/Outgoing/AccountPacketTests.cs | 3 +- .../Packets/Old/Outgoing/ItemPacketTests.cs | 150 ++++- .../Packets/Old/Outgoing/LightPacketTests.cs | 43 ++ .../Packets/Old/Outgoing/MessageTests.cs | 198 +++++++ .../Packets/Old/Outgoing/MobilePacketTests.cs | 537 ++++++++++++++++++ .../Network/Packets/PacketTestUtilities.cs | 20 +- Projects/Server/Network/NetState.cs | 13 +- .../Packets/Old Packets/MobilePackets.cs | 5 +- Projects/UOContent/Items/Maps/MapItem.cs | 41 +- 10 files changed, 1015 insertions(+), 33 deletions(-) create mode 100644 Projects/Server.Tests/Network/Packets/AttributeNormalizerUtilities.cs create mode 100644 Projects/Server.Tests/Network/Packets/Old/Outgoing/LightPacketTests.cs create mode 100644 Projects/Server.Tests/Network/Packets/Old/Outgoing/MessageTests.cs create mode 100644 Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs diff --git a/Projects/Server.Tests/Network/Packets/AttributeNormalizerUtilities.cs b/Projects/Server.Tests/Network/Packets/AttributeNormalizerUtilities.cs new file mode 100644 index 000000000..8a93d9fe0 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/AttributeNormalizerUtilities.cs @@ -0,0 +1,38 @@ +using System; +using Server.Network; + +namespace Server.Tests.Network.Packets +{ + public static class AttributeNormalizerUtilities + { + public static void Write(int cur, int max, bool normalize, Span data) + { + if (normalize && AttributeNormalizer.Enabled && max != 0) + { + int maximum = AttributeNormalizer.Maximum; + + ((ushort)maximum).CopyTo(data.Slice(0, 2)); + ((ushort)(cur * maximum / max)).CopyTo(data.Slice(2, 2)); + return; + } + + ((ushort)max).CopyTo(data.Slice(0, 2)); + ((ushort)cur).CopyTo(data.Slice(2, 2)); + } + + public static void WriteReverse(int cur, int max, bool normalize, Span data) + { + if (normalize && AttributeNormalizer.Enabled && max != 0) + { + int maximum = AttributeNormalizer.Maximum; + + ((ushort)(cur * maximum / max)).CopyTo(data.Slice(0, 2)); + ((ushort)maximum).CopyTo(data.Slice(2, 2)); + return; + } + + ((ushort)cur).CopyTo(data.Slice(0, 2)); + ((ushort)max).CopyTo(data.Slice(2, 2)); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs index d4d4eb838..1dbc1ba00 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs @@ -194,7 +194,8 @@ namespace Server.Tests.Network.Packets RemoteEndPoint = IPEndPoint.Parse("127.0.0.1") }) { - Account = account + Account = account, + ProtocolChanges = protocolChanges }; Span data = new SupportedFeatures(ns).Compile(); diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/ItemPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/ItemPacketTests.cs index 37a8b3ddb..d9f31efd9 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/ItemPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/ItemPacketTests.cs @@ -5,7 +5,7 @@ using Xunit; namespace Server.Tests.Network.Packets { - public class ItemPacketTests + public class ItemPacketTests : IClassFixture { [Fact] public void TestWorldItemPacket() @@ -323,7 +323,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[5 + 64 * 19]; // Max size int pos = 0; - ((byte)0x3C).CopyTo(ref pos, expectedData); + ((byte)0x3C).CopyTo(ref pos, expectedData); // Packet ID pos += 4; // Length + spell count ushort count = 0; @@ -360,7 +360,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[5 + 64 * 19]; // Max size int pos = 0; - ((byte)0x3C).CopyTo(ref pos, expectedData); + ((byte)0x3C).CopyTo(ref pos, expectedData); // Packet ID pos += 4; // Length + spell count ushort count = 0; @@ -384,5 +384,149 @@ namespace Server.Tests.Network.Packets AssertThat.Equal(data, expectedData); } + + [Fact] + public void TestContainerContentUpdate() + { + Serial serial = 0x1; + Item item = new Item(serial); + + Span data = new ContainerContentUpdate(item).Compile(); + + Span expectedData = stackalloc byte[20]; + int pos = 0; + + ((byte)0x25).CopyTo(ref pos, expectedData); // Packet ID + item.Serial.CopyTo(ref pos, expectedData); + ((ushort)item.ItemID).CopyTo(ref pos, expectedData); + ((byte)0).CopyTo(ref pos, expectedData); // signed, itemID offset + ((ushort)Math.Min(item.Amount, ushort.MaxValue)).CopyTo(ref pos, expectedData); + ((ushort)item.X).CopyTo(ref pos, expectedData); + ((ushort)item.Y).CopyTo(ref pos, expectedData); + (item.Parent?.Serial ?? Serial.Zero).CopyTo(ref pos, expectedData); + ((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestContainerContentUpdate6017() + { + Serial serial = 0x1; + Item item = new Item(serial); + + Span data = new ContainerContentUpdate6017(item).Compile(); + + Span expectedData = stackalloc byte[21]; + int pos = 0; + + ((byte)0x25).CopyTo(ref pos, expectedData); // Packet ID + item.Serial.CopyTo(ref pos, expectedData); + ((ushort)item.ItemID).CopyTo(ref pos, expectedData); + ((byte)0).CopyTo(ref pos, expectedData); // signed, itemID offset + ((ushort)Math.Min(item.Amount, ushort.MaxValue)).CopyTo(ref pos, expectedData); + ((ushort)item.X).CopyTo(ref pos, expectedData); + ((ushort)item.Y).CopyTo(ref pos, expectedData); + ((byte)0).CopyTo(ref pos, expectedData); // Grid Location? + (item.Parent?.Serial ?? Serial.Zero).CopyTo(ref pos, expectedData); + ((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestContainerContent() + { + Container cont = new Container(Serial.LastItem + 1); + cont.AddItem(new Item(Serial.LastItem + 2)); + + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new ContainerContent(m, cont).Compile(); + + int count = cont.Items.Count; + + Span expectedData = stackalloc byte[5 + cont.Items.Count * 19]; // Max Size + int pos = 0; + + ((byte)0x3C).CopyTo(ref pos, expectedData); // Packet ID + pos += 4; // Length + Count + + ushort written = 0; + + for (var i = 0; i < count; i++) + { + var child = cont.Items[i]; + if (child.Deleted || !m.CanSee(child)) + continue; + + child.Serial.CopyTo(ref pos, expectedData); + ((ushort)child.ItemID).CopyTo(ref pos, expectedData); + ((byte)0).CopyTo(ref pos, expectedData); // signed, itemID offset + ((ushort)Math.Min(child.Amount, ushort.MaxValue)).CopyTo(ref pos, expectedData); + ((ushort)child.X).CopyTo(ref pos, expectedData); + ((ushort)child.Y).CopyTo(ref pos, expectedData); + cont.Serial.CopyTo(ref pos, expectedData); + ((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)).CopyTo(ref pos, expectedData); + + written++; + } + + ((ushort)pos).CopyTo(expectedData.Slice(1, 2)); + written.CopyTo(expectedData.Slice(3, 2)); + + expectedData = expectedData.Slice(0, pos); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestContainerContent6017() + { + Container cont = new Container(Serial.LastItem + 1); + cont.AddItem(new Item(Serial.LastItem + 2)); + + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new ContainerContent6017(m, cont).Compile(); + + int count = cont.Items.Count; + + Span expectedData = stackalloc byte[5 + cont.Items.Count * 20]; + int pos = 0; + + ((byte)0x3C).CopyTo(ref pos, expectedData); // Packet ID + pos += 4; // Length + Count + + ushort written = 0; + + for (var i = 0; i < count; i++) + { + var child = cont.Items[i]; + if (child.Deleted || !m.CanSee(child)) + continue; + + child.Serial.CopyTo(ref pos, expectedData); + ((ushort)child.ItemID).CopyTo(ref pos, expectedData); + ((byte)0).CopyTo(ref pos, expectedData); // signed, itemID offset + ((ushort)Math.Min(child.Amount, ushort.MaxValue)).CopyTo(ref pos, expectedData); + ((ushort)child.X).CopyTo(ref pos, expectedData); + ((ushort)child.Y).CopyTo(ref pos, expectedData); + ((byte)0).CopyTo(ref pos, expectedData); // Grid Location? + cont.Serial.CopyTo(ref pos, expectedData); + ((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)).CopyTo(ref pos, expectedData); + + written++; + } + + ((ushort)pos).CopyTo(expectedData.Slice(1, 2)); + written.CopyTo(expectedData.Slice(3, 2)); + + expectedData = expectedData.Slice(0, pos); + + AssertThat.Equal(data, expectedData); + } } } diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/LightPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/LightPacketTests.cs new file mode 100644 index 000000000..fcde220e2 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/LightPacketTests.cs @@ -0,0 +1,43 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class LightPacketTests + { + [Fact] + public void TestGlobalLightLevel() + { + byte lightLevel = 5; + Span data = new GlobalLightLevel(lightLevel).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x4F, // Packet ID + lightLevel + }; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestPersonalLightLevel() + { + Serial serial = 0x1; + byte lightLevel = 5; + Span data = new PersonalLightLevel(serial, lightLevel).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x4E, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + lightLevel + }; + + serial.CopyTo(expectedData.Slice(1, 4)); + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/MessageTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MessageTests.cs new file mode 100644 index 000000000..da99f7520 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MessageTests.cs @@ -0,0 +1,198 @@ +using System; +using Xunit; +using Server.Network; + +namespace Server.Tests.Network.Packets +{ + public class MessageTests + { + [Fact] + public void TestMessageLocalized() + { + Serial serial = 0x1; + int graphic = 0x100; + var messageType = MessageType.Label; + int hue = 1024; + int font = 3; + int number = 150000; + string name = "Stuff"; + string args = "Arguments"; + + Span data = new MessageLocalized( + serial, + graphic, + messageType, + hue, + font, + number, + name, + args + ).Compile(); + + Span expectedData = stackalloc byte[50 + args.Length * 2]; + int pos = 0; + ((byte)0xC1).CopyTo(ref pos, expectedData); // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + serial.CopyTo(ref pos, expectedData); + ((ushort)graphic).CopyTo(ref pos, expectedData); + ((byte)messageType).CopyTo(ref pos, expectedData); + ((ushort)hue).CopyTo(ref pos, expectedData); + ((ushort)font).CopyTo(ref pos, expectedData); + number.CopyTo(ref pos, expectedData); + name.CopyASCIIFixedTo(ref pos, 30, expectedData); + args.CopyRawUnicodeLittleEndianTo(ref pos, expectedData); + expectedData.Clear(ref pos, 2); // Terminator + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMessageLocalizedAffix() + { + Serial serial = 0x1; + int graphic = 0x100; + var messageType = MessageType.Label; + int hue = 1024; + int font = 3; + int number = 150000; + string name = "Stuff"; + string args = "Arguments"; + var affixType = AffixType.System; + string affix = "Affix"; + + Span data = new MessageLocalizedAffix( + serial, + graphic, + messageType, + hue, + font, + number, + name, + affixType, + affix, + args + ).Compile(); + + Span expectedData = stackalloc byte[52 + affix.Length + args.Length * 2]; + int pos = 0; + ((byte)0xCC).CopyTo(ref pos, expectedData); // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + serial.CopyTo(ref pos, expectedData); + ((ushort)graphic).CopyTo(ref pos, expectedData); + ((byte)messageType).CopyTo(ref pos, expectedData); + ((ushort)hue).CopyTo(ref pos, expectedData); + ((ushort)font).CopyTo(ref pos, expectedData); + number.CopyTo(ref pos, expectedData); + ((byte)affixType).CopyTo(ref pos, expectedData); + name.CopyASCIIFixedTo(ref pos, 30, expectedData); + affix.CopyRawASCIITo(ref pos, expectedData); + expectedData.Clear(ref pos, 2); // Terminator + args.CopyRawUnicodeLittleEndianTo(ref pos, expectedData); + expectedData.Clear(ref pos, 1); // Terminator + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestAsciiMessage() + { + Serial serial = 0x1; + int graphic = 0x100; + var messageType = MessageType.Label; + int hue = 1024; + int font = 3; + string name = "Stuff"; + string text = "Some Text"; + + Span data = new AsciiMessage( + serial, + graphic, + messageType, + hue, + font, + name, + text + ).Compile(); + + Span expectedData = stackalloc byte[45 + text.Length]; + int pos = 0; + ((byte)0x1C).CopyTo(ref pos, expectedData); // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + serial.CopyTo(ref pos, expectedData); + ((ushort)graphic).CopyTo(ref pos, expectedData); + ((byte)messageType).CopyTo(ref pos, expectedData); + ((ushort)hue).CopyTo(ref pos, expectedData); + ((ushort)font).CopyTo(ref pos, expectedData); + name.CopyASCIIFixedTo(ref pos, 30, expectedData); + text.CopyRawASCIITo(ref pos, expectedData); + expectedData.Clear(ref pos, 1); // Terminator + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestUnicodeMessage() + { + Serial serial = 0x1; + int graphic = 0x100; + var messageType = MessageType.Label; + int hue = 1024; + int font = 3; + string lang = "ENU"; + string name = "Stuff"; + string text = "Some Text"; + + Span data = new UnicodeMessage( + serial, + graphic, + messageType, + hue, + font, + lang, + name, + text + ).Compile(); + + Span expectedData = stackalloc byte[50 + text.Length * 2]; + int pos = 0; + ((byte)0xAE).CopyTo(ref pos, expectedData); // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + serial.CopyTo(ref pos, expectedData); + ((ushort)graphic).CopyTo(ref pos, expectedData); + ((byte)messageType).CopyTo(ref pos, expectedData); + ((ushort)hue).CopyTo(ref pos, expectedData); + ((ushort)font).CopyTo(ref pos, expectedData); + lang.CopyASCIIFixedTo(ref pos, 4, expectedData); + name.CopyASCIIFixedTo(ref pos, 30, expectedData); + text.CopyRawUnicodeBigEndianTo(ref pos, expectedData); + expectedData.Clear(ref pos, 2); // Terminator + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestFollowMessage() + { + Serial serial = 0x1; + Serial serial2 = 0x2; + + Span data = new FollowMessage(serial, serial2).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x15, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Follower + 0x00, 0x00, 0x00, 0x00, // Followee + }; + + serial.CopyTo(expectedData.Slice(1, 4)); + serial2.CopyTo(expectedData.Slice(5, 4)); + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs new file mode 100644 index 000000000..0ed3f4529 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs @@ -0,0 +1,537 @@ +using System; +using System.Net; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class MobilePacketTests : IClassFixture + { + [Fact] + public void TestDeathAnimation() + { + Serial killed = 0x1; + Serial corpse = 0x1000; + + Span data = new DeathAnimation(killed, corpse).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xAF, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial of killed + 0x00, 0x00, 0x00, 0x00, // Serial of corpse + 0x00, 0x00, 0x00, 0x00 + }; + + killed.CopyTo(expectedData.Slice(1, 4)); + corpse.CopyTo(expectedData.Slice(5, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestBondStatus() + { + Serial petSerial = 0x1; + bool bonded = true; + + Span data = new BondedStatus(petSerial, bonded).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xBF, // Packet ID + 0x00, 0x0B, // Length + 0x00, 0x19, // Sub-packet + 0x00, // Sub command + 0x00, 0x00, 0x00, 0x00, // Serial + (byte)(bonded ? 0x1 : 0x0) + }; + + petSerial.CopyTo(expectedData.Slice(6, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileMoving() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + int noto = 10; + + Span data = new MobileMoving(m, noto).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x77, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Body + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, // Z + 0x00, // Direction + 0x00, 0x00, // Hue + 0x00, // Flags + 0x00 // Noto + }; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + ((ushort)m.Body).CopyTo(expectedData.Slice(5, 2)); + ((ushort)m.X).CopyTo(expectedData.Slice(7, 2)); + ((ushort)m.Y).CopyTo(expectedData.Slice(9, 2)); + expectedData[11] = (byte)m.Z; + expectedData[12] = (byte)m.Direction; + ((ushort)m.Hue).CopyTo(expectedData.Slice(13, 2)); + expectedData[15] = (byte)m.GetPacketFlags(); + expectedData[16] = (byte)noto; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileMovingOld() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + int noto = 10; + + Span data = new MobileMoving(m, noto).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x77, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Body + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, // Z + 0x00, // Direction + 0x00, 0x00, // Hue + 0x00, // Flags + 0x00 // Noto + }; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + ((ushort)m.Body).CopyTo(expectedData.Slice(5, 2)); + ((ushort)m.X).CopyTo(expectedData.Slice(7, 2)); + ((ushort)m.Y).CopyTo(expectedData.Slice(9, 2)); + expectedData[11] = (byte)m.Z; + expectedData[12] = (byte)m.Direction; + ((ushort)m.Hue).CopyTo(expectedData.Slice(13, 2)); + expectedData[15] = (byte)m.GetOldPacketFlags(); + expectedData[16] = (byte)noto; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileHits() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileHits(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xA1, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00 // Current Hits + }; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Hits, m.HitsMax, false, expectedData.Slice(5)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileHitsN() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileHitsN(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xA1, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00 // Current Hits + }; + + int max = AttributeNormalizer.Maximum; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Hits, m.HitsMax, true, expectedData.Slice(5)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileMana() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileMana(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xA2, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00 // Current Hits + }; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Mana, m.ManaMax, false, expectedData.Slice(5)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileManaN() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileManaN(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xA2, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00 // Current Hits + }; + + int max = AttributeNormalizer.Maximum; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Mana, m.ManaMax, true, expectedData.Slice(5)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileStam() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileStam(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xA3, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00 // Current Hits + }; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Stam, m.StamMax, false, expectedData.Slice(5)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileStamN() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileStamN(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xA3, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00 // Current Hits + }; + + int max = AttributeNormalizer.Maximum; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Stam, m.StamMax, true, expectedData.Slice(5)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileAttributes() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileAttributes(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x2D, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00, // Current Hits + 0x00, 0x00, // Max Mana + 0x00, 0x00, // Current Mana + 0x00, 0x00, // Max Stam + 0x00, 0x00 // Current Stam + }; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Hits, m.HitsMax, false, expectedData.Slice(5)); + AttributeNormalizerUtilities.Write(m.Mana, m.ManaMax, false, expectedData.Slice(9)); + AttributeNormalizerUtilities.Write(m.Stam, m.StamMax, false, expectedData.Slice(13)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileAttributesN() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + Span data = new MobileAttributesN(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x2D, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, // Max Hits + 0x00, 0x00, // Current Hits + 0x00, 0x00, // Max Mana + 0x00, 0x00, // Current Mana + 0x00, 0x00, // Max Stam + 0x00, 0x00 // Current Stam + }; + + int max = AttributeNormalizer.Maximum; + + m.Serial.CopyTo(expectedData.Slice(1, 4)); + AttributeNormalizerUtilities.Write(m.Hits, m.HitsMax, true, expectedData.Slice(5)); + AttributeNormalizerUtilities.Write(m.Mana, m.ManaMax, true, expectedData.Slice(9)); + AttributeNormalizerUtilities.Write(m.Stam, m.StamMax, true, expectedData.Slice(13)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileName() + { + var m = new Mobile(0x1) + { + Name = "Some Really Long Mobile Name That Gets Cut off", + }; + m.DefaultMobileInit(); + + Span data = new MobileName(m).Compile(); + + Span expectedData = stackalloc byte[37]; + int pos = 0; + ((byte)0x98).CopyTo(ref pos, expectedData); + ((ushort)0x25).CopyTo(ref pos, expectedData); + m.Serial.CopyTo(ref pos, expectedData); + (m.Name ?? "").CopyRawASCIITo(ref pos, 29, expectedData); + ((byte)0x0).CopyTo(ref pos, expectedData); // Null terminator + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileAnimation() + { + Serial mobile = 0x1; + int action = 200; + int frameCount = 5; + int repeatCount = 1; + bool reverse = false; + bool repeat = false; + byte delay = 5; + + Span data = new MobileAnimation( + mobile, + action, + frameCount, + repeatCount, + !reverse, + repeat, + delay + ).Compile(); + + Span expectedData = stackalloc byte[14]; + int pos = 0; + + ((byte)0x6E).CopyTo(ref pos, expectedData); + mobile.CopyTo(ref pos, expectedData); + ((ushort)action).CopyTo(ref pos, expectedData); + ((ushort)frameCount).CopyTo(ref pos, expectedData); + ((ushort)repeatCount).CopyTo(ref pos, expectedData); + reverse.CopyTo(ref pos, expectedData); + repeat.CopyTo(ref pos, expectedData); + delay.CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestNewMobileAnimation() + { + Serial mobile = 0x1; + int action = 200; + int frameCount = 5; + byte delay = 5; + + Span data = new NewMobileAnimation( + mobile, + action, + frameCount, + delay + ).Compile(); + + Span expectedData = stackalloc byte[10]; + int pos = 0; + + ((byte)0xE2).CopyTo(ref pos, expectedData); + mobile.CopyTo(ref pos, expectedData); + ((ushort)action).CopyTo(ref pos, expectedData); + ((ushort)frameCount).CopyTo(ref pos, expectedData); + delay.CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileStatusCompact() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + + bool canBeRenamed = false; + + Span data = new MobileStatusCompact(canBeRenamed, m).Compile(); + + Span expectedData = stackalloc byte[43]; + int pos = 0; + + ((byte)0x11).CopyTo(ref pos, expectedData); // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + m.Serial.CopyTo(ref pos, expectedData); + (m.Name ?? "").CopyASCIIFixedTo(ref pos, 30, expectedData); + + AttributeNormalizerUtilities.WriteReverse(m.Hits, m.HitsMax, true, expectedData.Slice(pos)); + pos += 4; + canBeRenamed.CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestMobileStatusExtended() + { + var beholder = new Mobile(0x1) + { + Name = "Random Mobile 1" + }; + beholder.DefaultMobileInit(); + + var beheld = new Mobile(0x2) + { + Name = "Random Mobile 2" + }; + beheld.DefaultMobileInit(); + + NetState ns = new NetState(new AccountPacketTests.TestConnectionContext + { + RemoteEndPoint = IPEndPoint.Parse("127.0.0.1") + }); + + Span data = new MobileStatus(beholder, beheld, ns).Compile(); + + Span expectedData = stackalloc byte[121]; // Max Size + int pos = 0; + + ((byte)0x11).CopyTo(ref pos, expectedData); + pos += 2; // Length + + int type; + bool notSelf = beholder != beheld; + + if (notSelf) type = 0; + else if (Core.HS && ns.ExtendedStatus) type = 6; + else if (Core.ML && ns.SupportsExpansion(Expansion.ML)) type = 5; + else type = Core.AOS ? 4 : 3; + + beheld.Serial.CopyTo(ref pos, expectedData); + beheld.Name.CopyASCIIFixedTo(ref pos, 30, expectedData); + + AttributeNormalizerUtilities.WriteReverse(beheld.Hits, beheld.HitsMax, notSelf, expectedData.Slice(pos)); + pos += 4; + + beheld.CanBeRenamedBy(beheld).CopyTo(ref pos, expectedData); + ((byte)type).CopyTo(ref pos, expectedData); + + if (type > 0) + { + beheld.Female.CopyTo(ref pos, expectedData); + ((ushort)beheld.Str).CopyTo(ref pos, expectedData); + ((ushort)beheld.Dex).CopyTo(ref pos, expectedData); + ((ushort)beheld.Int).CopyTo(ref pos, expectedData); + + AttributeNormalizerUtilities.WriteReverse(beheld.Stam, beheld.StamMax, notSelf, expectedData.Slice(pos)); + pos += 4; + + AttributeNormalizerUtilities.WriteReverse(beheld.Mana, beheld.ManaMax, notSelf, expectedData.Slice(pos)); + pos += 4; + + beheld.TotalGold.CopyTo(ref pos, expectedData); + ((ushort)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))).CopyTo(ref pos, expectedData); + ((ushort)(Mobile.BodyWeight + beheld.TotalWeight)).CopyTo(ref pos, expectedData); + + if (type >= 5) + { + ((ushort)beheld.MaxWeight).CopyTo(ref pos, expectedData); + ((byte)(beheld.Race.RaceID + 1)).CopyTo(ref pos, expectedData); // 0x00 for a non-ML enabled account + } + + ((ushort)beheld.StatCap).CopyTo(ref pos, expectedData); + ((byte)beheld.Followers).CopyTo(ref pos, expectedData); + ((byte)beheld.FollowersMax).CopyTo(ref pos, expectedData); + + if (type >= 4) + { + ((ushort)beheld.FireResistance).CopyTo(ref pos, expectedData); + ((ushort)beheld.ColdResistance).CopyTo(ref pos, expectedData); + ((ushort)beheld.PoisonResistance).CopyTo(ref pos, expectedData); + ((ushort)beheld.EnergyResistance).CopyTo(ref pos, expectedData); + ((ushort)beheld.Luck).CopyTo(ref pos, expectedData); + } + + int min = 0; + int max = 0; + beheld.Weapon?.GetStatusDamage(beheld, out min, out max); + + ((ushort)min).CopyTo(ref pos, expectedData); + ((ushort)max).CopyTo(ref pos, expectedData); + + beheld.TithingPoints.CopyTo(ref pos, expectedData); + + if (type >= 6) + for (var i = 0; i < 15; ++i) + ((ushort)beheld.GetAOSStatus(i)).CopyTo(ref pos, expectedData); + } + + ((ushort)pos).CopyTo(expectedData.Slice(1)); // Length + + expectedData = expectedData.Slice(0, pos); + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs b/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs index 4de648275..9e8258832 100644 --- a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs +++ b/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs @@ -119,7 +119,7 @@ namespace Server.Tests.Network.Packets [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyRawASCIITo(this string str, ref int pos, Span bytes) { - pos += Encoding.ASCII.GetBytes(str.AsSpan(), bytes.Slice(pos)); + pos += Encoding.ASCII.GetBytes(str, bytes.Slice(pos)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -133,7 +133,7 @@ namespace Server.Tests.Network.Packets public static void CopyASCIITo(this string str, ref int pos, Span bytes) { BinaryPrimitives.WriteUInt16BigEndian(bytes.Slice(pos, 2), (ushort)str.Length); - pos += 2 + Encoding.ASCII.GetBytes(str.AsSpan(), bytes.Slice(pos + 2)); + pos += 2 + Encoding.ASCII.GetBytes(str, bytes.Slice(pos + 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -148,7 +148,7 @@ namespace Server.Tests.Network.Packets public static void CopyASCIINullTo(this string str, ref int pos, Span bytes) { BinaryPrimitives.WriteUInt16BigEndian(bytes.Slice(pos, 2), (ushort)(str.Length + 1)); - pos += 3 + Encoding.ASCII.GetBytes(str.AsSpan(), bytes.Slice(pos + 2)); + pos += 3 + Encoding.ASCII.GetBytes(str, bytes.Slice(pos + 2)); bytes[pos - 1] = 0; // null terminator } @@ -211,7 +211,19 @@ namespace Server.Tests.Network.Packets public static void CopyUnicodeBigEndianTo(this string str, ref int pos, Span bytes) { BinaryPrimitives.WriteUInt16BigEndian(bytes.Slice(pos, 2), (ushort)str.Length); - pos += 2 + Encoding.BigEndianUnicode.GetBytes(str.AsSpan(), bytes.Slice(pos + 2)); + pos += 2 + Encoding.BigEndianUnicode.GetBytes(str, bytes.Slice(pos + 2)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyRawUnicodeBigEndianTo(this string str, ref int pos, Span bytes) + { + pos += Encoding.BigEndianUnicode.GetBytes(str, bytes.Slice(pos)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyRawUnicodeLittleEndianTo(this string str, ref int pos, Span bytes) + { + pos += Encoding.Unicode.GetBytes(str, bytes.Slice(pos)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Network/NetState.cs b/Projects/Server/Network/NetState.cs index ae302990b..566e0c5ef 100644 --- a/Projects/Server/Network/NetState.cs +++ b/Projects/Server/Network/NetState.cs @@ -630,7 +630,7 @@ namespace Server.Network { var info = ExpansionInfo.Table[i]; - if ((info.RequiredClient != null && Version >= info.RequiredClient) || (Flags & info.ClientFlags) != 0) + if (info.RequiredClient != null && Version >= info.RequiredClient || (Flags & info.ClientFlags) != 0) return info; } @@ -642,13 +642,10 @@ namespace Server.Network public ProtocolChanges ProtocolChanges { get; set; } - public bool SupportsExpansion(ExpansionInfo info, bool checkCoreExpansion = true) - { - if (info == null || (checkCoreExpansion && (int)Core.Expansion < info.ID)) - return false; - - return info.RequiredClient != null ? Version >= info.RequiredClient : (Flags & info.ClientFlags) != 0; - } + public bool SupportsExpansion(ExpansionInfo info, bool checkCoreExpansion = true) => + info != null && (!checkCoreExpansion || (int)Core.Expansion >= info.ID) && (info.RequiredClient != null + ? Version >= info.RequiredClient + : (Flags & info.ClientFlags) != 0); public bool SupportsExpansion(Expansion ex, bool checkCoreExpansion = true) => SupportsExpansion(ExpansionInfo.GetInfo(ex), checkCoreExpansion); diff --git a/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs b/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs index b5589fac6..08dac27d0 100644 --- a/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs @@ -68,7 +68,6 @@ namespace Server.Network } } - // Pre-7.0.0.0 Mobile Moving public sealed class MobileMovingOld : Packet { public MobileMovingOld(Mobile m, int noto) : base(0x77, 17) @@ -178,7 +177,6 @@ namespace Server.Network } } - // unsure of proper format, client crashes public sealed class MobileName : Packet { public MobileName(Mobile m) : base(0x98) @@ -186,7 +184,8 @@ namespace Server.Network EnsureCapacity(37); Stream.Write(m.Serial); - Stream.WriteAsciiFixed(m.Name ?? "", 30); + Stream.WriteAsciiFixed(m.Name ?? "", 29); + Stream.Write((byte)0); // Null terminator } } diff --git a/Projects/UOContent/Items/Maps/MapItem.cs b/Projects/UOContent/Items/Maps/MapItem.cs index 84f3d177a..871ec731f 100644 --- a/Projects/UOContent/Items/Maps/MapItem.cs +++ b/Projects/UOContent/Items/Maps/MapItem.cs @@ -12,12 +12,13 @@ namespace Server.Items private bool m_Editable; [Constructible] - public MapItem() : base(0x14EC) + public MapItem(Map facet = null) : base(0x14EC) { Weight = 1.0; Width = 200; Height = 200; + Facet = facet; } public MapItem(Serial serial) : base(serial) @@ -36,6 +37,9 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public int Height { get; set; } + [CommandProperty(AccessLevel.GameMaster)] + public Map Facet { get; set; } + public List Pins { get; } = new List(); public int OnCraft(int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, @@ -79,7 +83,18 @@ namespace Server.Items public virtual void DisplayTo(Mobile from) { - from.Send(new MapDetails(this)); + NetState ns = from.NetState; + + if (ns.NewCharacterList) // 7.0.13.0+ supports maps on all facets + from.Send(new MapDetailsNew(this)); + else if (Facet != null && Facet != Map.Felucca && Facet != Map.Trammel) // Is it Felucca and Trammel, or just Felucca? + { + from.SendMessage("You must have client 7.0.13.0 or higher to display this map."); + return; + } + else + from.Send(new MapDetails(this)); + from.Send(new MapDisplay(this)); for (int i = 0; i < Pins.Count; ++i) @@ -318,23 +333,21 @@ namespace Server.Items } } - /* private sealed class MapDetailsNew : Packet { - public MapDetailsNew( MapItem map ) : base ( 0xF5, 21 ) + public MapDetailsNew(MapItem map) : base(0xF5, 21) { - m_Stream.Write( (int) map.Serial ); - m_Stream.Write( (short) 0x139D ); - m_Stream.Write( (short) map.Bounds.Start.X ); - m_Stream.Write( (short) map.Bounds.Start.Y ); - m_Stream.Write( (short) map.Bounds.End.X ); - m_Stream.Write( (short) map.Bounds.End.Y ); - m_Stream.Write( (short) map.Width ); - m_Stream.Write( (short) map.Height ); - m_Stream.Write( (short) ( map.Facet == null ? 0 : map.Facet.MapID ) ); + Stream.Write(map.Serial); + Stream.Write((short)0x139D); + Stream.Write((short)map.Bounds.Start.X); + Stream.Write((short)map.Bounds.Start.Y); + Stream.Write((short)map.Bounds.End.X); + Stream.Write((short)map.Bounds.End.Y); + Stream.Write((short)map.Width); + Stream.Write((short)map.Height); + Stream.Write((short)(map.Facet?.MapID ?? 0)); } } - */ private abstract class MapCommand : Packet {