Creates more tests for outgoing packets (#165)

This commit is contained in:
Kamron Batman 2020-06-26 12:04:29 -07:00 • committed by GitHub
parent d76d60d900
commit 24b1a579f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 2321 additions and 1488 deletions

View file

@ -0,0 +1,552 @@
using System;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using Server.Accounting;
using Server.Network;
using Xunit;
namespace Server.Tests.Network.Packets
{
public class AccountPacketTests : IClassFixture<ServerFixture>
{
internal class TestAccount : IAccount, IComparable<TestAccount>
{
public int TotalGold { get; private set; }
public int TotalPlat { get; private set; }
public bool DepositGold(int amount)
{
TotalGold += amount;
return true;
}
public bool DepositPlat(int amount)
{
TotalPlat += amount;
return true;
}
public bool WithdrawGold(int amount)
{
if (TotalGold - amount < 0) return false;
TotalGold -= amount;
return true;
}
public bool WithdrawPlat(int amount)
{
if (TotalPlat - amount < 0) return false;
TotalPlat -= amount;
return true;
}
public long GetTotalGold() => TotalGold + TotalPlat * 100;
public int CompareTo(TestAccount other) => other == null ? 1 : Username.CompareTo(other.Username);
public int CompareTo(IAccount other) => other == null ? 1 : Username.CompareTo(other.Username);
public string Username { get; set; }
public string Email { get; set; }
public AccessLevel AccessLevel { get; set; }
public int Length { get; }
public int Limit { get; }
public int Count { get; }
private Mobile[] m_Mobiles;
private string m_Password;
public Mobile this[int index]
{
get => m_Mobiles[index];
set => m_Mobiles[index] = value;
}
public void Delete()
{
}
public void SetPassword(string password)
{
m_Password = password;
}
public bool CheckPassword(string password) => m_Password == password;
public TestAccount(Mobile[] mobiles)
{
m_Mobiles = mobiles;
foreach (var mobile in mobiles)
if (mobile != null)
mobile.Account = this;
Length = mobiles.Length;
Count = mobiles.Count(t => t != null);
Limit = mobiles.Length;
}
}
internal class TestConnectionContext : ConnectionContext
{
public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; }
public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; }
}
[Fact]
public void TestChangeCharacter()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var account = new TestAccount(new[] {firstMobile, null, null});
Span<byte> data = new ChangeCharacter(account).Compile();
Span<byte> expectedData = stackalloc byte[5 + account.Length * 60];
int pos = 0;
expectedData[pos++] = 0x81; // Packet ID
((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length
expectedData[pos++] = 1; // Count of non-null characters
expectedData[pos++] = 0;
for (var i = 0; i < account.Length; ++i)
{
Mobile m = account[i];
if (m == null)
{
expectedData.Clear(ref pos, 60);
}
else
{
m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData);
expectedData.Clear(ref pos, 30); // Password (empty)
}
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestClientVersionReq()
{
Span<byte> data = new ClientVersionReq().Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xBD, // Packet ID
0x00, 0x03 // Length
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestDeleteResult()
{
Span<byte> data = new DeleteResult(DeleteResultType.BadRequest).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x85, // Packet ID
(byte)DeleteResultType.BadRequest
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestPopupMessage()
{
Span<byte> data = new PopupMessage(PMMessage.IdleWarning).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x53, // Packet ID
(byte)PMMessage.IdleWarning
};
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(ProtocolChanges.Version70610)]
[InlineData(ProtocolChanges.Version6000)]
public void TestSupportedFeatures(ProtocolChanges protocolChanges)
{
NetState ns = new NetState(new TestConnectionContext
{
RemoteEndPoint = IPEndPoint.Parse("127.0.0.1")
});
ns.ProtocolChanges = protocolChanges;
ns.Account = new TestAccount(new Mobile[0]);
Span<byte> data = new SupportedFeatures(ns).Compile();
Span<byte> expectedData = stackalloc byte[ns.ExtendedSupportedFeatures ? 5 : 3];
expectedData[0] = 0xB9; // Packet ID
var flags = ExpansionInfo.GetFeatures(Expansion.EJ);
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)
((uint)flags).CopyTo(expectedData.Slice(1, 4));
else
((ushort)flags).CopyTo(expectedData.Slice(1, 2));
AssertThat.Equal(data, expectedData);
}
[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;
Span<byte> data = new LoginConfirm(m).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x1B, // Packet ID
0x00, 0x00, 0x00, 0x00, // Serial
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // Body
0x00, 0x00, // X
0x00, 0x00, // Y
0x00, 0x00, // Z
0x00, // Direction
0x00,
0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // Map Width
0x00, 0x00, // Map Height
0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
int pos = 1;
m.Serial.CopyTo(ref pos, expectedData);
pos += 4;
((ushort)m.Body).CopyTo(ref pos, expectedData);
((ushort)m.X).CopyTo(ref pos, expectedData);
((ushort)m.Y).CopyTo(ref pos, expectedData);
((ushort)m.Z).CopyTo(ref pos, expectedData);
expectedData[pos++] = (byte)m.Direction;
pos += 9;
var map = m.Map;
if (map == null || map == Map.Internal)
map = m.LogoutMap;
((ushort)(map?.Width ?? Map.Felucca.Width)).CopyTo(ref pos, expectedData);
((ushort)(map?.Height ?? Map.Felucca.Height)).CopyTo(ref pos, expectedData);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestLoginComplete()
{
Span<byte> data = new LoginComplete().Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x55 // Packet ID
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestCharacterListUpdate()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var account = new TestAccount(new[] {firstMobile, null, null, null, null});
Span<byte> data = new CharacterListUpdate(account).Compile();
Span<byte> expectedData = stackalloc byte[4 + account.Length * 60];
int pos = 0;
expectedData[pos++] = 0x86; // Packet ID
((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length
int highSlot = -1;
for (int i = account.Length - 1; i >= 0; i--)
if (account[i] != null)
{
highSlot = i;
break;
}
int count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5);
expectedData[pos++] = (byte)count;
for (int i = 0; i < count; i++)
{
var m = account[i];
if (m != null)
{
m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData);
expectedData.Clear(ref pos, 30);
}
else
{
expectedData.Clear(ref pos, 60);
}
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestCharacterList()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var account = new TestAccount(new[] {firstMobile, null, null, null, null});
var info = new[]
{
new CityInfo("Test City", "Test Building", 50, 100, 10, -10)
};
Span<byte> data = new CharacterList(account, info).Compile();
Span<byte> expectedData = stackalloc byte[11 + account.Length * 60 + info.Length * 89];
int pos = 0;
expectedData[pos++] = 0xA9; // Packet ID
((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length
int highSlot = -1;
for (int i = account.Length - 1; i >= 0; i--)
if (account[i] != null)
{
highSlot = i;
break;
}
int count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5);
expectedData[pos++] = (byte)count;
for (int i = 0; i < count; i++)
{
var m = account[i];
if (m != null)
{
m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData);
expectedData.Clear(ref pos, 30);
}
else
{
expectedData.Clear(ref pos, 60);
}
}
expectedData[pos++] = (byte)info.Length;
for (int i = 0; i < info.Length; i++)
{
var ci = info[i];
expectedData[pos++] = (byte)i;
ci.City.CopyASCIIFixedTo(ref pos, 32, expectedData);
ci.Building.CopyASCIIFixedTo(ref pos, 32, expectedData);
ci.X.CopyTo(ref pos, expectedData);
ci.Y.CopyTo(ref pos, expectedData);
ci.Z.CopyTo(ref pos, expectedData);
ci.Map.MapID.CopyTo(ref pos, expectedData);
ci.Description.CopyTo(ref pos, expectedData);
expectedData.Clear(ref pos, 4);
}
var flags = ExpansionInfo.GetInfo(Expansion.EJ).CharacterListFlags;
if (count > 6)
flags |= CharacterListFlags.SeventhCharacterSlot |
CharacterListFlags.SixthCharacterSlot; // 7th Character Slot
else if (count == 6)
flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot
else if (account.Limit == 1)
flags |= CharacterListFlags.SlotLimit &
CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character
((int)flags).CopyTo(ref pos, expectedData);
((short)-1).CopyTo(ref pos, expectedData);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestCharacterListOld()
{
var firstMobile = new Mobile(0x1);
firstMobile.DefaultMobileInit();
firstMobile.Name = "Test Mobile";
var account = new TestAccount(new[] {firstMobile, null, null, null, null});
var info = new[]
{
new CityInfo("Test City", "Test Building", 50, 100, 10, -10)
};
Span<byte> data = new CharacterListOld(account, info).Compile();
Span<byte> expectedData = stackalloc byte[9 + account.Length * 60 + info.Length * 63];
int pos = 0;
expectedData[pos++] = 0xA9; // Packet ID
((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length
int highSlot = -1;
for (int i = account.Length - 1; i >= 0; i--)
if (account[i] != null)
{
highSlot = i;
break;
}
int count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5);
expectedData[pos++] = (byte)count;
for (int i = 0; i < count; i++)
{
var m = account[i];
if (m != null)
{
m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData);
expectedData.Clear(ref pos, 30);
}
else
{
expectedData.Clear(ref pos, 60);
}
}
expectedData[pos++] = (byte)info.Length;
for (int i = 0; i < info.Length; i++)
{
var ci = info[i];
expectedData[pos++] = (byte)i;
ci.City.CopyASCIIFixedTo(ref pos, 31, expectedData);
ci.Building.CopyASCIIFixedTo(ref pos, 31, expectedData);
}
var flags = ExpansionInfo.GetInfo(Expansion.EJ).CharacterListFlags;
if (count > 6)
flags |= CharacterListFlags.SeventhCharacterSlot |
CharacterListFlags.SixthCharacterSlot; // 7th Character Slot
else if (count == 6)
flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot
else if (account.Limit == 1)
flags |= CharacterListFlags.SlotLimit &
CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character
((int)flags).CopyTo(ref pos, expectedData);
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestAccountLoginRej()
{
var reason = ALRReason.BadComm;
Span<byte> data = new AccountLoginRej(reason).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x82, // Packet ID
(byte)reason
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestAccountLoginAck()
{
var info = new[]
{
new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1"))
};
Span<byte> data = new AccountLoginAck(info).Compile();
Span<byte> expectedData = stackalloc byte[6 + info.Length * 40];
int pos = 0;
expectedData[pos++] = 0xA8; // Packet ID
((ushort)expectedData.Length).CopyTo(ref pos, expectedData);
expectedData[pos++] = 0x5D; // Unknown
((ushort)info.Length).CopyTo(ref pos, expectedData);
for (int i = 0; i < info.Length; i++)
{
var si = info[i];
((ushort)i).CopyTo(ref pos, expectedData);
si.Name.CopyASCIIFixedTo(ref pos, 32, expectedData);
expectedData[pos++] = (byte)si.FullPercent;
expectedData[pos++] = (byte)si.TimeZone;
Utility.GetAddressValue(si.Address.Address).CopyTo(ref pos, expectedData);
}
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestPlayServerAck()
{
var si = new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1"));
Span<byte> data = new PlayServerAck(si).Compile();
var addr = Utility.GetAddressValue(si.Address.Address);
Span<byte> expectedData = stackalloc byte[]
{
0x8C, // Packet ID
(byte)addr, // IP Address in LE
(byte)(addr >> 8),
(byte)(addr >> 16),
(byte)(addr >> 24),
0x00, 0x00, // Port
0x00, 0x00, 0x00, 0x00 // Auth ID
};
((ushort)si.Address.Port).CopyTo(expectedData.Slice(5, 2));
(-1).CopyTo(expectedData.Slice(7, 4)); // Auth ID
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -13,7 +13,7 @@ namespace Server.Tests.Network.Packets
Span<byte> expectedData = stackalloc byte[]
{
0xBA, // Packet
0xBA, // Packet ID
0x00, // Command
0xFF, 0xFF, // X
0xFF, 0xFF // Y
@ -32,7 +32,7 @@ namespace Server.Tests.Network.Packets
Span<byte> expectedData = stackalloc byte[]
{
0xBA, // Packet
0xBA, // Packet ID
0x01, // Command
0x00, 0x00, // X
0x00, 0x00 // Y
@ -54,7 +54,7 @@ namespace Server.Tests.Network.Packets
Span<byte> expectedData = stackalloc byte[]
{
0xBA, // Packet
0xBA, // Packet ID
0x00, // Command
0x00, 0x00, // X
0x00, 0x00, // Y
@ -77,7 +77,7 @@ namespace Server.Tests.Network.Packets
Span<byte> expectedData = stackalloc byte[]
{
0xBA, // Packet
0xBA, // Packet ID
0x01, // Command
0x00, 0x00, // X
0x00, 0x00, // Y

View file

@ -0,0 +1,107 @@
using System;
using Server.Network;
using Xunit;
namespace Server.Tests.Network.Packets
{
public class AttributeNormalizerTests
{
[Fact]
public void TestAttributeNormalizerEnabled()
{
AttributeNormalizer.Enabled = true;
AttributeNormalizer.Maximum = 25;
PacketWriter stream = new PacketWriter(4);
const short cur = 50;
const short max = 100;
AttributeNormalizer.Write(stream, cur, max);
Span<byte> expectedData = stackalloc byte[]
{
0x00, 0x00, // Maximum Normalized
0x00, 0x00 // Current Normalized
};
((short)AttributeNormalizer.Maximum).CopyTo(expectedData.Slice(0, 2));
((short)(cur * 25 / max)).CopyTo(expectedData.Slice(2, 2));
AssertThat.Equal(stream.ToArray(), expectedData);
}
[Fact]
public void TestAttributeNormalizerReversedEnabled()
{
AttributeNormalizer.Enabled = true;
AttributeNormalizer.Maximum = 25;
PacketWriter stream = new PacketWriter(4);
const short cur = 50;
const short max = 100;
AttributeNormalizer.WriteReverse(stream, cur, max);
Span<byte> expectedData = stackalloc byte[]
{
0x00, 0x00, // Current Normalized
0x00, 0x00 // Maximum Normalized
};
((short)AttributeNormalizer.Maximum).CopyTo(expectedData.Slice(2, 2));
((short)(cur * 25 / max)).CopyTo(expectedData.Slice(0, 2));
AssertThat.Equal(stream.ToArray(), expectedData);
}
[Fact]
public void TestAttributeNormalizerDisabled()
{
AttributeNormalizer.Enabled = false;
PacketWriter stream = new PacketWriter(4);
const short cur = 50;
const short max = 100;
AttributeNormalizer.Write(stream, cur, max);
Span<byte> expectedData = stackalloc byte[]
{
0x00, 0x00, // Maximum
0x00, 0x00 // Current
};
max.CopyTo(expectedData.Slice(0, 2));
cur.CopyTo(expectedData.Slice(2, 2));
AssertThat.Equal(stream.ToArray(), expectedData);
}
[Fact]
public void TestAttributeNormalizerReversedDisabled()
{
AttributeNormalizer.Enabled = false;
PacketWriter stream = new PacketWriter(4);
const short cur = 50;
const short max = 100;
AttributeNormalizer.WriteReverse(stream, cur, max);
Span<byte> expectedData = stackalloc byte[]
{
0x00, 0x00, // Current
0x00, 0x00 // Maximum
};
max.CopyTo(expectedData.Slice(2, 2));
cur.CopyTo(expectedData.Slice(0, 2));
AssertThat.Equal(stream.ToArray(), expectedData);
}
}
}

View file

@ -0,0 +1,66 @@
using System;
using Server.Network;
using Xunit;
namespace Server.Tests.Network.Packets
{
public class CombatPacketTests
{
[Fact]
public void TestSwing()
{
Serial attacker = 0x1000;
Serial defender = 0x2000;
Span<byte> data = new Swing(attacker, defender).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x2F, // Packet ID
0x00, // Unknown
0x00, 0x00, 0x00, 0x00, // Attacker
0x00, 0x00, 0x00, 0x00, // Defender
};
attacker.CopyTo(expectedData.Slice(2, 4));
defender.CopyTo(expectedData.Slice(6, 4));
AssertThat.Equal(data, expectedData);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void TestSetWarMode(bool warmode)
{
Span<byte> data = new SetWarMode(warmode).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x72, // Packet ID
warmode ? (byte)0x01 : (byte)0x00, // Mode
0x00, 0x32, 0x00 // Unknown
};
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestChangeCombatant()
{
Serial combatant = 0x1000;
Span<byte> data = new ChangeCombatant(combatant).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xAA, // Packet ID
0x00, 0x00, 0x00, 0x00 // Combatant
};
combatant.CopyTo(expectedData.Slice(1, 4));
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -15,11 +15,11 @@ namespace Server.Tests.Network.Packets
var m = new Mobile(0x1);
m.DefaultMobileInit();
Span<byte> data = new DamagePacketOld(m, inputAmount).Compile();
Span<byte> data = new DamagePacketOld(m.Serial, inputAmount).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xBF, // Packet
0xBF, // Packet ID
0x00, 0x0B, // Length
0x00, 0x22, // Sub-packet
0x01, // Command
@ -36,16 +36,16 @@ namespace Server.Tests.Network.Packets
[InlineData(-5, 0)]
[InlineData(1024, 1024)]
[InlineData(100000, 0xFFFF)]
public void TestDamagePacket(int inputAmount, ushort expectedAmount)
public void TestDamage(int inputAmount, ushort expectedAmount)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
Span<byte> data = new DamagePacket(m, inputAmount).Compile();
Span<byte> data = new DamagePacket(m.Serial, inputAmount).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0x0B, // Packet
0x0B, // Packet ID
0x00, 0x00, 0x00, 0x00, // Mobile Serial
0x00, 0x00 // Amount
};

View file

@ -10,7 +10,7 @@ namespace Server.Tests.Network.Packets
[Fact]
public void TestDisplayHuePicker()
{
ushort itemID = 0xFF01;
const ushort itemID = 0xFF01;
var huePicker = new HuePicker(itemID);
Span<byte> data = new DisplayHuePicker(huePicker).Compile();

View file

@ -13,7 +13,7 @@ namespace Server.Tests.Network.Packets
Span<byte> expectedData = stackalloc byte[]
{
0xBF, // Packet
0xBF, // Packet ID
0x00, 0x29, // Length
0x00, 0x18, // Sub-packet
0x00, 0x00, 0x00, 0x04, // 4 maps
@ -42,5 +42,21 @@ namespace Server.Tests.Network.Packets
AssertThat.Equal(data, expectedData);
}
[Fact]
public void TestMapChange()
{
Span<byte> data = new MapChange(Map.Felucca).Compile();
Span<byte> expectedData = stackalloc byte[]
{
0xBF, // Packet ID
0x00, 0x06, // Length
0x00, 0x08, // Sub-packet
0x00, // Felucca
};
AssertThat.Equal(data, expectedData);
}
}
}

View file

@ -39,7 +39,7 @@ namespace Server.Tests.Network.Packets
firstCont.Serial.CopyTo(ref pos, expectedData);
secondCont.Serial.CopyTo(ref pos, expectedData);
pos++;
name.CopyASCIITo(ref pos, 30, expectedData);
name.CopyASCIIFixedTo(ref pos, 30, expectedData);
AssertThat.Equal(data, expectedData);
}

View file

@ -80,5 +80,24 @@ namespace Server.Tests.Network.Packets
{
pos += Encoding.ASCII.GetBytes(str.AsSpan(0, Math.Min(str.Length, max)), bytes.Slice(pos));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyASCIIFixedTo(this string str, ref int pos, int max, Span<byte> bytes)
{
int bytesWritten = Encoding.ASCII.GetBytes(str.AsSpan(0, Math.Min(str.Length, max)), bytes.Slice(pos));
// This is not needed in the current stackalloc implementation, but not guaranteed in the future.
if (bytesWritten < max)
bytes.Slice(pos + bytesWritten, max - bytesWritten).Clear();
pos += max;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clear(this Span<byte> span, ref int pos, int amount)
{
span.Slice(pos, amount).Clear();
pos += amount;
}
}
}