diff --git a/Core/Annotations/Injectable.cs b/Core/Annotations/Injectable.cs index 667d0fb2..86b1d906 100644 --- a/Core/Annotations/Injectable.cs +++ b/Core/Annotations/Injectable.cs @@ -1,4 +1,4 @@ -namespace Types.Annotations; +namespace Core.Annotations; [AttributeUsage(AttributeTargets.Class)] public class Injectable(InjectionType injectionType = InjectionType.Scoped, Type? type = null) : Attribute diff --git a/Core/Context/ApplicationContext.cs b/Core/Context/ApplicationContext.cs index 51f2ce84..05b10522 100644 --- a/Core/Context/ApplicationContext.cs +++ b/Core/Context/ApplicationContext.cs @@ -1,6 +1,6 @@ -using Types.Annotations; +using Core.Annotations; -namespace Types.Context; +namespace Core.Context; [Injectable(InjectionType.Singleton)] public class ApplicationContext diff --git a/Core/Context/ContextVariable.cs b/Core/Context/ContextVariable.cs index 7bea953c..3e79b5f0 100644 --- a/Core/Context/ContextVariable.cs +++ b/Core/Context/ContextVariable.cs @@ -1,4 +1,4 @@ -namespace Types.Context; +namespace Core.Context; public class ContextVariable(object value, ContextVariableType contextVariableType) { diff --git a/Core/Context/ContextVariableType.cs b/Core/Context/ContextVariableType.cs index 0f64ca1b..409e7656 100644 --- a/Core/Context/ContextVariableType.cs +++ b/Core/Context/ContextVariableType.cs @@ -1,4 +1,4 @@ -namespace Types.Context; +namespace Core.Context; public enum ContextVariableType { diff --git a/Core/Core.csproj b/Core/Core.csproj index c84e1b8b..aa09572c 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -5,7 +5,6 @@ enable enable Library - Types diff --git a/Core/Models/Config/HttpConfig.cs b/Core/Models/Config/HttpConfig.cs index 71a1c612..b661f3b1 100644 --- a/Core/Models/Config/HttpConfig.cs +++ b/Core/Models/Config/HttpConfig.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Config; +namespace Core.Models.Config; public class HttpConfig { diff --git a/Core/Models/Eft/Common/Tables/BotBase.cs b/Core/Models/Eft/Common/Tables/BotBase.cs index a0b37417..df51dbf4 100644 --- a/Core/Models/Eft/Common/Tables/BotBase.cs +++ b/Core/Models/Eft/Common/Tables/BotBase.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Types.Models.Eft.Common.Tables; +namespace Core.Models.Eft.Common.Tables; public class BotBase { diff --git a/Core/Models/Eft/Common/Tables/BotCore.cs b/Core/Models/Eft/Common/Tables/BotCore.cs index 5d6c8952..38732b91 100644 --- a/Core/Models/Eft/Common/Tables/BotCore.cs +++ b/Core/Models/Eft/Common/Tables/BotCore.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Eft.Common.Tables; +namespace Core.Models.Eft.Common.Tables; public class BotCore { diff --git a/Core/Models/Eft/Common/Tables/BotType.cs b/Core/Models/Eft/Common/Tables/BotType.cs index baa8e24f..e9930fda 100644 --- a/Core/Models/Eft/Common/Tables/BotType.cs +++ b/Core/Models/Eft/Common/Tables/BotType.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Eft.Common.Tables; +namespace Core.Models.Eft.Common.Tables; public class BotType { diff --git a/Core/Models/Enums/ConfigTypes.cs b/Core/Models/Enums/ConfigTypes.cs index 822e6f27..66cfac6a 100644 --- a/Core/Models/Enums/ConfigTypes.cs +++ b/Core/Models/Enums/ConfigTypes.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Enums; +namespace Core.Models.Enums; public enum ConfigTypes { diff --git a/Core/Models/Logging/LogBackgroundColor.cs b/Core/Models/Logging/LogBackgroundColor.cs index 6ba9878d..08c5d2c8 100644 --- a/Core/Models/Logging/LogBackgroundColor.cs +++ b/Core/Models/Logging/LogBackgroundColor.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Logging; +namespace Core.Models.Logging; public enum LogBackgroundColor { diff --git a/Core/Models/Logging/LogTextColor.cs b/Core/Models/Logging/LogTextColor.cs index 4188878d..c2a76282 100644 --- a/Core/Models/Logging/LogTextColor.cs +++ b/Core/Models/Logging/LogTextColor.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Logging; +namespace Core.Models.Logging; public enum LogTextColor { diff --git a/Core/Models/Spt/Bots/Bots.cs b/Core/Models/Spt/Bots/Bots.cs index ae563b09..24605116 100644 --- a/Core/Models/Spt/Bots/Bots.cs +++ b/Core/Models/Spt/Bots/Bots.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Types.Models.Spt.Bots; +namespace Core.Models.Spt.Bots; public class Bots { diff --git a/Core/Models/Spt/Server/DatabaseTables.cs b/Core/Models/Spt/Server/DatabaseTables.cs index eea46689..fa6eb47a 100644 --- a/Core/Models/Spt/Server/DatabaseTables.cs +++ b/Core/Models/Spt/Server/DatabaseTables.cs @@ -1,4 +1,4 @@ -namespace Types.Models.Spt.Server; +namespace Core.Models.Spt.Server; public class DatabaseTables { diff --git a/Core/Models/Utils/ILogger.cs b/Core/Models/Utils/ILogger.cs index cd73d2f6..3554c800 100644 --- a/Core/Models/Utils/ILogger.cs +++ b/Core/Models/Utils/ILogger.cs @@ -1,6 +1,6 @@ -using Types.Models.Logging; +using Core.Models.Logging; -namespace Types.Models.Utils; +namespace Core.Models.Utils; public interface ILogger { diff --git a/Core/Servers/ConfigServer.cs b/Core/Servers/ConfigServer.cs index cd471290..50813186 100644 --- a/Core/Servers/ConfigServer.cs +++ b/Core/Servers/ConfigServer.cs @@ -1,6 +1,6 @@ -using Types.Annotations; +using Core.Annotations; -namespace Types.Servers; +namespace Core.Servers; [Injectable(InjectionType.Singleton)] public class ConfigServer diff --git a/Core/Servers/DatabaseServer.cs b/Core/Servers/DatabaseServer.cs index 7045e67d..ea6a69a4 100644 --- a/Core/Servers/DatabaseServer.cs +++ b/Core/Servers/DatabaseServer.cs @@ -1,4 +1,4 @@ -namespace Types.Servers; +namespace Core.Servers; public class DatabaseServer { diff --git a/Core/Servers/Http/IHttpListener.cs b/Core/Servers/Http/IHttpListener.cs index 4a74d910..5161850c 100644 --- a/Core/Servers/Http/IHttpListener.cs +++ b/Core/Servers/Http/IHttpListener.cs @@ -1,4 +1,4 @@ -namespace Types.Servers.Http; +namespace Core.Servers.Http; public interface IHttpListener { diff --git a/Core/Servers/HttpServer.cs b/Core/Servers/HttpServer.cs index d3135cb1..c4d9b749 100644 --- a/Core/Servers/HttpServer.cs +++ b/Core/Servers/HttpServer.cs @@ -1,13 +1,13 @@ using System.Net.WebSockets; +using Core.Context; +using Core.Models.Config; +using Core.Servers.Http; +using Core.Services; using Microsoft.Extensions.Primitives; -using Types.Annotations; -using Types.Context; -using Types.Models.Config; -using Types.Servers.Http; -using Types.Services; -using ILogger = Types.Models.Utils.ILogger; +using Core.Annotations; +using ILogger = Core.Models.Utils.ILogger; -namespace Types.Servers; +namespace Core.Servers; [Injectable(InjectionType.Singleton)] public class HttpServer diff --git a/Core/Servers/WebSocketServer.cs b/Core/Servers/WebSocketServer.cs index 42ce2f9e..5ec5648b 100644 --- a/Core/Servers/WebSocketServer.cs +++ b/Core/Servers/WebSocketServer.cs @@ -1,6 +1,6 @@ -using Types.Annotations; +using Core.Annotations; -namespace Types.Servers; +namespace Core.Servers; [Injectable(InjectionType.Singleton)] public class WebSocketServer diff --git a/Core/Services/I18nService.cs b/Core/Services/I18nService.cs index bc31e90a..e508af5a 100644 --- a/Core/Services/I18nService.cs +++ b/Core/Services/I18nService.cs @@ -1,4 +1,4 @@ -namespace Types.Services; +namespace Core.Services; public class I18nService { diff --git a/Core/Services/LocaleService.cs b/Core/Services/LocaleService.cs index c194da1c..ac343072 100644 --- a/Core/Services/LocaleService.cs +++ b/Core/Services/LocaleService.cs @@ -1,8 +1,8 @@ -using Types.Annotations; -using Types.Servers; -using ILogger = Types.Models.Utils.ILogger; +using Core.Annotations; +using Core.Servers; +using ILogger = Core.Models.Utils.ILogger; -namespace Types.Services; +namespace Core.Services; [Injectable(InjectionType.Singleton)] public class LocaleService diff --git a/Core/Services/LocalisationService.cs b/Core/Services/LocalisationService.cs index 6ae740f8..cbf8dfef 100644 --- a/Core/Services/LocalisationService.cs +++ b/Core/Services/LocalisationService.cs @@ -1,9 +1,9 @@ -using Types.Annotations; -using Types.Servers; -using Types.Utils; -using ILogger = Types.Models.Utils.ILogger; +using Core.Utils; +using Core.Annotations; +using Core.Servers; +using ILogger = Core.Models.Utils.ILogger; -namespace Types.Services; +namespace Core.Services; [Injectable(InjectionType.Singleton)] public class LocalisationService diff --git a/Core/Utils/HashUtil.cs b/Core/Utils/HashUtil.cs new file mode 100644 index 00000000..b1a3318c --- /dev/null +++ b/Core/Utils/HashUtil.cs @@ -0,0 +1,92 @@ +using System.Text; +using System.Text.RegularExpressions; +using System.Security.Cryptography; +using Core.Annotations; + +namespace Core.Utils; + +[Injectable(InjectionType.Singleton)] +public partial class HashUtil +{ + /// + /// Create a 24 character id using the sha256 algorithm + current timestamp + /// + /// 24 character hash + public static string Generate() + { + throw new NotImplementedException(); + } + + /// + /// is the passed in string a valid mongo id + /// + /// String to check + /// True when string is a valid mongo id + public static bool IsValidMongoId(string stringToCheck) + { + return MongoIdRegex().IsMatch(stringToCheck); + } + + public static string GenerateMd5ForData(string data) + { + return GenerateHashForData(HashingAlgorithm.MD5, data); + } + + public static string GenerateSha1ForData(string data) + { + return GenerateHashForData(HashingAlgorithm.SHA1, data); + } + + public static string GenerateCrc32ForData(string data) + { + // TODO: Could not find a ms way of doing this. + // May need a custom impl to avoid an external lib. - CJ + throw new NotImplementedException(); + } + + /// + /// Create a hash for the data parameter + /// + /// algorithm to use to hash + /// data to be hashed + /// hash value + /// thrown if the provided algorithm is not implemented> + public static string GenerateHashForData(HashingAlgorithm algorithm, string data) + { + switch (algorithm) + { + case HashingAlgorithm.MD5: + var md5HashData = MD5.HashData(Encoding.UTF8.GetBytes(data)); + return Convert.ToHexString(md5HashData).Replace("-", string.Empty); + + case HashingAlgorithm.SHA1: + var sha1HashData = SHA1.HashData(Encoding.UTF8.GetBytes(data)); + return Convert.ToHexString(sha1HashData).Replace("-", string.Empty); + } + + throw new NotImplementedException("Provided hash algorithm is not supported."); + } + + /// + /// Generates an account ID for a profile + /// + /// Generated account ID + public static int GenerateAccountId() + { + const int min = 1000000; + const int max = 1999999; + + var random = new Random(); + + return random.Next() * (max - min + 1) + min; + } + + [GeneratedRegex("^[a-fA-F0-9]{24}$", RegexOptions.IgnoreCase, "en")] + private static partial Regex MongoIdRegex(); +} + +public enum HashingAlgorithm +{ + MD5, + SHA1, +} \ No newline at end of file diff --git a/Core/Utils/Logging/SimpleTextLogger.cs b/Core/Utils/Logging/SimpleTextLogger.cs index a09f55af..989b41fa 100644 --- a/Core/Utils/Logging/SimpleTextLogger.cs +++ b/Core/Utils/Logging/SimpleTextLogger.cs @@ -1,8 +1,8 @@ -using Types.Annotations; -using Types.Models.Logging; -using ILogger = Types.Models.Utils.ILogger; +using Core.Models.Logging; +using Core.Annotations; +using ILogger = Core.Models.Utils.ILogger; -namespace Types.Utils.Logging; +namespace Core.Utils.Logging; [Injectable(InjectionType.Singleton)] public class SimpleTextLogger : ILogger diff --git a/Core/Utils/RandomUtil.cs b/Core/Utils/RandomUtil.cs index 73bc97f4..dd8c41e4 100644 --- a/Core/Utils/RandomUtil.cs +++ b/Core/Utils/RandomUtil.cs @@ -1,6 +1,173 @@ -namespace Types.Utils; +using System.Security.Cryptography; +using Core.Annotations; +namespace Core.Utils; + +// TODO: Finish porting this class +[Injectable(InjectionType.Singleton)] public class RandomUtil { + private readonly Random _random = new(); + /// + /// Generates a random integer between the specified minimum and maximum values, inclusive. + /// + /// The minimum value (inclusive). + /// The maximum value (inclusive). + /// A random integer between the specified minimum and maximum values. + public int GetInt(int min, int max) + { + // Prevents a potential integer overflow. + if (max == int.MaxValue) + { + max -= 1; + } + + // maxVal is exclusive of the passed value, so add 1 + return max > min ? _random.Next(min, max + 1) : min; + } + + /// + /// Generates a random integer between 1 (inclusive) and the specified maximum value (exclusive). + /// If the maximum value is less than or equal to 1, it returns 1. + /// + /// The upper bound (exclusive) for the random integer generation. + /// A random integer between 1 and max - 1, or 1 if max is less than or equal to 1. + public int GetIntEx(int max) + { + return max > 2 ? _random.Next(1, max - 1) : 1; + } + + /// + /// Generates a random floating-point number within the specified range. + /// + /// The minimum value of the range (inclusive). + /// The maximum value of the range (exclusive). + /// A random floating-point number between `min` (inclusive) and `max` (exclusive). + public float GetFloat(float min, float max) + { + return (float)GetSecureRandomNumber() * (max - min) + min; + } + + /// + /// Generates a random boolean value. + /// + /// A random boolean value, where the probability of `true` and `false` is approximately equal. + public bool GetBool() + { + return GetSecureRandomNumber() < 0.5; + } + + /// + /// Calculates the percentage of a given number and returns the result. + /// + /// The percentage to calculate. + /// The number to calculate the percentage of. + /// The number of decimal places to round the result to (default is 2). + /// The calculated percentage of the given number, rounded to the specified number of decimal places. + public float GetPercentOfValue(float percent, float number, int toFixed = 2) + { + var num = percent * number / 100; + + return (float)Math.Round(num, toFixed); + } + + /// + /// Reduces a given number by a specified percentage. + /// + /// The original number to be reduced. + /// The percentage by which to reduce the number. + /// The reduced number after applying the percentage reduction. + public float ReduceValueByPercent(float number, float percentage) + { + var reductionAmount = number * percentage / 100; + + return number - reductionAmount; + } + + /// + /// Determines if a random event occurs based on the given chance percentage. + /// + /// The percentage chance (0-100) that the event will occur. + /// `true` if the event occurs, `false` otherwise. + public bool GetChance100(float chancePercent) + { + chancePercent = Math.Clamp(chancePercent, 0f, 100f); + + return GetIntEx(100) <= chancePercent; + } + + /// + /// Returns a random string from the provided collection of strings. + /// + /// This method is separate from GetCollectionValue so we can use a generic inference with GetCollectionValue. + /// + /// The collection of strings to select a random value from. + /// A randomly selected string from the array. + public string GetStringCollectionValue(IEnumerable collection) + { + return collection.ElementAt(GetInt(0, collection.Count() - 1)); + } + + /// + /// Returns a random string from the provided collection of strings. + /// + /// + /// The type of elements in the collection. + /// A random element from the collection. + /// This was formerly getArrayValue() in the node server + public T GetCollectionValue(IEnumerable collection) + { + return collection.ElementAt(GetInt(0, collection.Count() - 1)); + } + + /// + /// Gets a random key from the given dictionary + /// + /// The dictionary from which to retrieve a key. + /// Type of key + /// Type of Value + /// A random TKey representing one of the keys of the dictionary. + public TKey GetKey(Dictionary dictionary) where TKey : notnull + { + return GetCollectionValue(dictionary.Keys); + } + + /// + /// Generates a secure random number between 0 (inclusive) and 1 (exclusive). + /// + /// This method uses the `crypto` module to generate a 48-bit random integer, + /// which is then divided by the maximum possible 48-bit integer value to + /// produce a floating-point number in the range [0, 1). + /// + /// A secure random number between 0 (inclusive) and 1 (exclusive). + private static double GetSecureRandomNumber() + { + var buffer = new byte[6]; + + using var rng = RandomNumberGenerator.Create(); + + // Fill buffer with random bytes + rng.GetBytes(buffer); + + var integer = 0; + for (var i = 0; i < 6; i++) + { + integer = (integer << 8) | buffer[i]; + } + + const ulong maxInt = 1UL << 48; + + return (double)integer / maxInt; + } + + /// + /// Determines the number of decimal places in a number. + /// + /// The number to analyze. + /// The number of decimal places, or 0 if none exist. + private static int GetNumberPrecision(double num) + { + return num.ToString().Split('.')[1]?.Length ?? 0; + } } \ No newline at end of file diff --git a/Core/Utils/TimeUtil.cs b/Core/Utils/TimeUtil.cs new file mode 100644 index 00000000..94fcbb61 --- /dev/null +++ b/Core/Utils/TimeUtil.cs @@ -0,0 +1,178 @@ +using Core.Annotations; + +namespace Core.Utils; + +[Injectable(InjectionType.Singleton)] +public class TimeUtil +{ + public const int OneHourAsSeconds = 3600; + + /// + /// Formats the time part of a date as a UTC string. + /// + /// The date to format in UTC. + /// The formatted time as 'HH-MM-SS'. + public string FormatTime(DateTime dateTime) + { + var hour = Pad(dateTime.ToUniversalTime().Hour); + var minute = Pad(dateTime.ToUniversalTime().Minute); + var second = Pad(dateTime.ToUniversalTime().Second); + + return $"{hour}-{minute}-{second}"; + } + + /// + /// Formats the date part of a date as a UTC string. + /// + /// The date to format in UTC. + /// The formatted date as 'YYYY-MM-DD'. + public string FormatDate(DateTime dateTime) + { + var day = Pad(dateTime.ToUniversalTime().Day); + var month = Pad(dateTime.ToUniversalTime().Month); + var year = Pad(dateTime.ToUniversalTime().Year); + + return $"{year}-{month}-{day}"; + } + + /// + /// Gets the current date as a formatted UTC string. + /// + /// The current date as 'YYYY-MM-DD'. + public string GetDate() + { + return FormatDate(DateTime.Now); + } + + /// + /// Gets the current time as a formatted UTC string. + /// + /// The current time as 'HH-MM-SS'. + public string GetTime() + { + return FormatTime(DateTime.Now); + } + + /// + /// Gets the current timestamp in seconds in UTC. + /// + /// The current timestamp in seconds since the Unix epoch in UTC. + public long GetTimeStamp() + { + return DateTimeOffset.Now.ToUnixTimeSeconds(); + } + + /// + /// Gets the start of day timestamp for the given date + /// + /// datetime to get the time stamp for, if null it uses current date. + /// Unix epoch for the start of day of the calculated date + public long GetStartOfDayTimeStamp(DateTime? dateTime) + { + var now = dateTime ?? DateTime.Now; + + return new DateTimeOffset(new DateTime(now.Year, now.Month, now.Day, 0, 0, 0)) + .ToUnixTimeSeconds(); + } + + /// + /// Get timestamp of today + passed in day count + /// + /// Days from now + /// + public long GetTimeStampFromNowDays(int daysFromNow) + { + return DateTimeOffset.Now.AddDays(daysFromNow).ToUnixTimeSeconds(); + } + + /// + /// Get timestamp of today + passed in hour count + /// + /// + /// + public long GetTimeStampFromNowHours(int hoursFromNow) + { + return DateTimeOffset.Now.AddHours(hoursFromNow).ToUnixTimeSeconds(); + } + + /// + /// Gets the current time in UTC in a format suitable for mail in EFT. + /// + /// The current time as 'HH:MM' in UTC. + public string GetTimeMailFormat() + { + return DateTime.UtcNow.ToString("HH:mm"); + } + + /// + /// Gets the current date in UTC in a format suitable for emails in EFT. + /// + /// The current date as 'DD.MM.YYYY' in UTC. + public string GetDateMailFormat() + { + return DateTime.UtcNow.ToString("dd.MM.yyyy"); + } + + /// + /// Converts a number of hours into seconds. + /// + /// The number of hours to convert. + /// The equivalent number of seconds. + public int GetHoursAsSeconds(int hours) + { + return OneHourAsSeconds * hours; + } + + /// + /// Gets the time stamp of the start of the next hour in UTC + /// + /// Time stamp of the next hour in unix time seconds + public long GetTimeStampOfNextHour() + { + var now = DateTime.UtcNow; + + var nextHour = new DateTime( + now.Year, + now.Month, + now.Day, + now.Hour, + 0, + 0, + DateTimeKind.Utc + ).AddHours(1); + + return new DateTimeOffset(nextHour).ToUnixTimeSeconds(); + } + + /// + /// Returns the current days timestamp at 00:00 + /// e.g. current time: 13th March 14:22 will return 13th March 00:00 + /// + /// Timestamp + public long GetTodayMidNightTimeStamp() + { + var now = DateTime.UtcNow; + + var midNight = new DateTime( + now.Year, + now.Month, + now.Day, + 0, + 0, + 0, + DateTimeKind.Utc + ); + + return new DateTimeOffset(midNight).ToUnixTimeSeconds(); + } + + /// + /// Pads a number with a leading zero if it is less than 10. + /// + /// The number to pad. + /// The padded number as a string. + private static string Pad(int number) + { + return number.ToString().PadLeft(2, '0'); + } +} \ No newline at end of file diff --git a/Server/Program.cs b/Server/Program.cs index 765f8ee9..6e6d8a21 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -1,5 +1,5 @@ -using Types.Annotations; -using Types.Servers; +using Core.Annotations; +using Core.Servers; namespace Server;