diff --git a/Projects/Server/Client/ClientVersion.cs b/Projects/Server/Client/ClientVersion.cs new file mode 100644 index 000000000..09e68d582 --- /dev/null +++ b/Projects/Server/Client/ClientVersion.cs @@ -0,0 +1,268 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ClientVersion.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.Runtime.CompilerServices; +using Server.Buffers; + +namespace Server; + +public enum ClientType +{ + Regular, + UOTD, + God, + SA +} + +public class ClientVersion : IComparable, IComparer +{ + public static readonly ClientVersion Version400a = new("4.0.0a"); + public static readonly ClientVersion Version407a = new("4.0.7a"); + public static readonly ClientVersion Version500a = new("5.0.0a"); + public static readonly ClientVersion Version502b = new("5.0.2b"); + public static readonly ClientVersion Version6000 = new("6.0.0.0"); + public static readonly ClientVersion Version6017 = new("6.0.1.7"); + public static readonly ClientVersion Version60142 = new("6.0.14.2"); + public static readonly ClientVersion Version7000 = new("7.0.0.0"); + public static readonly ClientVersion Version7090 = new("7.0.9.0"); + public static readonly ClientVersion Version70130 = new("7.0.13.0"); + public static readonly ClientVersion Version70160 = new("7.0.16.0"); + public static readonly ClientVersion Version70300 = new("7.0.30.0"); + public static readonly ClientVersion Version70331 = new("7.0.33.1"); + public static readonly ClientVersion Version704565 = new("7.0.45.65"); + public static readonly ClientVersion Version70500 = new("7.0.50.0"); + public static readonly ClientVersion Version70610 = new("7.0.61.0"); + + public ClientVersion(int maj, int min, int rev, int pat, ClientType type = ClientType.Regular) + { + Major = maj; + Minor = min; + Revision = rev; + Patch = pat; + Type = type; + + SourceString = Utility.Intern(ToStringImpl()); + } + + public ClientVersion(string fmt) + { + fmt = fmt.ToLower(); + SourceString = Utility.Intern(fmt); + + try + { + var br1 = fmt.IndexOfOrdinal('.'); + var br2 = fmt.IndexOf('.', br1 + 1); + + var br3 = br2 + 1; + while (br3 < fmt.Length && char.IsDigit(fmt, br3)) + { + br3++; + } + + Major = Utility.ToInt32(fmt.AsSpan()[..br1]); + Minor = Utility.ToInt32(fmt.Substring(br1 + 1, br2 - br1 - 1)); + Revision = Utility.ToInt32(fmt.Substring(br2 + 1, br3 - br2 - 1)); + + if (br3 < fmt.Length) + { + if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 + { + if (!char.IsWhiteSpace(fmt, br3)) + { + Patch = fmt[br3] - 'a' + 1; + } + } + else + { + Patch = Utility.ToInt32(fmt.Substring(br3 + 1, fmt.Length - br3 - 1)); + } + } + + if (fmt.InsensitiveContains("god") || fmt.InsensitiveContains("gq")) + { + Type = ClientType.God; + } + else if (fmt.InsensitiveContains("third dawn") || + fmt.InsensitiveContains("uo:td") || + fmt.InsensitiveContains("uotd") || + fmt.InsensitiveContains("uo3d") || + fmt.InsensitiveContains("uo:3d")) + { + Type = ClientType.UOTD; + } + else + { + Type = ClientType.Regular; + } + } + catch + { + Major = 0; + Minor = 0; + Revision = 0; + Patch = 0; + Type = ClientType.Regular; + } + } + + public int Major { get; } + + public int Minor { get; } + + public int Revision { get; } + + public int Patch { get; } + + public ClientType Type { get; } + + public string SourceString { get; } + + public int CompareTo(ClientVersion o) + { + if (o == null) + { + return 1; + } + + if (Major > o.Major) + { + return 1; + } + + if (Major < o.Major) + { + return -1; + } + + if (Minor > o.Minor) + { + return 1; + } + + if (Minor < o.Minor) + { + return -1; + } + + if (Revision > o.Revision) + { + return 1; + } + + if (Revision < o.Revision) + { + return -1; + } + + if (Patch > o.Patch) + { + return 1; + } + + if (Patch < o.Patch) + { + return -1; + } + + return 0; + } + + int IComparer.Compare(ClientVersion x, ClientVersion y) => Compare(x, y); + + public static bool operator ==(ClientVersion l, ClientVersion r) => Compare(l, r) == 0; + + public static bool operator !=(ClientVersion l, ClientVersion r) => Compare(l, r) != 0; + + public static bool operator >=(ClientVersion l, ClientVersion r) => Compare(l, r) >= 0; + + public static bool operator >(ClientVersion l, ClientVersion r) => Compare(l, r) > 0; + + public static bool operator <=(ClientVersion l, ClientVersion r) => Compare(l, r) <= 0; + + public static bool operator <(ClientVersion l, ClientVersion r) => Compare(l, r) < 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(Major, Minor, Revision, Patch, Type); + + public override bool Equals(object obj) + { + var v = obj as ClientVersion; + + return Major == v?.Major + && Minor == v.Minor + && Revision == v.Revision + && Patch == v.Patch + && Type == v.Type; + } + + private string ToStringImpl() + { + using var builder = new ValueStringBuilder(stackalloc char[32]); + + builder.Append(Major.ToString()); + builder.Append('.'); + builder.Append(Minor.ToString()); + builder.Append('.'); + builder.Append(Revision.ToString()); + + if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 + { + if (Patch > 0) + { + builder.Append((char)('a' + (Patch - 1))); + } + } + else + { + builder.Append('.'); + builder.Append(Patch.ToString()); + } + + if (Type != ClientType.Regular) + { + builder.Append(' '); + builder.Append(Type.ToString().ToLower()); + } + + return builder.ToString(); + } + + public override string ToString() => SourceString; + + public static bool IsNull(object x) => ReferenceEquals(x, null); + + public static int Compare(ClientVersion a, ClientVersion b) + { + if (IsNull(a) && IsNull(b)) + { + return 0; + } + + if (IsNull(a)) + { + return -1; + } + + if (IsNull(b)) + { + return 1; + } + + return a.CompareTo(b); + } +} diff --git a/Projects/Server/Client/UOClient.cs b/Projects/Server/Client/UOClient.cs new file mode 100644 index 000000000..e19b801f3 --- /dev/null +++ b/Projects/Server/Client/UOClient.cs @@ -0,0 +1,133 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: UOClient.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.Buffers.Binary; +using System.IO; +using System.Text.Json.Serialization; +using Server.Json; +using Server.Logging; + +namespace Server; + +public static class UOClient +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(UOClient)); + + private static bool _automaticallyDetected; + + public static CUOSettings CuoSettings { get; private set; } + public static ClientVersion ServerClientVersion { get; private set; } + + public static void Load() + { + ServerClientVersion = ServerConfiguration.GetSetting("clientData.clientVersion", (ClientVersion)null); + + if (ServerClientVersion == null) + { + ServerClientVersion = DetectCUOClient() ?? DetectClassicClient(); + _automaticallyDetected = true; + } + } + + public static void Configure() + { + if (ServerClientVersion == null) + { + logger.Warning("Could not detect client version."); + } + else if (CuoSettings.ClientVersion == ServerClientVersion) + { + logger.Information($"Automatically detected client version {ServerClientVersion} from CUO settings."); + } + else if (_automaticallyDetected) + { + logger.Information($"Automatically detected client version {ServerClientVersion}"); + } + else + { + logger.Information($"Manually configured to use client version {ServerClientVersion}"); + } + } + + private static ClientVersion DetectCUOClient() + { + var path = Core.FindDataFile("settings.json", false); + if (File.Exists(path)) + { + var settings = JsonConfig.Deserialize(path); + var file = new FileInfo(path); + + if (settings.UltimaOnlineDirectory != null) + { + settings.UltimaOnlineDirectory = PathUtility.GetFullPath(settings.UltimaOnlineDirectory, file.DirectoryName); + if (Directory.Exists(settings.UltimaOnlineDirectory)) + { + CuoSettings = settings; + } + } + + return settings.ClientVersion; + } + + return null; + } + + private static ClientVersion DetectClassicClient() + { + var path = Core.FindDataFile("client.exe", false); + + if (File.Exists(path)) + { + using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + var buffer = GC.AllocateUninitializedArray((int)fs.Length, true); + fs.Read(buffer); + // VS_VERSION_INFO (unicode) + Span vsVersionInfo = stackalloc byte[] + { + 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, + 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, + 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00, + 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00 + }; + + for (var i = 0; i < buffer.Length; i++) + { + if (vsVersionInfo.SequenceEqual(buffer.AsSpan(i, 30))) + { + var offset = i + 42; // 30 + 12 + + var minorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset)); + var majorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 2)); + var privatePart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 4)); + var buildPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 6)); + + return new ClientVersion(majorPart, minorPart, buildPart, privatePart); + } + } + } + + return null; + } + + public record CUOSettings + { + [JsonPropertyName("clientversion")] + public ClientVersion ClientVersion { get; set; } + + [JsonPropertyName("ultimaonlinedirectory")] + public string UltimaOnlineDirectory { get; set; } + } +} diff --git a/Projects/Server/ClientVersion.cs b/Projects/Server/ClientVersion.cs deleted file mode 100644 index 8325eb06a..000000000 --- a/Projects/Server/ClientVersion.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Buffers; - -namespace Server -{ - public enum ClientType - { - Regular, - UOTD, - God, - SA - } - - public class ClientVersion : IComparable, IComparer - { - public ClientVersion(int maj, int min, int rev, int pat, ClientType type = ClientType.Regular) - { - Major = maj; - Minor = min; - Revision = rev; - Patch = pat; - Type = type; - - SourceString = Utility.Intern(ToStringImpl()); - } - - public ClientVersion(string fmt) - { - fmt = fmt.ToLower(); - SourceString = Utility.Intern(fmt); - - try - { - var br1 = fmt.IndexOfOrdinal('.'); - var br2 = fmt.IndexOf('.', br1 + 1); - - var br3 = br2 + 1; - while (br3 < fmt.Length && char.IsDigit(fmt, br3)) - { - br3++; - } - - Major = Utility.ToInt32(fmt.AsSpan()[..br1]); - Minor = Utility.ToInt32(fmt.Substring(br1 + 1, br2 - br1 - 1)); - Revision = Utility.ToInt32(fmt.Substring(br2 + 1, br3 - br2 - 1)); - - if (br3 < fmt.Length) - { - if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 - { - if (!char.IsWhiteSpace(fmt, br3)) - { - Patch = fmt[br3] - 'a' + 1; - } - } - else - { - Patch = Utility.ToInt32(fmt.Substring(br3 + 1, fmt.Length - br3 - 1)); - } - } - - if (fmt.InsensitiveContains("god") || fmt.InsensitiveContains("gq")) - { - Type = ClientType.God; - } - else if (fmt.InsensitiveContains("third dawn") || - fmt.InsensitiveContains("uo:td") || - fmt.InsensitiveContains("uotd") || - fmt.InsensitiveContains("uo3d") || - fmt.InsensitiveContains("uo:3d")) - { - Type = ClientType.UOTD; - } - else - { - Type = ClientType.Regular; - } - } - catch - { - Major = 0; - Minor = 0; - Revision = 0; - Patch = 0; - Type = ClientType.Regular; - } - } - - public int Major { get; } - - public int Minor { get; } - - public int Revision { get; } - - public int Patch { get; } - - public ClientType Type { get; } - - public string SourceString { get; } - - public int CompareTo(ClientVersion o) - { - if (o == null) - { - return 1; - } - - if (Major > o.Major) - { - return 1; - } - - if (Major < o.Major) - { - return -1; - } - - if (Minor > o.Minor) - { - return 1; - } - - if (Minor < o.Minor) - { - return -1; - } - - if (Revision > o.Revision) - { - return 1; - } - - if (Revision < o.Revision) - { - return -1; - } - - if (Patch > o.Patch) - { - return 1; - } - - if (Patch < o.Patch) - { - return -1; - } - - return 0; - } - - int IComparer.Compare(ClientVersion x, ClientVersion y) => Compare(x, y); - - public static bool operator ==(ClientVersion l, ClientVersion r) => Compare(l, r) == 0; - - public static bool operator !=(ClientVersion l, ClientVersion r) => Compare(l, r) != 0; - - public static bool operator >=(ClientVersion l, ClientVersion r) => Compare(l, r) >= 0; - - public static bool operator >(ClientVersion l, ClientVersion r) => Compare(l, r) > 0; - - public static bool operator <=(ClientVersion l, ClientVersion r) => Compare(l, r) <= 0; - - public static bool operator <(ClientVersion l, ClientVersion r) => Compare(l, r) < 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(Major, Minor, Revision, Patch, Type); - - public override bool Equals(object obj) - { - var v = obj as ClientVersion; - - return Major == v?.Major - && Minor == v.Minor - && Revision == v.Revision - && Patch == v.Patch - && Type == v.Type; - } - - private string ToStringImpl() - { - using var builder = new ValueStringBuilder(stackalloc char[32]); - - builder.Append(Major.ToString()); - builder.Append('.'); - builder.Append(Minor.ToString()); - builder.Append('.'); - builder.Append(Revision.ToString()); - - if (Major <= 5 && Minor <= 0 && Revision <= 6) // Anything before 5.0.7 - { - if (Patch > 0) - { - builder.Append((char)('a' + (Patch - 1))); - } - } - else - { - builder.Append('.'); - builder.Append(Patch.ToString()); - } - - if (Type != ClientType.Regular) - { - builder.Append(' '); - builder.Append(Type.ToString().ToLower()); - } - - return builder.ToString(); - } - - public override string ToString() => SourceString; - - public static bool IsNull(object x) => ReferenceEquals(x, null); - - public static int Compare(ClientVersion a, ClientVersion b) - { - if (IsNull(a) && IsNull(b)) - { - return 0; - } - - if (IsNull(a)) - { - return -1; - } - - if (IsNull(b)) - { - return 1; - } - - return a.CompareTo(b); - } - } -} diff --git a/Projects/Server/Configuration/ServerConfiguration.cs b/Projects/Server/Configuration/ServerConfiguration.cs index d32519748..77f76db2f 100644 --- a/Projects/Server/Configuration/ServerConfiguration.cs +++ b/Projects/Server/Configuration/ServerConfiguration.cs @@ -15,424 +15,279 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Net; using Server.Json; using Server.Logging; -namespace Server +namespace Server; + +public static class ServerConfiguration { - public static class ServerConfiguration + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerConfiguration)); + + private const string _relPath = "Configuration/modernuo.json"; + private static readonly string m_FilePath = Path.Join(Core.BaseDirectory, _relPath); + private static ServerSettings m_Settings; + private static bool m_Mocked; + + public static List AssemblyDirectories => m_Settings.AssemblyDirectories; + + public static HashSet DataDirectories => m_Settings.DataDirectories; + + public static List Listeners => m_Settings.Listeners; + + public static ClientVersion GetSetting(string key, ClientVersion defaultValue) => + m_Settings.Settings.TryGetValue(key, out var value) ? new ClientVersion(value) : defaultValue; + + public static string GetSetting(string key, string defaultValue) => + m_Settings.Settings.TryGetValue(key, out var value) ? value : defaultValue; + + public static int GetSetting(string key, int defaultValue) { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerConfiguration)); + m_Settings.Settings.TryGetValue(key, out var strValue); + return int.TryParse(strValue, out var value) ? value : defaultValue; + } - private const string _relPath = "Configuration/modernuo.json"; - private static readonly string m_FilePath = Path.Join(Core.BaseDirectory, _relPath); - private static ServerSettings m_Settings; - private static bool m_Mocked; + public static long GetSetting(string key, long defaultValue) + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return long.TryParse(strValue, out var value) ? value : defaultValue; + } - public static List AssemblyDirectories => m_Settings.AssemblyDirectories; + public static bool GetSetting(string key, bool defaultValue) + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return bool.TryParse(strValue, out var value) ? value : defaultValue; + } - public static List DataDirectories => m_Settings.DataDirectories; + public static T GetSetting(string key, T defaultValue) where T : struct, Enum + { + m_Settings.Settings.TryGetValue(key, out var strValue); + return Enum.TryParse(strValue, out T value) ? value : defaultValue; + } - public static List Listeners => m_Settings.Listeners; - - public static ClientVersion GetSetting(string key, ClientVersion defaultValue) => - m_Settings.Settings.TryGetValue(key, out var value) ? new ClientVersion(value) : defaultValue; - - public static string GetSetting(string key, string defaultValue) => - m_Settings.Settings.TryGetValue(key, out var value) ? value : defaultValue; - - public static int GetSetting(string key, int defaultValue) + public static T? GetSetting(string key) where T : struct, Enum + { + if (!m_Settings.Settings.TryGetValue(key, out var strValue)) { - m_Settings.Settings.TryGetValue(key, out var strValue); - return int.TryParse(strValue, out var value) ? value : defaultValue; + return null; } - public static long GetSetting(string key, long defaultValue) + return Enum.TryParse(strValue, out T value) ? value : null; + } + + public static string GetOrUpdateSetting(string key, string defaultValue) + { + if (m_Settings.Settings.TryGetValue(key, out var value)) { - m_Settings.Settings.TryGetValue(key, out var strValue); - return long.TryParse(strValue, out var value) ? value : defaultValue; - } - - public static bool GetSetting(string key, bool defaultValue) - { - m_Settings.Settings.TryGetValue(key, out var strValue); - return bool.TryParse(strValue, out var value) ? value : defaultValue; - } - - public static T GetSetting(string key, T defaultValue) where T : struct, Enum - { - m_Settings.Settings.TryGetValue(key, out var strValue); - return Enum.TryParse(strValue, out T value) ? value : defaultValue; - } - - public static T? GetSetting(string key) where T : struct, Enum - { - if (!m_Settings.Settings.TryGetValue(key, out var strValue)) - { - return null; - } - - return Enum.TryParse(strValue, out T value) ? value : null; - } - - public static string GetOrUpdateSetting(string key, string defaultValue) - { - if (m_Settings.Settings.TryGetValue(key, out var value)) - { - return value; - } - - SetSetting(key, value = defaultValue); return value; } - public static int GetOrUpdateSetting(string key, int defaultValue) + SetSetting(key, value = defaultValue); + return value; + } + + public static int GetOrUpdateSetting(string key, int defaultValue) + { + int value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - int value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = int.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = int.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static long GetOrUpdateSetting(string key, long defaultValue) + return value; + } + + public static long GetOrUpdateSetting(string key, long defaultValue) + { + long value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - long value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = long.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = long.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static bool GetOrUpdateSetting(string key, bool defaultValue) + return value; + } + + public static bool GetOrUpdateSetting(string key, bool defaultValue) + { + bool value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - bool value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = bool.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = bool.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static TimeSpan GetOrUpdateSetting(string key, TimeSpan defaultValue) + return value; + } + + public static TimeSpan GetOrUpdateSetting(string key, TimeSpan defaultValue) + { + TimeSpan value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - TimeSpan value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = TimeSpan.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = TimeSpan.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static T GetOrUpdateSetting(string key, T defaultValue) where T : struct, Enum + return value; + } + + public static T GetOrUpdateSetting(string key, T defaultValue) where T : struct, Enum + { + T value; + + if (m_Settings.Settings.TryGetValue(key, out var strValue)) { - T value; - - if (m_Settings.Settings.TryGetValue(key, out var strValue)) - { - value = Enum.TryParse(strValue, out value) ? value : defaultValue; - } - else - { - SetSetting(key, (value = defaultValue).ToString()); - } - - return value; + value = Enum.TryParse(strValue, out value) ? value : defaultValue; + } + else + { + SetSetting(key, (value = defaultValue).ToString()); } - public static void SetSetting(string key, TimeSpan value) => SetSetting(key, value.ToString()); + return value; + } - public static void SetSetting(string key, int value) => SetSetting(key, value.ToString()); + public static void SetSetting(string key, TimeSpan value) => SetSetting(key, value.ToString()); - public static void SetSetting(string key, long value) => SetSetting(key, value.ToString()); + public static void SetSetting(string key, int value) => SetSetting(key, value.ToString()); - public static void SetSetting(string key, bool value) => SetSetting(key, value.ToString()); + public static void SetSetting(string key, long value) => SetSetting(key, value.ToString()); - public static void SetSetting(string key, T value) where T : struct, Enum => - SetSetting(key, value.ToString()); + public static void SetSetting(string key, bool value) => SetSetting(key, value.ToString()); - public static void SetSetting(string key, string value) + public static void SetSetting(string key, T value) where T : struct, Enum => + SetSetting(key, value.ToString()); + + public static void SetSetting(string key, string value) + { + m_Settings.Settings[key] = value; + Save(); + } + + // If mock is enabled we skip the console readline. + public static void Load(bool mocked = false) + { + m_Mocked = mocked; + var updated = false; + + if (File.Exists(m_FilePath)) { - m_Settings.Settings[key] = value; - Save(); + logger.Information($"Reading server configuration from {_relPath}..."); + m_Settings = JsonConfig.Deserialize(m_FilePath); + + if (m_Settings == null) + { + logger.Error("Reading server configuration failed"); + throw new FileNotFoundException($"Failed to deserialize {m_FilePath}."); + } + + logger.Information("Reading server configuration done"); + } + else + { + updated = true; + m_Settings = new ServerSettings(); } - // If mock is enabled we skip the console readline. - public static void Load(bool mocked = false) + if (mocked) { - m_Mocked = mocked; - var updated = false; + return; + } - if (File.Exists(m_FilePath)) + if (m_Settings.DataDirectories.Count == 0) + { + updated = true; + foreach (var directory in ServerConfigurationPrompts.GetDataDirectories()) { - logger.Information($"Reading server configuration from {_relPath}..."); - m_Settings = JsonConfig.Deserialize(m_FilePath); - - if (m_Settings == null) - { - logger.Error("Reading server configuration failed"); - throw new FileNotFoundException($"Failed to deserialize {m_FilePath}."); - } - - logger.Information("Reading server configuration done"); - } - else - { - updated = true; - m_Settings = new ServerSettings(); - } - - if (mocked) - { - return; - } - - if (m_Settings.DataDirectories.Count == 0) - { - updated = true; - m_Settings.DataDirectories.AddRange(GetDataDirectories()); - } - - if (m_Settings.Listeners.Count == 0) - { - updated = true; - m_Settings.Listeners.AddRange(GetListeners()); - } - - if (m_Settings.Expansion == null) - { - var expansion = GetSetting("currentExpansion"); - var hasExpansion = expansion != null; - - expansion ??= GetExpansion(); - - if (expansion <= Expansion.ML && !hasExpansion) - { - SetPre6000Support(); - } - - updated = true; - m_Settings.Expansion = expansion; - } - - Core.Expansion = m_Settings.Expansion.Value; - - if (updated) - { - Save(); - Console.Write("Server configuration saved to "); - Utility.PushColor(ConsoleColor.Green); - Console.WriteLine($"{_relPath}."); - Utility.PopColor(); + m_Settings.DataDirectories.Add(directory); } } - private static void SetPre6000Support() + UOClient.Load(); + var cuoClientFiles = UOClient.CuoSettings?.UltimaOnlineDirectory; + + if (cuoClientFiles != null) { - Console.WriteLine("Will you be using a client version older than 6.0.0.0?"); + DataDirectories.Add(cuoClientFiles); + } - do + if (m_Settings.Listeners.Count == 0) + { + updated = true; + m_Settings.Listeners.AddRange(ServerConfigurationPrompts.GetListeners()); + } + + bool? isPre60000 = null; + + if (m_Settings.Expansion == null) + { + var expansion = GetSetting("currentExpansion"); + var hasExpansion = expansion != null; + + expansion ??= ServerConfigurationPrompts.GetExpansion(); + + if (expansion <= Expansion.ML && !hasExpansion) { - Console.Write("y or [n]> "); - var input = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("n")) - { - Utility.PushColor(ConsoleColor.Yellow); - Console.WriteLine("Client >= 6.0.0.0 chosen."); - Utility.PopColor(); - return; - } - - if (input.InsensitiveStartsWith("y")) + isPre60000 = ServerConfigurationPrompts.GetIsClientPre6000(); + if (isPre60000 == true) { SetSetting("maps.enablePre6000Trammel", true.ToString()); - - Utility.PushColor(ConsoleColor.Yellow); - Console.WriteLine("Client <= 5.0.9.1 chosen."); - Utility.PopColor(); - return; } - - Console.Write("Invalid option "); - Utility.PushColor(ConsoleColor.Red); - Console.Write(input); - Utility.PopColor(); - Console.WriteLine(". Press y for yes or n for no."); - } while (true); - } - - private static Expansion GetExpansion() - { - Console.WriteLine("Please choose an expansion by typing the number or short name:"); - var expansions = ExpansionInfo.Table; - - for (int i = 0; i < expansions.Length; i++) - { - var info = expansions[i]; - Console.WriteLine(" - {0,2}: {1} ({2})", i, ((Expansion)info.ID).ToString(), info.Name); } - var maxExpansion = (Expansion)expansions[^1].ID; - var maxExpansionName = maxExpansion.ToString(); - - do - { - Console.Write("[enter for {0}]> ", maxExpansionName); - var input = Console.ReadLine(); - Expansion expansion; - - if (string.IsNullOrWhiteSpace(input)) - { - expansion = maxExpansion; - } - else if (int.TryParse(input, NumberStyles.Integer, null, out var number) && - number >= 0 && number < expansions.Length) - { - expansion = (Expansion)number; - } - else if (!Enum.TryParse(input, out expansion)) - { - Utility.PushColor(ConsoleColor.Red); - Console.Write(input); - Utility.PopColor(); - Console.WriteLine(" is an invalid expansion option."); - continue; - } - - Console.Write("Expansion set to "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(ExpansionInfo.GetInfo(expansion).Name); - Utility.PopColor(); - Console.WriteLine("."); - return expansion; - } while (true); + updated = true; + m_Settings.Expansion = expansion; } - private static List GetDataDirectories() + if (isPre60000 != true) { - Console.WriteLine("Please enter the absolute path to the Ultima Online data:"); - - var directories = new List(); - - do + if (ServerConfigurationPrompts.GetIsClient7090()) { - Console.Write("{0}> ", directories.Count > 0 ? "[enter to finish]" : " "); - var directory = Console.ReadLine(); - if (string.IsNullOrWhiteSpace(directory)) - { - break; - } - - if (Directory.Exists(directory)) - { - directories.Add(directory); - Console.Write("Added "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(directory); - Utility.PopColor(); - Console.WriteLine("."); - } - else - { - Utility.PushColor(ConsoleColor.Red); - Console.Write(directory); - Utility.PopColor(); - Console.WriteLine(" does not exist."); - } - } while (true); - - return directories; - } - - private static List GetListeners() - { - Console.WriteLine("Please enter the IP and ports to listen:"); - Console.WriteLine(" - Only enter IP addresses directly bound to this machine"); - Console.WriteLine(" - To listen to all IP addresses enter 0.0.0.0"); - - var ips = new List(); - - do - { - // IP:Port? - Console.Write("[{0}]> ", ips.Count > 0 ? "enter to finish" : "0.0.0.0:2593"); - var ipStr = Console.ReadLine(); - - IPEndPoint ip; - if (string.IsNullOrWhiteSpace(ipStr)) - { - if (ips.Count > 0) - { - break; - } - - ip = new IPEndPoint(IPAddress.Any, 2593); - } - else - { - if (!ipStr.ContainsOrdinal(':')) - { - ipStr += ":2593"; - } - - if (!IPEndPoint.TryParse(ipStr, out ip)) - { - Utility.PushColor(ConsoleColor.Red); - Console.Write(ipStr); - Utility.PopColor(); - Console.WriteLine(" is not a valid IP or port."); - continue; - } - } - - ips.Add(ip); - Console.Write("Added "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(ip); - Utility.PopColor(); - Console.WriteLine("."); - } while (true); - - return ips; - } - - public static void Save() - { - if (m_Mocked) - { - return; + updated = true; + SetSetting("maps.enablePostHSMultiComponentFormat", true); } + } - JsonConfig.Serialize(m_FilePath, m_Settings); + Core.Expansion = m_Settings.Expansion.Value; + + if (updated) + { + Save(); + Console.Write("Server configuration saved to "); + Utility.PushColor(ConsoleColor.Green); + Console.WriteLine($"{_relPath}."); + Utility.PopColor(); } } + + public static void Save() + { + if (m_Mocked) + { + return; + } + + JsonConfig.Serialize(m_FilePath, m_Settings); + } } diff --git a/Projects/Server/Configuration/ServerConfigurationPrompts.cs b/Projects/Server/Configuration/ServerConfigurationPrompts.cs new file mode 100644 index 000000000..7976870a6 --- /dev/null +++ b/Projects/Server/Configuration/ServerConfigurationPrompts.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; + +namespace Server; + +public static class ServerConfigurationPrompts +{ + internal static bool GetIsClient7090() + { + if (UOClient.ServerClientVersion != null) + { + return UOClient.ServerClientVersion >= ClientVersion.Version7090; + } + + Console.WriteLine("Will you be using a client version 7.0.9.0 or newer?"); + + do + { + Console.Write("[y] or n> "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("y")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client >= 7.0.9.0 chosen."); + Utility.PopColor(); + return true; + } + + if (input.InsensitiveStartsWith("n")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client < 7.0.9.0 chosen."); + Utility.PopColor(); + return false; + } + + Console.Write("Invalid option "); + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(". Press y for yes or n for no."); + } while (true); + } + + + internal static bool GetIsClientPre6000() + { + if (UOClient.ServerClientVersion != null) + { + return UOClient.ServerClientVersion < ClientVersion.Version6000; + } + + Console.WriteLine("Will you be using a client version older than 6.0.0.0?"); + + do + { + Console.Write("y or [n]> "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("n")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client >= 6.0.0.0 chosen."); + Utility.PopColor(); + return false; + } + + if (input.InsensitiveStartsWith("y")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client < 6.0.0.0 chosen."); + Utility.PopColor(); + return true; + } + + Console.Write("Invalid option "); + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(". Press y for yes or n for no."); + } while (true); + } + + internal static Expansion GetExpansion() + { + Console.WriteLine("Please choose an expansion by typing the number or short name:"); + var expansions = ExpansionInfo.Table; + + for (int i = 0; i < expansions.Length; i++) + { + var info = expansions[i]; + Console.WriteLine(" - {0,2}: {1} ({2})", i, ((Expansion)info.ID).ToString(), info.Name); + } + + var maxExpansion = (Expansion)expansions[^1].ID; + var maxExpansionName = maxExpansion.ToString(); + + do + { + Console.Write("[enter for {0}]> ", maxExpansionName); + var input = Console.ReadLine(); + Expansion expansion; + + if (string.IsNullOrWhiteSpace(input)) + { + expansion = maxExpansion; + } + else if (int.TryParse(input, NumberStyles.Integer, null, out var number) && + number >= 0 && number < expansions.Length) + { + expansion = (Expansion)number; + } + else if (!Enum.TryParse(input, out expansion)) + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(" is an invalid expansion option."); + continue; + } + + Console.Write("Expansion set to "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(ExpansionInfo.GetInfo(expansion).Name); + Utility.PopColor(); + Console.WriteLine("."); + return expansion; + } while (true); + } + + internal static List GetDataDirectories() + { + Console.WriteLine("Please enter the absolute path to your ClassicUO or Ultima Online data:"); + + var directories = new List(); + + do + { + Console.Write("{0}> ", directories.Count > 0 ? "[enter to finish]" : " "); + var directory = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(directory)) + { + break; + } + + if (Directory.Exists(directory)) + { + directories.Add(directory); + Console.Write("Added "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(directory); + Utility.PopColor(); + Console.WriteLine("."); + } + else + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(directory); + Utility.PopColor(); + Console.WriteLine(" does not exist."); + } + } while (true); + + return directories; + } + + internal static List GetListeners() + { + Console.WriteLine("Please enter the IP and ports to listen:"); + Console.WriteLine(" - Only enter IP addresses directly bound to this machine"); + Console.WriteLine(" - To listen to all IP addresses enter 0.0.0.0"); + + var ips = new List(); + + do + { + // IP:Port? + Console.Write("[{0}]> ", ips.Count > 0 ? "enter to finish" : "0.0.0.0:2593"); + var ipStr = Console.ReadLine(); + + IPEndPoint ip; + if (string.IsNullOrWhiteSpace(ipStr)) + { + if (ips.Count > 0) + { + break; + } + + ip = new IPEndPoint(IPAddress.Any, 2593); + } + else + { + if (!ipStr.ContainsOrdinal(':')) + { + ipStr += ":2593"; + } + + if (!IPEndPoint.TryParse(ipStr, out ip)) + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(ipStr); + Utility.PopColor(); + Console.WriteLine(" is not a valid IP or port."); + continue; + } + } + + ips.Add(ip); + Console.Write("Added "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(ip); + Utility.PopColor(); + Console.WriteLine("."); + } while (true); + + return ips; + } +} diff --git a/Projects/Server/Configuration/ServerSettings.cs b/Projects/Server/Configuration/ServerSettings.cs index 87f69a595..23eddb6de 100644 --- a/Projects/Server/Configuration/ServerSettings.cs +++ b/Projects/Server/Configuration/ServerSettings.cs @@ -25,7 +25,7 @@ namespace Server public List AssemblyDirectories { get; set; } = new(); [JsonPropertyName("dataDirectories")] - public List DataDirectories { get; set; } = new(); + public HashSet DataDirectories { get; set; } = new(); [JsonPropertyName("listeners")] public List Listeners { get; set; } = new(); diff --git a/Projects/Server/MultiData.cs b/Projects/Server/MultiData.cs index 6bda94398..ead7417af 100644 --- a/Projects/Server/MultiData.cs +++ b/Projects/Server/MultiData.cs @@ -11,7 +11,8 @@ namespace Server private static readonly BinaryReader m_IndexReader; private static readonly BinaryReader m_StreamReader; - private static readonly bool UsingUOPFormat; + public static readonly bool PostHSMulFormat; + public static readonly bool UsingUOPFormat; static MultiData() { @@ -21,9 +22,13 @@ namespace Server { LoadUOP(multiUOPPath); UsingUOPFormat = true; + PostHSMulFormat = false; return; } + // Client version 7.0.9.0+ + PostHSMulFormat = UOClient.ServerClientVersion >= ClientVersion.Version7090; + var idxPath = Core.FindDataFile("multi.idx"); var mulPath = Core.FindDataFile("multi.mul"); @@ -223,7 +228,7 @@ namespace Server m_StreamReader.BaseStream.Seek(lookup, SeekOrigin.Begin); - return new MultiComponentList(m_StreamReader, length / (MultiComponentList.PostHSFormat ? 16 : 12)); + return new MultiComponentList(m_StreamReader, length / (PostHSMulFormat ? 16 : 12)); } catch { @@ -378,15 +383,9 @@ namespace Server allTiles[i].OffsetX = reader.ReadInt16(); allTiles[i].OffsetY = reader.ReadInt16(); allTiles[i].OffsetZ = reader.ReadInt16(); - - if (PostHSFormat) - { - allTiles[i].Flags = (TileFlag)reader.ReadUInt64(); - } - else - { - allTiles[i].Flags = (TileFlag)reader.ReadUInt32(); - } + allTiles[i].Flags = MultiData.PostHSMulFormat + ? (TileFlag)reader.ReadUInt64() + : (TileFlag)reader.ReadUInt32(); var e = allTiles[i]; @@ -539,7 +538,7 @@ namespace Server public static void Configure() { // OSI Client Patch 7.0.9.0 - PostHSFormat = ServerConfiguration.GetOrUpdateSetting("maps.enablePostHSMultiComponentFormat", true); + PostHSFormat = ServerConfiguration.GetSetting("maps.enablePostHSMultiComponentFormat", true); } public static bool PostHSFormat { get; set; } diff --git a/Projects/Server/Network/NetState/NetState.ClientVersion.cs b/Projects/Server/Network/NetState/NetState.ClientVersion.cs index 4302ec0b1..844c9fb21 100644 --- a/Projects/Server/Network/NetState/NetState.ClientVersion.cs +++ b/Projects/Server/Network/NetState/NetState.ClientVersion.cs @@ -19,23 +19,6 @@ namespace Server.Network { public partial class NetState { - private static readonly ClientVersion m_Version400a = new("4.0.0a"); - private static readonly ClientVersion m_Version407a = new("4.0.7a"); - private static readonly ClientVersion m_Version500a = new("5.0.0a"); - private static readonly ClientVersion m_Version502b = new("5.0.2b"); - private static readonly ClientVersion m_Version6000 = new("6.0.0.0"); - private static readonly ClientVersion m_Version6017 = new("6.0.1.7"); - private static readonly ClientVersion m_Version60142 = new("6.0.14.2"); - private static readonly ClientVersion m_Version7000 = new("7.0.0.0"); - private static readonly ClientVersion m_Version7090 = new("7.0.9.0"); - private static readonly ClientVersion m_Version70130 = new("7.0.13.0"); - private static readonly ClientVersion m_Version70160 = new("7.0.16.0"); - private static readonly ClientVersion m_Version70300 = new("7.0.30.0"); - private static readonly ClientVersion m_Version70331 = new("7.0.33.1"); - private static readonly ClientVersion m_Version704565 = new("7.0.45.65"); - private static readonly ClientVersion m_Version70500 = new("7.0.50.0"); - private static readonly ClientVersion m_Version70610 = new("7.0.61.0"); - public ProtocolChanges ProtocolChanges { get; set; } public ClientFlags Flags { get; set; } @@ -52,23 +35,23 @@ namespace Server.Network public static ProtocolChanges ProtocolChangesByVersion(ClientVersion version) => version switch { - var v when v >= m_Version70610 => ProtocolChanges.Version70610, - var v when v >= m_Version70500 => ProtocolChanges.Version70500, - var v when v >= m_Version704565 => ProtocolChanges.Version704565, - var v when v >= m_Version70331 => ProtocolChanges.Version70331, - var v when v >= m_Version70300 => ProtocolChanges.Version70300, - var v when v >= m_Version70160 => ProtocolChanges.Version70160, - var v when v >= m_Version70130 => ProtocolChanges.Version70130, - var v when v >= m_Version7090 => ProtocolChanges.Version7090, - var v when v >= m_Version7000 => ProtocolChanges.Version7000, - var v when v >= m_Version60142 => ProtocolChanges.Version60142, - var v when v >= m_Version6017 => ProtocolChanges.Version6017, - var v when v >= m_Version6000 => ProtocolChanges.Version6000, - var v when v >= m_Version502b => ProtocolChanges.Version502b, - var v when v >= m_Version500a => ProtocolChanges.Version500a, - var v when v >= m_Version407a => ProtocolChanges.Version407a, - var v when v >= m_Version400a => ProtocolChanges.Version400a, - _ => ProtocolChanges.None + var v when v >= ClientVersion.Version70610 => ProtocolChanges.Version70610, + var v when v >= ClientVersion.Version70500 => ProtocolChanges.Version70500, + var v when v >= ClientVersion.Version704565 => ProtocolChanges.Version704565, + var v when v >= ClientVersion.Version70331 => ProtocolChanges.Version70331, + var v when v >= ClientVersion.Version70300 => ProtocolChanges.Version70300, + var v when v >= ClientVersion.Version70160 => ProtocolChanges.Version70160, + var v when v >= ClientVersion.Version70130 => ProtocolChanges.Version70130, + var v when v >= ClientVersion.Version7090 => ProtocolChanges.Version7090, + var v when v >= ClientVersion.Version7000 => ProtocolChanges.Version7000, + var v when v >= ClientVersion.Version60142 => ProtocolChanges.Version60142, + var v when v >= ClientVersion.Version6017 => ProtocolChanges.Version6017, + var v when v >= ClientVersion.Version6000 => ProtocolChanges.Version6000, + var v when v >= ClientVersion.Version502b => ProtocolChanges.Version502b, + var v when v >= ClientVersion.Version500a => ProtocolChanges.Version500a, + var v when v >= ClientVersion.Version407a => ProtocolChanges.Version407a, + var v when v >= ClientVersion.Version400a => ProtocolChanges.Version400a, + _ => ProtocolChanges.None }; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Utilities/PathUtility.cs b/Projects/Server/Utilities/PathUtility.cs index 6f22c46db..b85b7b07d 100644 --- a/Projects/Server/Utilities/PathUtility.cs +++ b/Projects/Server/Utilities/PathUtility.cs @@ -17,70 +17,69 @@ using System; using System.IO; using Server.Text; -namespace Server +namespace Server; + +public static class PathUtility { - public static class PathUtility + public static string EnsureDirectory(string dir) { - public static string EnsureDirectory(string dir) + var path = GetFullPath(dir, Core.BaseDirectory); + Directory.CreateDirectory(path); + + return path; + } + + public static void EnsureDirectory(this FileInfo fi) + { + var dir = GetFullPath(fi.DirectoryName, Core.BaseDirectory); + if (dir != null) { - var path = GetFullPath(dir, Core.BaseDirectory); - Directory.CreateDirectory(path); - - return path; - } - - public static void EnsureDirectory(this FileInfo fi) - { - var dir = GetFullPath(fi.DirectoryName, Core.BaseDirectory); - if (dir != null) - { - Directory.CreateDirectory(dir); - } - } - - public static void EnsureDirectory(this DirectoryInfo di) - { - var file = GetFullPath(di.FullName, Core.BaseDirectory); - Directory.CreateDirectory(file); - } - - public static string GetFullPath(string relativeOrAbsolutePath) => - GetFullPath(relativeOrAbsolutePath, Core.BaseDirectory); - - public static string GetFullPath(string relativeOrAbsolutePath, string basePath) => - relativeOrAbsolutePath switch - { - null => null, - "" => basePath, - _ => Path.IsPathRooted(relativeOrAbsolutePath) - ? relativeOrAbsolutePath - : Path.GetFullPath(relativeOrAbsolutePath, basePath) - }; - - public static string EnsureRandomPath(string basePath) - { - Span bytes = stackalloc byte[8]; - Utility.RandomBytes(bytes); - return EnsureDirectory(Path.Combine(basePath, bytes.ToHexString())); - } - - public static void CopyDirectory(string sourcePath, string destinationPath, bool recursive = true) - { - var searchOptions = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - foreach (var file in Directory.EnumerateFiles(sourcePath, "*", searchOptions)) - { - var fi = new FileInfo(file); - var relativePath = Path.GetRelativePath(sourcePath, fi.DirectoryName!); - var destFolder = Path.Combine(destinationPath, relativePath); - EnsureDirectory(destFolder); - fi.CopyTo(Path.Combine(destFolder, fi.Name)); - } - } - - public static void MoveDirectory(string sourcePath, string destinationPath) - { - CopyDirectory(sourcePath, destinationPath); - Directory.Delete(sourcePath, true); + Directory.CreateDirectory(dir); } } + + public static void EnsureDirectory(this DirectoryInfo di) + { + var file = GetFullPath(di.FullName, Core.BaseDirectory); + Directory.CreateDirectory(file); + } + + public static string GetFullPath(string relativeOrAbsolutePath) => + GetFullPath(relativeOrAbsolutePath, Core.BaseDirectory); + + public static string GetFullPath(string relativeOrAbsolutePath, string basePath) => + relativeOrAbsolutePath switch + { + null => null, + "" => basePath, + _ => Path.IsPathRooted(relativeOrAbsolutePath) + ? relativeOrAbsolutePath + : Path.GetFullPath(relativeOrAbsolutePath, basePath) + }; + + public static string EnsureRandomPath(string basePath) + { + Span bytes = stackalloc byte[8]; + Utility.RandomBytes(bytes); + return EnsureDirectory(Path.Combine(basePath, bytes.ToHexString())); + } + + public static void CopyDirectory(string sourcePath, string destinationPath, bool recursive = true) + { + var searchOptions = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + foreach (var file in Directory.EnumerateFiles(sourcePath, "*", searchOptions)) + { + var fi = new FileInfo(file); + var relativePath = Path.GetRelativePath(sourcePath, fi.DirectoryName!); + var destFolder = Path.Combine(destinationPath, relativePath); + EnsureDirectory(destFolder); + fi.CopyTo(Path.Combine(destFolder, fi.Name)); + } + } + + public static void MoveDirectory(string sourcePath, string destinationPath) + { + CopyDirectory(sourcePath, destinationPath); + Directory.Delete(sourcePath, true); + } } diff --git a/Projects/UOContent/Misc/AccountPrompt.cs b/Projects/UOContent/Misc/AccountPrompt.cs index 1483f022e..0d4a22e06 100644 --- a/Projects/UOContent/Misc/AccountPrompt.cs +++ b/Projects/UOContent/Misc/AccountPrompt.cs @@ -1,41 +1,42 @@ using System; using Server.Accounting; +using Server.Logging; -namespace Server.Misc +namespace Server.Misc; + +public static class AccountPrompt { - public static class AccountPrompt + private static readonly ILogger logger = LogFactory.GetLogger(typeof(AccountPrompt)); + + public static void Initialize() { - public static void Initialize() + if (Accounts.Count == 0) { - if (Accounts.Count == 0) + Console.WriteLine("This server has no accounts."); + Console.Write("Do you want to create the owner account now? (y/n): "); + + var answer = Console.ReadLine(); + if (answer is "y" or "Y") { - Console.WriteLine("This server has no accounts."); - Console.Write("Do you want to create the owner account now? (y/n): "); + Console.WriteLine(); - var answer = Console.ReadLine(); - if (answer is "y" or "Y") + Console.Write("Username: "); + var username = Console.ReadLine(); + + Console.Write("Password: "); + var password = Console.ReadLine(); + + var a = new Account(username, password) { - Console.WriteLine(); + AccessLevel = AccessLevel.Owner + }; - Console.Write("Username: "); - var username = Console.ReadLine(); - - Console.Write("Password: "); - var password = Console.ReadLine(); - - var a = new Account(username, password); - a.AccessLevel = AccessLevel.Owner; - - Console.WriteLine("Account created."); - - ServerAccess.AddProtectedAccount(a, true); - Console.WriteLine("Added {0} to the protected accounts list.", a.Username); - } - else - { - Console.WriteLine(); - Console.WriteLine("Account not created."); - } + logger.Information("Owner account created: {0}", username); + ServerAccess.AddProtectedAccount(a, true); + } + else + { + logger.Warning("No owner account created."); } } } diff --git a/Projects/UOContent/Misc/ClientVerification.cs b/Projects/UOContent/Misc/ClientVerification.cs index 7b264eec8..20c90f1f7 100644 --- a/Projects/UOContent/Misc/ClientVerification.cs +++ b/Projects/UOContent/Misc/ClientVerification.cs @@ -1,6 +1,4 @@ using System; -using System.Buffers.Binary; -using System.IO; using Server.Buffers; using Server.Gumps; using Server.Logging; @@ -14,7 +12,6 @@ namespace Server.Misc private static readonly ILogger logger = LogFactory.GetLogger(typeof(ClientVerification)); private static bool _enable; - private static bool _detectClientRequirement; private static InvalidClientResponse _invalidClientResponse; private static string _versionExpression; @@ -33,11 +30,6 @@ namespace Server.Misc MinRequired = ServerConfiguration.GetSetting("clientVerification.minRequired", (ClientVersion)null); MaxRequired = ServerConfiguration.GetSetting("clientVerification.maxRequired", (ClientVersion)null); - if (MinRequired == null && MaxRequired == null) - { - _detectClientRequirement = ServerConfiguration.GetOrUpdateSetting("clientVerification.detectFromClientExe", true); - } - _enable = ServerConfiguration.GetOrUpdateSetting("clientVerification.enable", true); _invalidClientResponse = ServerConfiguration.GetOrUpdateSetting("clientVerification.invalidClientResponse", InvalidClientResponse.Kick); @@ -53,40 +45,9 @@ namespace Server.Misc { EventSink.ClientVersionReceived += EventSink_ClientVersionReceived; - if (_detectClientRequirement) + if (MinRequired == null && MaxRequired == null) { - var path = Core.FindDataFile("client.exe", false); - - if (File.Exists(path)) - { - using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - var buffer = GC.AllocateUninitializedArray((int)fs.Length, true); - fs.Read(buffer); - // VS_VERSION_INFO (unicode) - Span vsVersionInfo = stackalloc byte[] - { - 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, - 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, - 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00, - 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00 - }; - - for (var i = 0; i < buffer.Length; i++) - { - if (vsVersionInfo.SequenceEqual(buffer.AsSpan(i, 30))) - { - var offset = i + 42; // 30 + 12 - - var minorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset)); - var majorPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 2)); - var privatePart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 4)); - var buildPart = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset + 6)); - - MinRequired = new ClientVersion(majorPart, minorPart, buildPart, privatePart); - break; - } - } - } + MinRequired = UOClient.ServerClientVersion; } if (MinRequired != null || MaxRequired != null) diff --git a/Projects/UOContent/Misc/ServerAccess.cs b/Projects/UOContent/Misc/ServerAccess.cs index 9b43d604a..357082c85 100644 --- a/Projects/UOContent/Misc/ServerAccess.cs +++ b/Projects/UOContent/Misc/ServerAccess.cs @@ -56,8 +56,11 @@ public static class ServerAccess } ServerAccessConfiguration = JsonConfig.Deserialize(path); - var protectedAccounts = string.Join(", ", ServerAccessConfiguration.ProtectedAccounts); - logger.Information("Protected accounts registered: {0}", protectedAccounts); + if (ServerAccessConfiguration.ProtectedAccounts.Count > 0) + { + var protectedAccounts = string.Join(", ", ServerAccessConfiguration.ProtectedAccounts); + logger.Information("Protected accounts registered: {0}", protectedAccounts); + } } public static void Initialize()