diff --git a/Projects/Scripts.Tests/EmptyTests.cs b/Projects/Scripts.Tests/EmptyTests.cs deleted file mode 100644 index a3b623714..000000000 --- a/Projects/Scripts.Tests/EmptyTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Xunit; - -namespace Server.Tests -{ - public class EmptyTests - { - [Fact] - public void EmptyTest() - { - Assert.True(true); - } - } -} diff --git a/Projects/Server.Tests/AssertExtensions.cs b/Projects/Server.Tests/AssertExtensions.cs new file mode 100644 index 000000000..34bc3b7c3 --- /dev/null +++ b/Projects/Server.Tests/AssertExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Text; + +namespace Server.Tests +{ + public static partial class AssertThat + { + public static string SpanToString(ReadOnlySpan bytes) + { + StringBuilder builder = new StringBuilder(); + builder.Append("["); + builder.AppendJoin(", ", bytes.ToArray()); + builder.Append("]"); + + return builder.ToString(); + } + + public static void Equal(ReadOnlySpan expected, ReadOnlySpan actual) => + Xunit.Assert.True( + MemoryExtensions.SequenceEqual(expected, actual), + $"Expected does not match actual.\nExpected:\t{SpanToString(expected)}\nActual:\t\t{SpanToString(actual)}" + ); + } +} diff --git a/Projects/Server.Tests/Network/Packets/DamageOldPacketTests.cs b/Projects/Server.Tests/Network/Packets/DamageOldPacketTests.cs deleted file mode 100644 index 214154734..000000000 --- a/Projects/Server.Tests/Network/Packets/DamageOldPacketTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network.Packets -{ - public class DamageOldPacketTests : IClassFixture - { - private Mobile mobile; - - public DamageOldPacketTests(ServerFixture fixture) => mobile = fixture.mobile; - - [Theory] - [InlineData(10, 10)] - [InlineData(-5, 0)] - [InlineData(1024, 255)] - public void TestDamagePacketOld(int inputAmount, byte expectedAmount) - { - DamagePacketOld packet = new DamagePacketOld(mobile, inputAmount); - - Span data = packet.Compile(false, out int length).AsSpan(0, length); - - byte[] expectedData = { - 0xBF, // Packet - 0x00, 0x0B, // Length - 0x00, 0x22, // Sub-packet - 0x01, // Command - 0x00, 0x00, 0x00, 0x01, // Serial - expectedAmount // Amount - }; - - Assert.Equal(data.ToArray(), expectedData); - } - } -} diff --git a/Projects/Server.Tests/Network/Packets/Outgoing/Old/ArrowPacketTests.cs b/Projects/Server.Tests/Network/Packets/Outgoing/Old/ArrowPacketTests.cs new file mode 100644 index 000000000..cf601391f --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Outgoing/Old/ArrowPacketTests.cs @@ -0,0 +1,89 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class ArrowPacketTests + { + [Fact] + public void TestCancelArrow() + { + Span data = new CancelArrow().Compile(); + + Span expectedData = stackalloc byte[] { + 0xBA, // Packet + 0x00, // Command + 0xFF, 0xFF, // X + 0xFF, 0xFF // Y + }; + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 10)] + [InlineData(100000, 100000)] + public void TestSetArrow(int x, int y) + { + Span data = new SetArrow(x, y).Compile(); + + Span expectedData = stackalloc byte[] { + 0xBA, // Packet + 0x01, // Command + 0x00, 0x00, // X + 0x00, 0x00 // Y + }; + + ((ushort)x).CopyTo(expectedData.Slice(2, 2)); + ((ushort)y).CopyTo(expectedData.Slice(4, 2)); + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 10)] + [InlineData(100000, 100000)] + public void TestCancelArrowHS(int x, int y) + { + Span data = new CancelArrowHS(x, y, 0x1).Compile(); + + Span expectedData = stackalloc byte[] { + 0xBA, // Packet + 0x00, // Command + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, 0x00, 0x00, 0x01 // Serial + }; + + ((ushort)x).CopyTo(expectedData.Slice(2, 2)); + ((ushort)y).CopyTo(expectedData.Slice(4, 2)); + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 10)] + [InlineData(100000, 100000)] + public void TestSetArrowHS(int x, int y) + { + Span data = new SetArrowHS(x, y, 0x1).Compile(); + + Span expectedData = stackalloc byte[] { + 0xBA, // Packet + 0x01, // Command + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, 0x00, 0x00, 0x01 // Serial + }; + + ((ushort)x).CopyTo(expectedData.Slice(2, 2)); + ((ushort)y).CopyTo(expectedData.Slice(4, 2)); + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Outgoing/Old/DamagePacketTests.cs b/Projects/Server.Tests/Network/Packets/Outgoing/Old/DamagePacketTests.cs new file mode 100644 index 000000000..fe86f60e9 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Outgoing/Old/DamagePacketTests.cs @@ -0,0 +1,55 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class DamagePacketTests : IClassFixture + { + private readonly Mobile mobile; + + public DamagePacketTests(ServerFixture fixture) => mobile = fixture.fromMobile; + + [Theory] + [InlineData(10, 10)] + [InlineData(-5, 0)] + [InlineData(1024, 0xFF)] + public void TestDamagePacketOld(int inputAmount, byte expectedAmount) + { + Span data = new DamagePacketOld(mobile, inputAmount).Compile(); + + Span expectedData = stackalloc byte[] { + 0xBF, // Packet + 0x00, 0x0B, // Length + 0x00, 0x22, // Sub-packet + 0x01, // Command + 0x00, 0x00, 0x00, 0x00, // Mobile Serial + expectedAmount // Amount + }; + + mobile.Serial.CopyTo(expectedData.Slice(6, 4)); + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(10, 10)] + [InlineData(-5, 0)] + [InlineData(1024, 1024)] + [InlineData(100000, 0xFFFF)] + public void TestDamagePacket(int inputAmount, ushort expectedAmount) + { + Span data = new DamagePacket(mobile, inputAmount).Compile(); + + Span expectedData = stackalloc byte[] { + 0x0B, // Packet + 0x00, 0x00, 0x00, 0x00, // Mobile Serial + 0x00, 0x00 // Amount + }; + + mobile.Serial.CopyTo(expectedData.Slice(1, 4)); + expectedAmount.CopyTo(expectedData.Slice(5, 2)); + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Outgoing/Old/MapPatchesPacketTests.cs b/Projects/Server.Tests/Network/Packets/Outgoing/Old/MapPatchesPacketTests.cs new file mode 100644 index 000000000..9d94805d1 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Outgoing/Old/MapPatchesPacketTests.cs @@ -0,0 +1,32 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class MapPatchesTests : IClassFixture + { + [Fact] + public void TestMapPatches() + { + Span data = new MapPatches().Compile(); + + Span expectedData = stackalloc byte[] { + 0xBF, // Packet + 0x00, 0x29, // Length + 0x00, 0x18, // Sub-packet + 0x00, 0x00, 0x00, 0x04, // 4 maps + 0x00, 0x00, 0x00, 0x00, // Felucca + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Trammel + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Ilshenar + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // Malas + 0x00, 0x00, 0x00, 0x00 + }; + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Outgoing/Old/SecureTradePacketTests.cs b/Projects/Server.Tests/Network/Packets/Outgoing/Old/SecureTradePacketTests.cs new file mode 100644 index 000000000..4654f3647 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Outgoing/Old/SecureTradePacketTests.cs @@ -0,0 +1,181 @@ +using System; +using Server.Items; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class SecureTradePacketTests : IClassFixture + { + private readonly Mobile to; + private readonly Container firstCont; + private readonly Container secondCont; + private readonly Item itemInFirstCont; + + public SecureTradePacketTests(ServerFixture fixture) + { + to = fixture.toMobile; + firstCont = fixture.fromCont; + secondCont = fixture.toCont; + itemInFirstCont = fixture.itemInFromCont; + } + + [Theory] + [InlineData("short-name")] + [InlineData("this is a really long name that is more than 30 characters, probably")] + public void TestDisplaySecureTrade(string name) + { + Span data = new DisplaySecureTrade(to, firstCont, secondCont, name).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x6F, // Packet ID + 0x00, 0x2F, // Length + (byte)TradeFlag.Display, // Command + 0x00, 0x00, 0x00, 0x00, // Mobile Serial + 0x00, 0x00, 0x00, 0x00, // First Container Serial + 0x00, 0x00, 0x00, 0x00, // Second Container Serial + 0x01, // true if has name + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Name + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + int pos = 4; + to.Serial.CopyTo(ref pos, expectedData); + firstCont.Serial.CopyTo(ref pos, expectedData); + secondCont.Serial.CopyTo(ref pos, expectedData); + pos++; + name.CopyASCIITo(ref pos, 30, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestCloseSecureTrade() + { + Span data = new CloseSecureTrade(firstCont).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x6F, // Packet ID + 0x00, 0x8, // Length + (byte)TradeFlag.Close, // Command + 0x00, 0x00, 0x00, 0x00 // Container Serial + }; + + firstCont.Serial.CopyTo(expectedData.Slice(4, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(true, false)] // Update first + [InlineData(false, true)] // Update second + public void TestUpdateSecureTrade(bool first, bool second) + { + Container cont = first ? firstCont : secondCont; + Span data = new UpdateSecureTrade(cont, first, second).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x6F, // Packet ID + 0x00, 0x10, // Length + (byte)TradeFlag.Update, // Command + 0x00, 0x00, 0x00, 0x00, // Container Serial + 0x00, 0x00, 0x00, first ? (byte)0x01 : (byte)0x00, // (int)1 if first + 0x00, 0x00, 0x00, second ? (byte)0x01 : (byte)0x00 // (int)1 if second + }; + + cont.Serial.CopyTo(expectedData.Slice(4, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(100000, 30, TradeFlag.UpdateGold)] + [InlineData(250000, 50000, TradeFlag.UpdateLedger)] + public void TestUpdateGoldSecureTrade(int gold, int plat, TradeFlag flag) + { + Span data = new UpdateSecureTrade(firstCont, flag, gold, plat).Compile(); + Span expectedData = stackalloc byte[] + { + 0x6F, // Packet ID + 0x00, 0x10, // Length + (byte)flag, // Command + 0x00, 0x00, 0x00, 0x00, // Container Serial + 0x00, 0x00, 0x00, 0x00, // Gold + 0x00, 0x00, 0x00, 0x00 // Platinum + }; + + int pos = 4; + firstCont.Serial.CopyTo(ref pos, expectedData); + gold.CopyTo(ref pos, expectedData); + plat.CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestSecureTradeEquip() + { + Span data = new SecureTradeEquip(itemInFirstCont, to).Compile(); + Span expectedData = stackalloc byte[] + { + 0x25, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Item Serial + 0x00, 0x00, // Item ItemID + 0x00, + 0x00, 0x00, // Item Amount + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, 0x00, 0x00, 0x00, // Mobile Serial + 0x00, 0x00 // Item Hue + }; + + int pos = 1; + itemInFirstCont.Serial.CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.ItemID).CopyTo(ref pos, expectedData); + pos++; + ((short)itemInFirstCont.Amount).CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.X).CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.Y).CopyTo(ref pos, expectedData); + to.Serial.CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.Hue).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestSecureTradeEquip6017() + { + Span data = new SecureTradeEquip6017(itemInFirstCont, to).Compile(); + Span expectedData = stackalloc byte[] + { + 0x25, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Item Serial + 0x00, 0x00, // Item ItemID + 0x00, // Unknown + 0x00, 0x00, // Item Amount + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, // Grid Location + 0x00, 0x00, 0x00, 0x00, // Mobile Serial + 0x00, 0x00 // Item Hue + }; + + int pos = 1; + itemInFirstCont.Serial.CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.ItemID).CopyTo(ref pos, expectedData); + pos++; + ((short)itemInFirstCont.Amount).CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.X).CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.Y).CopyTo(ref pos, expectedData); + pos++; + to.Serial.CopyTo(ref pos, expectedData); + ((short)itemInFirstCont.Hue).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Outgoing/Old/VendorBuyPacketTests.cs b/Projects/Server.Tests/Network/Packets/Outgoing/Old/VendorBuyPacketTests.cs new file mode 100644 index 000000000..bf801f2eb --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Outgoing/Old/VendorBuyPacketTests.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Server.Items; +using Server.Network; +using Xunit; +using Xunit.Abstractions; + +namespace Server.Tests.Network.Packets +{ + public class VendorBuyPacketTests : IClassFixture + { + private readonly Mobile vendor; + private readonly Container cont; + + private readonly List buyStates; + + public VendorBuyPacketTests(ServerFixture fixture) + { + vendor = fixture.fromMobile; + cont = fixture.fromCont; + + buyStates = new List + { + new BuyItemState("First Item", cont.Serial, Serial.NewItem, 10, 1, 0x01, 0), + new BuyItemState("Second Item", cont.Serial, Serial.NewItem, 20, 2, 0x0A, 0), + new BuyItemState("Third Item", cont.Serial, Serial.NewItem, 30, 10, 0x0F, 0) + }; + } + + [Fact] + public void TestVendorBuyContent() + { + Span data = new VendorBuyContent(buyStates).Compile(); + + Span expectedData = stackalloc byte[5 + buyStates.Count * 19]; + + int pos = 0; + + expectedData[pos++] = 0x3C; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + ((ushort)buyStates.Count).CopyTo(ref pos, expectedData); // Count + + for (int i = buyStates.Count - 1; i >= 0; i--) + { + BuyItemState buyState = buyStates[i]; + + buyState.MySerial.CopyTo(ref pos, expectedData); + ((ushort)buyState.ItemID).CopyTo(ref pos, expectedData); + pos++; // ItemID Offset + ((ushort)buyState.Amount).CopyTo(ref pos, expectedData); + ((ushort)(i + 1)).CopyTo(ref pos, expectedData); // X + expectedData[pos++] = 0; + expectedData[pos++] = 1; // Y + buyState.ContainerSerial.CopyTo(ref pos, expectedData); + ((ushort)buyState.Hue).CopyTo(ref pos, expectedData); + } + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestVendorBuyContent6017() + { + Span data = new VendorBuyContent6017(buyStates).Compile(); + + Span expectedData = stackalloc byte[5 + buyStates.Count * 20]; + + int pos = 0; + + expectedData[pos++] = 0x3C; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + ((ushort)buyStates.Count).CopyTo(ref pos, expectedData); // Count + + for (int i = buyStates.Count - 1; i >= 0; i--) + { + BuyItemState buyState = buyStates[i]; + + buyState.MySerial.CopyTo(ref pos, expectedData); + ((ushort)buyState.ItemID).CopyTo(ref pos, expectedData); + pos++; // ItemID Offset + ((ushort)buyState.Amount).CopyTo(ref pos, expectedData); + ((ushort)(i + 1)).CopyTo(ref pos, expectedData); // X + expectedData[pos++] = 0; + expectedData[pos++] = 1; // Y + expectedData[pos++] = 0; // Grid Location + buyState.ContainerSerial.CopyTo(ref pos, expectedData); + ((ushort)buyState.Hue).CopyTo(ref pos, expectedData); + } + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestDisplayBuyList() + { + Span data = new DisplayBuyList(vendor).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x24, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Vendor Serial + 0x00, 0x30 // Buy Window Gump Id + }; + + vendor.Serial.CopyTo(expectedData.Slice(1, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestDisplayBuyListHS() + { + Span data = new DisplayBuyListHS(vendor).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x24, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Vendor Serial + 0x00, 0x30, // Buy Window Gump Id + 0x00, 0x00 + }; + + vendor.Serial.CopyTo(expectedData.Slice(1, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestVendorBuyList() + { + Span data = new VendorBuyList(vendor, buyStates).Compile(); + + int length = 8 + 5 * 3 + buyStates.Sum(state => state.Description.Length + 1); + + Span expectedData = stackalloc byte[length]; + + int pos = 0; + + expectedData[pos++] = 0x74; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + Serial.MinusOne.CopyTo(ref pos, expectedData); // Vendor Buy Pack Serial or -1 + expectedData[pos++] = (byte)buyStates.Count; + + for (int i = 0; i < buyStates.Count; i++) + { + BuyItemState state = buyStates[i]; + state.Price.CopyTo(ref pos, expectedData); + string desc = state.Description ?? ""; + expectedData[pos++] = (byte)(desc.Length + 1); + desc.CopyASCIITo(ref pos, expectedData); + pos++; + } + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs b/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs new file mode 100644 index 000000000..5ed95ad2c --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs @@ -0,0 +1,84 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Text; +using Server.Network; + +namespace Server.Tests.Network.Packets +{ + public static class PacketTestUtilities + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span Compile(this Packet p) => + p.Compile(false, out int length).AsSpan(0, length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this ushort number, Span bytes) => BinaryPrimitives.WriteUInt16BigEndian(bytes, number); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this short number, Span bytes) => BinaryPrimitives.WriteInt16BigEndian(bytes, number); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this Serial s, Span bytes) => BinaryPrimitives.WriteUInt32BigEndian(bytes, s); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this uint number, Span bytes) => BinaryPrimitives.WriteUInt32BigEndian(bytes, number); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this int number, Span bytes) => BinaryPrimitives.WriteInt32BigEndian(bytes, number); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyASCIITo(this string str, Span bytes) => Encoding.ASCII.GetBytes(str.AsSpan(), bytes); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyASCIITo(this string str, int max, Span bytes) => Encoding.ASCII.GetBytes(str.AsSpan(0, Math.Min(str.Length, max)), bytes); + + // With Position Reference + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this ushort number, ref int pos, Span bytes) + { + BinaryPrimitives.WriteUInt16BigEndian(bytes.Slice(pos, 2), number); + pos += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this short number, ref int pos, Span bytes) + { + BinaryPrimitives.WriteInt16BigEndian(bytes.Slice(pos, 2), number); + pos += 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this Serial s, ref int pos, Span bytes) + { + BinaryPrimitives.WriteUInt32BigEndian(bytes.Slice(pos, 4), s); + pos += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this uint number, ref int pos, Span bytes) + { + BinaryPrimitives.WriteUInt32BigEndian(bytes.Slice(pos, 4), number); + pos += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyTo(this int number, ref int pos, Span bytes) + { + BinaryPrimitives.WriteInt32BigEndian(bytes.Slice(pos, 4), number); + pos += 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyASCIITo(this string str, ref int pos, Span bytes) + { + pos += Encoding.ASCII.GetBytes(str.AsSpan(), bytes.Slice(pos)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyASCIITo(this string str, ref int pos, int max, Span bytes) + { + pos += Encoding.ASCII.GetBytes(str.AsSpan(0, Math.Min(str.Length, max)), bytes.Slice(pos)); + } + } +} diff --git a/Projects/Server.Tests/ServerFixture.cs b/Projects/Server.Tests/ServerFixture.cs index e68435f6a..07a9f95c7 100644 --- a/Projects/Server.Tests/ServerFixture.cs +++ b/Projects/Server.Tests/ServerFixture.cs @@ -1,13 +1,19 @@ using System; +using Server.Items; using Server.Misc; namespace Server.Tests { public class ServerFixture : IDisposable { - public Mobile mobile { get; } + private static Mobile m_FromMobile; + private static Mobile m_ToMobile; + private static Container m_FromCont; + private static Container m_ToCont; + private static Item m_ItemInFromCont; - public ServerFixture() + // Global setup + static ServerFixture() { // Load Configurations ServerConfiguration.Load(true); @@ -15,11 +21,26 @@ namespace Server.Tests // Configure / Initialize MapDefinitions.Configure(); + // Load the world World.Load(); - mobile = new Mobile(0x1); + m_FromMobile = new Mobile(Serial.NewMobile); + m_FromMobile.DefaultMobileInit(); + + m_ToMobile = new Mobile(Serial.NewMobile); + m_ToMobile.DefaultMobileInit(); + + m_FromCont = new Container(Serial.NewItem); + m_ToCont = new Container(Serial.NewItem); + m_ItemInFromCont = new Item(Serial.NewItem) { Parent = m_FromCont }; } + public Mobile fromMobile => m_FromMobile; + public Mobile toMobile => m_ToMobile; + public Container fromCont => m_FromCont; + public Container toCont => m_ToCont; + public Item itemInFromCont => m_ItemInFromCont; + public void Dispose() { } diff --git a/Projects/Server/Network/Packets.cs b/Projects/Server/Network/Packets.cs index c56de30e8..f9e273ae7 100644 --- a/Projects/Server/Network/Packets.cs +++ b/Projects/Server/Network/Packets.cs @@ -57,336 +57,6 @@ namespace Server.Network Inspecific = 5 } - /*public enum CMEFlags - { - None = 0x00, - Locked = 0x01, - Arrow = 0x02, - x0004 = 0x04, - Color = 0x20, - x0040 = 0x40, - x0080 = 0x80 - }*/ - - public sealed class DamagePacketOld : Packet - { - public DamagePacketOld(Mobile m, int amount) : base(0xBF) - { - EnsureCapacity(11); - - Stream.Write((short)0x22); - Stream.Write((byte)1); - Stream.Write(m.Serial); - - if (amount > 255) - amount = 255; - else if (amount < 0) - amount = 0; - - Stream.Write((byte)amount); - } - } - - public sealed class DamagePacket : Packet - { - public DamagePacket(Mobile m, int amount) : base(0x0B, 7) - { - Stream.Write(m.Serial); - - if (amount > 0xFFFF) - amount = 0xFFFF; - else if (amount < 0) - amount = 0; - - Stream.Write((ushort)amount); - } - - /*public DamagePacket( Mobile m, int amount ) : base( 0xBF ) - { - EnsureCapacity( 11 ); - - m_Stream.Write( (short) 0x22 ); - m_Stream.Write( (byte) 1 ); - m_Stream.Write( (int) m.Serial ); - - if (amount > 255) - amount = 255; - else if (amount < 0) - amount = 0; - - m_Stream.Write( (byte)amount ); - }*/ - } - - public sealed class CancelArrow : Packet - { - public CancelArrow() : base(0xBA, 6) - { - Stream.Write((byte)0); - Stream.Write((short)-1); - Stream.Write((short)-1); - } - } - - public sealed class SetArrow : Packet - { - public SetArrow(int x, int y) : base(0xBA, 6) - { - Stream.Write((byte)1); - Stream.Write((short)x); - Stream.Write((short)y); - } - } - - public sealed class CancelArrowHS : Packet - { - public CancelArrowHS(int x, int y, Serial s) : base(0xBA, 10) - { - Stream.Write((byte)0); - Stream.Write((short)x); - Stream.Write((short)y); - Stream.Write(s); - } - } - - public sealed class SetArrowHS : Packet - { - public SetArrowHS(int x, int y, Serial s) : base(0xBA, 10) - { - Stream.Write((byte)1); - Stream.Write((short)x); - Stream.Write((short)y); - Stream.Write(s); - } - } - - public sealed class DisplaySecureTrade : Packet - { - public DisplaySecureTrade(Mobile them, Container first, Container second, string name) - : base(0x6F) - { - name ??= ""; - - EnsureCapacity(18 + name.Length); - - Stream.Write((byte)0); // Display - Stream.Write(them.Serial); - Stream.Write(first.Serial); - Stream.Write(second.Serial); - Stream.Write(true); - - Stream.WriteAsciiFixed(name, 30); - } - } - - public sealed class CloseSecureTrade : Packet - { - public CloseSecureTrade(Container cont) - : base(0x6F) - { - EnsureCapacity(8); - - Stream.Write((byte)1); // Close - Stream.Write(cont.Serial); - } - } - - public enum TradeFlag : byte - { - Display = 0x0, - Close = 0x1, - Update = 0x2, - UpdateGold = 0x3, - UpdateLedger = 0x4 - } - - public sealed class UpdateSecureTrade : Packet - { - public UpdateSecureTrade(Container cont, bool first, bool second) - : this(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0) - { - } - - public UpdateSecureTrade(Container cont, TradeFlag flag, int first, int second) - : base(0x6F) - { - EnsureCapacity(17); - - Stream.Write((byte)flag); - Stream.Write(cont.Serial); - Stream.Write(first); - Stream.Write(second); - } - } - - public sealed class SecureTradeEquip : Packet - { - public SecureTradeEquip(Item item, Mobile m) : base(0x25, 20) - { - Stream.Write(item.Serial); - Stream.Write((short)item.ItemID); - Stream.Write((byte)0); - Stream.Write((short)item.Amount); - Stream.Write((short)item.X); - Stream.Write((short)item.Y); - Stream.Write(m.Serial); - Stream.Write((short)item.Hue); - } - } - - public sealed class SecureTradeEquip6017 : Packet - { - public SecureTradeEquip6017(Item item, Mobile m) : base(0x25, 21) - { - Stream.Write(item.Serial); - Stream.Write((short)item.ItemID); - Stream.Write((byte)0); - Stream.Write((short)item.Amount); - Stream.Write((short)item.X); - Stream.Write((short)item.Y); - Stream.Write((byte)0); // Grid Location? - Stream.Write(m.Serial); - Stream.Write((short)item.Hue); - } - } - - public sealed class MapPatches : Packet - { - public MapPatches() : base(0xBF) - { - EnsureCapacity(9 + 4 * 8); - - Stream.Write((short)0x0018); - - Stream.Write(4); - - Stream.Write(Map.Felucca.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Felucca.Tiles.Patch.LandBlocks); - - Stream.Write(Map.Trammel.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Trammel.Tiles.Patch.LandBlocks); - - Stream.Write(Map.Ilshenar.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Ilshenar.Tiles.Patch.LandBlocks); - - Stream.Write(Map.Malas.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Malas.Tiles.Patch.LandBlocks); - } - } - - public sealed class ObjectHelpResponse : Packet - { - public ObjectHelpResponse(IEntity e, string text) : base(0xB7) - { - EnsureCapacity(9 + text.Length * 2); - - Stream.Write(e.Serial); - Stream.WriteBigUniNull(text); - } - } - - public sealed class VendorBuyContent : Packet - { - public VendorBuyContent(List list) - : base(0x3c) - { - EnsureCapacity(list.Count * 19 + 5); - - Stream.Write((short)list.Count); - - // The client sorts these by their X/Y value. - // OSI sends these in weird order. X/Y highest to lowest and serial lowest to highest - // These are already sorted by serial (done by the vendor class) but we have to send them by x/y - // (the x74 packet is sent in 'correct' order.) - for (var i = list.Count - 1; i >= 0; --i) - { - var bis = list[i]; - - Stream.Write(bis.MySerial); - Stream.Write((ushort)bis.ItemID); - Stream.Write((byte)0); // itemid offset - Stream.Write((ushort)bis.Amount); - Stream.Write((short)(i + 1)); // x - Stream.Write((short)1); // y - Stream.Write(bis.ContainerSerial); - Stream.Write((ushort)bis.Hue); - } - } - } - - public sealed class VendorBuyContent6017 : Packet - { - public VendorBuyContent6017(List list) : base(0x3c) - { - EnsureCapacity(list.Count * 20 + 5); - - Stream.Write((short)list.Count); - - // The client sorts these by their X/Y value. - // OSI sends these in weird order. X/Y highest to lowest and serial loewst to highest - // These are already sorted by serial (done by the vendor class) but we have to send them by x/y - // (the x74 packet is sent in 'correct' order.) - for (var i = list.Count - 1; i >= 0; --i) - { - var bis = list[i]; - - Stream.Write(bis.MySerial); - Stream.Write((ushort)bis.ItemID); - Stream.Write((byte)0); // itemid offset - Stream.Write((ushort)bis.Amount); - Stream.Write((short)(i + 1)); // x - Stream.Write((short)1); // y - Stream.Write((byte)0); // Grid Location? - Stream.Write(bis.ContainerSerial); - Stream.Write((ushort)bis.Hue); - } - } - } - - public sealed class DisplayBuyList : Packet - { - public DisplayBuyList(Mobile vendor) : base(0x24, 7) - { - Stream.Write(vendor.Serial); - Stream.Write((short)0x30); // buy window id? - } - } - - public sealed class DisplayBuyListHS : Packet - { - public DisplayBuyListHS(Mobile vendor) : base(0x24, 9) - { - Stream.Write(vendor.Serial); - Stream.Write((short)0x30); // buy window id? - Stream.Write((short)0x00); - } - } - - public sealed class VendorBuyList : Packet - { - public VendorBuyList(Mobile vendor, List list) - : base(0x74) - { - EnsureCapacity(256); - - Stream.Write(!(vendor.FindItemOnLayer(Layer.ShopBuy) is Container buyPack) ? Serial.MinusOne : buyPack.Serial); - - Stream.Write((byte)list.Count); - - for (var i = 0; i < list.Count; ++i) - { - var bis = list[i]; - - Stream.Write(bis.Price); - - var desc = bis.Description ?? ""; - - Stream.Write((byte)(desc.Length + 1)); - Stream.WriteAsciiNull(desc); - } - } - } - public sealed class VendorSellList : Packet { public VendorSellList(Mobile shopkeeper, ICollection sis) : base(0x9E) diff --git a/Projects/Server/Network/Packets/OldPackets/ArrowPackets.cs b/Projects/Server/Network/Packets/OldPackets/ArrowPackets.cs new file mode 100644 index 000000000..ca6fe8cfb --- /dev/null +++ b/Projects/Server/Network/Packets/OldPackets/ArrowPackets.cs @@ -0,0 +1,64 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ArrowPackets.cs - Created: 2020/05/03 - Updated: 2020/05/03 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class CancelArrow : Packet + { + public CancelArrow() : base(0xBA, 6) + { + Stream.Write((byte)0); + Stream.Write((short)-1); + Stream.Write((short)-1); + } + } + + public sealed class SetArrow : Packet + { + public SetArrow(int x, int y) : base(0xBA, 6) + { + Stream.Write((byte)1); + Stream.Write((short)x); + Stream.Write((short)y); + } + } + + public sealed class CancelArrowHS : Packet + { + public CancelArrowHS(int x, int y, Serial s) : base(0xBA, 10) + { + Stream.Write((byte)0); + Stream.Write((short)x); + Stream.Write((short)y); + Stream.Write(s); + } + } + + public sealed class SetArrowHS : Packet + { + public SetArrowHS(int x, int y, Serial s) : base(0xBA, 10) + { + Stream.Write((byte)1); + Stream.Write((short)x); + Stream.Write((short)y); + Stream.Write(s); + } + } +} diff --git a/Projects/Server/Network/Packets/OldPackets/DamagePackets.cs b/Projects/Server/Network/Packets/OldPackets/DamagePackets.cs new file mode 100644 index 000000000..de832bea8 --- /dev/null +++ b/Projects/Server/Network/Packets/OldPackets/DamagePackets.cs @@ -0,0 +1,56 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: DamagePackets.cs - Created: 2020/05/03 - Updated: 2020/05/03 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class DamagePacketOld : Packet + { + public DamagePacketOld(Mobile m, int amount) : base(0xBF) + { + EnsureCapacity(11); + + Stream.Write((short)0x22); + Stream.Write((byte)1); + Stream.Write(m.Serial); + + if (amount > 255) + amount = 255; + else if (amount < 0) + amount = 0; + + Stream.Write((byte)amount); + } + } + + public sealed class DamagePacket : Packet + { + public DamagePacket(Mobile m, int amount) : base(0x0B, 7) + { + Stream.Write(m.Serial); + + if (amount > 0xFFFF) + amount = 0xFFFF; + else if (amount < 0) + amount = 0; + + Stream.Write((ushort)amount); + } + } +} diff --git a/Projects/Server/Network/Packets/OldPackets/MapPatches.cs b/Projects/Server/Network/Packets/OldPackets/MapPatches.cs new file mode 100644 index 000000000..c5956b70b --- /dev/null +++ b/Projects/Server/Network/Packets/OldPackets/MapPatches.cs @@ -0,0 +1,47 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: MapPatches.cs - Created: 2020/05/03 - Updated: 2020/05/03 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class MapPatches : Packet + { + // TODO: Base this on the client version and expansion + public MapPatches() : base(0xBF) + { + EnsureCapacity(9 + 4 * 8); + + Stream.Write((short)0x0018); + + Stream.Write(4); + + Stream.Write(Map.Felucca.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Felucca.Tiles.Patch.LandBlocks); + + Stream.Write(Map.Trammel.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Trammel.Tiles.Patch.LandBlocks); + + Stream.Write(Map.Ilshenar.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Ilshenar.Tiles.Patch.LandBlocks); + + Stream.Write(Map.Malas.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Malas.Tiles.Patch.LandBlocks); + } + } +} diff --git a/Projects/Server/Network/Packets/OldPackets/ObjectHelpResponse.cs b/Projects/Server/Network/Packets/OldPackets/ObjectHelpResponse.cs new file mode 100644 index 000000000..1be6a902f --- /dev/null +++ b/Projects/Server/Network/Packets/OldPackets/ObjectHelpResponse.cs @@ -0,0 +1,34 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ObjectHelpResponse.cs * + * Created: 2020/05/03 - Updated: 2020/05/03 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class ObjectHelpResponse : Packet + { + public ObjectHelpResponse(IEntity e, string text) : base(0xB7) + { + EnsureCapacity(9 + text.Length * 2); + + Stream.Write(e.Serial); + Stream.WriteBigUniNull(text); + } + } +} diff --git a/Projects/Server/Network/Packets/OldPackets/SecureTradePackets.cs b/Projects/Server/Network/Packets/OldPackets/SecureTradePackets.cs new file mode 100644 index 000000000..65675d463 --- /dev/null +++ b/Projects/Server/Network/Packets/OldPackets/SecureTradePackets.cs @@ -0,0 +1,115 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: SecureTradePackets.cs * + * Created: 2020/05/03 - Updated: 2020/05/03 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using Server.Items; + +namespace Server.Network +{ + public sealed class DisplaySecureTrade : Packet + { + public DisplaySecureTrade(Mobile them, Container first, Container second, string name) + : base(0x6F) + { + name ??= ""; + + EnsureCapacity(18 + name.Length); + + Stream.Write((byte)0); // Display + Stream.Write(them.Serial); + Stream.Write(first.Serial); + Stream.Write(second.Serial); + Stream.Write(true); + + Stream.WriteAsciiFixed(name, 30); + } + } + + public sealed class CloseSecureTrade : Packet + { + public CloseSecureTrade(Container cont) + : base(0x6F) + { + EnsureCapacity(8); + + Stream.Write((byte)1); // Close + Stream.Write(cont.Serial); + } + } + + public enum TradeFlag : byte + { + Display = 0x0, + Close = 0x1, + Update = 0x2, + UpdateGold = 0x3, + UpdateLedger = 0x4 + } + + public sealed class UpdateSecureTrade : Packet + { + public UpdateSecureTrade(Container cont, bool first, bool second) + : this(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0) + { + } + + public UpdateSecureTrade(Container cont, TradeFlag flag, int first, int second) + : base(0x6F) + { + EnsureCapacity(17); + + Stream.Write((byte)flag); + Stream.Write(cont.Serial); + Stream.Write(first); + Stream.Write(second); + } + } + + public sealed class SecureTradeEquip : Packet + { + public SecureTradeEquip(Item item, Mobile m) : base(0x25, 20) + { + Stream.Write(item.Serial); + Stream.Write((short)item.ItemID); + Stream.Write((byte)0); + Stream.Write((short)item.Amount); + Stream.Write((short)item.X); + Stream.Write((short)item.Y); + Stream.Write(m.Serial); + Stream.Write((short)item.Hue); + } + } + + public sealed class SecureTradeEquip6017 : Packet + { + public SecureTradeEquip6017(Item item, Mobile m) : base(0x25, 21) + { + Stream.Write(item.Serial); + Stream.Write((short)item.ItemID); + Stream.Write((byte)0); + Stream.Write((short)item.Amount); + Stream.Write((short)item.X); + Stream.Write((short)item.Y); + Stream.Write((byte)0); // Grid Location? + Stream.Write(m.Serial); + Stream.Write((short)item.Hue); + } + } +} diff --git a/Projects/Server/Network/Packets/OldPackets/VendorBuyPackets.cs b/Projects/Server/Network/Packets/OldPackets/VendorBuyPackets.cs new file mode 100644 index 000000000..2a0efafbc --- /dev/null +++ b/Projects/Server/Network/Packets/OldPackets/VendorBuyPackets.cs @@ -0,0 +1,121 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: VendorBuyPackets.cs - Created: 2020/05/03 - Updated: 2020/05/03 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Collections.Generic; +using Server.Items; + +namespace Server.Network +{ + public sealed class VendorBuyContent : Packet + { + public VendorBuyContent(List list) : base(0x3C) + { + EnsureCapacity(list.Count * 19 + 5); + + Stream.Write((short)list.Count); + + for (var i = list.Count - 1; i >= 0; --i) + { + var bis = list[i]; + + Stream.Write(bis.MySerial); + Stream.Write((ushort)bis.ItemID); + Stream.Write((byte)0); // itemID offset + Stream.Write((ushort)bis.Amount); + Stream.Write((short)(i + 1)); // x + Stream.Write((short)1); // y + Stream.Write(bis.ContainerSerial); + Stream.Write((ushort)bis.Hue); + } + } + } + + public sealed class VendorBuyContent6017 : Packet + { + public VendorBuyContent6017(List list) : base(0x3C) + { + EnsureCapacity(list.Count * 20 + 5); + + Stream.Write((short)list.Count); + + // The client sorts these by their X/Y value. + // OSI sends these in weird order. X/Y highest to lowest and serial loewst to highest + // These are already sorted by serial (done by the vendor class) but we have to send them by x/y + // (the x74 packet is sent in 'correct' order.) + for (var i = list.Count - 1; i >= 0; --i) + { + var bis = list[i]; + + Stream.Write(bis.MySerial); + Stream.Write((ushort)bis.ItemID); + Stream.Write((byte)0); // itemID offset + Stream.Write((ushort)bis.Amount); + Stream.Write((short)(i + 1)); // x + Stream.Write((short)1); // y + Stream.Write((byte)0); // Grid Location? + Stream.Write(bis.ContainerSerial); + Stream.Write((ushort)bis.Hue); + } + } + } + + public sealed class DisplayBuyList : Packet + { + public DisplayBuyList(Mobile vendor) : base(0x24, 7) + { + Stream.Write(vendor.Serial); + Stream.Write((short)0x30); // buy window id? + } + } + + public sealed class DisplayBuyListHS : Packet + { + public DisplayBuyListHS(Mobile vendor) : base(0x24, 9) + { + Stream.Write(vendor.Serial); + Stream.Write((short)0x30); // buy window id? + Stream.Write((short)0x00); + } + } + + public sealed class VendorBuyList : Packet + { + public VendorBuyList(Mobile vendor, List list) : base(0x74) + { + EnsureCapacity(256); + + Stream.Write(!(vendor.FindItemOnLayer(Layer.ShopBuy) is Container buyPack) ? Serial.MinusOne : buyPack.Serial); + + Stream.Write((byte)list.Count); + + for (var i = 0; i < list.Count; ++i) + { + var bis = list[i]; + + Stream.Write(bis.Price); + + var desc = bis.Description ?? ""; + + Stream.Write((byte)(desc.Length + 1)); + Stream.WriteAsciiNull(desc); + } + } + } +}