Changes account packets to functional (#284)

- [X] Moved packets from objects to functions
- [X] Fixed bug where characters were showing up with their modded names in the character list

Bumps release version
This commit is contained in:
Kamron Batman 2020-10-25 23:47:29 -07:00 committed by GitHub
parent 90220feb41
commit 0e02cf0821
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 866 additions and 631 deletions

View file

@ -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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<TestAccount>
{
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);
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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
}
}
/// <summary>
/// Asks the client for it's version
/// </summary>
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);
}
}
}

View file

@ -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;

View file

@ -1,6 +1,5 @@
using System;
using System.Buffers;
using System.Net;
using Server.Network;
using Xunit;

View file

@ -0,0 +1,95 @@
using System;
using System.Linq;
using Server.Accounting;
namespace Server.Tests.Network
{
public class MockAccount : IAccount, IComparable<MockAccount>
{
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);
}
}

View file

@ -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<int> SendAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags) => Task.Run(() => 0);
public Task<int> ReceiveAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags) => Task.Run(() => 0);
public void Shutdown(SocketShutdown how)
{
}
}
}

View file

@ -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<byte> Compile(this Packet p) =>
p.Compile(false, out var length).AsSpan(0, length);
public static NetState CreateTestNetState() =>
new NetState(new MockSocket());
}
}

View file

@ -35,6 +35,10 @@ namespace Server.Network
{
}
public CircularBufferReader(CircularBuffer<byte> buffer) : this(buffer.GetSpan(0), buffer.GetSpan(1))
{
}
public CircularBufferReader(ReadOnlySpan<byte> first, ReadOnlySpan<byte> second)
{
_first = first;

View file

@ -29,6 +29,10 @@ namespace System.Buffers
public int Length { get; }
public int Position { get; private set; }
public CircularBufferWriter(CircularBuffer<byte> buffer) : this(buffer.GetSpan(0), buffer.GetSpan(1))
{
}
public CircularBufferWriter(Span<byte> first, Span<byte> 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();
}
}
/// <summary>
/// Writes a 4-byte unsigned integer value to the underlying stream.
/// </summary>

View file

@ -141,7 +141,7 @@ namespace System.Buffers
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteAscii(this Span<byte> 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<byte> 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<byte> 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));

View file

@ -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));
}

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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<int> SendAsync(IList<ArraySegment<byte>> buffer, SocketFlags flags);
public Task<int> ReceiveAsync(IList<ArraySegment<byte>> buffer, SocketFlags flags);
public void Shutdown(SocketShutdown how);
}
}

View file

@ -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<byte> 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<byte> buffer, int length)
{
if (Connection == null || BlockAllPackets || buffer.Length == 0)
if (Connection == null || BlockAllPackets || buffer.Length == 0 || length <= 0)
{
return;
}

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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<int> SendAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags) =>
Connection.SendAsync(buffers, flags);
public Task<int> ReceiveAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags) =>
Connection.ReceiveAsync(buffers, flags);
public void Shutdown(SocketShutdown how) => Connection.Shutdown(how);
}
}

View file

@ -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();
}

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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);
}
}
}

View file

@ -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();
}

View file

@ -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<JailRegion>()
) // 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) =>

View file

@ -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;