Fix bad serials (#304)

- [X] Fixes bad serial calculations
- [X] Tightens HexStrings

Bumps release version
Updates documentation
This commit is contained in:
Kamron Batman 2020-11-08 12:09:26 -08:00 • committed by GitHub
parent b0c1076cb5
commit ae436cb55f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 76 additions and 51 deletions

View file

@ -0,0 +1,240 @@
using System;
using System.Net;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class AccountPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestChangeCharacter()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.RawName = "Test Mobile";
var secondMobile = new Mobile(0x2);
secondMobile.DefaultMobileInit();
secondMobile.RawName = null;
var account = new MockAccount(new[] { firstMobile, null, secondMobile });
var expected = new ChangeCharacter(account).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendChangeCharacter(account);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestClientVersionReq()
{
var expected = new ClientVersionReq().Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendClientVersionRequest();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestDeleteResult()
{
var expected = new DeleteResult(DeleteResultType.BadRequest).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendCharacterDeleteResult(DeleteResultType.BadRequest);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestPopupMessage()
{
var expected = new PopupMessage(PMMessage.LoginSyncError).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendPopupMessage(PMMessage.LoginSyncError);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(ProtocolChanges.Version70610)]
[InlineData(ProtocolChanges.Version6000)]
public void TestSupportedFeatures(ProtocolChanges protocolChanges)
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var account = new MockAccount(new[] { firstMobile, null, null, null, null });
using var ns = PacketTestUtilities.CreateTestNetState();
ns.Account = account;
ns.ProtocolChanges = protocolChanges;
var expected = new SupportedFeatures(ns).Compile();
ns.SendSupportedFeature();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestLoginConfirm()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
m.Body = 0x100;
m.X = 100;
m.Y = 10;
m.Z = -10;
m.Direction = Direction.Down;
m.LogoutMap = Map.Felucca;
var expected = new LoginConfirm(m).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendLoginConfirmation(m);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestLoginComplete()
{
var expected = new LoginComplete().Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendLoginComplete();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestCharacterListUpdate()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.RawName = "Test Mobile";
var acct = new MockAccount(new[] { null, firstMobile, null, null, null });
var expected = new CharacterListUpdate(acct).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendCharacterListUpdate(acct);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestCharacterList70130()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var acct = new MockAccount(new[] { null, firstMobile, null, null, null });
var info = new[]
{
new CityInfo("Test City", "Test Building", 50, 100, 10, -10)
};
var expected = new CharacterList(acct, info).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.CityInfo = info;
ns.Account = acct;
ns.ProtocolChanges = ProtocolChanges.Version70130;
ns.SendCharacterList();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestCharacterListOld()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var acct = new MockAccount(new[] { null, firstMobile, null, null, null });
var info = new[]
{
new CityInfo("Test City", "Test Building", 50, 100, 10, -10)
};
var expected = new CharacterListOld(acct, info).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.CityInfo = info;
ns.Account = acct;
ns.SendCharacterList();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestAccountLoginRej()
{
var reason = ALRReason.BadComm;
var expected = new AccountLoginRej(reason).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendAccountLoginRejected(reason);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestAccountLoginAck()
{
var info = new[]
{
new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1"))
};
var expected = new AccountLoginAck(info).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.ServerInfo = info;
ns.SendAccountLoginAck();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestPlayServerAck()
{
var si = new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1"));
var authId = 0x123456;
var expected = new PlayServerAck(si, authId).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendPlayServerAck(si, authId);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
}
}

View file

@ -0,0 +1,361 @@
using System;
using Server.Accounting;
using Server.Network;
namespace Server.Tests.Network
{
public sealed class ChangeCharacter : Packet
{
public ChangeCharacter(IAccount a) : base(0x81)
{
EnsureCapacity(305);
var count = 0;
for (var i = 0; i < a.Length; ++i)
{
if (a[i] != null)
{
++count;
}
}
Stream.Write((byte)count);
Stream.Write((byte)0);
for (var i = 0; i < a.Length; ++i)
{
var m = a[i];
if (a[i] != null)
{
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
Stream.WriteAsciiFixed(name, 30);
Stream.Fill(30); // password
}
else
{
Stream.Fill(60);
}
}
}
}
public sealed class ClientVersionReq : Packet
{
public ClientVersionReq() : base(0xBD)
{
EnsureCapacity(3);
}
}
public sealed class DeleteResult : Packet
{
public DeleteResult(DeleteResultType res) : base(0x85, 2)
{
Stream.Write((byte)res);
}
}
public sealed class PopupMessage : Packet
{
public PopupMessage(PMMessage msg) : base(0x53, 2)
{
Stream.Write((byte)msg);
}
}
public sealed class SupportedFeatures : Packet
{
public SupportedFeatures(NetState ns) : base(0xB9, ns.ExtendedSupportedFeatures ? 5 : 3)
{
var flags = ExpansionInfo.CoreExpansion.SupportedFeatures;
if (ns.Account.Limit >= 6)
{
flags |= FeatureFlags.LiveAccount;
flags &= ~FeatureFlags.UOTD;
if (ns.Account.Limit > 6)
{
flags |= FeatureFlags.SeventhCharacterSlot;
}
else
{
flags |= FeatureFlags.SixthCharacterSlot;
}
}
if (ns.ExtendedSupportedFeatures)
{
Stream.Write((uint)flags);
}
else
{
Stream.Write((ushort)flags);
}
}
}
public sealed class LoginConfirm : Packet
{
public LoginConfirm(Mobile m) : base(0x1B, 37)
{
Stream.Write(m.Serial);
Stream.Write(0);
Stream.Write((short)m.Body);
Stream.Write((short)m.X);
Stream.Write((short)m.Y);
Stream.Write((short)m.Z);
Stream.Write((byte)m.Direction);
Stream.Write((byte)0);
Stream.Write(-1);
var map = m.Map;
if (map == null || map == Map.Internal)
{
map = m.LogoutMap;
}
Stream.Write((short)0);
Stream.Write((short)0);
Stream.Write((short)(map?.Width ?? Map.Felucca.Width));
Stream.Write((short)(map?.Height ?? Map.Felucca.Height));
Stream.Fill();
}
}
public sealed class LoginComplete : Packet
{
public LoginComplete() : base(0x55, 1)
{
}
}
public sealed class CharacterListUpdate : Packet
{
public CharacterListUpdate(IAccount a) : base(0x86)
{
EnsureCapacity(4 + a.Length * 60);
var highSlot = -1;
for (var i = 0; i < a.Length; ++i)
{
if (a[i] != null)
{
highSlot = i;
}
}
var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
Stream.Write((byte)count);
for (var i = 0; i < count; ++i)
{
var m = a[i];
if (m != null)
{
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
Stream.WriteAsciiFixed(name, 30);
Stream.Fill(30); // password
}
else
{
Stream.Fill(60);
}
}
}
}
public sealed class CharacterList : Packet
{
public CharacterList(IAccount a, CityInfo[] info) : base(0xA9)
{
EnsureCapacity(11 + a.Length * 60 + info.Length * 89);
var highSlot = -1;
for (var i = 0; i < a.Length; ++i)
{
if (a[i] != null)
{
highSlot = i;
}
}
var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
Stream.Write((byte)count);
for (var i = 0; i < count; ++i)
{
if (a[i] != null)
{
Stream.WriteAsciiFixed(a[i].Name, 30);
Stream.Fill(30); // password
}
else
{
Stream.Fill(60);
}
}
Stream.Write((byte)info.Length);
for (var i = 0; i < info.Length; ++i)
{
var ci = info[i];
Stream.Write((byte)i);
Stream.WriteAsciiFixed(ci.City, 32);
Stream.WriteAsciiFixed(ci.Building, 32);
Stream.Write(ci.X);
Stream.Write(ci.Y);
Stream.Write(ci.Z);
Stream.Write(ci.Map.MapID);
Stream.Write(ci.Description);
Stream.Write(0);
}
var flags = ExpansionInfo.CoreExpansion.CharacterListFlags;
if (count > 6)
{
flags |= CharacterListFlags.SeventhCharacterSlot |
CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required?
}
else if (count == 6)
{
flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot
}
else if (a.Limit == 1)
{
flags |= CharacterListFlags.SlotLimit &
CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character
}
Stream.Write((int)flags); // Additional Flags
Stream.Write((short)-1);
}
}
public sealed class CharacterListOld : Packet
{
public CharacterListOld(IAccount a, CityInfo[] info) : base(0xA9)
{
EnsureCapacity(9 + a.Length * 60 + info.Length * 63);
var highSlot = -1;
for (var i = 0; i < a.Length; ++i)
{
if (a[i] != null)
{
highSlot = i;
}
}
var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
Stream.Write((byte)count);
for (var i = 0; i < count; ++i)
{
var m = a[i];
if (m != null)
{
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
Stream.WriteAsciiFixed(name, 30);
Stream.Fill(30); // password
}
else
{
Stream.Fill(60);
}
}
Stream.Write((byte)info.Length);
for (var i = 0; i < info.Length; ++i)
{
var ci = info[i];
Stream.Write((byte)i);
Stream.WriteAsciiFixed(ci.City, 31);
Stream.WriteAsciiFixed(ci.Building, 31);
}
var flags = ExpansionInfo.CoreExpansion.CharacterListFlags;
if (count > 6)
{
flags |= CharacterListFlags.SeventhCharacterSlot |
CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required?
}
else if (count == 6)
{
flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot
}
else if (a.Limit == 1)
{
flags |= CharacterListFlags.SlotLimit &
CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character
}
Stream.Write((int)flags); // Additional Flags
}
}
public sealed class AccountLoginRej : Packet
{
public AccountLoginRej(ALRReason reason) : base(0x82, 2)
{
Stream.Write((byte)reason);
}
}
public sealed class AccountLoginAck : Packet
{
public AccountLoginAck(ServerInfo[] info) : base(0xA8)
{
EnsureCapacity(6 + info.Length * 40);
Stream.Write((byte)0x5D); // Unknown
Stream.Write((ushort)info.Length);
for (var i = 0; i < info.Length; ++i)
{
var si = info[i];
Stream.Write((ushort)i);
Stream.WriteAsciiFixed(si.Name, 32);
Stream.Write((byte)si.FullPercent);
Stream.Write((sbyte)si.TimeZone);
Stream.Write(Utility.GetAddressValue(si.Address.Address));
}
}
}
public sealed class PlayServerAck : Packet
{
public PlayServerAck(ServerInfo si, int authId) : base(0x8C, 11)
{
var addr = Utility.GetAddressValue(si.Address.Address);
Stream.Write((byte)addr);
Stream.Write((byte)(addr >> 8));
Stream.Write((byte)(addr >> 16));
Stream.Write((byte)(addr >> 24));
Stream.Write((short)si.Address.Port);
Stream.Write(authId);
}
}
}

View file

@ -0,0 +1,93 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
[Collection("Sequential Tests")]
public class AttributeNormalizerTests
{
[Fact]
public void TestAttributeNormalizerEnabled()
{
AttributeNormalizer.Enabled = true;
AttributeNormalizer.Maximum = 25;
var stream = new PacketWriter(4);
const ushort cur = 50;
const ushort max = 100;
AttributeNormalizer.Write(stream, cur, max);
Span<byte> expectedData = stackalloc byte[4];
var pos = 0;
expectedData.Write(ref pos, (ushort)AttributeNormalizer.Maximum);
expectedData.Write(ref pos, (ushort)(cur * 25 / max));
AssertThat.Equal(stream.ToArray(), expectedData);
}
[Fact]
public void TestAttributeNormalizerReversedEnabled()
{
AttributeNormalizer.Enabled = true;
AttributeNormalizer.Maximum = 25;
var stream = new PacketWriter(4);
const ushort cur = 50;
const ushort max = 100;
AttributeNormalizer.WriteReverse(stream, cur, max);
Span<byte> expectedData = stackalloc byte[4];
var pos = 0;
expectedData.Write(ref pos, (ushort)(cur * 25 / max));
expectedData.Write(ref pos, (ushort)AttributeNormalizer.Maximum);
AssertThat.Equal(stream.ToArray(), expectedData);
}
[Fact]
public void TestAttributeNormalizerDisabled()
{
AttributeNormalizer.Enabled = false;
var stream = new PacketWriter(4);
const ushort cur = 50;
const ushort max = 100;
AttributeNormalizer.Write(stream, cur, max);
Span<byte> expectedData = stackalloc byte[4];
var pos = 0;
expectedData.Write(ref pos, max);
expectedData.Write(ref pos, cur);
AssertThat.Equal(stream.ToArray(), expectedData);
}
[Fact]
public void TestAttributeNormalizerReversedDisabled()
{
AttributeNormalizer.Enabled = false;
var stream = new PacketWriter(4);
const ushort cur = 50;
const ushort max = 100;
AttributeNormalizer.WriteReverse(stream, cur, max);
Span<byte> expectedData = stackalloc byte[4];
var pos = 0;
expectedData.Write(ref pos, cur);
expectedData.Write(ref pos, max);
AssertThat.Equal(stream.ToArray(), expectedData);
}
}
}

View file

@ -0,0 +1,52 @@
using System;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class CombatPacketTests
{
[Fact]
public void TestSwing()
{
Serial attacker = 0x1000;
Serial defender = 0x2000;
var expected = new Swing(attacker, defender).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendSwing(attacker, defender);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void TestSetWarMode(bool warmode)
{
var expected = new SetWarMode(warmode).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendSetWarMode(warmode);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestChangeCombatant()
{
Serial serial = 0x1024;
var expected = new ChangeCombatant(serial).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendChangeCombatant(serial);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Network
{
public sealed class Swing : Packet
{
public Swing(Serial attacker, Serial defender) : base(0x2F, 10)
{
Stream.Write((byte)0);
Stream.Write(attacker);
Stream.Write(defender);
}
}
public sealed class SetWarMode : Packet
{
public SetWarMode(bool mode) : base(0x72, 5)
{
Stream.Write(mode);
Stream.Write((byte)0x00);
Stream.Write((byte)0x32);
Stream.Write((byte)0x00);
}
}
public sealed class ChangeCombatant : Packet
{
public ChangeCombatant(Serial combatant) : base(0xAA, 5)
{
Stream.Write(combatant);
}
}
}

View file

@ -0,0 +1,46 @@
using System;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class DamagePacketTests : IClassFixture<ServerFixture>
{
[Theory]
[InlineData(10)]
[InlineData(-5)]
[InlineData(1024)]
public void TestDamagePacketOld(int inputAmount)
{
Serial serial = 0x1024;
var expected = new DamagePacketOld(serial, inputAmount).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendDamage(serial, inputAmount);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Theory]
[InlineData(10)]
[InlineData(-5)]
[InlineData(1024)]
[InlineData(100000)]
public void TestDamage(int inputAmount)
{
Serial serial = 0x1024;
var expected = new DamagePacket(serial, inputAmount).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.ProtocolChanges = ProtocolChanges.DamagePacket;
ns.SendDamage(serial, inputAmount);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
}
}

View file

@ -0,0 +1,28 @@
using System;
namespace Server.Network
{
public sealed class DamagePacketOld : Packet
{
public DamagePacketOld(Serial mobile, int amount) : base(0xBF)
{
EnsureCapacity(11);
Stream.Write((short)0x22);
Stream.Write((byte)1);
Stream.Write(mobile);
Stream.Write((byte)Math.Clamp(amount, 0, 255));
}
}
public sealed class DamagePacket : Packet
{
public DamagePacket(Serial mobile, int amount) : base(0x0B, 7)
{
Stream.Write(mobile);
Stream.Write((ushort)Math.Clamp(amount, 0, 0xFFFF));
}
}
}

View file

@ -0,0 +1,34 @@
using System;
using System.Buffers;
using Server.HuePickers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class DisplayHuePickerTests
{
[Fact]
public void TestDisplayHuePicker()
{
const ushort itemID = 0xFF01;
var huePicker = new HuePicker(itemID);
var data = new DisplayHuePicker(huePicker).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0x95);
expectedData.Write(ref pos, huePicker.Serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
#else
pos += 2;
#endif
expectedData.Write(ref pos, itemID);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,203 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class EffectPackets
{
[Fact]
public void TestParticleEffect()
{
var effectType = EffectType.Moving;
Serial serial = 0x4000;
Serial from = 0x1000;
Serial to = 0x2000;
var itemId = 0x100;
var fromPoint = new Point3D(1000, 100, -10);
var toPoint = new Point3D(1500, 500, 0);
byte speed = 3;
byte duration = 2;
var direction = false;
var explode = false;
var hue = 0x1024;
var renderMode = 1;
ushort effect = 3;
ushort explodeEffect = 0;
ushort explodeSound = 0;
byte layer = 9;
ushort unknown = 0;
var data = new ParticleEffect(
effectType,
from,
to,
itemId,
fromPoint,
toPoint,
speed,
duration,
direction,
explode,
hue,
renderMode,
effect,
explodeEffect,
explodeSound,
serial,
layer,
unknown
).Compile();
Span<byte> expectedData = stackalloc byte[49];
var pos = 0;
expectedData.Write(ref pos, (byte)0xC7); // Packet ID
expectedData.Write(ref pos, (byte)effectType);
expectedData.Write(ref pos, from);
expectedData.Write(ref pos, to);
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, fromPoint);
expectedData.Write(ref pos, toPoint);
expectedData.Write(ref pos, speed);
expectedData.Write(ref pos, duration);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
#else
pos += 2;
#endif
expectedData.Write(ref pos, direction);
expectedData.Write(ref pos, explode);
expectedData.Write(ref pos, hue);
expectedData.Write(ref pos, renderMode);
expectedData.Write(ref pos, effect);
expectedData.Write(ref pos, explodeEffect);
expectedData.Write(ref pos, explodeSound);
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, unknown);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestHuedEffect()
{
var effectType = EffectType.Moving;
Serial from = 0x1000;
Serial to = 0x2000;
var itemId = 0x100;
var fromPoint = new Point3D(1000, 100, -10);
var toPoint = new Point3D(1500, 500, 0);
byte speed = 3;
byte duration = 2;
var direction = false;
var explode = false;
var hue = 0x1024;
var renderMode = 1;
var data = new HuedEffect(
effectType,
from,
to,
itemId,
fromPoint,
toPoint,
speed,
duration,
direction,
explode,
hue,
renderMode
).Compile();
Span<byte> expectedData = stackalloc byte[36];
var pos = 0;
expectedData.Write(ref pos, (byte)0xC0); // Packet ID
expectedData.Write(ref pos, (byte)effectType);
expectedData.Write(ref pos, from);
expectedData.Write(ref pos, to);
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, fromPoint);
expectedData.Write(ref pos, toPoint);
expectedData.Write(ref pos, speed);
expectedData.Write(ref pos, duration);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
#else
pos += 2;
#endif
expectedData.Write(ref pos, direction);
expectedData.Write(ref pos, explode);
expectedData.Write(ref pos, hue);
expectedData.Write(ref pos, renderMode);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestScreenEffect()
{
var type = ScreenEffectType.FadeOut;
var data = new ScreenEffect(type).Compile();
Span<byte> expectedData = stackalloc byte[28];
var pos = 0;
expectedData.Write(ref pos, (byte)0x70); // Packet ID
expectedData.Write(ref pos, (byte)0x04); // Effect
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, 0);
#else
pos += 8;
#endif
expectedData.Write(ref pos, (ushort)type); // Screen Effect Type
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, 0);
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestBoltEffect()
{
IEntity entity = new Entity(0x1000, new Point3D(1000, 100, -10), Map.Felucca);
var hue = 0x1024;
var data = new BoltEffect(entity, hue).Compile();
Span<byte> expectedData = stackalloc byte[36];
var pos = 0;
expectedData.Write(ref pos, (byte)0xC0); // Packet ID
expectedData.Write(ref pos, (byte)0x01); // Effect
expectedData.Write(ref pos, entity.Serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (ushort)0);
#else
pos += 6;
#endif
expectedData.Write(ref pos, entity.Location);
expectedData.Write(ref pos, entity.Location);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (ushort)0);
#else
pos += 6;
#endif
expectedData.Write(ref pos, hue);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,103 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class EquipmentPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestDisplayEquipmentInfo()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var item = new Item(World.NewItem);
var info = new EquipmentInfo(
500000,
m,
false,
new[]
{
new EquipInfoAttribute(500001, 1),
new EquipInfoAttribute(500002, 2),
new EquipInfoAttribute(500002, 3)
}
);
var data = new DisplayEquipmentInfo(item, info).Compile();
var attrs = info.Attributes;
var length = 17 + (info.Unidentified ? 4 : 0) + attrs.Length * 6;
if (info.Crafter != null)
{
length += 6 + (info.Crafter.Name?.Length ?? 0);
}
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (ushort)0x10); // Subcommand
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, info.Number);
if (info.Crafter != null)
{
var name = info.Crafter.Name ?? "";
expectedData.Write(ref pos, -3);
expectedData.Write(ref pos, (ushort)name.Length);
expectedData.WriteAscii(ref pos, name);
}
if (info.Unidentified)
{
expectedData.Write(ref pos, -4);
}
for (var i = 0; i < attrs.Length; i++)
{
var attr = attrs[i];
expectedData.Write(ref pos, attr.Number);
expectedData.Write(ref pos, (ushort)attr.Charges);
}
expectedData.Write(ref pos, -1);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestEquipUpdate()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var item = new Item(World.NewItem) { Parent = m };
var data = new EquipUpdate(item).Compile();
Span<byte> expectedData = stackalloc byte[15];
var pos = 0;
expectedData.Write(ref pos, (byte)0x2E); // Packet ID
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)item.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#else
pos++;
#endif
expectedData.Write(ref pos, (byte)item.Layer);
expectedData.Write(ref pos, item.Parent.Serial);
expectedData.Write(ref pos, (ushort)item.Hue);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,249 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using Server.Gumps;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class GumpPacketTests
{
[Fact]
public void TestCloseGump()
{
var typeId = 100;
var buttonId = 10;
var data = new CloseGump(typeId, buttonId).Compile();
Span<byte> expectedData = stackalloc byte[13];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)0xD); // Length
expectedData.Write(ref pos, (ushort)0x4); // Close Gump
expectedData.Write(ref pos, typeId);
expectedData.Write(ref pos, buttonId);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplaySignGump()
{
Serial gumpSerial = 0x1000;
var gumpId = 100;
var unknownString = "This is an unknown string";
var caption = "This is a caption";
var data = new DisplaySignGump(gumpSerial, gumpId, unknownString, caption).Compile();
Span<byte> expectedData = stackalloc byte[15 + unknownString.Length + caption.Length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x8B);
expectedData.Write(ref pos, (ushort)expectedData.Length);
expectedData.Write(ref pos, gumpSerial);
expectedData.Write(ref pos, (ushort)gumpId);
expectedData.Write(ref pos, (ushort)(unknownString.Length + 1));
expectedData.WriteAsciiNull(ref pos, unknownString);
expectedData.Write(ref pos, (ushort)(caption.Length + 1));
expectedData.WriteAsciiNull(ref pos, caption);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestFastGumpPacket()
{
var ns = new NetState(null);
var gump = new ResurrectGump(2);
var data = gump.Compile(ns).Compile();
Span<byte> expectedData = stackalloc byte[0x1000];
var pos = 0;
expectedData[pos++] = 0xB0; // Packet ID
pos += 2; // Length
expectedData.Write(ref pos, gump.Serial);
expectedData.Write(ref pos, gump.TypeID);
expectedData.Write(ref pos, gump.X);
expectedData.Write(ref pos, gump.Y);
pos += 2; // Layout Length
var layoutLength = 0;
if (!gump.Draggable)
{
expectedData.Write(ref pos, GumpUtilities.NoMoveBuffer);
layoutLength += GumpUtilities.NoMove.Length;
}
if (!gump.Closable)
{
expectedData.Write(ref pos, GumpUtilities.NoCloseBuffer);
layoutLength += GumpUtilities.NoClose.Length;
}
if (!gump.Disposable)
{
expectedData.Write(ref pos, GumpUtilities.NoDisposeBuffer);
layoutLength += GumpUtilities.NoDispose.Length;
}
if (!gump.Resizable)
{
expectedData.Write(ref pos, GumpUtilities.NoResizeBuffer);
layoutLength += GumpUtilities.NoResize.Length;
}
foreach (var entry in gump.Entries)
{
var str = entry.Compile(ns);
expectedData.WriteAscii(ref pos, str);
layoutLength += str.Length; // ASCII so 1:1
}
expectedData.Slice(19, 2).Write((ushort)layoutLength);
expectedData.Write(ref pos, (ushort)gump.Strings.Count);
for (var i = 0; i < gump.Strings.Count; ++i)
{
expectedData.WriteBigUni(ref pos, gump.Strings[i] ?? "");
}
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestPackedGumpPacket()
{
var ns = new NetState(null)
{
ProtocolChanges = ProtocolChanges.Unpack
};
var gump = new ResurrectGump(2);
var data = gump.Compile(ns).Compile();
Span<byte> expectedData = stackalloc byte[0x1000];
var pos = 0;
expectedData[pos++] = 0xDD; // Packet ID
pos += 2; // Length
expectedData.Write(ref pos, gump.Serial);
expectedData.Write(ref pos, gump.TypeID);
expectedData.Write(ref pos, gump.X);
expectedData.Write(ref pos, gump.Y);
var layoutList = new List<string>();
var bufferLength = 1; // Null terminated
if (!gump.Draggable)
{
layoutList.Add(GumpUtilities.NoMove);
bufferLength += GumpUtilities.NoMove.Length;
}
if (!gump.Closable)
{
layoutList.Add(GumpUtilities.NoClose);
bufferLength += GumpUtilities.NoClose.Length;
}
if (!gump.Disposable)
{
layoutList.Add(GumpUtilities.NoDispose);
bufferLength += GumpUtilities.NoDispose.Length;
}
if (!gump.Resizable)
{
layoutList.Add(GumpUtilities.NoResize);
bufferLength += GumpUtilities.NoResize.Length;
}
foreach (var entry in gump.Entries)
{
var str = entry.Compile(ns);
bufferLength += str.Length;
layoutList.Add(str);
}
var rawBuffer = ArrayPool<byte>.Shared.Rent(bufferLength);
Span<byte> buffer = rawBuffer;
var bufferPos = 0;
foreach (var layout in layoutList)
{
buffer.WriteAscii(ref bufferPos, layout);
}
#if NO_LOCAL_INIT
buffer.Write(ref bufferPos, (byte)0); // Layout terminator
#else
bufferPos++;
#endif
expectedData.WritePacked(ref pos, buffer.Slice(0, bufferPos));
ArrayPool<byte>.Shared.Return(rawBuffer);
expectedData.Write(ref pos, gump.Strings.Count);
bufferLength = gump.Strings.Sum(str => 2 + str.Length * 2);
rawBuffer = ArrayPool<byte>.Shared.Rent(bufferLength);
buffer = rawBuffer;
bufferPos = 0;
foreach (var str in gump.Strings)
{
buffer.WriteBigUni(ref bufferPos, str);
}
expectedData.WritePacked(ref pos, buffer.Slice(0, bufferPos));
ArrayPool<byte>.Shared.Return(rawBuffer);
// Length
expectedData.Slice(1, 2).Write((ushort)pos);
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
}
public class ResurrectGump : Gump
{
public ResurrectGump(int msg) : base(100, 0)
{
AddPage(0);
AddBackground(0, 0, 400, 350, 2600);
AddHtmlLocalized(0, 20, 400, 35, 1011022); // <center>Resurrection</center>
/* It is possible for you to be resurrected here by this healer. Do you wish to try?<br>
* CONTINUE - You chose to try to come back to life now.<br>
* CANCEL - You prefer to remain a ghost for now.
*/
AddHtmlLocalized(50, 55, 300, 140, 1011023 + msg, true, true);
AddButton(200, 227, 4005, 4007, 0);
AddHtmlLocalized(235, 230, 110, 35, 1011012); // CANCEL
AddButton(65, 227, 4005, 4007, 1);
AddHtmlLocalized(100, 230, 110, 35, 1011011); // CONTINUE
}
}
}

View file

@ -0,0 +1,606 @@
using System;
using System.Buffers;
using Server.Items;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class ItemPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestWorldItemPacket()
{
Serial serial = 0x1000;
var itemId = 1;
// Move to fixture
TileData.ItemTable[itemId] = new ItemData(
"Test Item Data",
TileFlag.Generic,
1,
1,
1,
1,
1
);
var item = new Item(serial)
{
ItemID = itemId,
Hue = 0x1024,
Amount = 10,
Location = new Point3D(1000, 100, -10),
Direction = Direction.Left
};
var data = new WorldItem(item).Compile();
Span<byte> expectedData = stackalloc byte[20]; // Max size
var pos = 0;
expectedData.Write(ref pos, (byte)0x1A);
pos += 2; // Length
if (item.Amount != 0)
{
expectedData.Write(ref pos, serial | 0x80000000);
}
else
{
expectedData.Write(ref pos, serial & 0x7FFFFFFF);
}
if (item is BaseMulti)
{
expectedData.Write(ref pos, (ushort)(item.ItemID | 0x4000));
}
else
{
expectedData.Write(ref pos, (ushort)item.ItemID);
}
if (item.Amount != 0)
{
expectedData.Write(ref pos, (ushort)item.Amount);
}
var direction = (byte)item.Direction;
var x = (ushort)(item.X & 0x7FFF);
if (direction != 0)
{
x |= 0x8000;
}
expectedData.Write(ref pos, x);
var hue = item.Hue;
var flags = item.GetPacketFlags();
var y = (ushort)(item.Y & 0x3FFF);
if (hue != 0)
{
y |= 0x8000;
}
if (flags != 0)
{
y |= 0x4000;
}
expectedData.Write(ref pos, y);
if (direction != 0)
{
expectedData.Write(ref pos, direction);
}
expectedData.Write(ref pos, (byte)item.Z);
if (hue != 0)
{
expectedData.Write(ref pos, (ushort)hue);
}
if (flags != 0)
{
expectedData.Write(ref pos, (byte)flags);
}
// Length
expectedData.Slice(1, 2).Write((ushort)pos);
// Slice the data to match in size
data = data.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestWorldItemSAPacket()
{
Serial serial = 0x1000;
ushort itemId = 1;
// Move to fixture
TileData.ItemTable[itemId] = new ItemData(
"Test Item Data",
TileFlag.Generic,
1,
1,
1,
1,
1
);
var item = new Item(serial)
{
ItemID = itemId,
Hue = 0x1024,
Amount = 10,
Location = new Point3D(1000, 100, -10)
};
var loc = item.Location;
var isMulti = item is BaseMulti;
var data = new WorldItemSA(item).Compile();
Span<byte> expectedData = stackalloc byte[24];
var pos = 0;
expectedData.Write(ref pos, (byte)0xF3); // Packet ID
expectedData.Write(ref pos, (ushort)0x1);
expectedData.Write(ref pos, (byte)(isMulti ? 0x2 : 0x00)); // Item Type (Regular, or Multi)
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)(item.ItemID & (isMulti ? 0x3FFF : 0xFFFF)));
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0)
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)item.Amount); // Amount (min?)
expectedData.Write(ref pos, (ushort)item.Amount); // Amount (max?)
expectedData.Write(ref pos, loc); // X, Y, Z
expectedData.Write(ref pos, (byte)item.Light); // Light
expectedData.Write(ref pos, (ushort)item.Hue); // Hue
expectedData.Write(ref pos, (byte)item.GetPacketFlags()); // Flags
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestWorldItemHSPacket()
{
Serial serial = 0x1000;
var itemId = 1;
// Move to fixture
TileData.ItemTable[itemId] = new ItemData(
"Test Item Data",
TileFlag.Generic,
1,
1,
1,
1,
1
);
var item = new Item(serial)
{
ItemID = itemId,
Hue = 0x1024,
Amount = 10,
Location = new Point3D(1000, 100, -10)
};
var loc = item.Location;
var isMulti = item is BaseMulti;
var data = new WorldItemHS(item).Compile();
Span<byte> expectedData = stackalloc byte[26];
var pos = 0;
expectedData.Write(ref pos, (byte)0xF3); // Packet ID
expectedData.Write(ref pos, (ushort)0x1);
expectedData.Write(ref pos, (byte)(isMulti ? 0x2 : 0x00)); // Item Type (Regular, or Multi)
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)(item.ItemID & (isMulti ? 0x3FFF : 0xFFFF)));
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)item.Amount); // Amount (min?)
expectedData.Write(ref pos, (ushort)item.Amount); // Amount (max?)
expectedData.Write(ref pos, loc); // X, Y, Z
expectedData.Write(ref pos, (byte)item.Light); // Light
expectedData.Write(ref pos, (ushort)item.Hue); // Hue
expectedData.Write(ref pos, (byte)item.GetPacketFlags()); // Flags
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0); // ??
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestContainerDisplay()
{
Serial serial = 0x1000;
ushort gumpId = 100;
var data = new ContainerDisplay(serial, gumpId).Compile();
Span<byte> expectedData = stackalloc byte[7];
var pos = 0;
expectedData.Write(ref pos, (byte)0x24); // Packet ID
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, gumpId);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestContainerDisplayHS()
{
Serial serial = 0x1000;
ushort gumpId = 100;
var data = new ContainerDisplayHS(serial, gumpId).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0x24); // Packet ID
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, gumpId);
expectedData.Write(ref pos, (ushort)0x7D); // Max Items?
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplaySpellbook()
{
Serial serial = 0x1000;
var data = new DisplaySpellbook(serial).Compile();
Span<byte> expectedData = stackalloc byte[7];
var pos = 0;
expectedData.Write(ref pos, (byte)0x24); // Packet ID
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, (ushort)0xFFFF); // Gump ID
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplaySpellbookHS()
{
Serial serial = 0x1000;
var data = new DisplaySpellbookHS(serial).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0x24); // Packet ID
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, (ushort)0xFFFF); // Gump ID
expectedData.Write(ref pos, (ushort)0x7D); // Max Items?
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestNewSpellbookContent()
{
Serial serial = 0x1000;
ushort graphic = 100;
ushort offset = 10;
ulong content = 0x123456789ABCDEF0;
var data = new NewSpellbookContent(serial, graphic, offset, content).Compile();
Span<byte> expectedData = stackalloc byte[23];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)0x17); // Length
expectedData.Write(ref pos, (ushort)0x1B); // Sub-packet
expectedData.Write(ref pos, (ushort)0x1); // Command
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, graphic);
expectedData.Write(ref pos, offset);
expectedData.WriteLE(ref pos, content);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestSpellbookContent()
{
Serial serial = 0x1000;
ushort offset = 10;
ulong content = 0x123456789ABCDEF0;
var data = new SpellbookContent(serial, offset, content).Compile();
Span<byte> expectedData = stackalloc byte[5 + 64 * 19]; // Max size
var pos = 0;
expectedData.Write(ref pos, (byte)0x3C); // Packet ID
pos += 4; // Length + spell count
ushort count = 0;
for (var i = 0; i < 64; i++)
{
if ((content & (1ul << i)) != 0)
{
expectedData.Write(ref pos, 0x7FFFFFFF - i);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (byte)0);
#else
pos += 3;
#endif
expectedData.Write(ref pos, (ushort)(i + offset));
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0); // X. Y
#else
pos += 4;
#endif
expectedData.Write(ref pos, serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
#else
pos += 2;
#endif
count++;
}
}
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData.Slice(3, 2).Write(count); // Count
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestSpellbookContent6017()
{
Serial serial = 0x1000;
ushort offset = 10;
ulong content = 0x123456789ABCDEF0;
var data = new SpellbookContent6017(serial, offset, content).Compile();
Span<byte> expectedData = stackalloc byte[5 + 64 * 20]; // Max size
var pos = 0;
expectedData.Write(ref pos, (byte)0x3C); // Packet ID
pos += 4; // Length + spell count
ushort count = 0;
for (var i = 0; i < 64; i++)
{
if ((content & (1ul << i)) != 0)
{
expectedData.Write(ref pos, 0x7FFFFFFF - i);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (byte)0);
#else
pos += 3;
#endif
expectedData.Write(ref pos, (ushort)(i + offset));
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0); // X. Y
expectedData.Write(ref pos, (byte)0); // Grid Location
#else
pos += 5;
#endif
expectedData.Write(ref pos, serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
#else
pos += 2;
#endif
count++;
}
}
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData.Slice(3, 2).Write(count); // Count
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestContainerContentUpdate()
{
Serial serial = 0x1;
var item = new Item(serial);
var data = new ContainerContentUpdate(item).Compile();
Span<byte> expectedData = stackalloc byte[20];
var pos = 0;
expectedData.Write(ref pos, (byte)0x25); // Packet ID
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)item.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // ItemID offset
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)Math.Min(item.Amount, ushort.MaxValue));
expectedData.Write(ref pos, (ushort)item.X);
expectedData.Write(ref pos, (ushort)item.Y);
expectedData.Write(ref pos, item.Parent?.Serial ?? Serial.Zero);
expectedData.Write(ref pos, (ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue));
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestContainerContentUpdate6017()
{
Serial serial = 0x1;
var item = new Item(serial);
var data = new ContainerContentUpdate6017(item).Compile();
Span<byte> expectedData = stackalloc byte[21];
var pos = 0;
expectedData.Write(ref pos, (byte)0x25); // Packet ID
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)item.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // ItemID offset
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)Math.Min(item.Amount, ushort.MaxValue));
expectedData.Write(ref pos, (ushort)item.X);
expectedData.Write(ref pos, (ushort)item.Y);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Grid Location?
#else
pos++;
#endif
expectedData.Write(ref pos, item.Parent?.Serial ?? Serial.Zero);
expectedData.Write(ref pos, (ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue));
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestContainerContent()
{
var cont = new Container(World.NewItem);
cont.AddItem(new Item(World.NewItem));
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new ContainerContent(m, cont).Compile();
Span<byte> expectedData = stackalloc byte[5 + cont.Items.Count * 19]; // Max Size
var pos = 0;
expectedData.Write(ref pos, (byte)0x3C); // Packet ID
pos += 4; // Length + Count
ushort count = 0;
var itemCount = cont.Items.Count;
for (var i = 0; i < itemCount; i++)
{
var child = cont.Items[i];
if (child.Deleted || !m.CanSee(child))
{
continue;
}
expectedData.Write(ref pos, child.Serial);
expectedData.Write(ref pos, (ushort)child.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // ItemID offset
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)Math.Min(child.Amount, ushort.MaxValue));
expectedData.Write(ref pos, (ushort)child.X);
expectedData.Write(ref pos, (ushort)child.Y);
expectedData.Write(ref pos, cont.Serial);
expectedData.Write(ref pos, (ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue));
count++;
}
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData.Slice(3, 2).Write(count); // Count
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestContainerContent6017()
{
var cont = new Container(World.NewItem);
cont.AddItem(new Item(World.NewItem));
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new ContainerContent6017(m, cont).Compile();
Span<byte> expectedData = stackalloc byte[5 + cont.Items.Count * 20];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3C); // Packet ID
pos += 4; // Length + Count
ushort count = 0;
var itemCount = cont.Items.Count;
for (var i = 0; i < itemCount; i++)
{
var child = cont.Items[i];
if (child.Deleted || !m.CanSee(child))
{
continue;
}
expectedData.Write(ref pos, child.Serial);
expectedData.Write(ref pos, (ushort)child.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // ItemID offset
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)Math.Min(child.Amount, ushort.MaxValue));
expectedData.Write(ref pos, (ushort)child.X);
expectedData.Write(ref pos, (ushort)child.Y);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Grid Location?
#else
pos++;
#endif
expectedData.Write(ref pos, cont.Serial);
expectedData.Write(ref pos, (ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue));
count++;
}
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData.Slice(3, 2).Write(count); // Count
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,42 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class LightPacketTests
{
[Fact]
public void TestGlobalLightLevel()
{
byte lightLevel = 5;
var data = new GlobalLightLevel(lightLevel).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0x4F); // Packet ID
expectedData.Write(ref pos, lightLevel);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestPersonalLightLevel()
{
Serial serial = 0x1;
byte lightLevel = 5;
var data = new PersonalLightLevel(serial, lightLevel).Compile();
Span<byte> expectedData = stackalloc byte[6];
var pos = 0;
expectedData.Write(ref pos, (byte)0x4E); // Packet ID
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, lightLevel);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,62 @@
using System;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class MapPatchesTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestMapPatches()
{
var data = new MapPatches().Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xBF, // Packet ID
0x00, 0x29, // Length
0x00, 0x18, // Sub-packet
0x00, 0x00, 0x00, 0x04, // 4 maps
0x00, 0x00, 0x00, 0x00, // Felucca
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // Trammel
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // Ilshenar
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // Malas
0x00, 0x00, 0x00, 0x00
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestInvalidMapEnable()
{
var data = new InvalidMapEnable().Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xC6 // Packet ID
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMapChange()
{
var data = new MapChange(Map.Felucca).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xBF, // Packet ID
0x00, 0x06, // Length
0x00, 0x08, // Sub-packet
0x00 // Felucca
};
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,256 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using Server.ContextMenus;
using Server.Menus.ItemLists;
using Server.Menus.Questions;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
internal class ContextMenuItem : Item
{
public ContextMenuItem(Serial serial) : base(serial)
{
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
list.Add(new ContextMenuEntry(500000));
list.Add(new ContextMenuEntry(500001));
list.Add(new ContextMenuEntry(500002));
}
}
public class MenuPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestDisplayItemListMenu()
{
var menu = new ItemListMenu(
"Which item would you choose?",
new[]
{
new ItemListEntry("Item 1", 0x01),
new ItemListEntry("Item 2", 0x100),
new ItemListEntry("Item 3", 0x1000, 250)
}
);
var data = new DisplayItemListMenu(menu).Compile();
var question = menu.Question;
var questionLength = Math.Min(255, question.Length);
var entriesCount = 0;
var length = 11 + questionLength;
foreach (var entry in menu.Entries)
{
length += 5 + entry.Name.Length;
if (entriesCount == 255)
{
break;
}
entriesCount++;
}
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x7C); // Packet ID
expectedData.Write(ref pos, (ushort)length);
expectedData.Write(ref pos, menu.Serial);
expectedData.Write(ref pos, (ushort)0x00);
expectedData.Write(ref pos, (byte)questionLength);
expectedData.WriteAscii(ref pos, question, 255);
expectedData.Write(ref pos, (byte)entriesCount);
for (var i = 0; i < entriesCount; i++)
{
var entry = menu.Entries[i];
expectedData.Write(ref pos, (ushort)entry.ItemID);
expectedData.Write(ref pos, (ushort)entry.Hue);
var name = entry.Name?.Trim() ?? "";
expectedData.Write(ref pos, (byte)Math.Min(255, name.Length));
expectedData.WriteAscii(ref pos, name, 255);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplayQuestionMenu()
{
var menu = new QuestionMenu(
"Which option would you choose?",
new[]
{
"Option 1",
"Option 2",
"Option 3"
}
);
var data = new DisplayQuestionMenu(menu).Compile();
var question = menu.Question;
var questionLength = Math.Min(255, question.Length);
var answersCount = 0;
var length = 11 + questionLength;
foreach (var answer in menu.Answers)
{
length += 5 + answer.Length;
if (answersCount == 255)
{
break;
}
answersCount++;
}
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x7C); // Packet ID
expectedData.Write(ref pos, (ushort)length);
expectedData.Write(ref pos, menu.Serial);
expectedData.Write(ref pos, (ushort)0x00);
expectedData.Write(ref pos, (byte)question.Length);
expectedData.WriteAscii(ref pos, question, 255);
expectedData.Write(ref pos, (byte)answersCount);
for (var i = 0; i < answersCount; i++)
{
var answer = menu.Answers[i];
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
#else
pos += 4;
#endif
expectedData.Write(ref pos, (byte)Math.Min(255, answer.Length));
expectedData.WriteAscii(ref pos, answer, 255);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplayContextMenu()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var item = new ContextMenuItem(World.NewItem);
var menu = new ContextMenu(m, item);
var data = new DisplayContextMenu(menu).Compile();
var length = 12 + menu.Entries.Length * 8;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (ushort)0x14); // Command
expectedData.Write(ref pos, (ushort)0x02); // Subcommand
expectedData.Write(ref pos, menu.Target.Serial);
var entries = menu.Entries;
expectedData.Write(ref pos, (byte)entries.Length);
for (var i = 0; i < entries.Length; i++)
{
var entry = entries[i];
expectedData.Write(ref pos, entry.Number);
expectedData.Write(ref pos, (ushort)i);
var flags = entry.Flags;
var range = entry.Range;
if (range == -1)
{
range = 18;
}
if (!(entry.Enabled && menu.From.InRange(item.GetWorldLocation(), range)))
{
flags |= CMEFlags.Disabled;
}
expectedData.Write(ref pos, (ushort)flags);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplayContextMenuOld()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var item = new ContextMenuItem(World.NewItem);
var menu = new ContextMenu(m, item);
var data = new DisplayContextMenuOld(menu).Compile();
var length = 12 + menu.Entries.Sum(entry => 6 + ((entry.Color & 0xFFFF) != 0xFFFF ? 2 : 0));
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (ushort)0x14); // Command
expectedData.Write(ref pos, (ushort)0x01); // Subcommand
expectedData.Write(ref pos, menu.Target.Serial);
var entries = menu.Entries;
expectedData.Write(ref pos, (byte)entries.Length);
for (var i = 0; i < entries.Length; i++)
{
var entry = entries[i];
expectedData.Write(ref pos, (ushort)i);
expectedData.Write(ref pos, (ushort)(entry.Number - 3000000));
var flags = entry.Flags;
var color = entry.Color & 0xFFFF;
if (color != 0xFFFF)
{
flags |= CMEFlags.Colored;
}
var range = entry.Range;
if (range == -1)
{
range = 18;
}
if (!(entry.Enabled && menu.From.InRange(item.GetWorldLocation(), range)))
{
flags |= CMEFlags.Disabled;
}
expectedData.Write(ref pos, (ushort)flags);
if ((flags & CMEFlags.Colored) != 0)
{
expectedData.Write(ref pos, (ushort)color);
}
}
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,191 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class MessageTests
{
[Fact]
public void TestMessageLocalized()
{
Serial serial = 0x1;
var graphic = 0x100;
var messageType = MessageType.Label;
var hue = 1024;
var font = 3;
var number = 150000;
var name = "Stuff";
var args = "Arguments";
var data = new MessageLocalized(
serial,
graphic,
messageType,
hue,
font,
number,
name,
args
).Compile();
Span<byte> expectedData = stackalloc byte[50 + args.Length * 2];
var pos = 0;
expectedData.Write(ref pos, (byte)0xC1); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, (ushort)graphic);
expectedData.Write(ref pos, (byte)messageType);
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (ushort)font);
expectedData.Write(ref pos, number);
expectedData.WriteAsciiFixed(ref pos, name, 30);
expectedData.WriteLittleUniNull(ref pos, args);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMessageLocalizedAffix()
{
Serial serial = 0x1;
var graphic = 0x100;
var messageType = MessageType.Label;
var hue = 1024;
var font = 3;
var number = 150000;
var name = "Stuff";
var args = "Arguments";
var affixType = AffixType.System;
var affix = "Affix";
var data = new MessageLocalizedAffix(
serial,
graphic,
messageType,
hue,
font,
number,
name,
affixType,
affix,
args
).Compile();
Span<byte> expectedData = stackalloc byte[52 + affix.Length + args.Length * 2];
var pos = 0;
expectedData.Write(ref pos, (byte)0xCC); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, (ushort)graphic);
expectedData.Write(ref pos, (byte)messageType);
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (ushort)font);
expectedData.Write(ref pos, number);
expectedData.Write(ref pos, (byte)affixType);
expectedData.WriteAsciiFixed(ref pos, name, 30);
expectedData.WriteAsciiNull(ref pos, affix);
expectedData.WriteBigUniNull(ref pos, args);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestAsciiMessage()
{
Serial serial = 0x1;
var graphic = 0x100;
var messageType = MessageType.Label;
var hue = 1024;
var font = 3;
var name = "Stuff";
var text = "Some Text";
var data = new AsciiMessage(
serial,
graphic,
messageType,
hue,
font,
name,
text
).Compile();
Span<byte> expectedData = stackalloc byte[45 + text.Length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x1C); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, (ushort)graphic);
expectedData.Write(ref pos, (byte)messageType);
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (ushort)font);
expectedData.WriteAsciiFixed(ref pos, name, 30);
expectedData.WriteAsciiNull(ref pos, text);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestUnicodeMessage()
{
Serial serial = 0x1;
var graphic = 0x100;
var messageType = MessageType.Label;
var hue = 1024;
var font = 3;
var lang = "ENU";
var name = "Stuff";
var text = "Some Text";
var data = new UnicodeMessage(
serial,
graphic,
messageType,
hue,
font,
lang,
name,
text
).Compile();
Span<byte> expectedData = stackalloc byte[50 + text.Length * 2];
var pos = 0;
expectedData.Write(ref pos, (byte)0xAE); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, (ushort)graphic);
expectedData.Write(ref pos, (byte)messageType);
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (ushort)font);
expectedData.WriteAsciiFixed(ref pos, lang, 4);
expectedData.WriteAsciiFixed(ref pos, name, 30);
expectedData.WriteBigUniNull(ref pos, text);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestFollowMessage()
{
Serial serial = 0x1;
Serial serial2 = 0x2;
var data = new FollowMessage(serial, serial2).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0x15); // Packet ID
expectedData.Write(ref pos, serial);
expectedData.Write(ref pos, serial2);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,915 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class MobilePacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestDeathAnimation()
{
Serial killed = 0x1;
Serial corpse = 0x1000;
var data = new DeathAnimation(killed, corpse).Compile();
Span<byte> expectedData = stackalloc byte[13];
var pos = 0;
expectedData.Write(ref pos, (byte)0xAF); // Packet ID
expectedData.Write(ref pos, killed);
expectedData.Write(ref pos, corpse);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestBondStatus()
{
Serial petSerial = 0x1;
var bonded = true;
var data = new BondedStatus(petSerial, bonded).Compile();
Span<byte> expectedData = stackalloc byte[11];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)0x0B); // Length
expectedData.Write(ref pos, (ushort)0x19); // Sub-packet
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Command
#else
pos++;
#endif
expectedData.Write(ref pos, petSerial);
expectedData.Write(ref pos, bonded);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileMoving()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var noto = 10;
var data = new MobileMoving(m, noto).Compile();
Span<byte> expectedData = stackalloc byte[17];
var pos = 0;
expectedData.Write(ref pos, (byte)0x77); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (ushort)m.Body);
expectedData.Write(ref pos, m.Location);
expectedData.Write(ref pos, (byte)m.Direction);
expectedData.Write(ref pos, (ushort)m.Hue);
expectedData.Write(ref pos, (byte)m.GetPacketFlags());
expectedData.Write(ref pos, (byte)noto);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileMovingOld()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var noto = 10;
var data = new MobileMoving(m, noto).Compile();
Span<byte> expectedData = stackalloc byte[17];
var pos = 0;
expectedData.Write(ref pos, (byte)0x77); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (ushort)m.Body);
expectedData.Write(ref pos, m.Location);
expectedData.Write(ref pos, (byte)m.Direction);
expectedData.Write(ref pos, (ushort)m.Hue);
expectedData.Write(ref pos, (byte)m.GetOldPacketFlags());
expectedData.Write(ref pos, (byte)noto);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileName()
{
var m = new Mobile(0x1)
{
Name = "Some Really Long Mobile Name That Gets Cut off"
};
m.DefaultMobileInit();
var data = new MobileName(m).Compile();
Span<byte> expectedData = stackalloc byte[37];
var pos = 0;
expectedData.Write(ref pos, (byte)0x98);
expectedData.Write(ref pos, (ushort)0x25);
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAsciiFixed(ref pos, m.Name ?? "", 29);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileAnimation()
{
Serial mobile = 0x1;
var action = 200;
var frameCount = 5;
var repeatCount = 1;
var reverse = false;
var repeat = false;
byte delay = 5;
var data = new MobileAnimation(
mobile,
action,
frameCount,
repeatCount,
!reverse,
repeat,
delay
).Compile();
Span<byte> expectedData = stackalloc byte[14];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6E); // Packet ID
expectedData.Write(ref pos, mobile);
expectedData.Write(ref pos, (ushort)action);
expectedData.Write(ref pos, (ushort)frameCount);
expectedData.Write(ref pos, (ushort)repeatCount);
expectedData.Write(ref pos, reverse);
expectedData.Write(ref pos, repeat);
expectedData.Write(ref pos, delay);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestNewMobileAnimation()
{
Serial mobile = 0x1;
var action = 200;
var frameCount = 5;
byte delay = 5;
var data = new NewMobileAnimation(
mobile,
action,
frameCount,
delay
).Compile();
Span<byte> expectedData = stackalloc byte[10];
var pos = 0;
expectedData.Write(ref pos, (byte)0xE2);
expectedData.Write(ref pos, mobile);
expectedData.Write(ref pos, (ushort)action);
expectedData.Write(ref pos, (ushort)frameCount);
expectedData.Write(ref pos, delay);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileStatusCompact()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var canBeRenamed = false;
var data = new MobileStatusCompact(canBeRenamed, m).Compile();
Span<byte> expectedData = stackalloc byte[43];
var pos = 0;
expectedData.Write(ref pos, (byte)0x11); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAsciiFixed(ref pos, m.Name ?? "", 30);
expectedData.WriteReverseAttribute(ref pos, m.Hits, m.HitsMax, true);
expectedData.Write(ref pos, canBeRenamed);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // type
#endif
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(ProtocolChanges.Version70610)]
[InlineData(ProtocolChanges.Version400a)]
[InlineData(ProtocolChanges.Version502b)]
public void TestMobileStatusExtended(ProtocolChanges changes)
{
var beholder = new Mobile(0x1)
{
Name = "Random Mobile 1"
};
beholder.DefaultMobileInit();
var beheld = new Mobile(0x2)
{
Name = "Random Mobile 2"
};
beheld.DefaultMobileInit();
var ns = new NetState(null)
{
ProtocolChanges = changes
};
var data = new MobileStatus(beholder, beheld, ns).Compile();
Span<byte> expectedData = stackalloc byte[121]; // Max Size
var pos = 0;
expectedData.Write(ref pos, (byte)0x11);
pos += 2; // Length
int type;
var notSelf = beholder != beheld;
if (notSelf)
{
type = 0;
}
else if (Core.HS && ns.ExtendedStatus)
{
type = 6;
}
else if (Core.ML && ns.SupportsExpansion(Expansion.ML))
{
type = 5;
}
else
{
type = Core.AOS ? 4 : 3;
}
expectedData.Write(ref pos, beheld.Serial);
expectedData.WriteAsciiFixed(ref pos, beheld.Name, 30);
expectedData.WriteReverseAttribute(ref pos, beheld.Hits, beheld.HitsMax, notSelf);
expectedData.Write(ref pos, beheld.CanBeRenamedBy(beheld));
expectedData.Write(ref pos, (byte)type);
if (type > 0)
{
expectedData.Write(ref pos, beheld.Female);
expectedData.Write(ref pos, (ushort)beheld.Str);
expectedData.Write(ref pos, (ushort)beheld.Dex);
expectedData.Write(ref pos, (ushort)beheld.Int);
expectedData.WriteReverseAttribute(ref pos, beheld.Stam, beheld.StamMax, notSelf);
expectedData.WriteReverseAttribute(ref pos, beheld.Mana, beheld.ManaMax, notSelf);
expectedData.Write(ref pos, beheld.TotalGold);
expectedData.Write(
ref pos,
(ushort)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))
);
expectedData.Write(ref pos, (ushort)(Mobile.BodyWeight + beheld.TotalWeight));
if (type >= 5)
{
expectedData.Write(ref pos, (ushort)beheld.MaxWeight);
expectedData.Write(ref pos, (byte)(beheld.Race.RaceID + 1)); // 0x00 for a non-ML enabled account
}
expectedData.Write(ref pos, (ushort)beheld.StatCap);
expectedData.Write(ref pos, (byte)beheld.Followers);
expectedData.Write(ref pos, (byte)beheld.FollowersMax);
if (type >= 4)
{
expectedData.Write(ref pos, (ushort)beheld.FireResistance);
expectedData.Write(ref pos, (ushort)beheld.ColdResistance);
expectedData.Write(ref pos, (ushort)beheld.PoisonResistance);
expectedData.Write(ref pos, (ushort)beheld.EnergyResistance);
expectedData.Write(ref pos, (ushort)beheld.Luck);
}
var min = 0;
var max = 0;
beheld.Weapon?.GetStatusDamage(beheld, out min, out max);
expectedData.Write(ref pos, (ushort)min);
expectedData.Write(ref pos, (ushort)max);
expectedData.Write(ref pos, beheld.TithingPoints);
if (type >= 6)
{
for (var i = 0; i < 15; ++i)
{
expectedData.Write(ref pos, (ushort)beheld.GetAOSStatus(i));
}
}
}
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData("None")]
[InlineData("Lesser")]
[InlineData("Lethal")]
public void TestHealthbarPoison(string pName)
{
var p = Poison.GetPoison(pName);
var m = new Mobile(0x1);
m.DefaultMobileInit();
m.Poison = p;
var data = new HealthbarPoison(m).Compile();
Span<byte> expectedData = stackalloc byte[12];
var pos = 0;
expectedData.Write(ref pos, (byte)0x17); // Packet ID
expectedData.Write(ref pos, (ushort)12); // Length
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, 0x10001); // Show Bar?, Poison Bar
expectedData.Write(ref pos, (byte)((p?.Level ?? -1) + 1));
AssertThat.Equal(data, expectedData);
Assert.Equal(p?.Level, m.Poison?.Level);
}
[Theory]
[InlineData(false, false)]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(true, true)]
public void TestYellowBar(bool isBlessed, bool isYellowHealth)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
m.Blessed = isBlessed;
m.YellowHealthbar = isYellowHealth;
var data = new HealthbarYellow(m).Compile();
Span<byte> expectedData = stackalloc byte[12];
var pos = 0;
expectedData.Write(ref pos, (byte)0x17); // Packet ID
expectedData.Write(ref pos, (ushort)12); // Length
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, 0x10002); // Show Bar?, Yellow Bar
expectedData.Write(ref pos, isBlessed || isYellowHealth);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileUpdate()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileUpdate(m).Compile();
Span<byte> expectedData = stackalloc byte[19];
var pos = 0;
var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue;
expectedData.Write(ref pos, (byte)0x20); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (ushort)m.Body);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Unknown
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (byte)m.GetPacketFlags());
expectedData.Write(ref pos, (ushort)m.X);
expectedData.Write(ref pos, (ushort)m.Y);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)2); // Unknown
#else
pos += 2;
#endif
expectedData.Write(ref pos, (byte)m.Direction);
expectedData.Write(ref pos, (byte)m.Z);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileUpdateOld()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileUpdateOld(m).Compile();
Span<byte> expectedData = stackalloc byte[19];
var pos = 0;
var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue;
expectedData.Write(ref pos, (byte)0x20); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (ushort)m.Body);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Unknown
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (byte)m.GetOldPacketFlags());
expectedData.Write(ref pos, (ushort)m.X);
expectedData.Write(ref pos, (ushort)m.Y);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)2); // Unknown
#else
pos += 2;
#endif
expectedData.Write(ref pos, (byte)m.Direction);
expectedData.Write(ref pos, (byte)m.Z);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(10, 1024, 0, 0)]
[InlineData(10, 1024, 11, 2048)]
public void TestMobileIncoming(int hairItemId, int hairHue, int facialHairItemId, int facialHairHue)
{
var beholder = new Mobile(0x1)
{
Name = "Random Mobile 1"
};
beholder.DefaultMobileInit();
var beheld = new Mobile(0x2)
{
Name = "Random Mobile 2"
};
beheld.DefaultMobileInit();
beheld.AddItem(
new Item((Serial)0x1000)
{
Layer = Layer.OneHanded
}
);
// Test Dupe
beheld.AddItem(
new Item((Serial)0x1001)
{
Layer = Layer.OneHanded
}
);
beheld.HairItemID = hairItemId;
beheld.HairHue = hairHue;
beheld.FacialHairItemID = facialHairItemId;
beheld.FacialHairHue = facialHairHue;
var data = new MobileIncoming(beholder, beheld).Compile();
Span<bool> layers = stackalloc bool[256];
#if NO_LOCAL_INIT
layers.Clear();
#endif
var items = beheld.Items;
var count = items.Count;
if (beheld.HairItemID > 0)
{
count++;
}
if (beheld.FacialHairItemID > 0)
{
count++;
}
var length = 23 + count * 9; // Max Size
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x78);
pos += 2; // Length
var isSolidHue = beheld.SolidHueOverride >= 0;
expectedData.Write(ref pos, beheld.Serial);
expectedData.Write(ref pos, (ushort)beheld.Body);
expectedData.Write(ref pos, (ushort)beheld.X);
expectedData.Write(ref pos, (ushort)beheld.Y);
expectedData.Write(ref pos, (byte)beheld.Z);
expectedData.Write(ref pos, (byte)beheld.Direction);
expectedData.Write(ref pos, (ushort)(isSolidHue ? beheld.SolidHueOverride : beheld.Hue));
expectedData.Write(ref pos, (byte)beheld.GetPacketFlags());
expectedData.Write(ref pos, (byte)Notoriety.Compute(beholder, beheld));
byte layer;
for (var i = 0; i < items.Count; i++)
{
var item = items[i];
layer = (byte)item.Layer;
if (!item.Deleted && !layers[layer] && beholder.CanSee(item))
{
layers[layer] = true;
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)(item.ItemID & 0xFFFF));
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, (ushort)(isSolidHue ? beheld.SolidHueOverride : item.Hue));
}
}
layer = (byte)Layer.Hair;
var itemId = beheld.HairItemID & 0xFFFF;
if (itemId > 0 && !layers[layer])
{
expectedData.Write(ref pos, HairInfo.FakeSerial(beheld));
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, (ushort)(isSolidHue ? beheld.SolidHueOverride : beheld.HairHue));
}
layer = (byte)Layer.FacialHair;
itemId = beheld.FacialHairItemID & 0xFFFF;
if (itemId > 0 && !layers[layer])
{
expectedData.Write(ref pos, FacialHairInfo.FakeSerial(beheld));
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, (ushort)(isSolidHue ? beheld.SolidHueOverride : beheld.FacialHairHue));
}
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0); // Zero serial, terminate list
#else
pos += 4;
#endif
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(ProtocolChanges.Version6000, 0, 0, 0, 0)]
[InlineData(ProtocolChanges.Version6000, 10, 1024, 0, 0)]
[InlineData(ProtocolChanges.Version6000, 10, 1024, 11, 2048)]
[InlineData(ProtocolChanges.Version7000, 0, 0, 0, 0)]
[InlineData(ProtocolChanges.Version7000, 10, 1024, 0, 0)]
[InlineData(ProtocolChanges.Version7000, 10, 1024, 11, 2048)]
public void TestMobileIncomingOld(
ProtocolChanges protocolChanges, int hairItemId, int hairHue, int facialHairItemId, int facialHairHue
)
{
var beholder = new Mobile(0x1)
{
Name = "Random Mobile 1"
};
beholder.DefaultMobileInit();
var beheld = new Mobile(0x2)
{
Name = "Random Mobile 2"
};
beheld.DefaultMobileInit();
beheld.AddItem(
new Item((Serial)0x1000)
{
Layer = Layer.OneHanded
}
);
// Test Dupe
beheld.AddItem(
new Item((Serial)0x1001)
{
Layer = Layer.OneHanded
}
);
beheld.HairItemID = hairItemId;
beheld.HairHue = hairHue;
beheld.FacialHairItemID = facialHairItemId;
beheld.FacialHairHue = facialHairHue;
var ns = new NetState(null)
{
ProtocolChanges = protocolChanges
};
var data = (ns.StygianAbyss
? (Packet)new MobileIncomingSA(beholder, beheld)
: new MobileIncomingOld(beholder, beheld))
.Compile();
Span<bool> layers = stackalloc bool[256];
#if NO_LOCAL_INIT
layers.Clear();
#endif
var items = beheld.Items;
var count = items.Count;
if (beheld.HairItemID > 0)
{
count++;
}
if (beheld.FacialHairItemID > 0)
{
count++;
}
var length = 23 + count * 9; // Max Size
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x78);
pos += 2; // Length
var isSolidHue = beheld.SolidHueOverride >= 0;
expectedData.Write(ref pos, beheld.Serial);
expectedData.Write(ref pos, (ushort)beheld.Body);
expectedData.Write(ref pos, (ushort)beheld.X);
expectedData.Write(ref pos, (ushort)beheld.Y);
expectedData.Write(ref pos, (byte)beheld.Z);
expectedData.Write(ref pos, (byte)beheld.Direction);
expectedData.Write(ref pos, (ushort)(isSolidHue ? beheld.SolidHueOverride : beheld.Hue));
expectedData.Write(ref pos, (byte)(ns.StygianAbyss ? beheld.GetOldPacketFlags() : beheld.GetPacketFlags()));
expectedData.Write(ref pos, (byte)Notoriety.Compute(beholder, beheld));
byte layer;
int itemId;
int hue;
for (var i = 0; i < items.Count; i++)
{
var item = items[i];
layer = (byte)item.Layer;
if (!item.Deleted && !layers[layer] && beholder.CanSee(item))
{
layers[layer] = true;
itemId = item.ItemID & 0x7FFF;
hue = isSolidHue ? beheld.SolidHueOverride : item.Hue;
if (hue != 0)
{
itemId |= 0x8000;
}
expectedData.Write(ref pos, item.Serial);
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, (ushort)hue);
}
}
layer = (byte)Layer.Hair;
itemId = beheld.HairItemID & 0x7FFF;
if (itemId > 0 && !layers[layer])
{
hue = isSolidHue ? beheld.SolidHueOverride : beheld.HairHue;
if (hue != 0)
{
itemId |= 0x8000;
}
expectedData.Write(ref pos, HairInfo.FakeSerial(beheld));
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, (ushort)hue);
}
layer = (byte)Layer.FacialHair;
itemId = beheld.FacialHairItemID & 0x7FFF;
if (itemId > 0 && !layers[layer])
{
hue = isSolidHue ? beheld.SolidHueOverride : beheld.FacialHairHue;
if (hue != 0)
{
itemId |= 0x8000;
}
expectedData.Write(ref pos, FacialHairInfo.FakeSerial(beheld));
expectedData.Write(ref pos, (ushort)itemId);
expectedData.Write(ref pos, layer);
expectedData.Write(ref pos, (ushort)hue);
}
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0); // Zero serial, terminate list
#else
pos += 4;
#endif
expectedData.Slice(1, 2).Write((ushort)pos); // Length
expectedData = expectedData.Slice(0, pos);
AssertThat.Equal(data, expectedData);
}
}
[Collection("Sequential Tests")]
public class SequentialMobilePacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestMobileHits()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileHits(m).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA1); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Hits, m.HitsMax, false);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileHitsN()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileHitsN(m).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA1); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Hits, m.HitsMax, true);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileMana()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileMana(m).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA2); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Mana, m.ManaMax, false);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileManaN()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileManaN(m).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA2); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Mana, m.ManaMax, true);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileStam()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileStam(m).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA3); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Stam, m.StamMax, false);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileStamN()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileStamN(m).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA3); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Stam, m.StamMax, true);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileAttributes()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileAttributes(m).Compile();
Span<byte> expectedData = stackalloc byte[17];
var pos = 0;
expectedData.Write(ref pos, (byte)0x2D); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Hits, m.HitsMax, false);
expectedData.WriteAttribute(ref pos, m.Mana, m.ManaMax, false);
expectedData.WriteAttribute(ref pos, m.Stam, m.StamMax, false);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMobileAttributesN()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new MobileAttributesN(m).Compile();
Span<byte> expectedData = stackalloc byte[17];
var pos = 0;
expectedData.Write(ref pos, (byte)0x2D); // Packet ID
expectedData.Write(ref pos, m.Serial);
expectedData.WriteAttribute(ref pos, m.Hits, m.HitsMax, true);
expectedData.WriteAttribute(ref pos, m.Mana, m.ManaMax, true);
expectedData.WriteAttribute(ref pos, m.Stam, m.StamMax, true);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,112 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class MovementPacketTests : IClassFixture<ServerFixture>
{
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
public void TestSpeedControl(byte speedControl)
{
var data = new SpeedControl(speedControl).Compile();
Span<byte> expectedData = stackalloc byte[6];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)6); // Length
expectedData.Write(ref pos, (ushort)0x26); // Command
expectedData.Write(ref pos, speedControl);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMovePlayer()
{
const Direction d = Direction.Left;
var data = new MovePlayer(d).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0x97); // Packet ID
expectedData.Write(ref pos, (byte)d);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMovementRej()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
const byte seq = 100;
var data = new MovementRej(seq, m).Compile();
Span<byte> expectedData = stackalloc byte[8];
var pos = 0;
expectedData.Write(ref pos, (byte)0x21); // Packet ID
expectedData.Write(ref pos, seq);
expectedData.Write(ref pos, (short)m.X);
expectedData.Write(ref pos, (short)m.Y);
expectedData.Write(ref pos, (byte)m.Direction);
expectedData.Write(ref pos, (byte)m.Z);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMovementAck()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
const byte seq = 100;
var noto = Notoriety.Compute(m, m);
var data = MovementAck.Instantiate(seq, m).Compile();
Span<byte> expectedData = stackalloc byte[3];
var pos = 0;
expectedData.Write(ref pos, (byte)0x22); // Packet ID
expectedData.Write(ref pos, seq);
expectedData.Write(ref pos, (byte)noto);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestNullFastwalkStack()
{
var data = new NullFastwalkStack().Compile();
Span<byte> expectedData = stackalloc byte[29];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)29); // Length
expectedData.Write(ref pos, (short)0x1); // Sub-packet
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0); // Key 1
expectedData.Write(ref pos, 0); // Key 2
expectedData.Write(ref pos, 0); // Key 3
expectedData.Write(ref pos, 0); // Key 4
expectedData.Write(ref pos, 0); // Key 5
expectedData.Write(ref pos, 0); // Key 6
#endif
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class ObjectHelpResponseTests
{
[Fact]
public void TestObjectHelpResponse()
{
Serial s = 0x100;
var text = "This is some testing text";
var data = new ObjectHelpResponse(s, text).Compile();
var length = 9 + text.Length * 2;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xB7); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, s);
expectedData.WriteBigUniNull(ref pos, text);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,526 @@
using System;
using System.Buffers;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class PlayerPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestStatLockInfo()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var data = new StatLockInfo(m).Compile();
Span<byte> expectedData = stackalloc byte[12];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)12); // Length
expectedData.Write(ref pos, (ushort)0x19); // Sub-packet
expectedData.Write(ref pos, (byte)2); // Command
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (ushort)(((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock));
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestChangeUpdateRange()
{
byte range = 10;
var data = new ChangeUpdateRange(range).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0xC8); // Packet ID
expectedData.Write(ref pos, range);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void TestDeathStatus(bool isDead)
{
var data = new DeathStatus(isDead).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
const byte dead = 0;
const byte alive = 2;
expectedData.Write(ref pos, (byte)0x2C); // Packet ID
expectedData.Write(ref pos, isDead ? dead : alive);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(0, true)]
[InlineData(0, false)]
[InlineData(100, true)]
[InlineData(1000, false)]
public void TestSpecialAbility(int abilityId, bool active)
{
var data = new ToggleSpecialAbility(abilityId, active).Compile();
Span<byte> expectedData = stackalloc byte[8];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)0x8); // Length
expectedData.Write(ref pos, (ushort)0x25); // Sub-packet
expectedData.Write(ref pos, (ushort)abilityId);
expectedData.Write(ref pos, active);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData("This is a header", "This is a body", "This is a footer")]
[InlineData(null, null, null)]
public void TestDisplayProfile(string header, string body, string footer)
{
Serial m = 0x1000;
var data = new DisplayProfile(m, header, body, footer).Compile();
header ??= "";
body ??= "";
footer ??= "";
var length = 12 + header.Length + footer.Length * 2 + body.Length * 2;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xB8); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, m); // Mobile Serial or Serial.Zero
expectedData.WriteAsciiNull(ref pos, header);
expectedData.WriteBigUniNull(ref pos, footer);
expectedData.WriteBigUniNull(ref pos, body);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(LRReason.CannotLift)]
[InlineData(LRReason.TryToSteal)]
public void TestLiftRej(LRReason reason)
{
var data = new LiftRej(reason).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0x27); // Packet ID
expectedData.Write(ref pos, (byte)reason);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestLogoutAck()
{
var data = new LogoutAck().Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0xD1); // Packet ID
expectedData.Write(ref pos, (byte)0x1); // 1 - Ack
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(1, 2, 3)]
[InlineData(4, 5, 6)]
public void TestWeather(int type, int density, int temp)
{
var data = new Weather(type, density, temp).Compile();
Span<byte> expectedData = stackalloc byte[4];
var pos = 0;
expectedData.Write(ref pos, (byte)0x65); // Packet ID
expectedData.Write(ref pos, (byte)type);
expectedData.Write(ref pos, (byte)density);
expectedData.Write(ref pos, (byte)temp);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestRemoveEntity()
{
Serial e = 0x1000;
var data = new RemoveEntity(e).Compile();
Span<byte> expectedData = stackalloc byte[5];
var pos = 0;
expectedData.Write(ref pos, (byte)0x1D); // Packet ID
expectedData.Write(ref pos, e);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestServerChange()
{
var p = new Point3D(100, 1000, 1);
var map = Map.Felucca;
var data = new ServerChange(p, map).Compile();
Span<byte> expectedData = stackalloc byte[16];
var pos = 0;
expectedData.Write(ref pos, (byte)0x76); // Packet ID
expectedData.Write(ref pos, (ushort)p.X);
expectedData.Write(ref pos, (ushort)p.Y);
expectedData.Write(ref pos, (short)p.Z);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Unknown
expectedData.Write(ref pos, 0); // Server X, Server Y
#else
pos += 5;
#endif
expectedData.Write(ref pos, (ushort)map.Width); // Server Width
expectedData.Write(ref pos, (ushort)map.Height); // Server Height
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestSkillUpdate()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var skills = m.Skills;
m.Skills[SkillName.Alchemy].BaseFixedPoint = 1000; // GM Alchemy
var data = new SkillUpdate(skills).Compile();
var length = 6 + skills.Length * 9;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3A); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (byte)0x02); // type: absolute, capped
for (var i = 0; i < skills.Length; i++)
{
var s = skills[i];
var v = s.NonRacialValue;
var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF);
expectedData.Write(ref pos, (ushort)(s.Info.SkillID + 1));
expectedData.Write(ref pos, (ushort)uv);
expectedData.Write(ref pos, (ushort)s.BaseFixedPoint);
expectedData.Write(ref pos, (byte)s.Lock);
expectedData.Write(ref pos, (ushort)s.CapFixedPoint);
}
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(0)]
[InlineData(10)]
[InlineData(255)]
public void TestSequence(byte num)
{
var data = new Sequence(num).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0x7B); // Packet ID
expectedData.Write(ref pos, num);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(SkillName.Alchemy, 0, 1)]
[InlineData(SkillName.Archery, 10, 1000)]
[InlineData(SkillName.Begging, 100000, 1000)]
public void TestSkillChange(SkillName skillName, int baseFixedPoint, int capFixedPoint)
{
// TODO: Eliminate all of this and just create a Skill directly
var m = new Mobile(0x1);
m.DefaultMobileInit();
var skill = m.Skills[skillName];
skill.BaseFixedPoint = baseFixedPoint;
skill.CapFixedPoint = capFixedPoint;
var data = new SkillChange(skill).Compile();
Span<byte> expectedData = stackalloc byte[13];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3A); // Packet ID
expectedData.Write(ref pos, (ushort)13); // Length
expectedData.Write(ref pos, (byte)0xDF); // type: delta, capped
var v = skill.NonRacialValue;
var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF);
expectedData.Write(ref pos, (ushort)skill.Info.SkillID);
expectedData.Write(ref pos, (ushort)uv);
expectedData.Write(ref pos, (ushort)skill.BaseFixedPoint);
expectedData.Write(ref pos, (byte)skill.Lock);
expectedData.Write(ref pos, (ushort)skill.CapFixedPoint);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("This is a URL, I promise")]
public void TestLaunchBrowser(string url)
{
var data = new LaunchBrowser(url).Compile();
url ??= "";
var length = 4 + url.Length;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA5); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.WriteAsciiNull(ref pos, url); // Note: use punycode for unicode URLs
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDragEffect()
{
var src = new Entity(0x1, new Point3D(1000, 100, 10), Map.Felucca);
var targ = new Entity(0x2, new Point3D(1125, 125, 5), Map.Felucca);
var itemID = 0x384;
var hue = 1024;
var amount = 25;
var data = new DragEffect(src, targ, itemID, hue, amount).Compile();
Span<byte> expectedData = stackalloc byte[26];
var pos = 0;
expectedData.Write(ref pos, (byte)0x23); // Packet ID
expectedData.Write(ref pos, (ushort)itemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#else
pos++;
#endif
expectedData.Write(ref pos, (ushort)hue);
expectedData.Write(ref pos, (ushort)amount);
expectedData.Write(ref pos, src.Serial);
expectedData.Write(ref pos, src.Location);
expectedData.Write(ref pos, targ.Serial);
expectedData.Write(ref pos, targ.Location);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(1, false)]
[InlineData(2, true)]
public void TestSeasonChange(int season, bool playSound)
{
var data = new SeasonChange(season, playSound).Compile();
Span<byte> expectedData = stackalloc byte[3];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBC); // Packet ID
expectedData.Write(ref pos, (byte)season);
expectedData.Write(ref pos, playSound);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(0x1024, "Test Title", true, true)]
[InlineData(0x1024, "Test Title", false, true)]
[InlineData(0x1024, "Test Title", true, false)]
public void TestDisplayPaperdoll(uint m, string title, bool warmode, bool canLift)
{
var data = new DisplayPaperdoll(m, title, warmode, canLift).Compile();
Span<byte> expectedData = stackalloc byte[66];
var pos = 0;
expectedData.Write(ref pos, (byte)0x88); // Packet ID
expectedData.Write(ref pos, m);
expectedData.WriteAsciiFixed(ref pos, title, 60);
byte flags = 0x00;
if (warmode)
{
flags |= 0x01;
}
if (canLift)
{
flags |= 0x02;
}
expectedData.Write(ref pos, flags);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(10, 1000, 10, 5)]
public void TestPlaySound(ushort soundID, int x, int y, int z)
{
var p = new Point3D(x, y, z);
var data = new PlaySound(soundID, p).Compile();
Span<byte> expectedData = stackalloc byte[12];
var pos = 0;
expectedData.Write(ref pos, (byte)0x54); // Packet ID
expectedData.Write(ref pos, (byte)1); // Flags
expectedData.Write(ref pos, soundID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0); // Volume
#else
pos += 2;
#endif
expectedData.Write(ref pos, (ushort)p.X);
expectedData.Write(ref pos, (ushort)p.Y);
expectedData.Write(ref pos, (short)p.Z);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(MusicName.Approach)]
[InlineData(MusicName.Combat1)]
[InlineData(MusicName.ValoriaShips)]
public void TestPlayMusic(MusicName music)
{
var data = new PlayMusic(music).Compile();
Span<byte> expectedData = stackalloc byte[3];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6D); // Packet ID
expectedData.Write(ref pos, (ushort)music); // Flags
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(10, 1, "Some text")]
[InlineData(100, 10, "Some more text")]
public void TestScrollMessage(int type, int tip, string text)
{
var data = new ScrollMessage(type, tip, text).Compile();
text ??= "";
var length = 10 + text.Length;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xA6); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (byte)type);
expectedData.Write(ref pos, tip);
expectedData.Write(ref pos, (ushort)text.Length);
expectedData.WriteAscii(ref pos, text);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestCurrentTime()
{
var date = DateTime.Parse("2020-01-01 14:10:05");
var data = new CurrentTime(date).Compile();
Span<byte> expectedData = stackalloc byte[4];
var pos = 0;
expectedData.Write(ref pos, (byte)0x5B); // Packet ID
expectedData.Write(ref pos, (byte)date.Hour);
expectedData.Write(ref pos, (byte)date.Minute);
expectedData.Write(ref pos, (byte)date.Second);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestPathfindMessage()
{
var p = new Point3D(1000, 10, 1);
var data = new PathfindMessage(p).Compile();
Span<byte> expectedData = stackalloc byte[7];
var pos = 0;
expectedData.Write(ref pos, (byte)0x38); // Packet ID
expectedData.Write(ref pos, (ushort)p.X);
expectedData.Write(ref pos, (ushort)p.Y);
expectedData.Write(ref pos, (short)p.Z);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(0)]
[InlineData(10)]
[InlineData(100)]
public void TestPingAck(byte ping)
{
var data = new PingAck(ping).Compile();
Span<byte> expectedData = stackalloc byte[2];
var pos = 0;
expectedData.Write(ref pos, (byte)0x73); // Packet ID
expectedData.Write(ref pos, ping);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestClearAbility()
{
var data = new ClearWeaponAbility().Compile();
Span<byte> expectedData = stackalloc byte[5];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)5); // Length
expectedData.Write(ref pos, (ushort)0x21); // Sub-packet
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,173 @@
using System;
using System.Buffers;
using Server.Items;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class SecureTradePacketTests : IClassFixture<ServerFixture>
{
[Theory]
[InlineData("short-name")]
[InlineData("this is a really long name that is more than 30 characters, probably")]
public void TestDisplaySecureTrade(string name)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var firstCont = new Container(World.NewItem);
var secondCont = new Container(World.NewItem);
var data = new DisplaySecureTrade(m, firstCont, secondCont, name).Compile();
var hasName = name.Length > 0;
Span<byte> expectedData = stackalloc byte[17 + (hasName ? 30 : 0)];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6F); // Packet ID
expectedData.Write(ref pos, (ushort)0x2F); // Length
expectedData.Write(ref pos, (byte)TradeFlag.Display); // Command
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, firstCont.Serial);
expectedData.Write(ref pos, secondCont.Serial);
expectedData.Write(ref pos, hasName);
if (hasName)
{
expectedData.WriteAsciiFixed(ref pos, name, 30);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestCloseSecureTrade()
{
var cont = new Container(World.NewItem);
var data = new CloseSecureTrade(cont).Compile();
Span<byte> expectedData = stackalloc byte[8];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6F); // Packet ID
expectedData.Write(ref pos, (ushort)0x8); // Length
expectedData.Write(ref pos, (byte)TradeFlag.Close); // Command
expectedData.Write(ref pos, cont.Serial);
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(true, false)] // Update first
[InlineData(false, true)] // Update second
public void TestUpdateSecureTrade(bool first, bool second)
{
var firstCont = new Container(World.NewItem);
var secondCont = new Container(World.NewItem);
var cont = first ? firstCont : secondCont;
var data = new UpdateSecureTrade(cont, first, second).Compile();
Span<byte> expectedData = stackalloc byte[16];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6F); // Packet ID
expectedData.Write(ref pos, (ushort)0x10); // Length
expectedData.Write(ref pos, (byte)TradeFlag.Update); // Command
expectedData.Write(ref pos, cont.Serial);
expectedData.Write(ref pos, first ? 1 : 0); // true if first
expectedData.Write(ref pos, second ? 1 : 0); // true if second
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(100000, 30, TradeFlag.UpdateGold)]
[InlineData(250000, 50000, TradeFlag.UpdateLedger)]
public void TestUpdateGoldSecureTrade(int gold, int plat, TradeFlag flag)
{
var cont = new Container(World.NewItem);
var data = new UpdateSecureTrade(cont, flag, gold, plat).Compile();
Span<byte> expectedData = stackalloc byte[16];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6F); // Packet ID
expectedData.Write(ref pos, (ushort)0x10); // Length
expectedData.Write(ref pos, (byte)flag); // Command
expectedData.Write(ref pos, cont.Serial);
expectedData.Write(ref pos, gold);
expectedData.Write(ref pos, plat);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestSecureTradeEquip()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var cont = new Container(World.NewItem);
var itemInCont = new Item(World.NewItem) { Parent = cont };
var data = new SecureTradeEquip(itemInCont, m).Compile();
Span<byte> expectedData = stackalloc byte[20];
var pos = 0;
expectedData.Write(ref pos, (byte)0x25); // Packet ID
expectedData.Write(ref pos, itemInCont.Serial);
expectedData.Write(ref pos, (short)itemInCont.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#else
pos++;
#endif
expectedData.Write(ref pos, (short)itemInCont.Amount);
expectedData.Write(ref pos, (short)itemInCont.X);
expectedData.Write(ref pos, (short)itemInCont.Y);
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (short)itemInCont.Hue);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestSecureTradeEquip6017()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var cont = new Container(World.NewItem);
var itemInCont = new Item(World.NewItem) { Parent = cont };
var data = new SecureTradeEquip6017(itemInCont, m).Compile();
Span<byte> expectedData = stackalloc byte[21];
var pos = 0;
expectedData.Write(ref pos, (byte)0x25); // Packet ID
expectedData.Write(ref pos, itemInCont.Serial);
expectedData.Write(ref pos, (short)itemInCont.ItemID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#else
pos++;
#endif
expectedData.Write(ref pos, (short)itemInCont.Amount);
expectedData.Write(ref pos, (short)itemInCont.X);
expectedData.Write(ref pos, (short)itemInCont.Y);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#else
pos++;
#endif
expectedData.Write(ref pos, m.Serial);
expectedData.Write(ref pos, (short)itemInCont.Hue);
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,161 @@
using System;
using System.Buffers;
using Server.Network;
using Server.Targeting;
using Xunit;
namespace Server.Tests.Network
{
public class TestMultiTarget : MultiTarget
{
public TestMultiTarget(
int multiID,
Point3D offset,
int range = 10,
bool allowGround = true,
TargetFlags flags = TargetFlags.None
) : base(multiID, offset, range, allowGround, flags)
{
}
}
public class TestTarget : Target
{
public TestTarget(
int range,
bool allowGround,
TargetFlags flags
) : base(range, allowGround, flags)
{
}
}
public class TargetPacketsTests
{
[Fact]
public void TestMultiTargetReqHS()
{
var multiID = 0x1024;
var p = new Point3D(1000, 100, 10);
MultiTarget t = new TestMultiTarget(multiID, p);
var data = new MultiTargetReqHS(t).Compile();
Span<byte> expectedData = stackalloc byte[30];
var pos = 0;
expectedData.Write(ref pos, (byte)0x99); // Packet ID
expectedData.Write(ref pos, t.AllowGround);
expectedData.Write(ref pos, t.TargetID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (ushort)0);
#else
pos += 12;
#endif
expectedData.Write(ref pos, (short)t.MultiID);
expectedData.Write(ref pos, (ushort)t.Offset.X);
expectedData.Write(ref pos, (ushort)t.Offset.Y);
expectedData.Write(ref pos, (short)t.Offset.Z);
#if NO_LOCAL_INIT
// Hue (4 bytes)
expectedData.Write(ref pos, 0);
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMultiTargetReq()
{
var multiID = 0x1024;
var p = new Point3D(1000, 100, 10);
MultiTarget t = new TestMultiTarget(multiID, p);
var data = new MultiTargetReq(t).Compile();
Span<byte> expectedData = stackalloc byte[26];
var pos = 0;
expectedData.Write(ref pos, (byte)0x99); // Packet ID
expectedData.Write(ref pos, t.AllowGround);
expectedData.Write(ref pos, t.TargetID);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (ushort)0);
#else
pos += 12;
#endif
expectedData.Write(ref pos, (short)t.MultiID);
expectedData.Write(ref pos, (ushort)t.Offset.X);
expectedData.Write(ref pos, (ushort)t.Offset.Y);
expectedData.Write(ref pos, (short)t.Offset.Z);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestCancelTarget()
{
var data = new CancelTarget().Compile();
Span<byte> expectedData = stackalloc byte[19];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6C); // Packet ID
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (byte)3); // Beneficial / Harmful
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (ushort)0);
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestTargetReq()
{
var t = new TestTarget(10, true, TargetFlags.Beneficial);
var data = new TargetReq(t).Compile();
Span<byte> expectedData = stackalloc byte[19];
var pos = 0;
expectedData.Write(ref pos, (byte)0x6C); // Packet ID
expectedData.Write(ref pos, t.AllowGround);
expectedData.Write(ref pos, t.TargetID);
expectedData.Write(ref pos, (byte)t.Flags);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (ushort)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (byte)0);
expectedData.Write(ref pos, (ushort)0);
#endif
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Buffers;
using Server.Network;
using Server.Prompts;
using Xunit;
namespace Server.Tests.Network
{
internal class TestPrompt : Prompt
{
}
public class UnicodePromptTests
{
[Fact]
public void TestUnicodePrompt()
{
var prompt = new TestPrompt();
var data = new UnicodePrompt(prompt).Compile();
Span<byte> expectedData = stackalloc byte[21];
var pos = 0;
expectedData.Write(ref pos, (byte)0xC2); // Packet ID
expectedData.Write(ref pos, (ushort)0x15); // Length
expectedData.Write(ref pos, prompt.Serial);
expectedData.Write(ref pos, prompt.Serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ulong)0);
expectedData.Write(ref pos, (ushort)0);
#endif
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,198 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using Server.Items;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class VendorBuyPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestVendorBuyContent()
{
var cont = new Container(World.NewItem);
var buyStates = new List<BuyItemState>
{
new BuyItemState("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0),
new BuyItemState("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0),
new BuyItemState("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0)
};
var data = new VendorBuyContent(buyStates).Compile();
Span<byte> expectedData = stackalloc byte[5 + buyStates.Count * 19];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3C); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, (ushort)buyStates.Count); // Count
for (var i = buyStates.Count - 1; i >= 0; i--)
{
var buyState = buyStates[i];
expectedData.Write(ref pos, buyState.MySerial);
expectedData.Write(ref pos, (ushort)buyState.ItemID);
pos++; // ItemID Offset
expectedData.Write(ref pos, (ushort)buyState.Amount);
expectedData.Write(ref pos, (ushort)(i + 1)); // X
expectedData.Write(ref pos, (ushort)1); // Y
expectedData.Write(ref pos, buyState.ContainerSerial);
expectedData.Write(ref pos, (ushort)buyState.Hue);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestVendorBuyContent6017()
{
var cont = new Container(World.NewItem);
var buyStates = new List<BuyItemState>
{
new BuyItemState("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0),
new BuyItemState("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0),
new BuyItemState("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0)
};
var data = new VendorBuyContent6017(buyStates).Compile();
Span<byte> expectedData = stackalloc byte[5 + buyStates.Count * 20];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3C); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, (ushort)buyStates.Count); // Count
for (var i = buyStates.Count - 1; i >= 0; i--)
{
var buyState = buyStates[i];
expectedData.Write(ref pos, buyState.MySerial);
expectedData.Write(ref pos, (ushort)buyState.ItemID);
pos++; // ItemID Offset
expectedData.Write(ref pos, (ushort)buyState.Amount);
expectedData.Write(ref pos, (ushort)(i + 1)); // X
expectedData.Write(ref pos, (ushort)1); // Y
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0); // Grid Location?
#else
pos++;
#endif
expectedData.Write(ref pos, buyState.ContainerSerial);
expectedData.Write(ref pos, (ushort)buyState.Hue);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplayBuyList()
{
var vendor = new Mobile(0x1);
vendor.DefaultMobileInit();
var data = new DisplayBuyList(vendor).Compile();
Span<byte> expectedData = stackalloc byte[7];
var pos = 0;
expectedData.Write(ref pos, (byte)0x24); // Packet ID
expectedData.Write(ref pos, vendor.Serial);
expectedData.Write(ref pos, (ushort)0x30); // Buy gump
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDisplayBuyListHS()
{
var vendor = new Mobile(0x1);
vendor.DefaultMobileInit();
var data = new DisplayBuyListHS(vendor).Compile();
Span<byte> expectedData = stackalloc byte[9];
var pos = 0;
expectedData.Write(ref pos, (byte)0x24); // Packet ID
expectedData.Write(ref pos, vendor.Serial);
expectedData.Write(ref pos, (ushort)0x30); // Buy gump
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (ushort)0);
#endif
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestVendorBuyList()
{
var vendor = new Mobile(0x1);
vendor.DefaultMobileInit();
var cont = new Container(World.NewItem);
var buyStates = new List<BuyItemState>
{
new BuyItemState("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0),
new BuyItemState("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0),
new BuyItemState("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0)
};
var data = new VendorBuyList(vendor, buyStates).Compile();
var length = 8 + buyStates.Sum(state => 6 + state.Description.Length);
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x74); // Packet ID
expectedData.Write(ref pos, (ushort)expectedData.Length); // Length
expectedData.Write(ref pos, Serial.MinusOne); // Vendor Buy Pack Serial or -1
expectedData.Write(ref pos, (byte)buyStates.Count);
for (var i = 0; i < buyStates.Count; i++)
{
var state = buyStates[i];
expectedData.Write(ref pos, state.Price);
var description = state.Description ?? "";
expectedData.Write(ref pos, (byte)Math.Min(255, description.Length + 1));
expectedData.WriteAsciiNull(ref pos, description, 255);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestEndVendorBuy()
{
var vendor = new Mobile(0x1);
vendor.DefaultMobileInit();
var data = new EndVendorBuy(vendor).Compile();
Span<byte> expectedData = stackalloc byte[8];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3B); // Packet ID
expectedData.Write(ref pos, (ushort)0x8); // Length
expectedData.Write(ref pos, vendor.Serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)-);
#endif
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,82 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class VendorSellPacketTests : IClassFixture<ServerFixture>
{
[Fact]
public void TestVendorSellList()
{
var vendor = new Mobile(0x1);
vendor.DefaultMobileInit();
var item1 = new Item(World.NewItem);
var item2 = new Item(World.NewItem) { Name = "Second Item" };
var item3 = new Item(World.NewItem);
var sellStates = new List<SellItemState>
{
new SellItemState(item1, 100, "Item 1"),
new SellItemState(item2, 100000, "Item 2"),
new SellItemState(item3, 1, "Item 3")
};
var data = new VendorSellList(vendor, sellStates).Compile();
var length = 9 + 14 * 3 + sellStates.Sum(
state =>
(string.IsNullOrWhiteSpace(state.Item.Name) ? state.Name ?? "" : state.Item.Name.Trim()).Length
);
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x9E); // Packet ID
expectedData.Write(ref pos, (ushort)length);
expectedData.Write(ref pos, vendor.Serial);
expectedData.Write(ref pos, (ushort)sellStates.Count);
for (var i = 0; i < sellStates.Count; i++)
{
var state = sellStates[i];
expectedData.Write(ref pos, state.Item.Serial);
expectedData.Write(ref pos, (ushort)state.Item.ItemID);
expectedData.Write(ref pos, (ushort)state.Item.Hue);
expectedData.Write(ref pos, (ushort)state.Item.Amount);
expectedData.Write(ref pos, (ushort)state.Price);
var name = string.IsNullOrWhiteSpace(state.Item.Name) ? state.Name ?? "" : state.Item.Name.Trim();
expectedData.Write(ref pos, (ushort)name.Length);
expectedData.WriteAscii(ref pos, name);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestEndVendorSell()
{
var vendor = new Mobile(0x1);
vendor.DefaultMobileInit();
var data = new EndVendorBuy(vendor).Compile();
Span<byte> expectedData = stackalloc byte[8];
var pos = 0;
expectedData.Write(ref pos, (byte)0x3B); // Packet ID
expectedData.Write(ref pos, (ushort)0x08); // Length
expectedData.Write(ref pos, vendor.Serial);
#if NO_LOCAL_INIT
expectedData.Write(ref pos, (byte)0);
#endif
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -0,0 +1,39 @@
using System;
using System.Buffers;
using Server.Network;
namespace Server.Tests.Network
{
public static class AttributeNormalizerUtilities
{
public static void WriteAttribute(this Span<byte> data, ref int pos, int cur, int max, bool normalize)
{
if (normalize && AttributeNormalizer.Enabled && max != 0)
{
var maximum = AttributeNormalizer.Maximum;
data.Write(ref pos, (ushort)maximum);
data.Write(ref pos, (ushort)(cur * maximum / max));
return;
}
data.Write(ref pos, (ushort)max);
data.Write(ref pos, (ushort)cur);
}
public static void WriteReverseAttribute(this Span<byte> data, ref int pos, int cur, int max, bool normalize)
{
if (normalize && AttributeNormalizer.Enabled && max != 0)
{
var maximum = AttributeNormalizer.Maximum;
data.Write(ref pos, (ushort)(cur * maximum / max));
data.Write(ref pos, (ushort)maximum);
return;
}
data.Write(ref pos, (ushort)cur);
data.Write(ref pos, (ushort)max);
}
}
}

View file

@ -0,0 +1,46 @@
using System;
using System.Buffers;
using System.IO.Compression;
using System.Text;
namespace Server.Tests.Network
{
public static class GumpUtilities
{
public const string NoMove = "{ nomove }";
public const string NoClose = "{ noclose }";
public const string NoDispose = "{ nodispose }";
public const string NoResize = "{ noresize }";
public static readonly byte[] NoMoveBuffer = Encoding.ASCII.GetBytes("{ nomove }");
public static readonly byte[] NoCloseBuffer = Encoding.ASCII.GetBytes("{ noclose }");
public static readonly byte[] NoDisposeBuffer = Encoding.ASCII.GetBytes("{ nodispose }");
public static readonly byte[] NoResizeBuffer = Encoding.ASCII.GetBytes("{ noresize }");
public static void WritePacked(this Span<byte> dest, ref int pos, ReadOnlySpan<byte> source)
{
var length = source.Length;
if (length == 0)
{
#if NO_LOCAL_INIT
dest.Write(ref pos, 0);
#else
pos += 4;
#endif
return;
}
var packLength = (ulong)dest.Length - 8;
ZlibError ce = Zlib.Pack(dest.Slice(pos + 8), ref packLength, source, ZlibQuality.Default);
if (ce != ZlibError.Okay)
{
Console.WriteLine("ZLib error: {0} (#{1})", ce, (int)ce);
}
dest.Write(ref pos, (int)(4 + packLength));
dest.Write(ref pos, length);
pos += (int)packLength;
}
}
}

View file

@ -0,0 +1,14 @@
using System;
using Server.Network;
namespace Server.Tests.Network
{
public static class PacketTestUtilities
{
public static Span<byte> Compile(this Packet p) =>
p.Compile(false, out var length).AsSpan(0, length);
public static NetState CreateTestNetState() =>
new NetState(new MockSocket());
}
}