From e6d30fef0fe3c9b31d36392365fa3b5d687ca88d Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:17:57 -0700 Subject: [PATCH] fix: Reverts GetAccount to use IAccount. Adds IAccount to serialization. (#1565) ### Summary - Fixes usernames not being `Intern`ed - Reverts methods related to getting accounts from returning `Account` to `IAccount`. - Makes `IAccount` also `ISerializable` - Adds `IGenericReader.ReadAccount()` and `IGenericWriter.Write(IAccount)` -> The read method supports the original serialization of username, and using `IAccount.Serial`. The write method only serializes the `Serial`. - Exposes `ReadStringRaw()` to allow some advanced scenarios. --- .../Packets/Outgoing/AccountPacketTests.cs | 20 +-- Projects/Server/IAccount.cs | 2 +- .../Server/Serialization/IGenericReader.cs | 2 + .../Accounting/Account.Migrations.cs | 23 +++- Projects/UOContent/Accounting/Account.cs | 5 +- Projects/UOContent/Accounting/Accounts.cs | 10 +- .../Server.Accounting.Account.v5.json | 125 ++++++++++++++++++ Projects/UOContent/Misc/SerializationExt.cs | 29 ++++ Projects/UOContent/Misc/ServerAccess.cs | 4 +- 9 files changed, 201 insertions(+), 19 deletions(-) create mode 100644 Projects/UOContent/Migrations/Server.Accounting.Account.v5.json create mode 100644 Projects/UOContent/Misc/SerializationExt.cs diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs index 21c0b80ed..a549658ba 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs @@ -47,15 +47,19 @@ public class AccountPacketTests : IClassFixture } } - public void Delete() - { - throw new NotImplementedException(); - } + public DateTime Created { get; set; } + public long SavePosition { get; set; } + public BufferWriter SaveBuffer { get; set; } + public Serial Serial { get; } + public void Deserialize(IGenericReader reader) => throw new NotImplementedException(); - public void SetPassword(string password) - { - throw new NotImplementedException(); - } + public void Serialize(IGenericWriter writer) => throw new NotImplementedException(); + + public bool Deleted { get; } + + public void Delete() => throw new NotImplementedException(); + + public void SetPassword(string password) => throw new NotImplementedException(); public bool CheckPassword(string password) => throw new NotImplementedException(); } diff --git a/Projects/Server/IAccount.cs b/Projects/Server/IAccount.cs index 2e24f5e5b..911bb8c0a 100644 --- a/Projects/Server/IAccount.cs +++ b/Projects/Server/IAccount.cs @@ -95,7 +95,7 @@ public interface IGoldAccount long GetTotalGold(); } -public interface IAccount : IGoldAccount, IComparable +public interface IAccount : ISerializable, IGoldAccount, IComparable { string Username { get; set; } string Email { get; set; } diff --git a/Projects/Server/Serialization/IGenericReader.cs b/Projects/Server/Serialization/IGenericReader.cs index 53e1cfcd8..213b6fd97 100644 --- a/Projects/Server/Serialization/IGenericReader.cs +++ b/Projects/Server/Serialization/IGenericReader.cs @@ -26,6 +26,8 @@ public interface IGenericReader DateTime LastSerialized { get; init; } string ReadString(bool intern = false); + public string ReadStringRaw(bool intern = false); + long ReadLong(); ulong ReadULong(); int ReadInt(); diff --git a/Projects/UOContent/Accounting/Account.Migrations.cs b/Projects/UOContent/Accounting/Account.Migrations.cs index 0bee60baf..bbfc108f6 100644 --- a/Projects/UOContent/Accounting/Account.Migrations.cs +++ b/Projects/UOContent/Accounting/Account.Migrations.cs @@ -7,9 +7,30 @@ namespace Server.Accounting { public partial class Account { + // Username was not interned + private void MigrateFrom(V4Content content) + { + _username = content.Username; + _passwordAlgorithm = content.PasswordAlgorithm; + _password = content.Password; + _accessLevel = content.AccessLevel; + _flags = content.Flags; + _lastLogin = content.LastLogin; + _totalGold = content.TotalGold; + _totalPlat = content.TotalPlat; + _mobiles = content.Mobiles; + _comments = content.Comments; + _tags = content.Tags; + _loginIPs = content.LoginIPs; + _ipRestrictions = content.IpRestrictions; + _totalGameTime = content.TotalGameTime; + _email = content.Email; + } + private void MigrateFrom(V3Content content) { _username = content.Username; + _username.Intern(); _passwordAlgorithm = content.PasswordAlgorithm; _password = content.Password; _accessLevel = content.AccessLevel; @@ -35,7 +56,7 @@ namespace Server.Accounting reader.Seek(0, SeekOrigin.Begin); } - _username = reader.ReadString(); + _username = reader.ReadString(true); _passwordAlgorithm = version < 2 ? (PasswordProtectionAlgorithm)reader.ReadInt() : reader.ReadEnum(); _password = reader.ReadString(); _accessLevel = version < 2 ? (AccessLevel)reader.ReadInt() : reader.ReadEnum(); diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index c76feb35d..b82304f9d 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -12,13 +12,14 @@ using Server.Network; namespace Server.Accounting { - [SerializationGenerator(4)] - public partial class Account : IAccount, IComparable, ISerializable + [SerializationGenerator(5)] + public partial class Account : IAccount, IComparable { public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0); public static readonly TimeSpan InactiveDuration = TimeSpan.FromDays(180.0); public static readonly TimeSpan EmptyInactiveDuration = TimeSpan.FromDays(30.0); + [InternString] [SerializableField(0)] private string _username; diff --git a/Projects/UOContent/Accounting/Accounts.cs b/Projects/UOContent/Accounting/Accounts.cs index 7be2410a8..5e0d58792 100644 --- a/Projects/UOContent/Accounting/Accounts.cs +++ b/Projects/UOContent/Accounting/Accounts.cs @@ -6,11 +6,11 @@ using Server.Logging; namespace Server.Accounting; -public class Accounts : GenericEntityPersistence +public class Accounts : GenericEntityPersistence { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Accounts)); - private static readonly Dictionary _accountsByName = new(32, StringComparer.OrdinalIgnoreCase); + private static readonly Dictionary _accountsByName = new(32, StringComparer.OrdinalIgnoreCase); public static int Count => _accountsByName.Count; @@ -29,7 +29,7 @@ public class Accounts : GenericEntityPersistence public static IEnumerable GetAccounts() => _accountsByName.Values; - public static Account GetAccount(string username) + public static IAccount GetAccount(string username) { _accountsByName.TryGetValue(username, out var a); return a; @@ -41,7 +41,7 @@ public class Accounts : GenericEntityPersistence _accountsPersistence.AddEntity(a); } - public static void Remove(Account a) + public static void Remove(IAccount a) { _accountsByName.Remove(a.Username); _accountsPersistence.RemoveEntity(a); @@ -91,5 +91,5 @@ public class Accounts : GenericEntityPersistence } } - public static IAccount FindAccount(Serial serial) => _accountsPersistence.FindEntity(serial); + public static IAccount FindAccount(Serial serial) => _accountsPersistence.FindEntity(serial); } diff --git a/Projects/UOContent/Migrations/Server.Accounting.Account.v5.json b/Projects/UOContent/Migrations/Server.Accounting.Account.v5.json new file mode 100644 index 000000000..8131e9dbe --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Accounting.Account.v5.json @@ -0,0 +1,125 @@ +{ + "version": 5, + "type": "Server.Accounting.Account", + "properties": [ + { + "name": "Username", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "InternString" + ] + }, + { + "name": "PasswordAlgorithm", + "type": "Server.Accounting.Security.PasswordProtectionAlgorithm", + "rule": "EnumMigrationRule" + }, + { + "name": "Password", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "AccessLevel", + "type": "Server.AccessLevel", + "rule": "EnumMigrationRule" + }, + { + "name": "Flags", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LastLogin", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TotalGold", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "TotalPlat", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Mobiles", + "type": "Server.Mobile[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "Server.Mobile", + "SerializableInterfaceMigrationRule" + ] + }, + { + "name": "Comments", + "type": "System.Collections.Generic.List\u003CServer.Accounting.AccountComment\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Accounting.AccountComment", + "SerializationMethodSignatureMigrationRule", + "" + ] + }, + { + "name": "Tags", + "type": "System.Collections.Generic.List\u003CServer.Accounting.AccountTag\u003E", + "rule": "ListMigrationRule", + "ruleArguments": [ + "Server.Accounting.AccountTag", + "SerializationMethodSignatureMigrationRule", + "" + ] + }, + { + "name": "LoginIPs", + "type": "System.Net.IPAddress[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "System.Net.IPAddress", + "PrimitiveTypeMigrationRule" + ] + }, + { + "name": "IpRestrictions", + "type": "string[]", + "rule": "ArrayMigrationRule", + "ruleArguments": [ + "string", + "PrimitiveTypeMigrationRule", + "" + ] + }, + { + "name": "TotalGameTime", + "type": "System.TimeSpan", + "rule": "PrimitiveTypeMigrationRule" + }, + { + "name": "Email", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/SerializationExt.cs b/Projects/UOContent/Misc/SerializationExt.cs new file mode 100644 index 000000000..6afc19da6 --- /dev/null +++ b/Projects/UOContent/Misc/SerializationExt.cs @@ -0,0 +1,29 @@ +using Server.Accounting; + +namespace Server.Misc; + +public static class SerializationExt +{ + public static IAccount ReadAccount(this IGenericReader reader) + { + return reader.ReadByte() switch + { + 0 => null, + 1 => Accounts.GetAccount(reader.ReadStringRaw()), + 2 => Accounts.FindAccount(reader.ReadSerial()) + }; + } + + public static void Write(this IGenericWriter writer, IAccount acct) + { + if (acct == null) + { + writer.Write((byte)0); + } + else + { + writer.Write((byte)2); + writer.Write(acct.Serial); + } + } +} diff --git a/Projects/UOContent/Misc/ServerAccess.cs b/Projects/UOContent/Misc/ServerAccess.cs index b5f86b9b7..f6786246c 100644 --- a/Projects/UOContent/Misc/ServerAccess.cs +++ b/Projects/UOContent/Misc/ServerAccess.cs @@ -76,8 +76,8 @@ public static class ServerAccess return; } - var acct = Accounts.GetAccount(username); - if (acct == null || !acct.Banned && acct.AccessLevel >= AccessLevel.Owner || !acct.CheckPassword(e.Password)) + if (Accounts.GetAccount(username) is not Account acct + || !acct.Banned && acct.AccessLevel >= AccessLevel.Owner || !acct.CheckPassword(e.Password)) { return; }