fix(houses): Converts house packets (1 of 2) (#502)
- [X] Adds `ToSpan()` for SpanWriter.
- [X] Moves house files to UOContent/Multis/Houses
- [X] Adds house packets
### SpanWriter.ToSpan()
`ToSpan()` returns an `SpanOwner` struct which allows access to the buffer as a Span by calling `Span`.
Example:
```cs
public SpanOwner CreatePacket()
{
var writer = new SpanWriter(0x400);
// write some stuff
return writer.ToSpan();
}
```
In this example, we are creating a packet and returning the `SpanOwner` for caching. This means in the future we can do something like this:
```cs
ns.Send(_cachedPacket.Span);
```
Where `_cachedPacket` is the `SpanOwner`
Notes:
Do not use a SpanWriter, or attempt to dispose of it after calling `ToSpan()`. Doing so will probably cause an NPE.
This commit is contained in:
parent
952a3e0265
commit
d070efeab3
14 changed files with 267 additions and 73 deletions
|
|
@ -20,6 +20,7 @@ using System.IO;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Microsoft.Toolkit.HighPerformance.Extensions;
|
||||
using Server.Network;
|
||||
using Server.Text;
|
||||
|
||||
|
|
@ -54,6 +55,45 @@ namespace System.Buffers
|
|||
|
||||
public Span<byte> RawBuffer => _buffer;
|
||||
|
||||
/**
|
||||
* Converts the writer to a Span<byte> using a SpanOwner.
|
||||
* If the buffer was stackalloc, it will be copied to a rented buffer.
|
||||
* Otherwise the existing rented buffer is used.
|
||||
*
|
||||
* Note:
|
||||
* Do not use the SpanWriter after calling this method.
|
||||
* This method will effectively dispose of the SpanWriter and is therefore considered terminal.
|
||||
*/
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SpanOwner ToSpan()
|
||||
{
|
||||
var toReturn = _arrayToReturnToPool;
|
||||
|
||||
SpanOwner apo;
|
||||
if (_position == 0)
|
||||
{
|
||||
apo = new SpanOwner(_position, Array.Empty<byte>());
|
||||
if (toReturn != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
else if (toReturn != null)
|
||||
{
|
||||
apo = new SpanOwner(_position, toReturn);
|
||||
}
|
||||
else
|
||||
{
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(_position);
|
||||
_buffer.CopyTo(buffer);
|
||||
apo = new SpanOwner(_position, buffer);
|
||||
}
|
||||
|
||||
this = default; // Don't allow two references to the same buffer
|
||||
return apo;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SpanWriter(Span<byte> initialBuffer, bool resize = false)
|
||||
{
|
||||
_resize = resize;
|
||||
|
|
@ -63,6 +103,7 @@ namespace System.Buffers
|
|||
_arrayToReturnToPool = null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SpanWriter(int initialCapacity, bool resize = false)
|
||||
{
|
||||
_resize = resize;
|
||||
|
|
@ -218,6 +259,7 @@ namespace System.Buffers
|
|||
Position += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var count = buffer.Length;
|
||||
|
|
@ -331,6 +373,7 @@ namespace System.Buffers
|
|||
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.End || offset >= -_buffer.Length,
|
||||
|
||||
"Attempting to seek to a negative position using SeekOrigin.End"
|
||||
);
|
||||
|
||||
|
|
@ -379,5 +422,35 @@ namespace System.Buffers
|
|||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
|
||||
public struct SpanOwner : IDisposable
|
||||
{
|
||||
private readonly int _length;
|
||||
private readonly byte[] _arrayToReturnToPool;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal SpanOwner(int length, byte[] buffer)
|
||||
{
|
||||
_length = length;
|
||||
_arrayToReturnToPool = buffer;
|
||||
}
|
||||
|
||||
public Span<byte> Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => MemoryMarshal.CreateSpan(ref _arrayToReturnToPool.DangerousGetReference(), _length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
byte[] toReturn = _arrayToReturnToPool;
|
||||
this = default;
|
||||
if (_length > 0)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using Server.Multis;
|
||||
using Server.Network;
|
||||
using Server.Tests;
|
||||
using Server.Tests.Network;
|
||||
using Xunit;
|
||||
|
||||
namespace UOContent.Tests
|
||||
{
|
||||
public class HousePacketTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0x1001u)]
|
||||
public void TestBeginHouseCustomization(uint serial)
|
||||
{
|
||||
var expected = new BeginHouseCustomization(serial).Compile();
|
||||
|
||||
var ns = PacketTestUtilities.CreateTestNetState();
|
||||
ns.SendBeginHouseCustomization(serial);
|
||||
|
||||
var result = ns.SendPipe.Reader.TryRead();
|
||||
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x1001u)]
|
||||
public void TestEndHouseCustomization(uint serial)
|
||||
{
|
||||
var expected = new EndHouseCustomization(serial).Compile();
|
||||
|
||||
var ns = PacketTestUtilities.CreateTestNetState();
|
||||
ns.SendEndHouseCustomization(serial);
|
||||
|
||||
var result = ns.SendPipe.Reader.TryRead();
|
||||
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x1001u, 0)]
|
||||
[InlineData(0x1001u, 100)]
|
||||
public void TestDesignStateGeneral(uint serial, int revision)
|
||||
{
|
||||
var expected = new DesignStateGeneral(serial, revision).Compile();
|
||||
|
||||
var ns = PacketTestUtilities.CreateTestNetState();
|
||||
ns.SendDesignStateGeneral(serial, revision);
|
||||
|
||||
var result = ns.SendPipe.Reader.TryRead();
|
||||
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs
Normal file
46
Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
namespace Server.Network
|
||||
{
|
||||
public class BeginHouseCustomization : Packet
|
||||
{
|
||||
public BeginHouseCustomization(Serial house) : base(0xBF)
|
||||
{
|
||||
EnsureCapacity(17);
|
||||
|
||||
Stream.Write((short)0x20);
|
||||
Stream.Write(house);
|
||||
Stream.Write((byte)0x04);
|
||||
Stream.Write((ushort)0x0000);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((byte)0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
public class EndHouseCustomization : Packet
|
||||
{
|
||||
public EndHouseCustomization(Serial house) : base(0xBF)
|
||||
{
|
||||
EnsureCapacity(17);
|
||||
|
||||
Stream.Write((short)0x20);
|
||||
Stream.Write(house);
|
||||
Stream.Write((byte)0x05);
|
||||
Stream.Write((ushort)0x0000);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((byte)0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DesignStateGeneral : Packet
|
||||
{
|
||||
public DesignStateGeneral(Serial house, int revision) : base(0xBF)
|
||||
{
|
||||
EnsureCapacity(13);
|
||||
|
||||
Stream.Write((short)0x1D);
|
||||
Stream.Write(house);
|
||||
Stream.Write(revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -869,7 +869,7 @@ namespace Server.Multis
|
|||
}
|
||||
|
||||
DesignContext.Add(m, this);
|
||||
m.Send(new BeginHouseCustomization(this));
|
||||
m.NetState.SendBeginHouseCustomization(Serial);
|
||||
|
||||
var ns = m.NetState;
|
||||
if (ns != null)
|
||||
|
|
@ -1237,7 +1237,7 @@ namespace Server.Multis
|
|||
DesignContext.Remove(from);
|
||||
|
||||
// Notify the client that customization has ended
|
||||
from.Send(new EndHouseCustomization(this));
|
||||
from.NetState.SendEndHouseCustomization(Serial);
|
||||
|
||||
// Notify the core that the foundation has changed and should be resent to all clients
|
||||
Delta(ItemDelta.Update);
|
||||
|
|
@ -1278,7 +1278,7 @@ namespace Server.Multis
|
|||
context.Foundation.Fixtures.Count)) * 500;
|
||||
var bankBalance = Banker.GetBalance(from);
|
||||
|
||||
from.SendGump(new ConfirmCommitGump(from, context.Foundation, bankBalance, oldPrice, newPrice));
|
||||
from.SendGump(new ConfirmCommitGump(context.Foundation, bankBalance, oldPrice, newPrice));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1704,7 +1704,7 @@ namespace Server.Multis
|
|||
DesignContext.Remove(from);
|
||||
|
||||
// Notify the client that customization has ended
|
||||
from.Send(new EndHouseCustomization(context.Foundation));
|
||||
from.NetState.SendEndHouseCustomization(context.Foundation.Serial);
|
||||
|
||||
// Refresh client with current visible design state
|
||||
context.Foundation.SendInfoTo(state);
|
||||
|
|
@ -1981,7 +1981,7 @@ namespace Server.Multis
|
|||
|
||||
public void SendGeneralInfoTo(NetState state)
|
||||
{
|
||||
state.Send(new DesignStateGeneral(Foundation, this));
|
||||
state.SendDesignStateGeneral(Foundation.Serial, Revision);
|
||||
}
|
||||
|
||||
public void SendDetailedInfoTo(NetState state)
|
||||
|
|
@ -2194,8 +2194,7 @@ namespace Server.Multis
|
|||
{
|
||||
private readonly HouseFoundation m_Foundation;
|
||||
|
||||
public ConfirmCommitGump(Mobile from, HouseFoundation foundation, int bankBalance, int oldPrice, int newPrice)
|
||||
: base(50, 50)
|
||||
public ConfirmCommitGump(HouseFoundation foundation, int bankBalance, int oldPrice, int newPrice) : base(50, 50)
|
||||
{
|
||||
m_Foundation = foundation;
|
||||
|
||||
|
|
@ -2383,53 +2382,6 @@ namespace Server.Multis
|
|||
}
|
||||
}
|
||||
|
||||
public class BeginHouseCustomization : Packet
|
||||
{
|
||||
public BeginHouseCustomization(HouseFoundation house)
|
||||
: base(0xBF)
|
||||
{
|
||||
EnsureCapacity(17);
|
||||
|
||||
Stream.Write((short)0x20);
|
||||
Stream.Write(house.Serial);
|
||||
Stream.Write((byte)0x04);
|
||||
Stream.Write((ushort)0x0000);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((byte)0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
public class EndHouseCustomization : Packet
|
||||
{
|
||||
public EndHouseCustomization(HouseFoundation house)
|
||||
: base(0xBF)
|
||||
{
|
||||
EnsureCapacity(17);
|
||||
|
||||
Stream.Write((short)0x20);
|
||||
Stream.Write(house.Serial);
|
||||
Stream.Write((byte)0x05);
|
||||
Stream.Write((ushort)0x0000);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((ushort)0xFFFF);
|
||||
Stream.Write((byte)0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DesignStateGeneral : Packet
|
||||
{
|
||||
public DesignStateGeneral(HouseFoundation house, DesignState state)
|
||||
: base(0xBF)
|
||||
{
|
||||
EnsureCapacity(13);
|
||||
|
||||
Stream.Write((short)0x1D);
|
||||
Stream.Write(house.Serial);
|
||||
Stream.Write(state.Revision);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DesignStateDetailed : Packet
|
||||
{
|
||||
public const int MaxItemsPerStairBuffer = 750;
|
||||
|
|
@ -2581,20 +2533,12 @@ namespace Server.Multis
|
|||
|
||||
++planeCount;
|
||||
|
||||
int size;
|
||||
|
||||
if (i == 0)
|
||||
int size = i switch
|
||||
{
|
||||
size = width * height * 2;
|
||||
}
|
||||
else if (i < 5)
|
||||
{
|
||||
size = (width - 1) * (height - 2) * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = width * (height - 1) * 2;
|
||||
}
|
||||
0 => width * height * 2,
|
||||
< 5 => (width - 1) * (height - 2) * 2,
|
||||
_ => width * (height - 1) * 2
|
||||
};
|
||||
|
||||
var inflatedBuffer = planeBuffers[i];
|
||||
|
||||
|
|
@ -2630,12 +2574,7 @@ namespace Server.Multis
|
|||
{
|
||||
++planeCount;
|
||||
|
||||
var count = totalStairsUsed - i * MaxItemsPerStairBuffer;
|
||||
|
||||
if (count > MaxItemsPerStairBuffer)
|
||||
{
|
||||
count = MaxItemsPerStairBuffer;
|
||||
}
|
||||
var count = Math.Min(MaxItemsPerStairBuffer, totalStairsUsed - i * MaxItemsPerStairBuffer);
|
||||
|
||||
var size = count * 5;
|
||||
|
||||
84
Projects/UOContent/Multis/Houses/HousePackets.cs
Normal file
84
Projects/UOContent/Multis/Houses/HousePackets.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: HousePackets.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.Buffers;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue