From ec1cc108218a3adb08b665de0bfb234c85eec053 Mon Sep 17 00:00:00 2001
From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com>
Date: Wed, 3 Mar 2021 02:09:15 -0800
Subject: [PATCH] fix(network): Converts House Design Detailed Packet (#538)
- [X] Converts design state detailed packet
- [X] Removes `Packet` class
- [X] Removes `Send(Packet)` function signatures
- [X] Removes `PacketWriter` class
- [X] Updates dependencies.
---
.github/workflows/build-test.yml | 2 +-
Directory.Build.props | 6 +-
.../Helpers}/Packet.cs | 0
.../Helpers}/PacketWriter.cs | 0
Projects/Server.Tests/Server.Tests.csproj | 2 +-
Projects/Server/Buffers/SpanWriter.cs | 2 +-
Projects/Server/Mobiles/Mobile.cs | 20 +-
Projects/Server/Network/NetState/NetState.cs | 72 ----
Projects/Server/Network/PacketUtilities.cs | 2 +-
.../Network/Packets/IncomingPlayerPackets.cs | 2 +-
.../Network/Packets/OutgoingMobilePackets.cs | 2 +-
.../Network/Packets/OutgoingPlayerPackets.cs | 2 +-
Projects/Server/Server.csproj | 2 +-
Projects/Server/Utilities/Utility.cs | 2 +-
.../Tests/Multis/Houses/HousePacketTests.cs | 29 ++
.../Tests/Multis/Houses/HousePackets.cs | 289 ++++++++++++++
.../UOContent.Tests/UOContent.Tests.csproj | 2 +-
Projects/UOContent/Accounting/Firewall.cs | 2 +-
...opsGumpCommand.cs => SpawnPropsCommand.cs} | 8 +-
.../Multis/Houses/HouseFoundation.cs | 359 +-----------------
.../UOContent/Multis/Houses/HousePackets.cs | 188 ++++++++-
.../Special Systems/Engines/TestCenter.cs | 2 +-
Projects/UOContent/UOContent.csproj | 2 +-
azure-pipelines.yml | 4 +-
24 files changed, 538 insertions(+), 463 deletions(-)
rename Projects/{Server/Network => Server.Tests/Helpers}/Packet.cs (100%)
rename Projects/{Server/Network => Server.Tests/Helpers}/PacketWriter.cs (100%)
rename Projects/UOContent/Engines/Spawners/{SpawnPropsGumpCommand.cs => SpawnPropsCommand.cs} (92%)
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 66f65a3a0..414a85a2d 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -26,7 +26,7 @@ jobs:
- name: Setup .NET 5
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 5.0.102
+ dotnet-version: 5.0.200
- name: Build
run: ./publish.cmd
- name: Test
diff --git a/Directory.Build.props b/Directory.Build.props
index 109556f2c..27af9da47 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -29,16 +29,16 @@
MUO
$(SolutionDir)
-
+
true
false
true
true
-
+
win-x64
-
+
osx-x64
diff --git a/Projects/Server/Network/Packet.cs b/Projects/Server.Tests/Helpers/Packet.cs
similarity index 100%
rename from Projects/Server/Network/Packet.cs
rename to Projects/Server.Tests/Helpers/Packet.cs
diff --git a/Projects/Server/Network/PacketWriter.cs b/Projects/Server.Tests/Helpers/PacketWriter.cs
similarity index 100%
rename from Projects/Server/Network/PacketWriter.cs
rename to Projects/Server.Tests/Helpers/PacketWriter.cs
diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj
index af438a866..7fe046cda 100644
--- a/Projects/Server.Tests/Server.Tests.csproj
+++ b/Projects/Server.Tests/Server.Tests.csproj
@@ -4,7 +4,7 @@
-
+
diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs
index 8200c4179..feaab3d50 100644
--- a/Projects/Server/Buffers/SpanWriter.cs
+++ b/Projects/Server/Buffers/SpanWriter.cs
@@ -20,7 +20,7 @@ using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
-using Microsoft.Toolkit.HighPerformance.Extensions;
+using Microsoft.Toolkit.HighPerformance;
using Server.Network;
using Server.Text;
diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs
index a1f1cfa5e..af401a00b 100644
--- a/Projects/Server/Mobiles/Mobile.cs
+++ b/Projects/Server/Mobiles/Mobile.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
-using Microsoft.Toolkit.HighPerformance.Extensions;
+using Microsoft.Toolkit.HighPerformance;
using Server.Accounting;
using Server.Buffers;
using Server.ContextMenus;
@@ -7047,24 +7047,6 @@ namespace Server
eable.Free();
}
- public bool Send(Packet p) => Send(p, false);
-
- public bool Send(Packet p, bool throwOnOffline)
- {
- if (m_NetState != null)
- {
- m_NetState.Send(p);
- return true;
- }
-
- if (throwOnOffline)
- {
- throw new MobileNotConnectedException(this, "Packet could not be sent.");
- }
-
- return false;
- }
-
///
/// Overridable. Event invoked before the Mobile says something.
///
diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs
index cc5925552..c1559bd2b 100644
--- a/Projects/Server/Network/NetState/NetState.cs
+++ b/Projects/Server/Network/NetState/NetState.cs
@@ -511,78 +511,6 @@ namespace Server.Network
}
}
- public virtual void Send(Packet p)
- {
- if (Connection == null || BlockAllPackets)
- {
- p.OnSend();
- return;
- }
-
- var writer = SendPipe.Writer;
-
- try
- {
- byte[] buffer = p.Compile(CompressionEnabled, out var length);
-
- if (buffer == null)
- {
- WriteConsole("null buffer send, disconnecting...", this);
- using (StreamWriter op = new StreamWriter("null_send.log", true))
- {
- op.WriteLine("{0} Client: {1}: null buffer send, disconnecting...", DateTime.UtcNow, this);
- op.WriteLine(new System.Diagnostics.StackTrace());
- }
-
- Disconnect("Attempted to send null packet buffer.");
- return;
- }
-
- if (buffer.Length <= 0 || length <= 0)
- {
- p.OnSend();
- return;
- }
-
- PacketSendProfile prof = null;
-
- if (Core.Profiling)
- {
- prof = PacketSendProfile.Acquire(p.PacketID);
- prof.Start();
- }
-
- var result = writer.TryGetMemory();
-
- if (result.Length >= length)
- {
- result.CopyFrom(buffer.AsSpan(0, length));
- writer.Advance((uint)length);
-
- if (!_flushQueued)
- {
- FlushPending.Enqueue(this);
- _flushQueued = true;
- }
- }
- else
- {
- WriteConsole("Too much data pending, disconnecting...");
- Disconnect("Too much data pending.");
- }
-
- prof?.Finish(length);
- }
- catch (Exception ex)
- {
-#if DEBUG
- Console.WriteLine(ex);
-#endif
- TraceException(ex);
- Disconnect("Exception while sending.");
- }
- }
-
internal void Start()
{
if (Interlocked.CompareExchange(ref _running, 1, 0) == 1 || Connection == null)
diff --git a/Projects/Server/Network/PacketUtilities.cs b/Projects/Server/Network/PacketUtilities.cs
index 2edbda4db..33a19ab2e 100644
--- a/Projects/Server/Network/PacketUtilities.cs
+++ b/Projects/Server/Network/PacketUtilities.cs
@@ -17,7 +17,7 @@ using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
-using Microsoft.Toolkit.HighPerformance.Memory;
+using Microsoft.Toolkit.HighPerformance;
namespace Server.Network
{
diff --git a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs
index 50f664788..6e5def9df 100644
--- a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs
+++ b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs
@@ -14,7 +14,7 @@
*************************************************************************/
using System.Diagnostics;
-using Microsoft.Toolkit.HighPerformance.Extensions;
+using Microsoft.Toolkit.HighPerformance;
using Server.Diagnostics;
using Server.Exceptions;
using Server.Gumps;
diff --git a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs
index 4cddfe7d2..b62ff6c63 100644
--- a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs
+++ b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs
@@ -17,7 +17,7 @@ using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
-using Microsoft.Toolkit.HighPerformance.Memory;
+using Microsoft.Toolkit.HighPerformance;
namespace Server.Network
{
diff --git a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs
index 97a8e81a0..59a59919d 100644
--- a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs
+++ b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs
@@ -61,7 +61,7 @@ namespace Server.Network
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SendDeathStatus(this NetState ns, bool dead) =>
- ns?.Send(stackalloc byte[] { 0x2C, dead ? 0 : 2 });
+ ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 });
public static void SendToggleSpecialAbility(this NetState ns, int abilityId, bool active)
{
diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj
index ed620140d..226e8eb82 100644
--- a/Projects/Server/Server.csproj
+++ b/Projects/Server/Server.csproj
@@ -28,7 +28,7 @@
-
+
diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs
index 7186aba67..30e02fe4a 100644
--- a/Projects/Server/Utilities/Utility.cs
+++ b/Projects/Server/Utilities/Utility.cs
@@ -8,7 +8,7 @@ using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml;
-using Microsoft.Toolkit.HighPerformance.Extensions;
+using Microsoft.Toolkit.HighPerformance;
using Server.Buffers;
using Server.Network;
using Server.Random;
diff --git a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs
index ef2c7b080..87b308814 100644
--- a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs
+++ b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs
@@ -1,4 +1,6 @@
using System;
+using System.Linq;
+using Server;
using Server.Multis;
using Server.Network;
using Server.Tests;
@@ -48,5 +50,32 @@ namespace UOContent.Tests
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
+
+ [Fact]
+ public void TestHouseDesignStateDetailed()
+ {
+ Serial serial = 0x40000001;
+ var revision = 10;
+ var tiles = new MultiTileEntry[250];
+ for (var i = 0; i < tiles.Length; i++)
+ {
+ tiles[i] = new MultiTileEntry(
+ (ushort)i,
+ (byte)i,
+ (byte)i,
+ (byte)(i / 50),
+ TileFlag.None
+ );
+ }
+ var mcl = new MultiComponentList(tiles.ToList());
+
+ var expected = new DesignStateDetailed(
+ serial, revision, mcl.Min.X, mcl.Min.Y, mcl.Max.X, mcl.Max.Y, tiles
+ ).Compile();
+
+ var actual = HousePackets.CreateHouseDesignStateDetailed(serial, revision, mcl);
+
+ AssertThat.Equal(actual, expected);
+ }
}
}
diff --git a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs
index 66b4076d1..fe94744d3 100644
--- a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs
+++ b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs
@@ -1,3 +1,9 @@
+using System;
+using System.Buffers;
+using System.IO;
+using System.IO.Compression;
+using Server.Multis;
+
namespace Server.Network
{
public class BeginHouseCustomization : Packet
@@ -43,4 +49,287 @@ namespace Server.Network
Stream.Write(revision);
}
}
+
+ public sealed class DesignStateDetailed : Packet
+ {
+ public const int MaxItemsPerStairBuffer = 750;
+
+ private readonly bool[] m_PlaneUsed = new bool[9];
+ private readonly byte[] m_PrimBuffer = new byte[4];
+
+ public DesignStateDetailed(uint serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles)
+ : base(0xD8)
+ {
+ EnsureCapacity(17 + tiles.Length * 5);
+
+ Write((byte)0x03); // Compression Type
+ Write((byte)0x00); // Unknown
+ Write(serial);
+ Write(revision);
+ Write((short)tiles.Length);
+ Write((short)0); // Buffer length : reserved
+ Write((byte)0); // Plane count : reserved
+
+ var totalLength = 1; // includes plane count
+
+ var width = xMax - xMin + 1;
+ var height = yMax - yMin + 1;
+
+ var planeBuffers = new byte[9][];
+
+ for (var i = 0; i < planeBuffers.Length; ++i)
+ {
+ planeBuffers[i] = ArrayPool.Shared.Rent(0x400);
+ }
+
+ var stairBuffers = new byte[6][];
+
+ for (var i = 0; i < stairBuffers.Length; ++i)
+ {
+ stairBuffers[i] = ArrayPool.Shared.Rent(MaxItemsPerStairBuffer * 5);
+ }
+
+ Clear(planeBuffers[0], width * height * 2);
+
+ for (var i = 0; i < 4; ++i)
+ {
+ Clear(planeBuffers[1 + i], (width - 1) * (height - 2) * 2);
+ Clear(planeBuffers[5 + i], width * (height - 1) * 2);
+ }
+
+ var totalStairsUsed = 0;
+
+ for (var i = 0; i < tiles.Length; ++i)
+ {
+ var mte = tiles[i];
+ var x = mte.OffsetX - xMin;
+ var y = mte.OffsetY - yMin;
+ int z = mte.OffsetZ;
+ var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0;
+ int plane, size;
+
+ switch (z)
+ {
+ case 0:
+ plane = 0;
+ break;
+ case 7:
+ plane = 1;
+ break;
+ case 27:
+ plane = 2;
+ break;
+ case 47:
+ plane = 3;
+ break;
+ case 67:
+ plane = 4;
+ break;
+ default:
+ {
+ var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer;
+ var stairBuffer = stairBuffers[stairBufferIndex];
+
+ var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5;
+
+ stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8);
+ stairBuffer[byteIndex++] = (byte)mte.ItemId;
+
+ stairBuffer[byteIndex++] = (byte)mte.OffsetX;
+ stairBuffer[byteIndex++] = (byte)mte.OffsetY;
+ stairBuffer[byteIndex] = (byte)mte.OffsetZ;
+
+ ++totalStairsUsed;
+
+ continue;
+ }
+ }
+
+ if (plane == 0)
+ {
+ size = height;
+ }
+ else if (floor)
+ {
+ size = height - 2;
+ x -= 1;
+ y -= 1;
+ }
+ else
+ {
+ size = height - 1;
+ plane += 4;
+ }
+
+ var index = (x * size + y) * 2;
+
+ if (x < 0 || y < 0 || y >= size || index + 1 >= 0x400)
+ {
+ var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer;
+ var stairBuffer = stairBuffers[stairBufferIndex];
+
+ var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5;
+
+ stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8);
+ stairBuffer[byteIndex++] = (byte)mte.ItemId;
+
+ stairBuffer[byteIndex++] = (byte)mte.OffsetX;
+ stairBuffer[byteIndex++] = (byte)mte.OffsetY;
+ stairBuffer[byteIndex] = (byte)mte.OffsetZ;
+
+ ++totalStairsUsed;
+ }
+ else
+ {
+ m_PlaneUsed[plane] = true;
+ planeBuffers[plane][index] = (byte)(mte.ItemId >> 8);
+ planeBuffers[plane][index + 1] = (byte)mte.ItemId;
+ }
+ }
+
+ var planeCount = 0;
+
+ var deflatedBuffer = ArrayPool.Shared.Rent(0x2000);
+
+ for (var i = 0; i < planeBuffers.Length; ++i)
+ {
+ if (!m_PlaneUsed[i])
+ {
+ ArrayPool.Shared.Return(planeBuffers[i]);
+ continue;
+ }
+
+ ++planeCount;
+
+ int size = i switch
+ {
+ 0 => width * height * 2,
+ < 5 => (width - 1) * (height - 2) * 2,
+ _ => width * (height - 1) * 2
+ };
+
+ var inflatedBuffer = planeBuffers[i];
+
+ var deflatedLength = deflatedBuffer.Length;
+ var ce = Zlib.Pack(
+ deflatedBuffer,
+ ref deflatedLength,
+ inflatedBuffer,
+ size,
+ ZlibQuality.Default
+ );
+
+ if (ce != ZlibError.Okay)
+ {
+ Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce);
+ deflatedLength = 0;
+ size = 0;
+ }
+
+ Write((byte)(0x20 | i));
+ Write((byte)size);
+ Write((byte)deflatedLength);
+ Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)));
+ Write(deflatedBuffer, 0, deflatedLength);
+
+ totalLength += 4 + deflatedLength;
+ ArrayPool.Shared.Return(inflatedBuffer);
+ }
+
+ var totalStairBuffersUsed = (totalStairsUsed + (MaxItemsPerStairBuffer - 1)) / MaxItemsPerStairBuffer;
+
+ for (var i = 0; i < totalStairBuffersUsed; ++i)
+ {
+ ++planeCount;
+
+ var count = Math.Min(MaxItemsPerStairBuffer, totalStairsUsed - i * MaxItemsPerStairBuffer);
+
+ var size = count * 5;
+
+ var inflatedBuffer = stairBuffers[i];
+
+ var deflatedLength = deflatedBuffer.Length;
+ var ce = Zlib.Pack(
+ deflatedBuffer,
+ ref deflatedLength,
+ inflatedBuffer,
+ size,
+ ZlibQuality.Default
+ );
+
+ if (ce != ZlibError.Okay)
+ {
+ Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce);
+ deflatedLength = 0;
+ size = 0;
+ }
+
+ Write((byte)(9 + i));
+ Write((byte)size);
+ Write((byte)deflatedLength);
+ Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)));
+ Write(deflatedBuffer, 0, deflatedLength);
+
+ totalLength += 4 + deflatedLength;
+ }
+
+ for (var i = 0; i < stairBuffers.Length; ++i)
+ {
+ ArrayPool.Shared.Return(stairBuffers[i]);
+ }
+
+ ArrayPool.Shared.Return(deflatedBuffer);
+
+ Stream.Seek(15, SeekOrigin.Begin);
+
+ Write((short)totalLength); // Buffer length
+ Write((byte)planeCount); // Plane count
+ }
+
+ public void Write(int value)
+ {
+ m_PrimBuffer[0] = (byte)(value >> 24);
+ m_PrimBuffer[1] = (byte)(value >> 16);
+ m_PrimBuffer[2] = (byte)(value >> 8);
+ m_PrimBuffer[3] = (byte)value;
+
+ Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4);
+ }
+
+ public void Write(uint value)
+ {
+ m_PrimBuffer[0] = (byte)(value >> 24);
+ m_PrimBuffer[1] = (byte)(value >> 16);
+ m_PrimBuffer[2] = (byte)(value >> 8);
+ m_PrimBuffer[3] = (byte)value;
+
+ Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4);
+ }
+
+ public void Write(short value)
+ {
+ m_PrimBuffer[0] = (byte)(value >> 8);
+ m_PrimBuffer[1] = (byte)value;
+
+ Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 2);
+ }
+
+ public void Write(byte value)
+ {
+ Stream.UnderlyingStream.WriteByte(value);
+ }
+
+ public void Write(byte[] buffer, int offset, int size)
+ {
+ Stream.UnderlyingStream.Write(buffer, offset, size);
+ }
+
+ public static void Clear(byte[] buffer, int size)
+ {
+ for (var i = 0; i < size; ++i)
+ {
+ buffer[i] = 0;
+ }
+ }
+ }
}
diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj
index 05672ef81..c7c1c1f59 100644
--- a/Projects/UOContent.Tests/UOContent.Tests.csproj
+++ b/Projects/UOContent.Tests/UOContent.Tests.csproj
@@ -4,7 +4,7 @@
-
+
diff --git a/Projects/UOContent/Accounting/Firewall.cs b/Projects/UOContent/Accounting/Firewall.cs
index b00b74cf5..d1deaf097 100644
--- a/Projects/UOContent/Accounting/Firewall.cs
+++ b/Projects/UOContent/Accounting/Firewall.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.CompilerServices;
-using Microsoft.Toolkit.HighPerformance.Extensions;
+using Microsoft.Toolkit.HighPerformance;
namespace Server
{
diff --git a/Projects/UOContent/Engines/Spawners/SpawnPropsGumpCommand.cs b/Projects/UOContent/Engines/Spawners/SpawnPropsCommand.cs
similarity index 92%
rename from Projects/UOContent/Engines/Spawners/SpawnPropsGumpCommand.cs
rename to Projects/UOContent/Engines/Spawners/SpawnPropsCommand.cs
index 6681067c3..1f3c0200a 100644
--- a/Projects/UOContent/Engines/Spawners/SpawnPropsGumpCommand.cs
+++ b/Projects/UOContent/Engines/Spawners/SpawnPropsCommand.cs
@@ -2,7 +2,7 @@
* ModernUO *
* Copyright (C) 2019-2021 - ModernUO Development Team *
* Email: hi@modernuo.com *
- * File: SpawnPropsGump.cs *
+ * File: SpawnPropsCommand.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 *
@@ -21,14 +21,14 @@ using Server.Targeting;
namespace Server.Engines.Spawners
{
- public class SpawnPropsGumpCommand : BaseCommand
+ public class SpawnPropsCommand : BaseCommand
{
public static void Initialize()
{
- TargetCommands.Register(new SpawnPropsGumpCommand());
+ TargetCommands.Register(new SpawnPropsCommand());
}
- public SpawnPropsGumpCommand()
+ public SpawnPropsCommand()
{
AccessLevel = AccessLevel.GameMaster;
Supports = CommandSupport.Complex | CommandSupport.Simple;
diff --git a/Projects/UOContent/Multis/Houses/HouseFoundation.cs b/Projects/UOContent/Multis/Houses/HouseFoundation.cs
index 4da353d76..e9ecefdd2 100644
--- a/Projects/UOContent/Multis/Houses/HouseFoundation.cs
+++ b/Projects/UOContent/Multis/Houses/HouseFoundation.cs
@@ -3,6 +3,7 @@ using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
+using System.Runtime.CompilerServices;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
@@ -1872,8 +1873,6 @@ namespace Server.Multis
public class DesignState
{
- private Packet m_PacketCache;
-
public DesignState(HouseFoundation foundation, MultiComponentList components)
{
Foundation = foundation;
@@ -1926,21 +1925,7 @@ namespace Server.Multis
}
}
- public Packet PacketCache
- {
- get => m_PacketCache;
- set
- {
- if (m_PacketCache == value)
- {
- return;
- }
-
- m_PacketCache?.Release();
-
- m_PacketCache = value;
- }
- }
+ public byte[] PacketCache { get; set; }
public HouseFoundation Foundation { get; }
@@ -1975,31 +1960,18 @@ namespace Server.Multis
public void OnRevised()
{
Revision = ++Foundation.LastRevision;
- m_PacketCache?.Release();
- m_PacketCache = null;
+ PacketCache = null;
}
- public void SendGeneralInfoTo(NetState state)
- {
- state.SendDesignStateGeneral(Foundation.Serial, Revision);
- }
+ public void SendGeneralInfoTo(NetState state) => state.SendDesignStateGeneral(Foundation.Serial, Revision);
+ public void SendDetailedInfoTo(NetState state) => state?.Send(PacketCache ??= SendDetails(Foundation.Serial));
- public void SendDetailedInfoTo(NetState state)
- {
- if (state == null)
- {
- return;
- }
-
- if (m_PacketCache == null)
- {
- DesignStateDetailed.SendDetails(state, Foundation, this);
- }
- else
- {
- state.Send(m_PacketCache);
- }
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte[] SendDetails(Serial house) => HousePackets.CreateHouseDesignStateDetailed(
+ house,
+ Revision,
+ Components
+ );
public void FreezeFixtures()
{
@@ -2375,317 +2347,8 @@ namespace Server.Multis
}
context.Foundation.Signpost?.SendInfoTo(state);
-
context.Foundation.SignHanger?.SendInfoTo(state);
-
context.Foundation.Sign?.SendInfoTo(state);
}
}
-
- public sealed class DesignStateDetailed : Packet
- {
- public const int MaxItemsPerStairBuffer = 750;
-
- private readonly bool[] m_PlaneUsed = new bool[9];
- private readonly byte[] m_PrimBuffer = new byte[4];
-
- public DesignStateDetailed(uint serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles)
- : base(0xD8)
- {
- EnsureCapacity(17 + tiles.Length * 5);
-
- Write((byte)0x03); // Compression Type
- Write((byte)0x00); // Unknown
- Write(serial);
- Write(revision);
- Write((short)tiles.Length);
- Write((short)0); // Buffer length : reserved
- Write((byte)0); // Plane count : reserved
-
- var totalLength = 1; // includes plane count
-
- var width = xMax - xMin + 1;
- var height = yMax - yMin + 1;
-
- var planeBuffers = new byte[9][];
-
- for (var i = 0; i < planeBuffers.Length; ++i)
- {
- planeBuffers[i] = ArrayPool.Shared.Rent(0x400);
- }
-
- var stairBuffers = new byte[6][];
-
- for (var i = 0; i < stairBuffers.Length; ++i)
- {
- stairBuffers[i] = ArrayPool.Shared.Rent(MaxItemsPerStairBuffer * 5);
- }
-
- Clear(planeBuffers[0], width * height * 2);
-
- for (var i = 0; i < 4; ++i)
- {
- Clear(planeBuffers[1 + i], (width - 1) * (height - 2) * 2);
- Clear(planeBuffers[5 + i], width * (height - 1) * 2);
- }
-
- var totalStairsUsed = 0;
-
- for (var i = 0; i < tiles.Length; ++i)
- {
- var mte = tiles[i];
- var x = mte.OffsetX - xMin;
- var y = mte.OffsetY - yMin;
- int z = mte.OffsetZ;
- var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0;
- int plane, size;
-
- switch (z)
- {
- case 0:
- plane = 0;
- break;
- case 7:
- plane = 1;
- break;
- case 27:
- plane = 2;
- break;
- case 47:
- plane = 3;
- break;
- case 67:
- plane = 4;
- break;
- default:
- {
- var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer;
- var stairBuffer = stairBuffers[stairBufferIndex];
-
- var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5;
-
- stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8);
- stairBuffer[byteIndex++] = (byte)mte.ItemId;
-
- stairBuffer[byteIndex++] = (byte)mte.OffsetX;
- stairBuffer[byteIndex++] = (byte)mte.OffsetY;
- stairBuffer[byteIndex] = (byte)mte.OffsetZ;
-
- ++totalStairsUsed;
-
- continue;
- }
- }
-
- if (plane == 0)
- {
- size = height;
- }
- else if (floor)
- {
- size = height - 2;
- x -= 1;
- y -= 1;
- }
- else
- {
- size = height - 1;
- plane += 4;
- }
-
- var index = (x * size + y) * 2;
-
- if (x < 0 || y < 0 || y >= size || index + 1 >= 0x400)
- {
- var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer;
- var stairBuffer = stairBuffers[stairBufferIndex];
-
- var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5;
-
- stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8);
- stairBuffer[byteIndex++] = (byte)mte.ItemId;
-
- stairBuffer[byteIndex++] = (byte)mte.OffsetX;
- stairBuffer[byteIndex++] = (byte)mte.OffsetY;
- stairBuffer[byteIndex] = (byte)mte.OffsetZ;
-
- ++totalStairsUsed;
- }
- else
- {
- m_PlaneUsed[plane] = true;
- planeBuffers[plane][index] = (byte)(mte.ItemId >> 8);
- planeBuffers[plane][index + 1] = (byte)mte.ItemId;
- }
- }
-
- var planeCount = 0;
-
- var deflatedBuffer = ArrayPool.Shared.Rent(0x2000);
-
- for (var i = 0; i < planeBuffers.Length; ++i)
- {
- if (!m_PlaneUsed[i])
- {
- ArrayPool.Shared.Return(planeBuffers[i]);
- continue;
- }
-
- ++planeCount;
-
- int size = i switch
- {
- 0 => width * height * 2,
- < 5 => (width - 1) * (height - 2) * 2,
- _ => width * (height - 1) * 2
- };
-
- var inflatedBuffer = planeBuffers[i];
-
- var deflatedLength = deflatedBuffer.Length;
- var ce = Zlib.Pack(
- deflatedBuffer,
- ref deflatedLength,
- inflatedBuffer,
- size,
- ZlibQuality.Default
- );
-
- if (ce != ZlibError.Okay)
- {
- Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce);
- deflatedLength = 0;
- size = 0;
- }
-
- Write((byte)(0x20 | i));
- Write((byte)size);
- Write((byte)deflatedLength);
- Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)));
- Write(deflatedBuffer, 0, deflatedLength);
-
- totalLength += 4 + deflatedLength;
- ArrayPool.Shared.Return(inflatedBuffer);
- }
-
- var totalStairBuffersUsed = (totalStairsUsed + (MaxItemsPerStairBuffer - 1)) / MaxItemsPerStairBuffer;
-
- for (var i = 0; i < totalStairBuffersUsed; ++i)
- {
- ++planeCount;
-
- var count = Math.Min(MaxItemsPerStairBuffer, totalStairsUsed - i * MaxItemsPerStairBuffer);
-
- var size = count * 5;
-
- var inflatedBuffer = stairBuffers[i];
-
- var deflatedLength = deflatedBuffer.Length;
- var ce = Zlib.Pack(
- deflatedBuffer,
- ref deflatedLength,
- inflatedBuffer,
- size,
- ZlibQuality.Default
- );
-
- if (ce != ZlibError.Okay)
- {
- Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce);
- deflatedLength = 0;
- size = 0;
- }
-
- Write((byte)(9 + i));
- Write((byte)size);
- Write((byte)deflatedLength);
- Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)));
- Write(deflatedBuffer, 0, deflatedLength);
-
- totalLength += 4 + deflatedLength;
- }
-
- for (var i = 0; i < stairBuffers.Length; ++i)
- {
- ArrayPool.Shared.Return(stairBuffers[i]);
- }
-
- ArrayPool.Shared.Return(deflatedBuffer);
-
- Stream.Seek(15, SeekOrigin.Begin);
-
- Write((short)totalLength); // Buffer length
- Write((byte)planeCount); // Plane count
- }
-
- public void Write(int value)
- {
- m_PrimBuffer[0] = (byte)(value >> 24);
- m_PrimBuffer[1] = (byte)(value >> 16);
- m_PrimBuffer[2] = (byte)(value >> 8);
- m_PrimBuffer[3] = (byte)value;
-
- Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4);
- }
-
- public void Write(uint value)
- {
- m_PrimBuffer[0] = (byte)(value >> 24);
- m_PrimBuffer[1] = (byte)(value >> 16);
- m_PrimBuffer[2] = (byte)(value >> 8);
- m_PrimBuffer[3] = (byte)value;
-
- Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4);
- }
-
- public void Write(short value)
- {
- m_PrimBuffer[0] = (byte)(value >> 8);
- m_PrimBuffer[1] = (byte)value;
-
- Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 2);
- }
-
- public void Write(byte value)
- {
- Stream.UnderlyingStream.WriteByte(value);
- }
-
- public void Write(byte[] buffer, int offset, int size)
- {
- Stream.UnderlyingStream.Write(buffer, offset, size);
- }
-
- public static void Clear(byte[] buffer, int size)
- {
- for (var i = 0; i < size; ++i)
- {
- buffer[i] = 0;
- }
- }
-
- public static void SendDetails(NetState ns, HouseFoundation house, DesignState state)
- {
- var p = state.PacketCache;
-
- if (p == null)
- {
- var mcl = state.Components;
- p = new DesignStateDetailed(
- house.Serial,
- state.Revision,
- mcl.Min.X,
- mcl.Min.Y,
- mcl.Max.X,
- mcl.Max.Y,
- mcl.List
- );
-
- p.SetStatic();
- state.PacketCache = p;
- }
-
- ns.Send(p);
- }
- }
}
diff --git a/Projects/UOContent/Multis/Houses/HousePackets.cs b/Projects/UOContent/Multis/Houses/HousePackets.cs
index 0b7bcf17d..e94aa1fbf 100644
--- a/Projects/UOContent/Multis/Houses/HousePackets.cs
+++ b/Projects/UOContent/Multis/Houses/HousePackets.cs
@@ -13,15 +13,17 @@
* along with this program. If not, see . *
*************************************************************************/
+using System;
using System.Buffers;
+using System.IO;
+using System.IO.Compression;
+using System.Runtime.CompilerServices;
using Server.Network;
namespace Server.Multis
{
public static class HousePackets
{
- private const int MaxItemsPerStairBuffer = 750;
-
public static void SendBeginHouseCustomization(this NetState ns, Serial house)
{
if (ns == null)
@@ -80,5 +82,187 @@ namespace Server.Multis
ns.Send(writer.Span);
}
+
+ private const int planeCount = 9;
+ private const int planeLength = 0x400; // ItemID (max 1024 items per plane within the design grid)
+ private const int stairsCount = 6;
+ private const int stairsLength = 5; // ItemID, X, Y, Z (max 4500 items)
+ private const int stairsPerBuffer = 750;
+
+ // Maximum size of the packed packet (31988 bytes)
+ private static readonly int maxPacketLength =
+ 18 +
+ (Zlib.MaxPackSize(planeLength) + 4) * planeCount + // 9369
+ (Zlib.MaxPackSize(stairsPerBuffer * stairsLength) + 4) * stairsCount; // 22602
+
+ public static byte[] CreateHouseDesignStateDetailed(uint serial, int revision, MultiComponentList components)
+ {
+ var xMin = components.Min.X;
+ var yMin = components.Min.Y;
+ var xMax = components.Max.X;
+ var yMax = components.Max.Y;
+ var tiles = components.List;
+
+ const int totalPlaneLength = planeLength * planeCount;
+ const int stairsBufferLength = stairsPerBuffer * stairsLength;
+ const int maxUnpackedSize = totalPlaneLength + stairsBufferLength * stairsCount;
+ using var inflatedWriter = new SpanWriter(maxUnpackedSize);
+
+ Span planesUsed = stackalloc bool[9];
+ var index = 0;
+ var stairsIndex = totalPlaneLength;
+ var totalStairsUsed = 0;
+ var width = xMax - xMin + 1;
+ var height = yMax - yMin + 1;
+
+ for (var i = 0; i < tiles.Length; ++i)
+ {
+ var mte = tiles[i];
+ var x = mte.OffsetX - xMin;
+ var y = mte.OffsetY - yMin;
+ int z = mte.OffsetZ;
+ var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0;
+
+ int plane = z switch
+ {
+ 0 => 0,
+ 7 => 1,
+ 27 => 2,
+ 47 => 3,
+ 67 => 4,
+ _ => -1
+ };
+
+ if (plane > -1)
+ {
+ int size;
+ if (plane == 0)
+ {
+ size = height;
+ }
+ else if (floor)
+ {
+ size = height - 2;
+ x -= 1;
+ y -= 1;
+ }
+ else
+ {
+ size = height - 1;
+ plane += 4;
+ }
+
+ index = (x * size + y) * 2;
+ if (x >= 0 && y >= 0 && y < size && index + 1 < 0x400)
+ {
+ var planeUsed = planesUsed[plane];
+ if (!planeUsed)
+ {
+ inflatedWriter.Seek(plane * planeLength, SeekOrigin.Begin);
+ inflatedWriter.Clear(planeLength);
+ planesUsed[plane] = true;
+ }
+
+ inflatedWriter.Seek(index, SeekOrigin.Begin);
+ inflatedWriter.Write(mte.ItemId);
+ continue;
+ }
+ }
+
+ inflatedWriter.Seek(stairsIndex, SeekOrigin.Begin);
+ inflatedWriter.Write(mte.ItemId);
+ inflatedWriter.Write((byte)mte.OffsetX);
+ inflatedWriter.Write((byte)mte.OffsetY);
+ inflatedWriter.Write((byte)mte.OffsetZ);
+ stairsIndex = inflatedWriter.Position;
+ totalStairsUsed++;
+ }
+
+ var buffer = GC.AllocateUninitializedArray(maxPacketLength);
+ var writer = new SpanWriter(buffer);
+ writer.Write((byte)0xD8); // Packet ID
+ writer.Seek(2, SeekOrigin.Current); // Length
+
+ writer.Write((byte)0x03); // Compression Type
+ writer.Write((byte)0x00); // Unknown
+ writer.Write(serial);
+ writer.Write(revision);
+ writer.Write((short)tiles.Length);
+ writer.Seek(3, SeekOrigin.Current); // Buffer Length, Plane Count
+
+ var totalPlanes = 0;
+ var totalLength = 1; // includes plane count
+
+ for (var i = 0; i < planeCount; i++)
+ {
+ if (!planesUsed[i])
+ {
+ inflatedWriter.Seek(planeCount, SeekOrigin.Current);
+ continue;
+ }
+
+ int size = i switch
+ {
+ 0 => width * height * 2,
+ < 5 => (width - 1) * (height - 2) * 2,
+ _ => width * (height - 1) * 2
+ };
+
+ var source = inflatedWriter.RawBuffer.Slice(i * planeLength, size);
+
+ writer.Write((byte)(0x20 | i));
+ WritePacked(source, ref writer, out int destLength);
+
+ totalLength += 4 + destLength;
+ totalPlanes++;
+ }
+
+ index = 0;
+ while (totalStairsUsed > 0)
+ {
+ var count = Math.Min(stairsPerBuffer, totalStairsUsed);
+ totalStairsUsed -= count;
+
+ var start = totalPlaneLength + index * stairsLength;
+ var size = count * 5;
+ var source = inflatedWriter.RawBuffer.Slice(start, size);
+
+ writer.Write((byte)(9 + index++));
+ WritePacked(source, ref writer, out int destLength);
+ totalLength += 4 + destLength;
+ totalPlanes++;
+ }
+
+ writer.Seek(15, SeekOrigin.Begin);
+ writer.Write((short)totalLength);
+ writer.Write((byte)totalPlanes);
+ writer.WritePacketLength();
+
+ // TODO: Avoid this somehow.
+ Array.Resize(ref buffer, writer.BytesWritten);
+ return buffer;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WritePacked(ReadOnlySpan source, ref SpanWriter writer, out int length)
+ {
+ var size = source.Length;
+ var dest = writer.RawBuffer.Slice(writer.Position + 3);
+ length = dest.Length;
+
+ var ce = Zlib.Pack(dest, ref length, source, ZlibQuality.Default);
+
+ if (ce != ZlibError.Okay)
+ {
+ Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce);
+ length = 0;
+ size = 0;
+ }
+
+ writer.Write((byte)size);
+ writer.Write((byte)length);
+ writer.Write((byte)(((size >> 4) & 0xF0) | ((length >> 8) & 0xF)));
+ writer.Seek(length, SeekOrigin.Current);
+ }
}
}
diff --git a/Projects/UOContent/Special Systems/Engines/TestCenter.cs b/Projects/UOContent/Special Systems/Engines/TestCenter.cs
index 30d3d52cb..25355bea2 100644
--- a/Projects/UOContent/Special Systems/Engines/TestCenter.cs
+++ b/Projects/UOContent/Special Systems/Engines/TestCenter.cs
@@ -1,6 +1,6 @@
using System;
using System.Text;
-using Microsoft.Toolkit.HighPerformance.Extensions;
+using Microsoft.Toolkit.HighPerformance;
using Server.Commands;
using Server.Gumps;
using Server.Network;
diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj
index c3d844ff4..c0c5b034f 100644
--- a/Projects/UOContent/UOContent.csproj
+++ b/Projects/UOContent/UOContent.csproj
@@ -29,7 +29,7 @@
false
-
+
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1c5a4ba79..d361ac282 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -18,7 +18,7 @@ jobs:
displayName: 'Install .NET 5'
inputs:
packageType: sdk
- version: '5.0.102'
+ version: 5.0.200
- task: NuGetAuthenticate@0
- script: ./publish.cmd Release win
displayName: 'Build'
@@ -56,7 +56,7 @@ jobs:
displayName: 'Install .NET 5'
inputs:
packageType: sdk
- version: '5.0.102'
+ version: 5.0.200
- task: NuGetAuthenticate@0
- script: ./publish.cmd Release $(os)
displayName: 'Build'