From 34af7a332c0d39e848a292872e3e644eb1faa963 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sat, 22 Jun 2024 10:19:06 -0500 Subject: [PATCH] Update projects to .net 8 (#326) * Update codebase to target .NET 8.0 and improve JSON serialization This commit switches our target framework from .NET 6.0 to .NET 8.0 and replaces Newtonsoft.Json with System.Text.Json for serialization. The JsonConverter classes have been updated to support the new JSON model and some enhancements were applied to the codebase such as fixing a command property and updating various package references. * Align with Develop * Update SharedLibraryCore package version The version of the SharedLibraryCore package reference has been updated across multiple projects from '2024.2.4.85' to '2024.2.5.9'. Meanwhile, version within SharedLibraryCore.csproj has been changed from '2024.02.04.085' to '2024.01.01.1'. Changes also include removal of .NET 8 requirement notice and reenabling of status upload to master communicator. * Update properties in IRConParser and IRConParserConfiguration to be settable The properties in the `IRConParser` and `IRConParserConfiguration` interfaces were updated to include setters. Previously, the properties in these interfaces were read-only. This change allows for the modifications and extensions of properties defined, thereby bolstering flexibility for the handling of games and parsers. * Replace RestEase with Refit in API usage Refit has been implemented as a replacement for RestEase in all API calls. As such, all related code, parameters and imports have been adjusted to function with Refit. Logic has also been added to handle certain Refit-specific behaviours. Occurrences of the RestEase package have been removed from the project. * Enable auto-redirect in HttpClient The HttpClient instance used in Application/Main.cs has been modified to automatically follow redirect responses. This was accomplished by adding "AllowAutoRedirect = true" to the HttpClientHandler used when creating the HttpClient. --------- Co-authored-by: Amos --- .../API/GameLogServer/IGameLogServer.cs | 8 +- Application/API/GameLogServer/LogInfo.cs | 13 +- Application/API/Master/ApiInstance.cs | 12 +- Application/API/Master/ApiServer.cs | 25 +- Application/API/Master/IMasterApi.cs | 133 +++--- Application/Application.csproj | 15 +- Application/IO/GameLogEventDetection.cs | 2 +- Application/IO/GameLogReaderHttp.cs | 4 +- Application/Localization/Configure.cs | 9 +- Application/Main.cs | 20 +- Application/Misc/MasterCommunication.cs | 15 +- Application/Misc/SerializationHelpers.cs | 377 +++++++++++------- Application/Plugin/Script/ScriptPlugin.cs | 1 + Application/Plugin/Script/ScriptPluginV2.cs | 1 + Data/Data.csproj | 20 +- Integrations/Cod/Integrations.Cod.csproj | 4 +- .../Source/Integrations.Source.csproj | 2 +- .../AutomessageFeed/AutomessageFeed.csproj | 4 +- Plugins/LiveRadar/LiveRadar.csproj | 4 +- Plugins/Login/Login.csproj | 4 +- Plugins/Mute/Mute.csproj | 5 +- Plugins/Mute/Plugin.cs | 3 +- .../ProfanityDeterment.csproj | 4 +- Plugins/Stats/Stats.csproj | 4 +- Plugins/Welcome/Plugin.cs | 7 +- Plugins/Welcome/Welcome.csproj | 4 +- .../Configuration/ApplicationConfiguration.cs | 2 +- .../Configuration/CommandProperties.cs | 57 ++- SharedLibraryCore/Events/CoreEvent.cs | 2 +- .../Helpers/BuildNumberJsonConverter.cs | 75 +++- SharedLibraryCore/Interfaces/IGameServer.cs | 32 +- SharedLibraryCore/Interfaces/IManager.cs | 1 + SharedLibraryCore/Interfaces/IRConParser.cs | 17 +- .../Interfaces/IRConParserConfiguration.cs | 43 +- SharedLibraryCore/Localization/Layout.cs | 7 +- SharedLibraryCore/SharedLibraryCore.csproj | 35 +- .../Controllers/ConfigurationController.cs | 11 +- WebfrontCore/WebfrontCore.csproj | 8 +- 38 files changed, 558 insertions(+), 432 deletions(-) diff --git a/Application/API/GameLogServer/IGameLogServer.cs b/Application/API/GameLogServer/IGameLogServer.cs index 302c9040..a0eb960b 100644 --- a/Application/API/GameLogServer/IGameLogServer.cs +++ b/Application/API/GameLogServer/IGameLogServer.cs @@ -1,12 +1,12 @@ using System.Threading.Tasks; -using RestEase; +using Refit; namespace IW4MAdmin.Application.API.GameLogServer { - [Header("User-Agent", "IW4MAdmin-RestEase")] + [Headers("User-Agent: IW4MAdmin-RestEase")] public interface IGameLogServer { - [Get("log/{path}/{key}")] - Task Log([Path] string path, [Path] string key); + [Get("/log/{path}/{key}")] + Task Log(string path, string key); } } diff --git a/Application/API/GameLogServer/LogInfo.cs b/Application/API/GameLogServer/LogInfo.cs index 612d4cdc..0286d096 100644 --- a/Application/API/GameLogServer/LogInfo.cs +++ b/Application/API/GameLogServer/LogInfo.cs @@ -1,19 +1,16 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; +using System.Text.Json.Serialization; namespace IW4MAdmin.Application.API.GameLogServer { public class LogInfo { - [JsonProperty("success")] + [JsonPropertyName("success")] public bool Success { get; set; } - [JsonProperty("length")] + [JsonPropertyName("length")] public int Length { get; set; } - [JsonProperty("data")] + [JsonPropertyName("data")] public string Data { get; set; } - [JsonProperty("next_key")] + [JsonPropertyName("next_key")] public string NextKey { get; set; } } } diff --git a/Application/API/Master/ApiInstance.cs b/Application/API/Master/ApiInstance.cs index 01a98baf..c70fdca4 100644 --- a/Application/API/Master/ApiInstance.cs +++ b/Application/API/Master/ApiInstance.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using SharedLibraryCore.Helpers; namespace IW4MAdmin.Application.API.Master @@ -12,32 +12,32 @@ namespace IW4MAdmin.Application.API.Master /// /// Unique ID of the instance /// - [JsonProperty("id")] + [JsonPropertyName("id")] public string Id { get; set; } /// /// Indicates how long the instance has been running /// - [JsonProperty("uptime")] + [JsonPropertyName("uptime")] public int Uptime { get; set; } /// /// Specifies the version of the instance /// - [JsonProperty("version")] + [JsonPropertyName("version")] [JsonConverter(typeof(BuildNumberJsonConverter))] public BuildNumber Version { get; set; } /// /// List of servers the instance is monitoring /// - [JsonProperty("servers")] + [JsonPropertyName("servers")] public List Servers { get; set; } /// /// Url IW4MAdmin is listening on /// - [JsonProperty("webfront_url")] + [JsonPropertyName("webfront_url")] public string WebfrontUrl { get; set; } } } diff --git a/Application/API/Master/ApiServer.cs b/Application/API/Master/ApiServer.cs index 123a5852..4c2fe3ca 100644 --- a/Application/API/Master/ApiServer.cs +++ b/Application/API/Master/ApiServer.cs @@ -1,31 +1,28 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; +using System.Text.Json.Serialization; namespace IW4MAdmin.Application.API.Master { public class ApiServer { - [JsonProperty("id")] + [JsonPropertyName("id")] public long Id { get; set; } - [JsonProperty("ip")] + [JsonPropertyName("ip")] public string IPAddress { get; set; } - [JsonProperty("port")] + [JsonPropertyName("port")] public short Port { get; set; } - [JsonProperty("version")] + [JsonPropertyName("version")] public string Version { get; set; } - [JsonProperty("gametype")] + [JsonPropertyName("gametype")] public string Gametype { get; set; } - [JsonProperty("map")] + [JsonPropertyName("map")] public string Map { get; set; } - [JsonProperty("game")] + [JsonPropertyName("game")] public string Game { get; set; } - [JsonProperty("hostname")] + [JsonPropertyName("hostname")] public string Hostname { get; set; } - [JsonProperty("clientnum")] + [JsonPropertyName("clientnum")] public int ClientNum { get; set; } - [JsonProperty("maxclientnum")] + [JsonPropertyName("maxclientnum")] public int MaxClientNum { get; set; } } } diff --git a/Application/API/Master/IMasterApi.cs b/Application/API/Master/IMasterApi.cs index c96689f7..5c628fc6 100644 --- a/Application/API/Master/IMasterApi.cs +++ b/Application/API/Master/IMasterApi.cs @@ -1,79 +1,70 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; using System.Threading.Tasks; using IW4MAdmin.Application.Plugin; -using Newtonsoft.Json; -using RestEase; +using Refit; using SharedLibraryCore.Helpers; -namespace IW4MAdmin.Application.API.Master +namespace IW4MAdmin.Application.API.Master; + +public class AuthenticationId { - public class AuthenticationId - { - [JsonProperty("id")] - public string Id { get; set; } - } - - public class TokenId - { - [JsonProperty("access_token")] - public string AccessToken { get; set; } - } - - public class VersionInfo - { - [JsonProperty("current-version-stable")] - [JsonConverter(typeof(BuildNumberJsonConverter))] - public BuildNumber CurrentVersionStable { get; set; } - - [JsonProperty("current-version-prerelease")] - [JsonConverter(typeof(BuildNumberJsonConverter))] - public BuildNumber CurrentVersionPrerelease { get; set; } - } - - public class ResultMessage - { - [JsonProperty("message")] - public string Message { get; set; } - } - - public class PluginSubscriptionContent - { - public string Content { get; set; } - public PluginType Type { get; set; } - } - - - /// - /// Defines the capabilities of the master API - /// - [Header("User-Agent", "IW4MAdmin-RestEase")] - public interface IMasterApi - { - [Header("Authorization")] - string AuthorizationToken { get; set; } - - [Post("authenticate")] - Task Authenticate([Body] AuthenticationId Id); - - [Post("instance/")] - [AllowAnyStatusCode] - Task> AddInstance([Body] ApiInstance instance); - - [Put("instance/{id}")] - [AllowAnyStatusCode] - Task> UpdateInstance([Path] string id, [Body] ApiInstance instance); - - [Get("version/{apiVersion}")] - Task GetVersion([Path] int apiVersion); - - [Get("localization")] - Task> GetLocalization(); - - [Get("localization/{languageTag}")] - Task GetLocalization([Path("languageTag")] string languageTag); - - [Get("plugin_subscriptions")] - Task> GetPluginSubscription([Query("instance_id")] Guid instanceId, [Query("subscription_id")] string subscription_id); - } + [JsonPropertyName("id")] public string Id { get; set; } +} + +public class TokenId +{ + [JsonPropertyName("access_token")] public string AccessToken { get; set; } +} + +public class VersionInfo +{ + [JsonPropertyName("current-version-stable")] + [JsonConverter(typeof(BuildNumberJsonConverter))] + public BuildNumber CurrentVersionStable { get; set; } + + [JsonPropertyName("current-version-prerelease")] + [JsonConverter(typeof(BuildNumberJsonConverter))] + public BuildNumber CurrentVersionPrerelease { get; set; } +} + +public class ResultMessage +{ + [JsonPropertyName("message")] public string Message { get; set; } +} + +public class PluginSubscriptionContent +{ + public string Content { get; set; } + public PluginType Type { get; set; } +} + +/// +/// Defines the capabilities of the master API +/// +[Headers("User-Agent: IW4MAdmin-RestEase")] +public interface IMasterApi +{ + [Post("/authenticate")] + Task Authenticate([Body] AuthenticationId Id); + + [Post("/instance/")] + Task> AddInstance([Body] ApiInstance instance, [Header("Authorization")] string authorization); + + [Put("/instance/{id}")] + Task> UpdateInstance(string id, [Body] ApiInstance instance, [Header("Authorization")] string authorization); + + [Get("/version/{apiVersion}")] + Task GetVersion(int apiVersion); + + [Get("/localization")] + Task> GetLocalization(); + + [Get("/localization/{languageTag}")] + Task GetLocalization(string languageTag); + + [Get("/plugin_subscriptions")] + Task> GetPluginSubscription([Query("instance_id")] Guid instanceId, + [Query("subscription_id")] string subscription_id); } diff --git a/Application/Application.csproj b/Application/Application.csproj index 858f10a5..1609bc45 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 false RaidMax.IW4MAdmin.Application 2020.0.0.0 @@ -21,20 +21,21 @@ IW4MAdmin.Application false + disable - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + diff --git a/Application/IO/GameLogEventDetection.cs b/Application/IO/GameLogEventDetection.cs index eca6fb8c..54b8b9f6 100644 --- a/Application/IO/GameLogEventDetection.cs +++ b/Application/IO/GameLogEventDetection.cs @@ -82,7 +82,7 @@ namespace IW4MAdmin.Application.IO { if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != Utilities.WORLD_ID) { - gameEvent.Origin = _server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId);; + gameEvent.Origin = _server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId); } if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Target) == GameEvent.EventRequiredEntity.Target) diff --git a/Application/IO/GameLogReaderHttp.cs b/Application/IO/GameLogReaderHttp.cs index a585fa89..16a4437d 100644 --- a/Application/IO/GameLogReaderHttp.cs +++ b/Application/IO/GameLogReaderHttp.cs @@ -1,5 +1,4 @@ using IW4MAdmin.Application.API.GameLogServer; -using RestEase; using SharedLibraryCore; using SharedLibraryCore.Interfaces; using System; @@ -7,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Refit; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace IW4MAdmin.Application.IO @@ -25,7 +25,7 @@ namespace IW4MAdmin.Application.IO public GameLogReaderHttp(Uri[] gameLogServerUris, IEventParser parser, ILogger logger) { _eventParser = parser; - _logServerApi = RestClient.For(gameLogServerUris[0].ToString()); + _logServerApi = RestService.For(gameLogServerUris[0].ToString()); _safeLogPath = gameLogServerUris[1].LocalPath.ToBase64UrlSafeString(); _logger = logger; } diff --git a/Application/Localization/Configure.cs b/Application/Localization/Configure.cs index b2155e97..abe5b731 100644 --- a/Application/Localization/Configure.cs +++ b/Application/Localization/Configure.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; +using System.Text.Json; using Microsoft.Extensions.Logging; using SharedLibraryCore.Configuration; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -59,11 +60,11 @@ namespace IW4MAdmin.Application.Localization var localizationDict = new Dictionary(); - foreach (string filePath in localizationFiles) + foreach (var filePath in localizationFiles) { var localizationContents = File.ReadAllText(filePath, Encoding.UTF8); - var eachLocalizationFile = Newtonsoft.Json.JsonConvert.DeserializeObject(localizationContents); - if (eachLocalizationFile == null) + var eachLocalizationFile = JsonSerializer.Deserialize(localizationContents); + if (eachLocalizationFile is null) { continue; } @@ -72,7 +73,7 @@ namespace IW4MAdmin.Application.Localization { if (!localizationDict.TryAdd(item.Key, item.Value)) { - logger.LogError("Could not add locale string {key} to localization", item.Key); + logger.LogError("Could not add locale string {Key} to localization", item.Key); } } } diff --git a/Application/Main.cs b/Application/Main.cs index bcded032..d4c3e62b 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -5,7 +5,6 @@ using IW4MAdmin.Application.Meta; using IW4MAdmin.Application.Migration; using IW4MAdmin.Application.Misc; using Microsoft.Extensions.DependencyInjection; -using RestEase; using SharedLibraryCore; using SharedLibraryCore.Configuration; using SharedLibraryCore.Database.Models; @@ -39,6 +38,7 @@ using ILogger = Microsoft.Extensions.Logging.ILogger; using IW4MAdmin.Plugins.Stats.Client.Abstractions; using IW4MAdmin.Plugins.Stats.Client; using Microsoft.Extensions.Hosting; +using Refit; using Stats.Client.Abstractions; using Stats.Client; using Stats.Config; @@ -94,15 +94,6 @@ namespace IW4MAdmin.Application Console.WriteLine($" Version {Utilities.GetVersionAsString()}"); Console.WriteLine("====================================================="); - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("!!!! IMPORTANT !!!!"); - Console.WriteLine("The next update of IW4MAdmin will require .NET 8."); - Console.WriteLine("This is a breaking change!"); - Console.WriteLine( - "Please update the ASP.NET Core Runtime: https://dotnet.microsoft.com/en-us/download/dotnet/8.0"); - Console.WriteLine("!!!!!!!!!!!!!!!!!!!"); - Console.ForegroundColor = ConsoleColor.Gray; - await LaunchAsync(); } @@ -451,12 +442,12 @@ namespace IW4MAdmin.Application var masterUri = Utilities.IsDevelopment ? new Uri("http://127.0.0.1:8080") : appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl; - var httpClient = new HttpClient + var httpClient = new HttpClient(new HttpClientHandler {AllowAutoRedirect = true}) { BaseAddress = masterUri, Timeout = TimeSpan.FromSeconds(15) }; - var masterRestClient = RestClient.For(httpClient); + var masterRestClient = RestService.For(httpClient); var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig); if (appConfig == null) @@ -469,10 +460,7 @@ namespace IW4MAdmin.Application // register override level names foreach (var (key, value) in appConfig.OverridePermissionLevelNames) { - if (!Utilities.PermissionLevelOverrides.ContainsKey(key)) - { - Utilities.PermissionLevelOverrides.Add(key, value); - } + Utilities.PermissionLevelOverrides.TryAdd(key, value); } // build the dependency list diff --git a/Application/Misc/MasterCommunication.cs b/Application/Misc/MasterCommunication.cs index 76fc0904..86adc1a6 100644 --- a/Application/Misc/MasterCommunication.cs +++ b/Application/Misc/MasterCommunication.cs @@ -1,5 +1,4 @@ using IW4MAdmin.Application.API.Master; -using RestEase; using SharedLibraryCore; using SharedLibraryCore.Configuration; using SharedLibraryCore.Helpers; @@ -9,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Refit; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace IW4MAdmin.Application.Misc @@ -28,6 +28,7 @@ namespace IW4MAdmin.Application.Misc private readonly int _apiVersion = 1; private bool _firstHeartBeat = true; private static readonly TimeSpan Interval = TimeSpan.FromSeconds(30); + private string _authorizationToken; public MasterCommunication(ILogger logger, ApplicationConfiguration appConfig, ITranslationLookup translationLookup, IMasterApi apiInstance, IManager manager) { @@ -128,7 +129,7 @@ namespace IW4MAdmin.Application.Misc Id = _appConfig.Id }); - _apiInstance.AuthorizationToken = $"Bearer {token.AccessToken}"; + _authorizationToken = $"Bearer {token.AccessToken}"; } var instance = new ApiInstance @@ -153,22 +154,22 @@ namespace IW4MAdmin.Application.Misc WebfrontUrl = _appConfig.WebfrontUrl }; - Response response; + IApiResponse response; if (_firstHeartBeat) { - response = await _apiInstance.AddInstance(instance); + response = await _apiInstance.AddInstance(instance, _authorizationToken); } else { - response = await _apiInstance.UpdateInstance(instance.Id, instance); + response = await _apiInstance.UpdateInstance(instance.Id, instance, _authorizationToken); _firstHeartBeat = false; } - if (response.ResponseMessage.StatusCode != System.Net.HttpStatusCode.OK) + if (response.StatusCode != System.Net.HttpStatusCode.OK) { - _logger.LogWarning("Non success response code from master is {StatusCode}, message is {Message}", response.ResponseMessage.StatusCode, response.StringContent); + _logger.LogWarning("Non success response code from master is {StatusCode}, message is {Message}", response.StatusCode, response.Error?.Content); } } } diff --git a/Application/Misc/SerializationHelpers.cs b/Application/Misc/SerializationHelpers.cs index bfb7e0b9..9d8a5f03 100644 --- a/Application/Misc/SerializationHelpers.cs +++ b/Application/Misc/SerializationHelpers.cs @@ -1,150 +1,255 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using SharedLibraryCore; +using SharedLibraryCore; using SharedLibraryCore.Database.Models; using System; using System.Net; +using System.Text.Json; +using System.Text.Json.Serialization; using Data.Models; using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.GameEvent; -namespace IW4MAdmin.Application.Misc +namespace IW4MAdmin.Application.Misc; + +public class IPAddressConverter : JsonConverter { - class IPAddressConverter : JsonConverter + public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(IPAddress)); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(value.ToString()); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - return IPAddress.Parse((string)reader.Value); - } + var ipAddressString = reader.GetString(); + return IPAddress.Parse(ipAddressString); } - class IPEndPointConverter : JsonConverter + public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options) { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(IPEndPoint)); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - IPEndPoint ep = (IPEndPoint)value; - JObject jo = new JObject(); - jo.Add("Address", JToken.FromObject(ep.Address, serializer)); - jo.Add("Port", ep.Port); - jo.WriteTo(writer); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - JObject jo = JObject.Load(reader); - IPAddress address = jo["Address"].ToObject(serializer); - int port = (int)jo["Port"]; - return new IPEndPoint(address, port); - } - } - - class ClientEntityConverter : JsonConverter - { - public override bool CanConvert(Type objectType) => objectType == typeof(EFClient); - - public override object ReadJson(JsonReader reader, Type objectType,object existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - { - return null; - } - - var jsonObject = JObject.Load(reader); - - return new EFClient - { - NetworkId = (long)jsonObject["NetworkId"], - ClientNumber = (int)jsonObject["ClientNumber"], - State = Enum.Parse(jsonObject["state"].ToString()), - CurrentAlias = new EFAlias() - { - IPAddress = (int?)jsonObject["IPAddress"], - Name = jsonObject["Name"].ToString() - } - }; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var client = value as EFClient; - var jsonObject = new JObject - { - { "NetworkId", client.NetworkId }, - { "ClientNumber", client.ClientNumber }, - { "IPAddress", client.CurrentAlias?.IPAddress }, - { "Name", client.CurrentAlias?.Name }, - { "State", (int)client.State } - }; - - jsonObject.WriteTo(writer); - } - } - - class GameEventConverter : JsonConverter - { - public override bool CanConvert(Type objectType) =>objectType == typeof(GameEvent); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); - - return new GameEvent - { - Type = Enum.Parse(jsonObject["Type"].ToString()), - Subtype = jsonObject["Subtype"]?.ToString(), - Source = Enum.Parse(jsonObject["Source"].ToString()), - RequiredEntity = Enum.Parse(jsonObject["RequiredEntity"].ToString()), - Data = jsonObject["Data"].ToString(), - Message = jsonObject["Message"].ToString(), - GameTime = (int?)jsonObject["GameTime"], - Origin = jsonObject["Origin"]?.ToObject(serializer), - Target = jsonObject["Target"]?.ToObject(serializer), - ImpersonationOrigin = jsonObject["ImpersonationOrigin"]?.ToObject(serializer), - IsRemote = (bool)jsonObject["IsRemote"], - Extra = null, // fix - Time = (DateTime)jsonObject["Time"], - IsBlocking = (bool)jsonObject["IsBlocking"] - }; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var gameEvent = value as GameEvent; - - var jsonObject = new JObject - { - { "Type", (int)gameEvent.Type }, - { "Subtype", gameEvent.Subtype }, - { "Source", (int)gameEvent.Source }, - { "RequiredEntity", (int)gameEvent.RequiredEntity }, - { "Data", gameEvent.Data }, - { "Message", gameEvent.Message }, - { "GameTime", gameEvent.GameTime }, - { "Origin", gameEvent.Origin != null ? JToken.FromObject(gameEvent.Origin, serializer) : null }, - { "Target", gameEvent.Target != null ? JToken.FromObject(gameEvent.Target, serializer) : null }, - { "ImpersonationOrigin", gameEvent.ImpersonationOrigin != null ? JToken.FromObject(gameEvent.ImpersonationOrigin, serializer) : null}, - { "IsRemote", gameEvent.IsRemote }, - { "Extra", gameEvent.Extra?.ToString() }, - { "Time", gameEvent.Time }, - { "IsBlocking", gameEvent.IsBlocking } - }; - - jsonObject.WriteTo(writer); - } + writer.WriteStringValue(value.ToString()); + } +} + +public class IPEndPointConverter : JsonConverter +{ + public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + IPAddress address = null; + var port = 0; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "Address": + var addressString = reader.GetString(); + address = IPAddress.Parse(addressString); + break; + case "Port": + port = reader.GetInt32(); + break; + } + } + + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + } + + return new IPEndPoint(address, port); + } + + public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteString("Address", value.Address.ToString()); + writer.WriteNumber("Port", value.Port); + writer.WriteEndObject(); + } +} + +public class ClientEntityConverter : JsonConverter +{ + public override EFClient Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + long networkId = default; + int clientNumber = default; + ClientState state = default; + var currentAlias = new EFAlias(); + int? ipAddress = null; + string name = null; + + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); // Advance to the value. + switch (propertyName) + { + case "NetworkId": + networkId = reader.GetInt64(); + break; + case "ClientNumber": + clientNumber = reader.GetInt32(); + break; + case "State": + state = (ClientState)reader.GetInt32(); + break; + case "IPAddress": + ipAddress = reader.TokenType != JsonTokenType.Null ? reader.GetInt32() : null; + break; + case "Name": + name = reader.GetString(); + break; + } + } + } + + currentAlias.IPAddress = ipAddress; + currentAlias.Name = name; + + return new EFClient + { + NetworkId = networkId, + ClientNumber = clientNumber, + State = state, + CurrentAlias = currentAlias + }; + } + + public override void Write(Utf8JsonWriter writer, EFClient value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteNumber("NetworkId", value.NetworkId); + writer.WriteNumber("ClientNumber", value.ClientNumber); + writer.WriteString("State", value.State.ToString()); + + if (value.CurrentAlias != null) + { + writer.WriteNumber("IPAddress", value.CurrentAlias.IPAddress ?? 0); + writer.WriteString("Name", value.CurrentAlias.Name); + } + else + { + writer.WriteNull("IPAddress"); + writer.WriteNull("Name"); + } + + writer.WriteEndObject(); + } +} + +public class GameEventConverter : JsonConverter +{ + public override GameEvent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + var gameEvent = new GameEvent(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "Type": + gameEvent.Type = (EventType)reader.GetInt32(); + break; + case "Subtype": + gameEvent.Subtype = reader.GetString(); + break; + case "Source": + gameEvent.Source = (EventSource)reader.GetInt32(); + break; + case "RequiredEntity": + gameEvent.RequiredEntity = (EventRequiredEntity)reader.GetInt32(); + break; + case "Data": + gameEvent.Data = reader.GetString(); + break; + case "Message": + gameEvent.Message = reader.GetString(); + break; + case "GameTime": + gameEvent.GameTime = reader.TokenType != JsonTokenType.Null ? reader.GetInt32() : null; + break; + case "Origin": + gameEvent.Origin = JsonSerializer.Deserialize(ref reader, options); + break; + case "Target": + gameEvent.Target = JsonSerializer.Deserialize(ref reader, options); + break; + case "ImpersonationOrigin": + gameEvent.ImpersonationOrigin = JsonSerializer.Deserialize(ref reader, options); + break; + case "IsRemote": + gameEvent.IsRemote = reader.GetBoolean(); + break; + case "Time": + gameEvent.Time = reader.GetDateTime(); + break; + case "IsBlocking": + gameEvent.IsBlocking = reader.GetBoolean(); + break; + } + } + } + + return gameEvent; + } + + public override void Write(Utf8JsonWriter writer, GameEvent value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteNumber("Type", (int)value.Type); + writer.WriteString("Subtype", value.Subtype); + writer.WriteNumber("Source", (int)value.Source); + writer.WriteNumber("RequiredEntity", (int)value.RequiredEntity); + writer.WriteString("Data", value.Data); + writer.WriteString("Message", value.Message); + if (value.GameTime.HasValue) + { + writer.WriteNumber("GameTime", value.GameTime.Value); + } + else + { + writer.WriteNull("GameTime"); + } + + if (value.Origin != null) + { + writer.WritePropertyName("Origin"); + JsonSerializer.Serialize(writer, value.Origin, options); + } + + if (value.Target != null) + { + writer.WritePropertyName("Target"); + JsonSerializer.Serialize(writer, value.Target, options); + } + + if (value.ImpersonationOrigin != null) + { + writer.WritePropertyName("ImpersonationOrigin"); + JsonSerializer.Serialize(writer, value.ImpersonationOrigin, options); + } + + writer.WriteBoolean("IsRemote", value.IsRemote); + writer.WriteString("Time", value.Time.ToString("o")); + writer.WriteBoolean("IsBlocking", value.IsBlocking); + + writer.WriteEndObject(); } } diff --git a/Application/Plugin/Script/ScriptPlugin.cs b/Application/Plugin/Script/ScriptPlugin.cs index b5da929f..49822c11 100644 --- a/Application/Plugin/Script/ScriptPlugin.cs +++ b/Application/Plugin/Script/ScriptPlugin.cs @@ -23,6 +23,7 @@ using SharedLibraryCore.Database.Models; using SharedLibraryCore.Exceptions; using SharedLibraryCore.Interfaces; using ILogger = Microsoft.Extensions.Logging.ILogger; +using Reference = Data.Models.Reference; namespace IW4MAdmin.Application.Plugin.Script { diff --git a/Application/Plugin/Script/ScriptPluginV2.cs b/Application/Plugin/Script/ScriptPluginV2.cs index 26edcaa2..8ac8b2a6 100644 --- a/Application/Plugin/Script/ScriptPluginV2.cs +++ b/Application/Plugin/Script/ScriptPluginV2.cs @@ -26,6 +26,7 @@ using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces.Events; using ILogger = Microsoft.Extensions.Logging.ILogger; using JavascriptEngine = Jint.Engine; +using Reference = Data.Models.Reference; namespace IW4MAdmin.Application.Plugin.Script; diff --git a/Data/Data.csproj b/Data/Data.csproj index 75cbf585..412a8aca 100644 --- a/Data/Data.csproj +++ b/Data/Data.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 Debug;Release;Prerelease AnyCPU RaidMax.IW4MAdmin.Data @@ -9,16 +9,20 @@ - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all runtime; build; native; contentfiles - - - - + + + + diff --git a/Integrations/Cod/Integrations.Cod.csproj b/Integrations/Cod/Integrations.Cod.csproj index 7d10e54a..de12b577 100644 --- a/Integrations/Cod/Integrations.Cod.csproj +++ b/Integrations/Cod/Integrations.Cod.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 Integrations.Cod Integrations.Cod Debug;Release;Prerelease @@ -17,7 +17,7 @@ - + diff --git a/Integrations/Source/Integrations.Source.csproj b/Integrations/Source/Integrations.Source.csproj index cd174636..b33da35b 100644 --- a/Integrations/Source/Integrations.Source.csproj +++ b/Integrations/Source/Integrations.Source.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 Integrations.Source Integrations.Source Debug;Release;Prerelease diff --git a/Plugins/AutomessageFeed/AutomessageFeed.csproj b/Plugins/AutomessageFeed/AutomessageFeed.csproj index 1a79d2e5..4a5a0ac7 100644 --- a/Plugins/AutomessageFeed/AutomessageFeed.csproj +++ b/Plugins/AutomessageFeed/AutomessageFeed.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 true Latest Debug;Release;Prerelease @@ -10,7 +10,7 @@ - + diff --git a/Plugins/LiveRadar/LiveRadar.csproj b/Plugins/LiveRadar/LiveRadar.csproj index a5a50206..a1630dfd 100644 --- a/Plugins/LiveRadar/LiveRadar.csproj +++ b/Plugins/LiveRadar/LiveRadar.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 true true false @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Login/Login.csproj b/Plugins/Login/Login.csproj index b3a92171..2a48f854 100644 --- a/Plugins/Login/Login.csproj +++ b/Plugins/Login/Login.csproj @@ -2,7 +2,7 @@ Library - net6.0 + net8.0 false @@ -19,7 +19,7 @@ - + diff --git a/Plugins/Mute/Mute.csproj b/Plugins/Mute/Mute.csproj index 19e6bb03..cb66c4d4 100644 --- a/Plugins/Mute/Mute.csproj +++ b/Plugins/Mute/Mute.csproj @@ -1,18 +1,17 @@ - net6.0 + net8.0 enable enable MrAmos123 Library Debug;Release;Prerelease AnyCPU - IW4MAdmin.Plugins.Mute - + diff --git a/Plugins/Mute/Plugin.cs b/Plugins/Mute/Plugin.cs index fc75ea27..d1c36815 100644 --- a/Plugins/Mute/Plugin.cs +++ b/Plugins/Mute/Plugin.cs @@ -22,7 +22,7 @@ public class Plugin : IPluginV2 public const string MuteKey = "IW4MMute"; public static IManager Manager { get; private set; } = null!; public static Server.Game[] SupportedGames { get; private set; } = Array.Empty(); - private static readonly string[] DisabledCommands = {nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"}; + private static readonly string[] DisabledCommands = [nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"]; private readonly IInteractionRegistration _interactionRegistration; private readonly IRemoteCommandService _remoteCommandService; private readonly MuteManager _muteManager; @@ -37,6 +37,7 @@ public class Plugin : IPluginV2 IManagementEventSubscriptions.Load += OnLoad; IManagementEventSubscriptions.Unload += OnUnload; + IManagementEventSubscriptions.ClientStateInitialized += OnClientStateInitialized; IGameServerEventSubscriptions.ClientDataUpdated += OnClientDataUpdated; diff --git a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj index 8334a992..9b3452fd 100644 --- a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj +++ b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj @@ -2,7 +2,7 @@ Library - net6.0 + net8.0 RaidMax.IW4MAdmin.Plugins.ProfanityDeterment @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Stats/Stats.csproj b/Plugins/Stats/Stats.csproj index 7aff2f0c..7d65d949 100644 --- a/Plugins/Stats/Stats.csproj +++ b/Plugins/Stats/Stats.csproj @@ -2,7 +2,7 @@ Library - net6.0 + net8.0 RaidMax.IW4MAdmin.Plugins.Stats @@ -17,7 +17,7 @@ - + diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index e5189f11..30d43870 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -6,8 +6,8 @@ using SharedLibraryCore.Database.Models; using System.Linq; using Microsoft.EntityFrameworkCore; using System.Net.Http; +using System.Text.Json; using System.Threading; -using Newtonsoft.Json.Linq; using Humanizer; using Data.Abstractions; using Data.Models; @@ -108,8 +108,9 @@ public class Plugin : IPluginV2 var response = await wc.GetStringAsync(new Uri( $"http://ip-api.com/json/{ip}?lang={Utilities.CurrentLocalization.LocalizationName.Split("-").First().ToLower()}")); - var responseObj = JObject.Parse(response); - response = responseObj["country"]?.ToString(); + + var json = JsonDocument.Parse(response); + response = json.RootElement.TryGetProperty("country", out var countryElement) ? countryElement.GetString() : null; return string.IsNullOrEmpty(response) ? Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_COUNTRY"] diff --git a/Plugins/Welcome/Welcome.csproj b/Plugins/Welcome/Welcome.csproj index de9255ef..5e856f32 100644 --- a/Plugins/Welcome/Welcome.csproj +++ b/Plugins/Welcome/Welcome.csproj @@ -2,7 +2,7 @@ Library - net6.0 + net8.0 RaidMax.IW4MAdmin.Plugins.Welcome @@ -20,7 +20,7 @@ - + diff --git a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs index 4e5c9c16..092fa7e3 100644 --- a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs +++ b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Text.Json.Serialization; using Data.Models.Misc; -using Newtonsoft.Json; using SharedLibraryCore.Configuration.Attributes; using SharedLibraryCore.Interfaces; using static Data.Models.Client.EFClient; diff --git a/SharedLibraryCore/Configuration/CommandProperties.cs b/SharedLibraryCore/Configuration/CommandProperties.cs index da524910..e56c0e31 100644 --- a/SharedLibraryCore/Configuration/CommandProperties.cs +++ b/SharedLibraryCore/Configuration/CommandProperties.cs @@ -1,41 +1,40 @@ using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; +using SharedLibraryCore.Helpers; using static Data.Models.Client.EFClient; using static SharedLibraryCore.Server; -namespace SharedLibraryCore.Configuration +namespace SharedLibraryCore.Configuration; + +/// +/// Config driven command properties +/// +public class CommandProperties { /// - /// Config driven command properties + /// Specifies the command name /// - public class CommandProperties - { - /// - /// Specifies the command name - /// - public string Name { get; set; } + public string Name { get; set; } - /// - /// Alias of this command - /// - public string Alias { get; set; } + /// + /// Alias of this command + /// + public string Alias { get; set; } - /// - /// Specifies the minimum permission level needed to execute the - /// - [JsonConverter(typeof(StringEnumConverter))] - public Permission MinimumPermission { get; set; } + /// + /// Specifies the minimum permission level needed to execute the + /// + [JsonConverter(typeof(JsonStringEnumConverter))] + public Permission MinimumPermission { get; set; } - /// - /// Indicates if the command can be run by another user (impersonation) - /// - public bool AllowImpersonation { get; set; } + /// + /// Indicates if the command can be run by another user (impersonation) + /// + public bool AllowImpersonation { get; set; } - /// - /// Specifies the games supporting the functionality of the command - /// - [JsonProperty(ItemConverterType = typeof(StringEnumConverter))] - public Game[] SupportedGames { get; set; } = Array.Empty(); - } + /// + /// Specifies the games supporting the functionality of the command + /// + [JsonConverter(typeof(GameArrayJsonConverter))] + public Game[] SupportedGames { get; set; } = Array.Empty(); } diff --git a/SharedLibraryCore/Events/CoreEvent.cs b/SharedLibraryCore/Events/CoreEvent.cs index 17d44b49..1891d64e 100644 --- a/SharedLibraryCore/Events/CoreEvent.cs +++ b/SharedLibraryCore/Events/CoreEvent.cs @@ -6,7 +6,7 @@ public abstract class CoreEvent { public Guid Id { get; } = Guid.NewGuid(); public Guid? CorrelationId { get; init; } - public object Source { get; init; } + public object Source { get; set; } public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow; public DateTimeOffset? ProcessedAt { get; set; } } diff --git a/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs b/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs index cdac5943..a5aae46a 100644 --- a/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs +++ b/SharedLibraryCore/Helpers/BuildNumberJsonConverter.cs @@ -1,27 +1,58 @@ using System; -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; -namespace SharedLibraryCore.Helpers +namespace SharedLibraryCore.Helpers; + +/// +/// JSON converter for the build number +/// +public class BuildNumberJsonConverter : JsonConverter { - /// - /// JSON converter for the build number - /// - public class BuildNumberJsonConverter : JsonConverter + public override BuildNumber Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override bool CanConvert(Type objectType) - { - return objectType == typeof(string); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, - JsonSerializer serializer) - { - return BuildNumber.Parse(reader.Value.ToString()); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(value.ToString()); - } + var stringValue = reader.GetString(); + return BuildNumber.Parse(stringValue); } -} \ No newline at end of file + + public override void Write(Utf8JsonWriter writer, BuildNumber value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} + +public class GameArrayJsonConverter : JsonConverter +{ + public override Server.Game[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + List games = []; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + break; + } + + var gameString = reader.GetString(); + var game = Enum.Parse(gameString); + games.Add(game); + } + + return games.ToArray(); + } + + public override void Write(Utf8JsonWriter writer, Server.Game[] value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var game in value) + { + writer.WriteStringValue(game.ToString()); + } + + writer.WriteEndArray(); + } +} + diff --git a/SharedLibraryCore/Interfaces/IGameServer.cs b/SharedLibraryCore/Interfaces/IGameServer.cs index e28bd2b1..6ae70bdf 100644 --- a/SharedLibraryCore/Interfaces/IGameServer.cs +++ b/SharedLibraryCore/Interfaces/IGameServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading; using System.Threading.Tasks; using Data.Models; @@ -19,6 +20,9 @@ namespace SharedLibraryCore.Interfaces /// Task Kick(string reason, EFClient target, EFClient origin, EFPenalty previousPenalty = null); + IPEndPoint ResolvedIpEndPoint { get; } + IRConParser RconParser { get; } + /// /// Execute a server command /// @@ -35,72 +39,72 @@ namespace SharedLibraryCore.Interfaces /// /// Task SetDvarAsync(string name, object value, CancellationToken token = default); - + /// /// Time the most recent match ended /// DateTime? MatchEndTime { get; } - + /// /// Time the current match started /// DateTime? MatchStartTime { get; } - + /// /// List of connected clients /// IReadOnlyList ConnectedClients { get; } - + /// /// Game code corresponding to the development studio project /// Reference.Game GameCode { get; } - + /// /// Indicates if the anticheat/custom callbacks/live radar integration is enabled /// bool IsLegacyGameIntegrationEnabled { get; } - + /// /// Unique identifier for the server (typically ip:port) /// string Id { get; } - + /// /// Network address the server is listening on /// string ListenAddress { get; } - + /// /// Network port the server is listening on /// int ListenPort { get; } - + /// /// Name of the server (hostname) /// string ServerName { get; } - + /// /// Current gametype /// string Gametype { get; } - + /// /// Game password (required to join) /// string GamePassword { get; } - + /// /// Number of private client slots /// int PrivateClientSlots { get; } - + /// /// Current map the game server is running /// Map Map { get; } - + /// /// Database id for EFServer table and references /// diff --git a/SharedLibraryCore/Interfaces/IManager.cs b/SharedLibraryCore/Interfaces/IManager.cs index 3f0b4c6e..8805f939 100644 --- a/SharedLibraryCore/Interfaces/IManager.cs +++ b/SharedLibraryCore/Interfaces/IManager.cs @@ -41,6 +41,7 @@ namespace SharedLibraryCore.Interfaces ILogger GetLogger(long serverId); IList GetServers(); + List Servers { get; } IList GetCommands(); IList GetMessageTokens(); IList GetActiveClients(); diff --git a/SharedLibraryCore/Interfaces/IRConParser.cs b/SharedLibraryCore/Interfaces/IRConParser.cs index deee71d6..6316276d 100644 --- a/SharedLibraryCore/Interfaces/IRConParser.cs +++ b/SharedLibraryCore/Interfaces/IRConParser.cs @@ -15,35 +15,35 @@ namespace SharedLibraryCore.Interfaces /// /// stores the game/client specific version (usually the value of the "version" DVAR) /// - string Version { get; } + string Version { get; set; } /// /// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...) /// - Game GameName { get; } + Game GameName { get; set; } /// /// indicates if the game supports generating a log path from DVAR retrieval /// of fs_game, fs_basepath, g_log /// - bool CanGenerateLogPath { get; } + bool CanGenerateLogPath { get; set; } /// /// specifies the name of the parser /// - string Name { get; } + string Name { get; set; } /// /// specifies the type of rcon engine /// eg: COD, Source /// - string RConEngine { get; } + string RConEngine { get; set; } /// /// indicates that the game does not log to the mods folder (when mod is loaded), /// but rather always to the fs_basegame directory /// - bool IsOneLog { get; } + bool IsOneLog { get; set; } /// /// retrieves the value of a given DVAR @@ -54,7 +54,8 @@ namespace SharedLibraryCore.Interfaces /// default value to return if dvar retrieval fails /// /// - Task> GetDvarAsync(IRConConnection connection, string dvarName, T fallbackValue = default, CancellationToken token = default); + Task> GetDvarAsync(IRConConnection connection, string dvarName, T fallbackValue = default, + CancellationToken token = default); /// /// set value of DVAR by name @@ -65,7 +66,7 @@ namespace SharedLibraryCore.Interfaces /// /// Task SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue, CancellationToken token = default); - + /// /// executes a console command on the server /// diff --git a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs index f3da4b2c..c6473199 100644 --- a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs @@ -10,74 +10,74 @@ namespace SharedLibraryCore.Interfaces /// /// stores the command format for console commands /// - CommandPrefix CommandPrefixes { get; } + CommandPrefix CommandPrefixes { get; set; } /// /// stores the regex info for parsing get status response /// - ParserRegex Status { get; } + ParserRegex Status { get; set; } /// /// stores regex info for parsing the map line from rcon status response /// - ParserRegex MapStatus { get; } + ParserRegex MapStatus { get; set; } /// /// stores regex info for parsing the gametype line from rcon status response /// - ParserRegex GametypeStatus { get; } + ParserRegex GametypeStatus { get; set; } /// /// stores regex info for parsing hostname line from rcon status response /// - ParserRegex HostnameStatus { get; } + ParserRegex HostnameStatus { get; set; } /// /// stores regex info for parsing max players line from rcon status response /// - ParserRegex MaxPlayersStatus { get; } + ParserRegex MaxPlayersStatus { get; set; } /// /// stores the regex info for parsing get DVAR responses /// - ParserRegex Dvar { get; } + ParserRegex Dvar { get; set; } /// /// stores the regex info for parsing the header of a status response /// - ParserRegex StatusHeader { get; } + ParserRegex StatusHeader { get; set; } /// /// Specifies the expected response message from rcon when the server is not running /// - string ServerNotRunningResponse { get; } + string ServerNotRunningResponse { get; set; } /// /// indicates if the application should wait for response from server /// when executing a command /// - bool WaitForResponse { get; } + bool WaitForResponse { get; set; } /// /// indicates the format expected for parsed guids /// - NumberStyles GuidNumberStyle { get; } + NumberStyles GuidNumberStyle { get; set; } /// /// specifies simple mappings for dvar names in scenarios where the needed /// information is not stored in a traditional dvar name /// - IDictionary OverrideDvarNameMapping { get; } + IDictionary OverrideDvarNameMapping { get; set; } /// /// specifies the default dvar values for games that don't support certain dvars /// - IDictionary DefaultDvarValues { get; } + IDictionary DefaultDvarValues { get; set; } /// /// contains a setup of commands that have override timeouts /// - IDictionary OverrideCommandTimeouts { get; } + IDictionary OverrideCommandTimeouts { get; set; } /// /// specifies how many lines can be used for ingame notice @@ -87,29 +87,30 @@ namespace SharedLibraryCore.Interfaces /// /// specifies how many characters can be displayed per notice line /// - int NoticeMaxCharactersPerLine { get; } + int NoticeMaxCharactersPerLine { get; set; } /// /// specifies the characters used to split a line /// - string NoticeLineSeparator { get; } + string NoticeLineSeparator { get; set; } /// /// Default port the game listens to RCon requests on /// - int? DefaultRConPort { get; } + int? DefaultRConPort { get; set; } /// /// Default Indicator of where the game is installed (ex file path or registry entry) /// - string DefaultInstallationDirectoryHint { get; } + string DefaultInstallationDirectoryHint { get; set; } - ColorCodeMapping ColorCodeMapping { get; } + ColorCodeMapping ColorCodeMapping { get; set; } + + short FloodProtectInterval { get; set; } - short FloodProtectInterval { get; } /// /// indicates if diacritics (accented characters) should be normalized /// - bool ShouldRemoveDiacritics { get; } + bool ShouldRemoveDiacritics { get; set; } } } diff --git a/SharedLibraryCore/Localization/Layout.cs b/SharedLibraryCore/Localization/Layout.cs index a4b330e7..c7f58bb6 100644 --- a/SharedLibraryCore/Localization/Layout.cs +++ b/SharedLibraryCore/Localization/Layout.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Globalization; +using System.Text.Json.Serialization; using SharedLibraryCore.Interfaces; namespace SharedLibraryCore.Localization @@ -8,6 +9,8 @@ namespace SharedLibraryCore.Localization { private string localizationName; + public Layout() { } + public Layout(Dictionary set) { LocalizationIndex = new TranslationLookup @@ -27,7 +30,7 @@ namespace SharedLibraryCore.Localization } public TranslationLookup LocalizationIndex { get; set; } - public CultureInfo Culture { get; private set; } + [JsonIgnore] public CultureInfo Culture { get; private set; } } public class TranslationLookup : ITranslationLookup @@ -47,4 +50,4 @@ namespace SharedLibraryCore.Localization } } } -} \ No newline at end of file +} diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index 081458e6..d77a87a6 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -2,9 +2,9 @@ Library - net6.0 + net8.0 RaidMax.IW4MAdmin.SharedLibraryCore - 2023.4.5.1 + 2024.01.01.1 RaidMax Forever None Debug;Release;Prerelease @@ -19,7 +19,7 @@ true MIT Shared Library for IW4MAdmin - 2023.4.5.1 + 2024.01.01.1 true $(NoWarn);1591 @@ -34,31 +34,30 @@ - + - - - - - - - - - - + + + + + + + + + - - true - Data.dll - + + true + Data.dll + diff --git a/WebfrontCore/Controllers/ConfigurationController.cs b/WebfrontCore/Controllers/ConfigurationController.cs index 784f3703..876c49e5 100644 --- a/WebfrontCore/Controllers/ConfigurationController.cs +++ b/WebfrontCore/Controllers/ConfigurationController.cs @@ -10,10 +10,9 @@ using SharedLibraryCore.Interfaces; using System.Linq; using System.Reflection; using System.Text; +using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using WebfrontCore.ViewModels; namespace WebfrontCore.Controllers @@ -92,15 +91,15 @@ namespace WebfrontCore.Controllers try { - var file = JObject.Parse(content); + var jsonDocument = JsonDocument.Parse(content); } - catch (JsonReaderException ex) + catch (JsonException ex) { return BadRequest($"{fileName}: {ex.Message}"); } - var path = System.IO.Path.Join(Utilities.OperatingDirectory, "Configuration", - fileName.Replace($"{System.IO.Path.DirectorySeparatorChar}", "")); + var path = Path.Join(Utilities.OperatingDirectory, "Configuration", + fileName.Replace($"{Path.DirectorySeparatorChar}", "")); // todo: move into a service at some point if (!System.IO.File.Exists(path)) diff --git a/WebfrontCore/WebfrontCore.csproj b/WebfrontCore/WebfrontCore.csproj index 8ce9eb47..b8502413 100644 --- a/WebfrontCore/WebfrontCore.csproj +++ b/WebfrontCore/WebfrontCore.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 true true true @@ -46,9 +46,9 @@ - - - + + +