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:
Kamron Batman 2024-05-16 22:53:01 -07:00 committed by GitHub
parent 35a292d2c7
commit 1f701e7b55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 279 additions and 282 deletions

View file

@ -14,10 +14,10 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: macos-12
name: MacOS 12
- os: macos-13 - os: macos-13
name: MacOS 13 name: MacOS 13
- os: macos-14
name: MacOS 14
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -27,6 +27,12 @@ jobs:
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.x
- name: Install Prerequisites
run: |
brew update
brew install icu4c libdeflate zstd argon2
- name: Set Library Path
run: echo "DYLD_LIBRARY_PATH=/opt/homebrew/lib:\$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
- name: Build - name: Build
run: ./publish.cmd Release run: ./publish.cmd Release
- name: Migration Changes - name: Migration Changes
@ -75,10 +81,10 @@ jobs:
run: dnf upgrade --refresh -y && dnf install -y epel-release epel-next-release run: dnf upgrade --refresh -y && dnf install -y epel-release epel-next-release
if: ${{ startsWith(matrix.name, 'CentOS') }} if: ${{ startsWith(matrix.name, 'CentOS') }}
- name: Install Prerequisites using dnf - name: Install Prerequisites using dnf
run: dnf makecache --refresh && dnf install -y findutils libicu zlib-devel zstd libargon2-devel tzdata run: dnf makecache --refresh && dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel
if: ${{ matrix.packageManager == 'dnf' }} if: ${{ matrix.packageManager == 'dnf' }}
- name: Install Prerequisites using apt - name: Install Prerequisites using apt
run: apt-get update -y && apt-get install -y curl libicu-dev libz-dev zstd libargon2-dev tzdata run: apt-get update -y && apt-get install -y curl libicu-dev libdeflate-dev zstd libargon2-dev
if: ${{ matrix.packageManager == 'apt' }} if: ${{ matrix.packageManager == 'apt' }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:

View file

@ -3,6 +3,7 @@ using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
using Server.Compression;
using Server.Gumps; using Server.Gumps;
using Server.Network; using Server.Network;
@ -165,14 +166,11 @@ namespace Server.Tests
wantLength &= ~4095; wantLength &= ~4095;
var packBuffer = ArrayPool<byte>.Shared.Rent(wantLength); var packBuffer = ArrayPool<byte>.Shared.Rent(wantLength);
var bytesPacked = Deflate.Standard.Pack(packBuffer, buffer.AsSpan(0, length));
var packLength = wantLength; Stream.Write(4 + bytesPacked);
Zlib.Pack(packBuffer, ref packLength, buffer, length, ZlibQuality.Default);
Stream.Write(4 + packLength);
Stream.Write(length); Stream.Write(length);
Stream.Write(packBuffer, 0, packLength); Stream.Write(packBuffer, 0, bytesPacked);
ArrayPool<byte>.Shared.Return(packBuffer); ArrayPool<byte>.Shared.Return(packBuffer);
} }

View file

@ -0,0 +1,23 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Deflate.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.IO.Compression;
namespace Server.Compression;
public static class Deflate
{
public static LibDeflateBinding Standard { get; } = new();
}

View file

@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Server.Buffers; using Server.Buffers;
using Server.Compression;
namespace Server; namespace Server;
@ -72,8 +73,8 @@ public static class MultiData
} }
var decompressedSize = entry.Size; var decompressedSize = entry.Size;
if (Zlib.Unpack(compressionBuffer, ref decompressedSize, buffer, entry.CompressedSize) != ZlibError.Okay if (Deflate.Standard.Unpack(compressionBuffer, buffer, out var bytesDecompressed) != LibDeflateResult.Success
|| decompressedSize != entry.Size) || decompressedSize != bytesDecompressed)
{ {
throw new FileLoadException($"Error loading file {stream.Name}. Failed to unpack entry {i}."); throw new FileLoadException($"Error loading file {stream.Name}. Failed to unpack entry {i}.");
} }
@ -118,11 +119,7 @@ public static class MultiData
} }
STArrayPool<byte>.Shared.Return(buffer); STArrayPool<byte>.Shared.Return(buffer);
STArrayPool<byte>.Shared.Return(compressionBuffer);
if (compressionBuffer != null)
{
STArrayPool<byte>.Shared.Return(compressionBuffer);
}
} }
private static void LoadMul(bool postHSMulFormat) private static void LoadMul(bool postHSMulFormat)

