Fix bad serials (#304)
- [X] Fixes bad serial calculations - [X] Tightens HexStrings Bumps release version Updates documentation
This commit is contained in:
parent
b0c1076cb5
commit
ae436cb55f
46 changed files with 76 additions and 51 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue