diff --git a/MinecraftClient/ChatBots/Alerts.cs b/MinecraftClient/ChatBots/Alerts.cs index c3f1f6a0..51a82a18 100644 --- a/MinecraftClient/ChatBots/Alerts.cs +++ b/MinecraftClient/ChatBots/Alerts.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -8,9 +9,85 @@ namespace MinecraftClient.ChatBots /// public class Alerts : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "Alerts"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.Alerts.Beep_Enabled$")] + public bool Beep_Enabled = true; + + [TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Words$")] + public bool Trigger_By_Words = false; + + [TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Rain$")] + public bool Trigger_By_Rain = false; + + [TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Thunderstorm$")] + public bool Trigger_By_Thunderstorm = false; + + [TomlInlineComment("$config.ChatBot.Alerts.Matches_File$")] + public string Matches_File = @"alerts.txt"; + + [TomlInlineComment("$config.ChatBot.Alerts.Excludes_File$")] + public string Excludes_File = @"alerts-exclude.txt"; + + [TomlInlineComment("$config.ChatBot.Alerts.Log_To_File$")] + public bool Log_To_File = false; + + [TomlInlineComment("$config.ChatBot.Alerts.Log_File$")] + public string Log_File = @"alerts-log.txt"; + + public void OnSettingUpdate() + { + if (!Enabled) return; + + bool checkSuccessed = true; + + if (Trigger_By_Words) + { + if (!System.IO.File.Exists(Matches_File)) + { + checkSuccessed = false; + LogToConsole(BotName, "File not found: " + System.IO.Path.GetFullPath(Matches_File)); + } + + if (!System.IO.File.Exists(Excludes_File)) + { + checkSuccessed = false; + LogToConsole(BotName, "File not found: " + System.IO.Path.GetFullPath(Excludes_File)); + } + + if (Log_To_File) + { + try + { + System.IO.File.AppendAllText(Log_File, string.Empty); + } + catch + { + checkSuccessed = false; + LogToConsole(BotName, "Can't write logs to " + System.IO.Path.GetFullPath(Excludes_File)); + } + } + } + + if (!checkSuccessed) + { + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + } + private string[] dictionary = Array.Empty(); private string[] excludelist = Array.Empty(); - private bool logToFile = false; + float curRainLevel = 0; float curThunderLevel = 0; const float threshold = 0.2f; @@ -20,11 +97,10 @@ namespace MinecraftClient.ChatBots /// public override void Initialize() { - if (Settings.Alerts_Trigger_By_Words) + if (Config.Trigger_By_Words) { - dictionary = LoadDistinctEntriesFromFile(Settings.Alerts_MatchesFile); - excludelist = LoadDistinctEntriesFromFile(Settings.Alerts_ExcludesFile); - logToFile = Settings.Alerts_File_Logging; + dictionary = LoadDistinctEntriesFromFile(Config.Matches_File); + excludelist = LoadDistinctEntriesFromFile(Config.Excludes_File); } } @@ -34,7 +110,7 @@ namespace MinecraftClient.ChatBots /// Received text public override void GetText(string text) { - if (Settings.Alerts_Trigger_By_Words) + if (Config.Trigger_By_Words) { //Remove color codes and convert to lowercase text = GetVerbatim(text).ToLower(); @@ -45,16 +121,16 @@ namespace MinecraftClient.ChatBots //Show an alert for each alert item found in text, if any foreach (string alert in dictionary.Where(alert => text.Contains(alert))) { - if (Settings.Alerts_Beep_Enabled) + if (Config.Beep_Enabled) Console.Beep(); //Text found ! ConsoleIO.WriteLine(text.Replace(alert, "§c" + alert + "§r")); - if (logToFile && Settings.Alerts_LogFile.Length > 0) + if (Config.Log_To_File && Config.Log_File.Length > 0) { DateTime now = DateTime.Now; string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']'; - System.IO.File.AppendAllText(Settings.Alerts_LogFile, TimeStamp + " " + GetVerbatim(text) + "\n"); + System.IO.File.AppendAllText(Config.Log_File, TimeStamp + " " + GetVerbatim(text) + "\n"); } } } @@ -65,9 +141,9 @@ namespace MinecraftClient.ChatBots { if (curRainLevel < threshold && level >= threshold) { - if (Settings.Alerts_Trigger_By_Rain) + if (Config.Trigger_By_Rain) { - if (Settings.Alerts_Beep_Enabled) + if (Config.Beep_Enabled) { Console.Beep(); Console.Beep(); @@ -77,9 +153,9 @@ namespace MinecraftClient.ChatBots } else if (curRainLevel >= threshold && level < threshold) { - if (Settings.Alerts_Trigger_By_Rain) + if (Config.Trigger_By_Rain) { - if (Settings.Alerts_Beep_Enabled) + if (Config.Beep_Enabled) { Console.Beep(); } @@ -93,9 +169,9 @@ namespace MinecraftClient.ChatBots { if (curThunderLevel < threshold && level >= threshold) { - if (Settings.Alerts_Trigger_By_Thunderstorm) + if (Config.Trigger_By_Thunderstorm) { - if (Settings.Alerts_Beep_Enabled) + if (Config.Beep_Enabled) { Console.Beep(); Console.Beep(); @@ -105,9 +181,9 @@ namespace MinecraftClient.ChatBots } else if (curThunderLevel >= threshold && level < threshold) { - if (Settings.Alerts_Trigger_By_Thunderstorm) + if (Config.Trigger_By_Thunderstorm) { - if (Settings.Alerts_Beep_Enabled) + if (Config.Beep_Enabled) { Console.Beep(); } diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index c216d807..9408cd5e 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -1,6 +1,6 @@ using System; -using System.Globalization; using MinecraftClient.Mapping; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -10,94 +10,94 @@ namespace MinecraftClient.ChatBots public class AntiAFK : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AntiAFK"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.AntiAfk.Delay$")] + public Range Delay = new(600); + + [TomlInlineComment("$config.ChatBot.AntiAfk.Command$")] + public string Command = "/ping"; + + [TomlInlineComment("$config.ChatBot.AntiAfk.Use_Terrain_Handling$")] + public bool Use_Terrain_Handling = false; + + [TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Range$")] + public int Walk_Range = 5; + + [TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Retries$")] + public int Walk_Retries = 20; + + public void OnSettingUpdate() + { + if (Walk_Range <= 0) + { + Walk_Range = 5; + LogToConsole(BotName, Translations.TryGet("bot.antiafk.invalid_walk_range")); + } + + if (Delay.min > Delay.max) + { + (Delay.min, Delay.max) = (Delay.max, Delay.min); + LogToConsole(BotName, Translations.TryGet("bot.antiafk.swapping")); + } + } + + public struct Range + { + public int min, max; + + public Range(int value) + { + value = Math.Max(value, 10); + min = max = value; + } + + public Range(int min, int max) + { + min = Math.Max(min, 10); + max = Math.Max(max, 10); + this.min = min; + this.max = max; + } + } + } + private int count; - private readonly string pingparam; - private int timeping = 600; - private int timepingMax = -1; - private bool useTerrainHandling = false; private bool previousSneakState = false; - private int walkRange = 5; - private readonly int walkRetries = 10; private readonly Random random = new(); /// /// This bot sends a /ping command every X seconds in order to stay non-afk. /// - /// Time amount between each ping (10 = 1s, 600 = 1 minute, etc.) Can be a range of numbers eg. 10-600 - - public AntiAFK(string pingparam, bool useTerrainHandling, int walkRange, int walkRetries) + public AntiAFK() { count = 0; - this.pingparam = pingparam; - this.useTerrainHandling = useTerrainHandling; - this.walkRange = walkRange; - this.walkRetries = walkRetries; } public override void Initialize() { - if (useTerrainHandling) + if (Config.Use_Terrain_Handling) { if (!GetTerrainEnabled()) { - useTerrainHandling = false; LogToConsole(Translations.TryGet("bot.antiafk.not_using_terrain_handling")); } - else - { - if (walkRange <= 0) - { - walkRange = 5; - LogToConsole(Translations.TryGet("bot.antiafk.invalid_walk_range")); - } - } } - - if (string.IsNullOrEmpty(pingparam)) - LogToConsole(Translations.TryGet("bot.antiafk.invalid_time")); - else - { - // Handle the random range - if (pingparam.Contains('-')) - { - string[] parts = pingparam.Split("-"); - - if (parts.Length == 2) - { - if (int.TryParse(parts[0].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int firstTime)) - { - timeping = firstTime; - - if (int.TryParse(parts[1].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int secondTime)) - timepingMax = secondTime; - else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range_partial", timeping)); - } - else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range")); - } - else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range")); - } - else - { - if (int.TryParse(pingparam.Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int value)) - timeping = value; - else LogToConsole(Translations.TryGet("bot.antiafk.invalid_value")); - } - } - - if (timepingMax != -1 && timeping > timepingMax) - { - (timeping, timepingMax) = (timepingMax, timeping); - LogToConsole(Translations.TryGet("bot.antiafk.swapping")); - } - - if (timeping < 10) { timeping = 10; } //To avoid flooding } public override void Update() { count++; - if ((timepingMax != -1 && count == random.Next(timeping, timepingMax)) || count == timeping) + if (count == random.Next(Config.Delay.min, Config.Delay.max)) { DoAntiAfkStuff(); count = 0; @@ -107,7 +107,7 @@ namespace MinecraftClient.ChatBots private void DoAntiAfkStuff() { - if (useTerrainHandling) + if (Config.Use_Terrain_Handling && GetTerrainEnabled()) { Location currentLocation = GetCurrentLocation(); Location goal; @@ -118,19 +118,19 @@ namespace MinecraftClient.ChatBots while (!moved) { - if (triesCounter++ >= walkRetries) + if (triesCounter++ >= Config.Walk_Retries) { useAlternativeMethod = true; break; } - goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange); + goal = GetRandomLocationWithinRangeXZ(currentLocation, Config.Walk_Range); // Prevent getting the same location while ((currentLocation.X == goal.X) && (currentLocation.Y == goal.Y) && (currentLocation.Z == goal.Z)) { LogToConsole("Same location!, generating new one"); - goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange); + goal = GetRandomLocationWithinRangeXZ(currentLocation, Config.Walk_Range); } if (!Movement.CheckChunkLoading(GetWorld(), currentLocation, goal)) @@ -151,7 +151,7 @@ namespace MinecraftClient.ChatBots } } - SendText(Settings.AntiAFK_Command); + SendText(Config.Command); Sneak(previousSneakState); previousSneakState = !previousSneakState; count = 0; diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index f9c6e820..522a8202 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -2,14 +2,89 @@ using System.Collections.Generic; using System.IO; using MinecraftClient.Mapping; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { /// /// The AutoAttack bot will automatically attack any hostile mob close to the player /// - class AutoAttack : ChatBot + public class AutoAttack : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoAttack"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.AutoAttack.Mode$")] + public AttackMode Mode = AttackMode.single; + + [TomlInlineComment("$config.ChatBot.AutoAttack.Priority$")] + public PriorityType Priority = PriorityType.distance; + + [TomlInlineComment("$config.ChatBot.AutoAttack.Cooldown_Time$")] + public CooldownConfig Cooldown_Time = new(false, 1.0); + + [TomlInlineComment("$config.ChatBot.AutoAttack.Interaction$")] + public InteractType Interaction = InteractType.Attack; + + [TomlInlineComment("$config.ChatBot.AutoAttack.Attack_Hostile$")] + public bool Attack_Hostile = true; + + [TomlInlineComment("$config.ChatBot.AutoAttack.Attack_Passive$")] + public bool Attack_Passive = false; + + [TomlInlineComment("$config.ChatBot.AutoAttack.List_Mode$")] + public ListType List_Mode = ListType.blacklist; + + [TomlInlineComment("$config.ChatBot.AutoAttack.Entites_List$")] + public List Entites_List = new() { EntityType.Zombie, EntityType.Cow }; + + public void OnSettingUpdate() + { + if (Cooldown_Time.Custom && Cooldown_Time.value <= 0) + { + LogToConsole(BotName, Translations.TryGet("bot.autoAttack.invalidcooldown")); + Cooldown_Time.value = 1.0; + } + } + + public enum AttackMode { single, multi }; + + public enum PriorityType { distance, health }; + + public enum ListType { blacklist, whitelist }; + + public struct CooldownConfig + { + public bool Custom; + public double value; + + public CooldownConfig() + { + Custom = false; + value = 0; + } + + public CooldownConfig(double value) + { + Custom = true; + this.value = value; + } + + public CooldownConfig(bool Override, double value) + { + this.Custom = Override; + this.value = value; + } + } + } + private readonly Dictionary entitiesToAttack = new(); // mobs within attack range private int attackCooldown = 6; private int attackCooldownCounter = 6; @@ -19,73 +94,20 @@ namespace MinecraftClient.ChatBots private readonly int attackRange = 4; private Double serverTPS; private float health = 100; - private readonly bool singleMode = true; - private readonly bool priorityDistance = true; - private readonly InteractType interactMode; private readonly bool attackHostile = true; private readonly bool attackPassive = false; - private readonly string listMode = "blacklist"; - private readonly List listedEntites = new(); - public AutoAttack( - string mode, string priority, bool overrideAttackSpeed = false, double cooldownSeconds = 1, InteractType interaction = InteractType.Attack) + public AutoAttack() { - if (mode == "single") - singleMode = true; - else if (mode == "multi") - singleMode = false; - else LogToConsoleTranslated("bot.autoAttack.mode", mode); - - if (priority == "distance") - priorityDistance = true; - else if (priority == "health") - priorityDistance = false; - else LogToConsoleTranslated("bot.autoAttack.priority", priority); - - interactMode = interaction; - - if (overrideAttackSpeed) + overrideAttackSpeed = Config.Cooldown_Time.Custom; + if (Config.Cooldown_Time.Custom) { - if (cooldownSeconds <= 0) - { - LogToConsoleTranslated("bot.autoAttack.invalidcooldown"); - } - else - { - this.overrideAttackSpeed = overrideAttackSpeed; - attackCooldownSeconds = cooldownSeconds; - attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); - } + attackCooldownSeconds = Config.Cooldown_Time.value; + attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); } - attackHostile = Settings.AutoAttack_Attack_Hostile; - attackPassive = Settings.AutoAttack_Attack_Passive; - - if (Settings.AutoAttack_ListMode.Length > 0) - { - listMode = Settings.AutoAttack_ListMode.ToLower(); - - if (!(listMode.Equals("whitelist", StringComparison.OrdinalIgnoreCase) || listMode.Equals("blacklist", StringComparison.OrdinalIgnoreCase))) - { - LogToConsole(Translations.TryGet("bot.autoAttack.invalidlist")); - listMode = "blacklist"; - } - } - else LogToConsole(Translations.TryGet("bot.autoAttack.invalidlist")); - - if (File.Exists(Settings.AutoAttack_ListFile)) - { - string[] entityList = LoadDistinctEntriesFromFile(Settings.AutoAttack_ListFile); - - if (entityList.Length > 0) - { - foreach (var item in entityList) - { - if (Enum.TryParse(item, true, out EntityType resultingType)) - listedEntites.Add(resultingType); - } - } - } + attackHostile = Config.Attack_Hostile; + attackPassive = Config.Attack_Passive; } public override void Initialize() @@ -108,10 +130,10 @@ namespace MinecraftClient.ChatBots attackCooldownCounter = attackCooldown; if (entitiesToAttack.Count > 0) { - if (singleMode) + if (Config.Mode == Configs.AttackMode.single) { int priorityEntity = 0; - if (priorityDistance) // closest distance priority + if (Config.Priority == Configs.PriorityType.distance) // closest distance priority { double distance = 5; foreach (var entity in entitiesToAttack) @@ -142,7 +164,7 @@ namespace MinecraftClient.ChatBots // check entity distance and health again if (ShouldAttackEntity(entitiesToAttack[priorityEntity])) { - InteractEntity(priorityEntity, interactMode); // hit the entity! + InteractEntity(priorityEntity, Config.Interaction); // hit the entity! SendAnimation(Inventory.Hand.MainHand); // Arm animation } } @@ -154,7 +176,7 @@ namespace MinecraftClient.ChatBots // check that we are in range once again. if (ShouldAttackEntity(entity.Value)) { - InteractEntity(entity.Key, interactMode); // hit the entity! + InteractEntity(entity.Key, Config.Interaction); // hit the entity! } } SendAnimation(Inventory.Hand.MainHand); // Arm animation @@ -206,10 +228,13 @@ namespace MinecraftClient.ChatBots if (attackPassive && entity.Type.IsPassive()) result = true; - if (listedEntites.Count > 0) + if (Config.Entites_List.Count > 0) { - bool inList = listedEntites.Contains(entity.Type); - result = listMode.Equals("blacklist") ? (!inList && result) : (inList); + bool inList = Config.Entites_List.Contains(entity.Type); + if (Config.List_Mode == Configs.ListType.blacklist) + result = !inList && result; + else + result = inList; } return result; diff --git a/MinecraftClient/ChatBots/AutoCraft.cs b/MinecraftClient/ChatBots/AutoCraft.cs index a07bdf33..74876699 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -4,11 +4,27 @@ using System.IO; using System.Linq; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { - class AutoCraft : ChatBot + public class AutoCraft : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoCraft"; + + public bool Enabled = false; + + public string configFile = @"autocraft\config.ini"; + + public void OnSettingUpdate() { } + } + private bool waitingForMaterials = false; private bool waitingForUpdate = false; private bool waitingForTable = false; @@ -26,7 +42,6 @@ namespace MinecraftClient.ChatBots private int updateTimeout = 0; private string timeoutAction = "unspecified"; - private readonly string configPath = @"autocraft\config.ini"; private string lastRecipe = ""; // Used in parsing recipe config private readonly Dictionary recipes = new(); @@ -158,11 +173,6 @@ namespace MinecraftClient.ChatBots } } - public AutoCraft(string configPath = @"autocraft\config.ini") - { - this.configPath = configPath; - } - public override void Initialize() { if (!GetInventoryEnabled()) @@ -247,9 +257,9 @@ namespace MinecraftClient.ChatBots public void LoadConfig() { - if (!File.Exists(configPath)) + if (!File.Exists(Config.configFile)) { - if (!Directory.Exists(configPath)) + if (!Directory.Exists(Config.configFile)) { Directory.CreateDirectory(@"autocraft"); } @@ -289,19 +299,19 @@ namespace MinecraftClient.ChatBots "# For the naming of the items, please see", "# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs" }; - File.WriteAllLines(configPath, content); + File.WriteAllLines(Config.configFile, content); } private void ParseConfig() { - string[] content = File.ReadAllLines(configPath); + string[] content = File.ReadAllLines(Config.configFile); if (content.Length <= 0) { - throw new Exception(Translations.Get("bot.autoCraft.exception.empty", configPath)); + throw new Exception(Translations.Get("bot.autoCraft.exception.empty", Config.configFile)); } if (content[0].ToLower() != "[autocraft]") { - throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", configPath)); + throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", Config.configFile)); } // local variable for use in parsing config diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index e101da54..c962cfaf 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -2,51 +2,43 @@ using System.Collections.Generic; using System.Linq; using MinecraftClient.Inventory; +using Tomlet.Attributes; +using static MinecraftClient.ChatBots.AutoDrop.Configs; namespace MinecraftClient.ChatBots { - class AutoDrop : ChatBot + public class AutoDrop : ChatBot { - private enum Mode + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs { - Include, // Items in list will be dropped - Exclude, // Items in list will be kept - Everything // Everything will be dropped + [NonSerialized] + private const string BotName = "AutoDrop"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.AutoDrop.Mode$")] + public DropMode Mode = DropMode.include; + + [TomlInlineComment("$config.ChatBot.AutoDrop.Items$")] + public List Items = new() { ItemType.Cobblestone, ItemType.Dirt }; + + public void OnSettingUpdate() { } + + public enum DropMode + { + include, // Items in list will be dropped + exclude, // Items in list will be kept + everything // Everything will be dropped + } } - private Mode dropMode = Mode.Include; - private bool enable = true; private int updateDebounce = 0; private readonly int updateDebounceValue = 2; private int inventoryUpdated = -1; - private readonly List itemList = new(); - - public AutoDrop(string mode, string itemList) - { - if (!Enum.TryParse(mode, true, out dropMode)) - { - LogToConsoleTranslated("bot.autoDrop.no_mode"); - } - if (dropMode != Mode.Everything) - this.itemList = ItemListParser(itemList).ToList(); - } - - /// - /// Convert an item type string to item type array - /// - /// String to convert - /// Item type array - private ItemType[] ItemListParser(string itemList) - { - string trimed = new(itemList.Where(c => !char.IsWhiteSpace(c)).ToArray()); - List result = new(); - foreach (string t in trimed.Split(',')) - if (Enum.TryParse(t, true, out ItemType item)) - result.Add(item); - return result.ToArray(); - } - public string CommandHandler(string cmd, string[] args) { if (args.Length > 0) @@ -54,19 +46,19 @@ namespace MinecraftClient.ChatBots switch (args[0].ToLower()) { case "on": - enable = true; + Config.Enabled = true; inventoryUpdated = 0; OnUpdateFinish(); return Translations.Get("bot.autoDrop.on"); case "off": - enable = false; + Config.Enabled = false; return Translations.Get("bot.autoDrop.off"); case "add": if (args.Length >= 2) { if (Enum.TryParse(args[1], true, out ItemType item)) { - itemList.Add(item); + Config.Items.Add(item); return Translations.Get("bot.autoDrop.added", item.ToString()); } else @@ -83,9 +75,9 @@ namespace MinecraftClient.ChatBots { if (Enum.TryParse(args[1], true, out ItemType item)) { - if (itemList.Contains(item)) + if (Config.Items.Contains(item)) { - itemList.Remove(item); + Config.Items.Remove(item); return Translations.Get("bot.autoDrop.removed", item.ToString()); } else @@ -103,9 +95,9 @@ namespace MinecraftClient.ChatBots return Translations.Get("cmd.inventory.help.usage") + ": remove "; } case "list": - if (itemList.Count > 0) + if (Config.Items.Count > 0) { - return Translations.Get("bot.autoDrop.list", itemList.Count, string.Join("\n", itemList)); + return Translations.Get("bot.autoDrop.list", Config.Items.Count, string.Join("\n", Config.Items)); } else { @@ -117,20 +109,20 @@ namespace MinecraftClient.ChatBots switch (args[1].ToLower()) { case "include": - dropMode = Mode.Include; + Config.Mode = DropMode.include; break; case "exclude": - dropMode = Mode.Exclude; + Config.Mode = DropMode.exclude; break; case "everything": - dropMode = Mode.Everything; + Config.Mode = DropMode.everything; break; default: return Translations.Get("bot.autoDrop.unknown_mode"); // Unknwon mode. Available modes: Include, Exclude, Everything } inventoryUpdated = 0; OnUpdateFinish(); - return Translations.Get("bot.autoDrop.switched", dropMode.ToString()); // Switched to {0} mode. + return Translations.Get("bot.autoDrop.switched", Config.Mode.ToString()); // Switched to {0} mode. } else { @@ -178,7 +170,7 @@ namespace MinecraftClient.ChatBots public override void OnInventoryUpdate(int inventoryId) { - if (enable) + if (Config.Enabled) { updateDebounce = updateDebounceValue; // Always interact container if available (larger ID) because they included player inventory (ID 0) @@ -199,28 +191,28 @@ namespace MinecraftClient.ChatBots } var inventory = GetInventories()[inventoryUpdated]; var items = inventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value); - if (dropMode == Mode.Include) + if (Config.Mode == DropMode.include) { foreach (var item in items) { // Ingore crafting result slot if (item.Key == 0) continue; - if (itemList.Contains(item.Value.Type)) + if (Config.Items.Contains(item.Value.Type)) { // Drop it !! WindowAction(inventoryUpdated, item.Key, WindowActionType.DropItemStack); } } } - else if (dropMode == Mode.Exclude) + else if (Config.Mode == DropMode.exclude) { foreach (var item in items) { // Ingore crafting result slot if (item.Key == 0) continue; - if (!itemList.Contains(item.Value.Type)) + if (!Config.Items.Contains(item.Value.Type)) { // Drop it !! WindowAction(inventoryUpdated, item.Key, WindowActionType.DropItemStack); diff --git a/MinecraftClient/ChatBots/AutoEat.cs b/MinecraftClient/ChatBots/AutoEat.cs index 2866fe4a..eb9c3762 100644 --- a/MinecraftClient/ChatBots/AutoEat.cs +++ b/MinecraftClient/ChatBots/AutoEat.cs @@ -1,19 +1,36 @@ -using MinecraftClient.Inventory; +using System; +using MinecraftClient.Inventory; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { - class AutoEat : ChatBot + public class AutoEat : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoEat"; + + public bool Enabled = false; + + public int Threshold = 6; + + public void OnSettingUpdate() + { + if (Threshold > 20) + Threshold = 20; + else if (Threshold < 0) + Threshold = 0; + } + } + byte LastSlot = 0; public static bool Eating = false; - private readonly int HungerThreshold = 6; private int DelayCounter = 0; - public AutoEat(int Threshold) - { - HungerThreshold = Threshold; - } - public override void Update() { if (DelayCounter > 0) @@ -31,7 +48,7 @@ namespace MinecraftClient.ChatBots public override void OnHealthUpdate(float health, int food) { if (health <= 0) return; // player dead - if (((food <= HungerThreshold) || (food < 20 && health < 20)) && !Eating) + if (((food <= Config.Threshold) || (food < 20 && health < 20)) && !Eating) { Eating = FindFoodAndEat(); if (!Eating) diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 4e11eada..6ff0d303 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -1,6 +1,9 @@ using System; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using Tomlet.Attributes; +using static MinecraftClient.ChatBots.AutoFishing.Configs; +using static MinecraftClient.ChatBots.AutoFishing.Configs.LocationConfig; namespace MinecraftClient.ChatBots { @@ -8,8 +11,122 @@ namespace MinecraftClient.ChatBots /// The AutoFishing bot semi-automates fishing. /// The player needs to have a fishing rod in hand, then manually send it using the UseItem command. /// - class AutoFishing : ChatBot + public class AutoFishing : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoFishing"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Antidespawn$")] + public bool Antidespawn = false; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Mainhand$")] + public bool Mainhand = true; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Auto_Start$")] + public bool Auto_Start = true; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Cast_Delay$")] + public double Cast_Delay = 0.4; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Fishing_Delay$")] + public double Fishing_Delay = 3.0; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Fishing_Timeout$")] + public double Fishing_Timeout = 300.0; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Durability_Limit$")] + public double Durability_Limit = 2; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Auto_Rod_Switch$")] + public bool Auto_Rod_Switch = true; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Stationary_Threshold$")] + public double Stationary_Threshold = 0.001; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Hook_Threshold$")] + public double Hook_Threshold = 0.2; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Log_Fish_Bobber$")] + public bool Log_Fish_Bobber = false; + + [TomlInlineComment("$config.ChatBot.AutoFishing.Movements$")] + public LocationConfig[] Movements = Array.Empty(); + + public void OnSettingUpdate() + { + if (Cast_Delay < 0) + Cast_Delay = 0; + + if (Fishing_Delay < 0) + Fishing_Delay = 0; + + if (Fishing_Timeout < 0) + Fishing_Timeout = 0; + + if (Durability_Limit < 0) + Durability_Limit = 0; + else if (Durability_Limit > 64) + Durability_Limit = 64; + + if (Stationary_Threshold < 0) + Stationary_Threshold = -Stationary_Threshold; + + if (Hook_Threshold < 0) + Hook_Threshold = -Hook_Threshold; + } + + public struct LocationConfig + { + public Coordination? XYZ; + public Facing? facing; + + public LocationConfig(float yaw, float pitch) + { + this.XYZ = null; + this.facing = new(yaw, pitch); + } + + public LocationConfig(double x, double y, double z) + { + this.XYZ = new(x, y, z); + this.facing = null; + } + + public LocationConfig(double x, double y, double z, float yaw, float pitch) + { + this.XYZ = new(x, y, z); + this.facing = new(yaw, pitch); + } + + public struct Coordination + { + public double x, y, z; + + public Coordination(double x, double y, double z) + { + this.x = x; this.y = y; this.z = z; + } + } + + public struct Facing + { + public float yaw, pitch; + + public Facing(float yaw, float pitch) + { + this.yaw = yaw; this.pitch = pitch; + } + } + } + } + private int fishCount = 0; private bool inventoryEnabled; private int castTimeout = 12; @@ -54,9 +171,9 @@ namespace MinecraftClient.ChatBots private void StartFishing() { isFishing = false; - if (Settings.AutoFishing_AutoStart) + if (Config.Auto_Start) { - double delay = Settings.AutoFishing_FishingDelay; + double delay = Config.Fishing_Delay; LogToConsole(Translations.Get("bot.autoFish.start", delay)); lock (stateLock) { @@ -84,7 +201,7 @@ namespace MinecraftClient.ChatBots private void UseFishRod() { - if (Settings.AutoFishing_Mainhand) + if (Config.Mainhand) UseItemInHand(); else UseItemInLeftHand(); @@ -100,7 +217,7 @@ namespace MinecraftClient.ChatBots break; case FishingState.WaitingToCast: if (AutoEat.Eating) - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = (int)(Config.Cast_Delay * 10); else if (--counter < 0) state = FishingState.CastingRod; break; @@ -116,28 +233,27 @@ namespace MinecraftClient.ChatBots castTimeout *= 2; // Exponential backoff LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0)); - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = (int)(Config.Cast_Delay * 10); state = FishingState.WaitingToCast; } break; case FishingState.WaitingFishToBite: - if (++counter > (int)(Settings.AutoFishing_FishingTimeout * 10)) + if (++counter > (int)(Config.Fishing_Timeout * 10)) { LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout")); - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = (int)(Config.Cast_Delay * 10); state = FishingState.WaitingToCast; } break; case FishingState.StartMove: if (--counter < 0) { - double[,]? locationList = Settings.AutoFishing_Location; - if (locationList != null) + if (Config.Movements.Length > 0) { if (GetTerrainEnabled()) { - UpdateLocation(locationList); + UpdateLocation(Config.Movements); state = FishingState.WaitingMovement; } else @@ -148,7 +264,7 @@ namespace MinecraftClient.ChatBots } else { - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = (int)(Config.Cast_Delay * 10); state = FishingState.DurabilityCheck; goto case FishingState.DurabilityCheck; } @@ -167,7 +283,7 @@ namespace MinecraftClient.ChatBots case FishingState.DurabilityCheck: if (DurabilityCheck()) { - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = (int)(Config.Cast_Delay * 10); state = FishingState.WaitingToCast; } break; @@ -181,7 +297,7 @@ namespace MinecraftClient.ChatBots { if (entity.Type == EntityType.FishingBobber && entity.ObjectData == GetPlayerEntityID()) { - if (Settings.AutoFishing_LogFishingBobber) + if (Config.Log_Fish_Bobber) LogToConsole(string.Format("FishingBobber spawn at {0}, distance = {1:0.00}", entity.Location, GetCurrentLocation().Distance(entity.Location))); LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.throw")); @@ -202,20 +318,20 @@ namespace MinecraftClient.ChatBots { if (entity != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID) { - if (Settings.AutoFishing_LogFishingBobber) + if (Config.Log_Fish_Bobber) LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location)); if (isFishing) { isFishing = false; - if (Settings.AutoFishing_Antidespawn) + if (Config.Antidespawn) { LogToConsoleTranslated("bot.autoFish.despawn"); lock (stateLock) { - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = (int)(Config.Cast_Delay * 10); state = FishingState.WaitingToCast; } } @@ -233,12 +349,12 @@ namespace MinecraftClient.ChatBots double Dz = LastPos.Z - Pos.Z; LastPos = Pos; - if (Settings.AutoFishing_LogFishingBobber) + if (Config.Log_Fish_Bobber) LogToConsole(string.Format("FishingBobber {0} Dx={1:0.000000} Dy={2:0.000000} Dz={3:0.000000}", Pos, Dx, Math.Abs(Dy), Dz)); - if (Math.Abs(Dx) < Math.Abs(Settings.AutoFishing_StationaryThreshold) && - Math.Abs(Dz) < Math.Abs(Settings.AutoFishing_StationaryThreshold) && - Math.Abs(Dy) > Math.Abs(Settings.AutoFishing_HookThreshold)) + if (Math.Abs(Dx) < Math.Abs(Config.Stationary_Threshold) && + Math.Abs(Dz) < Math.Abs(Config.Stationary_Threshold) && + Math.Abs(Dy) > Math.Abs(Config.Hook_Threshold)) { // prevent triggering multiple time if ((DateTime.Now - CaughtTime).TotalSeconds > 1) @@ -283,7 +399,7 @@ namespace MinecraftClient.ChatBots public void OnCaughtFish() { ++fishCount; - if (Settings.AutoFishing_Location != null) + if (Config.Movements.Length > 0) LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at", fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount)); else @@ -298,47 +414,36 @@ namespace MinecraftClient.ChatBots } } - private void UpdateLocation(double[,] locationList) + private void UpdateLocation(LocationConfig[] locationList) { - if (curLocationIdx >= locationList.GetLength(0)) + if (curLocationIdx >= locationList.Length) { - curLocationIdx = Math.Max(0, locationList.GetLength(0) - 2); + curLocationIdx = Math.Max(0, locationList.Length - 2); moveDir = -1; } else if (curLocationIdx < 0) { - curLocationIdx = Math.Min(locationList.GetLength(0) - 1, 1); + curLocationIdx = Math.Min(locationList.Length - 1, 1); moveDir = 1; } - int locationType = locationList.GetLength(1); + LocationConfig curConfig = locationList[curLocationIdx]; - if (locationType == 2) - { - nextYaw = (float)locationList[curLocationIdx, 0]; - nextPitch = (float)locationList[curLocationIdx, 1]; - } - else if (locationType == 3) - { - nextYaw = GetYaw(); - nextPitch = GetPitch(); - } - else if (locationType == 5) - { - nextYaw = (float)locationList[curLocationIdx, 3]; - nextPitch = (float)locationList[curLocationIdx, 4]; - } + if (curConfig.facing != null) + (nextYaw, nextPitch) = (curConfig.facing.Value.yaw, curConfig.facing.Value.pitch); + else + (nextYaw, nextPitch) = (GetYaw(), GetPitch()); - if (locationType == 3 || locationType == 5) + if (curConfig.XYZ != null) { Location current = GetCurrentLocation(); - Location goal = new(locationList[curLocationIdx, 0], locationList[curLocationIdx, 1], locationList[curLocationIdx, 2]); + Location goal = new(curConfig.XYZ.Value.x, curConfig.XYZ.Value.y, curConfig.XYZ.Value.z); bool isMoveSuccessed; if (!Movement.CheckChunkLoading(GetWorld(), current, goal)) { - LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z)); isMoveSuccessed = false; + LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z)); } else { @@ -347,8 +452,7 @@ namespace MinecraftClient.ChatBots if (!isMoveSuccessed) { - nextYaw = GetYaw(); - nextPitch = GetPitch(); + (nextYaw, nextPitch) = (GetYaw(), GetPitch()); LogToConsole(Translations.Get("cmd.move.fail", goal)); } else @@ -365,13 +469,13 @@ namespace MinecraftClient.ChatBots if (!inventoryEnabled) return true; - bool useMainHand = Settings.AutoFishing_Mainhand; + bool useMainHand = Config.Mainhand; Container container = GetPlayerInventory(); int itemSolt = useMainHand ? GetCurrentSlot() + 36 : 45; if (container.Items.TryGetValue(itemSolt, out Item? handItem) && - handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Settings.AutoFishing_DurabilityLimit) + handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Config.Durability_Limit) { isWaitingRod = false; return true; @@ -381,11 +485,11 @@ namespace MinecraftClient.ChatBots if (!isWaitingRod) LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.no_rod")); - if (Settings.AutoFishing_AutoRodSwitch) + if (Config.Auto_Rod_Switch) { foreach ((int slot, Item item) in container.Items) { - if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Settings.AutoFishing_DurabilityLimit) + if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Config.Durability_Limit) { WindowAction(0, slot, WindowActionType.LeftClick); WindowAction(0, itemSolt, WindowActionType.LeftClick); diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index b6f5a6e6..40b9c169 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -8,11 +9,59 @@ namespace MinecraftClient.ChatBots /// public class AutoRelog : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoRelog"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.AutoRelog.Delay$")] + public Range Delay = new(10); + + [TomlInlineComment("$config.ChatBot.AutoRelog.Retries$")] + public int Retries = 3; + + [TomlInlineComment("$config.ChatBot.AutoRelog.Ignore_Kick_Message$")] + public bool Ignore_Kick_Message = false; + + [TomlInlineComment("$config.ChatBot.AutoRelog.Kick_Messages_File$")] + public string Kick_Messages_File = @"kickmessages.txt"; + + public void OnSettingUpdate() + { + if (Delay.min > Delay.max) + (Delay.min, Delay.max) = (Delay.max, Delay.min); + + if (Retries == -1) + Retries = int.MaxValue; + } + + public struct Range + { + public int min, max; + + public Range(int value) + { + value = Math.Max(value, 1); + min = max = value; + } + + public Range(int min, int max) + { + min = Math.Max(min, 1); + max = Math.Max(max, 1); + this.min = min; + this.max = max; + } + } + } + private static readonly Random random = new(); private string[] dictionary = Array.Empty(); - private readonly int attempts; - private readonly int delayMin; - private readonly int delayMax; /// /// This bot automatically re-join the server if kick message contains predefined string @@ -20,34 +69,25 @@ namespace MinecraftClient.ChatBots /// Minimum delay before re-joining the server (in seconds) /// Maximum delay before re-joining the server (in seconds) /// Number of retries if connection fails (-1 = infinite) - public AutoRelog(int DelayBeforeRelogMin, int DelayBeforeRelogMax, int retries) + public AutoRelog() { - attempts = retries; - if (attempts == -1) { attempts = int.MaxValue; } - McClient.ReconnectionAttemptsLeft = attempts; - delayMin = DelayBeforeRelogMin; - delayMax = DelayBeforeRelogMax; - if (delayMin < 1) - delayMin = 1; - if (delayMax < delayMin) - delayMax = delayMin; - LogDebugToConsoleTranslated("bot.autoRelog.launch", attempts); + LogDebugToConsoleTranslated("bot.autoRelog.launch", Config.Retries); } public override void Initialize() { - McClient.ReconnectionAttemptsLeft = attempts; - if (Settings.AutoRelog_IgnoreKickMessage) + McClient.ReconnectionAttemptsLeft = Config.Retries; + if (Config.Ignore_Kick_Message) { LogDebugToConsoleTranslated("bot.autoRelog.no_kick_msg"); } else { - if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile)) + if (System.IO.File.Exists(Config.Kick_Messages_File)) { - LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile)); + LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Config.Kick_Messages_File)); - dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile, Encoding.UTF8); + dictionary = System.IO.File.ReadAllLines(Config.Kick_Messages_File, Encoding.UTF8); for (int i = 0; i < dictionary.Length; i++) { @@ -57,7 +97,7 @@ namespace MinecraftClient.ChatBots } else { - LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile)); + LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Config.Kick_Messages_File)); LogDebugToConsoleTranslated("bot.autoRelog.curr_dir", System.IO.Directory.GetCurrentDirectory()); } @@ -77,7 +117,7 @@ namespace MinecraftClient.ChatBots LogDebugToConsoleTranslated("bot.autoRelog.disconnect_msg", message); - if (Settings.AutoRelog_IgnoreKickMessage) + if (Config.Ignore_Kick_Message) { LaunchDelayedReconnection(null); return true; @@ -100,7 +140,7 @@ namespace MinecraftClient.ChatBots private void LaunchDelayedReconnection(string? msg) { - int delay = random.Next(delayMin, delayMax); + int delay = random.Next(Config.Delay.min, Config.Delay.max); LogDebugToConsoleTranslated(String.IsNullOrEmpty(msg) ? "bot.autoRelog.reconnect_always" : "bot.autoRelog.reconnect", msg); LogToConsoleTranslated("bot.autoRelog.wait", delay); System.Threading.Thread.Sleep(delay * 1000); @@ -109,9 +149,9 @@ namespace MinecraftClient.ChatBots public static bool OnDisconnectStatic(DisconnectReason reason, string message) { - if (Settings.AutoRelog_Enabled) + if (Config.Enabled) { - AutoRelog bot = new(Settings.AutoRelog_Delay_Min, Settings.AutoRelog_Delay_Max, Settings.AutoRelog_Retries); + AutoRelog bot = new(); bot.Initialize(); return bot.OnDisconnect(reason, message); } diff --git a/MinecraftClient/ChatBots/AutoRespond.cs b/MinecraftClient/ChatBots/AutoRespond.cs index 2b0738b5..d05ba350 100644 --- a/MinecraftClient/ChatBots/AutoRespond.cs +++ b/MinecraftClient/ChatBots/AutoRespond.cs @@ -1,31 +1,50 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; +using Tomlet.Attributes; +using static MinecraftClient.Settings; namespace MinecraftClient.ChatBots { /// /// This bot automatically runs actions when a user sends a message matching a specified rule /// - class AutoRespond : ChatBot + public class AutoRespond : ChatBot { - private readonly string matchesFile; - private readonly bool matchColors; + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoRespond"; + + public bool Enabled = false; + + public string Matches_File = @"matches.ini"; + + [TomlInlineComment("$config.ChatBot.AutoRespond.Match_Colors$")] + public bool Match_Colors = false; + + public void OnSettingUpdate() + { + if (!Enabled) return; + + if (!File.Exists(Matches_File)) + { + LogToConsole(BotName, Translations.TryGet("bot.autoRespond.file_not_found", Path.GetFullPath(Matches_File))); + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + } + private List? respondRules; private enum MessageType { Public, Private, Other }; - /// - /// Create a new AutoRespond bot - /// - /// INI File to load matches from - public AutoRespond(string matchesFile, bool matchColors) - { - this.matchesFile = matchesFile; - this.matchColors = matchColors; - } - /// /// Describe a respond rule based on a simple match or a regex /// @@ -96,7 +115,7 @@ namespace MinecraftClient.ChatBots string? toSend = null; - if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Bots_Owners.Contains(username.ToLower()))) + if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Config.Main.Advanced.BotOwners.Contains(username.ToLower()))) return null; switch (msgType) @@ -164,7 +183,7 @@ namespace MinecraftClient.ChatBots /// public override void Initialize() { - if (File.Exists(matchesFile)) + if (File.Exists(Config.Matches_File)) { Regex? matchRegex = null; string? matchString = null; @@ -175,9 +194,9 @@ namespace MinecraftClient.ChatBots TimeSpan cooldown = TimeSpan.Zero; respondRules = new List(); - LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(matchesFile)); + LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(Config.Matches_File)); - foreach (string lineRAW in File.ReadAllLines(matchesFile, Encoding.UTF8)) + foreach (string lineRAW in File.ReadAllLines(Config.Matches_File, Encoding.UTF8)) { string line = lineRAW.Split('#')[0].Trim(); if (line.Length > 0) @@ -211,8 +230,8 @@ namespace MinecraftClient.ChatBots case "action": matchAction = argValue; break; case "actionprivate": matchActionPrivate = argValue; break; case "actionother": matchActionOther = argValue; break; - case "ownersonly": ownersOnly = Settings.str2bool(argValue); break; - case "cooldown": cooldown = TimeSpan.FromSeconds(Settings.str2int(argValue)); break; + case "ownersonly": ownersOnly = bool.Parse(argValue); break; + case "cooldown": cooldown = TimeSpan.FromSeconds(int.Parse(argValue, NumberStyles.Any, CultureInfo.CurrentCulture)); break; } } } @@ -222,7 +241,7 @@ namespace MinecraftClient.ChatBots } else { - LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(matchesFile)); + LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(Config.Matches_File)); UnloadBot(); //No need to keep the bot active } } @@ -264,7 +283,7 @@ namespace MinecraftClient.ChatBots public override void GetText(string text) { //Remove colour codes - if (!matchColors) + if (!Config.Match_Colors) text = GetVerbatim(text); //Get Message type @@ -277,7 +296,7 @@ namespace MinecraftClient.ChatBots else message = text; //Do not process messages sent by the bot itself - if (msgType == MessageType.Other || sender != Settings.Username) + if (msgType == MessageType.Other || sender != InternalConfig.Username) { foreach (RespondRule rule in respondRules!) { diff --git a/MinecraftClient/ChatBots/ChatLog.cs b/MinecraftClient/ChatBots/ChatLog.cs index cc2e81b5..7da2f887 100644 --- a/MinecraftClient/ChatBots/ChatLog.cs +++ b/MinecraftClient/ChatBots/ChatLog.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using Tomlet.Attributes; +using static System.Net.WebRequestMethods; namespace MinecraftClient.ChatBots { @@ -9,13 +11,42 @@ namespace MinecraftClient.ChatBots public class ChatLog : ChatBot { - public enum MessageFilter { AllText, AllMessages, OnlyChat, OnlyWhispers, OnlyInternalCommands }; - private readonly bool dateandtime; - private readonly bool saveOther = true; - private readonly bool saveChat = true; - private readonly bool savePrivate = true; - private readonly bool saveInternal = true; - private readonly string logfile; + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "ChatLog"; + + public bool Enabled = false; + + public bool Add_DateTime = true; + + public string Log_File = @"chatlog-%username%-%serverip%.txt"; + + public MessageFilter Filter = MessageFilter.messages; + + public void OnSettingUpdate() + { + if (!Enabled) return; + + string Log_File_Full = Settings.Config.AppVar.ExpandVars(Log_File); + if (String.IsNullOrEmpty(Log_File_Full) || Log_File_Full.IndexOfAny(Path.GetInvalidPathChars()) >= 0) + { + LogToConsole(BotName, Translations.TryGet("bot.chatLog.invalid_file", Log_File_Full)); + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + + public enum MessageFilter { all, messages, chat, private_chat, internal_msg }; + } + + private bool saveOther = true; + private bool saveChat = true; + private bool savePrivate = true; + private bool saveInternal = true; private readonly object logfileLock = new(); /// @@ -25,59 +56,42 @@ namespace MinecraftClient.ChatBots /// The kind of messages to save /// Add a date and time before each message - public ChatLog(string file, MessageFilter filter, bool AddDateAndTime) + public ChatLog() + { + UpdateFilter(Config.Filter); + } + + public void UpdateFilter(Configs.MessageFilter filter) { - dateandtime = AddDateAndTime; - logfile = file; switch (filter) { - case MessageFilter.AllText: + case Configs.MessageFilter.all: saveOther = true; savePrivate = true; saveChat = true; break; - case MessageFilter.AllMessages: + case Configs.MessageFilter.messages: saveOther = false; savePrivate = true; saveChat = true; break; - case MessageFilter.OnlyChat: + case Configs.MessageFilter.chat: saveOther = false; savePrivate = false; saveChat = true; break; - case MessageFilter.OnlyWhispers: + case Configs.MessageFilter.private_chat: saveOther = false; savePrivate = true; saveChat = false; break; - case MessageFilter.OnlyInternalCommands: + case Configs.MessageFilter.internal_msg: saveOther = false; savePrivate = false; saveChat = false; saveInternal = true; break; } - if (String.IsNullOrEmpty(file) || file.IndexOfAny(Path.GetInvalidPathChars()) >= 0) - { - LogToConsoleTranslated("bot.chatLog.invalid_file", file); - UnloadBot(); - } - } - - public static MessageFilter str2filter(string filtername) - { - return Settings.ToLowerIfNeed(filtername) switch - { -#pragma warning disable format // @formatter:off - "all" => MessageFilter.AllText, - "messages" => MessageFilter.AllMessages, - "chat" => MessageFilter.OnlyChat, - "private" => MessageFilter.OnlyWhispers, - "internal" => MessageFilter.OnlyInternalCommands, - _ => MessageFilter.AllText, -#pragma warning restore format // @formatter:on - }; } public override void GetText(string text) @@ -110,14 +124,15 @@ namespace MinecraftClient.ChatBots private void Save(string tosave) { - if (dateandtime) + if (Config.Add_DateTime) tosave = GetTimestamp() + ' ' + tosave; + string Log_File_Full = Settings.Config.AppVar.ExpandVars(Config.Log_File); lock (logfileLock) { - string? directory = Path.GetDirectoryName(logfile); + string? directory = Path.GetDirectoryName(Log_File_Full); if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory)) Directory.CreateDirectory(directory); - FileStream stream = new(logfile, FileMode.OpenOrCreate); + FileStream stream = new(Log_File_Full, FileMode.OpenOrCreate); StreamWriter writer = new(stream); stream.Seek(0, SeekOrigin.End); writer.WriteLine(tosave); diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index e03eb253..37fbe221 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -1,23 +1,42 @@ using System; using System.Linq; using MinecraftClient.Mapping; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class FollowPlayer : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "FollowPlayer"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.FollowPlayer.Update_Limit$")] + public int Update_Limit = 10; + + [TomlInlineComment("$config.ChatBot.FollowPlayer.Stop_At_Distance$")] + public double Stop_At_Distance = 3.0; + + public void OnSettingUpdate() + { + if (Update_Limit < 0) + Update_Limit = 0; + + if (Stop_At_Distance < 0) + Stop_At_Distance = 0; + } + } + private string? _playerToFollow = null; private int _updateCounter = 0; - private readonly int _updateLimit; - private readonly int _stopAtDistance; private bool _unsafeEnabled = false; - public FollowPlayer(int updateLimit = 15, int stopAtDistance = 3) - { - _updateLimit = updateLimit; - _stopAtDistance = stopAtDistance; - } - public override void Initialize() { if (!GetEntityHandlingEnabled()) @@ -94,7 +113,7 @@ namespace MinecraftClient.ChatBots public override void OnEntityMove(Entity entity) { - if (_updateCounter < _updateLimit) + if (_updateCounter < Config.Update_Limit) return; _updateCounter = 0; @@ -114,7 +133,7 @@ namespace MinecraftClient.ChatBots // Stop at specified distance from plater (prevents pushing player around) double distance = entity.Location.Distance(GetCurrentLocation()); - if (distance < _stopAtDistance) + if (distance < Config.Stop_At_Distance) return; MoveToLocation(entity.Location, _unsafeEnabled); diff --git a/MinecraftClient/ChatBots/HangmanGame.cs b/MinecraftClient/ChatBots/HangmanGame.cs index c41324af..281ed68e 100644 --- a/MinecraftClient/ChatBots/HangmanGame.cs +++ b/MinecraftClient/ChatBots/HangmanGame.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -9,26 +10,33 @@ namespace MinecraftClient.ChatBots public class HangmanGame : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "HangmanGame"; + + public bool Enabled = false; + + public bool English = true; + + public string FileWords_EN = "hangman-en.txt"; + + public string FileWords_FR = "hangman-fr.txt"; + + public void OnSettingUpdate() { } + } + private int vie = 0; private readonly int vie_param = 10; private int compteur = 0; private readonly int compteur_param = 3000; //5 minutes private bool running = false; - private bool[] discovered; + private bool[] discovered = Array.Empty(); private string word = ""; private string letters = ""; - private readonly bool English; - - /// - /// Le jeu du Pendu / Hangman Game - /// - /// if true, the game will be in english. If false, the game will be in french. - - public HangmanGame(bool english) - { - English = english; - discovered = Array.Empty(); - } public override void Update() { @@ -40,8 +48,8 @@ namespace MinecraftClient.ChatBots } else { - SendText(English ? "You took too long to try a letter." : "Temps imparti écoulé !"); - SendText(English ? "Game canceled." : "Partie annulée."); + SendText(Config.English ? "You took too long to try a letter." : "Temps imparti écoulé !"); + SendText(Config.English ? "Game canceled." : "Partie annulée."); running = false; } } @@ -55,7 +63,7 @@ namespace MinecraftClient.ChatBots if (IsPrivateMessage(text, ref message, ref username)) { - if (Settings.Bots_Owners.Contains(username.ToLower())) + if (Settings.Config.Main.Advanced.BotOwners.Contains(username.ToLower())) { switch (message) { @@ -81,7 +89,7 @@ namespace MinecraftClient.ChatBots { if (letters.Contains(letter)) { - SendText(English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé.")); + SendText(Config.English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé.")); } else { @@ -91,29 +99,29 @@ namespace MinecraftClient.ChatBots if (word.Contains(letter)) { for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { discovered[i] = true; } } - SendText(English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)")); + SendText(Config.English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)")); } else { vie--; if (vie == 0) { - SendText(English ? "Game Over! :]" : "Perdu ! Partie terminée :]"); - SendText(English ? ("The word was: " + word) : ("Le mot était : " + word)); + SendText(Config.English ? "Game Over! :]" : "Perdu ! Partie terminée :]"); + SendText(Config.English ? ("The word was: " + word) : ("Le mot était : " + word)); running = false; } - else SendText(English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non.")); + else SendText(Config.English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non.")); } if (running) { - SendText(English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")") + SendText(Config.English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")") : ("Mot mystère : " + WordCached + " (vie : " + vie + ")")); } if (Winner) { - SendText(English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !")); + SendText(Config.English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !")); running = false; } } @@ -132,23 +140,23 @@ namespace MinecraftClient.ChatBots compteur = compteur_param; discovered = new bool[word.Length]; - SendText(English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio"); - SendText(English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")") + SendText(Config.English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio"); + SendText(Config.English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")") : ("Mot mystère : " + WordCached + " (vie : " + vie + ")")); - SendText(English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)")); + SendText(Config.English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)")); } private string Chooseword() { - if (System.IO.File.Exists(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR)) + if (System.IO.File.Exists(Config.English ? Config.FileWords_EN : Config.FileWords_FR)) { - string[] dico = System.IO.File.ReadAllLines(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR, Encoding.UTF8); + string[] dico = System.IO.File.ReadAllLines(Config.English ? Config.FileWords_EN : Config.FileWords_FR, Encoding.UTF8); return dico[new Random().Next(dico.Length)]; } else { - LogToConsole(English ? "File not found: " + Settings.Hangman_FileWords_EN : "Fichier introuvable : " + Settings.Hangman_FileWords_FR); - return English ? "WORDSAREMISSING" : "DICOMANQUANT"; + LogToConsole(Config.English ? "File not found: " + Config.FileWords_EN : "Fichier introuvable : " + Config.FileWords_FR); + return Config.English ? "WORDSAREMISSING" : "DICOMANQUANT"; } } diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index 8752d1fd..a596b9f4 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -11,6 +12,60 @@ namespace MinecraftClient.ChatBots /// public class Mailer : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "Mailer"; + + public bool Enabled = false; + + public string DatabaseFile = "MailerDatabase.ini"; + + public string IgnoreListFile = "MailerIgnoreList.ini"; + + public bool PublicInteractions = false; + + public int MaxMailsPerPlayer = 10; + + public int MaxDatabaseSize = 10000; + + public int MailRetentionDays = 30; + + public void OnSettingUpdate() + { + if (!Enabled) return; + + bool checkSuccessed = true; + + if (Config.MaxDatabaseSize <= 0) + { + LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.db_size")); + checkSuccessed = false; + } + + if (Config.MaxMailsPerPlayer <= 0) + { + LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.max_mails")); + checkSuccessed = false; + } + + if (Config.MailRetentionDays <= 0) + { + LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.mail_retention")); + checkSuccessed = false; + } + + if (!checkSuccessed) + { + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + } + /// /// Holds the list of ignored players /// @@ -161,58 +216,37 @@ namespace MinecraftClient.ChatBots public override void Initialize() { LogDebugToConsoleTranslated("bot.mailer.init"); - LogDebugToConsoleTranslated("bot.mailer.init.db" + Settings.Mailer_DatabaseFile); - LogDebugToConsoleTranslated("bot.mailer.init.ignore" + Settings.Mailer_IgnoreListFile); - LogDebugToConsoleTranslated("bot.mailer.init.public" + Settings.Mailer_PublicInteractions); - LogDebugToConsoleTranslated("bot.mailer.init.max_mails" + Settings.Mailer_MaxMailsPerPlayer); - LogDebugToConsoleTranslated("bot.mailer.init.db_size" + Settings.Mailer_MaxDatabaseSize); - LogDebugToConsoleTranslated("bot.mailer.init.mail_retention" + Settings.Mailer_MailRetentionDays + " days"); + LogDebugToConsoleTranslated("bot.mailer.init.db" + Config.DatabaseFile); + LogDebugToConsoleTranslated("bot.mailer.init.ignore" + Config.IgnoreListFile); + LogDebugToConsoleTranslated("bot.mailer.init.public" + Config.PublicInteractions); + LogDebugToConsoleTranslated("bot.mailer.init.max_mails" + Config.MaxMailsPerPlayer); + LogDebugToConsoleTranslated("bot.mailer.init.db_size" + Config.MaxDatabaseSize); + LogDebugToConsoleTranslated("bot.mailer.init.mail_retention" + Config.MailRetentionDays + " days"); - if (Settings.Mailer_MaxDatabaseSize <= 0) + if (!File.Exists(Config.DatabaseFile)) { - LogToConsoleTranslated("bot.mailer.init_fail.db_size"); - UnloadBot(); - return; + LogToConsoleTranslated("bot.mailer.create.db", Path.GetFullPath(Config.DatabaseFile)); + new MailDatabase().SaveToFile(Config.DatabaseFile); } - if (Settings.Mailer_MaxMailsPerPlayer <= 0) + if (!File.Exists(Config.IgnoreListFile)) { - LogToConsoleTranslated("bot.mailer.init_fail.max_mails"); - UnloadBot(); - return; - } - - if (Settings.Mailer_MailRetentionDays <= 0) - { - LogToConsoleTranslated("bot.mailer.init_fail.mail_retention"); - UnloadBot(); - return; - } - - if (!File.Exists(Settings.Mailer_DatabaseFile)) - { - LogToConsoleTranslated("bot.mailer.create.db", Path.GetFullPath(Settings.Mailer_DatabaseFile)); - new MailDatabase().SaveToFile(Settings.Mailer_DatabaseFile); - } - - if (!File.Exists(Settings.Mailer_IgnoreListFile)) - { - LogToConsoleTranslated("bot.mailer.create.ignore", Path.GetFullPath(Settings.Mailer_IgnoreListFile)); - new IgnoreList().SaveToFile(Settings.Mailer_IgnoreListFile); + LogToConsoleTranslated("bot.mailer.create.ignore", Path.GetFullPath(Config.IgnoreListFile)); + new IgnoreList().SaveToFile(Config.IgnoreListFile); } lock (readWriteLock) { - LogDebugToConsoleTranslated("bot.mailer.load.db", Path.GetFullPath(Settings.Mailer_DatabaseFile)); - mailDatabase = MailDatabase.FromFile(Settings.Mailer_DatabaseFile); + LogDebugToConsoleTranslated("bot.mailer.load.db", Path.GetFullPath(Config.DatabaseFile)); + mailDatabase = MailDatabase.FromFile(Config.DatabaseFile); - LogDebugToConsoleTranslated("bot.mailer.load.ignore", Path.GetFullPath(Settings.Mailer_IgnoreListFile)); - ignoreList = IgnoreList.FromFile(Settings.Mailer_IgnoreListFile); + LogDebugToConsoleTranslated("bot.mailer.load.ignore", Path.GetFullPath(Config.IgnoreListFile)); + ignoreList = IgnoreList.FromFile(Config.IgnoreListFile); } //Initialize file monitors. In case the bot needs to unload for some reason in the future, do not forget to .Dispose() them - mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Settings.Mailer_DatabaseFile)!, Path.GetFileName(Settings.Mailer_DatabaseFile), FileMonitorCallback); - ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Settings.Mailer_IgnoreListFile)!, Path.GetFileName(Settings.Mailer_IgnoreListFile), FileMonitorCallback); + mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.DatabaseFile)!, Path.GetFileName(Config.DatabaseFile), FileMonitorCallback); + ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.IgnoreListFile)!, Path.GetFileName(Config.IgnoreListFile), FileMonitorCallback); RegisterChatBotCommand("mailer", Translations.Get("bot.mailer.cmd"), "mailer ", ProcessInternalCommand); } @@ -224,7 +258,7 @@ namespace MinecraftClient.ChatBots { maxMessageLength = GetMaxChatMessageLength() - 44 // Deduct length of "/ 16CharPlayerName 16CharPlayerName mailed: " - - Settings.PrivateMsgsCmdName.Length; // Deduct length of "tell" command + - Settings.Config.Main.Advanced.PrivateMsgsCmdName.Length; // Deduct length of "tell" command } /// @@ -236,7 +270,7 @@ namespace MinecraftClient.ChatBots string username = ""; text = GetVerbatim(text); - if (IsPrivateMessage(text, ref message, ref username) || (Settings.Mailer_PublicInteractions && IsChatMessage(text, ref message, ref username))) + if (IsPrivateMessage(text, ref message, ref username) || (Config.PublicInteractions && IsChatMessage(text, ref message, ref username))) { string usernameLower = username.ToLower(); if (!ignoreList.Contains(usernameLower)) @@ -247,8 +281,8 @@ namespace MinecraftClient.ChatBots case "mail": case "tellonym": if (usernameLower != GetUsername().ToLower() - && mailDatabase.Count < Settings.Mailer_MaxDatabaseSize - && mailDatabase.Where(mail => mail.SenderLowercase == usernameLower).Count() < Settings.Mailer_MaxMailsPerPlayer) + && mailDatabase.Count < Config.MaxDatabaseSize + && mailDatabase.Where(mail => mail.SenderLowercase == usernameLower).Count() < Config.MaxMailsPerPlayer) { Queue args = new(Command.GetArgs(message)); if (args.Count >= 2) @@ -266,7 +300,7 @@ namespace MinecraftClient.ChatBots lock (readWriteLock) { mailDatabase.Add(mail); - mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile); + mailDatabase.SaveToFile(Config.DatabaseFile); } SendPrivateMessage(username, "Message saved!"); } @@ -307,8 +341,8 @@ namespace MinecraftClient.ChatBots lock (readWriteLock) { mailDatabase.RemoveAll(mail => mail.Delivered); - mailDatabase.RemoveAll(mail => mail.DateSent.AddDays(Settings.Mailer_MailRetentionDays) < DateTime.Now); - mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile); + mailDatabase.RemoveAll(mail => mail.DateSent.AddDays(Config.MailRetentionDays) < DateTime.Now); + mailDatabase.SaveToFile(Config.DatabaseFile); } nextMailSend = dateNow.AddSeconds(10); @@ -324,8 +358,8 @@ namespace MinecraftClient.ChatBots { lock (readWriteLock) { - mailDatabase = MailDatabase.FromFile(Settings.Mailer_DatabaseFile); - ignoreList = IgnoreList.FromFile(Settings.Mailer_IgnoreListFile); + mailDatabase = MailDatabase.FromFile(Config.DatabaseFile); + ignoreList = IgnoreList.FromFile(Config.IgnoreListFile); } } @@ -357,7 +391,7 @@ namespace MinecraftClient.ChatBots if (!ignoreList.Contains(username)) { ignoreList.Add(username); - ignoreList.SaveToFile(Settings.Mailer_IgnoreListFile); + ignoreList.SaveToFile(Config.IgnoreListFile); } } return Translations.Get("bot.mailer.cmd.ignore.added", args[1]); @@ -369,7 +403,7 @@ namespace MinecraftClient.ChatBots if (ignoreList.Contains(username)) { ignoreList.Remove(username); - ignoreList.SaveToFile(Settings.Mailer_IgnoreListFile); + ignoreList.SaveToFile(Config.IgnoreListFile); } } return Translations.Get("bot.mailer.cmd.ignore.removed", args[1]); diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index a03b3a72..83fc3dc1 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -5,41 +5,59 @@ using System.Globalization; using System.IO; using MinecraftClient.Mapping; using MinecraftClient.Protocol.Handlers; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { - class Map : ChatBot + public class Map : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "Map"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.Map.Should_Resize$")] + public bool Should_Resize = false; + + [TomlInlineComment("$config.ChatBot.Map.Resize_To$")] + public int Resize_To = 256; + + [TomlInlineComment("$config.ChatBot.Map.Auto_Render_On_Update$")] + public bool Auto_Render_On_Update = false; + + [TomlInlineComment("$config.ChatBot.Map.Delete_All_On_Unload$")] + public bool Delete_All_On_Unload = true; + + [TomlInlineComment("$config.ChatBot.Map.Notify_On_First_Update$")] + public bool Notify_On_First_Update = true; + + public void OnSettingUpdate() + { + if (Resize_To < 128) + Resize_To = 128; + } + } + private readonly string baseDirectory = @"Rendered_Maps"; private readonly Dictionary cachedMaps = new(); - private bool shouldResize = true; - private int resizeTo = 256; - private bool autoRenderOnUpdate = true; - private bool deleteAllOnExit = true; - private bool notifyOnFirstUpdate = true; public override void Initialize() { if (!Directory.Exists(baseDirectory)) Directory.CreateDirectory(baseDirectory); - shouldResize = Settings.Map_Should_Resize; - resizeTo = Settings.Map_Resize_To; - - if (resizeTo < 128) - resizeTo = 128; - - autoRenderOnUpdate = Settings.Map_Auto_Render_On_Update; - deleteAllOnExit = Settings.Map_Delete_All_On_Unload; - notifyOnFirstUpdate = Settings.Map_Notify_On_First_Update; - RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render or maps l|r ", OnMapCommand); } public override void OnUnload() { - if (deleteAllOnExit) + if (Config.Delete_All_On_Unload) { DirectoryInfo di = new(baseDirectory); FileInfo[] files = di.GetFiles(); @@ -124,7 +142,7 @@ namespace MinecraftClient.ChatBots { cachedMaps.Add(mapid, map); - if (notifyOnFirstUpdate) + if (Config.Notify_On_First_Update) LogToConsoleTranslated("bot.map.received_map", map.MapId); } else @@ -133,7 +151,7 @@ namespace MinecraftClient.ChatBots cachedMaps.Add(mapid, map); } - if (autoRenderOnUpdate) + if (Config.Auto_Render_On_Update) GenerateMapImage(map); } @@ -165,8 +183,8 @@ namespace MinecraftClient.ChatBots // Resize, double the image - if (shouldResize) - image = ResizeBitmap(image, resizeTo, resizeTo); + if (Config.Should_Resize) + image = ResizeBitmap(image, Config.Resize_To, Config.Resize_To); image.Save(fileName); LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName)); diff --git a/MinecraftClient/ChatBots/PlayerListLogger.cs b/MinecraftClient/ChatBots/PlayerListLogger.cs index b15d5f92..a8c6e333 100644 --- a/MinecraftClient/ChatBots/PlayerListLogger.cs +++ b/MinecraftClient/ChatBots/PlayerListLogger.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Text; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -10,28 +11,34 @@ namespace MinecraftClient.ChatBots public class PlayerListLogger : ChatBot { - private int count; - private readonly int timeping; - private readonly string file; + public static Configs Config = new(); - /// - /// This bot sends a /list command every X seconds and save the result. - /// - /// Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.) - - public PlayerListLogger(int pingparam, string filetosavein) + [TomlDoNotInlineObject] + public class Configs { - count = 0; - file = filetosavein; - timeping = pingparam; - if (timeping < 10) { timeping = 10; } //To avoid flooding + [NonSerialized] + private const string BotName = "PlayerListLogger"; + public bool Enabled = false; + + public string File = "playerlog.txt"; + + [TomlInlineComment("$config.ChatBot.PlayerListLogger.Delay$")] + public int Delay = 600; + + public void OnSettingUpdate() + { + if (Delay < 10) + Delay = 10; + } } + private int count = 0; + public override void Update() { count++; - if (count == timeping) + if (count == Config.Delay) { DateTime now = DateTime.Now; @@ -40,7 +47,7 @@ namespace MinecraftClient.ChatBots StringBuilder sb = new(); sb.AppendLine(string.Format("[{0}/{1}/{2} {3}:{4}]", now.Year, now.Month, now.Day, now.Hour, now.Minute)); sb.AppendLine(string.Join(", ", GetOnlinePlayers())).AppendLine(); - System.IO.File.AppendAllText(file, sb.ToString()); + System.IO.File.AppendAllText(Settings.Config.AppVar.ExpandVars(Config.File), sb.ToString()); count = 0; } diff --git a/MinecraftClient/ChatBots/RemoteControl.cs b/MinecraftClient/ChatBots/RemoteControl.cs index 3baae1a1..ac1d544e 100644 --- a/MinecraftClient/ChatBots/RemoteControl.cs +++ b/MinecraftClient/ChatBots/RemoteControl.cs @@ -1,4 +1,5 @@ using System; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -8,11 +9,28 @@ namespace MinecraftClient.ChatBots public class RemoteControl : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "RemoteControl"; + + public bool Enabled = false; + + public bool AutoTpaccept = true; + + public bool AutoTpaccept_Everyone = false; + + public void OnSettingUpdate() { } + } + public override void GetText(string text) { text = GetVerbatim(text).Trim(); string command = "", sender = ""; - if (IsPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim())) + if (IsPrivateMessage(text, ref command, ref sender) && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim())) { string? response = ""; PerformInternalCommand(command, ref response); @@ -26,9 +44,9 @@ namespace MinecraftClient.ChatBots SendPrivateMessage(sender, response); } } - else if (Settings.RemoteCtrl_AutoTpaccept + else if (Config.AutoTpaccept && IsTeleportRequest(text, ref sender) - && (Settings.RemoteCtrl_AutoTpaccept_Everyone || Settings.Bots_Owners.Contains(sender.ToLower().Trim()))) + && (Config.AutoTpaccept_Everyone || Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim()))) { SendText("/tpaccept"); } diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index 25ea1557..4232e8a0 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using MinecraftClient.Protocol; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -9,24 +10,35 @@ namespace MinecraftClient.ChatBots /// public class ReplayCapture : ChatBot { - private ReplayHandler? replay; - private readonly int backupInterval = 3000; // Unit: second * 10 - private int backupCounter = -1; + public static Configs Config = new(); - public ReplayCapture(int backupInterval) + [TomlDoNotInlineObject] + public class Configs { - if (backupInterval != -1) - this.backupInterval = backupInterval * 10; - else - this.backupInterval = -1; + [NonSerialized] + private const string BotName = "ReplayCapture"; + + public bool Enabled = false; + + [TomlInlineComment("$config.ChatBot.ReplayCapture.Backup_Interval$")] + public int Backup_Interval = 3000; + + public void OnSettingUpdate() + { + if (Backup_Interval < -1) + Backup_Interval = -1; + } } + private ReplayHandler? replay; + private int backupCounter = -1; + public override void Initialize() { SetNetworkPacketEventEnabled(true); replay = new ReplayHandler(GetProtocolVersion()); replay.MetaData.serverName = GetServerHost() + GetServerPort(); - backupCounter = backupInterval; + backupCounter = Config.Backup_Interval * 10; RegisterChatBotCommand("replay", Translations.Get("bot.replayCapture.cmd"), "replay ", Command); } @@ -38,12 +50,12 @@ namespace MinecraftClient.ChatBots public override void Update() { - if (backupInterval > 0 && replay!.RecordRunning) + if (Config.Backup_Interval > 0 && replay!.RecordRunning) { if (backupCounter <= 0) { replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr"); - backupCounter = backupInterval; + backupCounter = Config.Backup_Interval * 10; } else backupCounter--; } diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 84ee6483..3fc1acaf 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -107,7 +107,7 @@ namespace MinecraftClient.ChatBots } } - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { string caller = "Script"; try @@ -195,7 +195,7 @@ namespace MinecraftClient.ChatBots { if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/') { - instruction_line = Settings.ExpandVars(instruction_line, localVars); + instruction_line = Settings.Config.AppVar.ExpandVars(instruction_line, localVars); string instruction_name = instruction_line.Split(' ')[0]; switch (instruction_name.ToLower()) { diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index 8fe8af82..bd5296ad 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Text; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -11,6 +13,32 @@ namespace MinecraftClient.ChatBots public class ScriptScheduler : ChatBot { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "ScriptScheduler"; + + public bool Enabled = false; + + public string Tasks_File = @"tasks.ini"; + + public void OnSettingUpdate() + { + if (!Enabled) return; + + string Tasks_File_Full = Settings.Config.AppVar.ExpandVars(Tasks_File); + if (!File.Exists(Tasks_File_Full)) + { + LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.not_found", Path.GetFullPath(Tasks_File_Full))); + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + } + private class TaskDesc { public string? action = null; @@ -27,26 +55,20 @@ namespace MinecraftClient.ChatBots private static bool firstlogin_done = false; - private readonly string tasksfile; - private bool serverlogin_done; + private bool serverlogin_done = false; private readonly List tasks = new(); private int verifytasks_timeleft = 10; private readonly int verifytasks_delay = 10; - public ScriptScheduler(string tasksfile) - { - this.tasksfile = tasksfile; - serverlogin_done = false; - } - public override void Initialize() { //Load the given file from the startup parameters - if (System.IO.File.Exists(tasksfile)) + string Tasks_File_Full = Settings.Config.AppVar.ExpandVars(Config.Tasks_File); + if (File.Exists(Tasks_File_Full)) { - LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(tasksfile)); + LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(Tasks_File_Full)); TaskDesc? current_task = null; - string[] lines = System.IO.File.ReadAllLines(tasksfile, Encoding.UTF8); + string[] lines = System.IO.File.ReadAllLines(Tasks_File_Full, Encoding.UTF8); foreach (string lineRAW in lines) { string line = lineRAW.Split('#')[0].Trim(); @@ -70,10 +92,10 @@ namespace MinecraftClient.ChatBots string argValue = line[(argName.Length + 1)..]; switch (argName.ToLower()) { - case "triggeronfirstlogin": current_task.triggerOnFirstLogin = Settings.str2bool(argValue); break; - case "triggeronlogin": current_task.triggerOnLogin = Settings.str2bool(argValue); break; - case "triggerontime": current_task.triggerOnTime = Settings.str2bool(argValue); break; - case "triggeroninterval": current_task.triggerOnInterval = Settings.str2bool(argValue); break; + case "triggeronfirstlogin": current_task.triggerOnFirstLogin = bool.Parse(argValue); break; + case "triggeronlogin": current_task.triggerOnLogin = bool.Parse(argValue); break; + case "triggerontime": current_task.triggerOnTime = bool.Parse(argValue); break; + case "triggeroninterval": current_task.triggerOnInterval = bool.Parse(argValue); break; case "timevalue": try { current_task.triggerOnTime_Times.Add(DateTime.ParseExact(argValue, "HH:mm", CultureInfo.InvariantCulture)); } catch { } break; case "timeinterval": int interval; @@ -112,7 +134,7 @@ namespace MinecraftClient.ChatBots } else { - LogToConsoleTranslated("bot.scriptScheduler.not_found", System.IO.Path.GetFullPath(tasksfile)); + LogToConsoleTranslated("bot.scriptScheduler.not_found", Path.GetFullPath(Tasks_File_Full)); UnloadBot(); //No need to keep the bot active } } diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index ee1b8c7b..5e793152 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -138,7 +138,7 @@ namespace MinecraftClient.Commands // \ud83d\udd33: 🔳, \ud83d\udfe8: 🟨, \ud83d\udfe9: 🟩, \u25A1: □, \u25A3: ▣, \u25A0: ■ - string[] chunkStatusStr = Settings.EnableEmoji ? + string[] chunkStatusStr = Settings.Config.Main.Advanced.EnableEmoji ? new string[] { "\ud83d\udd33", "\ud83d\udfe8", "\ud83d\udfe9" } : new string[] { "\u25A1", "\u25A3", "\u25A0" }; // Output diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs index ca95c81a..12c94a3f 100644 --- a/MinecraftClient/Commands/Connect.cs +++ b/MinecraftClient/Commands/Connect.cs @@ -15,13 +15,13 @@ namespace MinecraftClient.Commands string[] args = GetArgs(command); if (args.Length > 1) { - if (!Settings.SetAccount(args[1])) + if (!Settings.Config.Main.Advanced.SetAccount(args[1])) { return Translations.Get("cmd.connect.unknown", args[1]); } } - if (Settings.SetServerIP(args[0])) + if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(args[0]), true)) { Program.Restart(); return ""; diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index c08404e0..c2bbda8a 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -11,11 +11,10 @@ namespace MinecraftClient.Commands public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) - { - Settings.DebugMessages = (GetArg(command).ToLower() == "on"); - } - else Settings.DebugMessages = !Settings.DebugMessages; - return Translations.Get(Settings.DebugMessages ? "cmd.debug.state_on" : "cmd.debug.state_off"); + Settings.Config.Logging.DebugMessages = (GetArg(command).ToLower() == "on"); + else + Settings.Config.Logging.DebugMessages = !Settings.Config.Logging.DebugMessages; + return Translations.Get(Settings.Config.Logging.DebugMessages ? "cmd.debug.state_on" : "cmd.debug.state_off"); } } } diff --git a/MinecraftClient/Commands/ExecIf.cs b/MinecraftClient/Commands/ExecIf.cs index 474c1c50..06b8619f 100644 --- a/MinecraftClient/Commands/ExecIf.cs +++ b/MinecraftClient/Commands/ExecIf.cs @@ -36,7 +36,7 @@ namespace MinecraftClient.Commands var interpreter = new Interpreter(); interpreter.SetVariable("MCC", handler); - foreach (KeyValuePair entry in Settings.GetVariables()) + foreach (KeyValuePair entry in Settings.Config.AppVar.GetVariables()) interpreter.SetVariable(entry.Key, entry.Value); var result = interpreter.Eval(expressionText); diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index c01e9964..11c214bf 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -185,7 +185,7 @@ namespace MinecraftClient.Commands response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title)); string? asciiArt = inventory.Type.GetAsciiArt(); - if (asciiArt != null && Settings.DisplayInventoryLayout) + if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout) response.AppendLine(asciiArt); int selectedHotbar = handler.GetCurrentSlot() + 1; diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index 6bc6e1fb..bbebcded 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -45,8 +45,8 @@ namespace MinecraftClient.Commands else if (args[0] == "gravity") { if (args.Count >= 2) - Settings.GravityEnabled = (args[1] == "on"); - if (Settings.GravityEnabled) + Settings.InternalConfig.GravityEnabled = (args[1] == "on"); + if (Settings.InternalConfig.GravityEnabled) return Translations.Get("cmd.move.gravity.enabled"); else return Translations.Get("cmd.move.gravity.disabled"); } diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs index 46beebbb..5b5b0e0f 100644 --- a/MinecraftClient/Commands/Reco.cs +++ b/MinecraftClient/Commands/Reco.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands string[] args = GetArgs(command); if (args.Length > 0) { - if (!Settings.SetAccount(args[0])) + if (!Settings.Config.Main.Advanced.SetAccount(args[0])) { return Translations.Get("cmd.connect.unknown", args[0]); } diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs index 9f6ccd78..9f8cfe5d 100644 --- a/MinecraftClient/Commands/Set.cs +++ b/MinecraftClient/Commands/Set.cs @@ -15,15 +15,16 @@ namespace MinecraftClient.Commands string[] temp = GetArg(command).Split('='); if (temp.Length > 1) { - if (Settings.SetVar(temp[0], GetArg(command).Substring(temp[0].Length + 1))) - { + if (Settings.Config.AppVar.SetVar(temp[0], GetArg(command).Substring(temp[0].Length + 1))) return ""; //Success - } - else return Translations.Get("cmd.set.format"); + else + return Translations.Get("cmd.set.format"); } - else return GetCmdDescTranslated(); + else + return GetCmdDescTranslated(); } - else return GetCmdDescTranslated(); + else + return GetCmdDescTranslated(); } } } diff --git a/MinecraftClient/Commands/SetRnd.cs b/MinecraftClient/Commands/SetRnd.cs index 6ee7bfb8..6e0d00b2 100644 --- a/MinecraftClient/Commands/SetRnd.cs +++ b/MinecraftClient/Commands/SetRnd.cs @@ -40,9 +40,9 @@ namespace MinecraftClient.Commands (num2, num1) = (num1, num2); // create a variable or set it to num1 <= varlue < num2 - if (Settings.SetVar(args[0], rand.Next(num1, num2))) + if (Settings.Config.AppVar.SetVar(args[0], rand.Next(num1, num2))) { - return string.Format("Set %{0}% to {1}.", args[0], Settings.GetVar(args[0])); //Success + return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success } else return Translations.Get("cmd.setrndnum.format"); } @@ -55,9 +55,9 @@ namespace MinecraftClient.Commands List values = ParseCommandLine(argString); // create a variable or set it to one of the values - if (values.Count > 0 && Settings.SetVar(args[0], values[rand.Next(0, values.Count)])) + if (values.Count > 0 && Settings.Config.AppVar.SetVar(args[0], values[rand.Next(0, values.Count)])) { - return string.Format("Set %{0}% to {1}.", args[0], Settings.GetVar(args[0])); //Success + return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success } else return Translations.Get("cmd.setrndstr.format"); } diff --git a/MinecraftClient/DefaultConfigResource.Designer.cs b/MinecraftClient/DefaultConfigResource.Designer.cs index d3dfe630..6fa58c16 100644 --- a/MinecraftClient/DefaultConfigResource.Designer.cs +++ b/MinecraftClient/DefaultConfigResource.Designer.cs @@ -8,11 +8,10 @@ // //------------------------------------------------------------------------------ -namespace MinecraftClient -{ +namespace MinecraftClient { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -20,55 +19,47 @@ namespace MinecraftClient // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class DefaultConfigResource - { - + public class DefaultConfigResource { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal DefaultConfigResource() - { + internal DefaultConfigResource() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager - { - get - { - if (object.ReferenceEquals(resourceMan, null)) - { + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.DefaultConfigResource", typeof(DefaultConfigResource).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture - { - get - { + public static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ ///║ Brewing Stand ║ @@ -82,16 +73,14 @@ namespace MinecraftClient ///║ Inventory ╚═══╝ ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║ 5 ║ 6 ║ 7 ║ 8 ║ 9 ║10 ║11 ║12 ║13 ║║ - ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. + ///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. /// - public static string ContainerType_BrewingStand - { - get - { + public static string ContainerType_BrewingStand { + get { return ResourceManager.GetString("ContainerType_BrewingStand", resourceCulture); } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ ///║ Crafting ║ @@ -105,24 +94,35 @@ namespace MinecraftClient ///║ Inventory ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║10 ║11 ║12 ║13 ║14 ║15 ║16 ║17 ║18 ║║ - ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. + ///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. /// - public static string ContainerType_Crafting - { - get - { + public static string ContainerType_Crafting { + get { return ResourceManager.GetString("ContainerType_Crafting", resourceCulture); } } - - public static string ContainerType_Furnace - { - get - { + + /// + /// Looks up a localized string similar to ╔═════════════════════════════════════╗ + ///║ Container ║ + ///║ ╔═══╗ ║ + ///║ ║ 0 ║ ║ + ///║ ╚═══╝ ╔═══╗ ║ + ///║ ⠂⡠⢂ ━━▶ ║ 2 ║ ║ + ///║ ⠂⡠⢂ ╚═══╝ ║ + ///║ ╔═══╗ ║ + ///║ ║ 1 ║ ║ + ///║ ╚═══╝ ║ + ///║ Inventory ║ + ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ + ///║║ 3 ║ 4 ║ 5 ║ 6 ║ 7 [rest of string was truncated]";. + /// + public static string ContainerType_Furnace { + get { return ResourceManager.GetString("ContainerType_Furnace", resourceCulture); } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ ///║ Container ║ @@ -136,19 +136,17 @@ namespace MinecraftClient ///║ Inventory ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║ 9 ║10 ║11 ║12 ║13 ║14 ║15 ║16 ║17 ║║ - ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. + ///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. /// - public static string ContainerType_Generic_3x3 - { - get - { + public static string ContainerType_Generic_3x3 { + get { return ResourceManager.GetString("ContainerType_Generic_3x3", resourceCulture); } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ - ///║ Chest ║ + ///║ Container ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ 7 ║ 8 ║║ ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║ @@ -159,19 +157,17 @@ namespace MinecraftClient ///║ Inventory ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║27 ║28 ║29 ║30 ║31 ║32 ║33 ║34 ║35 ║║ - ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. + ///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. /// - public static string ContainerType_Generic_9x3 - { - get - { + public static string ContainerType_Generic_9x3 { + get { return ResourceManager.GetString("ContainerType_Generic_9x3", resourceCulture); } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ - ///║ Large Container ║ + ///║ Container ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ 7 ║ 8 ║║ ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║ @@ -182,32 +178,56 @@ namespace MinecraftClient ///║║27 ║28 ║29 ║30 ║31 ║32 ║33 ║34 ║35 ║║ ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║ ///║║36 ║37 ║38 ║39 ║40 ║41 ║42 ║43 ║44 ║║ - ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. + ///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. /// - public static string ContainerType_Generic_9x6 - { - get - { + public static string ContainerType_Generic_9x6 { + get { return ResourceManager.GetString("ContainerType_Generic_9x6", resourceCulture); } } - - public static string ContainerType_Grindstone - { - get - { + + /// + /// Looks up a localized string similar to ╔═════════════════════════════════════╗ + ///║ Repair & Disenchant ║ + ///║ ╔═════════╗ ║ + ///║ ║ ╔═══╗ ║ ║ + ///║ ╔══║ ║ 0 ║ ║══╗ ║ + ///║ ║ ║ ╚═══╝ ║ ║ ║ + ///║ ║ ║ ╔═══╗ ║ ║ ╔═══╗ ║ + ///║ ║ ║ ║ 1 ║ ║ ║ ━━▶ ║ 2 ║ ║ + ///║ ║ ║ ╚═══╝ ║ ║ ╚═══╝ ║ + ///║ ║ ╠═════════╣ ║ ║ + ///║ ║ ║ ║ ║ ║ + ///║ ╚══╝ ╚══╝ ║ + ///║ [rest of string was truncated]";. + /// + public static string ContainerType_Grindstone { + get { return ResourceManager.GetString("ContainerType_Grindstone", resourceCulture); } } - - public static string ContainerType_Hopper - { - get - { + + /// + /// Looks up a localized string similar to ╔═════════════════════════════════════╗ + ///║ Container ║ + ///║ ╔═══╦═══╦═══╦═══╦═══╗ ║ + ///║ ║ 0 ║ 1 ║ 2 ║ 3 ║ 4 ║ ║ + ///║ ╚═══╩═══╩═══╩═══╩═══╝ ║ + ///║ Inventory ║ + ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ + ///║║ 5 ║ 6 ║ 7 ║ 8 ║ 9 ║10 ║11 ║12 ║13 ║║ + ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║ + ///║║14 ║15 ║16 ║17 ║18 ║19 ║20 ║21 ║22 ║║ + ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣║ + ///║║23 ║24 ║25 ║26 ║27 ║28 ║29 ║30 ║31 ║║ + ///║╚═══╩═══╩═══╩═══╩══ [rest of string was truncated]";. + /// + public static string ContainerType_Hopper { + get { return ResourceManager.GetString("ContainerType_Hopper", resourceCulture); } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ ///║╔═══╦═══════════╗ ║ @@ -221,66 +241,156 @@ namespace MinecraftClient ///║╚═══╩═══════════╩═══╝ ║ ///║╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗║ ///║║ 9 ║10 ║11 ║12 ║13 ║14 ║15 ║16 ║17 ║║ - ///║╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. + ///║╠═══╬═══╬═══╬═══╬══ [rest of string was truncated]";. /// - public static string ContainerType_PlayerInventory - { - get - { + public static string ContainerType_PlayerInventory { + get { return ResourceManager.GetString("ContainerType_PlayerInventory", resourceCulture); } } - + /// /// Looks up a localized string similar to # Startup Config File /// - ///[Main] + ///# New to Minecraft Console Client? See README and sample configuration files here: + ///# https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config /// + ///# Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download + ///# Some settings missing here after an upgrade? Try to delete this file and relaunch MCC to generate a new one + /// + ///[Main] ///# General settings ///# Leave blank to prompt user on startup - ///# Use "-" as password for offline mode - /// - ///login= - ///password= - ///serverip= - ///type=mojang # Account type. mojang or microsoft - ///method=mcc # Microsoft Account sign-in method. mcc OR browser - /// - ///# Advanced settings - /// - ///language=en_GB - ///consoletitle=%username%@%serverip% - Minecraft Console Client - ///internalcmdchar=slash # Use 'none', 'slash' or 'backslash' - ///messagec [rest of string was truncated]";. + ///# Use "-" as password fo [rest of string was truncated]";. /// - public static string MinecraftClient - { - get - { + public static string MinecraftClient { + get { return ResourceManager.GetString("MinecraftClient", resourceCulture); } } - + /// /// Looks up a localized string similar to [mcc] ///# Messages from MCC itself ///mcc.login=Login : + ///mcc.login_basic_io=Bitte gib einen Nutzernamen oder eine E-Mail deiner Wahl ein. + ///mcc.password=Passwort : + ///mcc.password_basic_io=Bitte gib das Passwort für {0} ein. + ///mcc.password_hidden=Passwort : {0} + ///mcc.offline=§8Das Programm läuft im Offline-Modus. + ///mcc.session_invalid=§8Gespeicherte Session ungültig oder abgelaufen. + ///mcc.session_valid=§8Gespeicherte Session gültig für {0}. + ///mcc.connecting=Verbinde zu {0}... + ///mcc.ip=Server-IP : + ///mcc.use_version=§8B [rest of string was truncated]";. + /// + public static string Translation_de { + get { + return ResourceManager.GetString("Translation_de", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.help_us_translate=Help us translate MCC: https://mccteam.github.io/guide/translation.html + ///mcc.run_with_default_settings=\nMCC is running with default settings. + ///mcc.settings_generated=§cSettings file MinecraftClient.ini has been generated. + ///mcc.login=Login : ///mcc.login_basic_io=Please type the username or email of your choice. ///mcc.password=Password : ///mcc.password_basic_io=Please type the password for {0}. ///mcc.password_hidden=Password : {0} - ///mcc.offline=§8You chose to run in offline mode. - ///mcc.session_invalid=§8Cached session is invalid or expired. - ///mcc.session_valid=§8Cached session is still valid for {0}. - ///mcc.connecting=Connecting to {0}... - ///mcc.ip=Server IP : - ///mcc.use_version=§8Using Minecraft version [rest of string was truncated]";. + ///mcc.offline=§8You chose [rest of string was truncated]";. /// - public static string TranslationEnglish - { - get - { - return ResourceManager.GetString("TranslationEnglish", resourceCulture); + public static string Translation_en { + get { + return ResourceManager.GetString("Translation_en", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.login=Connexion : + ///mcc.login_basic_io=Veuillez saisir un nom d'utilisateur ou une adresse email. + ///mcc.password=Mot de passe : + ///mcc.password_basic_io=Saisissez le mot de passe pour {0}. + ///mcc.password_hidden=Mot de passe : {0} + ///mcc.offline=§8Vous avez choisi d'utiliser le mode hors ligne. + ///mcc.session_invalid=§8Le cache de la session est invalide ou a expiré. + ///mcc.session_valid=§8Le cache de la session est encore valable pendant {0}. + ///mcc.connecting=Connexion à {0}... + /// [rest of string was truncated]";. + /// + public static string Translation_fr { + get { + return ResourceManager.GetString("Translation_fr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.login=Логин : + ///mcc.login_basic_io=Пожалуйста, введите имя пользователя или email по вашему выбору. + ///mcc.password=Пароль: + ///mcc.password_basic_io=Пожалуйста, введите пароль для {0}. + ///mcc.password_hidden=Пароль: {0} + ///mcc.offline=§8Вы выбрали запуск в автономном режиме. + ///mcc.session_invalid=§8Кэшированная сессия недействительна или истекла. + ///mcc.session_valid=§8Кэшированная сессия все еще действительна для {0}. + ///mcc.connecting=Подключение к {0}... + ///mcc.ip=IP сервера: + ///mc [rest of string was truncated]";. + /// + public static string Translation_ru { + get { + return ResourceManager.GetString("Translation_ru", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.login=Đăng nhập: + ///mcc.login_basic_io=Hãy nhập địa chỉ email hoặc tên tài khoản của bạn: + ///mcc.password=Mật khẩu: + ///mcc.password_basic_io=Hãy nhập mật khẩu cho {0}. + ///mcc.password_hidden=Password : {0} + ///mcc.offline=§8Bạn chọn sử dụng chế độ ngoại tuyến. + ///mcc.session_invalid=§8Phiên không hợp lệ hoặc đã hết hạn. + ///mcc.session_valid=§8Phiên vẫn còn hợp lệ cho {0}. + ///mcc.connecting=Đang kết nối tới {0}... + ///mcc.ip=Địa chỉ máy chủ: + ///mcc.use_version=§8Sử dụng Minecraft phiên bản [rest of string was truncated]";. + /// + public static string Translation_vi { + get { + return ResourceManager.GetString("Translation_vi", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.help_us_translate=帮助我们翻译MCC:https://mccteam.github.io/guide/translation.html + ///mcc.run_with_default_settings=\nMCC正在使用默认配置运行。 + ///mcc.settings_generated=§c配置文件 MinecraftClient.ini 已经生成。 + ///mcc.login=登录: + ///mcc.login_basic_io=请输入用户名或邮箱。 + ///mcc.password=密码: + ///mcc.password_basic_io=请输入{0}的密码。 + ///mcc.password_hidden=密码:{0} + ///mcc.offline=§8您正在使用离线模式。 + ///mcc.session_invalid=§8缓存无效或已过期。 + ///mcc.session_valid=§8{0}的缓存仍然有效。 + ///mcc.profile_key_invalid=§8缓存的聊天签名密钥需要刷新。 + ///mcc.profile_key_valid=§8{0}的聊天 [rest of string was truncated]";. + /// + public static string Translation_zh_Hans { + get { + return ResourceManager.GetString("Translation_zh_Hans", resourceCulture); } } } diff --git a/MinecraftClient/DefaultConfigResource.resx b/MinecraftClient/DefaultConfigResource.resx index ed1a6338..0c6c67bd 100644 --- a/MinecraftClient/DefaultConfigResource.resx +++ b/MinecraftClient/DefaultConfigResource.resx @@ -148,7 +148,22 @@ Resources\config\MinecraftClient.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - + + resources\lang\de.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + Resources\lang\en.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + resources\lang\fr.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + resources\lang\ru.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + resources\lang\vi.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + resources\lang\zh-hans.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + \ No newline at end of file diff --git a/MinecraftClient/FileMonitor.cs b/MinecraftClient/FileMonitor.cs index 378bb6b6..05c83208 100644 --- a/MinecraftClient/FileMonitor.cs +++ b/MinecraftClient/FileMonitor.cs @@ -22,7 +22,7 @@ namespace MinecraftClient /// Callback for file changes public FileMonitor(string folder, string filename, FileSystemEventHandler handler) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { string callerClass = new System.Diagnostics.StackFrame(1).GetMethod()!.DeclaringType!.Name; ConsoleIO.WriteLineFormatted(Translations.Get("filemonitor.init", callerClass, Path.Combine(folder, filename))); @@ -40,7 +40,7 @@ namespace MinecraftClient } catch { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { string callerClass = new System.Diagnostics.StackFrame(1).GetMethod()!.DeclaringType!.Name; ConsoleIO.WriteLineFormatted(Translations.Get("filemonitor.fail", callerClass)); diff --git a/MinecraftClient/Logger/FileLogLogger.cs b/MinecraftClient/Logger/FileLogLogger.cs index f065ed83..8418ec6c 100644 --- a/MinecraftClient/Logger/FileLogLogger.cs +++ b/MinecraftClient/Logger/FileLogLogger.cs @@ -26,7 +26,7 @@ namespace MinecraftClient.Logger { try { - if (!Settings.SaveColorCodes) + if (!Settings.Config.Logging.SaveColorCodes) msg = ChatBot.GetVerbatim(msg); if (prependTimestamp) msg = GetTimestamp() + ' ' + msg; diff --git a/MinecraftClient/Logger/FilteredLogger.cs b/MinecraftClient/Logger/FilteredLogger.cs index d3878f89..5ad42efc 100644 --- a/MinecraftClient/Logger/FilteredLogger.cs +++ b/MinecraftClient/Logger/FilteredLogger.cs @@ -1,4 +1,5 @@ using System.Text.RegularExpressions; +using static MinecraftClient.Settings; namespace MinecraftClient.Logger { @@ -10,11 +11,23 @@ namespace MinecraftClient.Logger { Regex? regexToUse = null; // Convert to bool for XOR later. Whitelist = 0, Blacklist = 1 - bool filterMode = Settings.FilterMode == Settings.FilterModeEnum.Blacklist; + bool filterMode = Config.Logging.FilterMode == LoggingConfigHealper.LoggingConfig.FilterModeEnum.blacklist; switch (channel) { - case FilterChannel.Chat: regexToUse = Settings.ChatFilter; break; - case FilterChannel.Debug: regexToUse = Settings.DebugFilter; break; + case FilterChannel.Chat: + string chat = Config.Logging.ChatFilterRegex; + if (string.IsNullOrEmpty(chat)) + regexToUse = null; + else + regexToUse = new(chat); + break; + case FilterChannel.Debug: + string debug = Config.Logging.DebugFilterRegex; + if (string.IsNullOrEmpty(debug)) + regexToUse = null; + else + regexToUse = new(debug); + break; } if (regexToUse != null) { diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs index 04cecd0e..28de9e7c 100644 --- a/MinecraftClient/Mapping/Movement.cs +++ b/MinecraftClient/Mapping/Movement.cs @@ -21,7 +21,7 @@ namespace MinecraftClient.Mapping /// Updated location after applying gravity public static Location HandleGravity(World world, Location location, ref double motionY) { - if (Settings.GravityEnabled) + if (Settings.InternalConfig.GravityEnabled) { Location onFoots = new(location.X, Math.Floor(location.Y), location.Z); Location belowFoots = Move(location, Direction.Down); diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index dc5265c5..f0b0b276 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -14,6 +14,7 @@ using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using static MinecraftClient.Settings; namespace MinecraftClient { @@ -142,12 +143,11 @@ namespace MinecraftClient /// The server port to use /// Minecraft protocol version to use /// ForgeInfo item stating that Forge is enabled - /// The text or command to send. Will only be sent if singlecommand is set to true. - public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo, string? command) + public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo) { - terrainAndMovementsEnabled = Settings.TerrainAndMovements; - inventoryHandlingEnabled = Settings.InventoryHandling; - entityHandlingEnabled = Settings.EntityHandling; + terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; + inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; + entityHandlingEnabled = Config.Main.Advanced.EntityHandling; sessionid = session.ID; if (!Guid.TryParse(session.PlayerID, out uuid)) @@ -159,64 +159,47 @@ namespace MinecraftClient this.protocolversion = protocolversion; this.playerKeyPair = playerKeyPair; - Log = Settings.LogToFile - ? new FileLogLogger(Settings.ExpandVars(Settings.LogFile), Settings.PrependTimestamp) + Log = Settings.Config.Logging.LogToFile + ? new FileLogLogger(Config.AppVar.ExpandVars(Settings.Config.Logging.LogFile), Settings.Config.Logging.PrependTimestamp) : new FilteredLogger(); - Log.DebugEnabled = Settings.DebugMessages; - Log.InfoEnabled = Settings.InfoMessages; - Log.ChatEnabled = Settings.ChatMessages; - Log.WarnEnabled = Settings.WarningMessages; - Log.ErrorEnabled = Settings.ErrorMessages; + Log.DebugEnabled = Config.Logging.DebugMessages; + Log.InfoEnabled = Config.Logging.InfoMessages; + Log.ChatEnabled = Config.Logging.ChatMessages; + Log.WarnEnabled = Config.Logging.WarningMessages; + Log.ErrorEnabled = Config.Logging.ErrorMessages; - if (command == null) - { - /* Load commands from Commands namespace */ - LoadCommands(); + /* Load commands from Commands namespace */ + LoadCommands(); - if (botsOnHold.Count == 0) - RegisterBots(); - } + if (botsOnHold.Count == 0) + RegisterBots(); try { client = ProxyHandler.NewTcpClient(host, port); client.ReceiveBufferSize = 1024 * 1024; - client.ReceiveTimeout = Settings.Timeout * 1000; // Default: 30 seconds + client.ReceiveTimeout = Config.Main.Advanced.TcpTimeout * 1000; // Default: 30 seconds handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, forgeInfo, this); Log.Info(Translations.Get("mcc.version_supported")); - if (command == null) - { - timeoutdetector = new(new Thread(new ParameterizedThreadStart(TimeoutDetector)), new CancellationTokenSource()); - timeoutdetector.Item1.Name = "MCC Connection timeout detector"; - timeoutdetector.Item1.Start(timeoutdetector.Item2.Token); - } + timeoutdetector = new(new Thread(new ParameterizedThreadStart(TimeoutDetector)), new CancellationTokenSource()); + timeoutdetector.Item1.Name = "MCC Connection timeout detector"; + timeoutdetector.Item1.Start(timeoutdetector.Item2.Token); try { if (handler.Login(this.playerKeyPair, session)) { - if (command != null) - { - handler.SendChatMessage(command, playerKeyPair); - Log.Info(Translations.Get("mcc.single_cmd", command)); - Thread.Sleep(5000); - handler.Disconnect(); - Thread.Sleep(1000); - } - else - { - foreach (ChatBot bot in botsOnHold) - BotLoad(bot, false); - botsOnHold.Clear(); + foreach (ChatBot bot in botsOnHold) + BotLoad(bot, false); + botsOnHold.Clear(); - Log.Info(Translations.Get("mcc.joined", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar))); + Log.Info(Translations.Get("mcc.joined", Config.Main.Advanced.InternalCmdChar.ToLogString())); - cmdprompt = new CancellationTokenSource(); - ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt); - ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput += ConsoleIO.AutocompleteHandler; - } + cmdprompt = new CancellationTokenSource(); + ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt); + ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived; + ConsoleInteractive.ConsoleReader.OnKeyInput += ConsoleIO.AutocompleteHandler; } else { @@ -253,7 +236,7 @@ namespace MinecraftClient ReconnectionAttemptsLeft--; Program.Restart(); } - else if (command == null && Settings.interactiveMode) + else if (InternalConfig.InteractiveMode) { ConsoleInteractive.ConsoleReader.StopReadThread(); ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; @@ -269,24 +252,24 @@ namespace MinecraftClient /// private void RegisterBots(bool reload = false) { - if (Settings.AntiAFK_Enabled) { BotLoad(new AntiAFK(Settings.AntiAFK_Delay, Settings.AntiAFK_UseTerrain_Handling, Settings.AntiAFK_Walk_Range, Settings.AntiAFK_Walk_Retries)); } - if (Settings.Hangman_Enabled) { BotLoad(new HangmanGame(Settings.Hangman_English)); } - if (Settings.Alerts_Enabled) { BotLoad(new Alerts()); } - if (Settings.ChatLog_Enabled) { BotLoad(new ChatLog(Settings.ExpandVars(Settings.ChatLog_File), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); } - if (Settings.PlayerLog_Enabled) { BotLoad(new PlayerListLogger(Settings.PlayerLog_Delay, Settings.ExpandVars(Settings.PlayerLog_File))); } - if (Settings.AutoRelog_Enabled) { BotLoad(new AutoRelog(Settings.AutoRelog_Delay_Min, Settings.AutoRelog_Delay_Max, Settings.AutoRelog_Retries)); } - if (Settings.ScriptScheduler_Enabled) { BotLoad(new ScriptScheduler(Settings.ExpandVars(Settings.ScriptScheduler_TasksFile))); } - if (Settings.RemoteCtrl_Enabled) { BotLoad(new RemoteControl()); } - if (Settings.AutoRespond_Enabled) { BotLoad(new AutoRespond(Settings.AutoRespond_Matches, Settings.AutoRespond_MatchColors)); } - if (Settings.AutoAttack_Enabled) { BotLoad(new AutoAttack(Settings.AutoAttack_Mode, Settings.AutoAttack_Priority, Settings.AutoAttack_OverrideAttackSpeed, Settings.AutoAttack_CooldownSeconds, Settings.AutoAttack_Interaction)); } - if (Settings.AutoFishing_Enabled) { BotLoad(new AutoFishing()); } - if (Settings.AutoEat_Enabled) { BotLoad(new AutoEat(Settings.AutoEat_hungerThreshold)); } - if (Settings.Mailer_Enabled) { BotLoad(new Mailer()); } - if (Settings.AutoCraft_Enabled) { BotLoad(new AutoCraft(Settings.AutoCraft_configFile)); } - if (Settings.AutoDrop_Enabled) { BotLoad(new AutoDrop(Settings.AutoDrop_Mode, Settings.AutoDrop_items)); } - if (Settings.ReplayMod_Enabled && reload) { BotLoad(new ReplayCapture(Settings.ReplayMod_BackupInterval)); } - if (Settings.FollowPlayer_Enabled) { BotLoad(new FollowPlayer(Settings.FollowPlayer_UpdateLimit, Settings.FollowPlayer_UpdateLimit)); } - if (Settings.Map_Enabled) { BotLoad(new Map()); } + if (Config.ChatBot.AntiAFK.Enabled) { BotLoad(new AntiAFK()); } + if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); } + if (Config.ChatBot.Alerts.Enabled) { BotLoad(new Alerts()); } + if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); } + if (Config.ChatBot.PlayerListLogger.Enabled) { BotLoad(new PlayerListLogger()); } + if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); } + if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); } + if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); } + if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); } + if (Config.ChatBot.AutoAttack.Enabled) { BotLoad(new AutoAttack()); } + if (Config.ChatBot.AutoFishing.Enabled) { BotLoad(new AutoFishing()); } + if (Config.ChatBot.AutoEat.Enabled) { BotLoad(new AutoEat()); } + if (Config.ChatBot.Mailer.Enabled) { BotLoad(new Mailer()); } + if (Config.ChatBot.AutoCraft.Enabled) { BotLoad(new AutoCraft()); } + if (Config.ChatBot.AutoDrop.Enabled) { BotLoad(new AutoDrop()); } + if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); } + if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); } + if (Config.ChatBot.Map.Enabled) { BotLoad(new Map()); } //Add your ChatBot here by uncommenting and adapting //BotLoad(new ChatBots.YourBot()); @@ -302,7 +285,7 @@ namespace MinecraftClient { string text = chatQueue.Dequeue(); handler.SendChatMessage(text, playerKeyPair); - nextMessageSendTime = DateTime.Now + Settings.messageCooldown; + nextMessageSendTime = DateTime.Now + TimeSpan.FromSeconds(Config.Main.Advanced.MessageCooldown); } } @@ -336,7 +319,7 @@ namespace MinecraftClient { lock (locationLock) { - for (int i = 0; i < Settings.MovementSpeed; i++) //Needs to run at 20 tps; MCC runs at 10 tps + for (int i = 0; i < Config.Main.Advanced.MovementSpeed; i++) //Needs to run at 20 tps; MCC runs at 10 tps { if (_yaw == null || _pitch == null) { @@ -349,10 +332,8 @@ namespace MinecraftClient Location next = path.Dequeue(); steps = Movement.Move2Steps(location, next, ref motionY); - if (Settings.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers - { + if (Config.Main.Advanced.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers UpdateLocation(location, next + new Location(0, 1, 0)); // Update yaw and pitch to look at next step - } } else { @@ -370,7 +351,7 @@ namespace MinecraftClient } } - if (Settings.AutoRespawn && respawnTicks > 0) + if (Config.Main.Advanced.AutoRespawn && respawnTicks > 0) { respawnTicks--; if (respawnTicks == 0) @@ -404,7 +385,7 @@ namespace MinecraftClient lock (lastKeepAliveLock) { - if (lastKeepAlive.AddSeconds(Settings.Timeout) < DateTime.Now) + if (lastKeepAlive.AddSeconds(Config.Main.Advanced.TcpTimeout) < DateTime.Now) { if (((CancellationToken)o!).IsCancellationRequested) return; @@ -572,11 +553,11 @@ namespace MinecraftClient text = text.Trim(); if (text.Length > 0) { - if (Settings.internalCmdChar == ' ' || text[0] == Settings.internalCmdChar) + if (Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' || text[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) { string? response_msg = ""; - string command = Settings.internalCmdChar == ' ' ? text : text[1..]; - if (!PerformInternalCommand(Settings.ExpandVars(command), ref response_msg, Settings.GetVariables()) && Settings.internalCmdChar == '/') + string command = Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' ? text : text[1..]; + if (!PerformInternalCommand(Config.AppVar.ExpandVars(command), ref response_msg, Settings.Config.AppVar.GetVariables()) && Config.Main.Advanced.InternalCmdChar.ToChar() == '/') { SendText(text); } @@ -659,7 +640,7 @@ namespace MinecraftClient } else response_msg = Translations.Get("icmd.unknown", command_name); } - else response_msg = Translations.Get("icmd.list", String.Join(", ", cmd_names.ToArray()), Settings.internalCmdChar); + else response_msg = Translations.Get("icmd.list", String.Join(", ", cmd_names.ToArray()), Config.Main.Advanced.InternalCmdChar.ToChar()); } else if (cmds.ContainsKey(command_name)) { @@ -845,7 +826,6 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.Initialize(), new ChatBot[] { b }); if (handler != null) DispatchBotEvent(bot => bot.AfterGameJoined(), new ChatBot[] { b }); - Settings.SingleCommand = ""; } /// @@ -2395,18 +2375,19 @@ namespace MinecraftClient /// public void OnGameJoined() { - if (!String.IsNullOrWhiteSpace(Settings.BrandInfo)) - handler.SendBrandInfo(Settings.BrandInfo.Trim()); + string? bandString = Config.Main.Advanced.BrandInfo.ToBrandString(); + if (!String.IsNullOrWhiteSpace(bandString)) + handler.SendBrandInfo(bandString.Trim()); - if (Settings.MCSettings_Enabled) + if (Config.MCSettings.Enabled) handler.SendClientSettings( - Settings.MCSettings_Locale, - Settings.MCSettings_RenderDistance, - Settings.MCSettings_Difficulty, - Settings.MCSettings_ChatMode, - Settings.MCSettings_ChatColors, - Settings.MCSettings_Skin_All, - Settings.MCSettings_MainHand); + Config.MCSettings.Locale, + Config.MCSettings.RenderDistance, + (byte)Config.MCSettings.Difficulty, + (byte)Config.MCSettings.ChatMode, + Config.MCSettings.ChatColors, + Config.MCSettings.Skin.GetByte(), + (byte)Config.MCSettings.MainHand); if (inventoryHandlingRequested) @@ -2484,15 +2465,15 @@ namespace MinecraftClient { case MovementType.Sneak: // https://minecraft.fandom.com/wiki/Sneaking#Effects - Sneaking 1.31m/s - Settings.MovementSpeed = 2; + Config.Main.Advanced.MovementSpeed = 2; break; case MovementType.Walk: // https://minecraft.fandom.com/wiki/Walking#Usage - Walking 4.317 m/s - Settings.MovementSpeed = 4; + Config.Main.Advanced.MovementSpeed = 4; break; case MovementType.Sprint: // https://minecraft.fandom.com/wiki/Sprinting#Usage - Sprinting 5.612 m/s - Settings.MovementSpeed = 5; + Config.Main.Advanced.MovementSpeed = 5; break; } } @@ -2601,7 +2582,7 @@ namespace MinecraftClient if (message.isSignedChat) { - if (!Settings.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!) + if (!Config.Signature.ShowIllegalSignedChat && !message.isSystemChat && !(bool)message.isSignatureLegal!) return; messageText = ChatParser.ParseSignedChat(message, links); } @@ -2615,7 +2596,7 @@ namespace MinecraftClient Log.Chat(messageText); - if (Settings.DisplayChatLinks) + if (Config.Main.Advanced.ShowChatLinks) foreach (string link in links) Log.Chat(Translations.Get("mcc.link", link)); @@ -3024,14 +3005,14 @@ namespace MinecraftClient if (health <= 0) { - if (Settings.AutoRespawn) + if (Config.Main.Advanced.AutoRespawn) { Log.Info(Translations.Get("mcc.player_dead_respawn")); respawnTicks = 10; } else { - Log.Info(Translations.Get("mcc.player_dead", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar))); + Log.Info(Translations.Get("mcc.player_dead", Config.Main.Advanced.InternalCmdChar.ToLogString())); } DispatchBotEvent(bot => bot.OnDeath()); } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 7148da0b..b7f45d16 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -36,9 +36,10 @@ - + - + + NU1701 @@ -72,4 +73,9 @@ + + + + + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 70bbad1a..e25f18be 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MinecraftClient.Inventory.ItemPalettes; @@ -16,6 +17,12 @@ using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Session; using MinecraftClient.WinAPI; +using Tomlet; +using Tomlet.Models; +using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; +using static System.Net.Mime.MediaTypeNames; namespace MinecraftClient { @@ -104,12 +111,12 @@ namespace MinecraftClient ConsoleIO.DebugReadInput(); } - settingsIniPath = "MinecraftClient.ini"; - //Process ini configuration file - if (args.Length >= 1 && System.IO.File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini") + bool needWriteDefaultSetting, newlyGenerated = false; + settingsIniPath = "MinecraftClient.ini"; + if (args.Length >= 1 && File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini") { - Settings.LoadFile(args[0]); + needWriteDefaultSetting = Settings.LoadFromFile(args[0]); settingsIniPath = args[0]; //remove ini configuration file from arguments array @@ -117,14 +124,34 @@ namespace MinecraftClient args_tmp.RemoveAt(0); args = args_tmp.ToArray(); } - else if (System.IO.File.Exists("MinecraftClient.ini")) + else if (File.Exists("MinecraftClient.ini")) { - Settings.LoadFile("MinecraftClient.ini"); + needWriteDefaultSetting = Settings.LoadFromFile("MinecraftClient.ini"); + } + else + { + needWriteDefaultSetting = true; + newlyGenerated = true; } - else Settings.WriteDefaultSettings("MinecraftClient.ini"); - //Load external translation file. Should be called AFTER settings loaded - Translations.LoadExternalTranslationFile(Settings.Language); + if (needWriteDefaultSetting) + { + (string gameLanguage, string[] langList) = Translations.GetTranslationPriority(); + Translations.LoadTranslationFile(langList); + Config.Main.Advanced.Language = gameLanguage; + Settings.WriteToFile("MinecraftClient.ini", false); + if (newlyGenerated) + ConsoleIO.WriteLineFormatted(Translations.TryGet("mcc.settings_generated")); + ConsoleIO.WriteLine(Translations.TryGet("mcc.run_with_default_settings")); + } + else + { + //Load external translation file. Should be called AFTER settings loaded + Translations.LoadTranslationFile(Translations.GetTranslationPriority(Config.Main.Advanced.Language)); + if (!Config.Main.Advanced.Language.StartsWith("en")) + ConsoleIO.WriteLine(Translations.TryGet("mcc.help_us_translate")); + Settings.WriteToFile("MinecraftClient.ini", true); // format + } //Other command-line arguments if (args.Length >= 1) @@ -222,55 +249,56 @@ namespace MinecraftClient } catch (ArgumentException e) { - Settings.interactiveMode = false; + InternalConfig.InteractiveMode = false; HandleFailure(e.Message); return; } } - if (Settings.ConsoleTitle != "") + if (Config.Main.Advanced.ConsoleTitle != "") { - Settings.Username = "New Window"; - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + InternalConfig.Username = "New Window"; + Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); } //Test line to troubleshoot invisible colors - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted(Translations.Get("debug.color_test", "[0123456789ABCDEF]: [§00§11§22§33§44§55§66§77§88§99§aA§bB§cC§dD§eE§fF§r]")); } //Load cached sessions from disk if necessary - if (Settings.SessionCaching == CacheType.Disk) + if (Config.Main.Advanced.SessionCache == CacheType.disk) { bool cacheLoaded = SessionCache.InitializeDiskCache(); - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) Translations.WriteLineFormatted(cacheLoaded ? "debug.session_cache_ok" : "debug.session_cache_fail"); } + // Setup exit cleaning code + ExitCleanUp.Add(() => { DoExit(0); }); + //Asking the user to type in missing data such as Username and Password - bool useBrowser = Settings.AccountType == ProtocolHandler.AccountType.Microsoft && Settings.LoginMethod == "browser"; - if (Settings.Login == "" && !useBrowser) + bool useBrowser = Config.Main.General.AccountType == LoginType.microsoft && Config.Main.General.Method == LoginMethod.browser; + if (string.IsNullOrEmpty(Config.Main.General.Account.Login) && !useBrowser) { ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.Get("mcc.login_basic_io") : Translations.Get("mcc.login")); - Settings.Login = ConsoleIO.ReadLine(); + Config.Main.General.Account.Login = ConsoleIO.ReadLine().Trim(); + if (string.IsNullOrEmpty(Config.Main.General.Account.Login)) + { + HandleFailure(Translations.Get("error.login.blocked"), false, ChatBot.DisconnectReason.LoginRejected); + return; + } } - if (Settings.Password == "" - && (Settings.SessionCaching == CacheType.None || !SessionCache.Contains(Settings.ToLowerIfNeed(Settings.Login))) - && !useBrowser) + if (string.IsNullOrEmpty(Config.Main.General.Account.Password) && !useBrowser && + (Config.Main.Advanced.SessionCache == CacheType.none || !SessionCache.Contains(Settings.ToLowerIfNeed(Config.Main.General.Account.Login)))) { RequestPassword(); } - - // Setup exit cleaning code - ExitCleanUp.Add(delegate () + else { - // Do NOT use Program.Exit() as creating new Thread cause program to freeze - if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt = null; ConsoleIO.Reset(); } - if (Settings.playerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); } - }); - + InternalConfig.Password = Config.Main.General.Account.Password; + } startupargs = args; InitializeClient(); @@ -281,10 +309,10 @@ namespace MinecraftClient /// private static void RequestPassword() { - ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.Get("mcc.password_basic_io", Settings.Login) + "\n" : Translations.Get("mcc.password")); + ConsoleIO.WriteLine(ConsoleIO.BasicIO ? Translations.Get("mcc.password_basic_io", Config.Main.General.Account.Login) + "\n" : Translations.Get("mcc.password")); string? password = ConsoleIO.BasicIO ? Console.ReadLine() : ConsoleIO.ReadPassword(); if (password == null || password == string.Empty) { password = "-"; } - Settings.Password = password; + InternalConfig.Password = password; } /// @@ -292,23 +320,25 @@ namespace MinecraftClient /// private static void InitializeClient() { + InternalConfig.MinecraftVersion = Config.Main.Advanced.MinecraftVersion; + SessionToken session = new(); PlayerKeyPair? playerKeyPair = null; ProtocolHandler.LoginResult result = ProtocolHandler.LoginResult.LoginRequired; - string loginLower = Settings.ToLowerIfNeed(Settings.Login); - if (Settings.Password == "-") + string loginLower = Settings.ToLowerIfNeed(Config.Main.General.Account.Login); + if (InternalConfig.Password == "-") { Translations.WriteLineFormatted("mcc.offline"); result = ProtocolHandler.LoginResult.Success; session.PlayerID = "0"; - session.PlayerName = Settings.Login; + session.PlayerName = Config.Main.General.Account.Login; } else { // Validate cached session or login new session. - if (Settings.SessionCaching != CacheType.None && SessionCache.Contains(loginLower)) + if (Config.Main.Advanced.SessionCache != CacheType.none && SessionCache.Contains(loginLower)) { session = SessionCache.Get(loginLower); result = ProtocolHandler.GetTokenValidation(session); @@ -330,8 +360,8 @@ namespace MinecraftClient } if (result != ProtocolHandler.LoginResult.Success - && Settings.Password == "" - && Settings.AccountType == ProtocolHandler.AccountType.Mojang) + && InternalConfig.Password == "" + && Config.Main.General.AccountType == LoginType.mojang) RequestPassword(); } else ConsoleIO.WriteLineFormatted(Translations.Get("mcc.session_valid", session.PlayerName)); @@ -339,12 +369,12 @@ namespace MinecraftClient if (result != ProtocolHandler.LoginResult.Success) { - Translations.WriteLine("mcc.connecting", Settings.AccountType == ProtocolHandler.AccountType.Mojang ? "Minecraft.net" : "Microsoft"); - result = ProtocolHandler.GetLogin(Settings.Login, Settings.Password, Settings.AccountType, out session); + Translations.WriteLine("mcc.connecting", Config.Main.General.AccountType == LoginType.mojang ? "Minecraft.net" : "Microsoft"); + result = ProtocolHandler.GetLogin(Config.Main.General.Account.Login, InternalConfig.Password, Config.Main.General.AccountType, out session); } } - if (result == ProtocolHandler.LoginResult.Success && Settings.SessionCaching != CacheType.None) + if (result == ProtocolHandler.LoginResult.Success && Config.Main.Advanced.SessionCache != CacheType.none) SessionCache.Store(loginLower, session); if (result == ProtocolHandler.LoginResult.Success) @@ -352,17 +382,17 @@ namespace MinecraftClient if (result == ProtocolHandler.LoginResult.Success) { - if (Settings.AccountType == ProtocolHandler.AccountType.Microsoft && Settings.Password != "-" && Settings.LoginWithSecureProfile) + if (Config.Main.General.AccountType == LoginType.microsoft && InternalConfig.Password != "-" && Config.Signature.LoginWithSecureProfile) { // Load cached profile key from disk if necessary - if (Settings.ProfileKeyCaching == CacheType.Disk) + if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk) { bool cacheKeyLoaded = KeysCache.InitializeDiskCache(); - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) Translations.WriteLineFormatted(cacheKeyLoaded ? "debug.keys_cache_ok" : "debug.keys_cache_fail"); } - if (Settings.ProfileKeyCaching != CacheType.None && KeysCache.Contains(loginLower)) + if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && KeysCache.Contains(loginLower)) { playerKeyPair = KeysCache.Get(loginLower); if (playerKeyPair.NeedRefresh()) @@ -375,36 +405,36 @@ namespace MinecraftClient { Translations.WriteLineFormatted("mcc.fetching_key"); playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID); - if (Settings.ProfileKeyCaching != CacheType.None && playerKeyPair != null) + if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null) { KeysCache.Store(loginLower, playerKeyPair); } } } - Settings.Username = session.PlayerName; + InternalConfig.Username = session.PlayerName; bool isRealms = false; - if (Settings.ConsoleTitle != "") - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + if (Config.Main.Advanced.ConsoleTitle != "") + Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); - if (Settings.playerHeadAsIcon) - ConsoleIcon.SetPlayerIconAsync(Settings.Username); + if (Config.Main.Advanced.PlayerHeadAsIcon) + ConsoleIcon.SetPlayerIconAsync(InternalConfig.Username); - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) Translations.WriteLine("debug.session_id", session.ID); List availableWorlds = new(); - if (Settings.MinecraftRealmsEnabled && !String.IsNullOrEmpty(session.ID)) - availableWorlds = ProtocolHandler.RealmsListWorlds(Settings.Username, session.PlayerID, session.ID); + if (Config.Main.Advanced.MinecraftRealms && !String.IsNullOrEmpty(session.ID)) + availableWorlds = ProtocolHandler.RealmsListWorlds(InternalConfig.Username, session.PlayerID, session.ID); - if (Settings.ServerIP == "") + if (InternalConfig.ServerIP == string.Empty) { Translations.Write("mcc.ip"); string addressInput = ConsoleIO.ReadLine(); if (addressInput.StartsWith("realms:")) { - if (Settings.MinecraftRealmsEnabled) + if (Config.Main.Advanced.MinecraftRealms) { if (availableWorlds.Count == 0) { @@ -416,12 +446,12 @@ namespace MinecraftClient worldId = availableWorlds[worldIndex]; if (availableWorlds.Contains(worldId)) { - string RealmsAddress = ProtocolHandler.GetRealmsWorldServerAddress(worldId, Settings.Username, session.PlayerID, session.ID); + string RealmsAddress = ProtocolHandler.GetRealmsWorldServerAddress(worldId, InternalConfig.Username, session.PlayerID, session.ID); if (RealmsAddress != "") { addressInput = RealmsAddress; isRealms = true; - Settings.ServerVersion = MCHighestVersion; + InternalConfig.MinecraftVersion = MCHighestVersion; } else { @@ -441,37 +471,37 @@ namespace MinecraftClient return; } } - Settings.SetServerIP(addressInput); + Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(addressInput), true); } //Get server version int protocolversion = 0; ForgeInfo? forgeInfo = null; - if (Settings.ServerVersion != "" && Settings.ToLowerIfNeed(Settings.ServerVersion) != "auto") + if (InternalConfig.MinecraftVersion != "" && Settings.ToLowerIfNeed(InternalConfig.MinecraftVersion) != "auto") { - protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion); + protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(InternalConfig.MinecraftVersion); if (protocolversion != 0) - { - ConsoleIO.WriteLineFormatted(Translations.Get("mcc.use_version", Settings.ServerVersion, protocolversion)); - } - else ConsoleIO.WriteLineFormatted(Translations.Get("mcc.unknown_version", Settings.ServerVersion)); + ConsoleIO.WriteLineFormatted(Translations.Get("mcc.use_version", InternalConfig.MinecraftVersion, protocolversion)); + else + ConsoleIO.WriteLineFormatted(Translations.Get("mcc.unknown_version", InternalConfig.MinecraftVersion)); if (useMcVersionOnce) { useMcVersionOnce = false; - Settings.ServerVersion = ""; + InternalConfig.MinecraftVersion = ""; } } //Retrieve server info if version is not manually set OR if need to retrieve Forge information - if (!isRealms && (protocolversion == 0 || Settings.ServerAutodetectForge || (Settings.ServerForceForge && !ProtocolHandler.ProtocolMayForceForge(protocolversion)))) + if (!isRealms && (protocolversion == 0 || (Config.Main.Advanced.EnableForge == ForgeConfigType.auto) || + ((Config.Main.Advanced.EnableForge == ForgeConfigType.force) && !ProtocolHandler.ProtocolMayForceForge(protocolversion)))) { if (protocolversion != 0) Translations.WriteLine("mcc.forge"); else Translations.WriteLine("mcc.retrieve"); - if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion, ref forgeInfo)) + if (!ProtocolHandler.GetServerInfo(InternalConfig.ServerIP, InternalConfig.ServerPort, ref protocolversion, ref forgeInfo)) { HandleFailure(Translations.Get("error.ping"), true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost); return; @@ -479,7 +509,7 @@ namespace MinecraftClient } //Force-enable Forge support? - if (!isRealms && Settings.ServerForceForge && forgeInfo == null) + if (!isRealms && (Config.Main.Advanced.EnableForge == ForgeConfigType.force) && forgeInfo == null) { if (ProtocolHandler.ProtocolMayForceForge(protocolversion)) { @@ -499,12 +529,11 @@ namespace MinecraftClient try { //Start the main TCP client - string? command = String.IsNullOrEmpty(Settings.SingleCommand) ? null : Settings.SingleCommand; - client = new McClient(session, playerKeyPair, Settings.ServerIP, Settings.ServerPort, protocolversion, forgeInfo, command); + client = new McClient(session, playerKeyPair, InternalConfig.ServerIP, InternalConfig.ServerPort, protocolversion, forgeInfo); //Update console title - if (Settings.ConsoleTitle != "") - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + if (Config.Main.Advanced.ConsoleTitle != "") + Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); } catch (NotSupportedException) { @@ -543,7 +572,8 @@ namespace MinecraftClient public static void ReloadSettings() { if (settingsIniPath != null) - Settings.LoadFile(settingsIniPath); + if(Settings.LoadFromFile(settingsIniPath)) + ConsoleIO.WriteLine(Translations.TryGet("config.loading", settingsIniPath)); } /// @@ -563,22 +593,30 @@ namespace MinecraftClient System.Threading.Thread.Sleep(delaySeconds * 1000); } Translations.WriteLine("mcc.restart"); + ReloadSettings(); InitializeClient(); })).Start(); } + public static void DoExit(int exitcode = 0) + { + if (settingsIniPath != null) + { + Settings.WriteToFile(settingsIniPath, true); + ConsoleIO.WriteLineFormatted(Translations.TryGet("config.saving", settingsIniPath)); + } + if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } + if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); } + if (Config.Main.Advanced.PlayerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); } + Environment.Exit(exitcode); + } + /// /// Disconnect the current client from the server and exit the app /// public static void Exit(int exitcode = 0) { - new Thread(new ThreadStart(delegate - { - if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); } - if (Settings.playerHeadAsIcon) { ConsoleIcon.RevertToMCCIcon(); } - Environment.Exit(exitcode); - })).Start(); + new Thread(new ThreadStart(() => { DoExit(exitcode); })).Start(); } /// @@ -595,7 +633,7 @@ namespace MinecraftClient ConsoleIO.Reset(); while (Console.KeyAvailable) Console.ReadKey(true); - Console.WriteLine(errorMessage); + ConsoleIO.WriteLine(errorMessage); if (disconnectReason.HasValue) { @@ -604,13 +642,13 @@ namespace MinecraftClient } } - if (Settings.interactiveMode) + if (InternalConfig.InteractiveMode) { if (versionError) { Translations.Write("mcc.server_version"); - Settings.ServerVersion = ConsoleInteractive.ConsoleReader.RequestImmediateInput(); - if (Settings.ServerVersion != "") + InternalConfig.MinecraftVersion = ConsoleInteractive.ConsoleReader.RequestImmediateInput(); + if (InternalConfig.MinecraftVersion != "") { useMcVersionOnce = true; Restart(); @@ -627,7 +665,8 @@ namespace MinecraftClient { bool exitThread = false; string command = " "; - ConsoleIO.WriteLineFormatted(Translations.Get("mcc.disconnected", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar))); + ConsoleIO.WriteLine(string.Empty); + ConsoleIO.WriteLineFormatted(Translations.Get("mcc.disconnected", Config.Main.Advanced.InternalCmdChar.ToLogString())); Translations.WriteLineFormatted("mcc.press_exit"); while (!cancellationTokenSource.IsCancellationRequested) @@ -645,13 +684,13 @@ namespace MinecraftClient { string message = ""; - if (Settings.internalCmdChar != ' ' - && command[0] == Settings.internalCmdChar) + if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' ' + && command[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) command = command[1..]; if (command.StartsWith("reco")) { - message = new Commands.Reco().Run(null, Settings.ExpandVars(command), null); + message = new Commands.Reco().Run(null, Config.AppVar.ExpandVars(command), null); if (message == "") { exitThread = true; @@ -660,7 +699,7 @@ namespace MinecraftClient } else if (command.StartsWith("connect")) { - message = new Commands.Connect().Run(null, Settings.ExpandVars(command), null); + message = new Commands.Connect().Run(null, Config.AppVar.ExpandVars(command), null); if (message == "") { exitThread = true; @@ -669,31 +708,26 @@ namespace MinecraftClient } else if (command.StartsWith("exit") || command.StartsWith("quit")) { - message = new Commands.Exit().Run(null, Settings.ExpandVars(command), null); + message = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null); } else if (command.StartsWith("help")) { ConsoleIO.WriteLineFormatted("§8MCC: " + - (Settings.internalCmdChar == ' ' - ? "" - : "" + Settings.internalCmdChar) + + Config.Main.Advanced.InternalCmdChar.ToLogString() + new Commands.Reco().GetCmdDescTranslated()); ConsoleIO.WriteLineFormatted("§8MCC: " + - (Settings.internalCmdChar == ' ' - ? "" - : "" + Settings.internalCmdChar) + + Config.Main.Advanced.InternalCmdChar.ToLogString() + new Commands.Connect().GetCmdDescTranslated()); } else - ConsoleIO.WriteLineFormatted(Translations.Get("icmd.unknown", - command.Split(' ')[0])); + ConsoleIO.WriteLineFormatted(Translations.Get("icmd.unknown", command.Split(' ')[0])); if (message != "") ConsoleIO.WriteLineFormatted("§8MCC: " + message); } else { - _ = new Commands.Exit().Run(null, Settings.ExpandVars(command), null); + _ = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null); } } diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketTypePalette.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketTypePalette.cs index a65a0079..968988f7 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketTypePalette.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketTypePalette.cs @@ -61,7 +61,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes } else if (forgeEnabled) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLogLine("Ignoring unknown packet ID of 0x" + packetId.ToString("X2")); return PacketTypesIn.Unknown; } @@ -92,7 +92,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes } else if (forgeEnabled) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLogLine("Ignoring unknown packet ID of 0x" + packetId.ToString("X2")); return PacketTypesOut.Unknown; } diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 73b7aebc..a84b61ea 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -13,6 +13,7 @@ using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Handlers { @@ -494,7 +495,7 @@ namespace MinecraftClient.Protocol.Handlers if (serverID == "-") Translations.WriteLineFormatted("mcc.server_offline"); - else if (Settings.DebugMessages) + else if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("mcc.handshake", serverID)); return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey, session); @@ -511,7 +512,7 @@ namespace MinecraftClient.Protocol.Handlers RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!; byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey(); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("debug.crypto"); if (serverIDhash != "-") @@ -534,7 +535,7 @@ namespace MinecraftClient.Protocol.Handlers { session.ServerIDhash = serverIDhash; session.ServerPublicKey = serverPublicKey; - SessionCache.Store(Settings.Login.ToLower(), session); + SessionCache.Store(Config.Main.General.Account.Login.ToLower(), session); } else { @@ -862,7 +863,7 @@ namespace MinecraftClient.Protocol.Handlers Protocol16Handler ComTmp = new(tcp); string result = ComTmp.ReadNextString(); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { // May contain formatting codes, cannot use WriteLineFormatted Console.ForegroundColor = ConsoleColor.DarkGray; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 75c4ed47..78868cbb 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -22,6 +22,7 @@ using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Handlers { @@ -461,8 +462,8 @@ namespace MinecraftClient.Protocol.Handlers { //Hide system messages or xp bar messages? messageType = dataTypes.ReadNextByte(packetData); - if ((messageType == 1 && !Settings.DisplaySystemMessages) - || (messageType == 2 && !Settings.DisplayXPBarMessages)) + if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) + || (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages)) break; if (protocolVersion >= MC_1_16_5_Version) @@ -482,8 +483,8 @@ namespace MinecraftClient.Protocol.Handlers string? unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; messageType = dataTypes.ReadNextVarInt(packetData); - if ((messageType == 1 && !Settings.DisplaySystemMessages) - || (messageType == 2 && !Settings.DisplayXPBarMessages)) + if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) + || (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages)) break; Guid senderUUID = dataTypes.ReadNextUUID(packetData); @@ -1603,7 +1604,7 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.SystemChat: string systemMessage = dataTypes.ReadNextString(packetData); int msgType = dataTypes.ReadNextVarInt(packetData); - if ((msgType == 1 && !Settings.DisplaySystemMessages)) + if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) break; handler.OnTextReceived(new(systemMessage, true, msgType, Guid.Empty, true)); break; @@ -1925,7 +1926,7 @@ namespace MinecraftClient.Protocol.Handlers { session.ServerIDhash = serverIDhash; session.ServerPublicKey = serverPublicKey; - SessionCache.Store(Settings.Login.ToLower(), session); + SessionCache.Store(Config.Main.General.Account.Login.ToLower(), session); } else { @@ -2119,7 +2120,7 @@ namespace MinecraftClient.Protocol.Handlers { string result = dataTypes.ReadNextString(packetData); //Get the Json data - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) { // May contain formatting codes, cannot use WriteLineFormatted Console.ForegroundColor = ConsoleColor.DarkGray; @@ -2230,7 +2231,7 @@ namespace MinecraftClient.Protocol.Handlers /// List< Argument Name, Argument Value > private List>? CollectCommandArguments(string command) { - if (!isOnlineMode || !Settings.SignMessageInCommand) + if (!isOnlineMode || !Config.Signature.SignMessageInCommand) return null; List> needSigned = new(); @@ -2371,7 +2372,7 @@ namespace MinecraftClient.Protocol.Handlers DateTimeOffset timeNow = DateTimeOffset.UtcNow; fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); - if (!isOnlineMode || playerKeyPair == null || !Settings.SignChat) + if (!isOnlineMode || playerKeyPair == null || !Config.Signature.SignChat) { fields.AddRange(dataTypes.GetLong(0)); // Salt: Long fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index d1be9b34..102d9fad 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -130,7 +130,7 @@ namespace MinecraftClient.Protocol.Handlers byte fmlProtocolVersion = dataTypes.ReadNextByte(packetData); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("forge.version", fmlProtocolVersion)); if (fmlProtocolVersion >= 1) @@ -140,7 +140,7 @@ namespace MinecraftClient.Protocol.Handlers SendForgeHandshakePacket(FMLHandshakeDiscriminator.ClientHello, new byte[] { fmlProtocolVersion }); // Then tell the server that we're running the same mods. - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("forge.send_mod"); byte[][] mods = new byte[forgeInfo.Mods.Count][]; for (int i = 0; i < forgeInfo.Mods.Count; i++) @@ -160,7 +160,7 @@ namespace MinecraftClient.Protocol.Handlers Thread.Sleep(2000); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("forge.accept"); // Tell the server that yes, we are OK with the mods it has // even though we don't actually care what mods it has. @@ -182,7 +182,7 @@ namespace MinecraftClient.Protocol.Handlers // with blocks and items. int registrySize = dataTypes.ReadNextVarInt(packetData); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("forge.registry", registrySize)); fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; @@ -194,7 +194,7 @@ namespace MinecraftClient.Protocol.Handlers bool hasNextRegistry = dataTypes.ReadNextBool(packetData); string registryName = dataTypes.ReadNextString(packetData); int registrySize = dataTypes.ReadNextVarInt(packetData); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("forge.registry_2", registryName, registrySize)); if (!hasNextRegistry) fmlHandshakeState = FMLHandshakeClientState.PENDINGCOMPLETE; @@ -206,7 +206,7 @@ namespace MinecraftClient.Protocol.Handlers // Just say yes. if (discriminator != FMLHandshakeDiscriminator.HandshakeAck) return false; - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("forge.accept_registry"); SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, new byte[] { (byte)FMLHandshakeClientState.PENDINGCOMPLETE }); @@ -220,7 +220,7 @@ namespace MinecraftClient.Protocol.Handlers SendForgeHandshakePacket(FMLHandshakeDiscriminator.HandshakeAck, new byte[] { (byte)FMLHandshakeClientState.COMPLETE }); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLine("forge.complete"); fmlHandshakeState = FMLHandshakeClientState.DONE; return true; @@ -300,7 +300,7 @@ namespace MinecraftClient.Protocol.Handlers // // [1]: Version is usually set to "FML2" for FML stuff and "1" for mods - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("forge.fml2.mod"); List mods = new(); @@ -332,7 +332,7 @@ namespace MinecraftClient.Protocol.Handlers // We are supposed to validate server info against our set of installed mods, then reply with our list // In MCC, we just want to send a valid response so we'll reply back with data collected from the server. - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("forge.fml2.mod_send"); // Packet ID 2: Client to Server Mod List @@ -368,7 +368,7 @@ namespace MinecraftClient.Protocol.Handlers // Registry Snapshot: ForgeRegistry.java > Snapshot > read(PacketBuffer) // Not documented yet. We're ignoring this packet in MCC - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { string registryName = dataTypes.ReadNextString(packetData); ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.registry", registryName)); @@ -387,7 +387,7 @@ namespace MinecraftClient.Protocol.Handlers // [1] Config data may containt a standard Minecraft string readable with dataTypes.readNextString() // We're ignoring this packet in MCC - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { string configName = dataTypes.ReadNextString(packetData); ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.config", configName)); @@ -398,7 +398,7 @@ namespace MinecraftClient.Protocol.Handlers break; default: - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.unknown", packetID)); break; } @@ -413,7 +413,7 @@ namespace MinecraftClient.Protocol.Handlers return true; } } - else if (Settings.DebugMessages) + else if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted(Translations.Get("forge.fml2.unknown_channel", fmlChannel)); } @@ -505,7 +505,7 @@ namespace MinecraftClient.Protocol.Handlers if (forgeInfo.Mods.Any()) { ConsoleIO.WriteLineFormatted(Translations.Get("forge.with_mod", forgeInfo.Mods.Count)); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { Translations.WriteLineFormatted("forge.mod_list"); foreach (ForgeInfo.ForgeMod mod in forgeInfo.Mods) diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index b47c4e08..dc29f421 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -5,13 +5,13 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; using MinecraftClient.Protocol.Message; +using static MinecraftClient.Settings; namespace MinecraftClient.Protocol { /// /// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed. /// - static class ChatParser { public enum MessageType @@ -47,7 +47,7 @@ namespace MinecraftClient.Protocol /// Returns the translated text public static string ParseSignedChat(ChatMessage message, List? links = null) { - string chatContent = Settings.ShowModifiedChat && message.unsignedContent != null ? message.unsignedContent : message.content; + string chatContent = Config.Signature.ShowModifiedChat && message.unsignedContent != null ? message.unsignedContent : message.content; string content = message.isJson ? ParseText(chatContent, links) : chatContent; string sender = message.displayName!; @@ -107,27 +107,27 @@ namespace MinecraftClient.Protocol string color = string.Empty; if (message.isSystemChat) { - if (Settings.MarkSystemMessage) + if (Config.Signature.MarkSystemMessage) color = "§z §r "; // Custom color code §z : Background Gray } else { if ((bool)message.isSignatureLegal!) { - if (Settings.ShowModifiedChat && message.unsignedContent != null) + if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) { - if (Settings.MarkModifiedMsg) + if (Config.Signature.MarkModifiedMsg) color = "§x §r "; // Custom color code §x : Background Yellow } else { - if (Settings.MarkLegallySignedMsg) + if (Config.Signature.MarkLegallySignedMsg) color = "§y §r "; // Custom color code §y : Background Green } } else { - if (Settings.MarkIllegallySignedMsg) + if (Config.Signature.MarkIllegallySignedMsg) color = "§w §r "; // Custom color code §w : Background Red } } @@ -203,12 +203,12 @@ namespace MinecraftClient.Protocol if (!Directory.Exists("lang")) Directory.CreateDirectory("lang"); - string Language_File = "lang" + Path.DirectorySeparatorChar + Settings.Language + ".lang"; + string Language_File = "lang" + Path.DirectorySeparatorChar + Config.Main.Advanced.Language + ".lang"; //File not found? Try downloading language file from Mojang's servers? if (!File.Exists(Language_File)) { - ConsoleIO.WriteLineFormatted(Translations.Get("chat.download", Settings.Language)); + ConsoleIO.WriteLineFormatted(Translations.Get("chat.download", Config.Main.Advanced.Language)); HttpClient httpClient = new(); try { @@ -217,11 +217,11 @@ namespace MinecraftClient.Protocol string assets_index = fetch_index.Result; fetch_index.Dispose(); - string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Settings.Language.ToLower() + ".json" }, StringSplitOptions.None); + string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Config.Main.Advanced.Language.ToLower() + ".json" }, StringSplitOptions.None); tmp = tmp[1].Split(new string[] { "hash\": \"" }, StringSplitOptions.None); string hash = tmp[1].Split('"')[0]; //Translations file identifier on Mojang's servers string translation_file_location = Settings.TranslationsFile_Website_Download + '/' + hash[..2] + '/' + hash; - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("chat.request", translation_file_location)); Task fetch_file = httpClient.GetStringAsync(translation_file_location); @@ -266,7 +266,7 @@ namespace MinecraftClient.Protocol } } - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) Translations.WriteLineFormatted("chat.loaded"); } else //No external dictionnary found. diff --git a/MinecraftClient/Protocol/MicrosoftAuthentication.cs b/MinecraftClient/Protocol/MicrosoftAuthentication.cs index cbb84a61..b782ea3d 100644 --- a/MinecraftClient/Protocol/MicrosoftAuthentication.cs +++ b/MinecraftClient/Protocol/MicrosoftAuthentication.cs @@ -6,6 +6,8 @@ using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; namespace MinecraftClient.Protocol { @@ -204,7 +206,7 @@ namespace MinecraftClient.Protocol var response = request.Post("application/x-www-form-urlencoded", postData); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } @@ -271,7 +273,7 @@ namespace MinecraftClient.Protocol request.Headers.Add("x-xbl-contract-version", "0"); var accessToken = loginResponse.AccessToken; - if (Settings.LoginMethod == "browser") + if (Config.Main.General.Method == LoginMethod.browser) { // Our own client ID must have d= in front of the token or HTTP status 400 // "Stolen" client ID must not have d= in front of the token or HTTP status 400 @@ -288,7 +290,7 @@ namespace MinecraftClient.Protocol + "\"TokenType\": \"JWT\"" + "}"; var response = request.Post("application/json", payload); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } @@ -338,7 +340,7 @@ namespace MinecraftClient.Protocol + "\"TokenType\": \"JWT\"" + "}"; var response = request.Post("application/json", payload); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } @@ -418,7 +420,7 @@ namespace MinecraftClient.Protocol string payload = "{\"identityToken\": \"XBL3.0 x=" + userHash + ";" + xstsToken + "\"}"; var response = request.Post("application/json", payload); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } @@ -440,7 +442,7 @@ namespace MinecraftClient.Protocol request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken)); var response = request.Get(); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } @@ -456,7 +458,7 @@ namespace MinecraftClient.Protocol request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken)); var response = request.Get(); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.ToString()); } diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 9d8f4dfd..94cce827 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -25,7 +25,7 @@ namespace MinecraftClient.Protocol.Keys response = request.Post("application/json", ""); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(response.Body.ToString()); } @@ -47,7 +47,7 @@ namespace MinecraftClient.Protocol.Keys { int code = (response == null) ? 0 : response.StatusCode; ConsoleIO.WriteLineFormatted("§cFetch profile key failed: HttpCode = " + code + ", Error = " + e.Message); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } diff --git a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs index 44998da4..c11ed0aa 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs @@ -4,6 +4,8 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Timers; using MinecraftClient.Protocol.Session; +using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; namespace MinecraftClient.Protocol.Keys { @@ -46,11 +48,11 @@ namespace MinecraftClient.Protocol.Keys keys.Add(login, playerKeyPair); } - if (Settings.ProfileKeyCaching == CacheType.Disk && updatetimer.Enabled == true) + if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk && updatetimer.Enabled == true) { pendingadds.Add(new KeyValuePair(login, playerKeyPair)); } - else if (Settings.ProfileKeyCaching == CacheType.Disk) + else if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk) { SaveToDisk(); } @@ -114,7 +116,7 @@ namespace MinecraftClient.Protocol.Keys //User-editable keys cache file in text format if (File.Exists(KeysCacheFilePlaintext)) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading_keys", KeysCacheFilePlaintext)); try @@ -133,27 +135,27 @@ namespace MinecraftClient.Protocol.Keys { PlayerKeyPair playerKeyPair = PlayerKeyPair.FromString(value); keys[login] = playerKeyPair; - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded_keys", playerKeyPair.ExpiresAt.ToString())); } catch (InvalidDataException e) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message)); } catch (FormatException e) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message)); } catch (ArgumentNullException e) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string_keys", value, e.Message)); } } - else if (Settings.DebugMessages) + else if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_line_keys", line)); } @@ -174,7 +176,7 @@ namespace MinecraftClient.Protocol.Keys /// private static void SaveToDisk() { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) Translations.WriteLineFormatted("cache.saving_keys"); List KeysCacheLines = new() diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index 7b12de0f..74a2fb5b 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -11,6 +11,8 @@ using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; namespace MinecraftClient.Protocol { @@ -64,7 +66,7 @@ namespace MinecraftClient.Protocol { ConsoleIO.WriteLineFormatted(Translations.Get("mcc.not_found", domainVal, e.GetType().FullName, e.Message)); } - }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30)); + }, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30)); } domain = domainVal; @@ -99,7 +101,7 @@ namespace MinecraftClient.Protocol { ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); } - }, TimeSpan.FromSeconds(Settings.ResolveSrvRecordsShortTimeout ? 10 : 30))) + }, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30))) { if (protocolversion != 0 && protocolversion != protocolversionTmp) Translations.WriteLineFormatted("error.version_different"); @@ -420,16 +422,16 @@ namespace MinecraftClient.Protocol /// Password /// In case of successful login, will contain session information for multiplayer /// Returns the status of the login (Success, Failure, etc.) - public static LoginResult GetLogin(string user, string pass, AccountType type, out SessionToken session) + public static LoginResult GetLogin(string user, string pass, LoginType type, out SessionToken session) { - if (type == AccountType.Microsoft) + if (type == LoginType.microsoft) { - if (Settings.LoginMethod == "mcc") + if (Config.Main.General.Method == LoginMethod.mcc) return MicrosoftMCCLogin(user, pass, out session); else return MicrosoftBrowserLogin(out session, user); } - else if (type == AccountType.Mojang) + else if (type == LoginType.mojang) { return MojangLogin(user, pass, out session); } @@ -494,7 +496,7 @@ namespace MinecraftClient.Protocol } catch (System.Security.Authentication.AuthenticationException e) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } @@ -502,7 +504,7 @@ namespace MinecraftClient.Protocol } catch (System.IO.IOException e) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } @@ -514,7 +516,7 @@ namespace MinecraftClient.Protocol } catch (Exception e) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } @@ -543,7 +545,7 @@ namespace MinecraftClient.Protocol { session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } @@ -601,7 +603,7 @@ namespace MinecraftClient.Protocol session.PlayerID = profile.UUID; session.ID = accessToken; session.RefreshToken = msaResponse.RefreshToken; - Settings.Login = msaResponse.Email; + Config.Main.General.Account.Login = msaResponse.Email; return LoginResult.Success; } else @@ -612,7 +614,7 @@ namespace MinecraftClient.Protocol catch (Exception e) { ConsoleIO.WriteLineFormatted("§cMicrosoft authenticate failed: " + e.Message); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } @@ -632,7 +634,7 @@ namespace MinecraftClient.Protocol var expTimestamp = long.Parse(json.Properties["exp"].StringValue, NumberStyles.Any, CultureInfo.CurrentCulture); var now = DateTime.Now; var tokenExp = UnixTimeStampToDateTime(expTimestamp); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine("Access token expiration time is " + tokenExp.ToString()); } @@ -772,7 +774,7 @@ namespace MinecraftClient.Protocol catch (Exception e) { ConsoleIO.WriteLineFormatted("§8" + e.GetType().ToString() + ": " + e.Message); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.StackTrace); } @@ -815,7 +817,7 @@ namespace MinecraftClient.Protocol catch (Exception e) { ConsoleIO.WriteLineFormatted("§8" + e.GetType().ToString() + ": " + e.Message); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + e.StackTrace); } @@ -890,14 +892,14 @@ namespace MinecraftClient.Protocol { try { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("debug.request", host)); TcpClient client = ProxyHandler.NewTcpClient(host, 443, true); SslStream stream = new(client.GetStream()); stream.AuthenticateAsClient(host, null, SslProtocols.Tls12, true); // Enable TLS 1.2. Hotfix for #1780 - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) foreach (string line in headers) ConsoleIO.WriteLineFormatted("§8> " + line); @@ -905,7 +907,7 @@ namespace MinecraftClient.Protocol System.IO.StreamReader sr = new(stream); string raw_result = sr.ReadToEnd(); - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine(""); foreach (string line in raw_result.Split('\n')) @@ -915,7 +917,7 @@ namespace MinecraftClient.Protocol if (raw_result.StartsWith("HTTP/1.1")) { postResult = raw_result[(raw_result.IndexOf("\r\n\r\n") + 4)..]; - statusCode = Settings.str2int(raw_result.Split(' ')[1]); + statusCode = int.Parse(raw_result.Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture); } else statusCode = 520; //Web server is returning an unknown error } diff --git a/MinecraftClient/Protocol/ProxiedWebRequest.cs b/MinecraftClient/Protocol/ProxiedWebRequest.cs index 3fcde5fc..73d9fe26 100644 --- a/MinecraftClient/Protocol/ProxiedWebRequest.cs +++ b/MinecraftClient/Protocol/ProxiedWebRequest.cs @@ -109,7 +109,7 @@ namespace MinecraftClient.Protocol requestMessage.Add(body); } else requestMessage.Add(""); // - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) { foreach (string l in requestMessage) { diff --git a/MinecraftClient/Protocol/ReplayHandler.cs b/MinecraftClient/Protocol/ReplayHandler.cs index 610273e3..abaeecfc 100644 --- a/MinecraftClient/Protocol/ReplayHandler.cs +++ b/MinecraftClient/Protocol/ReplayHandler.cs @@ -398,7 +398,7 @@ namespace MinecraftClient.Protocol private static void WriteDebugLog(string t) { - if (Settings.DebugMessages && logOutput) + if (Settings.Config.Logging.DebugMessages && logOutput) WriteLog(t); } diff --git a/MinecraftClient/Protocol/Session/CacheType.cs b/MinecraftClient/Protocol/Session/CacheType.cs deleted file mode 100644 index 81faed00..00000000 --- a/MinecraftClient/Protocol/Session/CacheType.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace MinecraftClient.Protocol.Session -{ - public enum CacheType - { - /// - /// Do not perform any session caching, always perform login requests from scratch. - /// - None, - - /// - /// Cache session information in memory to reuse session tokens across server joins. - /// - Memory, - - /// - /// Cache session information in a SessionCache file to share session tokens between different MCC instances. - /// - Disk - }; -} diff --git a/MinecraftClient/Protocol/Session/SessionCache.cs b/MinecraftClient/Protocol/Session/SessionCache.cs index 232c5e5e..82ebe005 100644 --- a/MinecraftClient/Protocol/Session/SessionCache.cs +++ b/MinecraftClient/Protocol/Session/SessionCache.cs @@ -4,6 +4,8 @@ using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Timers; +using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; namespace MinecraftClient.Protocol.Session { @@ -54,11 +56,11 @@ namespace MinecraftClient.Protocol.Session sessions.Add(login, session); } - if (Settings.SessionCaching == CacheType.Disk && updatetimer.Enabled == true) + if (Config.Main.Advanced.SessionCache == CacheType.disk && updatetimer.Enabled == true) { pendingadds.Add(new KeyValuePair(login, session)); } - else if (Settings.SessionCaching == CacheType.Disk) + else if (Config.Main.Advanced.SessionCache == CacheType.disk) { SaveToDisk(); } @@ -122,7 +124,7 @@ namespace MinecraftClient.Protocol.Session //Grab sessions in the Minecraft directory if (File.Exists(SessionCacheFileMinecraft)) { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading", Path.GetFileName(SessionCacheFileMinecraft))); Json.JSONData mcSession = new(Json.JSONData.DataType.String); try @@ -155,7 +157,7 @@ namespace MinecraftClient.Protocol.Session sessionItem["uuid"].StringValue.Replace("-", ""), clientID )); - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded", login, session.ID)); sessions[login] = session; } @@ -169,7 +171,7 @@ namespace MinecraftClient.Protocol.Session //Serialized session cache file in binary format if (File.Exists(SessionCacheFileSerialized)) { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.converting", SessionCacheFileSerialized)); try @@ -181,7 +183,7 @@ namespace MinecraftClient.Protocol.Session #pragma warning restore SYSLIB0011 // BinaryFormatter.Deserialize() is obsolete foreach (KeyValuePair item in sessionsTemp) { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded", item.Key, item.Value.ID)); sessions[item.Key] = item.Value; } @@ -199,7 +201,7 @@ namespace MinecraftClient.Protocol.Session //User-editable session cache file in text format if (File.Exists(SessionCacheFilePlaintext)) { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loading_session", SessionCacheFilePlaintext)); try @@ -215,17 +217,17 @@ namespace MinecraftClient.Protocol.Session { string login = Settings.ToLowerIfNeed(keyValue[0]); SessionToken session = SessionToken.FromString(keyValue[1]); - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.loaded", login, session.ID)); sessions[login] = session; } catch (InvalidDataException e) { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_string", keyValue[1], e.Message)); } } - else if (Settings.DebugMessages) + else if (Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted(Translations.Get("cache.ignore_line", line)); } @@ -246,7 +248,7 @@ namespace MinecraftClient.Protocol.Session /// private static void SaveToDisk() { - if (Settings.DebugMessages) + if (Config.Logging.DebugMessages) Translations.WriteLineFormatted("cache.saving"); List sessionCacheLines = new() diff --git a/MinecraftClient/Proxy/ProxyHandler.cs b/MinecraftClient/Proxy/ProxyHandler.cs index b62459bb..5d703dad 100644 --- a/MinecraftClient/Proxy/ProxyHandler.cs +++ b/MinecraftClient/Proxy/ProxyHandler.cs @@ -1,5 +1,7 @@ -using System.Net.Sockets; +using System; +using System.Net.Sockets; using Starksoft.Aspen.Proxy; +using Tomlet.Attributes; namespace MinecraftClient.Proxy { @@ -11,7 +13,45 @@ namespace MinecraftClient.Proxy public static class ProxyHandler { - public enum Type { HTTP, SOCKS4, SOCKS4a, SOCKS5 }; + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [TomlInlineComment("$Config.Proxy.Enabled_Login$")] + public bool Enabled_Login = false; + + [TomlInlineComment("$Config.Proxy.Enabled_Ingame$")] + public bool Enabled_Ingame = false; + + [TomlInlineComment("$Config.Proxy.Server$")] + public ProxyInfoConfig Server = new("0.0.0.0", 8080); + + [TomlInlineComment("$Config.Proxy.Proxy_Type$")] + public ProxyType Proxy_Type = ProxyType.HTTP; + + [TomlInlineComment("$Config.Proxy.Username$")] + public string Username = ""; + + [TomlInlineComment("$Config.Proxy.Password$")] + public string Password = ""; + + public void OnSettingUpdate() { } + + public struct ProxyInfoConfig + { + public string Host; + public ushort Port; + + public ProxyInfoConfig(string host, ushort port) + { + Host = host; + Port = port; + } + } + + public enum ProxyType { HTTP, SOCKS4, SOCKS4a, SOCKS5 }; + } private static readonly ProxyClientFactory factory = new(); private static IProxyClient? proxy; @@ -28,27 +68,26 @@ namespace MinecraftClient.Proxy { try { - if (login ? Settings.ProxyEnabledLogin : Settings.ProxyEnabledIngame) + if (login ? Config.Enabled_Login : Config.Enabled_Ingame) { ProxyType innerProxytype = ProxyType.Http; - switch (Settings.proxyType) + switch (Config.Proxy_Type) { - case Type.HTTP: innerProxytype = ProxyType.Http; break; - case Type.SOCKS4: innerProxytype = ProxyType.Socks4; break; - case Type.SOCKS4a: innerProxytype = ProxyType.Socks4a; break; - case Type.SOCKS5: innerProxytype = ProxyType.Socks5; break; + case Configs.ProxyType.HTTP: innerProxytype = ProxyType.Http; break; + case Configs.ProxyType.SOCKS4: innerProxytype = ProxyType.Socks4; break; + case Configs.ProxyType.SOCKS4a: innerProxytype = ProxyType.Socks4a; break; + case Configs.ProxyType.SOCKS5: innerProxytype = ProxyType.Socks5; break; } - if (Settings.ProxyUsername != "" && Settings.ProxyPassword != "") - { - proxy = factory.CreateProxyClient(innerProxytype, Settings.ProxyHost, Settings.ProxyPort, Settings.ProxyUsername, Settings.ProxyPassword); - } - else proxy = factory.CreateProxyClient(innerProxytype, Settings.ProxyHost, Settings.ProxyPort); + if (Config.Username != "" && Config.Password != "") + proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port, Config.Username, Config.Password); + else + proxy = factory.CreateProxyClient(innerProxytype, Config.Server.Host, Config.Server.Port); if (!proxy_ok) { - ConsoleIO.WriteLineFormatted(Translations.Get("proxy.connected", Settings.ProxyHost, Settings.ProxyPort)); + ConsoleIO.WriteLineFormatted(Translations.Get("proxy.connected", Config.Server.Host, Config.Server.Port)); proxy_ok = true; } diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini index 444f5b10..7694728e 100644 --- a/MinecraftClient/Resources/lang/en.ini +++ b/MinecraftClient/Resources/lang/en.ini @@ -1,5 +1,8 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate=Help us translate MCC: https://mccteam.github.io/guide/translation.html +mcc.run_with_default_settings=\nMCC is running with default settings. +mcc.settings_generated=§cSettings file MinecraftClient.ini has been generated. mcc.login=Login : mcc.login_basic_io=Please type the username or email of your choice. mcc.password=Password : @@ -67,7 +70,7 @@ error.forgeforce=Cannot force Forge support for this Minecraft version! error.login=Login failed : error.login.migrated=Account migrated, use e-mail as username. error.login.server=Login servers are unavailable. Please try again later. -error.login.blocked=Incorrect password, blacklisted IP or too many logins. +error.login.blocked=Incorrect username/password, blacklisted IP or too many logins. error.login.response=Invalid server response. error.login.premium=User not premium. error.login.network=Network error. @@ -643,3 +646,215 @@ bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n trigge # TestBot bot.testBot.told=Bot: {0} told me : {1} bot.testBot.said=Bot: {0} said : {1} + + +[config] + +config.load=Settings have been loaded from {0} +config.load.fail=§cFailed to load settings:§r +config.saving=§aThe current setting is saved as {0} + +# Head +config.Head=Startup Config File\n\n# New to Minecraft Console Client? See README and sample configuration files here:\n# https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config\n\n# Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download + +# Main.General +config.Main.General.account=Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup. +config.Main.General.login=Server address and port. Host can be filled in with domain name or IP. +config.Main.General.server_info=Account type: mojang OR microsoft. Also affects interactive login in console. +config.Main.General.method=Microsoft Account sign-in method: mcc OR browser. + +# Main.Advanced +config.Main.Advanced=Make sure you understand what each setting does before changing anything! +config.Main.Advanced.language=Fill in with in-game locale code, https://minecraft.fandom.com/wiki/Language +config.Main.Advanced.console_title= +config.Main.Advanced.internal_cmd_char=Use 'none', 'slash' or 'backslash' +config.Main.Advanced.message_cooldown=Minimum delay in seconds between messages to avoid being kicked for spam. +config.Main.Advanced.bot_owners=Name list or myfile.txt, one name per line. /!\ Server admins can impersonate owners! +config.Main.Advanced.mc_version=Use 'auto' or '1.X.X' values. Allows to skip server info retrieval. +config.Main.Advanced.mc_forge=Use 'auto', 'no' or 'force'. Force-enabling only works for MC 1.13+. +config.Main.Advanced.brand_info=Use 'mcc', 'vanilla', or 'none'. This is how MCC identifies itself to the server. +config.Main.Advanced.chatbot_log_file=Leave empty for no logfile +config.Main.Advanced.private_msgs_cmd_name=Used by RemoteControl bot +config.Main.Advanced.show_system_messages=System messages for server ops +config.Main.Advanced.show_xpbar_messages=Messages displayed above xp bar, set this to false in case of xp bar spam +config.Main.Advanced.show_chat_links=Decode links embedded in chat messages and show them in console +config.Main.Advanced.show_inventory_layout=Show inventory layout as ASCII art in inventory command +config.Main.Advanced.terrain_and_movements=Uses more ram, cpu, bandwidth but allows you to move around +config.Main.Advanced.inventory_handling=Toggle inventory handling (beta) +config.Main.Advanced.entity_handling=Toggle entity handling (beta) +config.Main.Advanced.session_cache=How to retain session tokens. Use 'none', 'memory' or 'disk' +config.Main.Advanced.profilekey_cache=How to retain profile key. Use 'none', 'memory' or 'disk' +config.Main.Advanced.resolve_srv_records=Use 'no', 'fast' (5s timeout), or 'yes'. Required for joining some servers. +config.Main.Advanced.account_list=See README > 'Servers and Accounts file' for more info about this +config.Main.Advanced.server_list=See README > 'Servers and Accounts file' for more info about this +config.Main.Advanced.player_head_icon=Only works on Windows XP-8 or Windows 10 with old console +config.Main.Advanced.exit_on_failure=Disable pauses on error, for using MCC in non-interactive scripts +config.Main.Advanced.script_cache=Cache compiled scripts for faster load on low-end devices +config.Main.Advanced.timestamps=Prepend timestamps to chat messages +config.Main.Advanced.auto_respawn=Toggle auto respawn if client player was dead (make sure your spawn point is safe) +config.Main.Advanced.minecraft_realms=Enable support for joining Minecraft Realms worlds +config.Main.Advanced.move_head_while_walking=Enable head movement while walking to avoid anti-cheat triggers +config.Main.Advanced.timeout=Set a custom timeout in seconds (Default: 30). Use only if you know what you're doing. +config.Main.Advanced.enable_emoji=If turned off, the emoji will be replaced with a simpler character (for /chunk status) +config.Main.Advanced.movement_speed=A movement speed higher than 2 may be considered cheating. +config.Main.Advanced.language.invaild=The language code is invalid. + +# Signature +config.Signature=Chat settings (affects minecraft 1.19+) +config.Signature.LoginWithSecureProfile=Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true" +config.Signature.SignChat=Whether to sign the chat send from MCC +config.Signature.SignMessageInCommand=Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me" +config.Signature.MarkLegallySignedMsg=Use green color block to mark chat with legitimate signatures +config.Signature.MarkModifiedMsg=Use yellow color block to mark chat that have been modified by the server. +config.Signature.MarkIllegallySignedMsg=Use red color block to mark chat without legitimate signature +config.Signature.MarkSystemMessage=Use gray color block to mark system message (always without signature) +config.Signature.ShowModifiedChat=Set to true to display messages modified by the server, false to display the original signed messages +config.Signature.ShowIllegalSignedChat=Whether to display chat and messages in commands without legal signatures + +# Logging +config.Logging=Only affect the messages on console. +config.Logging.DebugMessages=Please enable this before submitting bug reports. Thanks! +config.Logging.ChatMessages=Show server chat messages +config.Logging.InfoMessages=Informative messages (i.e Most of the message from MCC) +config.Logging.WarningMessages=Show warning messages +config.Logging.ErrorMessages=Show error messages +config.Logging.ChatFilter=Regex for filtering chat message +config.Logging.DebugFilter=Regex for filtering debug message +config.Logging.FilterMode=blacklist OR whitelist. Blacklist hide message match regex. Whitelist show message match regex +config.Logging.LogToFile=Write log messages to file +config.Logging.LogFile=Log file name +config.Logging.PrependTimestamp=Prepend timestamp to messages in log file +config.Logging.SaveColorCodes=Keep color codes in the saved text (§b) + +# AppVars +config.AppVars.Variables=can be used in some other fields as %yourvar%\n# %username% and %serverip% are reserved variables. + +# Proxy +config.Proxy=Connect to a server via a proxy instead of connecting directly\n# If Mojang session services are blocked on your network, set enabled=login to login using proxy but connect directly to the server\n# If connecting to port 25565 (Minecraft) is blocked on your network, set enabled=true to login & connect using the proxy\n# /!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences! +config.Proxy.Enabled_Login=Whether to connect to the login server through a proxy. +config.Proxy.Enabled_Ingame=Whether to connect to the game server through a proxy. +config.Proxy.Server=Proxy server must allow HTTPS for login, and non-443 ports for playing +config.Proxy.Proxy_Type=Supported types: HTTP, SOCKS4, SOCKS4a, SOCKS5 +config.Proxy.Username=Only required for password-protected proxies +config.Proxy.Password=Only required for password-protected proxies + +# ChatFormat +config.ChatFormat=MCC does it best to detect chat messages, but some server have unusual chat formats\n# When this happens, you'll need to configure chat format below, see README > 'Detecting chat messages'\n# Do not forget to uncomment (remove '#') these settings if modifying them +config.ChatFormat.Builtins=MCC support for common message formats. Set "false" to avoid conflicts with custom formats. + +# MCSettings +config.MCSettings=Settings below are sent to the server and only affect server-side things like your skin +config.MCSettings.Enabled=If disabled, settings below are not sent to the server +config.MCSettings.Locale=Use any language implemented in Minecraft +config.MCSettings.RenderDistance=Value range: [0 - 255] +config.MCSettings.Difficulty=MC 1.7- difficulty. peaceful, easy, normal, difficult +config.MCSettings.ChatMode=Use 'enabled', 'commands', or 'disabled'. Allows to mute yourself... +config.MCSettings.ChatColors=Allows disabling chat colors server-side +config.MCSettings.MainHand=MC 1.9+ main hand. left or right + +# ChatBot +config.ChatBot================================ #\n# Minecraft Console Client Bots #\n# =============================== # + +# ChatBot.Alerts +config.ChatBot.Alerts=Get alerted when specified words are detected in chat\n# Useful for moderating your server or detecting when someone is talking to you +config.ChatBot.Alerts.Beep_Enabled=Play a beep sound when a word is detected in addition to highlighting. +config.ChatBot.Alerts.Trigger_By_Words=Triggers an alert after receiving a specified keyword. +config.ChatBot.Alerts.Trigger_By_Rain=Trigger alerts when it rains and when it stops. +config.ChatBot.Alerts.Trigger_By_Thunderstorm=Triggers alerts at the beginning and end of thunderstorms. +config.ChatBot.Alerts.Matches_File=List of words/strings to alert you on, e.g. "Yourname" +config.ChatBot.Alerts.Excludes_File=List of words/strings to NOT alert you on, e.g. "" +config.ChatBot.Alerts.Log_To_File=Log alerts info a file. +config.ChatBot.Alerts.Log_File=The name of a file where alers logs will be written. + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk=Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection\n# /!\ Make sure your server rules do not forbid anti-AFK mechanisms!\n# /!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5) +config.ChatBot.AntiAfk.Delay=10 = 1s (Can also be a random number between 2 numbers, example: 50-600) (Default: 600) +config.ChatBot.AntiAfk.Command=Command to send to the server +config.ChatBot.AntiAfk.Use_Terrain_Handling=Use terrain handling to enable the bot to move around +config.ChatBot.AntiAfk.Walk_Range=The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be) +config.ChatBot.AntiAfk.Walk_Retries=How many timec can the bot fail trying to move before using the command method + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack=Automatically attack hostile mobs around you\n# You need to enable Entity Handling to use this bot\n# /!\ Make sure server rules allow your planned use of AutoAttack\n# /!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES! +config.ChatBot.AutoAttack.Mode=single or multi. single target one mob per attack. multi target all mobs in range per attack +config.ChatBot.AutoAttack.Priority=health or distance. Only needed when using single mode +config.ChatBot.AutoAttack.Cooldown_Time=How long to wait between each attack. Use auto to let MCC calculate it +config.ChatBot.AutoAttack.Interaction=Possible values: Interact, Attack (default), InteractAt (Interact and Attack) +config.ChatBot.AutoAttack.Attack_Hostile=Allow attacking hostile mobs +config.ChatBot.AutoAttack.Attack_Passive=Allow attacking passive mobs +config.ChatBot.AutoAttack.List_Mode=Wether to treat the entities list as a whitelist or as a blacklist +config.ChatBot.AutoAttack.Entites_List=All entity types can be found here: https://bit.ly/3Rg68lp + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft=Automatically craft items in your inventory\n# See README > 'Using the AutoCraft bot' for how to use\n# You need to enable Inventory Handling to use this bot\n# You should also enable Terrain and Movements if you need to use a crafting table + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop=Automatically drop items in inventory\n# You need to enable Inventory Handling to use this bot\n# See this file for an up-to-date list of item types you can use with this bot:\n# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs +config.ChatBot.AutoDrop.Mode=include, exclude or everything. Include: drop item IN the list. Exclude: drop item NOT IN the list +config.ChatBot.AutoDrop.Items=separate each item name with a comma ',': "ItemOne", "ItemTwo" + +# ChatBot.AutoEat +config.ChatBot.AutoEat=Automatically eat food when your Hunger value is low\n# You need to enable Inventory Handling to use this bot + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing=Automatically catch fish using a fishing rod\n# Guide: https://mccteam.github.io/guide/chat-bots.html#auto-fishing\n# /!\ Make sure server rules allow automated farming before using this bot +config.ChatBot.AutoFishing.Antidespawn=Keep it as false if you have not changed it before. +config.ChatBot.AutoFishing.Mainhand=Use the main hand or the off hand to hold the rod. +config.ChatBot.AutoFishing.Auto_Start=Whether to start fishing automatically after entering a world. +config.ChatBot.AutoFishing.Cast_Delay=How soon to re-cast after successful fishing. +config.ChatBot.AutoFishing.Fishing_Delay=How long after entering the game to start fishing (seconds). +config.ChatBot.AutoFishing.Fishing_Timeout=Fishing timeout (seconds). Timeout will trigger a re-cast. +config.ChatBot.AutoFishing.Durability_Limit=Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature. +config.ChatBot.AutoFishing.Auto_Rod_Switch=Switch to a new rod from inventory after the current rod is unavailable. +config.ChatBot.AutoFishing.Stationary_Threshold=Hooks moving in the X and Z axes below this threshold will be considered stationary. +config.ChatBot.AutoFishing.Hook_Threshold=A stationary hook moving on the Y-axis above this threshold will be considered to have caught a fish. +config.ChatBot.AutoFishing.Log_Fish_Bobber=For debugging purposes, you can use this log to adjust the two thresholds mentioned above. +config.ChatBot.AutoFishing.Movements=Some plugins do not allow the player to fish in one place. This allows the player to change position/angle after each fish caught. Usage can be found in the Guide. + + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog=Automatically relog when disconnected by server, for example because the server is restating\n# Put keywords/strings such as "Server is restarting" in kick messages file to relog when the message is seen\n# /!\ Use ignorekickmessage=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks +config.ChatBot.AutoRelog.Delay=use 10 for 10 seconds, 10-60 for a random delay between 10 and 60 seconds +config.ChatBot.AutoRelog.Retries=retries when failing to relog to the server. use -1 for unlimited retries +config.ChatBot.AutoRelog.Ignore_Kick_Message=when set to true, autorelog will reconnect regardless of kick messages +config.ChatBot.AutoRelog.Kick_Messages_File=file with list of matches in kick messages that will trigger autorelog + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond=Run commands or send messages automatically when a specified pattern is detected in chat\n# /!\ Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules\n# /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam +config.ChatBot.AutoRespond.Match_Colors=Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work) + +# ChatBot.ChatLog +config.ChatBot.ChatLog=Logs chat messages in a file on disk. + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer=Enabled you to make the bot follow you\n# NOTE: This is an experimental feature, the bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you\n# It's similar to making animals follow you when you're holding food in your hand.\n# This is due to a slow pathfinding algorithm, we're working on getting a better one\n# You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite,\n# this might clog the thread for terain handling) and thus slow the bot even more.\n# /!\ Make sure server rules allow an option like this in the rules of the server before using this bot +config.ChatBot.FollowPlayer.Update_Limit=The rate at which the bot does calculations (10 = 1s) (Default: 5) (You can tweak this if you feel the bot is too slow) +config.ChatBot.FollowPlayer.Stop_At_Distance=Do not follow the player if he is in the range of 3 blocks (prevents the bot from pushing a player in an infinite loop) + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame=A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time.\n# You need to have ChatFormat working correctly and add yourself in botowners to start the game with /tell start\n# /!\ This bot may get a bit spammy if many players are interacting with it + +# ChatBot.Mailer +config.ChatBot.Mailer=Relay messages between players and servers, like a mail plugin\n# This bot can store messages when the recipients are offline, and send them when they join the server\n# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins + +# ChatBot.Map +config.ChatBot.Map=Allows you to render maps into .jpg images\n# This is useful for solving captchas which use maps\n# The maps are rendered into Rendered_Maps folder.\n# NOTE:\n# This feature is currently only useful for solving captchas which use maps.\n# If some servers have a very short time for solving captchas, enabe auto_render_on_update and prepare to open the file quickly.\n# On linux you can use FTP to access generated files.\n# In the future it might will be possible to display maps directly in the console with a separate command.\n# /!\ Make sure server rules allow bots to be used on the server, or you risk being punished. +config.ChatBot.Map.Should_Resize=Should the map be resized? (Default one is small 128x128) +config.ChatBot.Map.Resize_To=The size to resize the map to (Note: the bigger it is, the lower the quallity is) +config.ChatBot.Map.Auto_Render_On_Update=Automatically render the map once it's received or updated from/by the server +config.ChatBot.Map.Delete_All_On_Unload=Delete all rendered maps on unload/reload (Does not delete the images if you exit the client) +config.ChatBot.Map.Notify_On_First_Update=Get a notification when you have gotten a map from the server for the first time + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger=Log the list of players periodically into a textual file +config.ChatBot.PlayerListLogger.Delay=10 = 1s + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl=Send MCC console commands to your bot through server PMs (/tell)\n# You need to have ChatFormat working correctly and add yourself in botowners to use the bot\n# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture=Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/)\n# Please note that due to technical limitations, the client player (you) will not be shown in the replay file\n# /!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT! +config.ChatBot.ReplayCapture.Backup_Interval=How long should replay file be auto-saved, in seconds. Use -1 to disable + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler=Schedule commands and scripts to launch on various events such as server join, date/time or time interval\n# See README > 'Using the Script Scheduler' for more info diff --git a/MinecraftClient/Resources/lang/zh-CHS.ini b/MinecraftClient/Resources/lang/zh-Hans.ini similarity index 94% rename from MinecraftClient/Resources/lang/zh-CHS.ini rename to MinecraftClient/Resources/lang/zh-Hans.ini index 24417e4f..b9c78242 100644 --- a/MinecraftClient/Resources/lang/zh-CHS.ini +++ b/MinecraftClient/Resources/lang/zh-Hans.ini @@ -1,5 +1,8 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate=帮助我们翻译MCC:https://mccteam.github.io/guide/translation.html +mcc.run_with_default_settings=\nMCC正在使用默认配置运行。 +mcc.settings_generated=§c配置文件 MinecraftClient.ini 已经生成。 mcc.login=登录: mcc.login_basic_io=请输入用户名或邮箱。 mcc.password=密码: @@ -30,7 +33,7 @@ mcc.single_cmd=§7已发送命令§8 {0} mcc.joined=已成功加入服务器。\n输入 '{0}quit' 离开服务器。 mcc.reconnect=等待5秒 (剩余{0}次尝试)... mcc.disconnect.lost=失去连接。 -mcc.disconnect.server=从服务器断开: +mcc.disconnect.server=从服务器断开连接: mcc.disconnect.login=连接失败: mcc.link=链接:{0} mcc.player_dead_respawn=你死了!1秒后自动重生。 @@ -65,7 +68,7 @@ error.forgeforce=无法为此Minecraft版本强制启动Forge支持! error.login=登录失败: error.login.migrated=帐户已迁移,使用电子邮件作为用户名。 error.login.server=登录服务器不可用。请稍后再试。 -error.login.blocked=密码错误、IP被禁用或登录次数过多。 +error.login.blocked=用户名/密码错误、IP被禁用或登录次数过多。 error.login.response=服务器响应无效。 error.login.premium=不是Premium用户。 error.login.network=网络错误。 @@ -528,3 +531,18 @@ bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n trigge # TestBot bot.testBot.told=Bot:{0}对我说:{1} bot.testBot.said=Bot:{0}说:{1} + +[config] + +config.load=已从 {0} 加载设置。 +config.load.fail=§c加载设置时出错:§r +config.saving=§a当前设置已保存至 {0} + +# Head +config.Head=启动配置文件\n\n# 对 MCC(Minecraft 命令行客户端)不熟悉?请看 README 和示例配置文件:\n# https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/config\n\n# 想升级到较新的版本吗?请访问 https://github.com/MCCTeam/Minecraft-Console-Client/#download + +# Main.General +config.Main.General.account=Login请填写邮箱或玩家名称。若要以离线模式登录请使用"-"作为密码。若留空则使用交互式登录。 +config.Main.General.login=游戏服务器的地址和端口,可填入域名或IP地址。(可删除端口字段,会解析SRV记录) +config.Main.General.server_info=帐户类型:mojang 或是 microsoft。此项设置也会影响交互式登录。 +config.Main.General.method=微软账户的登录方式:mcc 或是 browser(手动在网页上登录)。 diff --git a/MinecraftClient/Scripting/CSharpRunner.cs b/MinecraftClient/Scripting/CSharpRunner.cs index 65ea78a3..c3dd20f5 100644 --- a/MinecraftClient/Scripting/CSharpRunner.cs +++ b/MinecraftClient/Scripting/CSharpRunner.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; using DynamicRun.Builder; +using static MinecraftClient.Settings; namespace MinecraftClient { @@ -43,7 +44,7 @@ namespace MinecraftClient lock (CompileCache) { ///Process and compile script only if not already compiled - if (!Settings.CacheScripts || !CompileCache.ContainsKey(scriptHash)) + if (!Config.Main.Advanced.CacheScript || !CompileCache.ContainsKey(scriptHash)) { //Process different sections of the script file bool scriptMain = true; @@ -111,10 +112,10 @@ namespace MinecraftClient //Retrieve compiled assembly assembly = result.Assembly; - if (Settings.CacheScripts) + if (Config.Main.Advanced.CacheScript) CompileCache[scriptHash] = assembly!; } - else if (Settings.CacheScripts) + else if (Config.Main.Advanced.CacheScript) assembly = CompileCache[scriptHash]; } @@ -293,7 +294,7 @@ namespace MinecraftClient if (localVars != null && localVars.ContainsKey(varName)) return localVars[varName]; else - return Settings.GetVar(varName); + return Settings.Config.AppVar.GetVar(varName); } /// @@ -305,7 +306,7 @@ namespace MinecraftClient { if (localVars != null && localVars.ContainsKey(varName)) localVars.Remove(varName); - return Settings.SetVar(varName, varValue); + return Settings.Config.AppVar.SetVar(varName, varValue); } /// @@ -347,7 +348,7 @@ namespace MinecraftClient /// True if the account was found and loaded public bool SetAccount(string accountAlias, bool andReconnect = false) { - bool result = Settings.SetAccount(accountAlias); + bool result = Settings.Config.Main.Advanced.SetAccount(accountAlias); if (result && andReconnect) ReconnectToTheServer(); return result; @@ -360,7 +361,7 @@ namespace MinecraftClient /// True if the server IP was valid and loaded, false otherwise public bool SetServer(string server, bool andReconnect = false) { - bool result = Settings.SetServerIP(server); + bool result = Settings.Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(server), true); if (result && andReconnect) ReconnectToTheServer(); return result; diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 4007155d..51f04829 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.RegularExpressions; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using static MinecraftClient.Settings; namespace MinecraftClient { @@ -521,9 +522,9 @@ namespace MinecraftClient text = GetVerbatim(text); //User-defined regex for private chat messages - if (Settings.ChatFormat_Private != null) + if (Config.ChatFormat.Private != null) { - Match regexMatch = Settings.ChatFormat_Private.Match(text); + Match regexMatch = new Regex(Config.ChatFormat.Private).Match(text); if (regexMatch.Success && regexMatch.Groups.Count >= 3) { sender = regexMatch.Groups[1].Value; @@ -533,7 +534,7 @@ namespace MinecraftClient } //Built-in detection routine for private messages - if (Settings.ChatFormat_Builtins) + if (Config.ChatFormat.Builtins) { string[] tmp = text.Split(' '); try @@ -632,9 +633,9 @@ namespace MinecraftClient text = GetVerbatim(text); //User-defined regex for public chat messages - if (Settings.ChatFormat_Public != null) + if (Config.ChatFormat.Public != null) { - Match regexMatch = Settings.ChatFormat_Public.Match(text); + Match regexMatch = new Regex(Config.ChatFormat.Public).Match(text); if (regexMatch.Success && regexMatch.Groups.Count >= 3) { sender = regexMatch.Groups[1].Value; @@ -644,7 +645,7 @@ namespace MinecraftClient } //Built-in detection routine for public messages - if (Settings.ChatFormat_Builtins) + if (Config.ChatFormat.Builtins) { string[] tmp = text.Split(' '); @@ -735,9 +736,9 @@ namespace MinecraftClient text = GetVerbatim(text); //User-defined regex for teleport requests - if (Settings.ChatFormat_TeleportRequest != null) + if (Config.ChatFormat.TeleportRequest != null) { - Match regexMatch = Settings.ChatFormat_TeleportRequest.Match(text); + Match regexMatch = new Regex(Config.ChatFormat.TeleportRequest).Match(text); if (regexMatch.Success && regexMatch.Groups.Count >= 2) { sender = regexMatch.Groups[1].Value; @@ -746,7 +747,7 @@ namespace MinecraftClient } //Built-in detection routine for teleport requests - if (Settings.ChatFormat_Builtins) + if (Config.ChatFormat.Builtins) { string[] tmp = text.Split(' '); @@ -786,7 +787,26 @@ namespace MinecraftClient ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", GetType().Name, text)); else Handler.Log.Info(String.Format("[{0}] {1}", GetType().Name, text)); - string logfile = Settings.ExpandVars(Settings.chatbotLogFile); + string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); + + if (!String.IsNullOrEmpty(logfile)) + { + if (!File.Exists(logfile)) + { + try { Directory.CreateDirectory(Path.GetDirectoryName(logfile)!); } + catch { return; /* Invalid path or access denied */ } + try { File.WriteAllText(logfile, ""); } + catch { return; /* Invalid file name or access denied */ } + } + + File.AppendAllLines(logfile, new string[] { GetTimestamp() + ' ' + text }); + } + } + + protected static void LogToConsole(string botName, object? text) + { + ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text)); + string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); if (!String.IsNullOrEmpty(logfile)) { @@ -808,7 +828,7 @@ namespace MinecraftClient /// Debug log text to write protected void LogDebugToConsole(object text) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) LogToConsole(text); } @@ -840,7 +860,7 @@ namespace MinecraftClient /// Optional delay, in seconds, before restarting protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0) { - if (Settings.DebugMessages) + if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", GetType().Name)); McClient.ReconnectionAttemptsLeft = ExtraAttempts; Program.Restart(delaySeconds); @@ -873,7 +893,7 @@ namespace MinecraftClient /// Message protected void SendPrivateMessage(string player, string message) { - SendText(String.Format("/{0} {1} {2}", Settings.PrivateMsgsCmdName, player, message)); + SendText(String.Format("/{0} {1} {2}", Config.Main.Advanced.PrivateMsgsCmdName, player, message)); } /// diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index e2bce54c..8c3bcdee 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -1,330 +1,199 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using MinecraftClient.Mapping; +using System.Threading; using MinecraftClient.Protocol; -using MinecraftClient.Protocol.Session; +using MinecraftClient.Proxy; +using Tomlet; +using Tomlet.Attributes; +using Tomlet.Models; +using static MinecraftClient.Settings.AppVarConfigHelper; +using static MinecraftClient.Settings.ChatBotConfigHealper; +using static MinecraftClient.Settings.ChatFormatConfigHelper; +using static MinecraftClient.Settings.LoggingConfigHealper; +using static MinecraftClient.Settings.MainConfigHealper; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; +using static MinecraftClient.Settings.MCSettingsConfigHealper; +using static MinecraftClient.Settings.SignatureConfigHelper; namespace MinecraftClient { - /// - /// Contains main settings for Minecraft Console Client - /// Allows settings loading from an INI file - /// - public static class Settings { - //Minecraft Console Client client information used for BrandInfo setting - private const string MCCBrandInfo = "Minecraft-Console-Client/" + Program.Version; + // Minecraft Console Client client information used for BrandInfo setting + public const string MCCBrandInfo = "Minecraft-Console-Client/" + Program.Version; - //Main Settings. - //Login: Username or email adress used as login for Minecraft/Mojang account - //Username: The actual username of the user, obtained after login to the account - public static string Login = ""; - public static string Username = ""; - public static string Password = ""; - public static ProtocolHandler.AccountType AccountType = ProtocolHandler.AccountType.Mojang; - public static string LoginMethod = "mcc"; - public static string ServerIP = ""; - public static ushort ServerPort = 25565; - public static string ServerVersion = ""; - public static bool ServerForceForge = false; - public static bool ServerAutodetectForge = true; - public static string SingleCommand = ""; - public static string ConsoleTitle = ""; + //Other Settings + public static string TranslationsFile_FromMCDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\.minecraft\assets\objects\48\482e0dae05abfa35ab5cb076e41fda77b4fb9a08"; //MC 1.19 en_GB.lang + public static string TranslationsFile_Website_Index = "https://piston-meta.mojang.com/v1/packages/b5c7548ddb9e584e84a5f762da5b78211c715a63/1.19.json"; + public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net"; - //Proxy Settings - public static bool ProxyEnabledLogin = false; - public static bool ProxyEnabledIngame = false; - public static string ProxyHost = ""; - public static int ProxyPort = 0; - public static Proxy.ProxyHandler.Type proxyType = Proxy.ProxyHandler.Type.HTTP; - public static string ProxyUsername = ""; - public static string ProxyPassword = ""; + public static GlobalConfig Config = new(); - //Minecraft Settings - public static bool MCSettings_Enabled = true; - public static string MCSettings_Locale = "en_US"; - public static byte MCSettings_Difficulty = 0; - public static byte MCSettings_RenderDistance = 8; - public static byte MCSettings_ChatMode = 0; - public static bool MCSettings_ChatColors = true; - public static byte MCSettings_MainHand = 0; - public static bool MCSettings_Skin_Hat = true; - public static bool MCSettings_Skin_Cape = true; - public static bool MCSettings_Skin_Jacket = false; - public static bool MCSettings_Skin_Sleeve_Left = false; - public static bool MCSettings_Skin_Sleeve_Right = false; - public static bool MCSettings_Skin_Pants_Left = false; - public static bool MCSettings_Skin_Pants_Right = false; - public static byte MCSettings_Skin_All + public static class InternalConfig { - get + public static string ServerIP = String.Empty; + + public static ushort ServerPort = 25565; + + public static string Username = string.Empty; + + public static string Password = string.Empty; + + public static string MinecraftVersion = string.Empty; + + public static bool InteractiveMode = true; + + public static bool GravityEnabled = true; + } + + public class GlobalConfig + { + public HeadComment Head = new(); + + public MainConfig Main { - return (byte)( - ((MCSettings_Skin_Cape ? 1 : 0) << 0) - | ((MCSettings_Skin_Jacket ? 1 : 0) << 1) - | ((MCSettings_Skin_Sleeve_Left ? 1 : 0) << 2) - | ((MCSettings_Skin_Sleeve_Right ? 1 : 0) << 3) - | ((MCSettings_Skin_Pants_Left ? 1 : 0) << 4) - | ((MCSettings_Skin_Pants_Right ? 1 : 0) << 5) - | ((MCSettings_Skin_Hat ? 1 : 0) << 6) - ); + get { return MainConfigHealper.Config; } + set { MainConfigHealper.Config = value; MainConfigHealper.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.Signature$")] + public SignatureConfig Signature + { + get { return SignatureConfigHelper.Config; } + set { SignatureConfigHelper.Config = value; SignatureConfigHelper.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.Logging$")] + public LoggingConfig Logging + { + get { return LoggingConfigHealper.Config; } + set { LoggingConfigHealper.Config = value; LoggingConfigHealper.Config.OnSettingUpdate(); } + } + + public AppVarConfig AppVar + { + get { return AppVarConfigHelper.Config; } + set { AppVarConfigHelper.Config = value; AppVarConfigHelper.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.Proxy$")] + public ProxyHandler.Configs Proxy + { + get { return ProxyHandler.Config; } + set { ProxyHandler.Config = value; ProxyHandler.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.MCSettings$")] + public MCSettingsConfig MCSettings + { + get { return MCSettingsConfigHealper.Config; } + set { MCSettingsConfigHealper.Config = value; MCSettingsConfigHealper.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatFormat$")] + public ChatFormatConfig ChatFormat + { + get { return ChatFormatConfigHelper.Config; } + set { ChatFormatConfigHelper.Config = value; ChatFormatConfigHelper.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot$")] + public ChatBotConfig ChatBot + { + get { return ChatBotConfigHealper.Config; } + set { ChatBotConfigHealper.Config = value; } + } + + [TomlDoNotInlineObject] + public class HeadComment + { + [TomlPrecedingComment("$config.Head$")] + [TomlProperty("Current Version (Do not edit this)")] + public string Version { get; set; } = Program.BuildInfo ?? Program.MCHighestVersion; } } - //Other Settings - public static string TranslationsFile_FromMCDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\.minecraft\assets\objects\8b\8bf1298bd44b0e5b21d747394a8acd2c218e09ed"; //MC 1.17 en_GB.lang - public static string TranslationsFile_Website_Index = "https://launchermeta.mojang.com/v1/packages/e5af543d9b3ce1c063a97842c38e50e29f961f00/1.17.json"; - public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net"; - public static TimeSpan messageCooldown = TimeSpan.FromSeconds(2); - public static List Bots_Owners = new(); - public static string Language = "en_GB"; - public static bool interactiveMode = true; - public static char internalCmdChar = '/'; - public static bool playerHeadAsIcon = false; - public static string chatbotLogFile = ""; - public static bool CacheScripts = true; - public static string? BrandInfo = MCCBrandInfo; - public static bool DisplaySystemMessages = true; - public static bool DisplayXPBarMessages = true; - public static bool DisplayChatLinks = true; - public static bool DisplayInventoryLayout = true; - public static bool TerrainAndMovements = false; - public static bool GravityEnabled = true; - public static bool InventoryHandling = false; - public static string PrivateMsgsCmdName = "tell"; - public static CacheType SessionCaching = CacheType.Disk; - public static CacheType ProfileKeyCaching = CacheType.Disk; - public static bool ResolveSrvRecords = true; - public static bool ResolveSrvRecordsShortTimeout = true; - public static bool EntityHandling = false; - public static bool AutoRespawn = false; - public static bool MinecraftRealmsEnabled = true; - public static bool MoveHeadWhileWalking = true; - public static int Timeout = 30; - public static bool EnableEmoji = true; - public static int MovementSpeed = 2; - - // Signature - public static bool LoginWithSecureProfile = true; - public static bool SignChat = true; - public static bool SignMessageInCommand = true; - public static bool MarkLegallySignedMsg = false; - public static bool MarkModifiedMsg = true; - public static bool MarkIllegallySignedMsg = true; - public static bool MarkSystemMessage = false; - public static bool ShowModifiedChat = true; - public static bool ShowIllegalSignedChat = true; - - // Logging - public enum FilterModeEnum { Blacklist, Whitelist } - public static bool DebugMessages = false; - public static bool ChatMessages = true; - public static bool InfoMessages = true; - public static bool WarningMessages = true; - public static bool ErrorMessages = true; - public static Regex? ChatFilter = null; - public static Regex? DebugFilter = null; - public static FilterModeEnum FilterMode = FilterModeEnum.Blacklist; - public static bool LogToFile = false; - public static string LogFile = "console-log.txt"; - public static bool PrependTimestamp = false; - public static bool SaveColorCodes = false; - - //AntiAFK Settings - public static bool AntiAFK_Enabled = false; - public static string AntiAFK_Delay = "600"; - public static string AntiAFK_Command = "/ping"; - public static bool AntiAFK_UseTerrain_Handling = false; - public static int AntiAFK_Walk_Range = 5; - public static int AntiAFK_Walk_Retries = 20; - - //Hangman Settings - public static bool Hangman_Enabled = false; - public static bool Hangman_English = true; - public static string Hangman_FileWords_EN = "hangman-en.txt"; - public static string Hangman_FileWords_FR = "hangman-fr.txt"; - - //Alerts Settings - public static bool Alerts_Enabled = false; - public static bool Alerts_Trigger_By_Words = false; - public static bool Alerts_Trigger_By_Rain = false; - public static bool Alerts_Trigger_By_Thunderstorm = false; - public static bool Alerts_Beep_Enabled = true; - public static bool Alerts_File_Logging = false; - public static string Alerts_MatchesFile = "alerts.txt"; - public static string Alerts_ExcludesFile = "alerts-exclude.txt"; - public static string Alerts_LogFile = "alerts-log.txt"; - - //ChatLog Settings - public static bool ChatLog_Enabled = false; - public static bool ChatLog_DateTime = true; - public static string ChatLog_File = "chatlog.txt"; - public static ChatBots.ChatLog.MessageFilter ChatLog_Filter = ChatBots.ChatLog.MessageFilter.AllMessages; - - //PlayerListLog Settings - public static bool PlayerLog_Enabled = false; - public static string PlayerLog_File = "playerlog.txt"; - public static int PlayerLog_Delay = 600; - - //AutoRelog Settings - public static bool AutoRelog_Enabled = false; - public static int AutoRelog_Delay_Min = 10; - public static int AutoRelog_Delay_Max = 10; - public static int AutoRelog_Retries = 3; - public static bool AutoRelog_IgnoreKickMessage = false; - public static string AutoRelog_KickMessagesFile = "kickmessages.txt"; - - //Script Scheduler Settings - public static bool ScriptScheduler_Enabled = false; - public static string ScriptScheduler_TasksFile = "tasks.ini"; - - //Remote Control - public static bool RemoteCtrl_Enabled = false; - public static bool RemoteCtrl_AutoTpaccept = true; - public static bool RemoteCtrl_AutoTpaccept_Everyone = false; - - //Chat Message Parsing - public static bool ChatFormat_Builtins = true; - public static Regex? ChatFormat_Public = null; - public static Regex? ChatFormat_Private = null; - public static Regex? ChatFormat_TeleportRequest = null; - - //Auto Respond - public static bool AutoRespond_Enabled = false; - public static string AutoRespond_Matches = "matches.ini"; - public static bool AutoRespond_MatchColors = false; - - //Auto Attack - public static bool AutoAttack_Enabled = false; - public static string AutoAttack_Mode = "single"; - public static string AutoAttack_Priority = "distance"; - public static bool AutoAttack_OverrideAttackSpeed = false; - public static double AutoAttack_CooldownSeconds = 1; - public static InteractType AutoAttack_Interaction = InteractType.Attack; - public static bool AutoAttack_Attack_Hostile = true; - public static bool AutoAttack_Attack_Passive = false; - public static string AutoAttack_ListMode = "blacklist"; - public static string AutoAttack_ListFile = "autoattack-list.txt"; - - //Auto Fishing - public static bool AutoFishing_Enabled = false; - public static bool AutoFishing_Antidespawn = false; - public static bool AutoFishing_Mainhand = true; - public static bool AutoFishing_AutoStart = true; - public static double AutoFishing_CastDelay = 0.4; - public static double AutoFishing_FishingDelay = 3.0; - public static double AutoFishing_FishingTimeout = 300.0; - public static double AutoFishing_DurabilityLimit = 2; - public static bool AutoFishing_AutoRodSwitch = true; - public static double AutoFishing_StationaryThreshold = 0.001; - public static double AutoFishing_HookThreshold = 0.2; - public static bool AutoFishing_LogFishingBobber = false; - public static double[,]? AutoFishing_Location = null; - - //Auto Eating - public static bool AutoEat_Enabled = false; - public static int AutoEat_hungerThreshold = 6; - - //AutoCraft - public static bool AutoCraft_Enabled = false; - public static string AutoCraft_configFile = @"autocraft\config.ini"; - - //Mailer - public static bool Mailer_Enabled = false; - public static string Mailer_DatabaseFile = "MailerDatabase.ini"; - public static string Mailer_IgnoreListFile = "MailerIgnoreList.ini"; - public static bool Mailer_PublicInteractions = false; - public static int Mailer_MaxMailsPerPlayer = 10; - public static int Mailer_MaxDatabaseSize = 10000; - public static int Mailer_MailRetentionDays = 30; - - //AutoDrop - public static bool AutoDrop_Enabled = false; - public static string AutoDrop_Mode = "include"; - public static string AutoDrop_items = ""; - - // Replay Mod - public static bool ReplayMod_Enabled = false; - public static int ReplayMod_BackupInterval = 3000; - - // Follow Player - public static bool FollowPlayer_Enabled = false; - public static int FollowPlayer_UpdateLimit = 10; - public static int FollowPlayer_StopAtDistance = 3; - - // Map - public static bool Map_Enabled = false; - public static bool Map_Should_Resize = false; - public static int Map_Resize_To = 256; - public static bool Map_Auto_Render_On_Update = false; - public static bool Map_Delete_All_On_Unload = true; - public static bool Map_Notify_On_First_Update = true; - - //Custom app variables and Minecraft accounts - private static readonly Dictionary AppVars = new(); - private static readonly Dictionary> Accounts = new(); - private static readonly Dictionary> Servers = new(); - - //Temporary Server Alias storage when server list is not loaded yet - private static string? ServerAliasTemp = null; - - //Mapping for settings sections in the INI file - private enum Section { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer, ReplayMod, FollowPlayer, PlayerListLogger, Map, Logging, Signature }; - - /// - /// Get settings section from name - /// - /// Section name - /// Section enum - private static Section GetSection(string name) + public static bool LoadFromFile(string filepath) { - if (Enum.TryParse(name, true, out Section pMode)) - return pMode; - return Section.Default; - } - - /// - /// Load settings from the given INI file - /// - /// File to load - public static void LoadFile(string file) - { - Task.Factory.StartNew(() => ConsoleIO.WriteLogLine("[Settings] Loading Settings from " + Path.GetFullPath(file))); - if (File.Exists(file)) + TomlDocument document; + try + { + document = TomlParser.ParseFile(filepath); + } + catch (Exception ex) { try { - Section section = Section.Default; - foreach (var lineRAW in File.ReadLines(file)) + // The old configuration file has been backed up as A. + string configString = File.ReadAllText(filepath); + if (configString.Contains("Some settings missing here after an upgrade?")) { - string line = lineRAW.Split('#')[0].Trim(); - if (line.Length > 1) - { - if (line.Length > 2 && line[0] == '[' && line[^1] == ']') - section = GetSection(line[1..^1]); - else - { - string argName = line.Split('=')[0]; - if (section == Section.Main && argName == "password") - line = lineRAW.Trim(); //Do not strip # in passwords - if (line.Length > (argName.Length + 1)) - { - string argValue = line[(argName.Length + 1)..]; - LoadSingleSetting(section, argName, argValue); - } - } - } + string newFilePath = Path.ChangeExtension(filepath, ".backup.ini"); + File.Copy(filepath, newFilePath, true); + ConsoleIO.WriteLineFormatted("§cPlease use the newly generated MinecraftClient.ini"); + ConsoleIO.WriteLineFormatted("§cThe old MinecraftClient.ini has been backed up as " + newFilePath); + return true; } } - catch (IOException) { } + catch { } + ConsoleIO.WriteLineFormatted(Translations.GetOrNull("config.load.fail") ?? "§cFailed to load settings:§r"); + ConsoleIO.WriteLine(ex.Message); + ConsoleIO.WriteLine(Translations.GetOrNull("mcc.run_with_default_settings") ?? "\nMCC is running with default settings."); + return false; + } + + Config = TomletMain.To(document); + + return false; + } + + public static void WriteToFile(string filepath, bool backupOldFile) + { + string tomlString = TomletMain.TomlStringFrom(Config); + + string[] tomlList = tomlString.Split('\n'); + StringBuilder newConfig = new(); + foreach (string line in tomlList) + { + Match match = Regex.Match(line, @"^(.*)\s?#\s\$(.+)\$\s*$"); + if (match.Success && match.Groups.Count == 3) + { + string config = match.Groups[1].Value, comment = match.Groups[2].Value; + if (config.Length > 0) + newConfig.Append(config).Append(' ', Math.Max(1, 45 - config.Length) - 1); + newConfig.Append("# ").AppendLine(Translations.TryGet(comment).ReplaceLineEndings()); + } + else + { + newConfig.AppendLine(line); + } + } + + bool needUpdate = true; + string newConfigStr = newConfig.ToString(); + if (File.Exists(filepath)) + { + try + { + string oldConfigStr = File.ReadAllText(filepath); + if (oldConfigStr == newConfigStr) + needUpdate = false; + } + catch { } + } + + if (needUpdate) + { + if (backupOldFile && File.Exists(filepath)) + File.Copy(filepath, Path.ChangeExtension(filepath, ".backup.ini"), true); + File.WriteAllText(filepath, newConfigStr); } } @@ -344,19 +213,7 @@ namespace MinecraftClient //Load settings as --setting=value and --section.setting=value if (!argument.Contains('=')) throw new ArgumentException(Translations.Get("error.setting.argument_syntax", argument)); - Section section = Section.Main; - string argName = argument[2..].Split('=')[0]; - string argValue = argument[(argName.Length + 3)..]; - if (argName.Contains('.')) - { - string sectionName = argName.Split('.')[0]; - section = GetSection(sectionName); - if (section == Section.Default) - throw new ArgumentException(Translations.Get("error.setting.unknown_section", argument, sectionName)); - argName = argName.Split('.')[1]; - } - if (!LoadSingleSetting(section, argName, argValue)) - throw new ArgumentException(Translations.Get("error.setting.unknown_or_invalid", argument)); + throw new NotImplementedException(); } else if (argument.StartsWith("-") && argument.Length > 1) { @@ -367,679 +224,830 @@ namespace MinecraftClient { switch (positionalIndex) { - case 0: Login = argument; break; - case 1: Password = argument; break; - case 2: if (!SetServerIP(argument)) ServerAliasTemp = argument; break; - case 3: SingleCommand = argument; break; + case 0: + Config.Main.General.Account.Login = argument; + break; + case 1: + InternalConfig.Password = argument; + break; + case 2: + Config.Main.SetServerIP(new MainConfig.ServerInfoConfig(argument), true); + break; + case 3: + // SingleCommand = argument; + break; } positionalIndex++; } } } - /// - /// Load a single setting from INI file or command-line argument - /// - /// Settings section - /// Setting name - /// Setting value - /// TRUE if setting was valid - private static bool LoadSingleSetting(Section section, string argName, string argValue) + public static class MainConfigHealper { - switch (section) + public static MainConfig Config = new(); + + [TomlDoNotInlineObject] + public class MainConfig { - case Section.Main: - switch (ToLowerIfNeed(argName)) - { - case "login": Login = argValue; return true; - case "password": Password = argValue; return true; - case "type": - AccountType = argValue == "mojang" - ? ProtocolHandler.AccountType.Mojang - : ProtocolHandler.AccountType.Microsoft; return true; - case "method": - argValue = ToLowerIfNeed(argValue); - LoginMethod = argValue == "browser" ? "browser" : "mcc"; return true; - case "serverip": if (!SetServerIP(argValue)) ServerAliasTemp = argValue; return true; - case "singlecommand": SingleCommand = argValue; return true; - case "language": Language = argValue; return true; - case "consoletitle": ConsoleTitle = argValue; return true; - case "timestamps": ConsoleIO.EnableTimestamps = str2bool(argValue); return true; - case "exitonfailure": interactiveMode = !str2bool(argValue); return true; - case "playerheadicon": playerHeadAsIcon = str2bool(argValue); return true; - case "chatbotlogfile": chatbotLogFile = argValue; return true; - case "mcversion": ServerVersion = argValue; return true; - case "messagecooldown": messageCooldown = TimeSpan.FromSeconds(str2int(argValue)); return true; - case "scriptcache": CacheScripts = str2bool(argValue); return true; - case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); return true; - case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); return true; - case "showchatlinks": DisplayChatLinks = str2bool(argValue); return true; - case "showinventorylayout": DisplayInventoryLayout = str2bool(argValue); return true; - case "terrainandmovements": TerrainAndMovements = str2bool(argValue); return true; - case "entityhandling": EntityHandling = str2bool(argValue); return true; - case "enableentityhandling": EntityHandling = str2bool(argValue); return true; - case "inventoryhandling": InventoryHandling = str2bool(argValue); return true; - case "privatemsgscmdname": PrivateMsgsCmdName = ToLowerIfNeed(argValue).Trim(); return true; - case "autorespawn": AutoRespawn = str2bool(argValue); return true; - // Backward compatible so people can still enable debug with old config format - case "debugmessages": DebugMessages = str2bool(argValue); return true; - case "minecraftrealms": MinecraftRealmsEnabled = str2bool(argValue); return true; - case "moveheadwhilewalking": MoveHeadWhileWalking = str2bool(argValue); return true; - case "timeout": Timeout = str2int(argValue); return true; - case "enableemoji": EnableEmoji = str2bool(argValue); return true; - case "movementspwwd": MovementSpeed = str2int(argValue); return true; + public GeneralConfig General = new(); - case "botowners": - Bots_Owners.Clear(); - string lowerArgValue = ToLowerIfNeed(argValue); - string[] names = lowerArgValue.Split(','); - if (!argValue.Contains(',') && lowerArgValue.EndsWith(".txt") && File.Exists(argValue)) - names = File.ReadAllLines(argValue); - foreach (string name in names) - if (!String.IsNullOrWhiteSpace(name)) - Bots_Owners.Add(name.Trim()); - return true; + [TomlPrecedingComment("$config.Main.Advanced$")] + public AdvancedConfig Advanced = new(); - case "internalcmdchar": - argValue = ToLowerIfNeed(argValue); - switch (argValue) - { - case "none": internalCmdChar = ' '; break; - case "slash": internalCmdChar = '/'; break; - case "backslash": internalCmdChar = '\\'; break; - } - return true; - case "sessioncache": - if (argValue == "none") { SessionCaching = CacheType.None; } - else if (argValue == "memory") { SessionCaching = CacheType.Memory; } - else if (argValue == "disk") { SessionCaching = CacheType.Disk; } - return true; - - case "profilekeycache": - if (argValue == "none") { ProfileKeyCaching = CacheType.None; } - else if (argValue == "memory") { ProfileKeyCaching = CacheType.Memory; } - else if (argValue == "disk") { ProfileKeyCaching = CacheType.Disk; } - return true; - - case "accountlist": - if (File.Exists(argValue)) - { - foreach (string account_line in File.ReadAllLines(argValue)) - { - //Each line contains account data: 'Alias,Login,Password' - string[] account_data = account_line.Split('#')[0].Trim().Split(','); - if (account_data.Length == 3) - Accounts[ToLowerIfNeed(account_data[0])] - = new KeyValuePair(account_data[1], account_data[2]); - } - - //Try user value against aliases after load - SetAccount(Login); - } - return true; - - case "serverlist": - if (File.Exists(argValue)) - { - //Backup current server info - string server_host_temp = ServerIP; - ushort server_port_temp = ServerPort; - - foreach (string server_line in File.ReadAllLines(argValue)) - { - //Each line contains server data: 'Alias,Host:Port' - string[] server_data = server_line.Split('#')[0].Trim().Split(','); - server_data[0] = ToLowerIfNeed(server_data[0]); - if (server_data.Length == 2 - && server_data[0] != "localhost" - && !server_data[0].Contains('.') - && SetServerIP(server_data[1])) - Servers[server_data[0]] - = new KeyValuePair(ServerIP, ServerPort); - } - - //Restore current server info - ServerIP = server_host_temp; - ServerPort = server_port_temp; - - //Try server value against aliases after load - if (!String.IsNullOrEmpty(ServerAliasTemp)) - { - SetServerIP(ServerAliasTemp); - ServerAliasTemp = null; - } - } - return true; - - case "brandinfo": - BrandInfo = ToLowerIfNeed(argValue.Trim()) switch - { - "mcc" => MCCBrandInfo, - "vanilla" => "vanilla", - _ => null, - }; - return true; - - case "resolvesrvrecords": - if (ToLowerIfNeed(argValue.Trim()) == "fast") - { - ResolveSrvRecords = true; - ResolveSrvRecordsShortTimeout = true; - } - else - { - ResolveSrvRecords = str2bool(argValue); - ResolveSrvRecordsShortTimeout = false; - } - return true; - - case "mcforge": - if (ToLowerIfNeed(argValue) == "auto") - { - ServerAutodetectForge = true; - ServerForceForge = false; - } - else - { - ServerAutodetectForge = false; - ServerForceForge = str2bool(argValue); - } - return true; - } - break; - - case Section.Signature: - switch (ToLowerIfNeed(argName)) - { - case "login_with_secure_profile": LoginWithSecureProfile = str2bool(argValue); return true; - case "sign_chat": SignChat = str2bool(argValue); return true; - case "sign_message_in_command": SignMessageInCommand = str2bool(argValue); return true; - case "mark_legally_signed_msg": MarkLegallySignedMsg = str2bool(argValue); return true; - case "mark_modified_msg": MarkModifiedMsg = str2bool(argValue); return true; - case "mark_illegally_signed_msg": MarkIllegallySignedMsg = str2bool(argValue); return true; - case "mark_system_message": MarkSystemMessage = str2bool(argValue); return true; - case "show_modified_chat": ShowModifiedChat = str2bool(argValue); return true; - case "show_illegal_signed_chat": ShowIllegalSignedChat = str2bool(argValue); return true; - } - break; - - case Section.Logging: - switch (ToLowerIfNeed(argName)) - { - case "debugmessages": DebugMessages = str2bool(argValue); return true; - case "chatmessages": ChatMessages = str2bool(argValue); return true; - case "warningmessages": WarningMessages = str2bool(argValue); return true; - case "errormessages": ErrorMessages = str2bool(argValue); return true; - case "infomessages": InfoMessages = str2bool(argValue); return true; - case "chatfilter": ChatFilter = new Regex(argValue); return true; - case "debugfilter": DebugFilter = new Regex(argValue); return true; - case "filtermode": - if (ToLowerIfNeed(argValue).StartsWith("white")) - FilterMode = FilterModeEnum.Whitelist; - else - FilterMode = FilterModeEnum.Blacklist; - return true; - case "logtofile": LogToFile = str2bool(argValue); return true; - case "logfile": LogFile = argValue; return true; - case "prependtimestamp": PrependTimestamp = str2bool(argValue); return true; - case "savecolorcodes": SaveColorCodes = str2bool(argValue); return true; - } - break; - - case Section.Alerts: - switch (ToLowerIfNeed(argName)) - { - case "enabled": Alerts_Enabled = str2bool(argValue); return true; - case "trigger_by_words": Alerts_Trigger_By_Words = str2bool(argValue); return true; - case "trigger_by_rain": Alerts_Trigger_By_Rain = str2bool(argValue); return true; - case "trigger_by_thunderstorm": Alerts_Trigger_By_Thunderstorm = str2bool(argValue); return true; - case "alertsfile": Alerts_MatchesFile = argValue; return true; - case "excludesfile": Alerts_ExcludesFile = argValue; return true; - case "beeponalert": Alerts_Beep_Enabled = str2bool(argValue); return true; - case "logtofile": Alerts_File_Logging = str2bool(argValue); return true; - case "logfile": Alerts_LogFile = argValue; return true; - } - break; - - case Section.AntiAFK: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AntiAFK_Enabled = str2bool(argValue); return true; - case "delay": AntiAFK_Delay = argValue; return true; - case "command": AntiAFK_Command = argValue == "" ? "/ping" : argValue; return true; - case "use_terrain_handling": AntiAFK_UseTerrain_Handling = str2bool(argValue); return true; - case "walk_range": AntiAFK_Walk_Range = str2int(argValue); return true; - case "walk_retries": AntiAFK_Walk_Retries = str2int(argValue); return true; - } - break; - - case Section.AutoRelog: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoRelog_Enabled = str2bool(argValue); return true; - case "retries": AutoRelog_Retries = str2int(argValue); return true; - case "ignorekickmessage": AutoRelog_IgnoreKickMessage = str2bool(argValue); return true; - case "kickmessagesfile": AutoRelog_KickMessagesFile = argValue; return true; - - case "delay": - string[] delayParts = argValue.Split('-'); - if (delayParts.Length == 1) - { - AutoRelog_Delay_Min = str2int(delayParts[0]); - AutoRelog_Delay_Max = AutoRelog_Delay_Min; - } - else - { - AutoRelog_Delay_Min = str2int(delayParts[0]); - AutoRelog_Delay_Max = str2int(delayParts[1]); - } - return true; - } - break; - - case Section.ChatLog: - switch (ToLowerIfNeed(argName)) - { - case "enabled": ChatLog_Enabled = str2bool(argValue); return true; - case "timestamps": ChatLog_DateTime = str2bool(argValue); return true; - case "filter": ChatLog_Filter = ChatBots.ChatLog.str2filter(argValue); return true; - case "logfile": ChatLog_File = argValue; return true; - } - break; - - case Section.Hangman: - switch (ToLowerIfNeed(argName)) - { - case "enabled": Hangman_Enabled = str2bool(argValue); return true; - case "english": Hangman_English = str2bool(argValue); return true; - case "wordsfile": Hangman_FileWords_EN = argValue; return true; - case "fichiermots": Hangman_FileWords_FR = argValue; return true; - } - break; - - case Section.ScriptScheduler: - switch (ToLowerIfNeed(argName)) - { - case "enabled": ScriptScheduler_Enabled = str2bool(argValue); return true; - case "tasksfile": ScriptScheduler_TasksFile = argValue; return true; - } - break; - - case Section.RemoteControl: - switch (ToLowerIfNeed(argName)) - { - case "enabled": RemoteCtrl_Enabled = str2bool(argValue); return true; - case "autotpaccept": RemoteCtrl_AutoTpaccept = str2bool(argValue); return true; - case "tpaccepteveryone": RemoteCtrl_AutoTpaccept_Everyone = str2bool(argValue); return true; - } - break; - - case Section.ChatFormat: - switch (ToLowerIfNeed(argName)) - { - case "builtins": ChatFormat_Builtins = str2bool(argValue); return true; - case "public": ChatFormat_Public = new Regex(argValue); return true; - case "private": ChatFormat_Private = new Regex(argValue); return true; - case "tprequest": ChatFormat_TeleportRequest = new Regex(argValue); return true; - } - break; - - case Section.Proxy: - switch (ToLowerIfNeed(argName)) - { - case "enabled": - ProxyEnabledLogin = ProxyEnabledIngame = str2bool(argValue); - if (ToLowerIfNeed(argValue.Trim()) == "login") - ProxyEnabledLogin = true; - return true; - case "type": - argValue = ToLowerIfNeed(argValue); - if (argValue == "http") { proxyType = Proxy.ProxyHandler.Type.HTTP; } - else if (argValue == "socks4") { proxyType = Proxy.ProxyHandler.Type.SOCKS4; } - else if (argValue == "socks4a") { proxyType = Proxy.ProxyHandler.Type.SOCKS4a; } - else if (argValue == "socks5") { proxyType = Proxy.ProxyHandler.Type.SOCKS5; } - return true; - case "server": - string[] host_splitted = argValue.Split(':'); - if (host_splitted.Length == 1) - { - ProxyHost = host_splitted[0]; - ProxyPort = 80; - } - else if (host_splitted.Length == 2) - { - ProxyHost = host_splitted[0]; - ProxyPort = str2int(host_splitted[1]); - } - return true; - case "username": ProxyUsername = argValue; return true; - case "password": ProxyPassword = argValue; return true; - } - break; - - case Section.AppVars: - SetVar(argName, argValue); - return true; - - case Section.AutoRespond: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoRespond_Enabled = str2bool(argValue); return true; - case "matchesfile": AutoRespond_Matches = argValue; return true; - case "matchcolors": AutoRespond_MatchColors = str2bool(argValue); return true; - } - break; - - case Section.AutoAttack: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoAttack_Enabled = str2bool(argValue); return true; - case "mode": AutoAttack_Mode = ToLowerIfNeed(argValue); return true; - case "priority": AutoAttack_Priority = ToLowerIfNeed(argValue); return true; - case "cooldownseconds": - if (ToLowerIfNeed(argValue) == "auto") - { - AutoAttack_OverrideAttackSpeed = false; - } - else - { - AutoAttack_OverrideAttackSpeed = true; - AutoAttack_CooldownSeconds = str2float(argValue); - } - return true; - case "interaction": - return Enum.TryParse(argValue, true, out AutoAttack_Interaction); - case "attackhostile": - AutoAttack_Attack_Hostile = str2bool(argValue); return true; - case "attackpassive": - AutoAttack_Attack_Passive = str2bool(argValue); return true; - case "listmode": - AutoAttack_ListMode = argValue; return true; - case "listfile": - AutoAttack_ListFile = argValue; return true; - } - break; - - case Section.AutoFishing: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoFishing_Enabled = str2bool(argValue); return true; - case "antidespawn": AutoFishing_Antidespawn = str2bool(argValue); return true; - case "main_hand": AutoFishing_Mainhand = str2bool(argValue); return true; - case "auto_start": AutoFishing_AutoStart = str2bool(argValue); return true; - case "cast_delay": AutoFishing_CastDelay = str2double(argValue); return true; - case "fishing_delay": AutoFishing_FishingDelay = str2double(argValue); return true; - case "fishing_timeout": AutoFishing_FishingTimeout = str2double(argValue); return true; - case "durability_limit": AutoFishing_DurabilityLimit = str2int(argValue); return true; - case "auto_rod_switch": AutoFishing_AutoRodSwitch = str2bool(argValue); return true; - case "stationary_threshold": AutoFishing_StationaryThreshold = str2double(argValue); return true; - case "hook_threshold": AutoFishing_HookThreshold = str2double(argValue); return true; - case "log_fishing_bobber": AutoFishing_LogFishingBobber = str2bool(argValue); return true; - case "location": AutoFishing_Location = str2locationList(argValue); return true; - } - break; - - case Section.AutoEat: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoEat_Enabled = str2bool(argValue); return true; - case "threshold": AutoEat_hungerThreshold = str2int(argValue); return true; - } - break; - - case Section.AutoCraft: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoCraft_Enabled = str2bool(argValue); return true; - case "configfile": AutoCraft_configFile = argValue; return true; - } - break; - - case Section.AutoDrop: - switch (ToLowerIfNeed(argName)) - { - case "enabled": AutoDrop_Enabled = str2bool(argValue); return true; - case "mode": AutoDrop_Mode = argValue; return true; - case "items": AutoDrop_items = argValue; return true; - } - break; - - case Section.MCSettings: - switch (ToLowerIfNeed(argName)) - { - case "enabled": MCSettings_Enabled = str2bool(argValue); return true; - case "locale": MCSettings_Locale = argValue; return true; - case "difficulty": - switch (ToLowerIfNeed(argValue)) - { - case "peaceful": MCSettings_Difficulty = 0; break; - case "easy": MCSettings_Difficulty = 1; break; - case "normal": MCSettings_Difficulty = 2; break; - case "difficult": MCSettings_Difficulty = 3; break; - } - return true; - case "renderdistance": - MCSettings_RenderDistance = 2; - if (argValue.All(Char.IsDigit)) - { - MCSettings_RenderDistance = (byte)str2int(argValue); - } - else - { - switch (ToLowerIfNeed(argValue)) - { - case "tiny": MCSettings_RenderDistance = 2; break; - case "short": MCSettings_RenderDistance = 4; break; - case "medium": MCSettings_RenderDistance = 8; break; - case "far": MCSettings_RenderDistance = 16; break; - } - } - return true; - case "chatmode": - switch (ToLowerIfNeed(argValue)) - { - case "enabled": MCSettings_ChatMode = 0; break; - case "commands": MCSettings_ChatMode = 1; break; - case "disabled": MCSettings_ChatMode = 2; break; - } - return true; - case "chatcolors": MCSettings_ChatColors = str2bool(argValue); return true; - case "skin_cape": MCSettings_Skin_Cape = str2bool(argValue); return true; - case "skin_jacket": MCSettings_Skin_Jacket = str2bool(argValue); return true; - case "skin_sleeve_left": MCSettings_Skin_Sleeve_Left = str2bool(argValue); return true; - case "skin_sleeve_right": MCSettings_Skin_Sleeve_Right = str2bool(argValue); return true; - case "skin_pants_left": MCSettings_Skin_Pants_Left = str2bool(argValue); return true; - case "skin_pants_right": MCSettings_Skin_Pants_Right = str2bool(argValue); return true; - case "skin_hat": MCSettings_Skin_Hat = str2bool(argValue); return true; - case "main_hand": - switch (ToLowerIfNeed(argValue)) - { - case "left": MCSettings_MainHand = 0; break; - case "right": MCSettings_MainHand = 1; break; - } - return true; - } - break; - - case Section.Mailer: - switch (ToLowerIfNeed(argName)) - { - case "enabled": Mailer_Enabled = str2bool(argValue); return true; - case "database": Mailer_DatabaseFile = argValue; return true; - case "ignorelist": Mailer_IgnoreListFile = argValue; return true; - case "publicinteractions": Mailer_PublicInteractions = str2bool(argValue); return true; - case "maxmailsperplayer": Mailer_MaxMailsPerPlayer = str2int(argValue); return true; - case "maxdatabasesize": Mailer_MaxDatabaseSize = str2int(argValue); return true; - case "retentiondays": Mailer_MailRetentionDays = str2int(argValue); return true; - } - break; - - case Section.ReplayMod: - switch (ToLowerIfNeed(argName)) - { - case "enabled": ReplayMod_Enabled = str2bool(argValue); return true; - case "backupinterval": ReplayMod_BackupInterval = str2int(argValue); return true; - } - break; - - case Section.FollowPlayer: - switch (ToLowerIfNeed(argName)) - { - case "enabled": FollowPlayer_Enabled = str2bool(argValue); return true; - case "update_limit": FollowPlayer_UpdateLimit = str2int(argValue); return true; - case "stop_at_distance": FollowPlayer_StopAtDistance = str2int(argValue); return true; - } - break; - case Section.PlayerListLogger: - switch (ToLowerIfNeed(argName)) - { - case "enabled": PlayerLog_Enabled = str2bool(argValue); return true; - case "log_file": PlayerLog_File = argValue; return true; - case "log_delay": PlayerLog_Delay = str2int(argValue); return true; - } - break; - case Section.Map: - switch (ToLowerIfNeed(argName)) - { - case "enabled": Map_Enabled = str2bool(argValue); return true; - case "resize_map": Map_Should_Resize = str2bool(argValue); return true; - case "resize_to": Map_Resize_To = str2int(argValue); return true; - case "auto_render_on_update": Map_Auto_Render_On_Update = str2bool(argValue); return true; - case "delete_rendered_on_unload": Map_Delete_All_On_Unload = str2bool(argValue); return true; - case "notify_on_first_update": Map_Notify_On_First_Update = str2bool(argValue); return true; - } - break; - } - return false; - } - - /// - /// Write an INI file with default settings - /// - /// File to (over)write - public static void WriteDefaultSettings(string settingsfile) - { - // Load embedded default config and adjust line break for the current operating system - string settingsContents = String.Join(Environment.NewLine, - DefaultConfigResource.MinecraftClient.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)); - - // Write configuration file with current version number - File.WriteAllText(settingsfile, - "# Minecraft Console Client v" - + Program.Version - + Environment.NewLine - + settingsContents, Encoding.UTF8); - } - - /// - /// Convert the specified string to an integer, defaulting to zero if invalid argument - /// - /// String to parse as an integer - /// Integer value - public static int str2int(string str) - { - try - { - return Convert.ToInt32(str.Trim()); - } - catch - { - ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str)); - return 0; - } - } - - /// - /// Convert the specified string to a float number, defaulting to zero if invalid argument - /// - /// String to parse as a float number - /// Float number - public static float str2float(string str) - { - if (float.TryParse(str.Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out float num)) - return num; - else - { - ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2int", str)); - return 0; - } - } - - /// - /// Convert the specified string to a double number, defaulting to zero if invalid argument - /// - /// String to parse as a float number - /// Double number - public static double str2double(string str) - { - if (double.TryParse(str.Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out double num)) - return num; - else - { - ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2double", str)); - return 0; - } - } - - /// - /// Convert the specified string to a boolean value, defaulting to false if invalid argument - /// - /// String to parse as a boolean - /// Boolean value - public static bool str2bool(string str) - { - if (String.IsNullOrEmpty(str)) - return false; - str = str.Trim().ToLowerInvariant(); - return str == "true" || str == "1"; - } - - /// - /// Convert the specified string to a list of location, returning null if invalid argument - /// - /// String to parse as a location list - /// Location list (null or double[*,5] or double[*,3] or double[*,2]) - public static double[,]? str2locationList(string str) - { - string[] locationStrList = str.Split(';', StringSplitOptions.RemoveEmptyEntries); - double[,]? res = null; - int codLen = 0; - for (int i = 0; i < locationStrList.Length; ++i) - { - string[] coordinates_str_list = locationStrList[i].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - int curCodLen = coordinates_str_list.Length; - if ((curCodLen == 2 || curCodLen == 3 || curCodLen == 5) && (i == 0 || curCodLen == codLen)) + [NonSerialized] + public static readonly string[] AvailableLang = { - if (i == 0) - { - res = new double[locationStrList.Length, curCodLen]; - codLen = curCodLen; - } + "af_za", "ar_sa", "ast_es", "az_az", "ba_ru", "bar", "be_by", "bg_bg", "br_fr", "brb", "bs_ba", "ca_es", + "cs_cz", "cy_gb", "da_dk", "de_at", "de_ch", "de_de", "el_gr", "en_au", "en_ca", "en_gb", "en_nz", "eo_uy", + "es_ar", "es_cl", "es_ec", "es_es", "es_mx", "es_uy", "es_ve", "esan", "et_ee", "eu_es", "fa_ir", "fi_fi", + "fil_ph", "fo_fo", "fr_ca", "fr_fr", "fra_de", "fur_it", "fy_nl", "ga_ie", "gd_gb", "gl_es", "haw_us", "he_il", + "hi_in", "hr_hr", "hu_hu", "hy_am", "id_id", "ig_ng", "io_en", "is_is", "isv", "it_it", "ja_jp", "jbo_en", + "ka_ge", "kk_kz", "kn_in", "ko_kr", "ksh", "kw_gb", "la_la", "lb_lu", "li_li", "lmo", "lt_lt", "lv_lv", "lzh", + "mk_mk", "mn_mn", "ms_my", "mt_mt", "nds_de", "nl_be", "nl_nl", "nn_no", "oc_fr", "ovd", "pl_pl", "pt_br", + "pt_pt", "qya_aa", "ro_ro", "rpr", "ru_ru", "se_no", "sk_sk", "sl_si", "so_so", "sq_al", "sr_sp", "sv_se", + "sxu", "szl", "ta_in", "th_th", "tl_ph", "tlh_aa", "tok", "tr_tr", "tt_ru", "uk_ua", "val_es", "vec_it", + "vi_vn", "yi_de", "yo_ng", "zh_cn", "zh_hk", "zh_tw", "zlm_arab" + }; - for (int j = 0; j < curCodLen; ++j) + /// + /// Load server information in ServerIP and ServerPort variables from a "serverip:port" couple or server alias + /// + /// True if the server IP was valid and loaded, false otherwise + public bool SetServerIP(ServerInfoConfig serverInfo, bool checkAlias) + { + string serverStr = serverInfo.Host.ToLower(); + string[] sip = serverStr.Split(new[] { ":", ":" }, StringSplitOptions.None); + string host = sip[0]; + ushort port = 25565; + + if (sip.Length > 1) { - if (!double.TryParse(coordinates_str_list[j], NumberStyles.Any, CultureInfo.CurrentCulture, out res![i, j])) + if (serverInfo.Port != null) { - ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2locationList.convert_fail", coordinates_str_list[j])); - return null; + port = (ushort)serverInfo.Port; + } + else + { + try { port = Convert.ToUInt16(sip[1]); } + catch (FormatException) { return false; } } } + + if (host == "localhost" || host.Contains('.')) + { + //Server IP (IP or domain names contains at least a dot) + if (sip.Length == 1 && serverInfo.Port == null && host.Contains('.') && host.Any(c => char.IsLetter(c)) && + Settings.Config.Main.Advanced.ResolveSrvRecords != MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.no) + //Domain name without port may need Minecraft SRV Record lookup + ProtocolHandler.MinecraftServiceLookup(ref host, ref port); + InternalConfig.ServerIP = host; + InternalConfig.ServerPort = port; + return true; + } + else if (checkAlias && Advanced.ServerList.TryGetValue(serverStr, out ServerInfoConfig serverStr2)) + { + return SetServerIP(serverStr2, false); + } + + return false; } - else + + public void OnSettingUpdate() { - ConsoleIO.WriteLogLine(Translations.Get("error.setting.str2locationList.format_err", locationStrList[i])); - return null; + ConsoleIO.EnableTimestamps = Advanced.Timestamps; + + InternalConfig.InteractiveMode = !Advanced.ExitOnFailure; + + General.Account.Login ??= string.Empty; + General.Account.Password ??= string.Empty; + + General.Server.Host ??= string.Empty; + + if (Advanced.MessageCooldown < 0) + Advanced.MessageCooldown = 0; + + if (Advanced.TcpTimeout < 1) + Advanced.TcpTimeout = 1; + + if (Advanced.MovementSpeed < 1) + Advanced.MovementSpeed = 1; + + Advanced.Language = Regex.Replace(Advanced.Language, @"[^-^_^\w^*\d]", string.Empty).Replace('-', '_'); + if (!AvailableLang.Contains(Advanced.Language)) + { + Advanced.Language = Translations.GetTranslationPriority().Item1; + ConsoleIO.WriteLogLine("[Settings] " + Translations.GetOrNull("config.Main.Advanced.language.invaild") ?? "The language code is invalid."); + } + + if (!string.IsNullOrWhiteSpace(General.Server.Host)) + { + string[] sip = General.Server.Host.Split(new[] { ":", ":" }, StringSplitOptions.None); + General.Server.Host = sip[0]; + + if (sip.Length > 1) + { + try { General.Server.Port = Convert.ToUInt16(sip[1]); } + catch (FormatException) { } + } + } + + SetServerIP(General.Server, true); + } + + [TomlDoNotInlineObject] + public class GeneralConfig + { + [TomlInlineComment("$config.Main.General.account$")] + public AccountInfoConfig Account = new(string.Empty, string.Empty); + + [TomlInlineComment("$config.Main.General.login$")] + public ServerInfoConfig Server = new(string.Empty); + + [TomlInlineComment("$config.Main.General.server_info$")] + public LoginType AccountType = LoginType.microsoft; + + [TomlInlineComment("$config.Main.General.method$")] + public LoginMethod Method = LoginMethod.mcc; + + public enum LoginType { mojang, microsoft }; + + public enum LoginMethod { mcc, browser }; + } + + [TomlDoNotInlineObject] + public class AdvancedConfig + { + [TomlInlineComment("$config.Main.Advanced.language$")] + public string Language = "en_GB"; + + // [TomlInlineComment("$config.Main.Advanced.console_title$")] + public string ConsoleTitle = "%username%@%serverip% - Minecraft Console Client"; + + [TomlInlineComment("$config.Main.Advanced.internal_cmd_char$")] + public InternalCmdCharType InternalCmdChar = InternalCmdCharType.slash; + + [TomlInlineComment("$config.Main.Advanced.message_cooldown$")] + public int MessageCooldown = 2; + + [TomlInlineComment("$config.Main.Advanced.bot_owners$")] + public List BotOwners = new() { "Player1", "Player2" }; + + [TomlInlineComment("$config.Main.Advanced.mc_version$")] + public string MinecraftVersion = "auto"; + + [TomlInlineComment("$config.Main.Advanced.mc_forge$")] + public ForgeConfigType EnableForge = ForgeConfigType.auto; + + [TomlInlineComment("$config.Main.Advanced.brand_info$")] + public BrandInfoType BrandInfo = BrandInfoType.mcc; + + [TomlInlineComment("$config.Main.Advanced.chatbot_log_file$")] + public string ChatbotLogFile = ""; + + [TomlInlineComment("$config.Main.Advanced.private_msgs_cmd_name$")] + public string PrivateMsgsCmdName = "tell"; + + [TomlInlineComment("$config.Main.Advanced.show_system_messages$")] + public bool ShowSystemMessages = true; + + [TomlInlineComment("$config.Main.Advanced.show_xpbar_messages$")] + public bool ShowXPBarMessages = true; + + [TomlInlineComment("$config.Main.Advanced.show_chat_links$")] + public bool ShowChatLinks = true; + + [TomlInlineComment("$config.Main.Advanced.show_inventory_layout$")] + public bool ShowInventoryLayout = true; + + [TomlInlineComment("$config.Main.Advanced.terrain_and_movements$")] + public bool TerrainAndMovements = false; + + [TomlInlineComment("$config.Main.Advanced.inventory_handling$")] + public bool InventoryHandling = false; + + [TomlInlineComment("$config.Main.Advanced.entity_handling$")] + public bool EntityHandling = false; + + [TomlInlineComment("$config.Main.Advanced.session_cache$")] + public CacheType SessionCache = CacheType.disk; + + [TomlInlineComment("$config.Main.Advanced.profilekey_cache$")] + public CacheType ProfileKeyCache = CacheType.disk; + + [TomlInlineComment("$config.Main.Advanced.resolve_srv_records$")] + public ResolveSrvRecordType ResolveSrvRecords = ResolveSrvRecordType.fast; + + [TomlInlineComment("$config.Main.Advanced.account_list$")] + public Dictionary AccountList = new() { + { "AccountNikename1", new AccountInfoConfig("login1", "pass1") }, + { "AccountNikename2", new AccountInfoConfig("login2", "pass2") }, + }; + + [TomlInlineComment("$config.Main.Advanced.server_list$")] + public Dictionary ServerList = new() { + { "ServerNickname1", new ServerInfoConfig("test1.server.com") }, + { "ServerNickname2", new ServerInfoConfig("test2.server.com", 12345) }, + }; + + [TomlInlineComment("$config.Main.Advanced.player_head_icon$")] + public bool PlayerHeadAsIcon = true; + + [TomlInlineComment("$config.Main.Advanced.exit_on_failure$")] + public bool ExitOnFailure = false; + + [TomlInlineComment("$config.Main.Advanced.script_cache$")] + public bool CacheScript = true; + + [TomlInlineComment("$config.Main.Advanced.timestamps$")] + public bool Timestamps = false; + + [TomlInlineComment("$config.Main.Advanced.auto_respawn$")] + public bool AutoRespawn = false; + + [TomlInlineComment("$config.Main.Advanced.minecraft_realms$")] + public bool MinecraftRealms = false; + + [TomlInlineComment("$config.Main.Advanced.move_head_while_walking$")] + public bool MoveHeadWhileWalking = true; + + [TomlInlineComment("$config.Main.Advanced.timeout$")] + public int TcpTimeout = 30; + + [TomlInlineComment("$config.Main.Advanced.enable_emoji$")] + public bool EnableEmoji = true; + + [TomlInlineComment("$config.Main.Advanced.movement_speed$")] + public int MovementSpeed = 2; + + /// + /// Load login/password using an account alias + /// + /// True if the account was found and loaded + public bool SetAccount(string accountAlias) + { + if (AccountList.TryGetValue(accountAlias, out AccountInfoConfig accountInfo)) + { + Settings.Config.Main.General.Account = accountInfo; + return true; + } + else + { + return false; + } + } + + public enum InternalCmdCharType { none, slash, backslash }; + + public enum BrandInfoType { mcc, vanilla, empty }; + + public enum CacheType { none, memory, disk }; + + public enum ResolveSrvRecordType { no, fast, yes }; + + public enum ForgeConfigType { no, auto, force }; + } + + public struct AccountInfoConfig + { + public string Login = string.Empty, Password = string.Empty; + + public AccountInfoConfig(string Login) + { + this.Login = Login; + this.Password = "-"; + } + + public AccountInfoConfig(string Login, string Password) + { + this.Login = Login; + this.Password = Password; + } + } + + public struct ServerInfoConfig + { + public string Host = string.Empty; + public ushort? Port = null; + + public ServerInfoConfig(string Host) + { + string[] sip = Host.Split(new[] { ":", ":" }, StringSplitOptions.None); + this.Host = sip[0]; + + if (sip.Length > 1) + { + try { this.Port = Convert.ToUInt16(sip[1]); } + catch (FormatException) { } + } + } + + public ServerInfoConfig(string Host, ushort Port) + { + this.Host = Host.Split(new[] { ":", ":" }, StringSplitOptions.None)[0]; + this.Port = Port; + } } } - return res; } + public static class SignatureConfigHelper + { + public static SignatureConfig Config = new(); + + [TomlDoNotInlineObject] + public class SignatureConfig + { + [TomlInlineComment("$config.Signature.LoginWithSecureProfile$")] + public bool LoginWithSecureProfile = true; + + [TomlInlineComment("$config.Signature.SignChat$")] + public bool SignChat = true; + + [TomlInlineComment("$config.Signature.SignMessageInCommand$")] + public bool SignMessageInCommand = true; + + [TomlInlineComment("$config.Signature.MarkLegallySignedMsg$")] + public bool MarkLegallySignedMsg = false; + + [TomlInlineComment("$config.Signature.MarkModifiedMsg$")] + public bool MarkModifiedMsg = true; + + [TomlInlineComment("$config.Signature.MarkIllegallySignedMsg$")] + public bool MarkIllegallySignedMsg = true; + + [TomlInlineComment("$config.Signature.MarkSystemMessage$")] + public bool MarkSystemMessage = false; + + [TomlInlineComment("$config.Signature.ShowModifiedChat$")] + public bool ShowModifiedChat = true; + + [TomlInlineComment("$config.Signature.ShowIllegalSignedChat$")] + public bool ShowIllegalSignedChat = true; + + public void OnSettingUpdate() { } + } + } + + public static class LoggingConfigHealper + { + public static LoggingConfig Config = new(); + + [TomlDoNotInlineObject] + public class LoggingConfig + { + [TomlInlineComment("$config.Logging.DebugMessages$")] + public bool DebugMessages = false; + + [TomlInlineComment("$config.Logging.ChatMessages$")] + public bool ChatMessages = true; + + [TomlInlineComment("$config.Logging.InfoMessages$")] + public bool InfoMessages = true; + + [TomlInlineComment("$config.Logging.WarningMessages$")] + public bool WarningMessages = true; + + [TomlInlineComment("$config.Logging.ErrorMessages$")] + public bool ErrorMessages = true; + + [TomlInlineComment("$config.Logging.ChatFilter$")] + public string ChatFilterRegex = @".*"; + + [TomlInlineComment("$config.Logging.DebugFilter$")] + public string DebugFilterRegex = @".*"; + + [TomlInlineComment("$config.Logging.FilterMode$")] + public FilterModeEnum FilterMode = FilterModeEnum.whitelist; + + [TomlInlineComment("$config.Logging.LogToFile$")] + public bool LogToFile = false; + + [TomlInlineComment("$config.Logging.LogFile$")] + public string LogFile = @"console-log.txt"; + + [TomlInlineComment("$config.Logging.PrependTimestamp$")] + public bool PrependTimestamp = false; + + [TomlInlineComment("$config.Logging.SaveColorCodes$")] + public bool SaveColorCodes = false; + + public void OnSettingUpdate() { } + + public enum FilterModeEnum { blacklist, whitelist } + } + } + + public static class AppVarConfigHelper + { + public static AppVarConfig Config = new(); + + [TomlDoNotInlineObject] + public class AppVarConfig + { + [TomlPrecedingComment("$config.AppVars.Variables$")] + private readonly Dictionary VarStirng = new() { + { "your_var", "your_value" }, + { "your var 2", "your value 2" }, + }; + + public void OnSettingUpdate() { } + + + [NonSerialized] + private readonly Dictionary VarObject = new(); + + [NonSerialized] + readonly object varLock = new(); + + /// + /// Set a custom %variable% which will be available through expandVars() + /// + /// Name of the variable + /// Value of the variable + /// True if the parameters were valid + public bool SetVar(string varName, object varData) + { + varName = Settings.ToLowerIfNeed(new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray())); + if (varName.Length > 0) + { + bool isString = varData.GetType() == typeof(string); + lock (varLock) + { + if (isString) + { + if (VarObject.ContainsKey(varName)) + VarObject.Remove(varName); + VarStirng[varName] = (string)varData; + } + else + { + if (VarStirng.ContainsKey(varName)) + VarStirng.Remove(varName); + VarObject[varName] = varData; + } + } + return true; + } + else + { + return false; + } + } + + /// + /// Get a custom %variable% or null if the variable does not exist + /// + /// Variable name + /// The value or null if the variable does not exists + public object? GetVar(string varName) + { + if (VarStirng.TryGetValue(varName, out string? valueString)) + return valueString; + else if (VarObject.TryGetValue(varName, out object? valueObject)) + return valueObject; + else + return null; + } + + /// + /// Get a custom %variable% or null if the variable does not exist + /// + /// Variable name + /// The value or null if the variable does not exists + public bool TryGetVar(string varName, [NotNullWhen(true)] out object? varData) + { + if (VarStirng.TryGetValue(varName, out string? valueString)) + { + varData = valueString; + return true; + } + else if (VarObject.TryGetValue(varName, out object? valueObject)) + { + varData = valueObject; + return true; + } + else + { + varData = null; + return false; + } + } + + /// + /// Get a dictionary containing variables (names and value) + /// + /// A IDictionary containing a name and a vlaue key pairs of variables + public Dictionary GetVariables() + { + Dictionary res = new(VarObject); + foreach ((string varName, string varData) in VarStirng) + res.Add(varName, varData); + return res; + } + + /// + /// Replace %variables% with their value from global AppVars + /// + /// String to parse + /// Optional local variables overriding global variables + /// Modifier string + public string ExpandVars(string str, Dictionary? localVars = null) + { + StringBuilder result = new(); + for (int i = 0; i < str.Length; i++) + { + if (str[i] == '%') + { + bool varname_ok = false; + StringBuilder var_name = new(); + + for (int j = i + 1; j < str.Length; j++) + { + if (!char.IsLetterOrDigit(str[j]) && str[j] != '_') + { + if (str[j] == '%') + varname_ok = var_name.Length > 0; + break; + } + else var_name.Append(str[j]); + } + + if (varname_ok) + { + string varname = var_name.ToString(); + string varname_lower = Settings.ToLowerIfNeed(varname); + i = i + varname.Length + 1; + + switch (varname_lower) + { + case "username": result.Append(InternalConfig.Username); break; + case "login": result.Append(Settings.Config.Main.General.Account.Login); break; + case "serverip": result.Append(InternalConfig.ServerIP); break; + case "serverport": result.Append(InternalConfig.ServerPort); break; + case "datetime": + DateTime time = DateTime.Now; + result.Append(String.Format("{0}-{1}-{2} {3}:{4}:{5}", + time.Year.ToString("0000"), + time.Month.ToString("00"), + time.Day.ToString("00"), + time.Hour.ToString("00"), + time.Minute.ToString("00"), + time.Second.ToString("00"))); + + break; + default: + if (localVars != null && localVars.ContainsKey(varname_lower)) + result.Append(localVars[varname_lower].ToString()); + else if (TryGetVar(varname_lower, out object? var_value)) + result.Append(var_value.ToString()); + else + result.Append("%" + varname + '%'); + break; + } + } + else result.Append(str[i]); + } + else result.Append(str[i]); + } + return result.ToString(); + } + } + } + + public static class MCSettingsConfigHealper + { + public static MCSettingsConfig Config = new(); + + [TomlDoNotInlineObject] + public class MCSettingsConfig + { + [TomlInlineComment("$config.MCSettings.Enabled$")] + public bool Enabled = true; + + [TomlInlineComment("$config.MCSettings.Locale$")] + public string Locale = "en_US"; + + [TomlInlineComment("$config.MCSettings.RenderDistance$")] + public byte RenderDistance = 8; + + [TomlInlineComment("$config.MCSettings.Difficulty$")] + public DifficultyType Difficulty = DifficultyType.peaceful; + + [TomlInlineComment("$config.MCSettings.ChatMode$")] + public ChatModeType ChatMode = ChatModeType.enabled; + + [TomlInlineComment("$config.MCSettings.ChatColors$")] + public bool ChatColors = true; + + [TomlInlineComment("$config.MCSettings.MainHand$")] + public MainHandType MainHand = MainHandType.left; + + public SkinInfo Skin = new(); + + public void OnSettingUpdate() { } + + public enum DifficultyType { peaceful, easy, normal, difficult }; + + public enum ChatModeType { enabled, commands, disabled }; + + public enum MainHandType { left, right }; + + public struct SkinInfo + { + public bool Cape = true, Hat = true, Jacket = false; + public bool Sleeve_Left = false, Sleeve_Right = false; + public bool Pants_Left = false, Pants_Right = false; + + public SkinInfo() { } + + public SkinInfo(bool Cape, bool Hat, bool Jacket, bool Sleeve_Left, bool Sleeve_Right, bool Pants_Left, bool Pants_Right) + { + this.Cape = Cape; + this.Hat = Hat; + this.Jacket = Jacket; + this.Sleeve_Left = Sleeve_Left; + this.Sleeve_Right = Sleeve_Right; + this.Pants_Left = Pants_Left; + this.Pants_Right = Pants_Right; + } + + public byte GetByte() + { + return (byte)( + ((Cape ? 1 : 0) << 0) + | ((Jacket ? 1 : 0) << 1) + | ((Sleeve_Left ? 1 : 0) << 2) + | ((Sleeve_Right ? 1 : 0) << 3) + | ((Pants_Left ? 1 : 0) << 4) + | ((Pants_Right ? 1 : 0) << 5) + | ((Hat ? 1 : 0) << 6) + ); + } + } + } + } + + public static class ChatFormatConfigHelper + { + public static ChatFormatConfig Config = new(); + + [TomlDoNotInlineObject] + public class ChatFormatConfig + { + [TomlInlineComment("$config.ChatFormat.Builtins$")] + public bool Builtins = true; + + public string Public = @"^<([a-zA-Z0-9_]+)> (.+)$"; + + public string Private = @"^([a-zA-Z0-9_]+) whispers to you: (.+)$"; + + public string TeleportRequest = @"^([a-zA-Z0-9_]+) has requested (?:to|that you) teleport to (?:you|them)\.$"; + + public void OnSettingUpdate() { } + } + } + + public static class ChatBotConfigHealper + { + public static ChatBotConfig Config = new(); + + [TomlDoNotInlineObject] + public class ChatBotConfig + { + [TomlPrecedingComment("$config.ChatBot.Alerts$")] + public ChatBots.Alerts.Configs Alerts + { + get { return ChatBots.Alerts.Config; } + set { ChatBots.Alerts.Config = value; ChatBots.Alerts.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AntiAFK$")] + public ChatBots.AntiAFK.Configs AntiAFK + { + get { return ChatBots.AntiAFK.Config; } + set { ChatBots.AntiAFK.Config = value; ChatBots.AntiAFK.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoAttack$")] + public ChatBots.AutoAttack.Configs AutoAttack + { + get { return ChatBots.AutoAttack.Config; } + set { ChatBots.AutoAttack.Config = value; ChatBots.AutoAttack.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoCraft$")] + public ChatBots.AutoCraft.Configs AutoCraft + { + get { return ChatBots.AutoCraft.Config; } + set { ChatBots.AutoCraft.Config = value; ChatBots.AutoCraft.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoDrop$")] + public ChatBots.AutoDrop.Configs AutoDrop + { + get { return ChatBots.AutoDrop.Config; } + set { ChatBots.AutoDrop.Config = value; ChatBots.AutoDrop.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoEat$")] + public ChatBots.AutoEat.Configs AutoEat + { + get { return ChatBots.AutoEat.Config; } + set { ChatBots.AutoEat.Config = value; ChatBots.AutoEat.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoFishing$")] + public ChatBots.AutoFishing.Configs AutoFishing + { + get { return ChatBots.AutoFishing.Config; } + set { ChatBots.AutoFishing.Config = value; ChatBots.AutoFishing.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoRelog$")] + public ChatBots.AutoRelog.Configs AutoRelog + { + get { return ChatBots.AutoRelog.Config; } + set { ChatBots.AutoRelog.Config = value; ChatBots.AutoRelog.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.AutoRespond$")] + public ChatBots.AutoRespond.Configs AutoRespond + { + get { return ChatBots.AutoRespond.Config; } + set { ChatBots.AutoRespond.Config = value; ChatBots.AutoRespond.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.ChatLog$")] + public ChatBots.ChatLog.Configs ChatLog + { + get { return ChatBots.ChatLog.Config; } + set { ChatBots.ChatLog.Config = value; ChatBots.ChatLog.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.FollowPlayer$")] + public ChatBots.FollowPlayer.Configs FollowPlayer + { + get { return ChatBots.FollowPlayer.Config; } + set { ChatBots.FollowPlayer.Config = value; ChatBots.FollowPlayer.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.HangmanGame$")] + public ChatBots.HangmanGame.Configs HangmanGame + { + get { return ChatBots.HangmanGame.Config; } + set { ChatBots.HangmanGame.Config = value; ChatBots.HangmanGame.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.Mailer$")] + public ChatBots.Mailer.Configs Mailer + { + get { return ChatBots.Mailer.Config; } + set { ChatBots.Mailer.Config = value; ChatBots.Mailer.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.Map$")] + public ChatBots.Map.Configs Map + { + get { return ChatBots.Map.Config; } + set { ChatBots.Map.Config = value; ChatBots.Map.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.PlayerListLogger$")] + public ChatBots.PlayerListLogger.Configs PlayerListLogger + { + get { return ChatBots.PlayerListLogger.Config; } + set { ChatBots.PlayerListLogger.Config = value; ChatBots.PlayerListLogger.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.RemoteControl$")] + public ChatBots.RemoteControl.Configs RemoteControl + { + get { return ChatBots.RemoteControl.Config; } + set { ChatBots.RemoteControl.Config = value; ChatBots.RemoteControl.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.ReplayCapture$")] + public ChatBots.ReplayCapture.Configs ReplayCapture + { + get { return ChatBots.ReplayCapture.Config; } + set { ChatBots.ReplayCapture.Config = value; ChatBots.ReplayCapture.Config.OnSettingUpdate(); } + } + + [TomlPrecedingComment("$config.ChatBot.ScriptScheduler$")] + public ChatBots.ScriptScheduler.Configs ScriptScheduler + { + get { return ChatBots.ScriptScheduler.Config; } + set { ChatBots.ScriptScheduler.Config = value; ChatBots.ScriptScheduler.Config.OnSettingUpdate(); } + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static string ToLowerIfNeed(string str) { - const string lookupStringL = -"---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-"; + const string lookupStringL = "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-"; bool needLower = false; foreach (Char c in str) @@ -1064,173 +1072,44 @@ namespace MinecraftClient return str; } } + } - /// - /// Load login/password using an account alias - /// - /// True if the account was found and loaded - public static bool SetAccount(string accountAlias) + public static class InternalCmdCharTypeExtensions + { + public static char ToChar(this InternalCmdCharType type) { - accountAlias = Settings.ToLowerIfNeed(accountAlias); - if (Accounts.ContainsKey(accountAlias)) + return type switch { - Settings.Login = Accounts[accountAlias].Key; - Settings.Password = Accounts[accountAlias].Value; - return true; - } - else return false; + InternalCmdCharType.none => ' ', + InternalCmdCharType.slash => '/', + InternalCmdCharType.backslash => '\\', + _ => '/', + }; } - /// - /// Load server information in ServerIP and ServerPort variables from a "serverip:port" couple or server alias - /// - /// True if the server IP was valid and loaded, false otherwise - public static bool SetServerIP(string server) + public static string ToLogString(this InternalCmdCharType type) { - server = ToLowerIfNeed(server); - string[] sip = server.Split(':'); - string host = sip[0]; - ushort port = 25565; - - if (sip.Length > 1) + return type switch { - try - { - port = Convert.ToUInt16(sip[1]); - } - catch (FormatException) { return false; } - } - - if (host == "localhost" || host.Contains('.')) - { - //Server IP (IP or domain names contains at least a dot) - if (sip.Length == 1 && host.Contains('.') && host.Any(c => char.IsLetter(c)) && ResolveSrvRecords) - //Domain name without port may need Minecraft SRV Record lookup - ProtocolHandler.MinecraftServiceLookup(ref host, ref port); - ServerIP = host; - ServerPort = port; - return true; - } - else if (Servers.ContainsKey(server)) - { - //Server Alias (if no dot then treat the server as an alias) - ServerIP = Servers[server].Key; - ServerPort = Servers[server].Value; - return true; - } - - return false; + InternalCmdCharType.none => string.Empty, + InternalCmdCharType.slash => @"/", + InternalCmdCharType.backslash => @"\", + _ => @"/", + }; } - - /// - /// Set a custom %variable% which will be available through expandVars() - /// - /// Name of the variable - /// Value of the variable - /// True if the parameters were valid - public static bool SetVar(string varName, object varData) + } + + public static class BrandInfoTypeExtensions + { + public static string? ToBrandString(this BrandInfoType info) { - lock (AppVars) + return info switch { - varName = Settings.ToLowerIfNeed(new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray())); - if (varName.Length > 0) - { - AppVars[varName] = varData; - return true; - } - else return false; - } - } - - /// - /// Get a custom %variable% or null if the variable does not exist - /// - /// Variable name - /// The value or null if the variable does not exists - public static object? GetVar(string varName) - { - if (AppVars.ContainsKey(varName)) - return AppVars[varName]; - return null; - } - - /// - /// Get a dictionary containing variables (names and value) - /// - /// A IDictionary containing a name and a vlaue key pairs of variables - public static Dictionary GetVariables() - { - return AppVars; - } - - /// - /// Replace %variables% with their value from global AppVars - /// - /// String to parse - /// Optional local variables overriding global variables - /// Modifier string - public static string ExpandVars(string str, Dictionary? localVars = null) - { - StringBuilder result = new(); - for (int i = 0; i < str.Length; i++) - { - if (str[i] == '%') - { - bool varname_ok = false; - StringBuilder var_name = new(); - - for (int j = i + 1; j < str.Length; j++) - { - if (!char.IsLetterOrDigit(str[j]) && str[j] != '_') - { - if (str[j] == '%') - varname_ok = var_name.Length > 0; - break; - } - else var_name.Append(str[j]); - } - - if (varname_ok) - { - string varname = var_name.ToString(); - string varname_lower = Settings.ToLowerIfNeed(varname); - i = i + varname.Length + 1; - - switch (varname_lower) - { - case "username": result.Append(Username); break; - case "login": result.Append(Login); break; - case "serverip": result.Append(ServerIP); break; - case "serverport": result.Append(ServerPort); break; - case "datetime": - DateTime time = DateTime.Now; - result.Append(String.Format("{0}-{1}-{2} {3}:{4}:{5}", - time.Year.ToString("0000"), - time.Month.ToString("00"), - time.Day.ToString("00"), - time.Hour.ToString("00"), - time.Minute.ToString("00"), - time.Second.ToString("00"))); - - break; - default: - if (localVars != null && localVars.ContainsKey(varname_lower)) - { - result.Append(localVars[varname_lower].ToString()); - } - else if (AppVars.ContainsKey(varname_lower)) - { - result.Append(AppVars[varname_lower].ToString()); - } - else result.Append("%" + varname + '%'); - break; - } - } - else result.Append(str[i]); - } - else result.Append(str[i]); - } - return result.ToString(); + BrandInfoType.mcc => Settings.MCCBrandInfo, + BrandInfoType.vanilla => "vanilla", + BrandInfoType.empty => null, + _ => null, + }; } } } diff --git a/MinecraftClient/Translations.cs b/MinecraftClient/Translations.cs index 1bcf9c16..81776efb 100644 --- a/MinecraftClient/Translations.cs +++ b/MinecraftClient/Translations.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; @@ -15,7 +16,7 @@ namespace MinecraftClient /// public static class Translations { - private static readonly Dictionary translations; + private static readonly Dictionary translations = new(); private static readonly string translationFilePath = "lang" + Path.DirectorySeparatorChar + "mcc"; private static readonly string defaultTranslation = "en.ini"; private static readonly Regex translationKeyRegex = new(@"\(\[(.*?)\]\)", RegexOptions.Compiled); // Extract string inside ([ ]) @@ -52,6 +53,21 @@ namespace MinecraftClient return msgName; } + /// + /// Return a tranlation for the requested text. Support string formatting. If not found, return the original text + /// + /// text identifier + /// + /// Translated text or original text if not found + /// Useful when not sure msgName is a translation mapping key or a normal text + public static string? GetOrNull(string msgName, params object?[] args) + { + if (translations.ContainsKey(msgName)) + return Get(msgName, args); + else + return null; + } + /// /// Replace the translation key inside a sentence to translated text. Wrap the key in ([translation.key]) /// @@ -75,32 +91,495 @@ namespace MinecraftClient return Get(m.Groups[1].Value); } - /// - /// Initialize translations depending on system language. - /// English is the default for all unknown system languages. - /// - static Translations() + public static Tuple GetTranslationPriority() { - translations = new Dictionary(); - LoadDefaultTranslationsFile(); + string gameLanguage = "en_gb"; + List name = new(); + + string systemLanguage = string.IsNullOrWhiteSpace(CultureInfo.CurrentCulture.Name) + ? CultureInfo.CurrentCulture.Parent.Name + : CultureInfo.CurrentCulture.Name; + + switch (systemLanguage) + { + case "af": + case "af-ZA": + gameLanguage = "af_za"; + break; + case "ar": + case "ar-AE": + case "ar-BH": + case "ar-DZ": + case "ar-EG": + case "ar-IQ": + case "ar-JO": + case "ar-KW": + case "ar-LB": + case "ar-LY": + case "ar-MA": + case "ar-OM": + case "ar-QA": + case "ar-SA": + case "ar-SY": + case "ar-TN": + case "ar-YE": + gameLanguage = "ar_sa"; + break; + case "az": + case "az-Cyrl-AZ": + case "az-Latn-AZ": + gameLanguage = "az_az"; + break; + case "be": + case "be-BY": + gameLanguage = "be_by"; + break; + case "bg": + case "bg-BG": + gameLanguage = "bg_bg"; + break; + case "bs-Latn-BA": + gameLanguage = "bs_ba"; + break; + case "ca": + case "ca-ES": + gameLanguage = "ca_es"; + break; + case "cs": + case "cs-CZ": + gameLanguage = "cs_cz"; + break; + case "cy-GB": + gameLanguage = "cy_gb"; + break; + case "da": + case "da-DK": + gameLanguage = "da_dk"; + break; + case "de": + case "de-DE": + case "de-LI": + case "de-LU": + gameLanguage = "de_de"; + name.Add("de"); + break; + case "de-AT": + gameLanguage = "de_at"; + name.Add("de"); + break; + case "de-CH": + gameLanguage = "de_ch"; + name.Add("de"); + break; + case "dv": + case "dv-MV": + break; + case "el": + case "el-GR": + gameLanguage = "el_gr"; + break; + case "en": + case "en-029": + case "en-BZ": + case "en-IE": + case "en-JM": + case "en-PH": + case "en-TT": + case "en-ZA": + case "en-ZW": + case "en-GB": + gameLanguage = "en_gb"; + break; + case "en-AU": + gameLanguage = "en_au"; + break; + case "en-CA": + gameLanguage = "en_ca"; + break; + case "en-US": + gameLanguage = "en_us"; + break; + case "en-NZ": + gameLanguage = "en_nz"; + break; + case "es": + case "es-BO": + case "es-CO": + case "es-CR": + case "es-DO": + case "es-ES": + case "es-GT": + case "es-HN": + case "es-NI": + case "es-PA": + case "es-PE": + case "es-PR": + case "es-PY": + case "es-SV": + gameLanguage = "es_es"; + break; + case "es-AR": + gameLanguage = "es_ar"; + break; + case "es-CL": + gameLanguage = "es_cl"; + break; + case "es-EC": + gameLanguage = "es_ec"; + break; + case "es-MX": + gameLanguage = "es_mx"; + break; + case "es-UY": + gameLanguage = "es_uy"; + break; + case "es-VE": + gameLanguage = "es_ve"; + break; + case "et": + case "et-EE": + gameLanguage = "et_ee"; + break; + case "eu": + case "eu-ES": + gameLanguage = "eu_es"; + break; + case "fa": + case "fa-IR": + gameLanguage = "fa_ir"; + break; + case "fi": + case "fi-FI": + gameLanguage = "fi_fi"; + break; + case "fo": + case "fo-FO": + gameLanguage = "fo_fo"; + break; + case "fr": + case "fr-BE": + case "fr-FR": + case "fr-CH": + case "fr-LU": + case "fr-MC": + gameLanguage = "fr_fr"; + name.Add("fr"); + break; + case "fr-CA": + gameLanguage = "fr_ca"; + name.Add("fr"); + break; + case "gl": + case "gl-ES": + gameLanguage = "gl_es"; + break; + case "gu": + case "gu-IN": + break; + case "he": + case "he-IL": + gameLanguage = "he_il"; + break; + case "hi": + case "hi-IN": + gameLanguage = "hi_in"; + break; + case "hr": + case "hr-BA": + case "hr-HR": + gameLanguage = "hr_hr"; + break; + case "hu": + case "hu-HU": + gameLanguage = "hu_hu"; + break; + case "hy": + case "hy-AM": + gameLanguage = "hy_am"; + break; + case "id": + case "id-ID": + gameLanguage = "id_id"; + break; + case "is": + case "is-IS": + gameLanguage = "is_is"; + break; + case "it": + case "it-CH": + case "it-IT": + gameLanguage = "it_it"; + break; + case "ja": + case "ja-JP": + gameLanguage = "ja_jp"; + break; + case "ka": + case "ka-GE": + gameLanguage = "ka_ge"; + break; + case "kk": + case "kk-KZ": + gameLanguage = "kk_kz"; + break; + case "kn": + case "kn-IN": + gameLanguage = "kn_in"; + break; + case "kok": + case "kok-IN": + break; + case "ko": + case "ko-KR": + gameLanguage = "ko_kr"; + break; + case "ky": + case "ky-KG": + break; + case "lt": + case "lt-LT": + gameLanguage = "lt_lt"; + break; + case "lv": + case "lv-LV": + gameLanguage = "lv_lv"; + break; + case "mi-NZ": + break; + case "mk": + case "mk-MK": + gameLanguage = "mk_mk"; + break; + case "mn": + case "mn-MN": + gameLanguage = "mn_mn"; + break; + case "mr": + case "mr-IN": + break; + case "ms": + case "ms-BN": + case "ms-MY": + gameLanguage = "ms_my"; + break; + case "mt-MT": + gameLanguage = "mt_mt"; + break; + case "nb-NO": + break; + case "nl": + case "nl-NL": + gameLanguage = "nl_nl"; + break; + case "nl-BE": + gameLanguage = "nl_be"; + break; + case "nn-NO": + gameLanguage = "nn_no"; + break; + case "no": + gameLanguage = "no_no‌"; + break; + case "ns-ZA": + break; + case "pa": + case "pa-IN": + break; + case "pl": + case "pl-PL": + gameLanguage = "pl_pl‌"; + break; + case "pt": + case "pt-PT": + gameLanguage = "pt_pt‌"; + break; + case "pt-BR": + gameLanguage = "pt_br‌"; + break; + case "quz-BO": + break; + case "quz-EC": + break; + case "quz-PE": + break; + case "ro": + case "ro-RO": + gameLanguage = "ro_ro‌"; + break; + case "ru": + case "ru-RU": + gameLanguage = "ru_ru"; + name.Add("ru"); + break; + case "sa": + case "sa-IN": + break; + case "se-FI": + case "se-NO": + case "se-SE": + gameLanguage = "se_no"; + break; + case "sk": + case "sk-SK": + gameLanguage = "sk_sk"; + break; + case "sl": + case "sl-SI": + gameLanguage = "sl_si"; + break; + case "sma-NO": + break; + case "sma-SE": + break; + case "smj-NO": + break; + case "smj-SE": + break; + case "smn-FI": + break; + case "sms-FI": + break; + case "sq": + case "sq-AL": + gameLanguage = "sq_al"; + break; + case "sr": + case "sr-Cyrl-BA": + case "sr-Cyrl-CS": + case "sr-Latn-BA": + case "sr-Latn-CS": + gameLanguage = "sr_sp"; + break; + case "sv": + case "sv-FI": + case "sv-SE": + gameLanguage = "sv_se"; + break; + case "sw": + case "sw-KE": + break; + case "syr": + case "syr-SY": + break; + case "ta": + case "ta-IN": + gameLanguage = "ta_in"; + break; + case "te": + case "te-IN": + break; + case "th": + case "th-TH": + gameLanguage = "th_th"; + break; + case "tn-ZA": + break; + case "tr": + case "tr-TR": + gameLanguage = "tr_tr"; + break; + case "tt": + case "tt-RU": + gameLanguage = "tt_ru"; + break; + case "uk": + case "uk-UA": + gameLanguage = "uk_ua"; + break; + case "ur": + case "ur-PK": + break; + case "uz": + case "uz-Cyrl-UZ": + case "uz-Latn-UZ": + break; + case "vi": + case "vi-VN": + gameLanguage = "vi_vn"; + name.Add("vi"); + break; + case "xh-ZA": + break; + case "zh-Hans": /* CurrentCulture.Parent.Name */ + case "zh": + case "zh-CN": + case "zh-CHS": + case "zh-SG": + gameLanguage = "zh_cn"; + name.Add("zh_Hans"); + name.Add("zh_Hant"); + break; + case "zh-Hant": /* CurrentCulture.Parent.Name */ + case "zh-HK": + case "zh-CHT": + case "zh-MO": + gameLanguage = "zh_hk"; + name.Add("zh_Hant"); + name.Add("zh_Hans"); + break; + case "zh-TW": + gameLanguage = "zh_tw"; + name.Add("zh_Hant"); + name.Add("zh_Hans"); + break; + case "zu-ZA": + break; + } + + name.Add("en"); + + return new(gameLanguage, name.ToArray()); + } + + public static string[] GetTranslationPriority(string gameLanguage) + { + List name = new(); + + switch (gameLanguage) + { + case "de_at": + case "de_ch": + case "de_de": + name.Add("de"); + break; + case "en_au": + case "en_ca": + case "en_gb": + case "en_nz": + case "en_pt": + case "en_ud": + case "en_us": + break; + case "fr_ca": + case "fr_fr": + name.Add("fr"); + break; + case "ru_ru": + name.Add("ru"); + break; + case "vi_vn": + name.Add("vi"); + break; + case "zh_cn": + name.Add("zh_Hans"); + name.Add("zh_Hant"); + break; + case "zh_hk": + case "zh_tw": + name.Add("zh_Hant"); + name.Add("zh_Hans"); + break; + } + + name.Add("en"); + + return name.ToArray(); } /// - /// Load default translation file (English) + /// Load translation files /// - /// - /// This will be loaded during program start up. - /// - private static void LoadDefaultTranslationsFile() - { - string[] engLang = DefaultConfigResource.TranslationEnglish.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); // use embedded translations - ParseTranslationContent(engLang); - } - - /// - /// Load translation file depends on system language or by giving a file path. Default to English if translation file does not exist - /// - public static void LoadExternalTranslationFile(string language) + public static void LoadTranslationFile(string[] languageList) { /* * External translation files @@ -108,44 +587,39 @@ namespace MinecraftClient * Lang/abc.ini, e.g. Lang/eng.ini which is the default language file * Useful for adding new translations of fixing typos without recompiling */ - - // Try to convert Minecraft language file name to two letters language name - if (language == "zh_cn") - language = "zh-CHS"; - else if (language == "zh_tw") - language = "zh-CHT"; - else - language = language.Split('_')[0]; - - string systemLanguage = string.IsNullOrEmpty(CultureInfo.CurrentCulture.Parent.Name) // Parent.Name might be empty - ? CultureInfo.CurrentCulture.Name - : CultureInfo.CurrentCulture.Parent.Name; string baseDir = AppDomain.CurrentDomain.BaseDirectory; string langDir = baseDir + ((baseDir.EndsWith(Path.DirectorySeparatorChar) ? String.Empty : Path.DirectorySeparatorChar) + translationFilePath + Path.DirectorySeparatorChar); - string langFileSystemLanguage = langDir + systemLanguage + ".ini"; - string langFileConfigLanguage = langDir + language + ".ini"; - if (File.Exists(langFileConfigLanguage)) - {// Language set in ini config - ParseTranslationContent(File.ReadAllLines(langFileConfigLanguage)); - return; - } - else + foreach (string lang in languageList) { - if (Settings.DebugMessages) - ConsoleIO.WriteLogLine("[Translations] No translation file found for " + language + ". (Looked '" + langFileConfigLanguage + "'"); - } + bool fileLoaded = false; + string langFileName = string.Format("{0}{1}.ini", langDir, lang); - if (File.Exists(langFileSystemLanguage)) - {// Fallback to system language - ParseTranslationContent(File.ReadAllLines(langFileSystemLanguage)); - return; - } - else - { - if (Settings.DebugMessages) - ConsoleIO.WriteLogLine("[Translations] No translation file found for system language (" + systemLanguage + "). (Looked '" + langFileSystemLanguage + "'"); + if (File.Exists(langFileName)) // Language set in ini config + { + fileLoaded = true; + Dictionary trans = ParseTranslationContent(File.ReadAllLines(langFileName)); + foreach ((string key, string value) in trans) + if (!string.IsNullOrWhiteSpace(value) && !translations.ContainsKey(key)) + translations.Add(key, value); + } + + string? resourseLangFile = DefaultConfigResource.ResourceManager.GetString("Translation_" + lang); + if (resourseLangFile != null) + { + fileLoaded = true; + Dictionary trans = ParseTranslationContent(resourseLangFile.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)); + foreach ((string key, string value) in trans) + if (!string.IsNullOrWhiteSpace(value) && !translations.ContainsKey(key)) + translations.Add(key, value); + } + + if (!fileLoaded) + { + if (Settings.Config.Logging.DebugMessages) + ConsoleIO.WriteLogLine("[Translations] No translation file found for " + lang + ". (Looked '" + langFileName + "'"); + } } } @@ -153,8 +627,9 @@ namespace MinecraftClient /// Parse the given array to translation map /// /// Content of the translation file (in ini format) - private static void ParseTranslationContent(string[] content) + private static Dictionary ParseTranslationContent(string[] content) { + Dictionary translations = new(); foreach (string lineRaw in content) { string line = lineRaw.Trim(); @@ -172,6 +647,7 @@ namespace MinecraftClient translations[translationName] = translationValue; } } + return translations; } /// @@ -185,7 +661,7 @@ namespace MinecraftClient { Directory.CreateDirectory(translationFilePath); } - File.WriteAllText(defaultPath, DefaultConfigResource.TranslationEnglish, Encoding.UTF8); + File.WriteAllText(defaultPath, DefaultConfigResource.Translation_en, Encoding.UTF8); } #region Console writing method wrapper diff --git a/MinecraftClient/WinAPI/ConsoleIcon.cs b/MinecraftClient/WinAPI/ConsoleIcon.cs index 5d8ee345..36492f7c 100644 --- a/MinecraftClient/WinAPI/ConsoleIcon.cs +++ b/MinecraftClient/WinAPI/ConsoleIcon.cs @@ -67,6 +67,16 @@ namespace MinecraftClient.WinAPI imageStream.Dispose(); httpWebRequest.Dispose(); } + catch (AggregateException ae) + { + foreach (var ex in ae.InnerExceptions) + { + if (ex is HttpRequestException) //Skin not found? Reset to default icon + RevertToMCCIcon(); + else + throw ex; + } + } catch (HttpRequestException) //Skin not found? Reset to default icon { RevertToMCCIcon(); diff --git a/MinecraftClient/WinAPI/ExitCleanUp.cs b/MinecraftClient/WinAPI/ExitCleanUp.cs index 1de9864f..0c68d04c 100644 --- a/MinecraftClient/WinAPI/ExitCleanUp.cs +++ b/MinecraftClient/WinAPI/ExitCleanUp.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Threading; namespace MinecraftClient.WinAPI { @@ -79,9 +80,10 @@ namespace MinecraftClient.WinAPI } [DllImport("Kernel32")] - private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandler handler, bool add); - private delegate bool ConsoleCtrlHandler(CtrlType sig); - private static readonly ConsoleCtrlHandler? _handler; + private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); + + private delegate bool EventHandler(CtrlType sig); + static EventHandler? _handler; enum CtrlType {