View file

@ -16,10 +16,9 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Server.Buffers;
using Server.Collections; using Server.Collections;
using Server.Compression;
using Server.Gumps; using Server.Gumps;
using Server.Logging; using Server.Logging;
@ -49,7 +48,6 @@ public static class OutgoingGumpPackets
private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000); private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray<byte>(0x20000); private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
private static readonly OrderedSet<string> _stringsList = new(); private static readonly OrderedSet<string> _stringsList = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -63,36 +61,21 @@ public static class OutgoingGumpPackets
return; return;
} }
var wantLength = Zlib.MaxPackSize(length); var dest = writer.RawBuffer[(writer.Position + 8)..];
var packBuffer = _packBuffer;
byte[] rentedBuffer = null;
if (wantLength > packBuffer.Length) var bytesPacked = Deflate.Standard.Pack(dest, span);
if (bytesPacked == 0)
{ {
packBuffer = rentedBuffer = STArrayPool<byte>.Shared.Rent(wantLength); logger.Warning("Gump compression failed");
}
var packLength = wantLength;
var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default);
if (error != ZlibError.Okay)
{
logger.Warning("Gump compression failed: {Error}", error);
writer.Write(4); writer.Write(4);
writer.Write(0); writer.Write(0);
return; return;
} }
writer.Write(4 + packLength); writer.Write(4 + bytesPacked);
writer.Write(length); writer.Write(length);
writer.Write(packBuffer.AsSpan(0, packLength)); writer.Seek(bytesPacked, SeekOrigin.Current);
if (rentedBuffer != null)
{
STArrayPool<byte>.Shared.Return(rentedBuffer);
}
} }
public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries) public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries)
@ -141,11 +124,8 @@ public static class OutgoingGumpPackets
stringsWriter.WriteBigUni(s); stringsWriter.WriteBigUni(s);
} }
var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten);
var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten);
var maxLength = 40 + worstLayoutLength + worstStringsLength;
var writer = new SpanWriter(maxLength); var writer = new SpanWriter(0x10000);
writer.Write((byte)0xDD); // Packet ID writer.Write((byte)0xDD); // Packet ID
writer.Seek(2, SeekOrigin.Current); writer.Seek(2, SeekOrigin.Current);
@ -171,6 +151,8 @@ public static class OutgoingGumpPackets
{ {
_stringsList.Clear(); _stringsList.Clear();
} }
writer.Dispose();
} }
public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption) public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption)

View file

@ -29,13 +29,15 @@
<Delete Files="..\..\Distribution\Serilog.Sinks.Async.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Serilog.Sinks.Async.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Serilog.Sinks.Console.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Serilog.Sinks.Console.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\wepoll.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\wepoll.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\ZLib.Bindings.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\LibDeflate.Bindings.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdeflate.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdeflate.dylib" ContinueOnError="true" />
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" /> <PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
<PackageReference Include="LibDeflate.Bindings" Version="1.0.0.120" />
<PackageReference Include="PollGroup" Version="1.4.3" /> <PackageReference Include="PollGroup" Version="1.4.3" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" /> <PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="Zlib.Bindings" Version="1.11.0" />
<PackageReference Include="ModernUO.Serialization.Annotations" Version="2.9.1" /> <PackageReference Include="ModernUO.Serialization.Annotations" Version="2.9.1" />
<PackageReference Include="ModernUO.Serialization.Generator" Version="2.10.9" /> <PackageReference Include="ModernUO.Serialization.Generator" Version="2.10.9" />

View file

@ -2,6 +2,7 @@ using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Server.Compression;
namespace Server.Network namespace Server.Network
{ {
@ -209,20 +210,14 @@ namespace Server.Network
var inflatedBuffer = planeBuffers[i]; var inflatedBuffer = planeBuffers[i];
var deflatedLength = deflatedBuffer.Length; var deflatedLength = Deflate.Standard.Pack(
var ce = Zlib.Pack(
deflatedBuffer, deflatedBuffer,
ref deflatedLength, inflatedBuffer.AsSpan(0, size)
inflatedBuffer,
size,
ZlibQuality.Default
); );
if (ce != ZlibError.Okay) if (deflatedLength == 0)
{ {
Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce); Console.WriteLine("Compression error");
deflatedLength = 0;
size = 0;
} }
Write((byte)(0x20 | i)); Write((byte)(0x20 | i));
@ -247,20 +242,14 @@ namespace Server.Network
var inflatedBuffer = stairBuffers[i]; var inflatedBuffer = stairBuffers[i];
var deflatedLength = deflatedBuffer.Length; var deflatedLength = Deflate.Standard.Pack(
var ce = Zlib.Pack(
deflatedBuffer, deflatedBuffer,
ref deflatedLength, inflatedBuffer.AsSpan(0, size)
inflatedBuffer,
size,
ZlibQuality.Default
); );
if (ce != ZlibError.Okay) if (deflatedLength == 0)
{ {
Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce); Console.WriteLine("Compression error");
deflatedLength = 0;
size = 0;
} }
Write((byte)(9 + i)); Write((byte)(9 + i));

View file

@ -16,252 +16,245 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Server.Compression;
using Server.Logging; using Server.Logging;
using Server.Network; 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)); if (ns.CannotSendPackets())
public static void SendBeginHouseCustomization(this NetState ns, Serial house)
{ {
if (ns.CannotSendPackets()) return;
{
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);
} }
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;
{
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);
} }
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;
{
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);
} }
private const int planeCount = 9; var writer = new SpanWriter(stackalloc byte[13]);
private const int maxPlaneLength = 0x400; writer.Write((byte)0xBF); // Packet Id
private const int maxPerPlaneOffsetBuffer = 750; writer.Write((ushort)13);
private static readonly int maxPackedPlaneOffsetBuffer = Zlib.MaxPackSize(maxPerPlaneOffsetBuffer * 5); 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 mte = tiles[i];
var yMin = components.Min.Y; var x = mte.OffsetX - xMin;
var xMax = components.Max.X; var y = mte.OffsetY - yMin;
var yMax = components.Max.Y; int z = mte.OffsetZ;
var width = xMax - xMin + 1; var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0;
var height = yMax - yMin + 1;
var tiles = components.List;
var planeLength = width * height * 2; int plane = z switch
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 mte = tiles[i]; 0 => 0,
var x = mte.OffsetX - xMin; 7 => 1,
var y = mte.OffsetY - yMin; 27 => 2,
int z = mte.OffsetZ; 47 => 3,
var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0; 67 => 4,
_ => -1
};
int plane = z switch if (plane > -1)
{
int size;
if (plane == 0)
{ {
0 => 0, size = height;
7 => 1, }
27 => 2, else if (floor)
47 => 3,
67 => 4,
_ => -1
};
if (plane > -1)
{ {
int size; size = height - 2;
if (plane == 0) x -= 1;
{ y -= 1;
size = height; }
} else
else if (floor) {
{ size = height - 1;
size = height - 2; plane += 4;
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;
}
} }
planeOffsetWriter.Write(mte.ItemId); index = (x * size + y) * 2;
planeOffsetWriter.Write((byte)mte.OffsetX);
planeOffsetWriter.Write((byte)mte.OffsetY);
planeOffsetWriter.Write((byte)mte.OffsetZ);
totalPlaneOffsets++;
}
var maxPlanesLength = (Zlib.MaxPackSize(planeLength) + 4) * totalPlanes; if (x >= 0 && y >= 0 && y < size && index + 1 < maxPlaneLength)
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])
{ {
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; 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; planeOffsetWriter.Write(mte.ItemId);
while (totalPlaneOffsets > 0) planeOffsetWriter.Write((byte)mte.OffsetX);
{ planeOffsetWriter.Write((byte)mte.OffsetY);
var count = Math.Min(maxPerPlaneOffsetBuffer, totalPlaneOffsets); planeOffsetWriter.Write((byte)mte.OffsetZ);
totalPlaneOffsets -= count; totalPlaneOffsets++;
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;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] var writer = new SpanWriter(0x10000);
private static void WritePacked(ReadOnlySpan<byte> source, ref SpanWriter writer, out int length) 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; if (!planesUsed[i])
var dest = writer.RawBuffer[(writer.Position + 3)..];
length = dest.Length;
var ce = Zlib.Pack(dest, ref length, source, ZlibQuality.Default);
if (ce != ZlibError.Okay)
{ {
logger.Warning("ZLib error: {Error} (#{ErrorCode})", ce, (int)ce); continue;
length = 0;
size = 0;
} }
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)size);
writer.Write((byte)length); WritePacked(source, ref writer, out int destLength);
writer.Write((byte)(((size >> 4) & 0xF0) | ((length >> 8) & 0xF)));
writer.Seek(length, SeekOrigin.Current); 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);
} }
} }

View file

@ -24,7 +24,9 @@
<Delete Files="..\..\Distribution\Assemblies\ref\$(AssemblyName).dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Assemblies\ref\$(AssemblyName).dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Assemblies\$(AssemblyName).deps.json" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Assemblies\$(AssemblyName).deps.json" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Assemblies\$(AssemblyName).pdb" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Assemblies\$(AssemblyName).pdb" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Assemblies\ZLib.Bindings.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\LibDeflate.Bindings.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdeflate.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdeflate.dylib" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Assemblies\Zstd.Binaries.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Assemblies\Zstd.Binaries.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Assemblies\ModernUO.Serialization.Annotations.dll" ContinueOnError="true" /> <Delete Files="..\..\Distribution\Assemblies\ModernUO.Serialization.Annotations.dll" ContinueOnError="true" />
</Target> </Target>
@ -32,10 +34,10 @@
<ProjectReference Include="..\Server\Server.csproj" Private="false" PrivateAssets="All" IncludeAssets="None"> <ProjectReference Include="..\Server\Server.csproj" Private="false" PrivateAssets="All" IncludeAssets="None">
<IncludeInPackage>false</IncludeInPackage> <IncludeInPackage>false</IncludeInPackage>
</ProjectReference> </ProjectReference>
<PackageReference Include="LibDeflate.Bindings" Version="1.0.0.120" />
<PackageReference Include="MailKit" Version="4.5.0" /> <PackageReference Include="MailKit" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="8.0.0" />
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" /> <PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
<PackageReference Include="Zlib.Bindings" Version="1.11.0" />
<PackageReference Include="Argon2.Bindings" Version="1.16.1" /> <PackageReference Include="Argon2.Bindings" Version="1.16.1" />
<PackageReference Include="Zstd.Binaries" Version="1.6.0" /> <PackageReference Include="Zstd.Binaries" Version="1.6.0" />

View file

@ -62,7 +62,7 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc
- Run `./publish.cmd [release|debug (default: release)] [os] [arch (default: x64)]` - Run `./publish.cmd [release|debug (default: release)] [os] [arch (default: x64)]`
- `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/7.0/supported-os.md) - `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/7.0/supported-os.md)
- `win` - Windows 10/11/2019/2022 - `win` - Windows 10/11/2019/2022
- `osx` - MacOS 11/12/13 (Big Sur, Monterey, Ventura) - `osx` - MacOS 12/13/14 (Sonoma, Big Sur, Monterey)
- `linux` - Linux - `linux` - Linux
- `arch` - `arch`
- `x64` - Intel 64-bit - `x64` - Intel 64-bit
@ -74,13 +74,18 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc
dnf upgrade --refresh -y dnf upgrade --refresh -y
# CentOS does not come with EPEL enabled # CentOS does not come with EPEL enabled
dnf install -y epel-release epel-next-release dnf install -y epel-release epel-next-release
dnf install -y findutils libicu zlib-devel zstd libargon2-devel tzdata dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel
``` ```
### Ubuntu, Debian, etc ### Ubuntu, Debian, etc
```shell ```shell
apt-get update -y apt-get update -y
apt-get install -y libicu-dev libz-dev zstd libargon2-dev tzdata apt-get install -y libicu-dev libdeflate-dev zstd libargon2-dev
```
## OSX Requirements
```shell
brew install icu4c libdeflate zstd argon2
``` ```
## Running the Server ## Running the Server

View file

@ -1,4 +1,4 @@
{ {
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "0.13.2" "version": "0.13.3"
} }