diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs index 17a0241c0..c82fd5c12 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs @@ -1,8 +1,5 @@ using System; -using System.Buffers; -using System.Linq; using System.Net; -using Server.Accounting; using Server.Network; using Xunit; @@ -15,85 +12,57 @@ namespace Server.Tests.Network { var firstMobile = new Mobile(0x1); firstMobile.DefaultMobileInit(); - firstMobile.Name = "Test Mobile"; + firstMobile.RawName = "Test Mobile"; - var account = new TestAccount(new[] { firstMobile, null, null }); + var secondMobile = new Mobile(0x2); + secondMobile.DefaultMobileInit(); + secondMobile.RawName = null; - var data = new ChangeCharacter(account).Compile(); + var account = new MockAccount(new[] { firstMobile, null, secondMobile }); + var oldPacket = new ChangeCharacter(account).Compile(); - Span expectedData = stackalloc byte[5 + account.Length * 60]; - var pos = 0; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendChangeCharacter(ns, account); - expectedData.Write(ref pos, (byte)0x81); // Packet ID - expectedData.Write(ref pos, (ushort)expectedData.Length); // Length - expectedData.Write(ref pos, (byte)1); // Count of non-null characters - expectedData.Write(ref pos, (byte)0); - - for (var i = 0; i < account.Length; ++i) - { - var m = account[i]; - if (m == null) - { -#if NO_LOCAL_INIT - expectedData.Clear(ref pos, 60); -#else - pos += 60; -#endif - } - else - { - expectedData.WriteAsciiFixed(ref pos, m.Name, 30); -#if NO_LOCAL_INIT - expectedData.Clear(ref pos, 30); // Password (empty) -#else - pos += 30; -#endif - } - } - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), oldPacket); } [Fact] public void TestClientVersionReq() { - var data = new ClientVersionReq().Compile(); + var expected = new ClientVersionReq().Compile(); - Span expectedData = stackalloc byte[] - { - 0xBD, // Packet ID - 0x00, 0x03 // Length - }; + using var ns = PacketTestUtilities.CreateTestNetState(); - AssertThat.Equal(data, expectedData); + Packets.SendClientVersionRequest(ns); + + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] public void TestDeleteResult() { - var data = new DeleteResult(DeleteResultType.BadRequest).Compile(); + var expected = new DeleteResult(DeleteResultType.BadRequest).Compile(); - Span expectedData = stackalloc byte[2]; - var pos = 0; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendCharacterDeleteResult(ns, DeleteResultType.BadRequest); - expectedData.Write(ref pos, (byte)0x85); // Packet ID - expectedData.Write(ref pos, (byte)DeleteResultType.BadRequest); - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] public void TestPopupMessage() { - var data = new PopupMessage(PMMessage.IdleWarning).Compile(); + var expected = new PopupMessage(PMMessage.LoginSyncError).Compile(); - Span expectedData = stackalloc byte[2]; - var pos = 0; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendPopupMessage(ns, PMMessage.LoginSyncError); - expectedData.Write(ref pos, (byte)0x53); // Packet ID - expectedData.Write(ref pos, (byte)PMMessage.IdleWarning); - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Theory] @@ -105,48 +74,17 @@ namespace Server.Tests.Network firstMobile.DefaultMobileInit(); firstMobile.Name = "Test Mobile"; - var account = new TestAccount(new[] { firstMobile, null, null, null, null }); + var account = new MockAccount(new[] { firstMobile, null, null, null, null }); - var ns = new NetState(null) - { - Account = account, - ProtocolChanges = protocolChanges - }; + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.Account = account; + ns.ProtocolChanges = protocolChanges; - var data = new SupportedFeatures(ns).Compile(); + var expected = new SupportedFeatures(ns).Compile(); + Packets.SendSupportedFeature(ns); - Span expectedData = stackalloc byte[ns.ExtendedSupportedFeatures ? 5 : 3]; - var pos = 0; - - expectedData[pos++] = 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) - { - expectedData.Write(ref pos, (uint)flags); - } - else - { - expectedData.Write(ref pos, (ushort)flags); - } - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] @@ -161,59 +99,25 @@ namespace Server.Tests.Network m.Direction = Direction.Down; m.LogoutMap = Map.Felucca; - var data = new LoginConfirm(m).Compile(); + var expected = new LoginConfirm(m).Compile(); - Span expectedData = stackalloc byte[37]; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendLoginConfirmation(ns, m); - var pos = 0; - expectedData.Write(ref pos, (byte)0x1B); // Packet ID - expectedData.Write(ref pos, m.Serial); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, 0); -#else - pos += 4; -#endif - - expectedData.Write(ref pos, (ushort)m.Body); - expectedData.Write(ref pos, (ushort)m.X); - expectedData.Write(ref pos, (ushort)m.Y); - expectedData.Write(ref pos, (short)m.Z); - expectedData.Write(ref pos, (byte)m.Direction); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (byte)0); -#else - pos++; -#endif - expectedData.Write(ref pos, 0xFFFFFFFF); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, 0); -#else - pos += 4; -#endif - var map = m.Map; - - if (map == null || map == Map.Internal) - { - map = m.LogoutMap; - } - - expectedData.Write(ref pos, (ushort)(map?.Width ?? Map.Felucca.Width)); - expectedData.Write(ref pos, (ushort)(map?.Height ?? Map.Felucca.Height)); - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] public void TestLoginComplete() { - var data = new LoginComplete().Compile(); + var expected = new LoginComplete().Compile(); - Span expectedData = stackalloc byte[] - { - 0x55 // Packet ID - }; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendLoginComplete(ns); - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] @@ -221,175 +125,43 @@ namespace Server.Tests.Network { var firstMobile = new Mobile(0x1); firstMobile.DefaultMobileInit(); - firstMobile.Name = "Test Mobile"; + firstMobile.RawName = "Test Mobile"; - var account = new TestAccount(new[] { firstMobile, null, null, null, null }); + var acct = new MockAccount(new[] { null, firstMobile, null, null, null }); - var data = new CharacterListUpdate(account).Compile(); + var expected = new CharacterListUpdate(acct).Compile(); - Span expectedData = stackalloc byte[4 + account.Length * 60]; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendCharacterListUpdate(ns, acct); - var pos = 0; - expectedData.Write(ref pos, (byte)0x86); // Packet ID - expectedData.Write(ref pos, (ushort)expectedData.Length); // Length - - var highSlot = -1; - for (var i = account.Length - 1; i >= 0; i--) - { - if (account[i] != null) - { - highSlot = i; - break; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5); - expectedData.Write(ref pos, (byte)count); - - for (var i = 0; i < count; i++) - { - var m = account[i]; - - if (m != null) - { - expectedData.WriteAsciiFixed(ref pos, m.Name, 30); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, 0); - expectedData.Write(ref pos, (ushort)0); -#else - pos += 30; -#endif - } - else - { -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, 0); -#else - pos += 60; -#endif - } - } - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] - public void TestCharacterList() + public void TestCharacterList70130() { var firstMobile = new Mobile(0x1); firstMobile.DefaultMobileInit(); firstMobile.Name = "Test Mobile"; - var account = new TestAccount(new[] { firstMobile, null, null, null, null }); + var acct = new MockAccount(new[] { null, firstMobile, null, null, null }); var info = new[] { new CityInfo("Test City", "Test Building", 50, 100, 10, -10) }; - var data = new CharacterList(account, info).Compile(); + var expected = new CharacterList(acct, info).Compile(); - Span expectedData = stackalloc byte[11 + account.Length * 60 + info.Length * 89]; - var pos = 0; + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.CityInfo = info; + ns.Account = acct; + ns.ProtocolChanges = ProtocolChanges.Version70130; - expectedData.Write(ref pos, (byte)0xA9); // Packet ID - expectedData.Write(ref pos, (ushort)expectedData.Length); // Length + Packets.SendCharacterList(ns); - var highSlot = -1; - for (var i = account.Length - 1; i >= 0; i--) - { - if (account[i] != null) - { - highSlot = i; - break; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5); - expectedData.Write(ref pos, (byte)count); - - for (var i = 0; i < count; i++) - { - var m = account[i]; - if (m != null) - { - expectedData.WriteAsciiFixed(ref pos, m.Name, 30); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, 0); - expectedData.Write(ref pos, (ushort)0); -#else - pos += 30; -#endif - } - else - { -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, 0); -#else - pos += 60; -#endif - } - } - - expectedData.Write(ref pos, (byte)info.Length); - - for (var i = 0; i < info.Length; i++) - { - var ci = info[i]; - expectedData.Write(ref pos, (byte)i); - expectedData.WriteAsciiFixed(ref pos, ci.City, 32); - expectedData.WriteAsciiFixed(ref pos, ci.Building, 32); - expectedData.Write(ref pos, ci.X); - expectedData.Write(ref pos, ci.Y); - expectedData.Write(ref pos, ci.Z); - expectedData.Write(ref pos, ci.Map.MapID); - expectedData.Write(ref pos, ci.Description); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, 0); -#else - pos += 4; -#endif - } - - 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 - } - - expectedData.Write(ref pos, (int)flags); - expectedData.Write(ref pos, (short)-1); - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] @@ -399,110 +171,35 @@ namespace Server.Tests.Network firstMobile.DefaultMobileInit(); firstMobile.Name = "Test Mobile"; - var account = new TestAccount(new[] { firstMobile, null, null, null, null }); + var acct = new MockAccount(new[] { null, firstMobile, null, null, null }); var info = new[] { new CityInfo("Test City", "Test Building", 50, 100, 10, -10) }; - var data = new CharacterListOld(account, info).Compile(); + var expected = new CharacterListOld(acct, info).Compile(); - Span expectedData = stackalloc byte[9 + account.Length * 60 + info.Length * 63]; + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.CityInfo = info; + ns.Account = acct; - var pos = 0; - expectedData.Write(ref pos, (byte)0xA9); // Packet ID - expectedData.Write(ref pos, (ushort)expectedData.Length); // Length + Packets.SendCharacterList(ns); - var highSlot = -1; - for (var i = account.Length - 1; i >= 0; i--) - { - if (account[i] != null) - { - highSlot = i; - break; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5); - expectedData.Write(ref pos, (byte)count); - - for (var i = 0; i < count; i++) - { - var m = account[i]; - if (m != null) - { - expectedData.WriteAsciiFixed(ref pos, m.Name, 30); -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, 0); - expectedData.Write(ref pos, (ushort)0); -#else - pos += 30; -#endif - } - else - { -#if NO_LOCAL_INIT - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, (ulong)0); - expectedData.Write(ref pos, 0); -#else - pos += 60; -#endif - } - } - - expectedData.Write(ref pos, (byte)info.Length); - - for (var i = 0; i < info.Length; i++) - { - var ci = info[i]; - expectedData.Write(ref pos, (byte)i); - expectedData.WriteAsciiFixed(ref pos, ci.City, 31); - expectedData.WriteAsciiFixed(ref pos, ci.Building, 31); - } - - 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 - } - - expectedData.Write(ref pos, (int)flags); - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] public void TestAccountLoginRej() { var reason = ALRReason.BadComm; - var data = new AccountLoginRej(reason).Compile(); + var expected = new AccountLoginRej(reason).Compile(); - Span expectedData = stackalloc byte[2]; - var pos = 0; + using var ns = PacketTestUtilities.CreateTestNetState(); + Packets.SendAccountLoginRejected(ns, reason); - expectedData.Write(ref pos, (byte)0x82); // Packet ID - expectedData.Write(ref pos, (byte)reason); - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } [Fact] @@ -513,136 +210,31 @@ namespace Server.Tests.Network new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1")) }; - var data = new AccountLoginAck(info).Compile(); + var expected = new AccountLoginAck(info).Compile(); - Span expectedData = stackalloc byte[6 + info.Length * 40]; + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.ServerInfo = info; - var pos = 0; - expectedData.Write(ref pos, (byte)0xA8); // Packet ID - expectedData.Write(ref pos, (ushort)expectedData.Length); - expectedData.Write(ref pos, (byte)0x5D); // Unknown - expectedData.Write(ref pos, (ushort)info.Length); + Packets.SendAccountLoginAck(ns); - for (var i = 0; i < info.Length; i++) - { - var si = info[i]; - expectedData.Write(ref pos, (ushort)i); - expectedData.WriteAsciiFixed(ref pos, si.Name, 32); - expectedData.Write(ref pos, (byte)si.FullPercent); - expectedData.Write(ref pos, (byte)si.TimeZone); - expectedData.Write(ref pos, Utility.GetAddressValue(si.Address.Address)); - } - - AssertThat.Equal(data, expectedData); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(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 data = new PlayServerAck(si).Compile(); + var expected = new PlayServerAck(si, authId).Compile(); - var addr = Utility.GetAddressValue(si.Address.Address); + using var ns = PacketTestUtilities.CreateTestNetState(); - Span expectedData = stackalloc byte[11]; - var pos = 0; + Packets.SendPlayServerAck(ns, si, authId); - expectedData.Write(ref pos, (byte)0x8C); // Packet ID - expectedData.WriteLE(ref pos, addr); - expectedData.Write(ref pos, (ushort)si.Address.Port); - expectedData.Write(ref pos, -1); // Auth ID - - AssertThat.Equal(data, expectedData); - } - - internal class TestAccount : IAccount, IComparable - { - private readonly Mobile[] m_Mobiles; - private string m_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; - } - - 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(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; } - - 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 int CompareTo(TestAccount other) => other == null ? 1 : Username.CompareTo(other.Username); + ns.SendPipe.Reader.TryRead(out var buffer); + AssertThat.Equal(buffer.GetSpan(0), expected); } } } diff --git a/Projects/Server/Network/Packets/Old Packets/AccountPackets.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPackets.cs similarity index 76% rename from Projects/Server/Network/Packets/Old Packets/AccountPackets.cs rename to Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPackets.cs index 959a39696..a2f9d6aab 100644 --- a/Projects/Server/Network/Packets/Old Packets/AccountPackets.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPackets.cs @@ -1,52 +1,9 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AccountPackets.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - using System; using Server.Accounting; +using Server.Network; -namespace Server.Network +namespace Server.Tests.Network { - public enum ALRReason : byte - { - Invalid = 0x00, - InUse = 0x01, - Blocked = 0x02, - BadPass = 0x03, - Idle = 0xFE, - BadComm = 0xFF - } - - public enum PMMessage : byte - { - CharNoExist = 1, - CharExists = 2, - CharInWorld = 5, - LoginSyncError = 6, - IdleWarning = 7 - } - - public enum DeleteResultType - { - PasswordInvalid, - CharNotExist, - CharBeingPlayed, - CharTooYoung, - CharQueued, - BadRequest - } - public sealed class ChangeCharacter : Packet { public ChangeCharacter(IAccount a) : base(0x81) @@ -68,18 +25,10 @@ namespace Server.Network for (var i = 0; i < a.Length; ++i) { + var m = a[i]; if (a[i] != null) { - var name = a[i].Name; - - if (name == null) - { - name = "-null-"; - } - else if ((name = name.Trim()).Length == 0) - { - name = "-empty-"; - } + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); Stream.WriteAsciiFixed(name, 30); Stream.Fill(30); // password @@ -92,9 +41,6 @@ namespace Server.Network } } - /// - /// Asks the client for it's version - /// public sealed class ClientVersionReq : Packet { public ClientVersionReq() : base(0xBD) @@ -125,8 +71,6 @@ namespace Server.Network { var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; - flags |= Value; - if (ns.Account.Limit >= 6) { flags |= FeatureFlags.LiveAccount; @@ -151,10 +95,6 @@ namespace Server.Network Stream.Write((ushort)flags); } } - - public static FeatureFlags Value { get; set; } - - public static SupportedFeatures Instantiate(NetState ns) => new SupportedFeatures(ns); } public sealed class LoginConfirm : Packet @@ -189,8 +129,6 @@ namespace Server.Network public sealed class LoginComplete : Packet { - public static readonly Packet Instance = SetStatic(new LoginComplete()); - public LoginComplete() : base(0x55, 1) { } @@ -222,7 +160,8 @@ namespace Server.Network if (m != null) { - Stream.WriteAsciiFixed(m.Name, 30); + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + Stream.WriteAsciiFixed(name, 30); Stream.Fill(30); // password } else @@ -300,12 +239,10 @@ namespace Server.Network CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character } - Stream.Write((int)(flags | AdditionalFlags)); // Additional Flags + Stream.Write((int)flags); // Additional Flags Stream.Write((short)-1); } - - public static CharacterListFlags AdditionalFlags { get; set; } } public sealed class CharacterListOld : Packet @@ -330,9 +267,11 @@ namespace Server.Network for (var i = 0; i < count; ++i) { - if (a[i] != null) + var m = a[i]; + if (m != null) { - Stream.WriteAsciiFixed(a[i].Name, 30); + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + Stream.WriteAsciiFixed(name, 30); Stream.Fill(30); // password } else @@ -369,7 +308,7 @@ namespace Server.Network CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character } - Stream.Write((int)(flags | CharacterList.AdditionalFlags)); // Additional Flags + Stream.Write((int)flags); // Additional Flags } } @@ -406,9 +345,7 @@ namespace Server.Network public sealed class PlayServerAck : Packet { - internal static int m_AuthID = -1; - - public PlayServerAck(ServerInfo si) : base(0x8C, 11) + public PlayServerAck(ServerInfo si, int authId) : base(0x8C, 11) { var addr = Utility.GetAddressValue(si.Address.Address); @@ -418,7 +355,7 @@ namespace Server.Network Stream.Write((byte)(addr >> 24)); Stream.Write((short)si.Address.Port); - Stream.Write(m_AuthID); + Stream.Write(authId); } } } diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/GumpPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/GumpPacketTests.cs index ac1942f7f..8183fe941 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/GumpPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/GumpPacketTests.cs @@ -2,7 +2,6 @@ using System; using System.Buffers; using System.Collections.Generic; using System.Linq; -using System.Net; using Server.Gumps; using Server.Network; using Xunit; diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs index 2dcd6cc37..9056db768 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MobilePacketTests.cs @@ -1,6 +1,5 @@ using System; using System.Buffers; -using System.Net; using Server.Network; using Xunit; diff --git a/Projects/Server.Tests/Network/Packets/Utilities/MockAccount.cs b/Projects/Server.Tests/Network/Packets/Utilities/MockAccount.cs new file mode 100644 index 000000000..7bd4a94f2 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Utilities/MockAccount.cs @@ -0,0 +1,95 @@ +using System; +using System.Linq; +using Server.Accounting; + +namespace Server.Tests.Network +{ + public class MockAccount : IAccount, IComparable + { + private readonly Mobile[] m_Mobiles; + private string m_Password; + + public MockAccount(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; + } + + 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(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; } + + 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 int CompareTo(MockAccount other) => other == null ? 1 : Username.CompareTo(other.Username); + } +} diff --git a/Projects/Server.Tests/Network/Packets/Utilities/MockSocket.cs b/Projects/Server.Tests/Network/Packets/Utilities/MockSocket.cs new file mode 100644 index 000000000..ef6201638 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Utilities/MockSocket.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; +using Server.Network; + +namespace Server.Tests.Network +{ + public class MockSocket : ISocket + { + public EndPoint LocalEndPoint { get; set; } + public EndPoint RemoteEndPoint { get; set; } + public Task SendAsync(IList> buffers, SocketFlags flags) => Task.Run(() => 0); + + public Task ReceiveAsync(IList> buffers, SocketFlags flags) => Task.Run(() => 0); + + public void Shutdown(SocketShutdown how) + { + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs b/Projects/Server.Tests/Network/Packets/Utilities/PacketTestUtilities.cs similarity index 71% rename from Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs rename to Projects/Server.Tests/Network/Packets/Utilities/PacketTestUtilities.cs index 809d72140..b02bdffb4 100644 --- a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs +++ b/Projects/Server.Tests/Network/Packets/Utilities/PacketTestUtilities.cs @@ -1,13 +1,14 @@ using System; -using System.Runtime.CompilerServices; using Server.Network; namespace Server.Tests.Network { public static class PacketTestUtilities { - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span Compile(this Packet p) => p.Compile(false, out var length).AsSpan(0, length); + + public static NetState CreateTestNetState() => + new NetState(new MockSocket()); } } diff --git a/Projects/Server/Buffers/CircularBufferReader.cs b/Projects/Server/Buffers/CircularBufferReader.cs index 711cbed17..3b7198cd2 100644 --- a/Projects/Server/Buffers/CircularBufferReader.cs +++ b/Projects/Server/Buffers/CircularBufferReader.cs @@ -35,6 +35,10 @@ namespace Server.Network { } + public CircularBufferReader(CircularBuffer buffer) : this(buffer.GetSpan(0), buffer.GetSpan(1)) + { + } + public CircularBufferReader(ReadOnlySpan first, ReadOnlySpan second) { _first = first; diff --git a/Projects/Server/Buffers/CircularBufferWriter.cs b/Projects/Server/Buffers/CircularBufferWriter.cs index 5ec3742ee..f67fb88c6 100644 --- a/Projects/Server/Buffers/CircularBufferWriter.cs +++ b/Projects/Server/Buffers/CircularBufferWriter.cs @@ -29,6 +29,10 @@ namespace System.Buffers public int Length { get; } public int Position { get; private set; } + public CircularBufferWriter(CircularBuffer buffer) : this(buffer.GetSpan(0), buffer.GetSpan(1)) + { + } + public CircularBufferWriter(Span first, Span second) { _first = first; @@ -153,6 +157,34 @@ namespace System.Buffers } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLE(int value) + { + if (Position < _first.Length) + { + if (!BinaryPrimitives.TryWriteInt32LittleEndian(_first.Slice(Position), value)) + { + // Not enough space. Split the spans + Write((byte)(value >> 24)); + Write((byte)(value >> 16)); + Write((byte)(value >> 8)); + Write((byte)value); + } + else + { + Position += 4; + } + } + else if (BinaryPrimitives.TryWriteInt32LittleEndian(_second.Slice(Position - _first.Length), value)) + { + Position += 4; + } + else + { + throw new OutOfMemoryException(); + } + } + /// /// Writes a 4-byte unsigned integer value to the underlying stream. /// diff --git a/Projects/Server/Buffers/SpanWriteExtensions.cs b/Projects/Server/Buffers/SpanWriteExtensions.cs index 3746d8e10..a6d99a870 100644 --- a/Projects/Server/Buffers/SpanWriteExtensions.cs +++ b/Projects/Server/Buffers/SpanWriteExtensions.cs @@ -141,7 +141,7 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteAscii(this Span span, ref int pos, string value, int max) { - var length = value.Length <= max ? value.Length : max; + var length = Math.Min(value.Length, max); pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length)); } @@ -160,7 +160,7 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteAsciiNull(this Span span, ref int pos, string value, int max) { - var length = value.Length < max ? value.Length : max - 1; + var length = Math.Min(value.Length, max - 1); pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length)); #if NO_LOCAL_INIT @@ -172,7 +172,7 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteAsciiFixed(this Span span, ref int pos, string value, int amount) { - var length = value.Length <= amount ? value.Length : amount; + var length = Math.Min(value.Length, amount); #if NO_LOCAL_INIT int bytesWritten = Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length)); diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 2e56a79b7..8eb2e75e5 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -2940,13 +2940,13 @@ namespace Server if (ns.StygianAbyss) { - ns.Send(SupportedFeatures.Instantiate(ns)); + Packets.SendSupportedFeature(ns); ns.Send(new MobileUpdate(this)); ns.Send(new MobileAttributes(this)); } else { - ns.Send(SupportedFeatures.Instantiate(ns)); + Packets.SendSupportedFeature(ns); ns.Send(new MobileUpdateOld(this)); ns.Send(new MobileAttributes(this)); } @@ -3084,13 +3084,13 @@ namespace Server if (ns.StygianAbyss) { - ns.Send(SupportedFeatures.Instantiate(ns)); + Packets.SendSupportedFeature(ns); ns.Send(new MobileUpdate(this)); ns.Send(new MobileAttributes(this)); } else { - ns.Send(SupportedFeatures.Instantiate(ns)); + Packets.SendSupportedFeature(ns); ns.Send(new MobileUpdateOld(this)); ns.Send(new MobileAttributes(this)); } diff --git a/Projects/Server/Network/ISocket.cs b/Projects/Server/Network/ISocket.cs new file mode 100644 index 000000000..c8a459a6b --- /dev/null +++ b/Projects/Server/Network/ISocket.cs @@ -0,0 +1,36 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ISocket.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace Server.Network +{ + public interface ISocket + { + public EndPoint LocalEndPoint { get; } + + public EndPoint RemoteEndPoint { get; } + + public Task SendAsync(IList> buffer, SocketFlags flags); + + public Task ReceiveAsync(IList> buffer, SocketFlags flags); + + public void Shutdown(SocketShutdown how); + } +} diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index 4b4406edf..587ca0553 100644 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -75,7 +75,7 @@ namespace Server.Network Timer.DelayCall(checkAliveDuration, checkAliveDuration, CheckAllAlive); } - public NetState(Socket connection) + public NetState(ISocket connection) { m_Running = false; Connection = connection; @@ -141,7 +141,7 @@ namespace Server.Network public Pipe SendPipe { get; private set; } - public Socket Connection { get; private set; } + public ISocket Connection { get; private set; } public bool CompressionEnabled { get; set; } @@ -371,7 +371,7 @@ namespace Server.Network public virtual void Send(ref CircularBuffer buffer, int length) { - if (Connection == null || BlockAllPackets || buffer.Length == 0) + if (Connection == null || BlockAllPackets || buffer.Length == 0 || length <= 0) { return; } diff --git a/Projects/Server/Network/NetworkSocket.cs b/Projects/Server/Network/NetworkSocket.cs new file mode 100644 index 000000000..9f2905727 --- /dev/null +++ b/Projects/Server/Network/NetworkSocket.cs @@ -0,0 +1,40 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: NetworkSocket.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace Server.Network +{ + public class NetworkSocket : ISocket + { + public Socket Connection { get; } + public EndPoint LocalEndPoint => Connection.LocalEndPoint; + public EndPoint RemoteEndPoint => Connection.RemoteEndPoint; + + public NetworkSocket(Socket connection) => Connection = connection; + + public Task SendAsync(IList> buffers, SocketFlags flags) => + Connection.SendAsync(buffers, flags); + + public Task ReceiveAsync(IList> buffers, SocketFlags flags) => + Connection.ReceiveAsync(buffers, flags); + + public void Shutdown(SocketShutdown how) => Connection.Shutdown(how); + } +} diff --git a/Projects/Server/Network/PacketHandlers.cs b/Projects/Server/Network/PacketHandlers.cs index 350525664..bca8cb673 100644 --- a/Projects/Server/Network/PacketHandlers.cs +++ b/Projects/Server/Network/PacketHandlers.cs @@ -2060,7 +2060,7 @@ namespace Server.Network if (check != null && check.Map != Map.Internal && check != m) { state.WriteConsole("Account in use"); - state.Send(new PopupMessage(PMMessage.CharInWorld)); + Packets.SendPopupMessage(state, PMMessage.CharInWorld); return; } } @@ -2076,7 +2076,7 @@ namespace Server.Network // TODO: Make this wait one tick so we don't have to call it unnecessarily NetState.ProcessDisposedQueue(); - state.Send(new ClientVersionReq()); + Packets.SendClientVersionRequest(state); state.BlockAllPackets = true; @@ -2096,7 +2096,7 @@ namespace Server.Network public static void DoLogin(NetState state, Mobile m) { - state.Send(new LoginConfirm(m)); + Packets.SendLoginConfirmation(state, m); if (m.Map != null) { @@ -2110,7 +2110,7 @@ namespace Server.Network state.Send(SeasonChange.Instantiate(m.GetSeason(), true)); - state.Send(SupportedFeatures.Instantiate(state)); + Packets.SendSupportedFeature(state); state.Sequence = 0; @@ -2130,7 +2130,7 @@ namespace Server.Network m.SendEverything(); - state.Send(SupportedFeatures.Instantiate(state)); + Packets.SendSupportedFeature(state); state.Send(new MobileUpdate(m)); // state.Send( new MobileAttributes( m ) ); state.Send(new MobileStatus(m, m)); @@ -2153,7 +2153,7 @@ namespace Server.Network m.SendEverything(); - state.Send(SupportedFeatures.Instantiate(state)); + Packets.SendSupportedFeature(state); state.Send(new MobileUpdate(m)); // state.Send( new MobileAttributes( m ) ); state.Send(new MobileStatus(m, m)); @@ -2176,7 +2176,7 @@ namespace Server.Network m.SendEverything(); - state.Send(SupportedFeatures.Instantiate(state)); + Packets.SendSupportedFeature(state); state.Send(new MobileUpdateOld(m)); // state.Send( new MobileAttributes( m ) ); state.Send(new MobileStatus(m, m)); @@ -2184,7 +2184,7 @@ namespace Server.Network state.Send(new MobileIncomingOld(m, m)); } - state.Send(LoginComplete.Instance); + Packets.SendLoginComplete(state); state.Send(new CurrentTime()); state.Send(SeasonChange.Instantiate(m.GetSeason(), true)); if (m.Map != null) @@ -2278,7 +2278,7 @@ namespace Server.Network if (check != null && check.Map != Map.Internal) { state.WriteConsole("Account in use"); - state.Send(new PopupMessage(PMMessage.CharInWorld)); + Packets.SendPopupMessage(state, PMMessage.CharInWorld); return; } } @@ -2311,7 +2311,7 @@ namespace Server.Network race ); - state.Send(new ClientVersionReq()); + Packets.SendClientVersionRequest(state); state.BlockAllPackets = true; @@ -2403,7 +2403,7 @@ namespace Server.Network if (check != null && check.Map != Map.Internal) { state.WriteConsole("Account in use"); - state.Send(new PopupMessage(PMMessage.CharInWorld)); + Packets.SendPopupMessage(state, PMMessage.CharInWorld); return; } } @@ -2437,7 +2437,7 @@ namespace Server.Network race ); - state.Send(new ClientVersionReq()); + Packets.SendClientVersionRequest(state); state.BlockAllPackets = true; @@ -2547,16 +2547,8 @@ namespace Server.Network state.CompressionEnabled = true; state.PacketEncoder = NetworkCompression.Compress; - state.Send(SupportedFeatures.Instantiate(state)); - - if (state.NewCharacterList) - { - state.Send(new CharacterList(state.Account, state.CityInfo)); - } - else - { - state.Send(new CharacterListOld(state.Account, state.CityInfo)); - } + Packets.SendSupportedFeature(state); + Packets.SendCharacterList(state); } else { @@ -2578,10 +2570,10 @@ namespace Server.Network { var si = info[index]; - state.m_AuthID = PlayServerAck.m_AuthID = GenerateAuthID(state); + state.m_AuthID = GenerateAuthID(state); state.SentFirstPacket = false; - state.Send(new PlayServerAck(si)); + Packets.SendPlayServerAck(state, si, state.m_AuthID); } } @@ -2679,17 +2671,14 @@ namespace Server.Network } else { - var info = e.Servers.ToArray(); - - state.ServerInfo = info; - - state.Send(new AccountLoginAck(info)); + state.ServerInfo = e.Servers.ToArray(); + Packets.SendAccountLoginAck(state); } } public static void AccountLogin_ReplyRej(NetState state, ALRReason reason) { - state.Send(new AccountLoginRej(reason)); + Packets.SendAccountLoginRejected(state, reason); state.Dispose(); } diff --git a/Projects/Server/Network/Packets/AccountPackets.cs b/Projects/Server/Network/Packets/AccountPackets.cs new file mode 100644 index 000000000..e79e3fa84 --- /dev/null +++ b/Projects/Server/Network/Packets/AccountPackets.cs @@ -0,0 +1,488 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AccountPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.IO; +using Server.Accounting; +using System.Buffers; + +namespace Server.Network +{ + public enum ALRReason : byte + { + Invalid = 0, + InUse = 1, + Blocked = 2, + BadPass = 3, + Idle = 254, + BadComm = 255 + } + + public enum PMMessage : byte + { + None = 0, + CharNoExist = 1, + CharExists = 2, + CharInWorld = 5, + LoginSyncError = 6, + IdleWarning = 7 + } + + public enum DeleteResultType + { + PasswordInvalid, + CharNotExist, + CharBeingPlayed, + CharTooYoung, + CharQueued, + BadRequest + } + + public static partial class Packets + { + /** + * Packet: 0x81 + * Length: Up to 425 bytes + * + * Displays the list of characters during the login process. + * Note: Currently Unused + */ + public static void SendChangeCharacter(NetState ns, IAccount a) + { + if (ns == null || a == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + var length = 5 + a.Length * 60; + var writer = new CircularBufferWriter(buffer); + + writer.Write((byte)0x81); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0); // Count & Placeholder + + int count = 0; + + for (var i = 0; i < a.Length; ++i) + { + var m = a[i]; + + if (m == null) + { + writer.Clear(60); + } + else + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + + count++; + writer.WriteAscii(name, 30); + writer.Clear(30); // Password (empty) + } + } + + var position = writer.Position; + writer.Seek(3, SeekOrigin.Begin); + writer.Write((byte)count); + writer.Seek(position, SeekOrigin.Begin); + + ns.Send(ref buffer, writer.Position); + } + + /** + * Packet: 0xBD + * Length: 3 bytes + * + * Sends a requests for the client version + */ + public static void SendClientVersionRequest(NetState ns) + { + if (ns != null && ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + buffer[0] = 0xBD; // Packet ID + buffer[1] = 0x00; + buffer[2] = 0x03; // Length + + ns.Send(ref buffer, 3); + } + } + + /** + * Packet: 0x85 + * Length: 2 bytes + * + * Sends the result of a deletion request + */ + public static void SendCharacterDeleteResult(NetState ns, DeleteResultType res) + { + if (ns != null && ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + buffer[0] = 0x85; // Packet ID + buffer[1] = (byte)res; + + ns.Send(ref buffer, 2); + } + } + + /** + * Packet: 0x53 + * Length: 2 bytes + * + * Sends a PopupMessage with a predetermined message + */ + public static void SendPopupMessage(NetState ns, PMMessage msg) + { + if (ns != null && ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + buffer[0] = 0x53; // Packet ID + buffer[1] = (byte)msg; + + ns.Send(ref buffer, 2); + } + } + + /** + * Packet: 0xB9 + * Length: 3 or 5 bytes + * + * Sends support features based on the client version + */ + public static void SendSupportedFeature(NetState ns) + { + if (ns == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + 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; + } + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xB9); // Packet ID + + if (ns.ExtendedSupportedFeatures) + { + writer.Write((uint)flags); + } + else + { + writer.Write((ushort)flags); + } + + ns.Send(ref buffer, ns.ExtendedSupportedFeatures ? 5 : 3); + } + + /** + * Packet: 0x1B + * Length: 37 bytes + * + * Sends login confirmation + */ + public static void SendLoginConfirmation(NetState ns, Mobile m) + { + if (ns == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0x1B); // PacketID + writer.Write(m.Serial); + writer.Write(0); + writer.Write((short)m.Body); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((short)m.Z); + writer.Write((byte)m.Direction); + writer.Write((byte)0); + writer.Write(-1); + + writer.Write(0); + + var map = m.Map; + + if (map == null || map == Map.Internal) + { + map = m.LogoutMap; + } + + writer.Write((short)(map?.Width ?? Map.Felucca.Width)); + writer.Write((short)(map?.Height ?? Map.Felucca.Height)); + writer.Clear(); + + ns.Send(ref buffer, 37); + } + + /** + * Packet: 0x55 + * Length: 1 byte + * + * Sends login completion + */ + public static void SendLoginComplete(NetState ns) + { + if (ns != null && ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + buffer[0] = 0x55; // Packet ID + + ns.Send(ref buffer, 1); + } + } + + /** + * Packet: 0x86 + * Length: Up to 424 bytes + * + * Sends updated character list + */ + public static void SendCharacterListUpdate(NetState ns, IAccount a) + { + if (ns == null || a == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0x86); // Packet ID + writer.Seek(2, SeekOrigin.Current); // Length + + var highSlot = -1; + + for (var i = a.Length - 1; i >= 0; i--) + { + if (a[i] != null) + { + highSlot = i; + break; + } + } + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + writer.Write((byte)count); + + for (int i = 0; i < count; i++) + { + var m = a[i]; + + if (m == null) + { + writer.Clear(60); + } + else + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + writer.WriteAscii(name, 30); + writer.Clear(30); // password + } + } + + writer.WritePacketLength(); + ns.Send(ref buffer, writer.Position); + } + + /** + * Packet: 0xA9 + * Length: 1410 or more bytes + * + * Sends list of characters and starting cities. + */ + public static void SendCharacterList(NetState ns) + { + var acct = ns?.Account; + + if (acct == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + var client70130 = ns.NewCharacterList; + var textLength = client70130 ? 32 : 31; + + var cityInfo = ns.CityInfo; + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xA9); // Packet ID + writer.Seek(2, SeekOrigin.Current); // Length + + var highSlot = -1; + + for (var i = acct.Length - 1; i >= 0; i--) + { + if (acct[i] != null) + { + highSlot = i; + break; + } + } + + var count = Math.Max(Math.Max(highSlot + 1, acct.Limit), 5); + writer.Write((byte)count); + + for (int i = 0; i < count; i++) + { + var m = acct[i]; + + if (m == null) + { + writer.Clear(60); + } + else + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + writer.WriteAscii(name, 30); + writer.Clear(30); // password + } + } + + writer.Write((byte)cityInfo.Length); + + for (int i = 0; i < cityInfo.Length; ++i) + { + var ci = cityInfo[i]; + + writer.Write((byte)i); + writer.WriteAscii(ci.City, textLength); + writer.WriteAscii(ci.Building, textLength); + if (client70130) + { + writer.Write(ci.X); + writer.Write(ci.Y); + writer.Write(ci.Z); + writer.Write(ci.Map.MapID); + writer.Write(ci.Description); + writer.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 (acct.Limit == 1) + { + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + } + + writer.Write((int)flags); + if (client70130) + { + writer.Write((short)-1); + } + + writer.WritePacketLength(); + ns.Send(ref buffer, writer.Position); + } + + /** + * Packet: 0x82 + * Length: 2 bytes + * + * Sends a reason for rejecting the login + */ + public static void SendAccountLoginRejected(NetState ns, ALRReason reason) + { + if (ns != null && ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + buffer[0] = 0x82; // Packet ID + buffer[1] = (byte)reason; + + ns.Send(ref buffer, 2); + } + } + + /** + * Packet: 0xA8 + * Length: up to 240 bytes + * + * Sends login acknowledge with server listing + */ + public static void SendAccountLoginAck(NetState ns) + { + if (ns == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0xA8); // Packet ID + writer.Seek(2, SeekOrigin.Current); // Length + + writer.Write((byte)0x5D); + + var info = ns.ServerInfo; + writer.Write((ushort)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var si = info[i]; + + writer.Write((ushort)i); + writer.WriteAscii(si.Name, 32); + writer.Write((byte)si.FullPercent); + writer.Write((sbyte)si.TimeZone); + writer.Write(Utility.GetAddressValue(si.Address.Address)); + } + + writer.WritePacketLength(); + ns.Send(ref buffer, writer.Position); + } + + /** + * Packet: 0x8C + * Length: 11 bytes + * + * Sends acknowledge play server + */ + public static void SendPlayServerAck(NetState ns, ServerInfo si, int authId) + { + if (ns == null || !ns.SendPipe.Writer.GetAvailable(out var buffer)) + { + return; + } + + var writer = new CircularBufferWriter(buffer); + writer.Write((byte)0x8C); // Packet ID + + var addr = Utility.GetAddressValue(si.Address.Address); + writer.WriteLE(addr); + writer.Write((short)si.Address.Port); + writer.Write(authId); + + ns.Send(ref buffer, writer.Position); + } + } +} diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs index dd1a705db..d366dacd7 100644 --- a/Projects/Server/Network/TcpServer.cs +++ b/Projects/Server/Network/TcpServer.cs @@ -215,7 +215,7 @@ namespace Server.Network // ignored } - var ns = new NetState(socket); + var ns = new NetState(new NetworkSocket(socket)); m_ConnectedQueue.Enqueue(ns); ns.Start(); } diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs index 28ec4fcb9..68ae2769b 100644 --- a/Projects/UOContent/Accounting/AccountHandler.cs +++ b/Projects/UOContent/Accounting/AccountHandler.cs @@ -218,11 +218,14 @@ namespace Server.Misc if (!(state.Account is Account acct)) { state.Dispose(); + return; } - else if (index < 0 || index >= acct.Length) + + DeleteResultType res; + + if (index < 0 || index >= acct.Length) { - state.Send(new DeleteResult(DeleteResultType.BadRequest)); - state.Send(new CharacterListUpdate(acct)); + res = DeleteResultType.BadRequest; } else { @@ -230,25 +233,21 @@ namespace Server.Misc if (m == null) { - state.Send(new DeleteResult(DeleteResultType.CharNotExist)); - state.Send(new CharacterListUpdate(acct)); + res = DeleteResultType.CharNotExist; } else if (m.NetState != null) { - state.Send(new DeleteResult(DeleteResultType.CharBeingPlayed)); - state.Send(new CharacterListUpdate(acct)); + res = DeleteResultType.CharBeingPlayed; } else if (RestrictDeletion && DateTime.UtcNow < m.CreationTime + DeleteDelay) { - state.Send(new DeleteResult(DeleteResultType.CharTooYoung)); - state.Send(new CharacterListUpdate(acct)); + res = DeleteResultType.CharTooYoung; } else if (m.AccessLevel == AccessLevel.Player && Region.Find(m.LogoutLocation, m.LogoutMap).IsPartOf() ) // Don't need to check current location, if netstate is null, they're logged out { - state.Send(new DeleteResult(DeleteResultType.BadRequest)); - state.Send(new CharacterListUpdate(acct)); + res = DeleteResultType.BadRequest; } else { @@ -257,9 +256,14 @@ namespace Server.Misc acct.Comments.Add(new AccountComment("System", $"Character #{index + 1} {m} deleted by {state}")); m.Delete(); - state.Send(new CharacterListUpdate(acct)); + Packets.SendCharacterListUpdate(state, acct); + return; } } + + Packets.SendCharacterDeleteResult(state, res); + Packets.SendCharacterListUpdate(state, acct); + } public static bool CanCreate(IPAddress ip) => diff --git a/Projects/UOContent/Multis/HouseFoundation.cs b/Projects/UOContent/Multis/HouseFoundation.cs index 9edf0918e..ef2e2866b 100644 --- a/Projects/UOContent/Multis/HouseFoundation.cs +++ b/Projects/UOContent/Multis/HouseFoundation.cs @@ -1,11 +1,8 @@ using System; using System.Buffers; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.IO.Compression; -using System.Threading; -using System.Threading.Tasks; using Server.Gumps; using Server.Items; using Server.Mobiles;