diff --git a/Distribution/Data/expansion.json b/Distribution/Data/expansion.json new file mode 100644 index 000000000..e0b036f61 --- /dev/null +++ b/Distribution/Data/expansion.json @@ -0,0 +1,794 @@ +[ + { + "Name": "None", + "ClientVersion": null, + "ClientFlags": null, + "FeatureFlags": { + "None": true, + "T2A": false, + "UOR": false, + "UOTD": false, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": true, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": false, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "The Second Age", + "ClientVersion": null, + "ClientFlags": "Felucca", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": false, + "UOTD": false, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Renaissance", + "ClientVersion": null, + "ClientFlags": "Trammel", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": false, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Third Dawn", + "ClientVersion": null, + "ClientFlags": "Ilshenar", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Blackthorn's Revenge", + "ClientVersion": null, + "ClientFlags": "Ilshenar", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": false, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": true, + "AOS": false, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Age of Shadows", + "ClientVersion": null, + "ClientFlags": "Malas", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": false, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": false, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Samurai Empire", + "ClientVersion": null, + "ClientFlags": "Tokuno", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": false, + "EigthAge": false, + "NinthAge": false, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": false, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": false, + "Crystal": false, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Mondain's Legacy", + "ClientVersion": "5.0.0a", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": false, + "HS": false, + "Gothic": false, + "Rustic": false, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Stygian Abyss", + "ClientVersion": null, + "ClientFlags": "TerMur", + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": false, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": false, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "High Seas", + "ClientVersion": "7.0.9.0", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": false, + "Shadowguard": false, + "TOL": false, + "EJ": false + } + }, + { + "Name": "Time of Legends", + "ClientVersion": "7.0.45.65", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": false + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": false + } + }, + { + "Name": "Endless Journey", + "ClientVersion": "7.0.61.0", + "ClientFlags": null, + "FeatureFlags": { + "None": false, + "T2A": true, + "UOR": true, + "UOTD": true, + "LBR": true, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "EigthAge": false, + "NinthAge": true, + "TenthAge": false, + "IncreasedStorage": false, + "SeventhCharacterSlot": false, + "RoleplayFaces": false, + "TrialAccount": false, + "LiveAccount": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": true + }, + "CharacterListFlags": { + "None": false, + "Unk1": false, + "OverwriteConfigButton": false, + "OneCharacterSlot": false, + "ContextMenus": true, + "SlotLimit": false, + "AOS": true, + "SixthCharacterSlot": false, + "SE": true, + "ML": true, + "Unk2": false, + "UO3DClientType": false, + "Unk3": false, + "SeventhCharacterSlot": false, + "Unk4": false, + "NewMovementSystem": false, + "NewFeluccaAreas": false + }, + "HousingFlags": { + "None": false, + "AOS": true, + "SE": true, + "ML": true, + "Crystal": true, + "SA": true, + "HS": true, + "Gothic": true, + "Rustic": true, + "Jungle": true, + "Shadowguard": true, + "TOL": true, + "EJ": true + } + } +] diff --git a/Projects/Server.Tests/Fixtures/ServerFixture.cs b/Projects/Server.Tests/Fixtures/ServerFixture.cs index 4b0b7cae6..231f3ad25 100644 --- a/Projects/Server.Tests/Fixtures/ServerFixture.cs +++ b/Projects/Server.Tests/Fixtures/ServerFixture.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; namespace Server.Tests { @@ -7,8 +8,8 @@ namespace Server.Tests // Global setup static ServerFixture() { + Core.Assembly = Assembly.GetExecutingAssembly(); Core.LoopContext = new EventLoopContext(); - Core.Expansion = Expansion.EJ; // Load Configurations diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 2e428c64e..12e6fd669 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -10,5 +10,9 @@ + + + + diff --git a/Projects/Server/ExpansionInfo.cs b/Projects/Server/ExpansionInfo.cs index 4ed98bea1..7f38b22c7 100644 --- a/Projects/Server/ExpansionInfo.cs +++ b/Projects/Server/ExpansionInfo.cs @@ -1,4 +1,23 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ExpansionInfo.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.IO; +using System.Text.Json.Serialization; +using Server.Json; namespace Server { @@ -141,105 +160,37 @@ namespace Server { static ExpansionInfo() { - Table = new[] + var path = Path.Combine(Core.BaseDirectory, "Data/expansion.json"); + var expansions = JsonConfig.Deserialize>(path); + + Table = new ExpansionInfo[expansions.Count]; + + for (var i = 0; i < expansions.Count; i++) { - new ExpansionInfo( - 0, - "None", - ClientFlags.None, - FeatureFlags.ExpansionNone, - CharacterListFlags.ExpansionNone, - HousingFlags.None - ), - new ExpansionInfo( - 1, - "The Second Age", - ClientFlags.Felucca, - FeatureFlags.ExpansionT2A, - CharacterListFlags.ExpansionT2A, - HousingFlags.None - ), - new ExpansionInfo( - 2, - "Renaissance", - ClientFlags.Trammel, - FeatureFlags.ExpansionUOR, - CharacterListFlags.ExpansionUOR, - HousingFlags.None - ), - new ExpansionInfo( - 3, - "Third Dawn", - ClientFlags.Ilshenar, - FeatureFlags.ExpansionUOTD, - CharacterListFlags.ExpansionUOTD, - HousingFlags.None - ), - new ExpansionInfo( - 4, - "Blackthorn's Revenge", - ClientFlags.Ilshenar, - FeatureFlags.ExpansionLBR, - CharacterListFlags.ExpansionLBR, - HousingFlags.None - ), - new ExpansionInfo( - 5, - "Age of Shadows", - ClientFlags.Malas, - FeatureFlags.ExpansionAOS, - CharacterListFlags.ExpansionAOS, - HousingFlags.HousingAOS - ), - new ExpansionInfo( - 6, - "Samurai Empire", - ClientFlags.Tokuno, - FeatureFlags.ExpansionSE, - CharacterListFlags.ExpansionSE, - HousingFlags.HousingSE - ), - new ExpansionInfo( - 7, - "Mondain's Legacy", - new ClientVersion("5.0.0a"), - FeatureFlags.ExpansionML, - CharacterListFlags.ExpansionML, - HousingFlags.HousingML - ), - new ExpansionInfo( - 8, - "Stygian Abyss", - ClientFlags.TerMur, - FeatureFlags.ExpansionSA, - CharacterListFlags.ExpansionSA, - HousingFlags.HousingSA - ), - new ExpansionInfo( - 9, - "High Seas", - new ClientVersion("7.0.9.0"), - FeatureFlags.ExpansionHS, - CharacterListFlags.ExpansionHS, - HousingFlags.HousingHS - ), - new ExpansionInfo( - 10, - "Time of Legends", - new ClientVersion("7.0.45.65"), - FeatureFlags.ExpansionTOL, - CharacterListFlags.ExpansionTOL, - HousingFlags.HousingTOL - ), - new ExpansionInfo( - 11, - "Endless Journey", - new ClientVersion("7.0.61.0"), - FeatureFlags.ExpansionEJ, - CharacterListFlags.ExpansionEJ, - HousingFlags.HousingEJ - ) - }; + var expansion = expansions[i]; + if (expansion.ClientVersion != null) + { + Table[i] = new ExpansionInfo( + i, + expansion.Name, + expansion.ClientVersion, + expansion.FeatureFlags, + expansion.CharacterListFlags, + expansion.HousingFlags + ); + } + else + { + Table[i] = new ExpansionInfo( + i, + expansion.Name, + expansion.ClientFlags ?? ClientFlags.None, + expansion.FeatureFlags, + expansion.CharacterListFlags, + expansion.HousingFlags + ); + } + } } public ExpansionInfo( @@ -331,4 +282,22 @@ namespace Server public override string ToString() => Name; } + + public record ExpansionConfig + { + public string Name { get; init; } + + public ClientVersion? ClientVersion { get; init; } + + public ClientFlags? ClientFlags { get; init; } + + [JsonConverter(typeof(FlagsConverter))] + public FeatureFlags FeatureFlags { get; init; } + + [JsonConverter(typeof(FlagsConverter))] + public CharacterListFlags CharacterListFlags { get; init; } + + [JsonConverter(typeof(FlagsConverter))] + public HousingFlags HousingFlags { get; init; } + } } diff --git a/Projects/Server/Json/Converters/ClientVersionConverter.cs b/Projects/Server/Json/Converters/ClientVersionConverter.cs new file mode 100644 index 000000000..c1dc8ecb9 --- /dev/null +++ b/Projects/Server/Json/Converters/ClientVersionConverter.cs @@ -0,0 +1,37 @@ +/************************************************************************* + * ModernUO * + * Copyright 2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ClientVersionConverter.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.Text.Json; +using System.Text.Json.Serialization; + +namespace Server.Json +{ + public class ClientVersionConverter : JsonConverter + { + public override ClientVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + return new ClientVersion(reader.GetString()); + } + + throw new JsonException($"Value must be a string"); + } + + public override void Write(Utf8JsonWriter writer, ClientVersion value, JsonSerializerOptions options) => + writer.WriteStringValue(value.ToString()); + } +} diff --git a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs new file mode 100644 index 000000000..3e778d591 --- /dev/null +++ b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs @@ -0,0 +1,30 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ClientVersionConverterFactory.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.Net; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Server.Json +{ + public class ClientVersionConverterFactory : JsonConverterFactory + { + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(ClientVersion); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new ClientVersionConverter(); + } +} diff --git a/Projects/Server/Json/Converters/FlagsConverter.cs b/Projects/Server/Json/Converters/FlagsConverter.cs new file mode 100644 index 000000000..38584d05a --- /dev/null +++ b/Projects/Server/Json/Converters/FlagsConverter.cs @@ -0,0 +1,149 @@ +/************************************************************************* + * ModernUO * + * Copyright 2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: FlagsConverter.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.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Server.Json +{ + public class FlagsConverter : JsonConverter where T : struct, Enum + { + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var flags = 0ul; + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid Json structure for Flag object"); + } + + var key = reader.GetString(); + + reader.Read(); + + if (!reader.GetBoolean() || !Enum.TryParse(key, out var val)) + { + continue; + } + + flags |= ConvertToUInt64(underlyingType, val); + } + + switch (Type.GetTypeCode(underlyingType)) + { + case TypeCode.SByte: + { + var num = (sbyte)flags; + return Unsafe.As(ref num); + } + case TypeCode.Byte: + { + var num = (byte)flags; + return Unsafe.As(ref num); + } + case TypeCode.Int16: + { + var num = (short)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt16: + { + var num = (ushort)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt32: + { + var num = (uint)flags; + return Unsafe.As(ref num); + } + case TypeCode.Int64: + { + var num = (long)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt64: + { + return Unsafe.As(ref flags); + } + default: + { + var num = (int)flags; + return Unsafe.As(ref num); + } + } + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + var size = GetUnderlyingTypeLength(Type.GetTypeCode(underlyingType)) - 1; + var intValue = ConvertToUInt64(underlyingType, value); + + foreach (var flagName in Enum.GetNames(typeof(T))) + { + var flagValue = Enum.Parse(flagName, false); + var flag = ConvertToUInt64(underlyingType, flagValue); + if (flag == 0 || (flag & size) == 0) + { + writer.WriteBoolean(flagName, (intValue & flag) != 0); + } + } + + writer.WriteEndObject(); + } + + private static ulong ConvertToUInt64(Type underlyingType, object value) => + Type.GetTypeCode(underlyingType) switch + { + TypeCode.SByte => (ulong)(sbyte)value, + TypeCode.Byte => (byte)value, + TypeCode.Int16 => (ulong)(short)value, + TypeCode.UInt16 => (ushort)value, + TypeCode.Int32 => (ulong)(int)value, + TypeCode.UInt32 => (uint)value, + TypeCode.Int64 => (ulong)(long)value, + TypeCode.UInt64 => (ulong)value, + _ => throw new InvalidOperationException() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong GetUnderlyingTypeLength(TypeCode typeCode) => + typeCode switch + { + TypeCode.Byte => 8, + TypeCode.SByte => 8, + TypeCode.Int16 => 16, + TypeCode.UInt16 => 16, + TypeCode.Char => 16, + TypeCode.Int32 => 32, + TypeCode.UInt32 => 32, + TypeCode.Int64 => 64, + TypeCode.UInt64 => 64, + _ => 64 + }; + } +} diff --git a/Projects/Server/Json/JsonConfig.cs b/Projects/Server/Json/JsonConfig.cs index c44ba874c..163776d84 100644 --- a/Projects/Server/Json/JsonConfig.cs +++ b/Projects/Server/Json/JsonConfig.cs @@ -39,6 +39,7 @@ namespace Server.Json Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + options.Converters.Add(new ClientVersionConverterFactory()); options.Converters.Add(new GuidConverterFactory()); options.Converters.Add(new JsonStringEnumConverter()); options.Converters.Add(new MapConverterFactory());