feat: Replaces Zlib with LibDeflate (#1774)
> [!Warning] > Users on Linux/OSX will need to follow the Readme > and make sure `libdeflate` is properly installed > [!Note] > **Developer Note** > The API for compression has changed. Use `Deflate.Standard` for the same functionality. ### Summary * Replaces Zlib with LibDeflate for a 50% performance improvement! * Adds MacOS 14 to properly test Arm64
This commit is contained in:
parent
35a292d2c7
commit
1f701e7b55
11 changed files with 279 additions and 282 deletions
|
|
@ -16,252 +16,245 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Server.Compression;
|
||||
using Server.Logging;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server.Multis
|
||||
namespace Server.Multis;
|
||||
|
||||
public static class HousePackets
|
||||
{
|
||||
public static class HousePackets
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(HousePackets));
|
||||
|
||||
public static void SendBeginHouseCustomization(this NetState ns, Serial house)
|
||||
{
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(HousePackets));
|
||||
|
||||
public static void SendBeginHouseCustomization(this NetState ns, Serial house)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0xBF); // Packet Id
|
||||
writer.Write((ushort)17);
|
||||
writer.Write((short)0x20); // Sub-packet
|
||||
writer.Write(house);
|
||||
writer.Write((byte)0x04); // command
|
||||
writer.Write((ushort)0x0000);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((byte)0xFF);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendEndHouseCustomization(this NetState ns, Serial house)
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0xBF); // Packet Id
|
||||
writer.Write((ushort)17);
|
||||
writer.Write((short)0x20); // Sub-packet
|
||||
writer.Write(house);
|
||||
writer.Write((byte)0x04); // command
|
||||
writer.Write((ushort)0x0000);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((byte)0xFF);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendEndHouseCustomization(this NetState ns, Serial house)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0xBF); // Packet Id
|
||||
writer.Write((ushort)17);
|
||||
writer.Write((short)0x20); // Sub-packet
|
||||
writer.Write(house);
|
||||
writer.Write((byte)0x05); // command
|
||||
writer.Write((ushort)0x0000);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((byte)0xFF);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendDesignStateGeneral(this NetState ns, Serial house, int revision)
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0xBF); // Packet Id
|
||||
writer.Write((ushort)17);
|
||||
writer.Write((short)0x20); // Sub-packet
|
||||
writer.Write(house);
|
||||
writer.Write((byte)0x05); // command
|
||||
writer.Write((ushort)0x0000);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((ushort)0xFFFF);
|
||||
writer.Write((byte)0xFF);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendDesignStateGeneral(this NetState ns, Serial house, int revision)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[13]);
|
||||
writer.Write((byte)0xBF); // Packet Id
|
||||
writer.Write((ushort)13);
|
||||
writer.Write((short)0x1D); // Sub-packet
|
||||
writer.Write(house);
|
||||
writer.Write(revision);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
private const int planeCount = 9;
|
||||
private const int maxPlaneLength = 0x400;
|
||||
private const int maxPerPlaneOffsetBuffer = 750;
|
||||
private static readonly int maxPackedPlaneOffsetBuffer = Zlib.MaxPackSize(maxPerPlaneOffsetBuffer * 5);
|
||||
var writer = new SpanWriter(stackalloc byte[13]);
|
||||
writer.Write((byte)0xBF); // Packet Id
|
||||
writer.Write((ushort)13);
|
||||
writer.Write((short)0x1D); // Sub-packet
|
||||
writer.Write(house);
|
||||
writer.Write(revision);
|
||||
|
||||
public static byte[] CreateHouseDesignStateDetailed(Serial serial, int revision, MultiComponentList components)
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
private const int planeCount = 9;
|
||||
private const int maxPlaneLength = 0x400;
|
||||
private const int maxPerPlaneOffsetBuffer = 750;
|
||||
|
||||
public static byte[] CreateHouseDesignStateDetailed(Serial 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 width = xMax - xMin + 1;
|
||||
var height = yMax - yMin + 1;
|
||||
var tiles = components.List;
|
||||
|
||||
var planeLength = width * height * 2;
|
||||
using var planeOffsetWriter = new SpanWriter(0x500, true);
|
||||
using var planesWriter = new SpanWriter(planeLength * planeCount);
|
||||
|
||||
Span<bool> planesUsed = stackalloc bool[9];
|
||||
planesUsed.Clear();
|
||||
|
||||
int index;
|
||||
var totalPlaneOffsets = 0;
|
||||
var totalPlanes = 0;
|
||||
|
||||
for (var i = 0; i < tiles.Length; ++i)
|
||||
{
|
||||
var xMin = components.Min.X;
|
||||
var yMin = components.Min.Y;
|
||||
var xMax = components.Max.X;
|
||||
var yMax = components.Max.Y;
|
||||
var width = xMax - xMin + 1;
|
||||
var height = yMax - yMin + 1;
|
||||
var tiles = components.List;
|
||||
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;
|
||||
|
||||
var planeLength = width * height * 2;
|
||||
using var planeOffsetWriter = new SpanWriter(0x500, true);
|
||||
using var planesWriter = new SpanWriter(planeLength * planeCount);
|
||||
|
||||
Span<bool> planesUsed = stackalloc bool[9];
|
||||
planesUsed.Clear();
|
||||
|
||||
int index;
|
||||
var totalPlaneOffsets = 0;
|
||||
var totalPlanes = 0;
|
||||
|
||||
for (var i = 0; i < tiles.Length; ++i)
|
||||
int plane = z switch
|
||||
{
|
||||
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;
|
||||
0 => 0,
|
||||
7 => 1,
|
||||
27 => 2,
|
||||
47 => 3,
|
||||
67 => 4,
|
||||
_ => -1
|
||||
};
|
||||
|
||||
int plane = z switch
|
||||
if (plane > -1)
|
||||
{
|
||||
int size;
|
||||
if (plane == 0)
|
||||
{
|
||||
0 => 0,
|
||||
7 => 1,
|
||||
27 => 2,
|
||||
47 => 3,
|
||||
67 => 4,
|
||||
_ => -1
|
||||
};
|
||||
|
||||
if (plane > -1)
|
||||
size = height;
|
||||
}
|
||||
else if (floor)
|
||||
{
|
||||
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 < maxPlaneLength)
|
||||
{
|
||||
var planeUsed = planesUsed[plane];
|
||||
var planeWriterIndex = planeLength * plane;
|
||||
if (!planeUsed)
|
||||
{
|
||||
planesUsed[plane] = true;
|
||||
totalPlanes++;
|
||||
planesWriter.Seek(planeWriterIndex, SeekOrigin.Begin);
|
||||
planesWriter.Clear(planeLength);
|
||||
}
|
||||
|
||||
planesWriter.Seek(planeWriterIndex + index, SeekOrigin.Begin);
|
||||
planesWriter.Write(mte.ItemId);
|
||||
continue;
|
||||
}
|
||||
size = height - 2;
|
||||
x -= 1;
|
||||
y -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = height - 1;
|
||||
plane += 4;
|
||||
}
|
||||
|
||||
planeOffsetWriter.Write(mte.ItemId);
|
||||
planeOffsetWriter.Write((byte)mte.OffsetX);
|
||||
planeOffsetWriter.Write((byte)mte.OffsetY);
|
||||
planeOffsetWriter.Write((byte)mte.OffsetZ);
|
||||
totalPlaneOffsets++;
|
||||
}
|
||||
index = (x * size + y) * 2;
|
||||
|
||||
var maxPlanesLength = (Zlib.MaxPackSize(planeLength) + 4) * totalPlanes;
|
||||
var maxPlaneOffsetLength = totalPlaneOffsets == 0 ? 0
|
||||
: (maxPackedPlaneOffsetBuffer + 4) * (totalPlaneOffsets / maxPerPlaneOffsetBuffer + 1);
|
||||
|
||||
var buffer = GC.AllocateUninitializedArray<byte>(18 + maxPlanesLength + maxPlaneOffsetLength);
|
||||
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 totalLength = 1; // includes plane count
|
||||
|
||||
for (var i = 0; i < planeCount; i++)
|
||||
{
|
||||
if (!planesUsed[i])
|
||||
if (x >= 0 && y >= 0 && y < size && index + 1 < maxPlaneLength)
|
||||
{
|
||||
var planeUsed = planesUsed[plane];
|
||||
var planeWriterIndex = planeLength * plane;
|
||||
if (!planeUsed)
|
||||
{
|
||||
planesUsed[plane] = true;
|
||||
totalPlanes++;
|
||||
planesWriter.Seek(planeWriterIndex, SeekOrigin.Begin);
|
||||
planesWriter.Clear(planeLength);
|
||||
}
|
||||
|
||||
planesWriter.Seek(planeWriterIndex + index, SeekOrigin.Begin);
|
||||
planesWriter.Write(mte.ItemId);
|
||||
continue;
|
||||
}
|
||||
|
||||
int size = i switch
|
||||
{
|
||||
0 => planeLength,
|
||||
< 5 => (width - 1) * (height - 2) * 2,
|
||||
_ => width * (height - 1) * 2
|
||||
};
|
||||
|
||||
var planeWriterIndex = planeLength * i;
|
||||
|
||||
var source = planesWriter.RawBuffer.Slice(planeWriterIndex, size);
|
||||
|
||||
writer.Write((byte)(0x20 | i));
|
||||
WritePacked(source, ref writer, out int destLength);
|
||||
|
||||
totalLength += 4 + destLength;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
while (totalPlaneOffsets > 0)
|
||||
{
|
||||
var count = Math.Min(maxPerPlaneOffsetBuffer, totalPlaneOffsets);
|
||||
totalPlaneOffsets -= count;
|
||||
|
||||
var source = planeOffsetWriter.RawBuffer.Slice(index * 5, count * 5);
|
||||
|
||||
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;
|
||||
planeOffsetWriter.Write(mte.ItemId);
|
||||
planeOffsetWriter.Write((byte)mte.OffsetX);
|
||||
planeOffsetWriter.Write((byte)mte.OffsetY);
|
||||
planeOffsetWriter.Write((byte)mte.OffsetZ);
|
||||
totalPlaneOffsets++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WritePacked(ReadOnlySpan<byte> source, ref SpanWriter writer, out int length)
|
||||
var writer = new SpanWriter(0x10000);
|
||||
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 totalLength = 1; // includes plane count
|
||||
|
||||
for (var i = 0; i < planeCount; i++)
|
||||
{
|
||||
var size = source.Length;
|
||||
var dest = writer.RawBuffer[(writer.Position + 3)..];
|
||||
length = dest.Length;
|
||||
|
||||
var ce = Zlib.Pack(dest, ref length, source, ZlibQuality.Default);
|
||||
|
||||
if (ce != ZlibError.Okay)
|
||||
if (!planesUsed[i])
|
||||
{
|
||||
logger.Warning("ZLib error: {Error} (#{ErrorCode})", ce, (int)ce);
|
||||
length = 0;
|
||||
size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int size = i switch
|
||||
{
|
||||
0 => planeLength,
|
||||
< 5 => (width - 1) * (height - 2) * 2,
|
||||
_ => width * (height - 1) * 2
|
||||
};
|
||||
|
||||
var planeWriterIndex = planeLength * i;
|
||||
|
||||
var source = planesWriter.RawBuffer.Slice(planeWriterIndex, size);
|
||||
|
||||
writer.Write((byte)(0x20 | i));
|
||||
writer.Write((byte)size);
|
||||
writer.Write((byte)length);
|
||||
writer.Write((byte)(((size >> 4) & 0xF0) | ((length >> 8) & 0xF)));
|
||||
writer.Seek(length, SeekOrigin.Current);
|
||||
WritePacked(source, ref writer, out int destLength);
|
||||
|
||||
totalLength += 4 + destLength;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
while (totalPlaneOffsets > 0)
|
||||
{
|
||||
var count = Math.Min(maxPerPlaneOffsetBuffer, totalPlaneOffsets);
|
||||
totalPlaneOffsets -= count;
|
||||
var size = count * 5;
|
||||
|
||||
var source = planeOffsetWriter.RawBuffer.Slice(index * 5, size);
|
||||
|
||||
writer.Write((byte)(9 + index++));
|
||||
writer.Write((byte)size);
|
||||
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();
|
||||
|
||||
var buffer = writer.Span.ToArray();
|
||||
writer.Dispose();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void WritePacked(ReadOnlySpan<byte> source, ref SpanWriter writer, out int length)
|
||||
{
|
||||
var size = source.Length;
|
||||
var dest = writer.RawBuffer[(writer.Position + 2)..];
|
||||
|
||||
length = Deflate.Standard.Pack(dest, source);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
logger.Warning("House packet compression failed.");
|
||||
}
|
||||
|
||||
writer.Write((byte)length);
|
||||
writer.Write((byte)(((size >> 4) & 0xF0) | ((length >> 8) & 0xF)));
|
||||
writer.Seek(length, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue