diff --git a/ConsoleInteractive b/ConsoleInteractive index 225b10ec..7acaa0ab 160000 --- a/ConsoleInteractive +++ b/ConsoleInteractive @@ -1 +1 @@ -Subproject commit 225b10ec96af5c8f179a008cc442b502d23bc601 +Subproject commit 7acaa0ab31809f0f6512e6a76e53e15441de8e7c diff --git a/MinecraftClient/ChatBots/Alerts.cs b/MinecraftClient/ChatBots/Alerts.cs index c3f1f6a0..ffe8f857 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,53 +9,103 @@ namespace MinecraftClient.ChatBots /// public class Alerts : ChatBot { - private string[] dictionary = Array.Empty(); - private string[] excludelist = Array.Empty(); - private bool logToFile = false; + 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.Log_To_File$")] + public bool Log_To_File = false; + + [TomlInlineComment("$config.ChatBot.Alerts.Log_File$")] + public string Log_File = @"alerts-log.txt"; + + [TomlPrecedingComment("$config.ChatBot.Alerts.Matches$")] + public string[] Matches = new string[] { "Yourname", " whispers ", "-> me", "admin", ".com" }; + + [TomlPrecedingComment("$config.ChatBot.Alerts.Excludes$")] + public string[] Excludes = new string[] { "myserver.com", "Yourname>:", "Player Yourname", "Yourname joined", "Yourname left", "[Lockette] (Admin)", " Yourname:", "Yourname is" }; + + public void OnSettingUpdate() + { + Log_File ??= string.Empty; + + if (!Enabled) return; + + bool checkSuccessed = true; + + if (Trigger_By_Words) + { + 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(Log_File)); + } + } + } + + if (!checkSuccessed) + { + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + } + float curRainLevel = 0; float curThunderLevel = 0; const float threshold = 0.2f; - /// - /// Intitialize the Alerts bot - /// - public override void Initialize() - { - if (Settings.Alerts_Trigger_By_Words) - { - dictionary = LoadDistinctEntriesFromFile(Settings.Alerts_MatchesFile); - excludelist = LoadDistinctEntriesFromFile(Settings.Alerts_ExcludesFile); - logToFile = Settings.Alerts_File_Logging; - } - } - /// /// Process text received from the server to display alerts /// /// 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(); //Proceed only if no exclusions are found in text - if (!excludelist.Any(exclusion => text.Contains(exclusion))) + if (!Config.Excludes.Any(exclusion => text.Contains(exclusion))) { //Show an alert for each alert item found in text, if any - foreach (string alert in dictionary.Where(alert => text.Contains(alert))) + foreach (string alert in Config.Matches.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 +116,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 +128,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 +144,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 +156,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..0f89d202 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,104 +10,113 @@ namespace MinecraftClient.ChatBots public class AntiAFK : ChatBot { - private int count; - private readonly string pingparam; - private int timeping = 600; - private int timepingMax = -1; - private bool useTerrainHandling = false; + 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(60); + + [TomlInlineComment("$config.ChatBot.AntiAfk.Command$")] + public string Command = "/ping"; + + [TomlInlineComment("$config.ChatBot.AntiAfk.Use_Sneak$")] + public bool Use_Sneak = false; + + [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")); + } + + Delay.min = Math.Max(1.0, Delay.min); + Delay.max = Math.Max(1.0, Delay.max); + + Delay.min = Math.Min(int.MaxValue / 10, Delay.min); + Delay.max = Math.Min(int.MaxValue / 10, Delay.max); + + if (Delay.min > Delay.max) + { + (Delay.min, Delay.max) = (Delay.max, Delay.min); + LogToConsole(BotName, Translations.TryGet("bot.antiafk.swapping")); + } + + Command ??= string.Empty; + } + + public struct Range + { + public double min, max; + + public Range(int value) + { + min = max = value; + } + + public Range(int min, int max) + { + this.min = min; + this.max = max; + } + } + } + + private int count, nextrun = 50; 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 >= nextrun) { DoAntiAfkStuff(); count = 0; + nextrun = random.Next(Settings.DoubleToTick(Config.Delay.min), Settings.DoubleToTick(Config.Delay.max)); } } private void DoAntiAfkStuff() { - if (useTerrainHandling) + if (Config.Use_Terrain_Handling && GetTerrainEnabled()) { Location currentLocation = GetCurrentLocation(); Location goal; @@ -118,19 +127,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)) @@ -138,22 +147,27 @@ namespace MinecraftClient.ChatBots useAlternativeMethod = true; break; } - else moved = MoveToLocation(goal, allowUnsafe: false, allowDirectTeleport: false); + else + { + moved = MoveToLocation(goal, allowUnsafe: false, allowDirectTeleport: false); + } } - if (!useAlternativeMethod) + if (!useAlternativeMethod && Config.Use_Sneak) { // Solve the case when the bot was closed in 1x2, was sneaking, but then he was freed, this will make him not sneak anymore previousSneakState = false; Sneak(false); - return; } } - SendText(Settings.AntiAFK_Command); - Sneak(previousSneakState); - previousSneakState = !previousSneakState; + SendText(Config.Command); + if (Config.Use_Sneak) + { + Sneak(previousSneakState); + previousSneakState = !previousSneakState; + } count = 0; } diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index f9c6e820..dcd73349 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -1,15 +1,89 @@ using System; 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.whitelist; + + [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 +93,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 +129,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 +163,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 +175,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 +227,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..d463b024 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -1,14 +1,142 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; +using System.Text; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using Tomlet.Attributes; +using static MinecraftClient.ChatBots.AutoCraft.Configs; 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; + + [TomlInlineComment("$config.ChatBot.AutoCraft.CraftingTable$")] + public LocationConfig CraftingTable = new(123, 65, 456); + + [TomlInlineComment("$config.ChatBot.AutoCraft.OnFailure$")] + public OnFailConfig OnFailure = OnFailConfig.abort; + + [TomlPrecedingComment("$config.ChatBot.AutoCraft.Recipes$")] + public RecipeConfig[] Recipes = new RecipeConfig[] + { + new RecipeConfig( + Name: "Recipe-Name-1", + Type: CraftTypeConfig.player, + Result: ItemType.StoneBricks, + Slots: new ItemType[4] { ItemType.Stone, ItemType.Stone, ItemType.Stone, ItemType.Stone } + ), + new RecipeConfig( + Name: "Recipe-Name-2", + Type: CraftTypeConfig.table, + Result: ItemType.StoneBricks, + Slots: new ItemType[9] { + ItemType.Stone, ItemType.Stone, ItemType.Null, + ItemType.Stone, ItemType.Stone, ItemType.Null, + ItemType.Null, ItemType.Null, ItemType.Null, + } + ), + }; + + [NonSerialized] + public Location _Table_Location = Location.Zero; + + public void OnSettingUpdate() + { + _Table_Location = new Location(CraftingTable.X, CraftingTable.Y, CraftingTable.Z).ToFloor(); + + List nameList = new(); + foreach (RecipeConfig recipe in Recipes) + { + if (string.IsNullOrWhiteSpace(recipe.Name)) + { + recipe.Name = new Random().NextInt64().ToString(); + LogToConsole(BotName, Translations.TryGet("bot.autoCraft.exception.name_miss")); + } + if (nameList.Contains(recipe.Name)) + { + LogToConsole(BotName, Translations.TryGet("bot.autoCraft.exception.duplicate", recipe.Name)); + do + { + recipe.Name = new Random().NextInt64().ToString(); + } while (nameList.Contains(recipe.Name)); + } + nameList.Add(recipe.Name); + + int fixLength = -1; + if (recipe.Type == CraftTypeConfig.player && recipe.Slots.Length != 4) + fixLength = 4; + else if (recipe.Type == CraftTypeConfig.table && recipe.Slots.Length != 9) + fixLength = 9; + + if (fixLength > 0) + { + ItemType[] Slots = new ItemType[fixLength]; + for (int i = 0; i < fixLength; ++i) + Slots[i] = (i < recipe.Slots.Length) ? recipe.Slots[i] : ItemType.Null; + recipe.Slots = Slots; + LogToConsole(BotName, Translations.TryGet("bot.autocraft.invaild_slots")); + } + + if (recipe.Result == ItemType.Air || recipe.Result == ItemType.Null) + { + LogToConsole(BotName, Translations.TryGet("bot.autocraft.invaild_result")); + } + } + } + + public struct LocationConfig + { + public double X, Y, Z; + + public LocationConfig(double X, double Y, double Z) + { + this.X = X; + this.Y = Y; + this.Z = Z; + } + } + + public enum OnFailConfig { abort, wait } + + public class RecipeConfig + { + public string Name = "Recipe Name"; + + public CraftTypeConfig Type = CraftTypeConfig.player; + + public ItemType Result = ItemType.Air; + + public ItemType[] Slots = new ItemType[9] { + ItemType.Null, ItemType.Null, ItemType.Null, + ItemType.Null, ItemType.Null, ItemType.Null, + ItemType.Null, ItemType.Null, ItemType.Null, + }; + + public RecipeConfig() { } + + public RecipeConfig(string Name, CraftTypeConfig Type, ItemType Result, ItemType[] Slots) + { + this.Name = Name; + this.Type = Type; + this.Result = Result; + this.Slots = Slots; + } + } + + public enum CraftTypeConfig { player, table } + } + private bool waitingForMaterials = false; private bool waitingForUpdate = false; private bool waitingForTable = false; @@ -18,19 +146,12 @@ namespace MinecraftClient.ChatBots private Recipe? recipeInUse; private readonly List actionSteps = new(); - private Location tableLocation = new(); - private bool abortOnFailure = true; private int updateDebounceValue = 2; private int updateDebounce = 0; private readonly int updateTimeoutValue = 10; 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(); - private void ResetVar() { craftingFailed = false; @@ -158,11 +279,6 @@ namespace MinecraftClient.ChatBots } } - public AutoCraft(string configPath = @"autocraft\config.ini") - { - this.configPath = configPath; - } - public override void Initialize() { if (!GetInventoryEnabled()) @@ -174,7 +290,6 @@ namespace MinecraftClient.ChatBots } RegisterChatBotCommand("autocraft", Translations.Get("bot.autoCraft.cmd"), GetHelp(), CommandHandler); RegisterChatBotCommand("ac", Translations.Get("bot.autoCraft.alias"), GetHelp(), CommandHandler); - LoadConfig(); } public string CommandHandler(string cmd, string[] args) @@ -183,32 +298,39 @@ namespace MinecraftClient.ChatBots { switch (args[0]) { - case "load": - LoadConfig(); - return ""; case "list": - string names = string.Join(", ", recipes.Keys.ToList()); - return Translations.Get("bot.autoCraft.cmd.list", recipes.Count, names); - case "reload": - recipes.Clear(); - LoadConfig(); - return ""; - case "resetcfg": - WriteDefaultConfig(); - return Translations.Get("bot.autoCraft.cmd.resetcfg"); + StringBuilder nameList = new(); + foreach (RecipeConfig recipe in Config.Recipes) + nameList.Append(recipe.Name).Append(", "); + return Translations.Get("bot.autoCraft.cmd.list", Config.Recipes.Length, nameList.ToString()); case "start": if (args.Length >= 2) { string name = args[1]; - if (recipes.ContainsKey(name)) + + bool hasRecipe = false; + RecipeConfig? recipe = null; + foreach (RecipeConfig recipeConfig in Config.Recipes) + { + if (recipeConfig.Name == name) + { + hasRecipe = true; + recipe = recipeConfig; + break; + } + } + + if (hasRecipe) { ResetVar(); - PrepareCrafting(recipes[name]); + PrepareCrafting(recipe!); return ""; } - else return Translations.Get("bot.autoCraft.recipe_not_exist"); + else + return Translations.Get("bot.autoCraft.recipe_not_exist"); } - else return Translations.Get("bot.autoCraft.no_recipe_name"); + else + return Translations.Get("bot.autoCraft.no_recipe_name"); case "stop": StopCrafting(); return Translations.Get("bot.autoCraft.stop"); @@ -243,220 +365,6 @@ namespace MinecraftClient.ChatBots }; } - #region Config handling - - public void LoadConfig() - { - if (!File.Exists(configPath)) - { - if (!Directory.Exists(configPath)) - { - Directory.CreateDirectory(@"autocraft"); - } - WriteDefaultConfig(); - LogDebugToConsoleTranslated("bot.autoCraft.debug.no_config"); - } - try - { - ParseConfig(); - LogToConsoleTranslated("bot.autoCraft.loaded"); - } - catch (Exception e) - { - LogToConsoleTranslated("bot.autoCraft.error.config", "\n" + e.Message); - } - } - - private void WriteDefaultConfig() - { - string[] content = - { - "[AutoCraft]", - "# A valid autocraft config must begin with [AutoCraft]", - "", - "tablelocation=0,65,0 # Location of the crafting table if you intended to use it. Terrain and movements must be enabled. Format: x,y,z", - "onfailure=abort # What to do on crafting failure, abort or wait", - "", - "# You can define multiple recipes in a single config file", - "# This is an example of how to define a recipe", - "[Recipe]", - "name=whatever # name could be whatever you like. This field must be defined first", - "type=player # crafting table type: player or table", - "result=StoneButton # the resulting item", - "", - "# define slots with their deserved item", - "slot1=Stone # slot start with 1, count from left to right, top to bottom", - "# 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); - } - - private void ParseConfig() - { - string[] content = File.ReadAllLines(configPath); - if (content.Length <= 0) - { - throw new Exception(Translations.Get("bot.autoCraft.exception.empty", configPath)); - } - if (content[0].ToLower() != "[autocraft]") - { - throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", configPath)); - } - - // local variable for use in parsing config - string section = ""; - Dictionary recipes = new(); - - foreach (string l in content) - { - // ignore comment start with # - if (l.StartsWith("#")) - continue; - string line = l.Split('#')[0].Trim(); - if (line.Length <= 0) - continue; - - if (line[0] == '[' && line[^1] == ']') - { - section = line[1..^1].ToLower(); - continue; - } - - string key = line.Split('=')[0].ToLower(); - if (!(line.Length > (key.Length + 1))) - continue; - string value = line[(key.Length + 1)..]; - switch (section) - { - case "recipe": ParseRecipe(key, value); break; - case "autocraft": ParseMain(key, value); break; - } - } - - // check and save recipe - foreach (var pair in recipes) - { - if ((pair.Value.CraftingAreaType == ContainerType.PlayerInventory - || pair.Value.CraftingAreaType == ContainerType.Crafting) - && (pair.Value.Materials != null - && pair.Value.Materials.Count > 0) - && pair.Value.ResultItem != ItemType.Air) - { - // checking pass - this.recipes.Add(pair.Key, pair.Value); - } - else - { - throw new Exception(Translations.Get("bot.autoCraft.exception.item_miss", pair.Key)); - } - } - - - } - - #region Method for parsing different section of config - - private void ParseMain(string key, string value) - { - switch (key) - { - case "tablelocation": - string[] values = value.Split(','); - if (values.Length == 3) - { - tableLocation.X = Convert.ToInt32(values[0]); - tableLocation.Y = Convert.ToInt32(values[1]); - tableLocation.Z = Convert.ToInt32(values[2]); - } - else throw new Exception(Translations.Get("bot.autoCraft.exception.invalid_table", key)); - break; - case "onfailure": - abortOnFailure = value.ToLower() == "abort"; - break; - case "updatedebounce": - updateDebounceValue = Convert.ToInt32(value); - break; - } - } - - private void ParseRecipe(string key, string value) - { - if (key.StartsWith("slot")) - { - int slot = Convert.ToInt32(key[^1].ToString()); - if (slot > 0 && slot < 10) - { - if (recipes.ContainsKey(lastRecipe)) - { - if (Enum.TryParse(value, true, out ItemType itemType)) - { - Dictionary? materials = recipes[lastRecipe].Materials; - if (materials != null && materials.Count > 0) - { - materials.Add(slot, itemType); - } - else - { - recipes[lastRecipe].Materials = new Dictionary() - { - { slot, itemType } - }; - } - return; - } - else - { - throw new Exception(Translations.Get("bot.autoCraft.exception.item_name", lastRecipe, key)); - } - } - else - { - throw new Exception(Translations.Get("bot.autoCraft.exception.name_miss")); - } - } - else - { - throw new Exception(Translations.Get("bot.autoCraft.exception.slot", key)); - } - } - else - { - switch (key) - { - case "name": - if (!recipes.ContainsKey(value)) - { - recipes.Add(value, new Recipe()); - lastRecipe = value; - } - else - { - throw new Exception(Translations.Get("bot.autoCraft.exception.duplicate", value)); - } - break; - case "type": - if (recipes.ContainsKey(lastRecipe)) - { - recipes[lastRecipe].CraftingAreaType = value.ToLower() == "player" ? ContainerType.PlayerInventory : ContainerType.Crafting; - } - break; - case "result": - if (recipes.ContainsKey(lastRecipe)) - { - if (Enum.TryParse(value, true, out ItemType itemType)) - { - recipes[lastRecipe].ResultItem = itemType; - } - } - break; - } - } - } - #endregion - - #endregion - #region Core part of auto-crafting public override void OnInventoryUpdate(int inventoryId) @@ -525,9 +433,19 @@ namespace MinecraftClient.ChatBots /// Prepare the crafting action steps by the given recipe name and start crafting /// /// Name of the recipe to craft - private void PrepareCrafting(string name) + private void PrepareCrafting(RecipeConfig recipeConfig) { - PrepareCrafting(recipes[name]); + Dictionary materials = new(); + for (int i = 0; i < recipeConfig.Slots.Length; ++i) + if (recipeConfig.Slots[i] != ItemType.Null) + materials[i + 1] = recipeConfig.Slots[i]; + + ItemType ResultItem = recipeConfig.Result; + + ContainerType CraftingAreaType = + (recipeConfig.Type == CraftTypeConfig.player) ? ContainerType.PlayerInventory : ContainerType.Crafting; + + PrepareCrafting(new Recipe(materials, ResultItem, CraftingAreaType)); } /// @@ -548,7 +466,7 @@ namespace MinecraftClient.ChatBots if (inventoryInUse == -2) { // table required but not found. Try to open one - OpenTable(tableLocation); + OpenTable(Config._Table_Location); waitingForTable = true; SetTimeout(Translations.Get("bot.autoCraft.table_not_found")); return; @@ -698,7 +616,7 @@ namespace MinecraftClient.ChatBots // Inform user the missing meterial name LogToConsoleTranslated("bot.autoCraft.missing_material", actionSteps[index - 1].ItemType.ToString()); } - if (abortOnFailure) + if (Config.OnFailure == OnFailConfig.abort) { StopCrafting(); LogToConsoleTranslated("bot.autoCraft.aborted"); diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs new file mode 100644 index 00000000..9d0c585c --- /dev/null +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -0,0 +1,423 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MinecraftClient.Mapping; +using Tomlet.Attributes; + +namespace MinecraftClient.ChatBots +{ + public class AutoDig : ChatBot + { + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs + { + [NonSerialized] + private const string BotName = "AutoDig"; + + public bool Enabled = false; + + [NonSerialized] + [TomlInlineComment("$config.ChatBot.AutoDig.Auto_Tool_Switch$")] + public bool Auto_Tool_Switch = false; + + [NonSerialized] + [TomlInlineComment("$config.ChatBot.AutoDig.Durability_Limit$")] + public int Durability_Limit = 2; + + [NonSerialized] + [TomlInlineComment("$config.ChatBot.AutoDig.Drop_Low_Durability_Tools$")] + public bool Drop_Low_Durability_Tools = false; + + [TomlInlineComment("$config.ChatBot.AutoDig.Mode$")] + public ModeType Mode = ModeType.lookat; + + [TomlInlineComment("$config.ChatBot.AutoDig.Locations$")] + public Coordination[] Locations = new Coordination[] { new(123.5, 64, 234.5), new(124.5, 63, 235.5) }; + + [TomlInlineComment("$config.ChatBot.AutoDig.Location_Order$")] + public OrderType Location_Order = OrderType.distance; + + [TomlInlineComment("$config.ChatBot.AutoDig.Auto_Start_Delay$")] + public double Auto_Start_Delay = 3.0; + + [TomlInlineComment("$config.ChatBot.AutoDig.Dig_Timeout$")] + public double Dig_Timeout = 60.0; + + [TomlInlineComment("$config.ChatBot.AutoDig.Log_Block_Dig$")] + public bool Log_Block_Dig = true; + + [TomlInlineComment("$config.ChatBot.AutoDig.List_Type$")] + public ListType List_Type = ListType.whitelist; + + public List Blocks = new() { Material.Cobblestone, Material.Stone }; + + [NonSerialized] + public Location[] _Locations = Array.Empty(); + + public void OnSettingUpdate() + { + if (Auto_Start_Delay >= 0) + Auto_Start_Delay = Math.Max(0.1, Auto_Start_Delay); + + if (Dig_Timeout >= 0) + Dig_Timeout = Math.Max(0.1, Dig_Timeout); + + _Locations = new Location[Locations.Length]; + for (int i = 0; i < Locations.Length; ++i) + _Locations[i] = new(Locations[i].x, Locations[i].y, Locations[i].z); + } + + public enum ModeType { lookat, fixedpos, both }; + + public enum ListType { blacklist, whitelist }; + + public enum OrderType { distance, index }; + + 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; + } + } + } + + private bool inventoryEnabled; + + private int counter = 0; + private readonly object stateLock = new(); + private State state = State.WaitJoinGame; + + bool AlreadyWaitting = false; + private Location currentDig = Location.Zero; + + private enum State + { + WaitJoinGame, + WaitingStart, + Digging, + Stopping, + } + + public override void Initialize() + { + if (!GetTerrainEnabled()) + { + LogToConsoleTranslated("extra.terrainandmovement_required"); + LogToConsoleTranslated("general.bot_unload"); + UnloadBot(); + return; + } + + inventoryEnabled = GetInventoryEnabled(); + if (!inventoryEnabled && Config.Auto_Tool_Switch) + LogToConsoleTranslated("bot.autodig.no_inv_handle"); + + RegisterChatBotCommand("digbot", Translations.Get("bot.digbot.cmd"), GetHelp(), CommandHandler); + } + + public string CommandHandler(string cmd, string[] args) + { + if (args.Length > 0) + { + switch (args[0]) + { + case "start": + lock (stateLock) + { + counter = 0; + state = State.WaitingStart; + } + return Translations.Get("bot.autodig.start"); + case "stop": + StopDigging(); + return Translations.Get("bot.autodig.stop"); + case "help": + return GetCommandHelp(args.Length >= 2 ? args[1] : ""); + default: + return GetHelp(); + } + } + else return GetHelp(); + } + + private void StartDigging() + { + if (Config.Auto_Start_Delay > 0) + { + double delay = Config.Auto_Start_Delay; + LogToConsole(Translations.Get("bot.autodig.start_delay", delay)); + lock (stateLock) + { + counter = Settings.DoubleToTick(delay); + state = State.WaitingStart; + } + } + else + { + lock (stateLock) + { + state = State.WaitJoinGame; + } + } + } + + private void StopDigging() + { + state = State.Stopping; + lock (stateLock) + { + state = State.Stopping; + } + } + + public override void Update() + { + lock (stateLock) + { + switch (state) + { + case State.WaitJoinGame: + break; + case State.WaitingStart: + if (--counter < 0) + { + if (DoDigging()) + { + AlreadyWaitting = false; + state = State.Digging; + } + else + { + counter = 0; + state = State.WaitingStart; + } + } + break; + case State.Digging: + if (++counter > Settings.DoubleToTick(Config.Dig_Timeout)) + { + LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autodig.dig_timeout")); + state = State.WaitingStart; + counter = 0; + } + break; + case State.Stopping: + break; + } + } + } + + private bool DoDigging() + { + if (Config.Mode == Configs.ModeType.lookat || Config.Mode == Configs.ModeType.both) + { + (bool hasBlock, Location blockLoc, Block block) = GetLookingBlock(4.5, false); + if (!hasBlock) + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.too_far")); + } + return false; + } + else if (block.Type == Material.Air) + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.no_block")); + } + return false; + } + else if ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || + (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type))) + { + if (Config.Mode == Configs.ModeType.lookat || + (Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc))) + { + if (DigBlock(blockLoc, lookAtBlock: false)) + { + currentDig = blockLoc; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.dig", blockLoc.X, blockLoc.Y, blockLoc.Z, block.Type)); + return true; + } + else + { + LogToConsole(Translations.Get("cmd.dig.fail")); + return false; + } + } + else + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("bot.autodig.not_allow")); + } + return false; + } + } + else + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("bot.autodig.not_allow")); + } + return false; + } + } + else if (Config.Mode == Configs.ModeType.fixedpos && Config.Location_Order == Configs.OrderType.distance) + { + Location current = GetCurrentLocation(); + + double minDistance = double.MaxValue; + Location target = Location.Zero; + Block targetBlock = Block.Air; + foreach (Location location in Config._Locations) + { + Block block = GetWorld().GetBlock(location); + if (block.Type != Material.Air && + ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || + (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type)))) + { + double distance = current.Distance(location); + if (distance < minDistance) + { + minDistance = distance; + target = location; + targetBlock = block; + } + } + } + + if (minDistance <= 6.0) + { + if (DigBlock(target, lookAtBlock: true)) + { + currentDig = target; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.dig", target.X, target.Y, target.Z, targetBlock.Type)); + return true; + } + else + { + LogToConsole(Translations.Get("cmd.dig.fail")); + return false; + } + } + else + { + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.no_block")); + } + return false; + } + } + else if (Config.Mode == Configs.ModeType.fixedpos && Config.Location_Order == Configs.OrderType.index) + { + for (int i = 0; i < Config._Locations.Length; ++i) + { + Location blockLoc = Config._Locations[i]; + Block block = GetWorld().GetBlock(blockLoc); + if (block.Type != Material.Air && + ((Config.List_Type == Configs.ListType.whitelist && Config.Blocks.Contains(block.Type)) || + (Config.List_Type == Configs.ListType.blacklist && !Config.Blocks.Contains(block.Type)))) + { + if (DigBlock(blockLoc, lookAtBlock: true)) + { + currentDig = blockLoc; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.dig", blockLoc.X, blockLoc.Y, blockLoc.Z, block.Type)); + return true; + } + else + { + LogToConsole(Translations.Get("cmd.dig.fail")); + return false; + } + } + } + + if (!AlreadyWaitting) + { + AlreadyWaitting = true; + if (Config.Log_Block_Dig) + LogToConsole(Translations.Get("cmd.dig.no_block")); + } + return false; + } + return false; + } + + public override void OnBlockChange(Location location, Block block) + { + if (location == currentDig) + { + lock (stateLock) + { + if (state == State.Digging && location == currentDig) + { + currentDig = Location.Zero; + counter = 0; + state = State.WaitingStart; + } + } + } + } + + public override void AfterGameJoined() + { + StartDigging(); + } + + public override void OnRespawn() + { + StartDigging(); + } + + public override void OnDeath() + { + StopDigging(); + } + + public override bool OnDisconnect(DisconnectReason reason, string message) + { + StopDigging(); + + return base.OnDisconnect(reason, message); + } + + private static string GetHelp() + { + return Translations.Get("bot.autodig.available_cmd", "start, stop, help"); + } + + private string GetCommandHelp(string cmd) + { + return cmd.ToLower() switch + { +#pragma warning disable format // @formatter:off + "start" => Translations.Get("bot.autodig.help.start"), + "stop" => Translations.Get("bot.autodig.help.stop"), + "help" => Translations.Get("bot.autodig.help.help"), + _ => GetHelp(), +#pragma warning restore format // @formatter:on + }; + } + } +} diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index e101da54..a4e3f073 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -2,51 +2,42 @@ 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; + + 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 +45,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 +74,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 +94,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 +108,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 +169,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 +190,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..609ced3b 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -1,6 +1,8 @@ using System; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using Tomlet.Attributes; +using static MinecraftClient.ChatBots.AutoFishing.Configs; namespace MinecraftClient.ChatBots { @@ -8,8 +10,130 @@ 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.Enable_Move$")] + public bool Enable_Move = false; + + [TomlPrecedingComment("$config.ChatBot.AutoFishing.Movements$")] + public LocationConfig[] Movements = new LocationConfig[] + { + new LocationConfig(12.34, -23.45), + new LocationConfig(123.45, 64, -654.32, -25.14, 36.25), + new LocationConfig(-1245.63, 63.5, 1.2), + }; + + 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(double yaw, double 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, double yaw, double 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 double yaw, pitch; + + public Facing(double yaw, double pitch) + { + this.yaw = yaw; this.pitch = pitch; + } + } + } + } + private int fishCount = 0; private bool inventoryEnabled; private int castTimeout = 12; @@ -54,13 +178,13 @@ 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) { - counter = (int)(delay * 10); + counter = Settings.DoubleToTick(delay); state = FishingState.StartMove; } } @@ -84,7 +208,7 @@ namespace MinecraftClient.ChatBots private void UseFishRod() { - if (Settings.AutoFishing_Mainhand) + if (Config.Mainhand) UseItemInHand(); else UseItemInLeftHand(); @@ -100,7 +224,7 @@ namespace MinecraftClient.ChatBots break; case FishingState.WaitingToCast: if (AutoEat.Eating) - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); else if (--counter < 0) state = FishingState.CastingRod; break; @@ -116,28 +240,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 = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } break; case FishingState.WaitingFishToBite: - if (++counter > (int)(Settings.AutoFishing_FishingTimeout * 10)) + if (++counter > Settings.DoubleToTick(Config.Fishing_Timeout)) { LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout")); - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } break; case FishingState.StartMove: if (--counter < 0) { - double[,]? locationList = Settings.AutoFishing_Location; - if (locationList != null) + if (Config.Enable_Move && Config.Movements.Length > 0) { if (GetTerrainEnabled()) { - UpdateLocation(locationList); + UpdateLocation(Config.Movements); state = FishingState.WaitingMovement; } else @@ -148,7 +271,7 @@ namespace MinecraftClient.ChatBots } else { - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.DurabilityCheck; goto case FishingState.DurabilityCheck; } @@ -167,7 +290,7 @@ namespace MinecraftClient.ChatBots case FishingState.DurabilityCheck: if (DurabilityCheck()) { - counter = (int)(Settings.AutoFishing_CastDelay * 10); + counter = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } break; @@ -181,7 +304,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 +325,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 = Settings.DoubleToTick(Config.Cast_Delay); state = FishingState.WaitingToCast; } } @@ -233,12 +356,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 +406,7 @@ namespace MinecraftClient.ChatBots public void OnCaughtFish() { ++fishCount; - if (Settings.AutoFishing_Location != null) + if (Config.Enable_Move && 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 +421,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) = ((float)curConfig.facing.Value.yaw, (float)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 +459,7 @@ namespace MinecraftClient.ChatBots if (!isMoveSuccessed) { - nextYaw = GetYaw(); - nextPitch = GetPitch(); + (nextYaw, nextPitch) = (GetYaw(), GetPitch()); LogToConsole(Translations.Get("cmd.move.fail", goal)); } else @@ -365,13 +476,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 +492,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..ba16264a 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -1,5 +1,5 @@ using System; -using System.Text; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -8,11 +8,68 @@ 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(3); + + [TomlInlineComment("$config.ChatBot.AutoRelog.Retries$")] + public int Retries = 3; + + [TomlInlineComment("$config.ChatBot.AutoRelog.Ignore_Kick_Message$")] + public bool Ignore_Kick_Message = false; + + [TomlPrecedingComment("$config.ChatBot.AutoRelog.Kick_Messages$")] + public string[] Kick_Messages = new string[] { "Connection has been lost", "Server is restarting", "Server is full", "Too Many people" }; + + [NonSerialized] + public static int _BotRecoAttempts = 0; + + public void OnSettingUpdate() + { + Delay.min = Math.Max(0.1, Delay.min); + Delay.max = Math.Max(0.1, Delay.max); + + Delay.min = Math.Min(int.MaxValue / 10, Delay.min); + Delay.max = Math.Min(int.MaxValue / 10, Delay.max); + + if (Delay.min > Delay.max) + (Delay.min, Delay.max) = (Delay.max, Delay.min); + + if (Retries == -1) + Retries = int.MaxValue; + + if (Enabled) + for (int i = 0; i < Kick_Messages.Length; i++) + Kick_Messages[i] = Kick_Messages[i].ToLower(); + } + + public struct Range + { + public double min, max; + + public Range(int value) + { + min = max = value; + } + + public Range(int min, int max) + { + 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,48 +77,18 @@ 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)) - { - LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile)); - - dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile, Encoding.UTF8); - - for (int i = 0; i < dictionary.Length; i++) - { - LogDebugToConsoleTranslated("bot.autoRelog.loaded", dictionary[i]); - dictionary[i] = dictionary[i].ToLower(); - } - } - else - { - LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile)); - - LogDebugToConsoleTranslated("bot.autoRelog.curr_dir", System.IO.Directory.GetCurrentDirectory()); - } - } } public override bool OnDisconnect(DisconnectReason reason, string message) @@ -70,23 +97,25 @@ namespace MinecraftClient.ChatBots { LogDebugToConsoleTranslated("bot.autoRelog.ignore_user_logout"); } - else + else if (Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries) { message = GetVerbatim(message); string comp = message.ToLower(); LogDebugToConsoleTranslated("bot.autoRelog.disconnect_msg", message); - if (Settings.AutoRelog_IgnoreKickMessage) + if (Config.Ignore_Kick_Message) { + Configs._BotRecoAttempts++; LaunchDelayedReconnection(null); return true; } - foreach (string msg in dictionary) + foreach (string msg in Config.Kick_Messages) { if (comp.Contains(msg)) { + Configs._BotRecoAttempts++; LaunchDelayedReconnection(msg); return true; } @@ -100,7 +129,7 @@ namespace MinecraftClient.ChatBots private void LaunchDelayedReconnection(string? msg) { - int delay = random.Next(delayMin, delayMax); + int delay = random.Next(Settings.DoubleToTick(Config.Delay.min), Settings.DoubleToTick(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 +138,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..4ff83a6b 100644 --- a/MinecraftClient/ChatBots/AutoRespond.cs +++ b/MinecraftClient/ChatBots/AutoRespond.cs @@ -1,31 +1,52 @@ 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() + { + Matches_File ??= string.Empty; + + 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 +117,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 +185,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 +196,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 +232,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 +243,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 +285,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 +298,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..041b58cb 100644 --- a/MinecraftClient/ChatBots/ChatLog.cs +++ b/MinecraftClient/ChatBots/ChatLog.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -9,13 +10,44 @@ 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() + { + Log_File ??= string.Empty; + + 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 +57,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 +125,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..5d202ab1 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 double Update_Limit = 1; + + [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 < Settings.DoubleToTick(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..0eb923c9 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,37 @@ 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() + { + FileWords_EN ??= string.Empty; + FileWords_FR ??= string.Empty; + } + } + 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 +52,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 +67,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 +93,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 +103,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 +144,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..51eada78 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,63 @@ 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() + { + DatabaseFile ??= string.Empty; + IgnoreListFile ??= string.Empty; + + 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 +219,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 +261,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 +273,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 +284,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 +303,7 @@ namespace MinecraftClient.ChatBots lock (readWriteLock) { mailDatabase.Add(mail); - mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile); + mailDatabase.SaveToFile(Config.DatabaseFile); } SendPrivateMessage(username, "Message saved!"); } @@ -307,8 +344,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 +361,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 +394,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 +406,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..c589b6b6 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -3,43 +3,62 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; +using System.Runtime.Versioning; 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(); @@ -76,15 +95,22 @@ namespace MinecraftClient.ChatBots if (!cachedMaps.ContainsKey(mapId)) return Translations.TryGet("bot.map.cmd.not_found", mapId); - try + if (OperatingSystem.IsWindows()) { - McMap map = cachedMaps[mapId]; - GenerateMapImage(map); + try + { + McMap map = cachedMaps[mapId]; + GenerateMapImage(map); + } + catch (Exception e) + { + LogDebugToConsole(e.StackTrace!); + return Translations.TryGet("bot.map.failed_to_render", mapId); + } } - catch (Exception e) + else { - LogDebugToConsole(e.StackTrace!); - return Translations.TryGet("bot.map.failed_to_render", mapId); + LogToConsoleTranslated("bot.map.windows_only"); } return ""; @@ -124,7 +150,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,10 +159,16 @@ namespace MinecraftClient.ChatBots cachedMaps.Add(mapid, map); } - if (autoRenderOnUpdate) - GenerateMapImage(map); + if (Config.Auto_Render_On_Update) + { + if (OperatingSystem.IsWindows()) + GenerateMapImage(map); + else + LogToConsoleTranslated("bot.map.windows_only"); + } } + [SupportedOSPlatform("windows")] private void GenerateMapImage(McMap map) { string fileName = baseDirectory + "/Map_" + map.MapId + ".jpg"; @@ -144,6 +176,8 @@ namespace MinecraftClient.ChatBots if (File.Exists(fileName)) File.Delete(fileName); + /** Warning CA1416: Bitmap is only support Windows **/ + /* https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/system-drawing-common-windows-only */ Bitmap image = new(map.Width, map.Height); for (int x = 0; x < map.Width; ++x) @@ -165,12 +199,14 @@ 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)); } + + [SupportedOSPlatform("windows")] private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height) { Bitmap result = new(width, height); diff --git a/MinecraftClient/ChatBots/PlayerListLogger.cs b/MinecraftClient/ChatBots/PlayerListLogger.cs index b15d5f92..8c59af89 100644 --- a/MinecraftClient/ChatBots/PlayerListLogger.cs +++ b/MinecraftClient/ChatBots/PlayerListLogger.cs @@ -1,6 +1,6 @@ using System; -using System.Linq; using System.Text; +using Tomlet.Attributes; namespace MinecraftClient.ChatBots { @@ -10,28 +10,36 @@ 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 double Delay = 60; + + public void OnSettingUpdate() + { + File ??= string.Empty; + + if (Delay < 1.0) + Delay = 1.0; + } } + private int count = 0; + public override void Update() { count++; - if (count == timeping) + if (count >= Settings.DoubleToTick(Config.Delay)) { DateTime now = DateTime.Now; @@ -40,7 +48,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..642d2223 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -1,7 +1,6 @@ using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; +using Tomlet.Attributes; +using static MinecraftClient.ChatBots.ScriptScheduler.Configs; namespace MinecraftClient.ChatBots { @@ -11,143 +10,164 @@ namespace MinecraftClient.ChatBots public class ScriptScheduler : ChatBot { - private class TaskDesc + public static Configs Config = new(); + + [TomlDoNotInlineObject] + public class Configs { - public string? action = null; - public bool triggerOnFirstLogin = false; - public bool triggerOnLogin = false; - public bool triggerOnTime = false; - public bool triggerOnInterval = false; - public int triggerOnInterval_Interval = 0; - public int triggerOnInterval_Interval_Max = 0; - public int triggerOnInterval_Interval_Countdown = 0; - public List triggerOnTime_Times = new(); - public bool triggerOnTime_alreadyTriggered = false; - } + [NonSerialized] + private const string BotName = "ScriptScheduler"; - private static bool firstlogin_done = false; + public bool Enabled = false; - private readonly string tasksfile; - private bool serverlogin_done; - private readonly List tasks = new(); - private int verifytasks_timeleft = 10; - private readonly int verifytasks_delay = 10; + public TaskConfig[] TaskList = new TaskConfig[] { + new TaskConfig( + Task_Name: "Task Name 1", + Trigger_On_First_Login: false, + Trigger_On_Login: false, + Trigger_On_Times: new(true, new TimeSpan[] { new(14, 00, 00) }), + Trigger_On_Interval: new(true, 3.6, 4.8), + Action: "send /hello" + ), + new TaskConfig( + Task_Name: "Task Name 2", + Trigger_On_First_Login: false, + Trigger_On_Login: true, + Trigger_On_Times: new(false, Array.Empty() ), + Trigger_On_Interval: new(false, 1, 10), + Action: "send /login pass" + ), + }; - 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)) + public void OnSettingUpdate() { - LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(tasksfile)); - TaskDesc? current_task = null; - string[] lines = System.IO.File.ReadAllLines(tasksfile, Encoding.UTF8); - foreach (string lineRAW in lines) + foreach (TaskConfig task in TaskList) { - string line = lineRAW.Split('#')[0].Trim(); - if (line.Length > 0) + task.Trigger_On_Interval.MinTime = Math.Max(0.1, task.Trigger_On_Interval.MinTime); + task.Trigger_On_Interval.MaxTime = Math.Max(0.1, task.Trigger_On_Interval.MaxTime); + if (task.Trigger_On_Interval.MinTime > task.Trigger_On_Interval.MaxTime) + (task.Trigger_On_Interval.MinTime, task.Trigger_On_Interval.MaxTime) = (task.Trigger_On_Interval.MaxTime, task.Trigger_On_Interval.MinTime); + + //Look for a valid action + if (!String.IsNullOrWhiteSpace(task.Action)) { - if (line[0] == '[' && line[^1] == ']') + //Look for a valid trigger + if (task.Trigger_On_Login + || task.Trigger_On_First_Login + || (task.Trigger_On_Times.Enable && task.Trigger_On_Times.Times.Length > 0) + || (task.Trigger_On_Interval.Enable && task.Trigger_On_Interval.MinTime > 0)) { - switch (line[1..^1].ToLower()) - { - case "task": - CheckAddTask(current_task); - current_task = new TaskDesc(); //Create a blank task - break; - } + if (Settings.Config.Logging.DebugMessages) + LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.loaded_task", Task2String(task))); + task.Trigger_On_Interval_Countdown = Settings.DoubleToTick(task.Trigger_On_Interval.MinTime); //Init countdown for interval } - else if (current_task != null) + else { - string argName = line.Split('=')[0]; - if (line.Length > (argName.Length + 1)) - { - 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 "timevalue": try { current_task.triggerOnTime_Times.Add(DateTime.ParseExact(argValue, "HH:mm", CultureInfo.InvariantCulture)); } catch { } break; - case "timeinterval": - int interval; - int intervalMax = 0; - - if (argValue.Contains('-')) - { - string[] parts = argValue.Split("-"); - if (parts.Length == 2) - { - interval = int.Parse(parts[0].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture); - intervalMax = int.Parse(parts[1].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture); - } - else - { - interval = 1; - } - } - else - { - interval = int.Parse(argValue, NumberStyles.Any, CultureInfo.CurrentCulture); - } - - current_task.triggerOnInterval_Interval = interval; - current_task.triggerOnInterval_Interval_Max = intervalMax; - - break; - case "script": current_task.action = "script " + argValue; break; //backward compatibility with older tasks.ini - case "action": current_task.action = argValue; break; - } - } + if (Settings.Config.Logging.DebugMessages) + LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.no_trigger", Task2String(task))); } } - } - CheckAddTask(current_task); - } - else - { - LogToConsoleTranslated("bot.scriptScheduler.not_found", System.IO.Path.GetFullPath(tasksfile)); - UnloadBot(); //No need to keep the bot active - } - } - - private void CheckAddTask(TaskDesc? current_task) - { - //Check if we built a valid task before adding it - if (current_task != null) - { - //Look for a valid action - if (!String.IsNullOrWhiteSpace(current_task.action)) - { - //Look for a valid trigger - if (current_task.triggerOnLogin - || current_task.triggerOnFirstLogin - || (current_task.triggerOnTime && current_task.triggerOnTime_Times.Count > 0) - || (current_task.triggerOnInterval && current_task.triggerOnInterval_Interval > 0)) - { - - LogDebugToConsoleTranslated("bot.scriptScheduler.loaded_task", Task2String(current_task)); - current_task.triggerOnInterval_Interval_Countdown = current_task.triggerOnInterval_Interval; //Init countdown for interval - tasks.Add(current_task); - } else { - LogDebugToConsoleTranslated("bot.scriptScheduler.no_trigger", Task2String(current_task)); + task.Action ??= string.Empty; + if (Settings.Config.Logging.DebugMessages) + LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.no_action", Task2String(task))); } } - else + + if (Enabled && TaskList.Length == 0) { - LogDebugToConsoleTranslated("bot.scriptScheduler.no_action", Task2String(current_task)); + LogToConsole(BotName, Translations.TryGet("general.bot_unload")); + Enabled = false; + } + } + + public class TaskConfig + { + public string Task_Name = "Task Name (Can be empty)"; + public bool Trigger_On_First_Login = false; + public bool Trigger_On_Login = false; + public TriggerOnTimeConfig Trigger_On_Times = new(false, new TimeSpan[] { new(23, 59, 59) }); + public TriggerOnIntervalConfig Trigger_On_Interval = new(false, 1, 10); + public string Action = "send /hello"; + + [NonSerialized] + public bool Trigger_On_Time_Already_Triggered = false; + + [NonSerialized] + public int Trigger_On_Interval_Countdown = 0; + + public TaskConfig() { } + + public TaskConfig(string Task_Name, bool Trigger_On_First_Login, bool Trigger_On_Login, TriggerOnTimeConfig Trigger_On_Times, TriggerOnIntervalConfig Trigger_On_Interval, string Action) + { + this.Task_Name = Task_Name; + this.Trigger_On_First_Login = Trigger_On_First_Login; + this.Trigger_On_Login = Trigger_On_Login; + this.Trigger_On_Times = Trigger_On_Times; + this.Trigger_On_Interval = Trigger_On_Interval; + this.Action = Action; + } + + public struct TriggerOnTimeConfig + { + public bool Enable = false; + public TimeSpan[] Times; + + public TriggerOnTimeConfig(bool Enable, TimeSpan[] Time) + { + this.Enable = Enable; + this.Times = Time; + } + + public TriggerOnTimeConfig(TimeSpan[] Time) + { + this.Enable = true; + this.Times = Time; + } + } + + public struct TriggerOnIntervalConfig + { + public bool Enable = false; + public double MinTime, MaxTime; + + public TriggerOnIntervalConfig(double value) + { + this.Enable = true; + MinTime = MaxTime = value; + } + + public TriggerOnIntervalConfig(bool Enable, double value) + { + this.Enable = Enable; + MinTime = MaxTime = value; + } + + public TriggerOnIntervalConfig(double min, double max) + { + this.MinTime = min; + this.MaxTime = max; + } + + public TriggerOnIntervalConfig(bool Enable, double min, double max) + { + this.Enable = Enable; + this.MinTime = min; + this.MaxTime = max; + } } } } + private Random random = new(); + + private static bool firstlogin_done = false; + + private bool serverlogin_done = false; + private int verifytasks_timeleft = 10; + private readonly int verifytasks_delay = 10; + public override void Update() { if (verifytasks_timeleft <= 0) @@ -155,55 +175,40 @@ namespace MinecraftClient.ChatBots verifytasks_timeleft = verifytasks_delay; if (serverlogin_done) { - foreach (TaskDesc task in tasks) + foreach (TaskConfig task in Config.TaskList) { - if (task.triggerOnTime) + if (task.Trigger_On_Times.Enable) { bool matching_time_found = false; - foreach (DateTime time in task.triggerOnTime_Times) + foreach (TimeSpan time in task.Trigger_On_Times.Times) { - if (time.Hour == DateTime.Now.Hour && time.Minute == DateTime.Now.Minute) + if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute) { matching_time_found = true; - if (!task.triggerOnTime_alreadyTriggered) + if (!task.Trigger_On_Time_Already_Triggered) { - task.triggerOnTime_alreadyTriggered = true; - LogDebugToConsoleTranslated("bot.scriptScheduler.running_time", task.action); - PerformInternalCommand(task.action!); + task.Trigger_On_Time_Already_Triggered = true; + LogDebugToConsoleTranslated("bot.scriptScheduler.running_time", task.Action); + PerformInternalCommand(task.Action); } } } if (!matching_time_found) - task.triggerOnTime_alreadyTriggered = false; + task.Trigger_On_Time_Already_Triggered = false; } - if (task.triggerOnInterval) - { - if (task.triggerOnInterval_Interval_Countdown == 0) - { - int time = task.triggerOnInterval_Interval; - - if (task.triggerOnInterval_Interval_Max != 0) - time = new Random().Next(task.triggerOnInterval_Interval, task.triggerOnInterval_Interval_Max); - - task.triggerOnInterval_Interval_Countdown = time; - LogDebugToConsoleTranslated("bot.scriptScheduler.running_inverval", task.action); - PerformInternalCommand(task.action!); - } - else task.triggerOnInterval_Interval_Countdown--; - } } } else { - foreach (TaskDesc task in tasks) + foreach (TaskConfig task in Config.TaskList) { - if (task.triggerOnLogin || (firstlogin_done == false && task.triggerOnFirstLogin)) + if (task.Trigger_On_Login || (firstlogin_done == false && task.Trigger_On_First_Login)) { - LogDebugToConsoleTranslated("bot.scriptScheduler.running_login", task.action); - PerformInternalCommand(task.action!); + LogDebugToConsoleTranslated("bot.scriptScheduler.running_login", task.Action); + PerformInternalCommand(task.Action); } } @@ -212,6 +217,22 @@ namespace MinecraftClient.ChatBots } } else verifytasks_timeleft--; + + foreach (TaskConfig task in Config.TaskList) + { + if (task.Trigger_On_Interval.Enable) + { + if (task.Trigger_On_Interval_Countdown == 0) + { + task.Trigger_On_Interval_Countdown = random.Next( + Settings.DoubleToTick(task.Trigger_On_Interval.MinTime), Settings.DoubleToTick(task.Trigger_On_Interval.MaxTime) + ); + LogDebugToConsoleTranslated("bot.scriptScheduler.running_inverval", task.Action); + PerformInternalCommand(task.Action); + } + else task.Trigger_On_Interval_Countdown--; + } + } } public override bool OnDisconnect(DisconnectReason reason, string message) @@ -220,17 +241,17 @@ namespace MinecraftClient.ChatBots return false; } - private static string Task2String(TaskDesc task) + private static string Task2String(TaskConfig task) { return Translations.Get( "bot.scriptScheduler.task", - task.triggerOnFirstLogin, - task.triggerOnLogin, - task.triggerOnTime, - task.triggerOnInterval, - String.Join(", ", task.triggerOnTime_Times), - task.triggerOnInterval_Interval, - task.action + task.Trigger_On_First_Login, + task.Trigger_On_Login, + task.Trigger_On_Times.Enable, + task.Trigger_On_Interval.Enable, + task.Trigger_On_Times.Times, + task.Trigger_On_Interval.MinTime + '-' + task.Trigger_On_Interval.MaxTime, + task.Action ); } } diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index ee1b8c7b..8d606e25 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -32,16 +32,16 @@ namespace MinecraftClient.Commands sb.Append(World.GetChunkLoadingStatus(handler.GetWorld())); sb.Append('\n'); - sb.Append(String.Format("Current location:{0}, chunk: ({1}, {2}).\n", current, current.ChunkX, current.ChunkZ)); + sb.AppendLine(Translations.Get("cmd.chunk.current", current, current.ChunkX, current.ChunkZ)); if (markedChunkPos != null) { - sb.Append("Marked location: "); + sb.Append(Translations.Get("cmd.chunk.marked")); if (args.Length == 1 + 3) sb.Append(String.Format("X:{0:0.00} Y:{1:0.00} Z:{2:0.00}, ", double.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture), double.Parse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture), double.Parse(args[3], NumberStyles.Any, CultureInfo.CurrentCulture))); - sb.Append(String.Format("chunk: ({0}, {1}).\n", markChunkX, markChunkZ)); + sb.AppendLine(Translations.Get("cmd.chunk.chunk_pos", markChunkX, markChunkZ));; } int consoleHeight = Math.Max(Console.BufferHeight - 2, 25); @@ -127,7 +127,7 @@ namespace MinecraftClient.Commands if (markedChunkPos != null && (((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) || ((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth))) - sb.Append("§x§0Since the marked chunk is outside the graph, it will not be displayed!§r\n"); + sb.AppendLine(Translations.Get("cmd.chunk.outside")); else { topMost = Math.Min(topMost, markChunkZ); @@ -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 @@ -165,8 +165,7 @@ namespace MinecraftClient.Commands sb.Append('\n'); } - sb.Append("Player:§z §r, MarkedChunk:§w §r, "); - sb.Append(string.Format("NotReceived:{0}, Loading:{1}, Loaded:{2}", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); + sb.AppendLine(Translations.Get("cmd.chunk.icon", "§z §r", "§w §r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); return sb.ToString(); } else if (args[0] == "setloading") // For debugging @@ -174,7 +173,7 @@ namespace MinecraftClient.Commands Tuple? chunkPos = ParseChunkPos(args); if (chunkPos != null) { - handler.Log.Info("§x§0This command is used for debugging, make sure you know what you are doing.§r"); + handler.Log.Info(Translations.Get("cmd.chunk.for_debug")); World world = handler.GetWorld(); (int chunkX, int chunkZ) = chunkPos; ChunkColumn? chunkColumn = world[chunkX, chunkZ]; @@ -191,7 +190,7 @@ namespace MinecraftClient.Commands Tuple? chunkPos = ParseChunkPos(args); if (chunkPos != null) { - handler.Log.Info("§x§0This command is used for debugging, make sure you know what you are doing.§r"); + handler.Log.Info(Translations.Get("cmd.chunk.for_debug")); World world = handler.GetWorld(); (int chunkX, int chunkZ) = chunkPos; ChunkColumn? chunkColumn = world[chunkX, chunkZ]; @@ -208,7 +207,7 @@ namespace MinecraftClient.Commands Tuple? chunkPos = ParseChunkPos(args); if (chunkPos != null) { - handler.Log.Info("§x§0This command is used for debugging, make sure you know what you are doing.§r"); + handler.Log.Info(Translations.Get("cmd.chunk.for_debug")); World world = handler.GetWorld(); (int chunkX, int chunkZ) = chunkPos; world[chunkX, chunkZ] = null; 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/Dig.cs b/MinecraftClient/Commands/Dig.cs index 9438d1bc..ce825e98 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -33,14 +33,17 @@ namespace MinecraftClient.Commands try { Location current = handler.GetCurrentLocation(); - Location blockToBreak = Location.Parse(current, args[0], args[1], args[2]); + Location blockToBreak = Location.Parse(current.ToFloor(), args[0], args[1], args[2]); if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25) return Translations.Get("cmd.dig.too_far"); Block block = handler.GetWorld().GetBlock(blockToBreak); if (block.Type == Material.Air) return Translations.Get("cmd.dig.no_block"); else if (handler.DigBlock(blockToBreak)) + { + blockToBreak = blockToBreak.ToCenter(); return Translations.Get("cmd.dig.dig", blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.Type); + } else return Translations.Get("cmd.dig.fail"); } 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/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 05284afd..5d8bf32e 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -18,7 +18,7 @@ namespace MinecraftClient.Commands string[] args = GetArgs(command); if (args.Length >= 3) { - Location block = Location.Parse(handler.GetCurrentLocation(), args[0], args[1], args[2]).ToFloor(); + Location block = Location.Parse(handler.GetCurrentLocation().ToFloor(), args[0], args[1], args[2]).ToFloor(); Location blockCenter = block.ToCenter(); bool res = handler.PlaceBlock(block, Direction.Down); return Translations.Get("cmd.useblock.use", blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"); diff --git a/MinecraftClient/DefaultConfigResource.Designer.cs b/MinecraftClient/DefaultConfigResource.Designer.cs index d3dfe630..cb31f5aa 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 - { - + internal 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)) - { + internal 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 - { + internal 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 - { + internal 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 - { + internal 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]";. + /// + internal 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 - { + internal 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 - { + internal 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 - { + internal 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]";. + /// + internal 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]";. + /// + internal static string ContainerType_Hopper { + get { return ResourceManager.GetString("ContainerType_Hopper", resourceCulture); } } - + /// /// Looks up a localized string similar to ╔═════════════════════════════════════╗ ///║╔═══╦═══════════╗ ║ @@ -221,66 +241,168 @@ 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 - { + internal static string ContainerType_PlayerInventory { + get { return ResourceManager.GetString("ContainerType_PlayerInventory", resourceCulture); } } - - /// - /// Looks up a localized string similar to # Startup Config File - /// - ///[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]";. - /// - public static string MinecraftClient - { - get - { - return ResourceManager.GetString("MinecraftClient", resourceCulture); - } - } - + /// /// Looks up a localized string similar to [mcc] ///# Messages from MCC itself + ///mcc.help_us_translate= + ///mcc.run_with_default_settings= + ///mcc.settings_generated= + ///mcc.has_update= + ///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 Ses [rest of string was truncated]";. + /// + internal 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: {0} + ///mcc.run_with_default_settings=\nMCC is running with default settings. + ///mcc.settings_generated=§cSettings file MinecraftClient.ini has been generated. + ///mcc.has_update=§eNew version of MCC available: {0} ///mcc.login=Login : ///mcc.login_basic_io=Please type the username or email of your choice. - ///mcc.password=Password : + ///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.password_hidden=Password(invisible): {0} + ///mcc.off [rest of string was truncated]";. /// - public static string TranslationEnglish - { - get - { - return ResourceManager.GetString("TranslationEnglish", resourceCulture); + internal static string Translation_en { + get { + return ResourceManager.GetString("Translation_en", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.help_us_translate= + ///mcc.run_with_default_settings= + ///mcc.settings_generated= + ///mcc.has_update= + ///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_va [rest of string was truncated]";. + /// + internal static string Translation_fr { + get { + return ResourceManager.GetString("Translation_fr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.help_us_translate= + ///mcc.run_with_default_settings= + ///mcc.settings_generated= + ///mcc.has_update= + ///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Кэшированная се [rest of string was truncated]";. + /// + internal static string Translation_ru { + get { + return ResourceManager.GetString("Translation_ru", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.help_us_translate= + ///mcc.run_with_default_settings= + ///mcc.settings_generated= + ///mcc.has_update= + ///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.profile_ke [rest of string was truncated]";. + /// + internal 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:{0} + ///mcc.run_with_default_settings=\nMCC正在使用默认配置运行。 + ///mcc.settings_generated=§c配置文件 MinecraftClient.ini 已经生成。 + ///mcc.has_update=§e新版本的MCC已经推出:{0} + ///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]";. + /// + internal static string Translation_zh_Hans { + get { + return ResourceManager.GetString("Translation_zh_Hans", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [mcc] + ///# Messages from MCC itself + ///mcc.help_us_translate=幫助我們翻譯MCC:{0} + ///mcc.run_with_default_settings=\nMCC正在使用預設配置執行。 + ///mcc.settings_generated=§c配置檔案 MinecraftClient.ini 已經生成。 + ///mcc.has_update=§e新版本的MCC已經推出:{0} + ///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 [rest of string was truncated]";. + /// + internal static string Translation_zh_Hant { + get { + return ResourceManager.GetString("Translation_zh_Hant", resourceCulture); } } } diff --git a/MinecraftClient/DefaultConfigResource.resx b/MinecraftClient/DefaultConfigResource.resx index ed1a6338..4094de3f 100644 --- a/MinecraftClient/DefaultConfigResource.resx +++ b/MinecraftClient/DefaultConfigResource.resx @@ -145,10 +145,25 @@ Resources\containers\ContainerType.PlayerInventory.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - 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 + + + resources\lang\zh-hant.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/Inventory/Item.cs b/MinecraftClient/Inventory/Item.cs index 6d21a5fb..37e72017 100644 --- a/MinecraftClient/Inventory/Item.cs +++ b/MinecraftClient/Inventory/Item.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using System.Text; -using System.Linq; using System.Globalization; +using System.Linq; +using System.Text; namespace MinecraftClient.Inventory { 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..168e126b 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 { @@ -8,19 +9,38 @@ namespace MinecraftClient.Logger protected bool ShouldDisplay(FilterChannel channel, string msg) { + if (Config.Logging.FilterMode == LoggingConfigHealper.LoggingConfig.FilterModeEnum.disable) + return true; + Regex? regexToUse = null; // Convert to bool for XOR later. Whitelist = 0, Blacklist = 1 - bool filterMode = Settings.FilterMode == Settings.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) { // IsMatch and white/blacklist result can be represented using XOR // e.g. matched(true) ^ blacklist(true) => shouldn't log(false) - return regexToUse.IsMatch(msg) ^ filterMode; + if (Config.Logging.FilterMode == LoggingConfigHealper.LoggingConfig.FilterModeEnum.blacklist) + return !regexToUse.IsMatch(msg); + else if (Config.Logging.FilterMode == LoggingConfigHealper.LoggingConfig.FilterModeEnum.whitelist) + return regexToUse.IsMatch(msg); + else + return true; } else return true; } 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/Mapping/RaycastHelper.cs b/MinecraftClient/Mapping/RaycastHelper.cs index 764d55e1..a1afecef 100644 --- a/MinecraftClient/Mapping/RaycastHelper.cs +++ b/MinecraftClient/Mapping/RaycastHelper.cs @@ -1,10 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using MinecraftClient.Protocol; namespace MinecraftClient.Mapping { diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index dc5265c5..c70ecb5c 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,25 +252,25 @@ 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.Alerts.Enabled) { BotLoad(new Alerts()); } + if (Config.ChatBot.AntiAFK.Enabled) { BotLoad(new AntiAFK()); } + if (Config.ChatBot.AutoAttack.Enabled) { BotLoad(new AutoAttack()); } + if (Config.ChatBot.AutoCraft.Enabled) { BotLoad(new AutoCraft()); } + if (Config.ChatBot.AutoDig.Enabled) { BotLoad(new AutoDig()); } + if (Config.ChatBot.AutoDrop.Enabled) { BotLoad(new AutoDrop()); } + if (Config.ChatBot.AutoEat.Enabled) { BotLoad(new AutoEat()); } + if (Config.ChatBot.AutoFishing.Enabled) { BotLoad(new AutoFishing()); } + if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); } + if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); } + if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); } + if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); } + if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); } + if (Config.ChatBot.Mailer.Enabled) { BotLoad(new Mailer()); } + if (Config.ChatBot.Map.Enabled) { BotLoad(new Map()); } + if (Config.ChatBot.PlayerListLogger.Enabled) { BotLoad(new PlayerListLogger()); } + if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); } + if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); } + if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); } //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; @@ -471,7 +452,7 @@ namespace MinecraftClient if (timeoutdetector != null) { - if (Thread.CurrentThread != timeoutdetector.Item1) + if (timeoutdetector != null && Thread.CurrentThread != timeoutdetector.Item1) timeoutdetector.Item2.Cancel(); timeoutdetector = null; } @@ -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()); } @@ -3312,6 +3293,17 @@ namespace MinecraftClient } } + /// + /// Called when a block is changed. + /// + /// The location of the block. + /// The block + public void OnBlockChange(Location location, Block block) + { + world.SetBlock(location, block); + DispatchBotEvent(bot => bot.OnBlockChange(location, block)); + } + #endregion } } 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..c0ad8e90 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -3,9 +3,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; 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,10 @@ using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Session; using MinecraftClient.WinAPI; +using Tomlet; +using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; namespace MinecraftClient { @@ -37,6 +42,7 @@ namespace MinecraftClient { private static McClient? client; public static string[]? startupargs; + public static readonly CultureInfo ActualCulture = CultureInfo.CurrentCulture; public const string Version = MCHighestVersion; public const string MCLowestVersion = "1.4.6"; @@ -45,20 +51,25 @@ namespace MinecraftClient private static Tuple? offlinePrompt = null; private static bool useMcVersionOnce = false; - private static string? settingsIniPath = null; + private static string settingsIniPath = "MinecraftClient.ini"; /// /// The main entry point of Minecraft Console Client /// static void Main(string[] args) { - new Thread(() => + InitCulture(); + + Task.Run(() => { + // "ToLower" require "CultureInfo" to be initialized on first run, which can take a lot of time. + _ = "a".ToLower(); + //Take advantage of Windows 10 / Mac / Linux UTF-8 console - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (OperatingSystem.IsWindows()) { // If we're on windows, check if our version is Win10 or greater. - if (WindowsVersion.WinMajorVersion >= 10) + if (OperatingSystem.IsWindowsVersionAtLeast(10)) Console.OutputEncoding = Console.InputEncoding = Encoding.UTF8; } else @@ -69,10 +80,7 @@ namespace MinecraftClient // Fix issue #2119 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - - // "ToLower" require "CultureInfo" to be initialized on first run, which can take a lot of time. - _ = "a".ToLower(); - }).Start(); + }); //Setup ConsoleIO ConsoleIO.LogPrefix = "§8[MCC] "; @@ -104,12 +112,11 @@ 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 loadSucceed, needWriteDefaultSetting, newlyGenerated = false; + if (args.Length >= 1 && File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini") { - Settings.LoadFile(args[0]); + (loadSucceed, needWriteDefaultSetting) = Settings.LoadFromFile(args[0]); settingsIniPath = args[0]; //remove ini configuration file from arguments array @@ -117,14 +124,81 @@ 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"); + (loadSucceed, needWriteDefaultSetting) = Settings.LoadFromFile("MinecraftClient.ini"); + } + else + { + loadSucceed = true; + needWriteDefaultSetting = true; + newlyGenerated = true; } - else Settings.WriteDefaultSettings("MinecraftClient.ini"); - //Load external translation file. Should be called AFTER settings loaded - Translations.LoadExternalTranslationFile(Settings.Language); + if (!loadSucceed) + { + ConsoleInteractive.ConsoleReader.StopReadThread(); + string command = " "; + while (command.Length > 0) + { + ConsoleIO.WriteLine(string.Empty); + ConsoleIO.WriteLineFormatted(Translations.Get("mcc.invaild_config", Config.Main.Advanced.InternalCmdChar.ToLogString())); + Translations.WriteLineFormatted("mcc.press_exit"); + command = ConsoleInteractive.ConsoleReader.RequestImmediateInput().Trim(); + if (command.Length > 0) + { + if (Config.Main.Advanced.InternalCmdChar.ToChar() != ' ' + && command[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) + command = command[1..]; + + if (command.StartsWith("exit") || command.StartsWith("quit")) + { + return; + } + else if (command.StartsWith("new")) + { + (string gameLanguage, string[] langList) = Translations.GetTranslationPriority(); + Config.Main.Advanced.Language = gameLanguage; + Translations.LoadTranslationFile(langList); + + WriteBackSettings(true); + ConsoleIO.WriteLineFormatted(Translations.Get("mcc.gen_new_config", settingsIniPath)); + return; + } + } + else + { + return; + } + } + return; + } + else if (needWriteDefaultSetting) + { + (string gameLanguage, string[] langList) = Translations.GetTranslationPriority(); + Translations.LoadTranslationFile(langList); + Config.Main.Advanced.Language = gameLanguage; + + WriteBackSettings(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.TranslationDocUrl)); + WriteBackSettings(true); // format + } + + bool needPromptUpdate = true; + if (Settings.CheckUpdate(Config.Head.CurrentVersion, Config.Head.LatestVersion)) + { + needPromptUpdate = false; + ConsoleIO.WriteLineFormatted(Translations.TryGet("mcc.has_update", Settings.GithubLatestReleaseUrl)); + } //Other command-line arguments if (args.Length >= 1) @@ -140,6 +214,12 @@ namespace MinecraftClient return; } + if (args.Contains("--trim-translation")) + { + Translations.TrimAllTranslations(); + return; + } + if (args.Contains("--generate")) { string dataGenerator = ""; @@ -222,55 +302,92 @@ namespace MinecraftClient } catch (ArgumentException e) { - Settings.interactiveMode = false; + InternalConfig.InteractiveMode = false; HandleFailure(e.Message); return; } } - if (Settings.ConsoleTitle != "") + // Check for updates + Task.Run(() => { - Settings.Username = "New Window"; - Console.Title = Settings.ExpandVars(Settings.ConsoleTitle); + HttpClientHandler httpClientHandler = new() { AllowAutoRedirect = false }; + HttpClient httpClient = new(httpClientHandler); + Task? httpWebRequest = null; + try + { + httpWebRequest = httpClient.GetAsync(Settings.GithubLatestReleaseUrl, HttpCompletionOption.ResponseHeadersRead); + httpWebRequest.Wait(); + HttpResponseMessage res = httpWebRequest.Result; + if (res.Headers.Location != null) + { + Match match = Regex.Match(res.Headers.Location.ToString(), Settings.GithubReleaseUrl + @"/tag/(\d{4})(\d{2})(\d{2})-(\d+)"); + if (match.Success && match.Groups.Count == 5) + { + string year = match.Groups[1].Value, month = match.Groups[2].Value, day = match.Groups[3].Value, run = match.Groups[4].Value; + string latestVersion = string.Format("GitHub build {0}, built on {1}-{2}-{3}", run, year, month, day); + if (needPromptUpdate) + if (Settings.CheckUpdate(Config.Head.CurrentVersion, Config.Head.LatestVersion)) + ConsoleIO.WriteLineFormatted(Translations.TryGet("mcc.has_update", Settings.GithubLatestReleaseUrl)); + if (latestVersion != Config.Head.LatestVersion) + { + Config.Head.LatestVersion = latestVersion; + WriteBackSettings(false); + } + } + } + } + catch (Exception) { } + finally { httpWebRequest?.Dispose(); } + httpClient.Dispose(); + httpClientHandler.Dispose(); + }); + + if (Config.Main.Advanced.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) + InternalConfig.Username = Config.Main.General.Account.Login; + 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 +398,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 +409,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 = InternalConfig.Username; } 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 +449,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,72 +458,42 @@ 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 && Config.Main.Advanced.SessionCache != CacheType.none) + SessionCache.Store(loginLower, session); + + if (result == ProtocolHandler.LoginResult.Success) + session.SessionPreCheckTask = Task.Factory.StartNew(() => session.SessionPreCheck()); } - if (result == ProtocolHandler.LoginResult.Success && Settings.SessionCaching != CacheType.None) - SessionCache.Store(loginLower, session); - - if (result == ProtocolHandler.LoginResult.Success) - session.SessionPreCheckTask = Task.Factory.StartNew(() => session.SessionPreCheck()); - if (result == ProtocolHandler.LoginResult.Success) { - if (Settings.AccountType == ProtocolHandler.AccountType.Microsoft && Settings.Password != "-" && Settings.LoginWithSecureProfile) - { - // Load cached profile key from disk if necessary - if (Settings.ProfileKeyCaching == CacheType.Disk) - { - bool cacheKeyLoaded = KeysCache.InitializeDiskCache(); - if (Settings.DebugMessages) - Translations.WriteLineFormatted(cacheKeyLoaded ? "debug.keys_cache_ok" : "debug.keys_cache_fail"); - } - - if (Settings.ProfileKeyCaching != CacheType.None && KeysCache.Contains(loginLower)) - { - playerKeyPair = KeysCache.Get(loginLower); - if (playerKeyPair.NeedRefresh()) - Translations.WriteLineFormatted("mcc.profile_key_invalid"); - else - ConsoleIO.WriteLineFormatted(Translations.Get("mcc.profile_key_valid", session.PlayerName)); - } - - if (playerKeyPair == null || playerKeyPair.NeedRefresh()) - { - Translations.WriteLineFormatted("mcc.fetching_key"); - playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID); - if (Settings.ProfileKeyCaching != 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 && OperatingSystem.IsWindows()) + 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 +505,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,45 +530,76 @@ 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; } } + if (Config.Main.General.AccountType == LoginType.microsoft && InternalConfig.Password != "-" && + Config.Signature.LoginWithSecureProfile && protocolversion >= 759 /* 1.19 and above */) + { + // Load cached profile key from disk if necessary + if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk) + { + bool cacheKeyLoaded = KeysCache.InitializeDiskCache(); + if (Config.Logging.DebugMessages) + Translations.WriteLineFormatted(cacheKeyLoaded ? "debug.keys_cache_ok" : "debug.keys_cache_fail"); + } + + if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && KeysCache.Contains(loginLower)) + { + playerKeyPair = KeysCache.Get(loginLower); + if (playerKeyPair.NeedRefresh()) + Translations.WriteLineFormatted("mcc.profile_key_invalid"); + else + ConsoleIO.WriteLineFormatted(Translations.Get("mcc.profile_key_valid", session.PlayerName)); + } + + if (playerKeyPair == null || playerKeyPair.NeedRefresh()) + { + Translations.WriteLineFormatted("mcc.fetching_key"); + playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID); + if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null) + { + KeysCache.Store(loginLower, playerKeyPair); + } + } + } + //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 +619,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) { @@ -542,8 +661,16 @@ namespace MinecraftClient /// public static void ReloadSettings() { - if (settingsIniPath != null) - Settings.LoadFile(settingsIniPath); + if(Settings.LoadFromFile(settingsIniPath).Item1) + ConsoleIO.WriteLine(Translations.TryGet("config.loading", settingsIniPath)); + } + + /// + /// Write-back settings + /// + public static void WriteBackSettings(bool enableBackup = true) + { + Settings.WriteToFile(settingsIniPath, enableBackup); } /// @@ -563,22 +690,28 @@ namespace MinecraftClient System.Threading.Thread.Sleep(delaySeconds * 1000); } Translations.WriteLine("mcc.restart"); + ReloadSettings(); InitializeClient(); })).Start(); } + public static void DoExit(int exitcode = 0) + { + WriteBackSettings(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 +728,7 @@ namespace MinecraftClient ConsoleIO.Reset(); while (Console.KeyAvailable) Console.ReadKey(true); - Console.WriteLine(errorMessage); + ConsoleIO.WriteLine(errorMessage); if (disconnectReason.HasValue) { @@ -604,13 +737,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 +760,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 +779,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 +794,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 +803,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); } } @@ -732,6 +861,15 @@ namespace MinecraftClient return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); } + public static void InitCulture() + { + CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US"); + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + } + /// /// Static initialization of build information, read from assembly information /// 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..f8f20206 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 { @@ -242,6 +243,7 @@ namespace MinecraftClient.Protocol.Handlers } catch (ObjectDisposedException) { } catch (OperationCanceledException) { } + catch (NullReferenceException) { } if (cancelToken.IsCancellationRequested) return; @@ -461,8 +463,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 +484,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); @@ -974,18 +976,20 @@ namespace MinecraftClient.Protocol.Handlers int blocksSize = dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < blocksSize; i++) { - ulong block = (ulong)dataTypes.ReadNextVarLong(packetData); - int blockId = (int)(block >> 12); - int localX = (int)((block >> 8) & 0x0F); - int localZ = (int)((block >> 4) & 0x0F); - int localY = (int)(block & 0x0F); + ulong chunkSectionPosition = (ulong)dataTypes.ReadNextVarLong(packetData); + int blockId = (int)(chunkSectionPosition >> 12); + int localX = (int)((chunkSectionPosition >> 8) & 0x0F); + int localZ = (int)((chunkSectionPosition >> 4) & 0x0F); + int localY = (int)(chunkSectionPosition & 0x0F); - Block b = new((ushort)blockId); + Block block = new((ushort)blockId); int blockX = (sectionX * 16) + localX; int blockY = (sectionY * 16) + localY; int blockZ = (sectionZ * 16) + localZ; - var l = new Location(blockX, blockY, blockZ); - handler.GetWorld().SetBlock(l, b); + + Location location = new(blockX, blockY, blockZ); + + handler.OnBlockChange(location, block); } } else @@ -1017,8 +1021,10 @@ namespace MinecraftClient.Protocol.Handlers int blockX = locationXZ >> 4; int blockZ = locationXZ & 0x0F; + + Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); Block block = new(blockIdMeta); - handler.GetWorld().SetBlock(new Location(chunkX, chunkZ, blockX, blockY, blockZ), block); + handler.OnBlockChange(location, block); } } } @@ -1048,11 +1054,16 @@ namespace MinecraftClient.Protocol.Handlers int blockZ = dataTypes.ReadNextInt(packetData); short blockId = (short)dataTypes.ReadNextVarInt(packetData); byte blockMeta = dataTypes.ReadNextByte(packetData); - handler.GetWorld().SetBlock(new Location(blockX, blockY, blockZ), new Block(blockId, blockMeta)); + + Location location = new(blockX, blockY, blockZ); + Block block = new(blockId, blockMeta); + handler.OnBlockChange(location, block); } else { - handler.GetWorld().SetBlock(dataTypes.ReadNextLocation(packetData), new Block((ushort)dataTypes.ReadNextVarInt(packetData))); + Location location = dataTypes.ReadNextLocation(packetData); + Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); + handler.OnBlockChange(location, block); } } break; @@ -1603,7 +1614,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 +1936,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 +2130,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 +2241,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 +2382,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/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 724055d8..089fe342 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -432,5 +432,12 @@ namespace MinecraftClient.Protocol /// Event type /// Depends on Reason public void OnGameEvent(byte reason, float value); + + /// + /// Called when a block is changed. + /// + /// The location of the block. + /// The block + public void OnBlockChange(Location location, Block block); } } 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..b9bfc7f3 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs @@ -3,7 +3,8 @@ using System.Collections.Generic; 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 +47,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 +115,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 +134,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 +175,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..8a0e225d 100644 --- a/MinecraftClient/Proxy/ProxyHandler.cs +++ b/MinecraftClient/Proxy/ProxyHandler.cs @@ -1,5 +1,6 @@ using System.Net.Sockets; using Starksoft.Aspen.Proxy; +using Tomlet.Attributes; namespace MinecraftClient.Proxy { @@ -11,7 +12,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 +67,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/config/MinecraftClient.ini b/MinecraftClient/Resources/config/MinecraftClient.ini deleted file mode 100644 index bf862aac..00000000 --- a/MinecraftClient/Resources/config/MinecraftClient.ini +++ /dev/null @@ -1,319 +0,0 @@ -# Startup Config File - -# 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=microsoft # Account type. mojang or microsoft. Also affects interactive login in console. -method=mcc # Microsoft Account sign-in method. mcc OR browser - -# Advanced settings -# Make sure you understand what each setting does before changing anything! -language=en_GB -consoletitle=%username%@%serverip% - Minecraft Console Client -internalcmdchar=slash # Use 'none', 'slash' or 'backslash' -messagecooldown=1 # Minimum delay in seconds between messages to avoid being kicked for spam. -botowners=Player1,Player2,Player3 # Name list or myfile.txt, one name per line. /!\ Server admins can impersonate owners! -mcversion=auto # Use 'auto' or '1.X.X' values. Allows to skip server info retrieval. -mcforge=auto # Use 'auto', 'false' or 'true'. Force-enabling only works for MC 1.13+. -brandinfo=mcc # Use 'mcc', 'vanilla', or 'none'. This is how MCC identifies itself to the server. -chatbotlogfile= # Leave empty for no logfile -privatemsgscmdname=tell # Used by RemoteControl bot -showsystemmessages=true # System messages for server ops -showxpbarmessages=true # Messages displayed above xp bar, set this to false in case of xp bar spam -showchatlinks=true # Decode links embedded in chat messages and show them in console -showinventorylayout=true # Show inventory layout as ASCII art in inventory command -terrainandmovements=false # Uses more ram, cpu, bandwidth but allows you to move around -inventoryhandling=false # Toggle inventory handling (beta) -entityhandling=false # Toggle entity handling (beta) -sessioncache=disk # How to retain session tokens. Use 'none', 'memory' or 'disk' -profilekeycache=disk # How to retain profile key. Use 'none', 'memory' or 'disk' -resolvesrvrecords=fast # Use 'false', 'fast' (5s timeout), or 'true'. Required for joining some servers. -accountlist=accounts.txt # See README > 'Servers and Accounts file' for more info about this file -serverlist=servers.txt # See README > 'Servers and Accounts file' for more info about this file -playerheadicon=true # Only works on Windows XP-8 or Windows 10 with old console -exitonfailure=false # Disable pauses on error, for using MCC in non-interactive scripts -scriptcache=true # Cache compiled scripts for faster load on low-end devices -timestamps=false # Prepend timestamps to chat messages -autorespawn=false # Toggle auto respawn if client player was dead (make sure your spawn point is safe) -minecraftrealms=false # Enable support for joining Minecraft Realms worlds -moveheadwhilewalking=true # Enable head movement while walking to avoid anti-cheat triggers -timeout=30 # Set a custom timeout in seconds (Default: 30). Use only if you know what you're doing. -enableemoji=true # If turned off, the emoji will be replaced with a simpler character (for /chunk status) -movementspeed=2 # A movement speed higher than 2 may be considered cheating. - -[Signature] -# Chat settings (affects minecraft 1.19+) -login_with_secure_profile=true # Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true" -sign_chat=true # Whether to sign the chat send from MCC -sign_message_in_command=true # Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me" -mark_legally_signed_msg=false # Use green color block to mark chat with legitimate signatures -mark_modified_msg=true # Use yellow color block to mark chat that have been modified by the server. -mark_illegally_signed_msg=true # Use red color block to mark chat without legitimate signature -mark_system_message=false # Use gray color block to mark system message (always without signature) -show_modified_chat=true # Set to true to display messages modified by the server, false to display the original signed messages -show_illegal_signed_chat=true # Whether to display chat and messages in commands without legal signatures - -[Logging] -# Only affect the messages on console. -debugmessages=false # Please enable this before submitting bug reports. Thanks! -chatmessages=true # Show server chat messages -warningmessages=true # Show warning messages -errormessages=true # Show error messages -infomessages=true # Informative messages (i.e Most of the message from MCC) -#chatfilter= # Regex for filtering chat message -#debugfilter= # Regex for filtering debug message -filtermode=blacklist # blacklist OR whitelist. Blacklist hide message match regex. Whitelist show message match regex -logtofile=false # Write log messages to file -logfile=console-log-%username%-%serverip%.txt # Log file name -prependtimestamp=false # Prepend timestamp to messages in log file -savecolorcodes=false # Keep color codes in the saved text (§b) - -[AppVars] -# yourvar=yourvalue -# can be used in some other fields as %yourvar% -# %username% and %serverip% are reserved variables. - -[Proxy] -# Connect to a server via a proxy instead of connecting directly -# If Mojang session services are blocked on your network, set enabled=login to login using proxy but connect directly to the server -# If connecting to port 25565 (Minecraft) is blocked on your network, set enabled=true to login & connect using the proxy -# /!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences! -enabled=false # Use 'false', 'true', or 'login' for login only -type=HTTP # Supported types: HTTP, SOCKS4, SOCKS4a, SOCKS5 -server=0.0.0.0:0000 # Proxy server must allow HTTPS for login, and non-443 ports for playing -username= # Only required for password-protected proxies -password= # Only required for password-protected proxies - -[ChatFormat] -# MCC does it best to detect chat messages, but some server have unusual chat formats -# When this happens, you'll need to configure chat format below, see README > 'Detecting chat messages' -# Do not forget to uncomment (remove '#') these settings if modifying them -builtins=true # MCC support for common message formats. Set "false" to avoid conflicts with custom formats. -# public=^<([a-zA-Z0-9_]+)> (.+)$ -# private=^([a-zA-Z0-9_]+) whispers to you: (.+)$ -# tprequest=^([a-zA-Z0-9_]+) has requested (?:to|that you) teleport to (?:you|them)\.$ - -[MCSettings] -# Settings below are sent to the server and only affect server-side things like your skin -enabled=true # If disabled, settings below are not sent to the server -locale=en_US # Use any language implemented in Minecraft -renderdistance=medium # Use tiny, short, medium, far, or chunk amount [0 - 255] -difficulty=normal # MC 1.7- difficulty. peaceful, easy, normal, difficult -chatmode=enabled # Use 'enabled', 'commands', or 'disabled'. Allows to mute yourself... -chatcolors=true # Allows disabling chat colors server-side -main_hand=left # MC 1.9+ main hand. left or right -skin_cape=true -skin_hat=true -skin_jacket=false -skin_sleeve_left=false -skin_sleeve_right=false -skin_pants_left=false -skin_pants_right=false - -# =============================== # -# Minecraft Console Client Bots # -# =============================== # - -# Although Minecraft Console Client is only a Chat client by default, it can also do many more things -# Below you will find configuration sections to enable Bots that can automatically perform tasks for you -# BY ENABLING BOTS, YOU AGREE TO COMPLY WITH SERVER RULES, AND FACE CONSEQUENCES FROM SERVER STAFF IF YOU DON'T - -[Alerts] -# Get alerted when specified words are detected in chat -# Useful for moderating your server or detecting when someone is talking to you -enabled=false -trigger_by_words=false # Triggers an alert after receiving a specified keyword. -trigger_by_rain=false # Trigger alerts when it rains and when it stops. -trigger_by_thunderstorm=false # Triggers alerts at the beginning and end of thunderstorms. -alertsfile=alerts.txt # List of words/strings to alert you on, e.g. "Yourname" -excludesfile=alerts-exclude.txt # List of words/strings to NOT alert you on, e.g. "" -beeponalert=true # Play a beep sound when a word is detected in addition to highlighting -logtofile=false # Log alerts info a file -logfile=alerts-log.txt # The name of a file where alers logs will be written - -[AntiAFK] -# Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection -# /!\ Make sure your server rules do not forbid anti-AFK mechanisms! -# /!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5) -enabled=false -delay=600 # 10 = 1s (Can also be a random number between 2 numbers, example: 50-600) (Default: 600) -command=/ping # Command to send to the server -use_terrain_handling=false # Use terrain handling to enable the bot to move around -walk_range=5 # The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be) -walk_retries=20 # How many timec can the bot fail trying to move before using the command method - -[AutoRelog] -# Automatically relog when disconnected by server, for example because the server is restating -# Put keywords/strings such as "Server is restarting" in kick messages file to relog when the message is seen -# /!\ Use ignorekickmessage=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks -enabled=false -delay=10 # use 10 for 10 seconds, 10-60 for a random delay between 10 and 60 seconds -retries=3 # retries when failing to relog to the server. use -1 for unlimited retries -kickmessagesfile=kickmessages.txt # file with list of matches in kick messages that will trigger autorelog -ignorekickmessage=false # when set to true, autorelog will reconnect regardless of kick messages - -[ChatLog] -# Logs chat messages in a file on disk -enabled=false -timestamps=true -filter=messages -logfile=chatlog-%username%-%serverip%.txt - -[Hangman] -# A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time. -# You need to have ChatFormat working correctly and add yourself in botowners to start the game with /tell start -# /!\ This bot may get a bit spammy if many players are interacting with it -enabled=false -english=true -wordsfile=hangman-en.txt -fichiermots=hangman-fr.txt - -[ScriptScheduler] -# Schedule commands and scripts to launch on various events such as server join, date/time or time interval -# See README > 'Using the Script Scheduler' for more info -enabled=false -tasksfile=tasks.ini - -[RemoteControl] -# Send MCC console commands to your bot through server PMs (/tell) -# You need to have ChatFormat working correctly and add yourself in botowners to use the bot -# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins -enabled=false -autotpaccept=true -tpaccepteveryone=false - -[AutoRespond] -# Run commands or send messages automatically when a specified pattern is detected in chat -# /!\ Server admins can spoof chat messages (/nick, /tellraw) so keep this in mind when implementing AutoRespond rules -# /!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam -enabled=false -matchesfile=matches.ini -matchcolors=false # Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work) - -[AutoAttack] -# Automatically attack hostile mobs around you -# You need to enable Entity Handling to use this bot -# /!\ Make sure server rules allow your planned use of AutoAttack -# /!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES! -enabled=false -mode=single # single or multi. single target one mob per attack. multi target all mobs in range per attack -priority=distance # health or distance. Only needed when using single mode -cooldownseconds=auto # How long to wait between each attack. Use auto to let MCC calculate it -interaction=Attack # Possible values: Interact, Attack (default), InteractAt (Interact and Attack) -attackhostile=true # Allow attacking hostile mobs -attackpassive=false # Allow attacking passive mobs -listmode=blacklist # Wether to treat the list from the file bellow as a whitelist or as a blacklist -# A path to the file which contains blacklisted or whitelisted entities, entity types are written on a new line -# All entity types can be found here: https://bit.ly/3Rg68lp -# The file is not created by default. -listfile=autoattack-list.txt - -[AutoFishing] -# Automatically catch fish using a fishing rod -# Guide: https://mccteam.github.io/guide/chat-bots.html#auto-fishing -# /!\ Make sure server rules allow automated farming before using this bot -enabled=false -antidespawn=false -main_hand=true # Use the main hand or the off hand to hold the rod. -auto_start=true # Whether to start fishing automatically after entering a world. -cast_delay=0.4 # How soon to re-cast after successful fishing. -fishing_delay=3.0 # How long after entering the game to start fishing (seconds). -fishing_timeout=300.0 # Fishing timeout (seconds). Timeout will trigger a re-cast. -durability_limit=2 # Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature. -auto_rod_switch=true # Switch to a new rod from inventory after the current rod is unavailable. -stationary_threshold=0.001 # Hooks moving in the X and Z axes below this threshold will be considered stationary. -hook_threshold=0.2 # A stationary hook moving on the Y-axis above this threshold will be considered to have caught a fish. -log_fishing_bobber=false # For debugging purposes, you can use this log to adjust the two thresholds mentioned above. -location= # Some plugins do not allow the player to fish in one place. This allows the player to change position/angle after each fish caught. - # Floating-point numbers can be used for both coordinates and angles. Leave blank to disable this function. - # Change the angle only (recommended): location=yaw_1, pitch_1; yaw_2, pitch_2; ...; yaw_n, pitch_n - # Change position only: location=x1, y1, z1; x2, y2, z2; ...; xn, yn, zn - # Change both angle and position: location=x1, y1, z1, yaw_1, pitch_1; x2, y2, z2, yaw_2, pitch_2; ... ;xn, yn, zn, yaw_n, pitch_n - -[AutoEat] -# Automatically eat food when your Hunger value is low -# You need to enable Inventory Handling to use this bot -enabled=false -threshold=6 - -[AutoCraft] -# Automatically craft items in your inventory -# See README > 'Using the AutoCraft bot' for how to use -# You need to enable Inventory Handling to use this bot -# You should also enable Terrain and Movements if you need to use a crafting table -enabled=false -configfile=autocraft\config.ini - -[Mailer] -# Relay messages between players and servers, like a mail plugin -# This bot can store messages when the recipients are offline, and send them when they join the server -# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins -enabled=false -database=MailerDatabase.ini -ignorelist=MailerIgnoreList.ini -publicinteractions=false -maxmailsperplayer=10 -maxdatabasesize=10000 -retentiondays=30 - -[AutoDrop] -# Automatically drop items in inventory -# You need to enable Inventory Handling to use this bot -enabled=false -mode=include # include, exclude or everything. Include: drop item IN the list. Exclude: drop item NOT IN the list -items= # separate each item name with a comma ',': ItemOne,ItemTwo -# See this file for an up-to-date list of item types you can use with this bot: -# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs - -[ReplayMod] -# Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/) -# Please note that due to technical limitations, the client player (you) will not be shown in the replay file -# /!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT! -enabled=false -backupinterval=300 # How long should replay file be auto-saved, in seconds. Use -1 to disable - -[FollowPlayer] -# Enabled you to make the bot follow you -# 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 -# It's similar to making animals follow you when you're holding food in your hand. -# This is due to a slow pathfinding algorithm, we're working on getting a better one -# 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, -# this might clog the thread for terain handling) and thus slow the bot even more. -# /!\ Make sure server rules allow an option like this in the rules of the server before using this bot -enabled=false -update_limit=10 # The rate at which the bot does calculations (10 = 1s) (Default: 5) (You can tweak this if you feel the bot is too slow) -stop_at_distance=3 # 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) - -[PlayerListLogger] -# Log the list of players periodically into a textual file -enabled=false -log_file=playerlog.txt -log_delay=600 # 10 = 1s - -[Map] -# Allows you to render maps into .jpg images -# This is useful for solving captchas which use maps -# The maps are rendered into Rendered_Maps folder. -# NOTE: -# This feature is currently only useful for solving captchas which use maps. -# If some servers have a very short time for solving captchas, enabe auto_render_on_update and prepare to open the file quickly. -# On linux you can use FTP to access generated files. -# In the future it might will be possible to display maps directly in the console with a separate command. -# /!\ Make sure server rules allow bots to be used on the server, or you risk being punished. -enabled=false -resize_map=false # Should the map be resized? (Default one is small 128x128) -resize_to=256 # The size to resize the map to (Note: the bigger it is, the lower the quallity is) -auto_render_on_update=false # Automatically render the map once it's received or updated from/by the server -delete_rendered_on_unload=true # Delete all rendered maps on unload/reload (Does not delete the images if you exit the client) -notify_on_first_update=false # Get a notification when you have gotten a map from the server for the first time - # Note: Will be printed for each map in vicinity, could cause spam if there are a lot of maps \ No newline at end of file diff --git a/MinecraftClient/Resources/lang/de.ini b/MinecraftClient/Resources/lang/de.ini index 8f734620..32792f5d 100644 --- a/MinecraftClient/Resources/lang/de.ini +++ b/MinecraftClient/Resources/lang/de.ini @@ -1,15 +1,22 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate= +mcc.run_with_default_settings= +mcc.settings_generated= +mcc.has_update= mcc.login=Login : mcc.login_basic_io=Bitte gib einen Nutzernamen oder eine E-Mail deiner Wahl ein. -mcc.password=Passwort : +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.profile_key_invalid= +mcc.profile_key_valid= mcc.connecting=Verbinde zu {0}... -mcc.ip=Server-IP : +mcc.fetching_key= +mcc.ip=Server-IP : mcc.use_version=§8Benutze Minecraft-Version {0} (protocol v{1}) mcc.unknown_version=§8Unbekannte oder nicht unterstützte MC-Version {0}.\nWechsele in den Autodetection-Modus. mcc.forge=Prüfe, ob der Server Forge benutzt... @@ -20,7 +27,7 @@ mcc.not_found=§8SRV-Lookup fehlgeschlagen für {0}\n{1}: {2} mcc.retrieve=Erhalte Server-Info... mcc.restart=Starte Minecraft Console Client neu... mcc.restart_delay=Warte {0} Sekunden vor Neustart... -mcc.server_version=Server-Version : +mcc.server_version=Server-Version : mcc.disconnected=Mit keinem Server verbunden. Schreibe '{0}help' für weitere Hilfe. mcc.press_exit=Oder drücke Enter, um den Minecraft Console Client zu verlassen. mcc.version_supported=Version wird unterstützt.\nMelde an... @@ -41,13 +48,16 @@ mcc.with_forge=, mit Forge mcc.handshake=§8Handshake erfolgreich. (Server ID: {0}) mcc.realms_available=Du hast Zugang zu den folgenden Realms-Welten mcc.realms_join=Nutze realms:index als Server-IP, um der Realms-Welt beizutreten - +mcc.generator.generating= +mcc.generator.done= [debug] # Messages from MCC Debug Mode debug.color_test=Farbtest: Dein Terminal sollte {0} anzeigen. debug.session_cache_ok=§8Sitzungsdaten wurden erfolgreich vom Speicher geladen. debug.session_cache_fail=§8Es konnten keine Sitzungsdaten vom Speicher geladen werden. +debug.keys_cache_ok= +debug.keys_cache_fail= debug.session_id=Erfolgreich. (session ID: {0}) debug.crypto=§8Crypto-Schlüssel & -Hash wurden generiert. debug.request=§8Starte Anfrage zu {0} @@ -58,7 +68,7 @@ error.ping=Ping an IP fehlgeschlagen. error.unsupported=Kann nicht zum Server verbinden : Diese Version wird nicht unterstützt ! error.determine=Konnte Server-Version nicht bestimmen. error.forgeforce=Der zwangshafte Forge-Support wird für diese Minecraft-Version nicht unterstützt! -error.login=Minecraft Login fehlgeschlagen : +error.login=Minecraft Login fehlgeschlagen : error.login.migrated=Account wurde migriert, benutze eine E-Mail als Nutzername. error.login.server=Login-Server nicht erreichbar. Bitte versuche es später erneut. error.login.blocked=Falsches Passwort, gesperrte IP oder zu viele Logins. @@ -67,7 +77,6 @@ error.login.premium=Dies ist kein Premium-Benutzer. error.login.network=Netzwerkfehler. error.login.ssl=SSL-Fehler. error.login.unknown=Unbekannter Fehler. -error.login.ssl_help=§8Anscheinend hast du dieses Programm mit MONO gestartet.\nBeim ersten Mal musst du HTTPS-Zertifikate mit folgendem Befehl importieren:\nmozroots --import --ask-remove error.login.cancel=Benutzer gecancelled. error.login_failed=Nutzer konnte auf diesem Server nicht angemeldet werden. error.join=Konnte dem Server nicht beitreten. @@ -81,10 +90,7 @@ error.no_version_report=§8Server gibt keine Protokoll-Version an, Autodetection error.connection_timeout=§8Es gab einen Timeout während des Verbindungsaufbaus zu dieser IP. error.forge=§8Forge Login Handshake konnte nicht erfolgreich abgeschlossen werden. error.forge_encrypt=§8Forge StartEncryption Handshake konnte nicht erfolgreich abgeschlossen werden. -error.setting.str2int=Konnte '{0}' nicht in einen Int umwandeln. Bitte überprüfe die Einstellungen. error.setting.argument_syntax={0}: Invalid syntax, expecting --argname=value or --section.argname=value -error.setting.unknown_section={0}: Unknown setting section '{1}' -error.setting.unknown_or_invalid={0}: Unknown setting or invalid value error.http_code=§8Es gab einen Fehlercode vom Server: {0} error.auth=§8Es gab einen Fehlercode vom Server, während die Authentication erneuert wurde: {0} error.realms.ip_error=Konnte die Server-IP deiner Realms-Welt nicht abfragen @@ -92,6 +98,11 @@ error.realms.access_denied=Diese Realms-Welt existiert nicht oder der Zugang wur error.realms.server_unavailable=Der Realms-Server braucht ein wenig Zeit zum starten. Bitte versuche es später erneut. error.realms.server_id=Invalid or unknown Realms server ID. error.realms.disabled=Versuche der Realms-Welt beizutreten, allerdings wurde der Realms-Support in der Config deaktiviert +error.missing.argument= +error.usage= +error.generator.invalid= +error.generator.path= +error.generator.json= [internal command] # MCC internal help command @@ -173,6 +184,14 @@ cache.ignore_line=§8Ignoriere ungültigen Sitzungstoken Zeile: {0} cache.read_fail_plain=§8Konnte Sitzungscache nicht vom Speicher lesen: {0} cache.saving=§8Speichere Sitzungscache auf Festplatte cache.save_fail=§8Konnte Sitzungscache auf keine Festplatte schreiben: {0} +# Profile Key Cache +cache.loading_keys= +cache.loaded_keys= +cache.ignore_string_keys= +cache.ignore_line_keys= +cache.read_fail_plain_keys= +cache.saving_keys= +cache.save_fail_keys= [proxy] proxy.connected=§8Verbunden mit Proxy {0}:{1} @@ -186,6 +205,7 @@ chat.fail=§8Konnte Datei nicht herunterladen. chat.from_dir=§8Nutze en_GB.lang als Standard von deinem Minecraft-Pfad. chat.loaded=§8Übersetzungsdatei geladen. chat.not_found=§8Übersetzungsdatei konnte nicht gefunden werden: '{0}'\nEinige Nachrichten können ohne diese Datei nicht korrekt angezeigt werden. +chat.message_chain_broken= [general] # General message/information (i.e. Done) @@ -201,12 +221,23 @@ general.available_cmd=Verfügbare Befehle: {0} # Animation cmd.animation.desc=Schwinge deinen Arm. +# Bots +cmd.bots.desc= +cmd.bots.list= +cmd.bots.notfound= +cmd.bots.noloaded= +cmd.bots.unloaded= +cmd.bots.unloaded_all= + # ChangeSlot cmd.changeSlot.desc=Ändere deine Hotbar cmd.changeSlot.nan=Konnte Slot nicht ändern: Dies ist keine Nummer. cmd.changeSlot.changed=Slot geändert auf {0} cmd.changeSlot.fail=Konnte Slot nicht ändern. +# Chunk +cmd.chunk.desc= + # Connect cmd.connect.desc=Verbinde zum angegebenen Server. cmd.connect.unknown=Unbekannter Account '{0}'. @@ -248,6 +279,19 @@ cmd.entityCmd.distance=Entfernung cmd.entityCmd.location=Position cmd.entityCmd.type=Typ +# Exec If +cmd.execif.desc= +cmd.execif.executed= +cmd.execif.executed_no_output= +cmd.execif.error_occured= +cmd.execif.error= + +# Exec Multi +cmd.execmulti.desc= +cmd.execmulti.executed= +cmd.execmulti.result= +cmd.execmulti.no_result= + # Exit cmd.exit.desc=Verbindung zum Server verloren. @@ -267,12 +311,15 @@ cmd.inventory.close_fail=Konnte das Inventar #{0} nicht schließen cmd.inventory.not_exist=Inventar #{0} existiert nicht. cmd.inventory.inventory=Inventar cmd.inventory.inventories=Inventare +cmd.inventory.inventories_available= cmd.inventory.hotbar=Deine ausgewählte Hotbar ist {0} cmd.inventory.damage=Schaden cmd.inventory.left=Links cmd.inventory.right=Rechts cmd.inventory.middle=Mittel cmd.inventory.clicking={0} klicke Slote {1} im Fenster #{2} +cmd.inventory.shiftclick= +cmd.inventory.shiftclick_fail= cmd.inventory.no_item=Kein Item im Slot #{0} cmd.inventory.drop=Droppe 1 Item aus Slot #{0} cmd.inventory.drop_stack=Droppe ganzen Item Stack aus Slot #{0} @@ -284,10 +331,29 @@ cmd.inventory.help.usage=Benutzung cmd.inventory.help.list=Liste deine Inventare auf. cmd.inventory.help.close=Schließe einen offenen Container. cmd.inventory.help.click=Klicke auf ein Item. +cmd.inventory.help.shiftclick= cmd.inventory.help.drop=Werfe ein Item aus deinem Inventar. cmd.inventory.help.creativegive=Gebe dir ein Item im Kreativmodus. cmd.inventory.help.creativedelete=Leere Slot im Kreativmodus. -cmd.inventory.help.unknown=Unbekannte Aktion. +cmd.inventory.help.inventories= +cmd.inventory.help.search= +cmd.inventory.help.unknown=Unbekannte Aktion. +cmd.inventory.found_items= +cmd.inventory.no_found_items= + +# Leave bed +cmd.bed.desc= +cmd.bed.leaving= +cmd.bed.trying_to_use= +cmd.bed.in= +cmd.bed.not_in= +cmd.bed.not_a_bed= +cmd.bed.searching= +cmd.bed.bed_not_found= +cmd.bed.found_a_bed_at= +cmd.bed.cant_reach_safely= +cmd.bed.moving= +cmd.bed.failed_to_reach_in_time= # List cmd.list.desc=Liste alle Spieler auf. @@ -301,6 +367,8 @@ cmd.look.desc=Schaue in eine Richtung oder auf Koordinaten. cmd.look.unknown=Unbekannte Richtung '{0}' cmd.look.at=Schaue nach YAW: {0} PITCH: {1} cmd.look.block=Schaue auf {0} +cmd.look.inspection= +cmd.look.noinspection= # Move cmd.move.desc=Laufe oder fange an zu laufen. @@ -313,11 +381,21 @@ cmd.move.fail=Konnte Pfad nach {0} nicht berechnen. cmd.move.suggestforce=Weg nach {0} konnte nicht berechnet werden. Benutze den -f Parameter, um unsichere Wege zu aktivieren. cmd.move.gravity.enabled=Gravitation ist aktiv. cmd.move.gravity.disabled=Gravitation ist deaktiviert. - +cmd.move.chunk_loading_status= +cmd.move.chunk_not_loaded= # Reco cmd.reco.desc=Starte neu und verbinde erneut zum Server. +# Reload +cmd.reload.started= +cmd.reload.warning1= +cmd.reload.warning2= +cmd.reload.warning3= +cmd.reload.warning4= +cmd.reload.finished= +cmd.reload.desc= + # Respawn cmd.respawn.desc=Benutze dies, um nach deinem Tod zu respawnen. cmd.respawn.done=Du wurdest respawned. @@ -353,6 +431,7 @@ cmd.tps.current=Aktuelle TPS # Useblock cmd.useblock.desc=Plaziere einen Block oder öffne eine Kiste +cmd.useblock.use= # Useitem cmd.useitem.desc=Benutze (links klick) ein Item auf der Hand @@ -362,9 +441,18 @@ cmd.useitem.use=Item wurde benutzt. [bot] # ChatBots. Naming style: bot.. +# Alerts +bot.alerts.start_rain= +bot.alerts.end_rain= +bot.alerts.start_thunderstorm= +bot.alerts.end_thunderstorm= + +# Anti AFK +bot.antiafk.not_using_terrain_handling= +bot.antiafk.swapping= +bot.antiafk.invalid_walk_range= + # AutoAttack -bot.autoAttack.mode=Unbekannter Angriffsmodus: {0}. Benutze Einfach als Standard. -bot.autoAttack.priority=Unbekannte Priorität: {0}. Benutze Entfernung als Standard. bot.autoAttack.invalidcooldown=Angriffscooldown kann nicht kleiner als 0 sein. Benutze Auto als Standard. # AutoCraft @@ -393,15 +481,11 @@ bot.autoCraft.aborted=Crafting abgebrochen! Überprüfe die vorhandenen Material bot.autoCraft.craft_fail=Crafting fehlgeschlagen! Warte auf weitere Materialien... bot.autoCraft.timeout=Aktion timeout! Grund: {0} bot.autoCraft.error.config=Fehler bei der Übergabe der Config: {0} -bot.autoCraft.exception.empty=Leere Konfigurationsdatei: {0} -bot.autoCraft.exception.invalid=Ungültige Konfigurationsdatei: {0} -bot.autoCraft.exception.item_miss=Fehlendes Item in Rezept: {0} -bot.autoCraft.exception.invalid_table=Ungültiges tablelocation Format: {0} -bot.autoCraft.exception.item_name=Ungültiger Itemname im Rezept {0} bei {1} bot.autoCraft.exception.name_miss=Fehlender Rezeptname während der Rezeptübergabe -bot.autoCraft.exception.slot=Ungültiges Slot-Feld im Rezept: {0} bot.autoCraft.exception.duplicate=Doppelter Rezeptname angegeben: {0} bot.autoCraft.debug.no_config=Keine Config-Datei gefunden. Schreibe eine neue... +bot.autocraft.invaild_slots= +bot.autocraft.invaild_invaild_result= # AutoDrop bot.autoDrop.cmd=AutoDrop ChatBot Befehl @@ -420,9 +504,17 @@ bot.autoDrop.no_mode=Kann drop Modus nicht aus Config Datei lesen. Benutze inclu bot.autoDrop.no_inventory=Konnte Inventar nicht finden {0}! # AutoFish +bot.autoFish.no_inv_handle= +bot.autoFish.start= bot.autoFish.throw=Angel ausgeworfen! bot.autoFish.caught=Fisch gefangen! +bot.autoFish.caught_at= bot.autoFish.no_rod=Keine Angelrute in der Hand. Ist sie kaputt? +bot.autoFish.despawn= +bot.autoFish.fishing_timeout= +bot.autoFish.cast_timeout= +bot.autoFish.update_lookat= +bot.autoFish.switch= # AutoRelog bot.autoRelog.launch=Starte mit {0} reconnect Versuchen @@ -466,7 +558,7 @@ bot.mailer.init_fail.mail_retention=Konnte Mailer nicht aktivieren: Die Auslaufz bot.mailer.create.db=Erstelle neue Datenbank: {0} bot.mailer.create.ignore=Erstelle neue Ignore-Liste: {0} bot.mailer.load.db=Lade Datenbank-Datei: {0} -bot.mailer.load.ignore=Lade Ignore-Liste: +bot.mailer.load.ignore=Lade Ignore-Liste: bot.mailer.cmd=Mailer-Befehl @@ -482,12 +574,43 @@ bot.mailer.cmd.ignore.removed=Entferne {0} von der Ignore Liste! bot.mailer.cmd.ignore.invalid=Fehlender oder ungültiger Name. Benutzung: {0} bot.mailer.cmd.help=Siehe Benutzung +# Maps +bot.map.cmd.desc= +bot.map.cmd.not_found= +bot.map.cmd.invalid_id= +bot.map.received= +bot.map.no_maps= +bot.map.received_map= +bot.map.rendered= +bot.map.failed_to_render= +bot.map.list_item= + # ReplayCapture bot.replayCapture.cmd=replay Befehl bot.replayCapture.created=Replay-Datei erstellt. bot.replayCapture.stopped=Record angehalten. bot.replayCapture.restart=Aufnahme wurde angehalten. Starte das Programm neu um eine neue Aufnahme zu erstellen. +# Follow player +cmd.follow.desc= +cmd.follow.usage= +cmd.follow.already_stopped= +cmd.follow.stopping= +cmd.follow.invalid_name= +cmd.follow.invalid_player= +cmd.follow.cant_reach_player= +cmd.follow.already_following= +cmd.follow.switched= +cmd.follow.started= +cmd.follow.unsafe_enabled= +cmd.follow.note= +cmd.follow.player_came_to_the_range= +cmd.follow.resuming= +cmd.follow.player_left_the_range= +cmd.follow.pausing= +cmd.follow.player_left= +cmd.follow.stopping= + # Script bot.script.not_found=§8[MCC] [{0}] Konnte Skriptdatei nicht finden: {1} bot.script.file_not_found=Datei nicht gefunden: '{0}' @@ -495,8 +618,6 @@ bot.script.fail=Konnte Skript '{0}' nicht starten ({1}). bot.script.pm.loaded=Skript '{0}' geladen. # ScriptScheduler -bot.scriptScheduler.loading=Lade Aufgaben von '{0}' -bot.scriptScheduler.not_found=Datei nicht gefunden: '{0}' bot.scriptScheduler.loaded_task=Lade Aufgabe:\n{0} bot.scriptScheduler.no_trigger=Diese Aufgabe wird niemals ausgelöst:\n{0} bot.scriptScheduler.no_action=Keine Aktion für diese Aufgabe:\n{0} @@ -508,3 +629,220 @@ 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= +config.load.fail= +config.write.fail= +config.backup.fail= +config.saving= + +# Head +config.Head= + +# Main.General +config.Main.General.account= +config.Main.General.login= +config.Main.General.server_info= +config.Main.General.method= + +# Main.Advanced +config.Main.Advanced= +config.Main.Advanced.language= +config.Main.Advanced.internal_cmd_char= +config.Main.Advanced.message_cooldown= +config.Main.Advanced.bot_owners= +config.Main.Advanced.mc_version= +config.Main.Advanced.mc_forge= +config.Main.Advanced.brand_info= +config.Main.Advanced.chatbot_log_file= +config.Main.Advanced.private_msgs_cmd_name= +config.Main.Advanced.show_system_messages= +config.Main.Advanced.show_xpbar_messages= +config.Main.Advanced.show_chat_links= +config.Main.Advanced.show_inventory_layout= +config.Main.Advanced.terrain_and_movements= +config.Main.Advanced.inventory_handling= +config.Main.Advanced.entity_handling= +config.Main.Advanced.session_cache= +config.Main.Advanced.profilekey_cache= +config.Main.Advanced.resolve_srv_records= +config.Main.Advanced.account_list= +config.Main.Advanced.server_list= +config.Main.Advanced.player_head_icon= +config.Main.Advanced.exit_on_failure= +config.Main.Advanced.script_cache= +config.Main.Advanced.timestamps= +config.Main.Advanced.auto_respawn= +config.Main.Advanced.minecraft_realms= +config.Main.Advanced.move_head_while_walking= +config.Main.Advanced.timeout= +config.Main.Advanced.enable_emoji= +config.Main.Advanced.movement_speed= +config.Main.Advanced.language.invaild= + +# Signature +config.Signature= +config.Signature.LoginWithSecureProfile= +config.Signature.SignChat= +config.Signature.SignMessageInCommand= +config.Signature.MarkLegallySignedMsg= +config.Signature.MarkModifiedMsg= +config.Signature.MarkIllegallySignedMsg= +config.Signature.MarkSystemMessage= +config.Signature.ShowModifiedChat= +config.Signature.ShowIllegalSignedChat= + +# Logging +config.Logging= +config.Logging.DebugMessages= +config.Logging.ChatMessages= +config.Logging.InfoMessages= +config.Logging.WarningMessages= +config.Logging.ErrorMessages= +config.Logging.ChatFilter= +config.Logging.DebugFilter= +config.Logging.FilterMode= +config.Logging.LogToFile= +config.Logging.LogFile= +config.Logging.PrependTimestamp= +config.Logging.SaveColorCodes= + +# AppVars +config.AppVars.Variables= + +# Proxy +config.Proxy= +config.Proxy.Enabled_Login= +config.Proxy.Enabled_Ingame= +config.Proxy.Server= +config.Proxy.Proxy_Type= +config.Proxy.Username= +config.Proxy.Password= + +# ChatFormat +config.ChatFormat= +config.ChatFormat.Builtins= +config.ChatFormat.UserDefined= + +# MCSettings +config.MCSettings= +config.MCSettings.Enabled= +config.MCSettings.Locale= +config.MCSettings.RenderDistance= +config.MCSettings.Difficulty= +config.MCSettings.ChatMode= +config.MCSettings.ChatColors= +config.MCSettings.MainHand= + +# ChatBot +config.ChatBot= + +# ChatBot.Alerts +config.ChatBot.Alerts= +config.ChatBot.Alerts.Beep_Enabled= +config.ChatBot.Alerts.Trigger_By_Words= +config.ChatBot.Alerts.Trigger_By_Rain= +config.ChatBot.Alerts.Trigger_By_Thunderstorm= +config.ChatBot.Alerts.Matches= +config.ChatBot.Alerts.Excludes= +config.ChatBot.Alerts.Log_To_File= +config.ChatBot.Alerts.Log_File= + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk= +config.ChatBot.AntiAfk.Delay= +config.ChatBot.AntiAfk.Command= +config.ChatBot.AntiAfk.Use_Terrain_Handling= +config.ChatBot.AntiAfk.Walk_Range= +config.ChatBot.AntiAfk.Walk_Retries= + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack= +config.ChatBot.AutoAttack.Mode= +config.ChatBot.AutoAttack.Priority= +config.ChatBot.AutoAttack.Cooldown_Time= +config.ChatBot.AutoAttack.Interaction= +config.ChatBot.AutoAttack.Attack_Hostile= +config.ChatBot.AutoAttack.Attack_Passive= +config.ChatBot.AutoAttack.List_Mode= +config.ChatBot.AutoAttack.Entites_List= + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft= +config.ChatBot.AutoCraft.CraftingTable= +config.ChatBot.AutoCraft.OnFailure= +config.ChatBot.AutoCraft.Recipes= + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop= +config.ChatBot.AutoDrop.Mode= + +# ChatBot.AutoEat +config.ChatBot.AutoEat= + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing= +config.ChatBot.AutoFishing.Antidespawn= +config.ChatBot.AutoFishing.Mainhand= +config.ChatBot.AutoFishing.Auto_Start= +config.ChatBot.AutoFishing.Cast_Delay= +config.ChatBot.AutoFishing.Fishing_Delay= +config.ChatBot.AutoFishing.Fishing_Timeout= +config.ChatBot.AutoFishing.Durability_Limit= +config.ChatBot.AutoFishing.Auto_Rod_Switch= +config.ChatBot.AutoFishing.Stationary_Threshold= +config.ChatBot.AutoFishing.Hook_Threshold= +config.ChatBot.AutoFishing.Log_Fish_Bobber= +config.ChatBot.AutoFishing.Enable_Move= +config.ChatBot.AutoFishing.Movements= + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog= +config.ChatBot.AutoRelog.Delay= +config.ChatBot.AutoRelog.Retries= +config.ChatBot.AutoRelog.Ignore_Kick_Message= +config.ChatBot.AutoRelog.Kick_Messages= + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond= +config.ChatBot.AutoRespond.Match_Colors= + +# ChatBot.ChatLog +config.ChatBot.ChatLog= + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer= +config.ChatBot.FollowPlayer.Update_Limit= +config.ChatBot.FollowPlayer.Stop_At_Distance= + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame= + +# ChatBot.Mailer +config.ChatBot.Mailer= + +# ChatBot.Map +config.ChatBot.Map= +config.ChatBot.Map.Should_Resize= +config.ChatBot.Map.Resize_To= +config.ChatBot.Map.Auto_Render_On_Update= +config.ChatBot.Map.Delete_All_On_Unload= +config.ChatBot.Map.Notify_On_First_Update= + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger= +config.ChatBot.PlayerListLogger.Delay= + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl= + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture= +config.ChatBot.ReplayCapture.Backup_Interval= + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler= + diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini index 444f5b10..8bb1e67d 100644 --- a/MinecraftClient/Resources/lang/en.ini +++ b/MinecraftClient/Resources/lang/en.ini @@ -1,10 +1,14 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate=Help us translate MCC: {0} +mcc.run_with_default_settings=\nMCC is running with default settings. +mcc.settings_generated=§cSettings file MinecraftClient.ini has been generated. +mcc.has_update=§eNew version of MCC available: {0} mcc.login=Login : mcc.login_basic_io=Please type the username or email of your choice. -mcc.password=Password : +mcc.password=Password: mcc.password_basic_io=Please type the password for {0}. -mcc.password_hidden=Password : {0} +mcc.password_hidden=Password(invisible): {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}. @@ -46,6 +50,8 @@ mcc.realms_available=You have access to the following Realms worlds mcc.realms_join=Use realms: as server IP to join a Realms world mcc.generator.generating=Generating {0} palette using the dataset: {1} mcc.generator.done=Succesfully generated {0} palette using the dataset: {1} +mcc.invaild_config=Failed to parse the settings file, enter "{0}new" to generate a new configuration. +mcc.gen_new_config=New configuration file "{0}" is generated. [debug] # Messages from MCC Debug Mode @@ -67,13 +73,12 @@ 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. error.login.ssl=SSL Error. error.login.unknown=Unknown Error. -error.login.ssl_help=§8It appears that you are using Mono to run this program.\nThe first time, you have to import HTTPS certificates using:\nmozroots --import --ask-remove error.login.cancel=User cancelled. error.login_failed=Failed to login to this server. error.join=An error occured when attempting to join this server. @@ -87,13 +92,7 @@ error.no_version_report=§8Server does not report its protocol version, autodete error.connection_timeout=§8A timeout occured while attempting to connect to this IP. error.forge=§8Forge Login Handshake did not complete successfully error.forge_encrypt=§8Forge StartEncryption Handshake did not complete successfully -error.setting.str2int=Failed to convert '{0}' into an integer. Please check your settings. -error.setting.str2double=Failed to convert '{0}' to a floating-point number. Please check your settings. -error.setting.str2locationList.convert_fail=Failed to convert '{0}' to a floating point number. Please check your settings. -error.setting.str2locationList.format_err=Wrong format, can't parse '{0}' into position data.. Please check your settings. error.setting.argument_syntax={0}: Invalid syntax, expecting --argname=value or --section.argname=value -error.setting.unknown_section={0}: Unknown setting section '{1}' -error.setting.unknown_or_invalid={0}: Unknown setting or invalid value error.http_code=§8Got error code from server: {0} error.auth=§8Got error code from server while refreshing authentication: {0} error.realms.ip_error=Cannot retrieve the server IP of your Realms world @@ -239,7 +238,13 @@ cmd.changeSlot.changed=Changed to slot {0} cmd.changeSlot.fail=Could not change slot # Chunk -cmd.chunk.desc=Displays the chunk loading status.\nChange enableemoji=false in the settings if the display is confusing. +cmd.chunk.desc=Displays the chunk loading status.\nChange EnableEmoji=false in the settings if the display is confusing. +cmd.chunk.current=Current location:{0}, chunk: ({1}, {2}). +cmd.chunk.marked=Marked location: +cmd.chunk.chunk_pos=chunk: ({0}, {1}). +cmd.chunk.outside=§x§0Since the marked chunk is outside the graph, it will not be displayed!§r +cmd.chunk.icon=Player:{0}, MarkedChunk:{1}, NotReceived:{2}, Loading:{3}, Loaded:{4} +cmd.chunk.for_debug=§x§0This command is used for debugging, make sure you know what you are doing.§r # Connect cmd.connect.desc=connect to the specified server. @@ -421,7 +426,7 @@ cmd.setrndstr.format=setrnd variable string1 "\"string2\" string3" # Sneak cmd.sneak.desc=Toggles sneaking cmd.sneak.on=You are sneaking now -cmd.sneak.off=You aren't sneaking anymore +cmd.sneak.off=You are not sneaking anymore # DropItem cmd.dropItem.desc=Drop specified type of items from player inventory or opened container @@ -434,7 +439,7 @@ cmd.tps.current=Current tps # Useblock cmd.useblock.desc=Place a block or open chest -cmd.useblock.use=Useblock at ({0:0.0}, {1:0.0}, {2:0.0}) {3} +cmd.useblock.use=Useblock at ({0:0.0}, {1:0.0}, {2:0.0}) {3}. # Useitem cmd.useitem.desc=Use (left click) an item on the hand @@ -445,23 +450,18 @@ cmd.useitem.use=Used an item # ChatBots. Naming style: bot.. # Alerts -bot.alerts.start_rain=§cWeather change: It's raining now.§r -bot.alerts.end_rain=§cWeather change: It's no longer raining.§r -bot.alerts.start_thunderstorm=§cWeather change: It's a thunderstorm.§r -bot.alerts.end_thunderstorm=§cWeather change: It's no longer a thunderstorm.§r +bot.alerts.start_rain=§cWeather change: It is raining now.§r +bot.alerts.end_rain=§cWeather change: It is no longer raining.§r +bot.alerts.start_thunderstorm=§cWeather change: It is a thunderstorm.§r +bot.alerts.end_thunderstorm=§cWeather change: It is no longer a thunderstorm.§r # Anti AFK bot.antiafk.not_using_terrain_handling=The terrain handling is not enabled in the settings of the client, enable it if you want to use it with this bot. Using alternative (command) method. -bot.antiafk.invalid_range_partial=Invalid time range provided, using the first part of the range {0} as the time! -bot.antiafk.invalid_range=Invalid time range provided, using default time of 600! -bot.antiafk.invalid_value=Invalid time provided, using default time of 600! bot.antiafk.swapping=The time range begins with a bigger value, swapped them around. bot.antiafk.invalid_walk_range=Invalid walk range provided, must be a positive integer greater than 0, using default value of 5! # AutoAttack -bot.autoAttack.mode=Unknown attack mode: {0}. Using single mode as default. -bot.autoAttack.priority=Unknown priority: {0}. Using distance priority as default. -bot.autoAttack.invalidcooldown=Attack cooldown value cannot be smaller than 0. Using auto as default +bot.autoAttack.invalidcooldown=Attack cooldown value cannot be smaller than 0. # AutoCraft bot.autoCraft.cmd=Auto-crafting ChatBot command @@ -489,15 +489,23 @@ bot.autoCraft.aborted=Crafting aborted! Check your available materials. bot.autoCraft.craft_fail=Crafting failed! Waiting for more materials bot.autoCraft.timeout=Action timeout! Reason: {0} bot.autoCraft.error.config=Error while parsing config: {0} -bot.autoCraft.exception.empty=Empty configuration file: {0} -bot.autoCraft.exception.invalid=Invalid configuration file: {0} -bot.autoCraft.exception.item_miss=Missing item in recipe: {0} -bot.autoCraft.exception.invalid_table=Invalid tablelocation format: {0} -bot.autoCraft.exception.item_name=Invalid item name in recipe {0} at {1} bot.autoCraft.exception.name_miss=Missing recipe name while parsing a recipe -bot.autoCraft.exception.slot=Invalid slot field in recipe: {0} bot.autoCraft.exception.duplicate=Duplicate recipe name specified: {0} bot.autoCraft.debug.no_config=No config found. Writing a new one. +bot.autocraft.invaild_slots=The number of slots does not match and has been adjusted automatically. +bot.autocraft.invaild_invaild_result=Invalid result item! + +# AutoDig +bot.autodig.start_delay=Digging will start in {0:0.0} second(s). +bot.autodig.dig_timeout=Digging block timeout, retry. +bot.autodig.not_allow=The block currently pointed to is not in the allowed list. +bot.autodig.cmd=Auto-digging ChatBot command +bot.autodig.available_cmd=Available commands: {0}. Use /digbot help for more information. +bot.autodig.start=Automatic digging has started. +bot.autodig.stop=Auto-digging has been stopped. +bot.autodig.help.start=Start the automatic digging bot. +bot.autodig.help.stop=Deactivate the automatic digging bot. +bot.autodig.help.help=Get the command description. Usage: /digbot help # AutoDrop bot.autoDrop.cmd=AutoDrop ChatBot command @@ -596,6 +604,7 @@ bot.map.received_map=Received a new Map, with Id: {0} bot.map.rendered=Succesfully rendered a map with id '{0}' to: '{1}' bot.map.failed_to_render=Failed to render the map with id: '{0}' bot.map.list_item=- Map id: {0} (Last Updated: {1}) +bot.map.windows_only=Save to file is currently only available for the windows platform. # ReplayCapture bot.replayCapture.cmd=replay command @@ -610,7 +619,7 @@ cmd.follow.already_stopped=Already stopped cmd.follow.stopping=Stopped following! cmd.follow.invalid_name=Invalid or empty player name provided! cmd.follow.invalid_player=The specified player is either not connected out out of the range! -cmd.follow.cant_reach_player=Can\'t reach the player, he is either in chunks that are not loaded, too far away or not reachable by a bot due to obstacles like gaps or water bodies! +cmd.follow.cant_reach_player=Can not reach the player, he is either in chunks that are not loaded, too far away or not reachable by a bot due to obstacles like gaps or water bodies! cmd.follow.already_following=Already following {0}! cmd.follow.switched=Switched to following {0}! cmd.follow.started=Started following {0}! @@ -630,8 +639,6 @@ bot.script.fail=Script '{0}' failed to run ({1}). bot.script.pm.loaded=Script '{0}' loaded. # ScriptScheduler -bot.scriptScheduler.loading=Loading tasks from '{0}' -bot.scriptScheduler.not_found=File not found: '{0}' bot.scriptScheduler.loaded_task=Loaded task:\n{0} bot.scriptScheduler.no_trigger=This task will never trigger:\n{0} bot.scriptScheduler.no_action=No action for task:\n{0} @@ -643,3 +650,233 @@ 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.write.fail=§cFailed to write to settings file {0}§r +config.backup.fail=§cFailed to write to backup file {0}§r +config.saving=§aThe current setting is saved as {0} + +# Head +config.Head=Startup Config File\n# Please do not record extraneous data in this file as it will be overwritten by MCC.\n\n# New to Minecraft Console Client? Check out this document: https://mccteam.github.io/guide/configuration.html\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=The address of the game server, "Host" can be filled in with domain name or IP address. (The "Port" field can be deleted, it will be resolved automatically) +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". If the login always fails, please try to use the "browser" once. + +# 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, check https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239 +config.Main.Advanced.internal_cmd_char=Use "none", "slash"(/) or "backslash"(\). +config.Main.Advanced.message_cooldown=Controls the minimum interval (in seconds) between sending each message to the server. +config.Main.Advanced.bot_owners=Set the owner of the bot. /!\ 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=For remote control of the 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. +config.Main.Advanced.entity_handling=Toggle entity handling. +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=AccountList: It allows a fast account switching without directly using the credentials\n# Usage examples: "/tell reco Player2", "/connect Player1" +config.Main.Advanced.server_list=ServerList: It allows an easier and faster server switching with short aliases instead of full server IP\n# Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias.\n# Usage examples: "/tell connect Server1", "/connect Server2" +config.Main.Advanced.player_head_icon=Only works on Windows XP-8 or Windows 10 with old console. +config.Main.Advanced.exit_on_failure=Whether to exit directly when an error occurs, 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=Customize the TCP connection timeout with the server. (in seconds) +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 signature related 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=This setting affects only the messages in the 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="disable" or "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.(look like "§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=true to login using proxy.\n# If the connection to the Minecraft game server is blocked by the firewall, set Enabled_Ingame=true to use a proxy to connect to the game server.\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 https://mccteam.github.io/guide/configuration.html#chat-format +config.ChatFormat.Builtins=MCC support for common message formats. Set "false" to avoid conflicts with custom formats. +config.ChatFormat.UserDefined=Whether to use the custom regular expressions below for detection. + +# 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=List of words/strings to alert you on. +config.ChatBot.Alerts.Excludes=List of words/strings to NOT alert you on. +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=The time interval for execution. (in seconds) +config.ChatBot.AntiAfk.Command=Command to send to the server. +config.ChatBot.AntiAfk.Use_Sneak=Whether to sneak when sending the command. +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 times 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. Set "Custom = false" 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 https://mccteam.github.io/guide/chat-bots.html#auto-craft 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 +config.ChatBot.AutoCraft.CraftingTable=Location of the crafting table if you intended to use it. Terrain and movements must be enabled. +config.ChatBot.AutoCraft.OnFailure=What to do on crafting failure, "abort" or "wait". +config.ChatBot.AutoCraft.Recipes=Recipes.Name: The name can be whatever you like and it is used to represent the recipe.\n# Recipes.Type: crafting table type: "player" or "table"\n# Recipes.Result: the resulting item\n# Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots.\n# For the naming of the items, please see:\n# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs + +# AutoDig +config.ChatBot.AutoDig=Auto-digging blocks.\n# You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig.\n# Since MCC does not yet support accurate calculation of the collision volume of blocks, all blocks are considered as complete cubes when obtaining the position of the lookahead.\n# For the naming of the block, please see https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs +config.ChatBot.AutoDig.Auto_Tool_Switch=Automatically switch to the appropriate tool. +config.ChatBot.AutoDig.Durability_Limit=Will not use tools with less durability than this. Set to zero to disable this feature. +config.ChatBot.AutoDig.Drop_Low_Durability_Tools=Whether to drop the current tool when its durability is too low. +config.ChatBot.AutoDig.Mode="lookat", "fixedpos" or "both". Digging the block being looked at, the block in a fixed position, or the block that needs to be all met. +config.ChatBot.AutoDig.Locations=The position of the blocks when using "fixedpos" or "both" mode. +config.ChatBot.AutoDig.Location_Order="distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list. +config.ChatBot.AutoDig.Auto_Start_Delay=How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start. +config.ChatBot.AutoDig.Dig_Timeout=Mining a block for more than "Dig_Timeout" seconds will be considered a timeout. +config.ChatBot.AutoDig.Log_Block_Dig=Whether to output logs when digging blocks. +config.ChatBot.AutoDig.List_Type=Wether to treat the blocks list as a "whitelist" or as a "blacklist". + +# 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 + +# 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 mainhand or the offhand 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=Hook movement in the X and Z axis less than this value will be considered stationary. +config.ChatBot.AutoFishing.Hook_Threshold=A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish. +config.ChatBot.AutoFishing.Log_Fish_Bobber=Used to adjust the above two thresholds, which when enabled will print the change in the position of the fishhook entity upon receipt of its movement packet. +config.ChatBot.AutoFishing.Enable_Move=This allows the player to change position/facing after each fish caught. +config.ChatBot.AutoFishing.Movements=It will move in order "1->2->3->4->3->2->1->2->..." and can change position or facing or both each time. It is recommended to change the facing only. + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog=Automatically relog when disconnected by server, for example because the server is restating\n# /!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks +config.ChatBot.AutoRelog.Delay=The delay time before joining the server. (in 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=If the kickout message matches any of the strings, then autorelog will be triggered. + +# 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 (in seconds) (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 is 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=(In seconds) + +# 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 https://mccteam.github.io/guide/chat-bots.html#script-scheduler for more info diff --git a/MinecraftClient/Resources/lang/fr.ini b/MinecraftClient/Resources/lang/fr.ini index 3b6e904a..8f29db97 100644 --- a/MinecraftClient/Resources/lang/fr.ini +++ b/MinecraftClient/Resources/lang/fr.ini @@ -1,15 +1,22 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate= +mcc.run_with_default_settings= +mcc.settings_generated= +mcc.has_update= mcc.login=Connexion : mcc.login_basic_io=Veuillez saisir un nom d'utilisateur ou une adresse email. -mcc.password=Mot de passe : +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.profile_key_invalid= +mcc.profile_key_valid= mcc.connecting=Connexion à {0}... -mcc.ip=Adresse IP du Serveur : +mcc.fetching_key= +mcc.ip=Adresse IP du Serveur : mcc.use_version=§8Utilisation de la version {0} de Minecraft (protocole v{1}) mcc.unknown_version=§8Version MC non connue ou non supportée. {0}.\nPassage en mode autodétection. mcc.forge=Vérification si le serveur utilise Forge... @@ -20,7 +27,7 @@ mcc.not_found=§8Échec de la résolution SRV pour {0}\n{1} : {2} mcc.retrieve=Récupération des informations du serveur... mcc.restart=Redémarrage de Minecraft Console Client... mcc.restart_delay=Attente de {0} secondes avant de redémarrer... -mcc.server_version=Version du serveur : +mcc.server_version=Version du serveur : mcc.disconnected=Actuellement non connecté à un serveur. Utilisez '{0}help' pour obtenir de l'aide. mcc.press_exit=Ou appuyez sur la touche Entrer pour quitter Minecraft Console Client. mcc.version_supported=La version est prise en charge.\nConnexion... @@ -41,13 +48,16 @@ mcc.with_forge=, avec Forge mcc.handshake=§8Handshake réussi. (ID Serveur : {0}) mcc.realms_available=Vous avez accès aux mondes Realms suivants mcc.realms_join=Utilisez realms: comme ip de serveur pour rejoindre un monde Realms - +mcc.generator.generating= +mcc.generator.done= [debug] # Messages from MCC Debug Mode debug.color_test=Test des couleurs : Votre terminal devrait afficher {0} debug.session_cache_ok=§8Informations de Session chargées depuis le disque avec succès. debug.session_cache_fail=§8Aucune session n'a pu être chargé depuis le disque +debug.keys_cache_ok= +debug.keys_cache_fail= debug.session_id=Succès. (ID de Session : {0}) debug.crypto=§8Clé de chiffrement & hash générés. debug.request=§8Envoi d'une requête à {0} @@ -58,7 +68,7 @@ error.ping=Échec du ping sur l'IP. error.unsupported=Impossible de se connecter au serveur : Cette version n'est pas prise en charge ! error.determine=La version du serveur n'a pas pu être déterminée. error.forgeforce=Impossible de forcer le support de Forge sur cette version de Minecraft ! -error.login=Échec d'authentification : +error.login=Échec d'authentification : error.login.migrated=Compte migré, utiliser l'email à la place du nom d'utilisateur. error.login.server=Serveur d'authentification indisponible. Veuillez réessayer ultérieurement. error.login.blocked=Mot de passe incorrect, IP bannie ou trop de connexions. @@ -67,7 +77,6 @@ error.login.premium=L'utilisateur n'est pas premium. error.login.network=Erreur réseau. error.login.ssl=Erreur SSL. error.login.unknown=Erreur inconnue. -error.login.ssl_help=§8Il semblerait que vous utilisez Mono pour lancer ce programme.\nLa première fois, vous devez importer les certificats HTTPS en utilisant :\nmozroots --import --ask-remove error.login.cancel=Annulé par l'utilisateur. error.login_failed=Échec de la connexion au serveur. error.join=Une erreur est survenue lors de la connexion au serveur. @@ -81,10 +90,7 @@ error.no_version_report=§8Le serveur n'indique pas la version de son protocole. error.connection_timeout=§8La connexion à cette adresse IP n'a pas abouti (timeout). error.forge=§8Le Handshake Forge (Login) n'a pas réussi error.forge_encrypt=§8Le Handshake Forge (StartEncryption) n'a pas réussi -error.setting.str2int=Échec de la conversion '{0}' en nombre entier. Veuillez vérifier la configuration. error.setting.argument_syntax={0} : Syntaxe invalide, --argname=value ou --section.argname=value attendu -error.setting.unknown_section={0} : Section de configuration '{1}' inconnue -error.setting.unknown_or_invalid={0} : Paramètre inconnu ou valeur invalide error.http_code=§8Code d'erreur indiqué par le serveur : {0} error.auth=§8Le serveur a retourné un code d'erreur lors du rafraichissement de l'authentification : {0} error.realms.ip_error=Impossible de déterminer l'IP du serveur de votre monde Realms @@ -92,6 +98,11 @@ error.realms.access_denied=Ce monde Realms n'existe pas ou l'accès a été refu error.realms.server_unavailable=Le serveur peut nécessiter un peu de temps pour démarrer. Veuillez réessayer plus tard. error.realms.server_id=ID Realms invalide ou inconnu. error.realms.disabled=Tentative de connexion à un monde Realms mais le support Realms est désactivé dans la configuration +error.missing.argument= +error.usage= +error.generator.invalid= +error.generator.path= +error.generator.json= [internal command] # MCC internal help command @@ -173,6 +184,14 @@ cache.ignore_line=§8Ignoré une ligne de jeton de session invalide : {0} cache.read_fail_plain=§8Échec de la lecture ddu fichier de cache de sessions : {0} cache.saving=§8Sauvegarde du cache de sessions sur le disque cache.save_fail=§8Échec d'écriture du fichier de cache de sessions : {0} +# Profile Key Cache +cache.loading_keys= +cache.loaded_keys= +cache.ignore_string_keys= +cache.ignore_line_keys= +cache.read_fail_plain_keys= +cache.saving_keys= +cache.save_fail_keys= [proxy] proxy.connected=§8Connecté au proxy {0}:{1} @@ -186,6 +205,7 @@ chat.fail=§8Échec de téléchargement du fichier. chat.from_dir=§8Utilisation à la place de en_GB.lang depuis votre jeu Minecraft. chat.loaded=§8Fichier de traductions chargé. chat.not_found=§8Fichier de traductions non trouvé : '{0}'\nCertains messages ne seront pas affichés correctement sans ce fichier. +chat.message_chain_broken= [general] # General message/information (i.e. Done) @@ -201,12 +221,23 @@ general.available_cmd=Commandes disponibles : {0} # Animation cmd.animation.desc=Balancer le bras. +# Bots +cmd.bots.desc= +cmd.bots.list= +cmd.bots.notfound= +cmd.bots.noloaded= +cmd.bots.unloaded= +cmd.bots.unloaded_all= + # ChangeSlot cmd.changeSlot.desc=Changer de slot (emplacement) dans la hotbar cmd.changeSlot.nan=Le slot n'a pas pu être changé : Numéro invalide cmd.changeSlot.changed=Slot sélectionné : {0} cmd.changeSlot.fail=Le slot n'a pas pu être sélectionné +# Chunk +cmd.chunk.desc= + # Connect cmd.connect.desc=Se connecter au serveur spécifié cmd.connect.unknown=Compte '{0}' inconnu. @@ -248,6 +279,19 @@ cmd.entityCmd.distance=Distance cmd.entityCmd.location=Emplacement cmd.entityCmd.type=Type +# Exec If +cmd.execif.desc= +cmd.execif.executed= +cmd.execif.executed_no_output= +cmd.execif.error_occured= +cmd.execif.error= + +# Exec Multi +cmd.execmulti.desc= +cmd.execmulti.executed= +cmd.execmulti.result= +cmd.execmulti.no_result= + # Exit cmd.exit.desc=Se déconnecter du serveur. @@ -267,15 +311,18 @@ cmd.inventory.close_fail=Impossible de fermer l'inventaire #{0} cmd.inventory.not_exist=L'inventaire #{0} n'existe pas cmd.inventory.inventory=Inventaire cmd.inventory.inventories=Inventaires +cmd.inventory.inventories_available= cmd.inventory.hotbar=Votre sélection dans la horbar : {0} cmd.inventory.damage=Dommage cmd.inventory.left=gauche cmd.inventory.right=droit cmd.inventory.middle=du milieu cmd.inventory.clicking=Clic {0} sur le slot {1} dans la fenêtre #{2} +cmd.inventory.shiftclick= +cmd.inventory.shiftclick_fail= cmd.inventory.no_item=Aucun objet dans le slot #{0} cmd.inventory.drop=Lâché 1 objet depuis le slot #{0} -cmd.inventory.drop_stack=Lâché tous les objets du slot #{0} +cmd.inventory.drop_stack=Lâché tous les objets du slot #{0} # Inventory Help cmd.inventory.help.basic=Utilisation de base cmd.inventory.help.available=Actions possibles @@ -284,10 +331,29 @@ cmd.inventory.help.usage=Utilisation cmd.inventory.help.list=Lister votre inventaire. cmd.inventory.help.close=Fermer un conteneur ouvert. cmd.inventory.help.click=Cliquer sur un objet. +cmd.inventory.help.shiftclick= cmd.inventory.help.drop=Lâcher un objet de votre inventaire. cmd.inventory.help.creativegive=Se donner un objet en mode créatif. cmd.inventory.help.creativedelete=Vider un slot en mode créatif. -cmd.inventory.help.unknown=Action inconnue. +cmd.inventory.help.inventories= +cmd.inventory.help.search= +cmd.inventory.help.unknown=Action inconnue. +cmd.inventory.found_items= +cmd.inventory.no_found_items= + +# Leave bed +cmd.bed.desc= +cmd.bed.leaving= +cmd.bed.trying_to_use= +cmd.bed.in= +cmd.bed.not_in= +cmd.bed.not_a_bed= +cmd.bed.searching= +cmd.bed.bed_not_found= +cmd.bed.found_a_bed_at= +cmd.bed.cant_reach_safely= +cmd.bed.moving= +cmd.bed.failed_to_reach_in_time= # List cmd.list.desc=Lister les joueurs connectés. @@ -301,6 +367,8 @@ cmd.look.desc=Regarder dans une direction ou des coordonnées. cmd.look.unknown=Direction inconnue : '{0}' cmd.look.at=Rotation de la tête (YAW:{0}, PITCH:{1}) cmd.look.block=Rotation de la tête vers : {0} +cmd.look.inspection= +cmd.look.noinspection= # Move cmd.move.desc=Marcher ou commencer à marcher. @@ -313,10 +381,21 @@ cmd.move.fail=Échec de calcul du chemin vers {0} cmd.move.suggestforce=Échec de calcul du chemin vers {0}. Utilisez -f pour autoriser les mouvements risqués. cmd.move.gravity.enabled=La gravité est activée. cmd.move.gravity.disabled=La gravité est désactivée. +cmd.move.chunk_loading_status= +cmd.move.chunk_not_loaded= # Reco cmd.reco.desc=Relancer le programme et se reconnecter au serveur +# Reload +cmd.reload.started= +cmd.reload.warning1= +cmd.reload.warning2= +cmd.reload.warning3= +cmd.reload.warning4= +cmd.reload.finished= +cmd.reload.desc= + # Respawn cmd.respawn.desc=Réapparaitre sur le serveur si vous êtes mort. cmd.respawn.done=Vous êtes réapparu. @@ -352,6 +431,7 @@ cmd.tps.current=TPS actuel # Useblock cmd.useblock.desc=Placer un bloc ou ouvrir un coffre +cmd.useblock.use= # Useitem cmd.useitem.desc=Utiliser (clic gauche) un objet dans la main @@ -361,9 +441,18 @@ cmd.useitem.use=Objet utilisé [bot] # ChatBots. Naming style: bot.. +# Alerts +bot.alerts.start_rain= +bot.alerts.end_rain= +bot.alerts.start_thunderstorm= +bot.alerts.end_thunderstorm= + +# Anti AFK +bot.antiafk.not_using_terrain_handling= +bot.antiafk.swapping= +bot.antiafk.invalid_walk_range= + # AutoAttack -bot.autoAttack.mode=Mode d'attaque inconnu : {0}. Utilisation du mode par défaut (single). -bot.autoAttack.priority=Priorité inconnue : {0}. Utilisation du mode par défaut (privilégier la distance). bot.autoAttack.invalidcooldown=La valeur du Cooldown d'attaque ne peut pas être plus faible que 0. Utilisation du mode par défaut (distance automatique). # AutoCraft @@ -392,15 +481,11 @@ bot.autoCraft.aborted=Fabrication interrompue ! Vérifiez vos matériaux disponi bot.autoCraft.craft_fail=Échec de la fabrication ! Attente de plus de matériaux bot.autoCraft.timeout=Le délai d'attente de l'action expiré ! Raison : {0} bot.autoCraft.error.config=Erreur de traitement de la configuration : {0} -bot.autoCraft.exception.empty=Fichier de configuration vide : {0} -bot.autoCraft.exception.invalid=Fichier de configuration invalide : {0} -bot.autoCraft.exception.item_miss=Objet manquant dans la recette : {0} -bot.autoCraft.exception.invalid_table=Le format du lieu de l'établi est invalide : {0} -bot.autoCraft.exception.item_name=Nom d'objet invalide dans la recette {0} à {1} bot.autoCraft.exception.name_miss=Nom manquant lors du chargement d'une recette -bot.autoCraft.exception.slot=Slot invalide dans la recette : {0} bot.autoCraft.exception.duplicate=Nom de recette spécifié en double : {0} bot.autoCraft.debug.no_config=Fichier de configuration introuvable. Création d'un nouveau fichier. +bot.autocraft.invaild_slots= +bot.autocraft.invaild_invaild_result= # AutoDrop bot.autoDrop.cmd=Commande du ChatBot AutoDrop @@ -419,9 +504,17 @@ bot.autoDrop.no_mode=Impossible de lire le mode de Drop dans la configuration. U bot.autoDrop.no_inventory=Impossible de trouver l'inventaire {0} ! # AutoFish +bot.autoFish.no_inv_handle= +bot.autoFish.start= bot.autoFish.throw=Canne à pêche lancée bot.autoFish.caught=Poisson attrapé ! +bot.autoFish.caught_at= bot.autoFish.no_rod=Pas de canne a pêche dans la main. Peut-être a-t-elle cassé ? +bot.autoFish.despawn= +bot.autoFish.fishing_timeout= +bot.autoFish.cast_timeout= +bot.autoFish.update_lookat= +bot.autoFish.switch= # AutoRelog bot.autoRelog.launch=Lancement avec {0} tentatives de reconnexion @@ -465,7 +558,7 @@ bot.mailer.init_fail.mail_retention=Impossible d'activer le Mailer : La rétenti bot.mailer.create.db=Création d'un nouveau fichier de base de données : {0} bot.mailer.create.ignore=Création d'une nouvelle liste d'exclusions : {0} bot.mailer.load.db=Chargement de la base de données : {0} -bot.mailer.load.ignore=Chargement de la liste d'exclusions : +bot.mailer.load.ignore=Chargement de la liste d'exclusions : bot.mailer.cmd=Commande du Mailer @@ -481,12 +574,43 @@ bot.mailer.cmd.ignore.removed=Retrait de {0} de la liste d'exclusions ! bot.mailer.cmd.ignore.invalid=Nom manquant ou invalide. Utilisation : {0} bot.mailer.cmd.help=Voir l'utilisation +# Maps +bot.map.cmd.desc= +bot.map.cmd.not_found= +bot.map.cmd.invalid_id= +bot.map.received= +bot.map.no_maps= +bot.map.received_map= +bot.map.rendered= +bot.map.failed_to_render= +bot.map.list_item= + # ReplayCapture bot.replayCapture.cmd=Commande de Replay bot.replayCapture.created=Fichier de Replay créé. bot.replayCapture.stopped=Enregistrement arrêté. bot.replayCapture.restart=L'enregistrement a été stoppé. Redémarrez le programme pour commencer un nouvel enregistrement. +# Follow player +cmd.follow.desc= +cmd.follow.usage= +cmd.follow.already_stopped= +cmd.follow.stopping= +cmd.follow.invalid_name= +cmd.follow.invalid_player= +cmd.follow.cant_reach_player= +cmd.follow.already_following= +cmd.follow.switched= +cmd.follow.started= +cmd.follow.unsafe_enabled= +cmd.follow.note= +cmd.follow.player_came_to_the_range= +cmd.follow.resuming= +cmd.follow.player_left_the_range= +cmd.follow.pausing= +cmd.follow.player_left= +cmd.follow.stopping= + # Script bot.script.not_found=§8[MCC] [{0}] Impossible de trouver le fichier de script : {1} bot.script.file_not_found=Fichier non trouvé : '{0}' @@ -494,8 +618,6 @@ bot.script.fail=Le script '{0}' n'a pas pu être lancé ({1}). bot.script.pm.loaded=Script '{0}' chargé. # ScriptScheduler -bot.scriptScheduler.loading=Chargement des tâches depuis '{0}' -bot.scriptScheduler.not_found=Fichier non trouvé : '{0}' bot.scriptScheduler.loaded_task=Tâche chargée :\n{0} bot.scriptScheduler.no_trigger=Cette tâche ne sera jamais déclenchée :\n{0} bot.scriptScheduler.no_action=Aucune action pour cette tâche :\n{0} @@ -507,3 +629,220 @@ bot.scriptScheduler.task=triggeronfirstlogin : {0}\n triggeronlogin : {1}\n trig # TestBot bot.testBot.told=Bot : {0} m'a dit : {1} bot.testBot.said=Bot : {0} a dit : {1} + + +[config] + +config.load= +config.load.fail= +config.write.fail= +config.backup.fail= +config.saving= + +# Head +config.Head= + +# Main.General +config.Main.General.account= +config.Main.General.login= +config.Main.General.server_info= +config.Main.General.method= + +# Main.Advanced +config.Main.Advanced= +config.Main.Advanced.language= +config.Main.Advanced.internal_cmd_char= +config.Main.Advanced.message_cooldown= +config.Main.Advanced.bot_owners= +config.Main.Advanced.mc_version= +config.Main.Advanced.mc_forge= +config.Main.Advanced.brand_info= +config.Main.Advanced.chatbot_log_file= +config.Main.Advanced.private_msgs_cmd_name= +config.Main.Advanced.show_system_messages= +config.Main.Advanced.show_xpbar_messages= +config.Main.Advanced.show_chat_links= +config.Main.Advanced.show_inventory_layout= +config.Main.Advanced.terrain_and_movements= +config.Main.Advanced.inventory_handling= +config.Main.Advanced.entity_handling= +config.Main.Advanced.session_cache= +config.Main.Advanced.profilekey_cache= +config.Main.Advanced.resolve_srv_records= +config.Main.Advanced.account_list= +config.Main.Advanced.server_list= +config.Main.Advanced.player_head_icon= +config.Main.Advanced.exit_on_failure= +config.Main.Advanced.script_cache= +config.Main.Advanced.timestamps= +config.Main.Advanced.auto_respawn= +config.Main.Advanced.minecraft_realms= +config.Main.Advanced.move_head_while_walking= +config.Main.Advanced.timeout= +config.Main.Advanced.enable_emoji= +config.Main.Advanced.movement_speed= +config.Main.Advanced.language.invaild= + +# Signature +config.Signature= +config.Signature.LoginWithSecureProfile= +config.Signature.SignChat= +config.Signature.SignMessageInCommand= +config.Signature.MarkLegallySignedMsg= +config.Signature.MarkModifiedMsg= +config.Signature.MarkIllegallySignedMsg= +config.Signature.MarkSystemMessage= +config.Signature.ShowModifiedChat= +config.Signature.ShowIllegalSignedChat= + +# Logging +config.Logging= +config.Logging.DebugMessages= +config.Logging.ChatMessages= +config.Logging.InfoMessages= +config.Logging.WarningMessages= +config.Logging.ErrorMessages= +config.Logging.ChatFilter= +config.Logging.DebugFilter= +config.Logging.FilterMode= +config.Logging.LogToFile= +config.Logging.LogFile= +config.Logging.PrependTimestamp= +config.Logging.SaveColorCodes= + +# AppVars +config.AppVars.Variables= + +# Proxy +config.Proxy= +config.Proxy.Enabled_Login= +config.Proxy.Enabled_Ingame= +config.Proxy.Server= +config.Proxy.Proxy_Type= +config.Proxy.Username= +config.Proxy.Password= + +# ChatFormat +config.ChatFormat= +config.ChatFormat.Builtins= +config.ChatFormat.UserDefined= + +# MCSettings +config.MCSettings= +config.MCSettings.Enabled= +config.MCSettings.Locale= +config.MCSettings.RenderDistance= +config.MCSettings.Difficulty= +config.MCSettings.ChatMode= +config.MCSettings.ChatColors= +config.MCSettings.MainHand= + +# ChatBot +config.ChatBot= + +# ChatBot.Alerts +config.ChatBot.Alerts= +config.ChatBot.Alerts.Beep_Enabled= +config.ChatBot.Alerts.Trigger_By_Words= +config.ChatBot.Alerts.Trigger_By_Rain= +config.ChatBot.Alerts.Trigger_By_Thunderstorm= +config.ChatBot.Alerts.Matches= +config.ChatBot.Alerts.Excludes= +config.ChatBot.Alerts.Log_To_File= +config.ChatBot.Alerts.Log_File= + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk= +config.ChatBot.AntiAfk.Delay= +config.ChatBot.AntiAfk.Command= +config.ChatBot.AntiAfk.Use_Terrain_Handling= +config.ChatBot.AntiAfk.Walk_Range= +config.ChatBot.AntiAfk.Walk_Retries= + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack= +config.ChatBot.AutoAttack.Mode= +config.ChatBot.AutoAttack.Priority= +config.ChatBot.AutoAttack.Cooldown_Time= +config.ChatBot.AutoAttack.Interaction= +config.ChatBot.AutoAttack.Attack_Hostile= +config.ChatBot.AutoAttack.Attack_Passive= +config.ChatBot.AutoAttack.List_Mode= +config.ChatBot.AutoAttack.Entites_List= + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft= +config.ChatBot.AutoCraft.CraftingTable= +config.ChatBot.AutoCraft.OnFailure= +config.ChatBot.AutoCraft.Recipes= + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop= +config.ChatBot.AutoDrop.Mode= + +# ChatBot.AutoEat +config.ChatBot.AutoEat= + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing= +config.ChatBot.AutoFishing.Antidespawn= +config.ChatBot.AutoFishing.Mainhand= +config.ChatBot.AutoFishing.Auto_Start= +config.ChatBot.AutoFishing.Cast_Delay= +config.ChatBot.AutoFishing.Fishing_Delay= +config.ChatBot.AutoFishing.Fishing_Timeout= +config.ChatBot.AutoFishing.Durability_Limit= +config.ChatBot.AutoFishing.Auto_Rod_Switch= +config.ChatBot.AutoFishing.Stationary_Threshold= +config.ChatBot.AutoFishing.Hook_Threshold= +config.ChatBot.AutoFishing.Log_Fish_Bobber= +config.ChatBot.AutoFishing.Enable_Move= +config.ChatBot.AutoFishing.Movements= + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog= +config.ChatBot.AutoRelog.Delay= +config.ChatBot.AutoRelog.Retries= +config.ChatBot.AutoRelog.Ignore_Kick_Message= +config.ChatBot.AutoRelog.Kick_Messages= + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond= +config.ChatBot.AutoRespond.Match_Colors= + +# ChatBot.ChatLog +config.ChatBot.ChatLog= + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer= +config.ChatBot.FollowPlayer.Update_Limit= +config.ChatBot.FollowPlayer.Stop_At_Distance= + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame= + +# ChatBot.Mailer +config.ChatBot.Mailer= + +# ChatBot.Map +config.ChatBot.Map= +config.ChatBot.Map.Should_Resize= +config.ChatBot.Map.Resize_To= +config.ChatBot.Map.Auto_Render_On_Update= +config.ChatBot.Map.Delete_All_On_Unload= +config.ChatBot.Map.Notify_On_First_Update= + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger= +config.ChatBot.PlayerListLogger.Delay= + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl= + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture= +config.ChatBot.ReplayCapture.Backup_Interval= + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler= + diff --git a/MinecraftClient/Resources/lang/ru.ini b/MinecraftClient/Resources/lang/ru.ini index 83757a3c..3352c035 100644 --- a/MinecraftClient/Resources/lang/ru.ini +++ b/MinecraftClient/Resources/lang/ru.ini @@ -1,15 +1,22 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate= +mcc.run_with_default_settings= +mcc.settings_generated= +mcc.has_update= mcc.login=Логин : mcc.login_basic_io=Пожалуйста, введите имя пользователя или email по вашему выбору. -mcc.password=Пароль: +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= +mcc.profile_key_valid= mcc.connecting=Подключение к {0}... -mcc.ip=IP сервера: +mcc.fetching_key= +mcc.ip=IP сервера: mcc.use_version=§8Используется Minecraft версии {0} (протокол v{1}) mcc.unknown_version=§8Неизвестная или не поддерживаемая версия MC {0}.\nПереключение в режим автоопределения. mcc.forge=Проверка, запущен ли на сервере Forge... @@ -20,7 +27,7 @@ mcc.not_found=§8Failed to perform SRV lookup for {0}\n{1}: {2} mcc.retrieve=Получение информации о сервере... mcc.restart=Перезапуск консольного клиента Minecraft... mcc.restart_delay=Ожидание {0} секунд перед перезапуском... -mcc.server_version=Версия сервера: +mcc.server_version=Версия сервера: mcc.disconnected=Не подключен ни к одному серверу. Используйте '{0}help' для получения справки. mcc.press_exit=Нажмите Enter, чтобы выйти из консольного клиента Minecraft. mcc.version_supported=Версия поддерживается.\nВойти в игру... @@ -41,13 +48,16 @@ mcc.with_forge=, с Forge mcc.handshake=§8Handshake successful. (ID сервера: {0}) mcc.realms_available=У вас есть доступ к следующим мирам Realms mcc.realms_join=Используйте realms:index как IP сервера, чтобы присоединиться к миру Realms - +mcc.generator.generating= +mcc.generator.done= [debug] # Messages from MCC Debug Mode debug.color_test=Цветовой тест: В вашем терминале должно отображаться {0} debug.session_cache_ok=§8Данные сессии успешно загружены с диска. debug.session_cache_fail=§8Ни одна сессия не могла быть загружена с диска +debug.keys_cache_ok= +debug.keys_cache_fail= debug.session_id=Успех. (ID сессии: {0}) debug.crypto=§8Сгенерированы криптографические ключи и хэш. debug.request=§8Выполнение запроса к {0} @@ -58,7 +68,7 @@ error.ping=Не удалось выполнить ping этого IP. error.unsupported=Не удается подключиться к серверу: эта версия не поддерживается! error.determine=Не удалось определить версию сервера. error.forgeforce=Не удается принудительно включить поддержку Forge для этой версии Minecraft! -error.login=Не удалось войти в систему Minecraft : +error.login=Не удалось войти в систему Minecraft : error.login.migrated=Аккаунт перенесен, используйте e-mail в качестве имени пользователя. error.login.server=Серверы входа недоступны. Пожалуйста, повторите попытку позже. error.login.blocked=Неправильный пароль, IP из черного списка или слишком много логинов. @@ -67,7 +77,6 @@ error.login.premium=Пользователь не премиум-класса. error.login.network=Ошибка сети. error.login.ssl=Ошибка SSL. error.login.unknown=Неизвестная ошибка. -error.login.ssl_help=§8Похоже, что вы используете Mono для запуска этой программы.\nВ первый раз, вы должны импортировать HTTPS сертификаты, используя:\nmozroots --import --ask-remove error.login.cancel=Пользователь отменен. error.login_failed=Не удалось войти на этот сервер. error.join=Не удалось подключиться к этому серверу. @@ -81,10 +90,7 @@ error.no_version_report=§8Сервер не сообщает версию св error.connection_timeout=§8При попытке подключения к этому IP произошел тайм-аут. error.forge=§8Forge Login Handshake не завершилось успешно error.forge_encrypt=§8Forge StartEncryption Handshake не завершилось успешно -error.setting.str2int=Не удалось преобразовать '{0}' в int. Проверьте свои настройки. error.setting.argument_syntax={0}: Неверный синтаксис, ожидается --argname=value или --section.argname=value -error.setting.unknown_section={0}: Неизвестный раздел настройки '{1}' -error.setting.unknown_or_invalid={0}: Неизвестная настройка или недопустимое значение error.http_code=§8Получен код ошибки от сервера: {0} error.auth=§8Получили код ошибки от сервера при обновлении аутентификации: {0} error.realms.ip_error=Невозможно получить IP-адрес сервера вашего мира Realms @@ -92,6 +98,11 @@ error.realms.access_denied=Этот мир Realms не существует ил error.realms.server_unavailable=Серверу Realms может потребоваться некоторое время для запуска. Пожалуйста, повторите попытку позже. error.realms.server_id=Неверный или неизвестный идентификатор сервера Realms. error.realms.disabled=Попытка присоединиться к миру Realms, но поддержка Realms отключена в конфигурации +error.missing.argument= +error.usage= +error.generator.invalid= +error.generator.path= +error.generator.json= [internal command] # MCC internal help command @@ -114,7 +125,7 @@ exception.chatbot.init= Методы ChatBot НЕ должны вызывать exception.csrunner.invalid_head=Предоставленный скрипт не имеет действительного заголовка MCCScript [chatbot] -# API ChatBot +# ChatBot API chatbot.reconnect=[{0}] Отключение и повторное подключение к серверу [filemonitor] @@ -173,6 +184,14 @@ cache.ignore_line=§8Игнорирование недопустимой стр cache.read_fail_plain=§8Не удалось прочитать кэш сессии с диска: {0} cache.saving=§8Сохранение кэша сессии на диск cache.save_fail=§8Не удалось записать кэш сессии на диск: {0} +# Profile Key Cache +cache.loading_keys= +cache.loaded_keys= +cache.ignore_string_keys= +cache.ignore_line_keys= +cache.read_fail_plain_keys= +cache.saving_keys= +cache.save_fail_keys= [proxy] proxy.connected=§8Подключен к прокси {0}:{1} @@ -186,6 +205,7 @@ chat.fail=§8Не удалось загрузить файл. chat.from_dir=§8Дополнительно к en_GB.lang из вашей директории Minecraft. chat.loaded=§8Файл переводов загружен. chat.not_found=§8Файл переводов не найден: '{0}'\nНекоторые сообщения не будут правильно печататься без этого файла. +chat.message_chain_broken= [general] # General message/information (i.e. Done) @@ -201,12 +221,23 @@ general.available_cmd=Доступные команды: {0} # Animation cmd.animation.desc=Взмах руки. +# Bots +cmd.bots.desc= +cmd.bots.list= +cmd.bots.notfound= +cmd.bots.noloaded= +cmd.bots.unloaded= +cmd.bots.unloaded_all= + # ChangeSlot cmd.changeSlot.desc=Изменить горячую панель cmd.changeSlot.nan=Не удалось изменить слот: Not a Number cmd.changeSlot.changed=Изменился на слот {0} cmd.changeSlot.fail=Не удалось изменить слот +# Chunk +cmd.chunk.desc= + # Connect cmd.connect.desc=подключиться к указанному серверу. cmd.connect.unknown=Неизвестная учетная запись '{0}'. @@ -248,6 +279,19 @@ cmd.entityCmd.distance=Дальность cmd.entityCmd.location=Местоположение cmd.entityCmd.type=Type +# Exec If +cmd.execif.desc= +cmd.execif.executed= +cmd.execif.executed_no_output= +cmd.execif.error_occured= +cmd.execif.error= + +# Exec Multi +cmd.execmulti.desc= +cmd.execmulti.executed= +cmd.execmulti.result= +cmd.execmulti.no_result= + # Exit cmd.exit.desc=отключиться от сервера. @@ -267,12 +311,15 @@ cmd.inventory.close_fail=Не удалось закрыть опись #{0} cmd.inventory.not_exist=Инвентаризация #{0} не существует cmd.inventory.inventory=Инвентарь cmd.inventory.inventories=Инвентарь +cmd.inventory.inventories_available= cmd.inventory.hotbar=Выбранная вами горячая панель - {0} cmd.inventory.damage=Ущерб cmd.inventory.left=Левый cmd.inventory.right=Правый cmd.inventory.middle=Середина cmd.inventory.clicking={0} щелчок по слоту {1} в окне #{2} +cmd.inventory.shiftclick= +cmd.inventory.shiftclick_fail= cmd.inventory.no_item=Нет предмета в слоте #{0} cmd.inventory.drop=Сбросил 1 предмет из слота #{0} cmd.inventory.drop_stack=Выбросил всю стопку предметов из слота #{0} @@ -284,10 +331,29 @@ cmd.inventory.help.usage=Применение cmd.inventory.help.list=Список инвентаря. cmd.inventory.help.close=Закрыть открытый контейнер. cmd.inventory.help.click=Нажать на элемент. +cmd.inventory.help.shiftclick= cmd.inventory.help.drop=Убрать предмет из инвентаря. cmd.inventory.help.creativegive=Выдать предмет в творческом режиме. cmd.inventory.help.creativedelete=Очистить слот в творческом режиме. -cmd.inventory.help.unknown=Неизвестное действие. +cmd.inventory.help.inventories= +cmd.inventory.help.search= +cmd.inventory.help.unknown=Неизвестное действие. +cmd.inventory.found_items= +cmd.inventory.no_found_items= + +# Leave bed +cmd.bed.desc= +cmd.bed.leaving= +cmd.bed.trying_to_use= +cmd.bed.in= +cmd.bed.not_in= +cmd.bed.not_a_bed= +cmd.bed.searching= +cmd.bed.bed_not_found= +cmd.bed.found_a_bed_at= +cmd.bed.cant_reach_safely= +cmd.bed.moving= +cmd.bed.failed_to_reach_in_time= # List cmd.list.desc=получить список игроков. @@ -301,6 +367,8 @@ cmd.look.desc=смотреть направление или координат cmd.look.unknown=Неизвестное направление '{0}' cmd.look.at=Посмотреть на YAW: {0} PITCH: {1} cmd.look.block=Посмотреть на {0} +cmd.look.inspection= +cmd.look.noinspection= # Move cmd.move.desc= идти или начать идти. @@ -310,10 +378,24 @@ cmd.move.moving=Перемещение {0} cmd.move.dir_fail=Невозможно двигаться в этом направлении. cmd.move.walk=Путь к {0} cmd.move.fail=Не удалось вычислить путь к {0} +cmd.move.suggestforce= +cmd.move.gravity.enabled= +cmd.move.gravity.disabled= +cmd.move.chunk_loading_status= +cmd.move.chunk_not_loaded= # Reco cmd.reco.desc=перезапустить и заново подключиться к серверу. +# Reload +cmd.reload.started= +cmd.reload.warning1= +cmd.reload.warning2= +cmd.reload.warning3= +cmd.reload.warning4= +cmd.reload.finished= +cmd.reload.desc= + # Respawn cmd.respawn.desc=Используйте это, чтобы возродиться, если вы мертвы. cmd.respawn.done=Вы переродились. @@ -328,6 +410,11 @@ cmd.send.desc=отправить сообщение в чат или коман cmd.set.desc=установить пользовательскую %переменную%. cmd.set.format=имя переменной должно быть A-Za-z0-9. +# SetRnd +cmd.setrnd.desc= +cmd.setrndnum.format= +cmd.setrndstr.format= + # Sneak cmd.sneak.desc=Выключает подкрадывание cmd.sneak.on=Вы сейчас крадетесь @@ -344,6 +431,7 @@ cmd.tps.current=Текущий tps # Useblock cmd.useblock.desc=Поставить блок или открыть сундук +cmd.useblock.use= # Useitem cmd.useitem.desc=Использовать (левый клик) предмет на руке @@ -353,9 +441,18 @@ cmd.useitem.use=Использовать предмет [bot] # ChatBots. Naming style: bot.. +# Alerts +bot.alerts.start_rain= +bot.alerts.end_rain= +bot.alerts.start_thunderstorm= +bot.alerts.end_thunderstorm= + +# Anti AFK +bot.antiafk.not_using_terrain_handling= +bot.antiafk.swapping= +bot.antiafk.invalid_walk_range= + # AutoAttack -bot.autoAttack.mode=Неизвестный режим атаки: {0}. По умолчанию используется одиночный режим. -bot.autoAttack.priority=Неизвестный приоритет: {0}. По умолчанию используется приоритет расстояния. bot.autoAttack.invalidcooldown=Значение кулдауна атаки не может быть меньше 0. По умолчанию используется auto. # AutoCraft @@ -384,15 +481,11 @@ bot.autoCraft.aborted=Разработка прервана! Проверьте bot.autoCraft.craft_fail=Разработка не удалась! Ожидание новых материалов bot.autoCraft.timeout=Время выполнения действия истекло! Причина: {0} bot.autoCraft.error.config=Ошибка при разборе конфига: {0} -bot.autoCraft.exception.empty=Пустой файл конфигурации: {0} -bot.autoCraft.exception.invalid=Неправильный файл конфигурации: {0} -bot.autoCraft.exception.item_miss=Пропущен элемент в рецепте: {0} -bot.autoCraft.exception.invalid_table=Неправильный формат расположения таблицы: {0} -bot.autoCraft.exception.item_name=Неправильное имя элемента в рецепте {0} в {1} bot.autoCraft.exception.name_miss=Пропущено имя рецепта при разборе рецепта -bot.autoCraft.exception.slot=Неправильное поле слота в рецепте: {0} bot.autoCraft.exception.duplicate=Дублируется указанное имя рецепта: {0} bot.autoCraft.debug.no_config=Конфигурация не найдена. Записываем новый. +bot.autocraft.invaild_slots= +bot.autocraft.invaild_invaild_result= # AutoDrop bot.autoDrop.cmd=Команда автоудаления ЧатБота @@ -411,9 +504,17 @@ bot.autoDrop.no_mode=Невозможно прочитать режим паде bot.autoDrop.no_inventory=Не удается найти инвентарь {0}! # AutoFish +bot.autoFish.no_inv_handle= +bot.autoFish.start= bot.autoFish.throw=Закинул удочку bot.autoFish.caught=Поймал рыбу! +bot.autoFish.caught_at= bot.autoFish.no_rod=Нет удочки под рукой. Может быть сломана? +bot.autoFish.despawn= +bot.autoFish.fishing_timeout= +bot.autoFish.cast_timeout= +bot.autoFish.update_lookat= +bot.autoFish.switch= # AutoRelog bot.autoRelog.launch=Запуск с {0} попытками повторного подключения @@ -457,7 +558,7 @@ bot.mailer.init_fail.mail_retention=Невозможно включить Mailer bot.mailer.create.db=Создание нового файла базы данных: {0} bot.mailer.create.ignore=Создание нового списка игнорирования: {0} bot.mailer.load.db=Загрузка файла базы данных: {0} -bot.mailer.load.ignore=Загрузка списка игнорирования: +bot.mailer.load.ignore=Загрузка списка игнорирования: bot.mailer.cmd=команда почтового клиента @@ -473,12 +574,43 @@ bot.mailer.cmd.ignore.removed=Удалил {0} из списка игнорир bot.mailer.cmd.ignore.invalid=Пропущено или недопустимое имя. Использование: {0} <имя пользователя bot.mailer.cmd.help=Смотрите использование +# Maps +bot.map.cmd.desc= +bot.map.cmd.not_found= +bot.map.cmd.invalid_id= +bot.map.received= +bot.map.no_maps= +bot.map.received_map= +bot.map.rendered= +bot.map.failed_to_render= +bot.map.list_item= + # ReplayCapture bot.replayCapture.cmd=команда воспроизведения bot.replayCapture.created=Файл воспроизведения создан. bot.replayCapture.stopped=Запись остановлена. bot.replayCapture.restart=Запись была остановлена. Перезапустите программу, чтобы начать другую запись. +# Follow player +cmd.follow.desc= +cmd.follow.usage= +cmd.follow.already_stopped= +cmd.follow.stopping= +cmd.follow.invalid_name= +cmd.follow.invalid_player= +cmd.follow.cant_reach_player= +cmd.follow.already_following= +cmd.follow.switched= +cmd.follow.started= +cmd.follow.unsafe_enabled= +cmd.follow.note= +cmd.follow.player_came_to_the_range= +cmd.follow.resuming= +cmd.follow.player_left_the_range= +cmd.follow.pausing= +cmd.follow.player_left= +cmd.follow.stopping= + # Script bot.script.not_found=§8[MCC] [{0}] Не удается найти файл скрипта: {1} bot.script.file_not_found=Файл не найден: '{0}' @@ -486,8 +618,6 @@ bot.script.fail=Сценарий '{0}' не удалось запустить ({ bot.script.pm.loaded=Сценарий '{0}' загружен. # ScriptScheduler -bot.scriptScheduler.loading=Загрузка задач из '{0}' -bot.scriptScheduler.not_found=Файл не найден: '{0}' bot.scriptScheduler.loaded_task=Загруженная задача:\n{0} bot.scriptScheduler.no_trigger=Эта задача никогда не сработает:\n{0} bot.scriptScheduler.no_action=Нет действия для задачи:\n{0} @@ -499,3 +629,220 @@ bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n trigge # TestBot bot.testBot.told=Bot: {0} сказал мне : {1} bot.testBot.said=Бот: {0} сказал: {1} + + +[config] + +config.load= +config.load.fail= +config.write.fail= +config.backup.fail= +config.saving= + +# Head +config.Head= + +# Main.General +config.Main.General.account= +config.Main.General.login= +config.Main.General.server_info= +config.Main.General.method= + +# Main.Advanced +config.Main.Advanced= +config.Main.Advanced.language= +config.Main.Advanced.internal_cmd_char= +config.Main.Advanced.message_cooldown= +config.Main.Advanced.bot_owners= +config.Main.Advanced.mc_version= +config.Main.Advanced.mc_forge= +config.Main.Advanced.brand_info= +config.Main.Advanced.chatbot_log_file= +config.Main.Advanced.private_msgs_cmd_name= +config.Main.Advanced.show_system_messages= +config.Main.Advanced.show_xpbar_messages= +config.Main.Advanced.show_chat_links= +config.Main.Advanced.show_inventory_layout= +config.Main.Advanced.terrain_and_movements= +config.Main.Advanced.inventory_handling= +config.Main.Advanced.entity_handling= +config.Main.Advanced.session_cache= +config.Main.Advanced.profilekey_cache= +config.Main.Advanced.resolve_srv_records= +config.Main.Advanced.account_list= +config.Main.Advanced.server_list= +config.Main.Advanced.player_head_icon= +config.Main.Advanced.exit_on_failure= +config.Main.Advanced.script_cache= +config.Main.Advanced.timestamps= +config.Main.Advanced.auto_respawn= +config.Main.Advanced.minecraft_realms= +config.Main.Advanced.move_head_while_walking= +config.Main.Advanced.timeout= +config.Main.Advanced.enable_emoji= +config.Main.Advanced.movement_speed= +config.Main.Advanced.language.invaild= + +# Signature +config.Signature= +config.Signature.LoginWithSecureProfile= +config.Signature.SignChat= +config.Signature.SignMessageInCommand= +config.Signature.MarkLegallySignedMsg= +config.Signature.MarkModifiedMsg= +config.Signature.MarkIllegallySignedMsg= +config.Signature.MarkSystemMessage= +config.Signature.ShowModifiedChat= +config.Signature.ShowIllegalSignedChat= + +# Logging +config.Logging= +config.Logging.DebugMessages= +config.Logging.ChatMessages= +config.Logging.InfoMessages= +config.Logging.WarningMessages= +config.Logging.ErrorMessages= +config.Logging.ChatFilter= +config.Logging.DebugFilter= +config.Logging.FilterMode= +config.Logging.LogToFile= +config.Logging.LogFile= +config.Logging.PrependTimestamp= +config.Logging.SaveColorCodes= + +# AppVars +config.AppVars.Variables= + +# Proxy +config.Proxy= +config.Proxy.Enabled_Login= +config.Proxy.Enabled_Ingame= +config.Proxy.Server= +config.Proxy.Proxy_Type= +config.Proxy.Username= +config.Proxy.Password= + +# ChatFormat +config.ChatFormat= +config.ChatFormat.Builtins= +config.ChatFormat.UserDefined= + +# MCSettings +config.MCSettings= +config.MCSettings.Enabled= +config.MCSettings.Locale= +config.MCSettings.RenderDistance= +config.MCSettings.Difficulty= +config.MCSettings.ChatMode= +config.MCSettings.ChatColors= +config.MCSettings.MainHand= + +# ChatBot +config.ChatBot= + +# ChatBot.Alerts +config.ChatBot.Alerts= +config.ChatBot.Alerts.Beep_Enabled= +config.ChatBot.Alerts.Trigger_By_Words= +config.ChatBot.Alerts.Trigger_By_Rain= +config.ChatBot.Alerts.Trigger_By_Thunderstorm= +config.ChatBot.Alerts.Matches= +config.ChatBot.Alerts.Excludes= +config.ChatBot.Alerts.Log_To_File= +config.ChatBot.Alerts.Log_File= + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk= +config.ChatBot.AntiAfk.Delay= +config.ChatBot.AntiAfk.Command= +config.ChatBot.AntiAfk.Use_Terrain_Handling= +config.ChatBot.AntiAfk.Walk_Range= +config.ChatBot.AntiAfk.Walk_Retries= + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack= +config.ChatBot.AutoAttack.Mode= +config.ChatBot.AutoAttack.Priority= +config.ChatBot.AutoAttack.Cooldown_Time= +config.ChatBot.AutoAttack.Interaction= +config.ChatBot.AutoAttack.Attack_Hostile= +config.ChatBot.AutoAttack.Attack_Passive= +config.ChatBot.AutoAttack.List_Mode= +config.ChatBot.AutoAttack.Entites_List= + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft= +config.ChatBot.AutoCraft.CraftingTable= +config.ChatBot.AutoCraft.OnFailure= +config.ChatBot.AutoCraft.Recipes= + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop= +config.ChatBot.AutoDrop.Mode= + +# ChatBot.AutoEat +config.ChatBot.AutoEat= + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing= +config.ChatBot.AutoFishing.Antidespawn= +config.ChatBot.AutoFishing.Mainhand= +config.ChatBot.AutoFishing.Auto_Start= +config.ChatBot.AutoFishing.Cast_Delay= +config.ChatBot.AutoFishing.Fishing_Delay= +config.ChatBot.AutoFishing.Fishing_Timeout= +config.ChatBot.AutoFishing.Durability_Limit= +config.ChatBot.AutoFishing.Auto_Rod_Switch= +config.ChatBot.AutoFishing.Stationary_Threshold= +config.ChatBot.AutoFishing.Hook_Threshold= +config.ChatBot.AutoFishing.Log_Fish_Bobber= +config.ChatBot.AutoFishing.Enable_Move= +config.ChatBot.AutoFishing.Movements= + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog= +config.ChatBot.AutoRelog.Delay= +config.ChatBot.AutoRelog.Retries= +config.ChatBot.AutoRelog.Ignore_Kick_Message= +config.ChatBot.AutoRelog.Kick_Messages= + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond= +config.ChatBot.AutoRespond.Match_Colors= + +# ChatBot.ChatLog +config.ChatBot.ChatLog= + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer= +config.ChatBot.FollowPlayer.Update_Limit= +config.ChatBot.FollowPlayer.Stop_At_Distance= + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame= + +# ChatBot.Mailer +config.ChatBot.Mailer= + +# ChatBot.Map +config.ChatBot.Map= +config.ChatBot.Map.Should_Resize= +config.ChatBot.Map.Resize_To= +config.ChatBot.Map.Auto_Render_On_Update= +config.ChatBot.Map.Delete_All_On_Unload= +config.ChatBot.Map.Notify_On_First_Update= + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger= +config.ChatBot.PlayerListLogger.Delay= + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl= + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture= +config.ChatBot.ReplayCapture.Backup_Interval= + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler= + diff --git a/MinecraftClient/Resources/lang/vi.ini b/MinecraftClient/Resources/lang/vi.ini index 23d88967..c84b9047 100644 --- a/MinecraftClient/Resources/lang/vi.ini +++ b/MinecraftClient/Resources/lang/vi.ini @@ -1,5 +1,9 @@ [mcc] # Messages from MCC itself +mcc.help_us_translate= +mcc.run_with_default_settings= +mcc.settings_generated= +mcc.has_update= 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: @@ -8,8 +12,11 @@ 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.profile_key_invalid= +mcc.profile_key_valid= mcc.connecting=Đang kết nối tới {0}... -mcc.ip=Địa chỉ máy chủ: +mcc.fetching_key= +mcc.ip=Địa chỉ máy chủ: mcc.use_version=§8Sử dụng Minecraft phiên bản {0} (protocol v{1}) mcc.unknown_version=§8Phiên bản không hợp lệ {0}.\nChuyển sang chế độ nhận diện tự động. mcc.forge=Đang kiểm tra xem máy chủ có dùng Forge... @@ -20,7 +27,7 @@ mcc.not_found=§8Tìm kiếm SRV thất bại cho {0}\n{1}: {2} mcc.retrieve=Đang nhận thông tin máy chủ... mcc.restart=Đang khởi động lại Minecraft Console Client... mcc.restart_delay=Đang chờ {0} giây trước khi khởi động lại... -mcc.server_version=Phiên bản máy chủ: +mcc.server_version=Phiên bản máy chủ: mcc.disconnected=Chưa kết nối tới máy chủ nào. Dùng '{0}help' để in ra hướng dẫn. mcc.press_exit=Hoặc nhấn Enter để đóng Minecraft Console Client mcc.version_supported=Phiên bản hợp lệ \nĐang đăng nhập @@ -41,13 +48,16 @@ mcc.with_forge=, với Forge mcc.handshake=§8Bắt tay thành công (ID máy chủ: {0}) mcc.realms_available=Bạn có quyền tham gia vào những máy chủ Realm này mcc.realms_join=Dùng realm:[thứ tự] để tham gia máy chủ Realm - +mcc.generator.generating= +mcc.generator.done= [debug] # Messages from MCC Debug Mode debug.color_test=Color test: Màn hình của bạn sẽ hiển thị {0} debug.session_cache_ok=§8Đã load thông tin phiên thành công debug.session_cache_fail=§8Không có phiên nào có thể load. +debug.keys_cache_ok= +debug.keys_cache_fail= debug.session_id=Thành công. (session ID: {0}) debug.crypto=§8Khóa mật mã và hash đã được tạo. debug.request=§8Đang gửi yêu cầu tới {0} @@ -67,7 +77,6 @@ error.login.premium=User not premium. error.login.network=Network error. error.login.ssl=SSL Error. error.login.unknown=Unknown Error. -error.login.ssl_help=§8It appears that you are using Mono to run this program.\nThe first time, you have to import HTTPS certificates using:\nmozroots --import --ask-remove error.login.cancel=User cancelled. error.login_failed=Failed to login to this server. error.join=Failed to join this server. @@ -81,10 +90,7 @@ error.no_version_report=§8Server does not report its protocol version, autodete error.connection_timeout=§8A timeout occured while attempting to connect to this IP. error.forge=§8Forge Login Handshake did not complete successfully error.forge_encrypt=§8Forge StartEncryption Handshake did not complete successfully -error.setting.str2int=Failed to convert '{0}' into an int. Please check your settings. error.setting.argument_syntax={0}: Invalid syntax, expecting --argname=value or --section.argname=value -error.setting.unknown_section={0}: Unknown setting section '{1}' -error.setting.unknown_or_invalid={0}: Unknown setting or invalid value error.http_code=§8Got error code from server: {0} error.auth=§8Got error code from server while refreshing authentication: {0} error.realms.ip_error=Cannot retrieve the server IP of your Realms world @@ -92,6 +98,11 @@ error.realms.access_denied=This Realms world does not exist or access was denied error.realms.server_unavailable=Realms server may require some time to start up. Please retry again later. error.realms.server_id=Invalid or unknown Realms server ID. error.realms.disabled=Trying to join a Realms world but Realms support is disabled in config +error.missing.argument= +error.usage= +error.generator.invalid= +error.generator.path= +error.generator.json= [internal command] # MCC internal help command @@ -173,6 +184,14 @@ cache.ignore_line=§8Ignoring invalid session token line: {0} cache.read_fail_plain=§8Failed to read session cache from disk: {0} cache.saving=§8Saving session cache to disk cache.save_fail=§8Failed to write session cache to disk: {0} +# Profile Key Cache +cache.loading_keys= +cache.loaded_keys= +cache.ignore_string_keys= +cache.ignore_line_keys= +cache.read_fail_plain_keys= +cache.saving_keys= +cache.save_fail_keys= [proxy] proxy.connected=§8Connected to proxy {0}:{1} @@ -186,6 +205,7 @@ chat.fail=§8Failed to download the file. chat.from_dir=§8Defaulting to en_GB.lang from your Minecraft directory. chat.loaded=§8Translations file loaded. chat.not_found=§8Translations file not found: '{0}'\nSome messages won't be properly printed without this file. +chat.message_chain_broken= [general] # General message/information (i.e. Done) @@ -201,12 +221,23 @@ general.available_cmd=Available commands: {0} # Animation cmd.animation.desc=Swing your arm. +# Bots +cmd.bots.desc= +cmd.bots.list= +cmd.bots.notfound= +cmd.bots.noloaded= +cmd.bots.unloaded= +cmd.bots.unloaded_all= + # ChangeSlot cmd.changeSlot.desc=Change hotbar cmd.changeSlot.nan=Could not change slot: Not a Number cmd.changeSlot.changed=Changed to slot {0} cmd.changeSlot.fail=Could not change slot +# Chunk +cmd.chunk.desc= + # Connect cmd.connect.desc=connect to the specified server. cmd.connect.unknown=Unknown account '{0}'. @@ -248,6 +279,19 @@ cmd.entityCmd.distance=Distance cmd.entityCmd.location=Location cmd.entityCmd.type=Type +# Exec If +cmd.execif.desc= +cmd.execif.executed= +cmd.execif.executed_no_output= +cmd.execif.error_occured= +cmd.execif.error= + +# Exec Multi +cmd.execmulti.desc= +cmd.execmulti.executed= +cmd.execmulti.result= +cmd.execmulti.no_result= + # Exit cmd.exit.desc=disconnect from the server. @@ -267,12 +311,15 @@ cmd.inventory.close_fail=Failed to close Inventory #{0} cmd.inventory.not_exist=Inventory #{0} do not exist cmd.inventory.inventory=Inventory cmd.inventory.inventories=Inventories +cmd.inventory.inventories_available= cmd.inventory.hotbar=Your selected hotbar is {0} cmd.inventory.damage=Damage cmd.inventory.left=Left cmd.inventory.right=Right cmd.inventory.middle=Middle cmd.inventory.clicking={0} clicking slot {1} in window #{2} +cmd.inventory.shiftclick= +cmd.inventory.shiftclick_fail= cmd.inventory.no_item=No item in slot #{0} cmd.inventory.drop=Dropped 1 item from slot #{0} cmd.inventory.drop_stack=Dropped whole item stack from slot #{0} @@ -284,10 +331,29 @@ cmd.inventory.help.usage=Usage cmd.inventory.help.list=List your inventory. cmd.inventory.help.close=Close an opened container. cmd.inventory.help.click=Click on an item. +cmd.inventory.help.shiftclick= cmd.inventory.help.drop=Drop an item from inventory. cmd.inventory.help.creativegive=Give item in creative mode. cmd.inventory.help.creativedelete=Clear slot in creative mode. -cmd.inventory.help.unknown=Unknown action. +cmd.inventory.help.inventories= +cmd.inventory.help.search= +cmd.inventory.help.unknown=Unknown action. +cmd.inventory.found_items= +cmd.inventory.no_found_items= + +# Leave bed +cmd.bed.desc= +cmd.bed.leaving= +cmd.bed.trying_to_use= +cmd.bed.in= +cmd.bed.not_in= +cmd.bed.not_a_bed= +cmd.bed.searching= +cmd.bed.bed_not_found= +cmd.bed.found_a_bed_at= +cmd.bed.cant_reach_safely= +cmd.bed.moving= +cmd.bed.failed_to_reach_in_time= # List cmd.list.desc=get the player list. @@ -301,6 +367,8 @@ cmd.look.desc=look at direction or coordinates. cmd.look.unknown=Unknown direction '{0}' cmd.look.at=Looking at YAW: {0} PITCH: {1} cmd.look.block=Looking at {0} +cmd.look.inspection= +cmd.look.noinspection= # Move cmd.move.desc=walk or start walking. @@ -310,10 +378,24 @@ cmd.move.moving=Moving {0} cmd.move.dir_fail=Cannot move in that direction. cmd.move.walk=Walking to {0} cmd.move.fail=Failed to compute path to {0} +cmd.move.suggestforce= +cmd.move.gravity.enabled= +cmd.move.gravity.disabled= +cmd.move.chunk_loading_status= +cmd.move.chunk_not_loaded= # Reco cmd.reco.desc=restart and reconnect to the server. +# Reload +cmd.reload.started= +cmd.reload.warning1= +cmd.reload.warning2= +cmd.reload.warning3= +cmd.reload.warning4= +cmd.reload.finished= +cmd.reload.desc= + # Respawn cmd.respawn.desc=Use this to respawn if you are dead. cmd.respawn.done=You have respawned. @@ -328,6 +410,11 @@ cmd.send.desc=send a chat message or command. cmd.set.desc=set a custom %variable%. cmd.set.format=variable name must be A-Za-z0-9. +# SetRnd +cmd.setrnd.desc= +cmd.setrndnum.format= +cmd.setrndstr.format= + # Sneak cmd.sneak.desc=Toggles sneaking cmd.sneak.on=You are sneaking now @@ -344,6 +431,7 @@ cmd.tps.current=Current tps # Useblock cmd.useblock.desc=Place a block or open chest +cmd.useblock.use= # Useitem cmd.useitem.desc=Use (left click) an item on the hand @@ -353,9 +441,18 @@ cmd.useitem.use=Used an item [bot] # ChatBots. Naming style: bot.. +# Alerts +bot.alerts.start_rain= +bot.alerts.end_rain= +bot.alerts.start_thunderstorm= +bot.alerts.end_thunderstorm= + +# Anti AFK +bot.antiafk.not_using_terrain_handling= +bot.antiafk.swapping= +bot.antiafk.invalid_walk_range= + # AutoAttack -bot.autoAttack.mode=Unknown attack mode: {0}. Using single mode as default. -bot.autoAttack.priority=Unknown priority: {0}. Using distance priority as default. bot.autoAttack.invalidcooldown=Attack cooldown value cannot be smaller than 0. Using auto as default # AutoCraft @@ -384,15 +481,11 @@ bot.autoCraft.aborted=Crafting aborted! Check your available materials. bot.autoCraft.craft_fail=Crafting failed! Waiting for more materials bot.autoCraft.timeout=Action timeout! Reason: {0} bot.autoCraft.error.config=Error while parsing config: {0} -bot.autoCraft.exception.empty=Empty configuration file: {0} -bot.autoCraft.exception.invalid=Invalid configuration file: {0} -bot.autoCraft.exception.item_miss=Missing item in recipe: {0} -bot.autoCraft.exception.invalid_table=Invalid tablelocation format: {0} -bot.autoCraft.exception.item_name=Invalid item name in recipe {0} at {1} bot.autoCraft.exception.name_miss=Missing recipe name while parsing a recipe -bot.autoCraft.exception.slot=Invalid slot field in recipe: {0} bot.autoCraft.exception.duplicate=Duplicate recipe name specified: {0} bot.autoCraft.debug.no_config=No config found. Writing a new one. +bot.autocraft.invaild_slots= +bot.autocraft.invaild_invaild_result= # AutoDrop bot.autoDrop.cmd=AutoDrop ChatBot command @@ -411,9 +504,17 @@ bot.autoDrop.no_mode=Cannot read drop mode from config. Using include mode. bot.autoDrop.no_inventory=Cannot find inventory {0}! # AutoFish +bot.autoFish.no_inv_handle= +bot.autoFish.start= bot.autoFish.throw=Threw a fishing rod bot.autoFish.caught=Caught a fish! +bot.autoFish.caught_at= bot.autoFish.no_rod=No Fishing Rod on hand. Maybe broken? +bot.autoFish.despawn= +bot.autoFish.fishing_timeout= +bot.autoFish.cast_timeout= +bot.autoFish.update_lookat= +bot.autoFish.switch= # AutoRelog bot.autoRelog.launch=Launching with {0} reconnection attempts @@ -457,7 +558,7 @@ bot.mailer.init_fail.mail_retention=Cannot enable Mailer: Mail Retention must be bot.mailer.create.db=Creating new database file: {0} bot.mailer.create.ignore=Creating new ignore list: {0} bot.mailer.load.db=Loading database file: {0} -bot.mailer.load.ignore=Loading ignore list: +bot.mailer.load.ignore=Loading ignore list: bot.mailer.cmd=mailer command @@ -473,12 +574,43 @@ bot.mailer.cmd.ignore.removed=Removed {0} from the ignore list! bot.mailer.cmd.ignore.invalid=Missing or invalid name. Usage: {0} bot.mailer.cmd.help=See usage +# Maps +bot.map.cmd.desc= +bot.map.cmd.not_found= +bot.map.cmd.invalid_id= +bot.map.received= +bot.map.no_maps= +bot.map.received_map= +bot.map.rendered= +bot.map.failed_to_render= +bot.map.list_item= + # ReplayCapture bot.replayCapture.cmd=replay command bot.replayCapture.created=Replay file created. bot.replayCapture.stopped=Record stopped. bot.replayCapture.restart=Record was stopped. Restart the program to start another record. +# Follow player +cmd.follow.desc= +cmd.follow.usage= +cmd.follow.already_stopped= +cmd.follow.stopping= +cmd.follow.invalid_name= +cmd.follow.invalid_player= +cmd.follow.cant_reach_player= +cmd.follow.already_following= +cmd.follow.switched= +cmd.follow.started= +cmd.follow.unsafe_enabled= +cmd.follow.note= +cmd.follow.player_came_to_the_range= +cmd.follow.resuming= +cmd.follow.player_left_the_range= +cmd.follow.pausing= +cmd.follow.player_left= +cmd.follow.stopping= + # Script bot.script.not_found=§8[MCC] [{0}] Cannot find script file: {1} bot.script.file_not_found=File not found: '{0}' @@ -486,8 +618,6 @@ bot.script.fail=Script '{0}' failed to run ({1}). bot.script.pm.loaded=Script '{0}' loaded. # ScriptScheduler -bot.scriptScheduler.loading=Loading tasks from '{0}' -bot.scriptScheduler.not_found=File not found: '{0}' bot.scriptScheduler.loaded_task=Loaded task:\n{0} bot.scriptScheduler.no_trigger=This task will never trigger:\n{0} bot.scriptScheduler.no_action=No action for task:\n{0} @@ -499,3 +629,220 @@ 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= +config.load.fail= +config.write.fail= +config.backup.fail= +config.saving= + +# Head +config.Head= + +# Main.General +config.Main.General.account= +config.Main.General.login= +config.Main.General.server_info= +config.Main.General.method= + +# Main.Advanced +config.Main.Advanced= +config.Main.Advanced.language= +config.Main.Advanced.internal_cmd_char= +config.Main.Advanced.message_cooldown= +config.Main.Advanced.bot_owners= +config.Main.Advanced.mc_version= +config.Main.Advanced.mc_forge= +config.Main.Advanced.brand_info= +config.Main.Advanced.chatbot_log_file= +config.Main.Advanced.private_msgs_cmd_name= +config.Main.Advanced.show_system_messages= +config.Main.Advanced.show_xpbar_messages= +config.Main.Advanced.show_chat_links= +config.Main.Advanced.show_inventory_layout= +config.Main.Advanced.terrain_and_movements= +config.Main.Advanced.inventory_handling= +config.Main.Advanced.entity_handling= +config.Main.Advanced.session_cache= +config.Main.Advanced.profilekey_cache= +config.Main.Advanced.resolve_srv_records= +config.Main.Advanced.account_list= +config.Main.Advanced.server_list= +config.Main.Advanced.player_head_icon= +config.Main.Advanced.exit_on_failure= +config.Main.Advanced.script_cache= +config.Main.Advanced.timestamps= +config.Main.Advanced.auto_respawn= +config.Main.Advanced.minecraft_realms= +config.Main.Advanced.move_head_while_walking= +config.Main.Advanced.timeout= +config.Main.Advanced.enable_emoji= +config.Main.Advanced.movement_speed= +config.Main.Advanced.language.invaild= + +# Signature +config.Signature= +config.Signature.LoginWithSecureProfile= +config.Signature.SignChat= +config.Signature.SignMessageInCommand= +config.Signature.MarkLegallySignedMsg= +config.Signature.MarkModifiedMsg= +config.Signature.MarkIllegallySignedMsg= +config.Signature.MarkSystemMessage= +config.Signature.ShowModifiedChat= +config.Signature.ShowIllegalSignedChat= + +# Logging +config.Logging= +config.Logging.DebugMessages= +config.Logging.ChatMessages= +config.Logging.InfoMessages= +config.Logging.WarningMessages= +config.Logging.ErrorMessages= +config.Logging.ChatFilter= +config.Logging.DebugFilter= +config.Logging.FilterMode= +config.Logging.LogToFile= +config.Logging.LogFile= +config.Logging.PrependTimestamp= +config.Logging.SaveColorCodes= + +# AppVars +config.AppVars.Variables= + +# Proxy +config.Proxy= +config.Proxy.Enabled_Login= +config.Proxy.Enabled_Ingame= +config.Proxy.Server= +config.Proxy.Proxy_Type= +config.Proxy.Username= +config.Proxy.Password= + +# ChatFormat +config.ChatFormat= +config.ChatFormat.Builtins= +config.ChatFormat.UserDefined= + +# MCSettings +config.MCSettings= +config.MCSettings.Enabled= +config.MCSettings.Locale= +config.MCSettings.RenderDistance= +config.MCSettings.Difficulty= +config.MCSettings.ChatMode= +config.MCSettings.ChatColors= +config.MCSettings.MainHand= + +# ChatBot +config.ChatBot= + +# ChatBot.Alerts +config.ChatBot.Alerts= +config.ChatBot.Alerts.Beep_Enabled= +config.ChatBot.Alerts.Trigger_By_Words= +config.ChatBot.Alerts.Trigger_By_Rain= +config.ChatBot.Alerts.Trigger_By_Thunderstorm= +config.ChatBot.Alerts.Matches= +config.ChatBot.Alerts.Excludes= +config.ChatBot.Alerts.Log_To_File= +config.ChatBot.Alerts.Log_File= + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk= +config.ChatBot.AntiAfk.Delay= +config.ChatBot.AntiAfk.Command= +config.ChatBot.AntiAfk.Use_Terrain_Handling= +config.ChatBot.AntiAfk.Walk_Range= +config.ChatBot.AntiAfk.Walk_Retries= + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack= +config.ChatBot.AutoAttack.Mode= +config.ChatBot.AutoAttack.Priority= +config.ChatBot.AutoAttack.Cooldown_Time= +config.ChatBot.AutoAttack.Interaction= +config.ChatBot.AutoAttack.Attack_Hostile= +config.ChatBot.AutoAttack.Attack_Passive= +config.ChatBot.AutoAttack.List_Mode= +config.ChatBot.AutoAttack.Entites_List= + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft= +config.ChatBot.AutoCraft.CraftingTable= +config.ChatBot.AutoCraft.OnFailure= +config.ChatBot.AutoCraft.Recipes= + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop= +config.ChatBot.AutoDrop.Mode= + +# ChatBot.AutoEat +config.ChatBot.AutoEat= + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing= +config.ChatBot.AutoFishing.Antidespawn= +config.ChatBot.AutoFishing.Mainhand= +config.ChatBot.AutoFishing.Auto_Start= +config.ChatBot.AutoFishing.Cast_Delay= +config.ChatBot.AutoFishing.Fishing_Delay= +config.ChatBot.AutoFishing.Fishing_Timeout= +config.ChatBot.AutoFishing.Durability_Limit= +config.ChatBot.AutoFishing.Auto_Rod_Switch= +config.ChatBot.AutoFishing.Stationary_Threshold= +config.ChatBot.AutoFishing.Hook_Threshold= +config.ChatBot.AutoFishing.Log_Fish_Bobber= +config.ChatBot.AutoFishing.Enable_Move= +config.ChatBot.AutoFishing.Movements= + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog= +config.ChatBot.AutoRelog.Delay= +config.ChatBot.AutoRelog.Retries= +config.ChatBot.AutoRelog.Ignore_Kick_Message= +config.ChatBot.AutoRelog.Kick_Messages= + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond= +config.ChatBot.AutoRespond.Match_Colors= + +# ChatBot.ChatLog +config.ChatBot.ChatLog= + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer= +config.ChatBot.FollowPlayer.Update_Limit= +config.ChatBot.FollowPlayer.Stop_At_Distance= + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame= + +# ChatBot.Mailer +config.ChatBot.Mailer= + +# ChatBot.Map +config.ChatBot.Map= +config.ChatBot.Map.Should_Resize= +config.ChatBot.Map.Resize_To= +config.ChatBot.Map.Auto_Render_On_Update= +config.ChatBot.Map.Delete_All_On_Unload= +config.ChatBot.Map.Notify_On_First_Update= + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger= +config.ChatBot.PlayerListLogger.Delay= + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl= + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture= +config.ChatBot.ReplayCapture.Backup_Interval= + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler= + diff --git a/MinecraftClient/Resources/lang/zh-CHS.ini b/MinecraftClient/Resources/lang/zh-CHS.ini deleted file mode 100644 index 24417e4f..00000000 --- a/MinecraftClient/Resources/lang/zh-CHS.ini +++ /dev/null @@ -1,530 +0,0 @@ -[mcc] -# Messages from MCC itself -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}的聊天签名密钥缓存仍然有效. -mcc.connecting=正在连接至{0}... -mcc.ip=服务器IP: -mcc.use_version=§8正在运行Minecraft版本{0} (v{1}协议) -mcc.unknown_version=§8未知或不受支持的Minecraft版本{0}。\n正在切换至自动检测模式。 -mcc.forge=检查服务器是否正在运行Forge... -mcc.forgeforce=正在强制启动Forge支持。 -mcc.resolve=正在解析{0}... -mcc.found=§8已找到服务器{0}:{1},域名:{2} -mcc.not_found=§8无法执行{0}的SRV解析\n{1}:{2} -mcc.retrieve=正在获取服务器信息... -mcc.restart=正在重启Minecraft Console Client... -mcc.restart_delay=等待{0}秒后重启... -mcc.server_version=服务器版本: -mcc.disconnected=未连接至任何服务器。输入 '{0}help' 获得帮助。 -mcc.press_exit=或者敲击回车退出Minecraft Console Client。 -mcc.version_supported=该版本受到支持\n正在登录... -mcc.single_cmd=§7已发送命令§8 {0} -mcc.joined=已成功加入服务器。\n输入 '{0}quit' 离开服务器。 -mcc.reconnect=等待5秒 (剩余{0}次尝试)... -mcc.disconnect.lost=失去连接。 -mcc.disconnect.server=从服务器断开: -mcc.disconnect.login=连接失败: -mcc.link=链接:{0} -mcc.player_dead_respawn=你死了!1秒后自动重生。 -mcc.player_dead=你死了!输入 '{0}respawn' 重生。 -mcc.server_offline=§8服务器正处于离线模式。 -mcc.session=检查会话... -mcc.session_fail=检查会话失败 -mcc.server_protocol=§8服务器版本:{0} (v{1}协议) -mcc.with_forge=, 带有Forge -mcc.handshake=§8握手成功。 (服务器ID:{0}) -mcc.realms_available==您可以访问以下Realms世界 -mcc.realms_join=请使用realms:<序号>作为服务器IP加入Realms世界 - - -[debug] -# Messages from MCC Debug Mode -debug.color_test=颜色测试:终端应该显示的颜色是:{0} -debug.session_cache_ok=§8已成功从磁盘中加载会话数据。 -debug.session_cache_fail=§8无法从磁盘加载会话 -debug.keys_cache_ok=§8已成功从磁盘中加载聊天签名密钥。 -debug.keys_cache_fail=§8无法从磁盘中加载聊天签名密钥。 -debug.session_id=成功!(会话ID:{0}) -debug.crypto=§8密钥和哈希值已生成: -debug.request=§8正在请求{0} - -[error] -# Error messages from MCC -error.ping=ping此IP失败。 -error.unsupported=无法连接到服务器:不支持此版本! -error.determine=无法确定服务器版本。 -error.forgeforce=无法为此Minecraft版本强制启动Forge支持! -error.login=登录失败: -error.login.migrated=帐户已迁移,使用电子邮件作为用户名。 -error.login.server=登录服务器不可用。请稍后再试。 -error.login.blocked=密码错误、IP被禁用或登录次数过多。 -error.login.response=服务器响应无效。 -error.login.premium=不是Premium用户。 -error.login.network=网络错误。 -error.login.ssl=SSL错误。 -error.login.unknown=未知错误。 -error.login.ssl_help=§8您似乎正在使用Mono运行此程序.\n当第一次运行,您必须使用以下命令导入HTTPS证书:\nmozroots --import --ask-remove -error.login.cancel=用户取消。 -error.login_failed=无法登录到此服务器。 -error.join=加入服务器时发生错误。 -error.connect=无法连接到此IP。 -error.timeout=连接超时 -error.unexpect_response=§8来自服务器的意外响应(这是Minecraft服务器吗?) -error.invalid_response=§8对握手包的响应无效 -error.invalid_encrypt=§8对StartEncryption数据包的响应无效 -error.version_different=§8服务器报告的版本与手动设置的版本不同。登录可能无法工作。 -error.no_version_report=§8服务器未报告其协议版本,自动检测将不起作用。 -error.connection_timeout=§8尝试连接到此IP时超时。 -error.forge=§8Forge登录握手未成功完成 -error.forge_encrypt=§8Forge StartEncryption握手未成功完成 -error.setting.str2int=无法将'{0}'转换为一个整数。请检查设置。 -error.setting.str2double=无法将'{0}'转换为一个浮点数。请检查设置。 -error.setting.argument_syntax={0}:无效语法,应为 --argname=value 或 --section.argname=value -error.setting.unknown_section={0}:未知设置部分'{1}' -error.setting.unknown_or_invalid={0}:未知设置或无效值 -error.http_code=§8接收到服务器错误:{0} -error.auth=§8在刷新身份验证时接收到服务器错误:{0} -error.realms.ip_error=无法获取您Realms世界的服务器IP -error.realms.access_denied=此Realms世界不存在或访问被拒绝 -error.realms.server_unavailable=Realms服务器可能需要一些时间来启动。请稍后再试。 -error.realms.server_id=Realms服务器ID无效或未知。 -error.realms.disabled=正在尝试加入Realms世界,但配置中禁用了Realms支持 - -[internal command] -# MCC internal help command -icmd.help=help <命令名称> :显示有关命令的简要帮助。 -icmd.unknown=未知命令 '{0}'。请使用 'help' 命令来获取命令列表。 -icmd.list=help <命令名称>。可用命令:{0}。在服务器上获取帮助,请改用 '{1}send /help'。 -icmd.error=OnInternalCommand: 来自{0}的错误{1} - -[exception] -# Exception messages threw by MCC -exception.user_logout=用户发起的注销应该通过调用Disconnect()来完成 -exception.unknown_direction=未知方向 -exception.palette.block=请为此Minecraft版本更新方块类型处理。详细请参考 Material.cs -exception.palette.entity=请为此Minecraft版本更新实体类型处理。详细请参考 EntityType.cs -exception.palette.item=请为此Minecraft版本更新物品类型处理。详细请参考 ItemType.cs -exception.palette.packet=请为此Minecraft版本更新数据包类型调色板。详细请参考 PacketTypePalette.cs -exception.packet_process=无法处理传入的{0}类型的数据包。(数据包ID:{1},协议:{2},登陆阶段:{3},内部异常:{4})。 -exception.version_unsupport=版本{0}的协议未被支持。 -exception.chatbot.init=不应在构造函数中调用ChatBot的方法,因为作为API处理程序的模块尚未初始化。请重写 Initialize() 或 AfterGameJoined() 来执行初始化任务。 -exception.csrunner.invalid_head=提供的脚本没有有效的MCCScript头 - -[chatbot] -# ChatBot API -chatbot.reconnect=[{0}] 断开并重新连接到服务器 - -[filemonitor] -# FileMonitor -filemonitor.init=§8[{0}] 正在为文件{1}初始化FileSystemWatcher -filemonitor.fail=§8[{0}] 无法初始化FileSystemWatcher,正在使用轮询重试 - -[extra] -# Inventory, Terrain & Movements, Entity related messages -# Terrain & Movements -extra.terrainandmovement_enabled=地形和移动现在已启用。 -extra.terrainandmovement_disabled=§c该MC版本目前未支持处理地形和移动。 -extra.terrainandmovement_required=请先在配置文件中启用地形和移动。 -# Inventory -extra.inventory_enabled=物品栏处理现在已启用。 -extra.inventory_disabled=§c该MC版本目前未支持处理物品栏。 -extra.inventory_required=请先在配置文件中启用InventoryHandling。 -extra.inventory_interact=请使用 /inventory 来与其交互。 -extra.inventory_open=物品栏# {0}已开启:{1} -extra.inventory_close=物品栏# {0}已关闭。 -# Entity -extra.entity_disabled=§c该MC版本当前未支持处理实体。 -extra.entity_required=请先在配置文件中启用EntityHandling。 - - -[forge] -# Messages from Forge handler -forge.version=§8Forge协议版本:{0} -forge.send_mod=§8向服务器发送伪造的forge模组列表... -forge.accept=§8接受来自的服务器模组列表... -forge.registry=§8已接收的注册表包含{0}个条目 -forge.registry_2=§8已接收注册表{0},包含{1}个条目 -forge.accept_registry=§8接受服务器注册表... -forge.complete=Forge服务器连接完成! -forge.with_mod=§8服务器正在运行Forge,有{0}个模组。 -forge.no_mod=§8正在运行的服务器没有Forge模组。 -forge.mod_list=§8模组列表: -# FML2 -forge.fml2.mod=§8收到FM2服务器模组列表 -forge.fml2.mod_send=§8发回FML2客户端的模组列表 -forge.fml2.registry=§8确认FML2服务器注册表:{0} -forge.fml2.config=§8确认FML2服务器配置:{0} -forge.fml2.unknown=§8收到未知的FML2握手信息,编号:{0} -forge.fml2.unknown_channel=§8忽略未知的FML2登录消息通道:{0} - -[cache] -# Session Cache -cache.loading=§8加载Minecraft配置文件:{0} -cache.loaded=§8已加载会话:{0}:{1} -cache.converting=§8从磁盘转换会话缓存:{0} -cache.read_fail=§8无法从磁盘读取序列化会话缓存:{0} -cache.malformed=§8从磁盘读取序列化会话缓存时,获取到格式错误的数据:{0} -cache.loading_session=§8从磁盘加载会话缓存:{0} -cache.ignore_string=§8忽略会话令牌字符串'{0}':{1} -cache.ignore_line=§8忽略无效的会话令牌行:{0} -cache.read_fail_plain=§8无法从磁盘读取会话缓存:{0} -cache.saving=§8将会话缓存保存到磁盘 -cache.save_fail=§8无法将会话缓存写入磁盘:{0} -# Profile Key Cache -cache.loading_keys=§8从磁盘加载聊天签名密钥缓存: {0} -cache.loaded_keys=§8已加载签名密钥,下次刷新于 {0} -cache.ignore_string_keys=§8忽略聊天签名密钥字符串 '{0}':{1} -cache.ignore_line_keys=§8忽略无效的聊天签名密钥行:{0} -cache.read_fail_plain_keys=§8无法从磁盘读取聊天签名密钥缓存:{0} -cache.saving_keys=§8将聊天签名密钥保存到磁盘 -cache.save_fail_keys=§8无法将聊天签名密钥写入磁盘:{0} - -[proxy] -proxy.connected=§8已连接到代理{0}:{1} - -[chat] -# Chat Parser -chat.download=§8从Mojang服务器下载 '{0}.lang'... -chat.request=§8正在请求{0}... -chat.done=§8完成。文件另存为 '{0}' -chat.fail=§8下载文件失败。 -chat.from_dir=§8默认为你的Minecraft目录中的en_GB.lang -chat.loaded=§8已加载翻译文件。 -chat.not_found=§8找不到翻译文件:'{0}'\n如果没有此文件,某些信息将无法正确打印。 - -[general] -# General message/information (i.e. Done) -general.done=完成 -general.fail=失败 -general.bot_unload=将会卸载此bot。 -general.available_cmd=可用命令:{0} - - -[cmd] -# Commands. Naming style: cmd.. - -# Animation -cmd.animation.desc=挥动你的手臂。 - -# ChangeSlot -cmd.changeSlot.desc=变更快捷栏栏位 -cmd.changeSlot.nan=无法变更栏位:不是数字 -cmd.changeSlot.changed=已变更为栏位{0} -cmd.changeSlot.fail=无法变更栏位 - -# Connect -cmd.connect.desc=连接到指定的服务器。 -cmd.connect.unknown=未知帐户 '{0}'。 -cmd.connect.invalid_ip=无效的服务器IP '{0}'。 - -# Debug -cmd.debug.desc=切换调试消息。 -cmd.debug.state_on=调试消息现在已打开 -cmd.debug.state_off=调试消息现在已关闭 - -# Dig -cmd.dig.desc=试图破坏一个方块 -cmd.dig.too_far=你离这个方块太远了。 -cmd.dig.no_block=这个地方没有方块 (空气) -cmd.dig.dig=尝试挖掘位于({0}, {1}, {2})的方块({3})。 -cmd.dig.fail=无法开始挖掘方块。 - -# Entitycmd -cmd.entityCmd.attacked=已攻击实体 -cmd.entityCmd.used=已使用实体 -cmd.entityCmd.not_found=找不到实体 - -cmd.entityCmd.entity=实体 -cmd.entityCmd.entities=实体集 -cmd.entityCmd.nickname=昵称 -cmd.entityCmd.customname=自定义名称 -cmd.entityCmd.latency=延迟 -cmd.entityCmd.item=物品 -cmd.entityCmd.equipment=装备 -cmd.entityCmd.mainhand=主手 -cmd.entityCmd.offhane=副手 -cmd.entityCmd.helmet=头盔 -cmd.entityCmd.chestplate=胸甲 -cmd.entityCmd.leggings=护腿 -cmd.entityCmd.boots=靴子 -cmd.entityCmd.pose=姿势 -cmd.entityCmd.health=生命值 -cmd.entityCmd.distance=距离 -cmd.entityCmd.location=位置 -cmd.entityCmd.type=类型 - -# Exit -cmd.exit.desc=断开与服务器的连接。 - -# Health -cmd.health.desc=显示生命值和饱食度。 -cmd.health.response=生命值:{0},饱食度:{1},等级:{2},总经验值:{3} - -# Inventory -cmd.inventory.desc=物品栏相关命令 -cmd.inventory.creative_done=向栏位#{2}请求{0} x{1} -cmd.inventory.creative_delete=请求清除栏位 #{0} -cmd.inventory.creative_fail=请求创造模式操作失败 -cmd.inventory.need_creative=你必须在创造模式 -cmd.inventory.container_not_found=找不到容器,请使用显式ID重试 -cmd.inventory.close=关闭物品栏 #{0} -cmd.inventory.close_fail=关闭物品栏失败 #{0} -cmd.inventory.not_exist=物品栏#{0}不存在 -cmd.inventory.inventory=物品栏 -cmd.inventory.inventories=物品栏集 -cmd.inventory.hotbar=您选择的快捷栏是{0} -cmd.inventory.damage=武器伤害值 -cmd.inventory.left=左 -cmd.inventory.right=右 -cmd.inventory.middle=中间 -cmd.inventory.clicking={0}正在点击窗口#{2}中的栏位{1} -cmd.inventory.no_item=栏位#{0}中没有物品 -cmd.inventory.drop=从栏位#{0}中丢弃了1个物品 -cmd.inventory.drop_stack=从栏位#{0}中丢弃了所有堆叠的物品 -# Inventory Help -cmd.inventory.help.basic=基本用法 -cmd.inventory.help.available=可用操作 -cmd.inventory.help.help={0}\n使用 '/inventory help ' 获取帮助。\n'player' 和 'container' 可以简化为 'p' 和 'c'。\n请注意,'[]' 中的参数是可选的。 -cmd.inventory.help.usage=用法 -cmd.inventory.help.list=列出你的物品栏。 -cmd.inventory.help.close=关闭打开的容器。 -cmd.inventory.help.click=单击物品。 -cmd.inventory.help.drop=从物品栏中丢弃物品。 -cmd.inventory.help.creativegive=在创造模式中给予物品。 -cmd.inventory.help.creativedelete=在创造性模式中清除栏位。 -cmd.inventory.help.unknown=未知操作。 - -# List 列表设置 -cmd.list.desc=获取玩家列表。 -cmd.list.players=玩家列表:{0} - -# Log -cmd.log.desc=将文本记录到控制台。 - -# Look -cmd.look.desc=查看方向或坐标。 -cmd.look.unknown=未知方向 '{0}'。 -cmd.look.at=当前视角 偏航角:{0} 俯仰角:{1}。 -cmd.look.block=正看向位于 {0} 的方块。 -cmd.look.inspection=与视线相交的第一个方块是 {0} ({1:0}, {2:0}, {3:0}),距离玩家 {4:0.0}({5:0.0})。 -cmd.look.noinspection=在 {0} 米内没有任何方块与视线相交。 - -# Move -cmd.move.desc=移动或开始移动。 -cmd.move.enable=在下次服务器登录、重生或更换世界时启用地形和移动。 -cmd.move.disable=禁用地形和移动。 -cmd.move.moving=移动{0} -cmd.move.dir_fail=不能朝此方向移动。 -cmd.move.walk=移动到{0} -cmd.move.fail=无法计算到达{0}的路径。 -cmd.move.suggestforce=无法计算安全到达{0}的路径. 请使用 -f 参数允许不安全移动。 -cmd.move.gravity.enabled=重力已开启。 -cmd.move.gravity.disabled=重力已关闭。 - -# Reco -cmd.reco.desc=重新启动并重新连接到服务器。 - -# Respawn -cmd.respawn.desc=如果你死亡了,请用这个来重生。 -cmd.respawn.done=你重生了。 - -# Script -cmd.script.desc=运行脚本文件。 - -# Send -cmd.send.desc=发送聊天信息或命令。 - -# Set -cmd.set.desc=设置自定义 %variable%。 -cmd.set.format=变量名范围必须为 A-Za-z0-9。 - -# SetRnd -cmd.setrnd.desc=随机为自定义 %变量名% 赋值。 -cmd.setrndnum.format=setrnd 变量名 -7to17 -cmd.setrndstr.format=setrnd 变量名 字符串1 "\"字符串2\" 字符串3" - -# Sneak -cmd.sneak.desc=切换到潜行 -cmd.sneak.on=现在你在潜行状态。 -cmd.sneak.off=你不在潜行状态了。 - -# DropItem -cmd.dropItem.desc=丢弃玩家物品栏中的指定类型物品或打开的容器 -cmd.dropItem.dropped=已从物品栏#{1}中丢弃所有{0} -cmd.dropItem.unknown_item=未知物品:{0} - -# Tps -cmd.tps.desc=显示服务器当前tps (tick per second)。(可能不精确) -cmd.tps.current=当前TPS - -# Useblock -cmd.useblock.desc=放置一个方块或打开箱子 - -# Useitem -cmd.useitem.desc=使用(左键单击)手上的物品 -cmd.useitem.use=使用了一个物品。 - - -[bot] -# ChatBots. Naming style: bot.. - -# Alerts -bot.alerts.start_rain=§c天气变化:开始下雨了。§r -bot.alerts.end_rain=§c天气变化:雨停了。§r -bot.alerts.start_thunderstorm=§c天气变化:现在是雷雨天。§r -bot.alerts.end_thunderstorm=§c天气变化:现在不再是雷雨天了。§r - -# AutoAttack -bot.autoAttack.mode=未知的攻击模式:{0},使用单一模式作为默认值。 -bot.autoAttack.priority=未知优先模式:{0},使用距离优先作为默认值。 -bot.autoAttack.invalidcooldown=攻击冷却时间值不能小于0,已使用自动作为默认值 - -# AutoCraft -bot.autoCraft.cmd=自动制作ChatBot命令 -bot.autoCraft.alias=自动制作ChatBot命令的别名 -bot.autoCraft.cmd.list=已加载{0}个配方:{1} -bot.autoCraft.cmd.resetcfg=将配置重置为默认值 -bot.autoCraft.recipe_not_exist=指定的配方名称不存在。请检查配置文件。 -bot.autoCraft.no_recipe_name=请指定要制作的配方名称。 -bot.autoCraft.stop=AutoCraft已停止 -bot.autoCraft.available_cmd=可用命令:{0}。可使用 /autocraft help <命令名> 了解更多信息。您可以使用 /ac 作为命令别名。 -bot.autoCraft.help.load=加载配置文件。 -bot.autoCraft.help.list=列出可用配方。 -bot.autoCraft.help.reload=重新加载配置文件。 -bot.autoCraft.help.resetcfg=将默认示例配置写入默认位置。 -bot.autoCraft.help.start=开始制作。用法:/autocraft start <配方名称> -bot.autoCraft.help.stop=停止当前正在进行的制作过程 -bot.autoCraft.help.help=获取命令描述。用法: /autocraft help <命令名> -bot.autoCraft.loaded=已成功加载 -bot.autoCraft.start=AutoCraft启动中:{0} -bot.autoCraft.start_fail=无法启动AutoCraft。请检查用于制作{0}的可用材料 -bot.autoCraft.table_not_found=找不到工作台 -bot.autoCraft.close_inventory=物品栏#{0}被AutoCraft关闭 -bot.autoCraft.missing_material=缺失材料:{0} -bot.autoCraft.aborted=制作被终止!请检查可用材料。 -bot.autoCraft.craft_fail=制作失败!等待更多材料 -bot.autoCraft.timeout=动作超时!原因:{0} -bot.autoCraft.error.config=分析配置时出错:{0} -bot.autoCraft.exception.empty=空配置文件:{0} -bot.autoCraft.exception.invalid=配置文件无效:{0} -bot.autoCraft.exception.item_miss=配方中缺少物品:{0} -bot.autoCraft.exception.invalid_table=tablelocation格式无效:{0} -bot.autoCraft.exception.item_name=配方{0}中在{1}的物品名称无效 -bot.autoCraft.exception.name_miss=解析配方时缺少配方名称 -bot.autoCraft.exception.slot=配方中的栏位字段无效:{0} -bot.autoCraft.exception.duplicate=指定了重复的配方名称:{0} -bot.autoCraft.debug.no_config=找不到配置。请新建一个。 - -# AutoDrop -bot.autoDrop.cmd=AutoDrop ChatBot命令 -bot.autoDrop.alias=AutoDrop ChatBot命令别名 -bot.autoDrop.on=已启用AutoDrop -bot.autoDrop.off=已禁用AutoDrop -bot.autoDrop.added=已添加物品{0} -bot.autoDrop.incorrect_name=物品名称不正确:{0}。请再试一次。 -bot.autoDrop.removed=已删除物品{0} -bot.autoDrop.not_in_list=物品不在列表中 -bot.autoDrop.no_item=列表中没有物品 -bot.autoDrop.list=列表中总计{0}个物品:\n {1} -bot.autoDrop.switched= 切换到{0}模式。 -bot.autoDrop.unknown_mode=未知模式。可用的模式:Include, Exclude, Everything -bot.autoDrop.no_mode=无法从配置中读取丢弃模式(drop mode)。使用Include模式。 -bot.autoDrop.no_inventory=找不到物品栏{0}! - -# AutoFish -bot.autoFish.throw=抛竿 -bot.autoFish.caught=钓到鱼了! -bot.autoFish.no_rod=手上没有鱼竿,可能用坏了? - -# AutoRelog -bot.autoRelog.launch=已启动,尝试了{0}次重新连接 -bot.autoRelog.no_kick_msg=在没有kick消息文件的情况下初始化 -bot.autoRelog.loading=从文件{0}加载消息 -bot.autoRelog.loaded=已加载消息:{0} -bot.autoRelog.not_found=找不到文件或目录:{0} -bot.autoRelog.curr_dir=当前目录为:{0} -bot.autoRelog.ignore_user_logout=由用户或MCC bot启动的断开连接。忽略。 -bot.autoRelog.disconnect_msg=连接断开,收到消息:{0} -bot.autoRelog.reconnect_always=忽略kick消息,仍要重新连接。 -bot.autoRelog.reconnect=信息包含 '{0}'。重新连接。 -bot.autoRelog.reconnect_ignore=不包含任何已定义关键字的消息,忽略。 -bot.autoRelog.wait=等待{0}秒后重新连接... - -# AutoRespond -bot.autoRespond.loading=正在从'{0}'加载匹配项 -bot.autoRespond.file_not_found=找不到文件或目录: '{0}' -bot.autoRespond.loaded_match=加载匹配项:\n{0} -bot.autoRespond.no_trigger=这个匹配永远不会触发:\n{0} -bot.autoRespond.no_action=匹配没有对应操作:\n{0} -bot.autoRespond.match_run=进行操作:{0} -bot.autoRespond.match=match: {0}\nregex: {1}\naction: {2}\nactionPrivate: {3}\nactionOther: {4}\nownersOnly: {5}\ncooldown: {6} - -# ChatLog -bot.chatLog.invalid_file=路径'{0}'包含无效字符。 - -# Mailer -bot.mailer.init=使用设置初始化Mailer: -bot.mailer.init.db= - 数据库文件:{0} -bot.mailer.init.ignore= - 忽略列表:{0} -bot.mailer.init.public= - 公开交互:{0} -bot.mailer.init.max_mails= - 每位玩家最多邮件数:{0} -bot.mailer.init.db_size= - 最大数据库大小:{0} -bot.mailer.init.mail_retention= - 邮件保留天数:{0} - -bot.mailer.init_fail.db_size=无法启用Mailer:最大数据库大小必须大于0。请检查设置。 -bot.mailer.init_fail.max_mails=无法启用Mailer:每个玩家的最大邮件数必须大于0。请检查设置。 -bot.mailer.init_fail.mail_retention=无法启用Mailer:邮件保留天数必须大于0。请检查设置。 - -bot.mailer.create.db=创建新数据库文件:{0} -bot.mailer.create.ignore=创建新忽略列表:{0} -bot.mailer.load.db=加载数据库文件:{0} -bot.mailer.load.ignore=加载忽略列表: - -bot.mailer.cmd=mailer 命令 - -bot.mailer.saving=正在保存邮件:{0} -bot.mailer.user_ignored={0}已被忽略! -bot.mailer.process_mails=正在查找要发送的邮件 @ {0} -bot.mailer.delivered=已发送:{0} - -bot.mailer.cmd.getmails=--- 数据库中的邮件 ---\n{0} -bot.mailer.cmd.getignored=--- 忽略列表 ---\n{0} -bot.mailer.cmd.ignore.added=添加{0}到忽略列表! -bot.mailer.cmd.ignore.removed={0}已从忽略列表中删除! -bot.mailer.cmd.ignore.invalid=丢失或无效的名称。用法:{0}<用户名> -bot.mailer.cmd.help=查看用法 - -# ReplayCapture -bot.replayCapture.cmd=replay 命令 -bot.replayCapture.created=已创建重播文件。 -bot.replayCapture.stopped=录制已停止。 -bot.replayCapture.restart=录制已停止。请重新启动程序以进行另一段录制。 - -# Script -bot.script.not_found=§8[MCC] [{0}] 找不到脚本文件:{1} -bot.script.file_not_found=找不到文件:'{0}' -bot.script.fail=脚本'{0}'运行失败 ({1})。 -bot.script.pm.loaded=脚本'{0}'加载。 - -# ScriptScheduler -bot.scriptScheduler.loading=正在从'{0}'加载任务 -bot.scriptScheduler.not_found=找不到文件:'{0}' -bot.scriptScheduler.loaded_task=已加载任务:\n{0} -bot.scriptScheduler.no_trigger=这个任务永远不会触发:\n{0} -bot.scriptScheduler.no_action=任务没有对应操作:\n{0} -bot.scriptScheduler.running_time=时间 / 运行中的操作:{0} -bot.scriptScheduler.running_inverval=间隔 / 运行中的操作:{0} -bot.scriptScheduler.running_login=登录 / 运行中的操作:{0} -bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n triggerontime: {2}\n triggeroninterval: {3}\n timevalue: {4}\n timeinterval: {5}\n action: {6} - -# TestBot -bot.testBot.told=Bot:{0}对我说:{1} -bot.testBot.said=Bot:{0}说:{1} diff --git a/MinecraftClient/Resources/lang/zh-Hans.ini b/MinecraftClient/Resources/lang/zh-Hans.ini new file mode 100644 index 00000000..2ecc2f1c --- /dev/null +++ b/MinecraftClient/Resources/lang/zh-Hans.ini @@ -0,0 +1,883 @@ +[mcc] +# Messages from MCC itself +mcc.help_us_translate=帮助我们翻译MCC:{0} +mcc.run_with_default_settings=\nMCC正在使用默认配置运行。 +mcc.settings_generated=§c配置文件 MinecraftClient.ini 已经生成。 +mcc.has_update=§e新版本的MCC已经推出:{0} +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}的聊天签名密钥缓存仍然有效. +mcc.connecting=正在连接至{0}... +mcc.fetching_key=正在从微软获取聊天签名密钥。 +mcc.ip=服务器IP: +mcc.use_version=§8正在运行Minecraft版本{0} (v{1}协议) +mcc.unknown_version=§8未知或不受支持的Minecraft版本{0}。\n正在切换至自动检测模式。 +mcc.forge=检查服务器是否正在运行Forge... +mcc.forgeforce=正在强制启动Forge支持。 +mcc.resolve=正在解析{0}... +mcc.found=§8已找到服务器{0}:{1},域名:{2} +mcc.not_found=§8无法执行{0}的SRV解析\n{1}:{2} +mcc.retrieve=正在获取服务器信息... +mcc.restart=正在重启Minecraft Console Client... +mcc.restart_delay=等待 {0} 秒后重启... +mcc.server_version=服务器版本: +mcc.disconnected=未连接至任何服务器。输入 '{0}help' 获得帮助。 +mcc.press_exit=或者敲击回车退出Minecraft Console Client。 +mcc.version_supported=该版本受到支持\n正在登录... +mcc.single_cmd=§7已发送命令§8 {0} +mcc.joined=已成功加入服务器。\n输入 '{0}quit' 离开服务器。 +mcc.reconnect=等待5秒 (剩余{0}次尝试)... +mcc.disconnect.lost=失去连接。 +mcc.disconnect.server=从服务器断开连接: +mcc.disconnect.login=连接失败: +mcc.link=链接:{0} +mcc.player_dead_respawn=你死了!1秒后自动重生。 +mcc.player_dead=你死了!输入 '{0}respawn' 重生。 +mcc.server_offline=§8服务器正处于离线模式。 +mcc.session=检查会话... +mcc.session_fail=检查会话失败 +mcc.server_protocol=§8服务器的Minecraft版本:{0} (协议v{1}) +mcc.with_forge=, 带有Forge +mcc.handshake=§8握手成功。 (服务器ID:{0}) +mcc.realms_available==您可以访问以下Realms世界 +mcc.realms_join=请使用"realms:<序号>"作为服务器IP加入Realms世界 +mcc.generator.generating=正在从 {1} 生成 {0} 信息。 +mcc.generator.done=已完成从 {0} 信息生成,使用 {1} +mcc.invaild_config=解析配置文件失败,输入 "{0}new" 以生成一个新的配置。 +mcc.gen_new_config=已生成新的配置文件 "{0}" 。 + +[debug] +# Messages from MCC Debug Mode +debug.color_test=颜色测试:终端应该显示:{0} +debug.session_cache_ok=§8已成功从磁盘中加载会话数据。 +debug.session_cache_fail=§8无法从磁盘加载缓存的会话数据。 +debug.keys_cache_ok=§8已成功从磁盘中加载聊天签名密钥。 +debug.keys_cache_fail=§8无法从磁盘中加载聊天签名密钥。 +debug.session_id=成功!(会话ID:{0}) +debug.crypto=§8密钥和哈希值已生成: +debug.request=§8正在请求{0} + +[error] +# Error messages from MCC +error.ping=ping此IP失败。 +error.unsupported=无法连接到服务器:不支持此版本! +error.determine=无法确定服务器版本。 +error.forgeforce=无法为此Minecraft版本强制启动Forge支持! +error.login=登录失败: +error.login.migrated=帐户已迁移,请使用电子邮件作为用户名。 +error.login.server=登录服务器不可用。请稍后再试。 +error.login.blocked=用户名/密码错误、IP被禁用或登录次数过多。 +error.login.response=服务器返回了无效的响应。 +error.login.premium=不是Premium用户。 +error.login.network=网络错误。 +error.login.ssl=SSL错误。 +error.login.unknown=未知错误。 +error.login.cancel=用户取消。 +error.login_failed=无法登录到此服务器。 +error.join=加入服务器时发生错误。 +error.connect=无法连接到此IP。 +error.timeout=连接超时 +error.unexpect_response=§8来自服务器的意外响应(这是Minecraft服务器吗?) +error.invalid_response=§8对握手包的响应无效 +error.invalid_encrypt=§8对StartEncryption数据包的响应无效 +error.version_different=§8服务器报告的版本与手动设置的版本不同。登录可能无法工作。 +error.no_version_report=§8服务器未报告其协议版本,自动检测将不起作用。 +error.connection_timeout=§8尝试连接到此IP时超时。 +error.forge=§8Forge登录握手未成功完成 +error.forge_encrypt=§8Forge StartEncryption握手未成功完成 +error.setting.argument_syntax={0}:无效语法,应为 --argname=value 或 --section.argname=value +error.http_code=§8接收到服务器错误:{0} +error.auth=§8在刷新身份验证时接收到服务器错误:{0} +error.realms.ip_error=无法获取您Realms世界的服务器IP +error.realms.access_denied=此Realms世界不存在或访问被拒绝 +error.realms.server_unavailable=Realms服务器可能需要一些时间来启动。请稍后再试。 +error.realms.server_id=Realms服务器ID无效或未知。 +error.realms.disabled=正在尝试加入Realms世界,但配置中禁用了Realms支持 +error.missing.argument=缺少参数 {0} +error.usage=使用方法: +error.generator.invalid=该生成器命令用法无效! +error.generator.path=提供的数据路径无效! (路径不存在或是输入错误) +error.generator.json=提供的路径必须指向一个.json格式的文件! + +[internal command] +# MCC internal help command +icmd.help=help <命令名称> :显示有关命令的简要帮助。 +icmd.unknown=未知命令 '{0}'。请使用 'help' 命令来获取命令列表。 +icmd.list=help <命令名称>。可用命令:{0}。在服务器上获取帮助,请改用 '{1}send /help'。 +icmd.error=OnInternalCommand: 来自{0}的错误{1} + +[exception] +# Exception messages threw by MCC +exception.user_logout=用户发起的注销应该通过调用Disconnect()来完成 +exception.unknown_direction=未知方向 +exception.palette.block=请为此Minecraft版本更新方块类型处理。详细请参考 Material.cs +exception.palette.entity=请为此Minecraft版本更新实体类型处理。详细请参考 EntityType.cs +exception.palette.item=请为此Minecraft版本更新物品类型处理。详细请参考 ItemType.cs +exception.palette.packet=请为此Minecraft版本更新数据包类型调色板。详细请参考 PacketTypePalette.cs +exception.packet_process=无法处理传入的{0}类型的数据包。(数据包ID:{1},协议:{2},登陆阶段:{3},内部异常:{4})。 +exception.version_unsupport=版本{0}的协议未被支持。 +exception.chatbot.init=不应在构造函数中调用ChatBot的方法,因为作为API处理程序的模块尚未初始化。请重写 Initialize() 或 AfterGameJoined() 来执行初始化任务。 +exception.csrunner.invalid_head=提供的脚本没有有效的MCCScript头 + +[chatbot] +# ChatBot API +chatbot.reconnect=[{0}] 断开并重新连接到服务器 + +[filemonitor] +# FileMonitor +filemonitor.init=§8[{0}] 正在为文件{1}初始化FileSystemWatcher +filemonitor.fail=§8[{0}] 无法初始化FileSystemWatcher,正在使用轮询重试 + +[extra] +# Inventory, Terrain & Movements, Entity related messages +# Terrain & Movements +extra.terrainandmovement_enabled=地形和移动处理现在已启用。 +extra.terrainandmovement_disabled=§c该游戏版本目前还不支持地形和移动处理。 +extra.terrainandmovement_required=请先在配置文件中启用地形和移动处理。 +# Inventory +extra.inventory_enabled=库存(物品)处理现在已启用。 +extra.inventory_disabled=§c该MC版本目前未支持处理库存(物品)。 +extra.inventory_required=请先在配置文件中启用"Main.Advanced.inventory_handling"。 +extra.inventory_interact=请使用 /inventory 来与其交互。 +extra.inventory_open=容器# {0}已开启:{1} +extra.inventory_close=容器# {0}已关闭。 +# Entity +extra.entity_disabled=§c该MC版本当前还不支持处理实体。 +extra.entity_required=请先在配置文件中启用"Main.Advanced.entity_handling"。 + + +[forge] +# Messages from Forge handler +forge.version=§8Forge协议版本:{0} +forge.send_mod=§8向服务器发送伪造的forge模组列表... +forge.accept=§8接受来自的服务器模组列表... +forge.registry=§8已接收的注册表包含{0}个条目 +forge.registry_2=§8已接收注册表{0},包含{1}个条目 +forge.accept_registry=§8接受服务器注册表... +forge.complete=Forge服务器连接完成! +forge.with_mod=§8服务器正在运行Forge,有{0}个模组。 +forge.no_mod=§8正在运行的服务器没有Forge模组。 +forge.mod_list=§8模组列表: +# FML2 +forge.fml2.mod=§8收到FM2服务器模组列表 +forge.fml2.mod_send=§8发回FML2客户端的模组列表 +forge.fml2.registry=§8确认FML2服务器注册表:{0} +forge.fml2.config=§8确认FML2服务器配置:{0} +forge.fml2.unknown=§8收到未知的FML2握手信息,编号:{0} +forge.fml2.unknown_channel=§8忽略未知的FML2登录消息通道:{0} + +[cache] +# Session Cache +cache.loading=§8加载Minecraft配置文件:{0} +cache.loaded=§8已加载会话:{0}:{1} +cache.converting=§8从磁盘转换会话缓存:{0} +cache.read_fail=§8无法从磁盘读取序列化会话缓存:{0} +cache.malformed=§8从磁盘读取序列化会话缓存时,获取到格式错误的数据:{0} +cache.loading_session=§8从磁盘加载会话缓存:{0} +cache.ignore_string=§8忽略会话令牌字符串'{0}':{1} +cache.ignore_line=§8忽略无效的会话令牌行:{0} +cache.read_fail_plain=§8无法从磁盘读取会话缓存:{0} +cache.saving=§8将会话缓存保存到磁盘 +cache.save_fail=§8无法将会话缓存写入磁盘:{0} +# Profile Key Cache +cache.loading_keys=§8从磁盘加载聊天签名密钥缓存: {0} +cache.loaded_keys=§8已加载签名密钥,下次刷新于 {0} +cache.ignore_string_keys=§8忽略聊天签名密钥字符串 '{0}':{1} +cache.ignore_line_keys=§8忽略无效的聊天签名密钥行:{0} +cache.read_fail_plain_keys=§8无法从磁盘读取聊天签名密钥缓存:{0} +cache.saving_keys=§8将聊天签名密钥保存到磁盘 +cache.save_fail_keys=§8无法将聊天签名密钥写入磁盘:{0} + +[proxy] +proxy.connected=§8已连接到代理{0}:{1} + +[chat] +# Chat Parser +chat.download=§8正在从Mojang服务器下载语言文件 '{0}.lang'... +chat.request=§8正在请求{0}... +chat.done=§8下载完成。文件另存为 '{0}' +chat.fail=§8下载文件失败。 +chat.from_dir=§8默认为你的Minecraft目录中的en_GB.lang +chat.loaded=§8已加载翻译文件。 +chat.not_found=§8找不到翻译文件:'{0}'\n如果没有此文件,某些信息将无法正确打印。 +chat.message_chain_broken=玩家 {0} 的消息签名链已经被破坏。(签名不在可信) + +[general] +# General message/information (i.e. Done) +general.done=完成 +general.fail=失败 +general.bot_unload=将会卸载此bot。 +general.available_cmd=可用命令:{0} + + +[cmd] +# Commands. Naming style: cmd.. + +# Animation +cmd.animation.desc=挥动你的手臂。 + +# Bots +cmd.bots.desc=列出全部 ChatBot ,加载或卸载一个 ChatBot。 +cmd.bots.list=已加载的 ChatBot +cmd.bots.notfound=该 ChatBot 并未加载,请检查输入。 +cmd.bots.noloaded=没有 ChatBot 被加载。 +cmd.bots.unloaded=成功卸载 ChatBot:{0} +cmd.bots.unloaded_all=成功卸载所有 ChatBot。 + +# ChangeSlot +cmd.changeSlot.desc=变更快捷栏栏位 +cmd.changeSlot.nan=无法变更栏位:不是数字 +cmd.changeSlot.changed=已变更为栏位{0} +cmd.changeSlot.fail=无法变更栏位 + +# Chunk +cmd.chunk.desc=显示区块加载状态。\n如果显示错乱竟在设置中更改 EnableEmoji=false 。 +cmd.chunk.current=当前位置:{0},所在区块:({1}, {2})。 +cmd.chunk.marked=标记的位置: +cmd.chunk.chunk_pos=区块:({0}, {1})。 +cmd.chunk.outside=§x§0由于被标记的区块距离玩家太远,它不会被显示在图中§r +cmd.chunk.icon=玩家:{0},标记的区块:{1},未收到:{2},加载中:{3},已加载:{4} +cmd.chunk.for_debug=§x§0此命令仅用于调试使用,确保你已经了解执行该命令会造成的影响。§r + +# Connect +cmd.connect.desc=连接到指定的服务器。 +cmd.connect.unknown=未知帐户 '{0}'。 +cmd.connect.invalid_ip=无效的服务器IP '{0}'。 + +# Debug +cmd.debug.desc=切换调试消息。 +cmd.debug.state_on=调试消息现在已打开 +cmd.debug.state_off=调试消息现在已关闭 + +# Dig +cmd.dig.desc=试图破坏一个方块 +cmd.dig.too_far=你离这个方块太远了。 +cmd.dig.no_block=这个地方没有方块 (空气) +cmd.dig.dig=尝试挖掘位于({0}, {1}, {2})的方块({3})。 +cmd.dig.fail=无法开始挖掘方块。 + +# Entitycmd +cmd.entityCmd.attacked=已攻击实体 +cmd.entityCmd.used=已使用实体 +cmd.entityCmd.not_found=找不到实体 + +cmd.entityCmd.entity=实体 +cmd.entityCmd.entities=实体集 +cmd.entityCmd.nickname=昵称 +cmd.entityCmd.customname=自定义名称 +cmd.entityCmd.latency=延迟 +cmd.entityCmd.item=物品 +cmd.entityCmd.equipment=装备 +cmd.entityCmd.mainhand=主手 +cmd.entityCmd.offhane=副手 +cmd.entityCmd.helmet=头盔 +cmd.entityCmd.chestplate=胸甲 +cmd.entityCmd.leggings=护腿 +cmd.entityCmd.boots=靴子 +cmd.entityCmd.pose=姿势 +cmd.entityCmd.health=生命值 +cmd.entityCmd.distance=距离 +cmd.entityCmd.location=位置 +cmd.entityCmd.type=类型 + +# Exec If +cmd.execif.desc=允许你在某个条件成立时执行一个命令。(你可以使用"MinecraftClient.ini"中的变量和使用"/set"命令定义的变量,以及CSharp表达式)。 +cmd.execif.executed=条件'{0}'满足,已执行命令'{1}',得到结果'{2}'。 +cmd.execif.executed_no_output=条件'{0}'满足,已执行命令'{1}',该命令没有返回任何结果。 +cmd.execif.error_occured=在执行命令 {0} 时出现错误。 +cmd.execif.error=错误:{0} + +# Exec Multi +cmd.execmulti.desc=依次执行多个命令。 +cmd.execmulti.executed=执行了命令 '{0}' , +cmd.execmulti.result=结果为 '{0}'! +cmd.execmulti.no_result=没有返回结果! + +# Exit +cmd.exit.desc=断开与服务器的连接。 + +# Health +cmd.health.desc=显示生命值和饱食度。 +cmd.health.response=生命值:{0},饱食度:{1},等级:{2},总经验值:{3} + +# Inventory +cmd.inventory.desc=容器相关命令 +cmd.inventory.creative_done=向容器#{2}请求{0} x{1} +cmd.inventory.creative_delete=请求清除栏位 #{0} +cmd.inventory.creative_fail=请求创造模式操作失败 +cmd.inventory.need_creative=你必须在创造模式 +cmd.inventory.container_not_found=找不到容器,请使用显式ID重试 +cmd.inventory.close=关闭容器 #{0} +cmd.inventory.close_fail=关闭容器失败 #{0} +cmd.inventory.not_exist=容器#{0}不存在 +cmd.inventory.inventory=容器 +cmd.inventory.inventories=容器 +cmd.inventory.inventories_available=可用容器 +cmd.inventory.hotbar=您选择的快捷栏是{0} +cmd.inventory.damage=武器伤害值 +cmd.inventory.left=左 +cmd.inventory.right=右 +cmd.inventory.middle=中间 +cmd.inventory.clicking={0}正在点击容器#{2}中的栏位{1} +cmd.inventory.shiftclick=按住Shift键点击容器#{1}中的栏位{0} +cmd.inventory.shiftclick_fail=执行Shift点击失败,这可能是因为该容器类型目前不被支持。 +cmd.inventory.no_item=栏位#{0}中没有物品 +cmd.inventory.drop=从栏位#{0}中丢弃了1个物品 +cmd.inventory.drop_stack=从栏位#{0}中丢弃了所有堆叠的物品 +# Inventory Help +cmd.inventory.help.basic=基本用法 +cmd.inventory.help.available=可用操作 +cmd.inventory.help.help={0}\n使用 '/inventory help ' 获取帮助。\n'player' 和 'container' 可以简化为 'p' 和 'c'。\n请注意,'[]' 中的参数是可选的。 +cmd.inventory.help.usage=用法 +cmd.inventory.help.list=列出所有容器。 +cmd.inventory.help.close=关闭打开的容器。 +cmd.inventory.help.click=单击物品。 +cmd.inventory.help.shiftclick=按住Shift键点击一个物品。 +cmd.inventory.help.drop=从容器中丢弃物品。 +cmd.inventory.help.creativegive=在创造模式中给予物品。 +cmd.inventory.help.creativedelete=在创造性模式中清除栏位。 +cmd.inventory.help.inventories=列出所有可用的窗口。 +cmd.inventory.help.search=在打开的所有窗口中搜索物品。 +cmd.inventory.help.unknown=未知操作。 +cmd.inventory.found_items=找到物品 +cmd.inventory.no_found_items=在任何窗口中都没有找到该物品! + +# Leave bed +cmd.bed.desc=用于右键床开始睡觉或离开正在睡觉的床。 +cmd.bed.leaving=已向服务器发送离开床的数据包。 +cmd.bed.trying_to_use=试图在位于 (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) 上的床上睡觉,结果:{3} +cmd.bed.in=成功地躺在了床上! +cmd.bed.not_in=上床睡觉失败了。(PS: 你必须使用床头对应的坐标) +cmd.bed.not_a_bed=位于 (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) 的方块不是一个床! +cmd.bed.searching=在半径为{0}的范围内寻找床... +cmd.bed.bed_not_found=没有找到床! +cmd.bed.found_a_bed_at=在 (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) 找到了一个床。 +cmd.bed.cant_reach_safely=无法安全地到达床边! +cmd.bed.moving=正在移动到床所在的位置: (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) +cmd.bed.failed_to_reach_in_time=无法在30秒内到达(X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}),放弃本次寻路。 + +# List +cmd.list.desc=获取玩家列表。 +cmd.list.players=玩家列表:{0} + +# Log +cmd.log.desc=将文本记录到控制台。 + +# Look +cmd.look.desc=查看方向或坐标。 +cmd.look.unknown=未知方向 '{0}'。 +cmd.look.at=当前视角 偏航角:{0} 俯仰角:{1}。 +cmd.look.block=正看向位于 {0} 的方块。 +cmd.look.inspection=与视线相交的第一个方块是 {0} ({1:0}, {2:0}, {3:0}),距离玩家 {4:0.0}({5:0.0})。 +cmd.look.noinspection=在 {0} 米内没有任何方块与视线相交。 + +# Move +cmd.move.desc=移动或开始移动。 +cmd.move.enable=在下次服务器登录、重生或更换世界时启用地形和移动。 +cmd.move.disable=禁用地形和移动。 +cmd.move.moving=移动{0} +cmd.move.dir_fail=不能朝此方向移动。 +cmd.move.walk=移动到{0} +cmd.move.fail=无法计算到达{0}的路径。 +cmd.move.suggestforce=无法计算安全到达{0}的路径. 请使用 -f 参数允许不安全移动。 +cmd.move.gravity.enabled=重力已开启。 +cmd.move.gravity.disabled=重力已关闭。 +cmd.move.chunk_loading_status=区块加载进度:{0:P} - 共{2}个,加载完成了{1}个。 +cmd.move.chunk_not_loaded=目标位置所在的区块还没有被加载。你可以使用"/chunk status {0:0.0} {1:0.0} {2:0.0}"来查看区块的加载状态。 + +# Reco +cmd.reco.desc=重新启动并重新连接到服务器。 + +# Reload +cmd.reload.started=重新加载设置中... +cmd.reload.warning1=这条命令不会影响在连接到服务器之前的某些设置。 +cmd.reload.warning2=一些通过命令行参数传递的设置可能会被覆盖! +cmd.reload.warning3=你有可能需要重新连接(/reco)才能使某些设置生效。 +cmd.reload.warning4=由于技术限制,ReplayCapturer 将不会被强制重载! +cmd.reload.finished=重新加载设置完成! +cmd.reload.desc=重新加载MCC的设置。 + +# Respawn +cmd.respawn.desc=如果你死亡了,请用这个来重生。 +cmd.respawn.done=你重生了。 + +# Script +cmd.script.desc=运行脚本文件。 + +# Send +cmd.send.desc=发送聊天信息或命令。 + +# Set +cmd.set.desc=设置自定义 %variable%。 +cmd.set.format=变量名范围必须为 A-Za-z0-9。 + +# SetRnd +cmd.setrnd.desc=随机为自定义 %变量名% 赋值。 +cmd.setrndnum.format=setrnd 变量名 -7to17 +cmd.setrndstr.format=setrnd 变量名 字符串1 "\"字符串2\" 字符串3" + +# Sneak +cmd.sneak.desc=切换到潜行 +cmd.sneak.on=现在你在潜行状态。 +cmd.sneak.off=你不在潜行状态了。 + +# DropItem +cmd.dropItem.desc=丢弃玩家容器中的指定类型物品或打开的容器 +cmd.dropItem.dropped=已从容器#{1}中丢弃所有{0} +cmd.dropItem.unknown_item=未知物品:{0} + +# Tps +cmd.tps.desc=显示服务器当前tps (tick per second)。(可能不精确) +cmd.tps.current=当前TPS + +# Useblock +cmd.useblock.desc=放置一个方块或打开箱子 +cmd.useblock.use=使用位于 ({0:0.0}, {1:0.0}, {2:0.0}) 的 {3}。 + +# Useitem +cmd.useitem.desc=使用(左键单击)手上的物品 +cmd.useitem.use=使用了一个物品。 + + +[bot] +# ChatBots. Naming style: bot.. + +# Alerts +bot.alerts.start_rain=§c天气变化:开始下雨了。§r +bot.alerts.end_rain=§c天气变化:雨停了。§r +bot.alerts.start_thunderstorm=§c天气变化:现在是雷雨天。§r +bot.alerts.end_thunderstorm=§c天气变化:现在不再是雷雨天了。§r + +# Anti AFK +bot.antiafk.not_using_terrain_handling=当前未启用地形处理,如果你想使用相关的特性,请在设置中启用它。当前将继续使用替代方法运行(定时发送命令)。 +bot.antiafk.swapping=最短时间大于最长时间,已将它们交换。 +bot.antiafk.invalid_walk_range=当前提供的行走范围无效,它必须是一个大于0的正整数,将使用默认值5! + +# AutoAttack +bot.autoAttack.invalidcooldown=攻击冷却时间值不能小于0,已使用自动作为默认值 + +# AutoCraft +bot.autoCraft.cmd=自动制作ChatBot命令 +bot.autoCraft.alias=自动制作ChatBot命令的别名 +bot.autoCraft.cmd.list=已加载{0}个配方:{1} +bot.autoCraft.cmd.resetcfg=将配置重置为默认值 +bot.autoCraft.recipe_not_exist=指定的配方名称不存在。请检查配置文件。 +bot.autoCraft.no_recipe_name=请指定要制作的配方名称。 +bot.autoCraft.stop=AutoCraft已停止 +bot.autoCraft.available_cmd=可用命令:{0}。可使用 /autocraft help <命令名> 了解更多信息。您可以使用 /ac 作为命令别名。 +bot.autoCraft.help.load=加载配置文件。 +bot.autoCraft.help.list=列出可用配方。 +bot.autoCraft.help.reload=重新加载配置文件。 +bot.autoCraft.help.resetcfg=将默认示例配置写入默认位置。 +bot.autoCraft.help.start=开始制作。用法:/autocraft start <配方名称> +bot.autoCraft.help.stop=停止当前正在进行的制作过程 +bot.autoCraft.help.help=获取命令描述。用法:/autocraft help <命令名> +bot.autoCraft.loaded=已成功加载 +bot.autoCraft.start=AutoCraft启动中:{0} +bot.autoCraft.start_fail=无法启动AutoCraft。请检查用于制作{0}的可用材料 +bot.autoCraft.table_not_found=找不到工作台 +bot.autoCraft.close_inventory=容器#{0}被AutoCraft关闭 +bot.autoCraft.missing_material=缺失材料:{0} +bot.autoCraft.aborted=制作被终止!请检查可用材料。 +bot.autoCraft.craft_fail=制作失败!等待更多材料 +bot.autoCraft.timeout=动作超时!原因:{0} +bot.autoCraft.error.config=分析配置时出错:{0} +bot.autoCraft.exception.name_miss=解析配方时缺少配方名称 +bot.autoCraft.exception.duplicate=指定了重复的配方名称:{0} +bot.autoCraft.debug.no_config=找不到配置。请新建一个。 +bot.autocraft.invaild_slots=配方的物品数量不匹配,已自动调整。 +bot.autocraft.invaild_invaild_result=无效的输出物品! + +# AutoDig +bot.autodig.start_delay=将在 {0:0.0} 秒后开始自动挖掘。 +bot.autodig.dig_timeout=挖掘方块超时,重试。 +bot.autodig.not_allow=当前所看向的方块不在允许挖掘列表中。 +bot.autodig.cmd=自动挖掘 ChatBot 命令 +bot.autodig.available_cmd=可用命令:{0}。可使用 /digbot help <命令名> 了解更多信息。 +bot.autodig.start=开始自动挖掘。 +bot.autodig.stop=停止自动挖掘。 +bot.autodig.help.start=开始运行自动挖掘。 +bot.autodig.help.stop=停止运行自动挖掘。 +bot.autodig.help.help=获取命令描述。用法:/digbot help <命令名> + +# AutoDrop +bot.autoDrop.cmd=AutoDrop ChatBot命令 +bot.autoDrop.alias=AutoDrop ChatBot命令别名 +bot.autoDrop.on=已启用AutoDrop +bot.autoDrop.off=已禁用AutoDrop +bot.autoDrop.added=已添加物品{0} +bot.autoDrop.incorrect_name=物品名称不正确:{0}。请再试一次。 +bot.autoDrop.removed=已删除物品{0} +bot.autoDrop.not_in_list=物品不在列表中 +bot.autoDrop.no_item=列表中没有物品 +bot.autoDrop.list=列表中总计{0}个物品:\n {1} +bot.autoDrop.switched= 切换到{0}模式。 +bot.autoDrop.unknown_mode=未知模式。可用的模式:Include, Exclude, Everything +bot.autoDrop.no_mode=无法从配置中读取丢弃模式(drop mode)。使用Include模式。 +bot.autoDrop.no_inventory=找不到容器{0}! + +# AutoFish +bot.autoFish.no_inv_handle=未启用库存(物品)处理。将不支持检查鱼竿耐久度都和自动切换鱼竿。 +bot.autoFish.start=将在 {0:0.0} 秒后自动开始钓鱼。 +bot.autoFish.throw=抛竿成功。 +bot.autoFish.caught=钓到了一条鱼!(总计 {0} 条) +bot.autoFish.caught_at=在 ({0:0.0},{1:0.0},{2:0.0}) 掉到了一条鱼!(总计 {0} 条) +bot.autoFish.no_rod=没有可使用的钓鱼竿了。也许是用坏了或耐久度过低? +bot.autoFish.despawn=鱼钩消失,将会重新抛竿。 +bot.autoFish.fishing_timeout=钓鱼超时,将于几秒钟后重新抛竿。 +bot.autoFish.cast_timeout=抛竿超时,将在等待一段时间后重试。(超时时间延长至 {0:0.0} 秒) +bot.autoFish.update_lookat=更新当前朝向 偏航角(yaw) = {0:0.00}, 俯仰角(pitch) = {1:0.00}。 +bot.autoFish.switch=切换到位于背包 {0} 位置的鱼竿,剩余耐用 {1}/64。 + +# AutoRelog +bot.autoRelog.launch=已启动,尝试了{0}次重新连接 +bot.autoRelog.no_kick_msg=在没有kick消息文件的情况下初始化 +bot.autoRelog.loading=从文件{0}加载消息 +bot.autoRelog.loaded=已加载消息:{0} +bot.autoRelog.not_found=找不到文件或目录:{0} +bot.autoRelog.curr_dir=当前目录为:{0} +bot.autoRelog.ignore_user_logout=由用户或MCC bot启动的断开连接。忽略。 +bot.autoRelog.disconnect_msg=连接断开,收到消息:{0} +bot.autoRelog.reconnect_always=忽略kick消息,仍要重新连接。 +bot.autoRelog.reconnect=信息包含 '{0}'。重新连接。 +bot.autoRelog.reconnect_ignore=不包含任何已定义关键字的消息,忽略。 +bot.autoRelog.wait=等待{0}秒后重新连接... + +# AutoRespond +bot.autoRespond.loading=正在从'{0}'加载匹配项 +bot.autoRespond.file_not_found=找不到文件或目录: '{0}' +bot.autoRespond.loaded_match=加载匹配项:\n{0} +bot.autoRespond.no_trigger=这个匹配永远不会触发:\n{0} +bot.autoRespond.no_action=匹配没有对应操作:\n{0} +bot.autoRespond.match_run=进行操作:{0} +bot.autoRespond.match=match: {0}\nregex: {1}\naction: {2}\nactionPrivate: {3}\nactionOther: {4}\nownersOnly: {5}\ncooldown: {6} + +# ChatLog +bot.chatLog.invalid_file=路径'{0}'包含无效字符。 + +# Mailer +bot.mailer.init=使用设置初始化Mailer: +bot.mailer.init.db= - 数据库文件:{0} +bot.mailer.init.ignore= - 忽略列表:{0} +bot.mailer.init.public= - 公开交互:{0} +bot.mailer.init.max_mails= - 每位玩家最多邮件数:{0} +bot.mailer.init.db_size= - 最大数据库大小:{0} +bot.mailer.init.mail_retention= - 邮件保留天数:{0} + +bot.mailer.init_fail.db_size=无法启用Mailer:最大数据库大小必须大于0。请检查设置。 +bot.mailer.init_fail.max_mails=无法启用Mailer:每个玩家的最大邮件数必须大于0。请检查设置。 +bot.mailer.init_fail.mail_retention=无法启用Mailer:邮件保留天数必须大于0。请检查设置。 + +bot.mailer.create.db=创建新数据库文件:{0} +bot.mailer.create.ignore=创建新忽略列表:{0} +bot.mailer.load.db=加载数据库文件:{0} +bot.mailer.load.ignore=加载忽略列表: + +bot.mailer.cmd=mailer 命令 + +bot.mailer.saving=正在保存邮件:{0} +bot.mailer.user_ignored={0}已被忽略! +bot.mailer.process_mails=正在查找要发送的邮件 @ {0} +bot.mailer.delivered=已发送:{0} + +bot.mailer.cmd.getmails=--- 数据库中的邮件 ---\n{0} +bot.mailer.cmd.getignored=--- 忽略列表 ---\n{0} +bot.mailer.cmd.ignore.added=添加{0}到忽略列表! +bot.mailer.cmd.ignore.removed={0}已从忽略列表中删除! +bot.mailer.cmd.ignore.invalid=丢失或无效的名称。用法:{0}<用户名> +bot.mailer.cmd.help=查看用法 + +# Maps +bot.map.cmd.desc=渲染物品形式的地图 +bot.map.cmd.not_found=没有找到编号为 '{0}' 的地图! +bot.map.cmd.invalid_id=无效的地图编号,必须是一个数字。 +bot.map.received=从服务器接收到的地图有: +bot.map.no_maps=没有收到过地图! +bot.map.received_map=收到一个编号为 {0} 的新地图。 +bot.map.rendered=成功接收到地图 '{0}' ,保存为 '{1}' +bot.map.failed_to_render=无法渲染编号为 '{0}' 的地图。 +bot.map.list_item=- 地图编号:{0}(最近更新于:{1}) +bot.map.windows_only=保存地图到文件功能目前仅可用于Windows平台。 + +# ReplayCapture +bot.replayCapture.cmd=replay 命令 +bot.replayCapture.created=已创建重播文件。 +bot.replayCapture.stopped=录制已停止。 +bot.replayCapture.restart=录制已停止。请重新启动程序以进行另一段录制。 + +# Follow player +cmd.follow.desc=让机器人跟随指定的玩家 +cmd.follow.usage=follow [-f] (使用 -f 允许机器人途径不安全的地方) +cmd.follow.already_stopped=已经停止过了 +cmd.follow.stopping=已停止! +cmd.follow.invalid_name=提供的玩家名无效! +cmd.follow.invalid_player=指定的玩家没有上线或距离太远! +cmd.follow.cant_reach_player=无法寻路到该玩家,有可能他在没有加载的区块中,或是距离太远,也有可能是间隙或水体等障碍使机器人无法到达。 +cmd.follow.already_following=已经在跟随 {0} 了! +cmd.follow.switched=切换为跟随 {0}! +cmd.follow.started=开始跟随 {0}! +cmd.follow.unsafe_enabled=启用了允许途径不安全位置(注意:机器人可能会受伤或死亡!) +cmd.follow.note=注意:此机器人的速度很慢,你需要慢慢地走,而且要保持很近的距离,这样它才能跟上,有点像拿着食物让动物跟着你。这是由于寻路算法的限制,我们正在努力改进它。 +cmd.follow.player_came_to_the_range=玩家 {0} 回到了可寻路范围之内! +cmd.follow.resuming=继续跟随! +cmd.follow.player_left_the_range=玩家 {0} 离开了可寻路范围! +cmd.follow.pausing=已暂停! +cmd.follow.player_left=玩家 {0} 离开了服务器! +cmd.follow.stopping=已停止! + +# Script +bot.script.not_found=§8[MCC] [{0}] 找不到脚本文件:{1} +bot.script.file_not_found=找不到文件:'{0}' +bot.script.fail=脚本'{0}'运行失败 ({1})。 +bot.script.pm.loaded=脚本'{0}'加载。 + +# ScriptScheduler +bot.scriptScheduler.loaded_task=已加载任务:\n{0} +bot.scriptScheduler.no_trigger=这个任务永远不会触发:\n{0} +bot.scriptScheduler.no_action=任务没有对应操作:\n{0} +bot.scriptScheduler.running_time=时间 / 运行中的操作:{0} +bot.scriptScheduler.running_inverval=间隔 / 运行中的操作:{0} +bot.scriptScheduler.running_login=登录 / 运行中的操作:{0} +bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n triggerontime: {2}\n triggeroninterval: {3}\n timevalue: {4}\n timeinterval: {5}\n action: {6} + +# TestBot +bot.testBot.told=Bot:{0}对我说:{1} +bot.testBot.said=Bot:{0}说:{1} + + +[config] + +config.load=已从 {0} 加载设置。 +config.load.fail=§c加载设置时出错:§r +config.write.fail=§保存备份文件({0})时出错:§r +config.backup.fail=§c写入设置文件({0})时出错:§r +config.saving=§a当前设置已保存至 {0} + +# Head +config.Head=启动配置文件\n\n# 对 MCC(Minecraft 命令行客户端)不熟悉?请看这个文档:https://mccteam.github.io/guide/configuration.html\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(手动在网页上登录)。 + +# Main.Advanced +config.Main.Advanced=在更改这里的某项设置之前,请确保你理解了该选项的影响。 +config.Main.Advanced.language=请使用Minecraft的语言代码填写,详见 https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239 +config.Main.Advanced.internal_cmd_char=MCC内部命令的前缀,可使用 "none", "slash"(/) 或 "backslash"(\)。 +config.Main.Advanced.message_cooldown=控制向服务器发送消息的最小间隔时间(秒)。 +config.Main.Advanced.bot_owners=设置机器人的所有者。/!\服务器管理员可以伪装成任何玩家! +config.Main.Advanced.mc_version=游戏版本,可使用 "auto"(自动) 或类似 "1.X.X" 的值。设定具体版本将跳过从服务器解析的过程。 +config.Main.Advanced.mc_forge=可使用 "auto"(自动),"no"(禁用) 或是 "force"(强制启用,仅在 1.13 及更高的版本中可用)。 +config.Main.Advanced.brand_info=客户端标识,可用 "mcc","vanilla"(原版客户端) 或 "none"(空标识)。这用于改变MCC向服务器发送的客户端标识内容。 +config.Main.Advanced.chatbot_log_file=留空将禁用 ChatBot 写入日志文件。 +config.Main.Advanced.private_msgs_cmd_name=远程控制功能将会使用它。 +config.Main.Advanced.show_system_messages=显示游戏服务器的系统消息(来自管理员或命令方块等)。 +config.Main.Advanced.show_xpbar_messages=显示经验条上方的消息,如果被此类消息刷屏请禁用此选项。 +config.Main.Advanced.show_chat_links=解码聊天信息里的链接,并在控制台单独显示。 +config.Main.Advanced.show_inventory_layout=以字符画形式显示库存布局。 +config.Main.Advanced.terrain_and_movements=开启地形处理将消耗更多的内存、CPU和网络带宽,但这允许你进行移动以及和方块交互。 +config.Main.Advanced.inventory_handling=启用库存处理(可操作背包、箱子等容器)。 +config.Main.Advanced.entity_handling=启用实体处理。 +config.Main.Advanced.session_cache=如何缓存会话令牌。可使用 "none"(不缓存),"memory"(内存缓存) 或 "disk"(磁盘缓存)。 +config.Main.Advanced.profilekey_cache=如何缓存聊天签名密钥。可使用 "none"(不缓存),"memory"(内存缓存) 或 "disk"(磁盘缓存)。 +config.Main.Advanced.resolve_srv_records=可填写 "no","fast"(超时时间为五秒钟)或是 "yes"。加入某些服务器需要开启此项。 +config.Main.Advanced.account_list=AccountList:使你可以不用输入账号信息而快速在多个账号间切换\n# 可用命令示例:"/tell reco Player2","/connect Player1" +config.Main.Advanced.server_list=ServerList:可用使用服务器别名快速连接到该服务器\n# 别名不能包含空格和小数点",而且 "localhost" 不能作为别名使用。\n# 可用命令示例:"/tell connect Server1","/connect Server2" +config.Main.Advanced.player_head_icon=使用玩家皮肤头像作为窗口图标,这仅在部分旧版控制台中有效。 +config.Main.Advanced.exit_on_failure=发生错误时是否直接退出,用于在非交互式脚本中使用MCC。 +config.Main.Advanced.script_cache=缓存已编译的脚本,以便在低端设备上更快的加载。 +config.Main.Advanced.timestamps=在聊天信息头部添加时间戳。 +config.Main.Advanced.auto_respawn=死亡时自动重生(开启前请确保你的出生点是安全的) +config.Main.Advanced.minecraft_realms=启用对加入我的世界领域(Realms)服务器的支持。 +config.Main.Advanced.move_head_while_walking=在移动时转向头部。 +config.Main.Advanced.timeout=与服务器的TCP连接超时时间(秒)。 +config.Main.Advanced.enable_emoji=如果关闭,Emoji表情符号将被替换成更简单的字符(用于 "/chunk status" 命令) +config.Main.Advanced.movement_speed=高于 2 的移动速度可能会被检测为作弊。 +config.Main.Advanced.language.invaild=无效的语言代码! + +# Signature +config.Signature=聊天签名相关设置(影响1.19及以上版本) +config.Signature.LoginWithSecureProfile=仅微软账户可用。如禁用此项,将无法签名消息和进入某些的服务器。 +config.Signature.SignChat=是否签名发送的聊天消息。 +config.Signature.SignMessageInCommand=是否签名指令中的消息。例如"/msg"和"/me"中的消息。 +config.Signature.MarkLegallySignedMsg=是否使用绿色色块标识拥有合法签名的聊天。 +config.Signature.MarkModifiedMsg=是否使用黄色色块标识被服务器更改过的聊天。 +config.Signature.MarkIllegallySignedMsg=是否使用红色色块标识没有合法签名的聊天。 +config.Signature.MarkSystemMessage=是否使用灰色色块标识系统消息(它们总是不会被签名)。 +config.Signature.ShowModifiedChat=设置为 true,显示被服务器修改过的信息;设置为 false,显示经过签名的原始信息。 +config.Signature.ShowIllegalSignedChat=是否显示没有被正确签名的聊天消息。 + +# Logging +config.Logging=此项设置仅会影响到控制台中的信息(日志)。 +config.Logging.DebugMessages=请在提交错误报告之前先启用此项。谢谢! +config.Logging.ChatMessages=是否显示来自服务器的聊天消息。 +config.Logging.InfoMessages=信息性的消息。(大部分来自MCC内部) +config.Logging.WarningMessages=显示警告消息。 +config.Logging.ErrorMessages=显示错误消息。 +config.Logging.ChatFilter=过滤聊天消息所用的正则表达式。 +config.Logging.DebugFilter=过滤调试消息所用的正则表达式。 +config.Logging.FilterMode=过滤方式:"disable"(禁用),"blacklist"(隐藏匹配的消息) 或 "whitelist"(仅显示匹配的消息) +config.Logging.LogToFile=是否将日志信息写入到文件。 +config.Logging.LogFile=日志文件名称。 +config.Logging.PrependTimestamp=写入日志文件时是否添加时间戳。 +config.Logging.SaveColorCodes=是否保留消息中的颜色字符。(例如"§b") + +# AppVars +config.AppVars.Variables=可以在某些字段中以"%yourvar%"的形式使用。\n# %username% 和 %serverip% 时保留的变量名。 + +# Proxy +config.Proxy=通过代理连接到服务器。\n# 如果Mojang/微软登录服务被防火墙阻断,设置Enabled_Login=true以使用代理进行登录。\n# 如果到Minecraft游戏服务器的连接被防火墙阻止,设置Enabled_Ingame=true以使用代理连接游戏服务器。\n# /!\ 在启用代理前,请确保你的服务器规则允许使用代理或VPN,否则你可能面临被封禁等风险! +config.Proxy.Enabled_Login=是否使用代理连接Mojang或微软的登录服务器。 +config.Proxy.Enabled_Ingame=是否通过代理连接Minecraft游戏服务器。 +config.Proxy.Server=代理服务器必须允许HTTPS登录。 +config.Proxy.Proxy_Type=支持的代理类型:"HTTP","SOCKS4","SOCKS4a","SOCKS5"。 +config.Proxy.Username=只有连接到受密码保护的代理才需要。 +config.Proxy.Password=只有连接到受密码保护的代理才需要。 + +# ChatFormat +config.ChatFormat=MCC会尽力检测聊天信息,但有些服务器有不寻常的聊天格式\n# 当这种情况发生时,你需要在下面自定义匹配聊天所用的正则表达式,详见 https://mccteam.github.io/guide/configuration.html#chat-format +config.ChatFormat.Builtins=是否启用MCC内置的聊天检测规则。设置为 false 以避免与自定义格式冲突。 +config.ChatFormat.UserDefined=是否启用下方的自定义正则表达式进行聊天检测。 + +# MCSettings +config.MCSettings=下面的设置将会被发送到游戏服务器,只影响一些服务器端的东西,比如你的皮肤。 +config.MCSettings.Enabled=如果禁用,下面的设置就不会被发送到服务器上。 +config.MCSettings.Locale=请使用Minecraft的语言代码填写,详见[Main.Advanced.Language] +config.MCSettings.RenderDistance=渲染距离,取值范围[0 - 255]。 +config.MCSettings.Difficulty=Minecraft 1.7及更早版本难度。"peaceful","easy","normal","difficult"。 +config.MCSettings.ChatMode=使用 "enabled"(完全启用聊天)、"commands"(仅限命令)或 "disabled"(完全禁用聊天)。这允许你禁言自己... +config.MCSettings.ChatColors=这允许你在服务器端禁用聊天颜色。 +config.MCSettings.MainHand=在1.9及更高版本中的主手设置。"left"(左手) 或 "right"(右手)。 + +# ChatBot +config.ChatBot================================ #\n# Minecraft Console Client Bots #\n# =============================== # + +# ChatBot.Alerts +config.ChatBot.Alerts=当检测到特定聊天消息或特定事件发生时提醒你\n # 对检测特定玩家的聊天消息很有用。 +config.ChatBot.Alerts.Beep_Enabled=除了高亮显示外,当检测到一个词时还会播放类似蜂鸣器的哔哔声。 +config.ChatBot.Alerts.Trigger_By_Words=在收到指定的关键词后触发提醒。 +config.ChatBot.Alerts.Trigger_By_Rain=在开始下雨和停止下雨时触发提醒。 +config.ChatBot.Alerts.Trigger_By_Thunderstorm=在雷暴天气的开始与结束触发提醒。 +config.ChatBot.Alerts.Matches=触发提醒的聊天关键词列表。 +config.ChatBot.Alerts.Excludes=出现这些关键词后该条消息一定不触发提醒。 +config.ChatBot.Alerts.Log_To_File=是否将提醒消息写入到日志文件。 +config.ChatBot.Alerts.Log_File=日志文件的路径。 + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk=定期发送命令,或让机器人随机走动,以避免检测到挂机后被踢出服务器\n # /!\启用前请确保你的服务器规则不禁止反AFK机制!\n# /!\如果启用随机移动,请将机器人围在围栏里,以防走失!(建议尺寸5x5x5) +config.ChatBot.AntiAfk.Delay=执行操作的间隔时间。(秒) +config.ChatBot.AntiAfk.Command=发送给服务器的指令。 +config.ChatBot.AntiAfk.Use_Sneak=在发送命令时是否蹲下。 +config.ChatBot.AntiAfk.Use_Terrain_Handling=启用地形处理,以使机器人能够四处移动。 +config.ChatBot.AntiAfk.Walk_Range=机器人可以随机移动的范围(注意:范围越大,速度越慢) +config.ChatBot.AntiAfk.Walk_Retries=尝试移动失败几次后在改为发送命令模式。 + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack=自动攻击周围的生物\n# 使用此功能之前,你需要开启实体处理。\n# /!\确保你的服务器允许使用自动攻击。\n# /!\服务器插件可能会认为此功能时作弊,并可能会封禁你的账号,所以请自己检查服务器规则是否允许。 +config.ChatBot.AutoAttack.Mode="single"(单目标) 或 "multi"(多目标)。一次攻击一个生物还是范围内的所有生物。 +config.ChatBot.AutoAttack.Priority="health"(生命值)或 "distance"(距离)。当使用"single"模式时,以哪一个属性确定优先级。 +config.ChatBot.AutoAttack.Cooldown_Time=每次攻击间的冷却时间,设置 "Custom = false" 以让MCC自动计算攻击速度。 +config.ChatBot.AutoAttack.Interaction=可选项:"Interact"(交互),"Attack"(攻击) 或 "InteractAt"(交互并攻击) +config.ChatBot.AutoAttack.Attack_Hostile=是否攻击敌对生物。 +config.ChatBot.AutoAttack.Attack_Passive=是否攻击被动生物。 +config.ChatBot.AutoAttack.List_Mode=将实体列表作为 "whitelist"(白名单)还是 "blacklist"(黑名单)。 +config.ChatBot.AutoAttack.Entites_List=所有可用的实体类型可以在这里找到:https://bit.ly/3Rg68lp + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft=自动使用背包中的物品进行合成。\n# 请看 https://mccteam.github.io/guide/chat-bots.html#auto-craft\n# 你需要启用库存处理来使用这个功能\n# 如果需要使用工作台,你还需要启用地形处理。 +config.ChatBot.AutoCraft.CraftingTable=如果你打算使用工作台,请填写它所在的位置。(需要启用地形处理) +config.ChatBot.AutoCraft.OnFailure=合成失败时应该怎么处理,"abort"(中止)或 "wait"(等待)。 +config.ChatBot.AutoCraft.Recipes=Recipes.Name:给该配方起一个独一无二的名字。(不能包含空白字符)Recipes.Type:合成类型,"player"(背包2x2)或 "table"(工作台3x3)\n# Recipes.Result:合成的目标物品\n# Recipes.Slots:合成的物品摆放方式,以从左到右、从上到下的格式填写。需留空请填写"Null"。\n# 最新的物品命名请看:https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs + +# AutoDig +config.ChatBot.AutoDig=自动挖掘方块。\n# 你可以使用 "/digbot start" 和 "/digbot stop" 指令来控制 AutoDig 的启停。\n# 由于MCC目前还不支持精确计算方块的碰撞体积,在获取看向的方块时,视线上所有的方块都被看作是完整的立方体。\n# 查询方块的名字,请访问 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs +config.ChatBot.AutoDig.Auto_Tool_Switch=自动切换到合适的工具。 +config.ChatBot.AutoDig.Durability_Limit=不会使用低于此耐久度的工具。(需要启用库存处理) +config.ChatBot.AutoDig.Drop_Low_Durability_Tools=在当前使用的工具耐久度过低后,是否丢掉它。 +config.ChatBot.AutoDig.Mode="lookat","fixedpos" 或 "both"。挖掘看向的方块还是固定位置的方块,或者是两个条件都满足的方块。 +config.ChatBot.AutoDig.Locations=使用 "fixedpos" 或 "both" 模式时,方块的坐标。 +config.ChatBot.AutoDig.Location_Order="distance" 或 "index",当使用 "fixedpos" 模式时,按照到玩家的距离,还是列表中的顺序确定挖掘的方块。 +config.ChatBot.AutoDig.Auto_Start_Delay=进入游戏后等待多少秒后开始自动挖掘,设置为-1禁用自动开始。 +config.ChatBot.AutoDig.Dig_Timeout=若挖掘一个方块用时超过这个值,将会重新获取目标进行挖掘。 +config.ChatBot.AutoDig.Log_Block_Dig=是否输出挖掘方块的相关信息。 +config.ChatBot.AutoDig.List_Type=将方块列表作为 "whitelist"(白名单)还是 "blacklist"(黑名单)。 + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop=自动从背包/库存中丢弃指定的物品\n# 你需要启用库存处理来使用这个功能。\n# 可用物品请看 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs +config.ChatBot.AutoDrop.Mode="include"(丢弃列表中的物品),"exclude"(丢弃列表外的所有物品) 或 "everything"(丢弃所有物品) + +# ChatBot.AutoEat +config.ChatBot.AutoEat=在饱食度较低是自动在背包中寻找食物食用。\n# 你需要启用库存处理来使用这个功能。 + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing=使用鱼竿自动钓鱼。指南:https://mccteam.github.io/guide/chat-bots.html#auto-fishingn# /!\ 启用前请确保服务器允许自动钓鱼。 +config.ChatBot.AutoFishing.Antidespawn=如果你之前没有启用过这个选项,请保持它为 false 。 +config.ChatBot.AutoFishing.Mainhand=使用主手还是副手拿鱼竿。 +config.ChatBot.AutoFishing.Auto_Start=是否在进入服务器后自动开始钓鱼,禁用此功能后,你需要使用"/usehand"手动使用鱼竿一次。 +config.ChatBot.AutoFishing.Cast_Delay=钓到鱼后多久开始下一次钓鱼(抛竿)。 +config.ChatBot.AutoFishing.Fishing_Delay=进入服务器后多久后开始自动钓鱼。(秒) +config.ChatBot.AutoFishing.Fishing_Timeout=多少秒后没有钓到鱼视为超时。超时后会重新抛竿。 +config.ChatBot.AutoFishing.Durability_Limit=不会使用低于此耐久度的鱼竿(鱼竿耐久度最高为64)。(需要启用库存处理) +config.ChatBot.AutoFishing.Auto_Rod_Switch=在当前鱼竿不可用后自动切换到背包中的其他鱼竿。(需要启用库存处理) +config.ChatBot.AutoFishing.Stationary_Threshold=鱼钩在X轴和Z轴方向上的移动小于这个值将被认为是静止的,过高的阈值会在抛竿途中触发收竿。 +config.ChatBot.AutoFishing.Hook_Threshold=一个“静止”的鱼钩,在Y轴方向上的移动超过这个阈值将被认为钓到了鱼。 +config.ChatBot.AutoFishing.Log_Fish_Bobber=用于调整以上两个阈值,启用后会在收到鱼钩实体移动数据包后打印其坐标变化。 +config.ChatBot.AutoFishing.Enable_Move=这允许玩家在钓到鱼后改变其位置或朝向。(需要启用地形处理) +config.ChatBot.AutoFishing.Movements=会按照 "1->2->3->4->3->2->1->2->..." 的顺序执行。每次可用改变位置、朝向或是都改变。推荐只改变朝向。 + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog=在被服务器断开连接时自动重连,例如服务器重启时。\n# /!\ 谨慎使用Ignore_Kick_Message=true,这会在服务器管理员将你踢出时依然连回! +config.ChatBot.AutoRelog.Delay=重新加入到服务器前的延迟时间。(单位:秒) +config.ChatBot.AutoRelog.Retries=重新登录服务器失败时的重试次数,使用-1表示无限重试。 +config.ChatBot.AutoRelog.Ignore_Kick_Message=当设置为 true 时,将不考虑踢出的信息直接重连。 +config.ChatBot.AutoRelog.Kick_Messages=如果踢出信息与这其中的任何一个字符串匹配,那么将触发自动重连。 + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond=当聊天消息与文件中的规则匹配时,自动执行指定命令。\n# /!\ 服务器管理员可以以任意玩家的身份发送任意消息,记住这一点!\n# 此机器人如果设置的不得当可能会造成刷屏,建议设置一个冷却时间。 +config.ChatBot.AutoRespond.Match_Colors=不要删除文本中的颜色代码(使用§字符的代码)。注意:启用后你的匹配模板也必须包括颜色代码。 + +# ChatBot.ChatLog +config.ChatBot.ChatLog=将聊天信息写入到日志文件中。 + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer=让机器人跟随指定玩家\n# 注意这是一个实验性的功能,目前的寻路速度可能很慢,你可能需要时常等一会机器人来让它跟上你。\n# 你可以调整"Update_Limit",找到最适合你的速度。(注意不要设置的太低,这样可能导致反效果或使MCC卡顿)。\n# /!\ 在使用此功能之前,请先确保服务器规则允许你这样做。 +config.ChatBot.FollowPlayer.Update_Limit=机器人寻路的间隔时间(以秒为单位) +config.ChatBot.FollowPlayer.Stop_At_Distance=如果玩家在该范围内,则视为已经接近玩家了。(防止机器人将玩家推开而产生无限循环) + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame=一个用于演示聊天互动的小游戏。玩家可以一次一个字母地猜出神秘的单词。\n# 你需要正确地使用 ChatFormat,并在 botowners 中添加自己,用/tell start\n# /!\ 这个机器人可能会造成刷屏,如果许多玩家与它互动。 + +# ChatBot.Mailer +config.ChatBot.Mailer=在玩家和服务器之间中继消息,就像一个邮件插件一样。\n# 这个机器人可以在收件人离线时存储消息,并在他们加入服务器时发送消息。\n# /!\ 服务器管理员可以以任意玩家的身份发送任意消息,请记住这一点。 + +# ChatBot.Map +config.ChatBot.Map=允许你将地图渲染成.jpg图片,该图片会被渲染到Rendered_Maps文件夹中。\n# 注意:这个功能目前只对解决使用地图的验证码有用。\n# 如果一些服务器解决验证码的时间很短,请启用Auto_Render_On_Update并准备快速打开该文件。\n# 在linux上,你可以使用FTP来访问生成的文件。 +config.ChatBot.Map.Should_Resize=是否需要调整地图的大小?(默认大小是128x128) +config.ChatBot.Map.Resize_To=将地图调整到什么大小?(注意:大小越大,质量越低) +config.ChatBot.Map.Auto_Render_On_Update=一旦接收到新的地图或已有地图被更新,自动渲染该地图。 +config.ChatBot.Map.Delete_All_On_Unload=在卸载/重新加载地图时删除所有已渲染的地图(退出MCC时不会删除图像) +config.ChatBot.Map.Notify_On_First_Update=当第一次从服务器上收到一张地图时,发送一个通知。 + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger=定期记录当前的玩家列表到文件中。 +config.ChatBot.PlayerListLogger.Delay=(单位:秒) + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl=通过游戏中的私聊向机器人发送MCC控制台命令\n# 你需要先配置好[ChatFormat]章节的设置,并在[Main.Advanced.bot_owners]中添加自己的账号。\n# /!\ 服务器管理员可以以任意玩家的身份发送任意消息,仅在信任他们时启用本功能。 + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture=使用"/replay start"开始记录游戏,并在之后使用 Replay Mod (https://www.replaymod.com/) 进行重放。\n# 请注意,由于技术限制,玩家自身不会显示在重放文件中。\n# /!\ 你应该使用"/replay stop"停止记录或者使用"/quit"退出程序,否则回放文件可能会损坏。 +config.ChatBot.ReplayCapture.Backup_Interval=每间隔多少秒自动保存一次回放文件,以秒为单位。使用-1禁用自动保存。 + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler=在加入服务器时、到达特定时间时或以设定的时间间隔执行命令或脚本文件\n# 详细使用方法请查看 https://mccteam.github.io/guide/chat-bots.html#script-scheduler + diff --git a/MinecraftClient/Resources/lang/zh-Hant.ini b/MinecraftClient/Resources/lang/zh-Hant.ini new file mode 100644 index 00000000..4fcc4163 --- /dev/null +++ b/MinecraftClient/Resources/lang/zh-Hant.ini @@ -0,0 +1,883 @@ +[mcc] +# Messages from MCC itself +mcc.help_us_translate=幫助我們翻譯MCC:{0} +mcc.run_with_default_settings=\nMCC正在使用預設配置執行。 +mcc.settings_generated=§c配置檔案 MinecraftClient.ini 已經生成。 +mcc.has_update=§e新版本的MCC已經推出:{0} +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}的聊天簽名金鑰快取仍然有效. +mcc.connecting=正在連線至{0}... +mcc.fetching_key=正在從微軟獲取聊天簽名金鑰。 +mcc.ip=伺服器IP: +mcc.use_version=§8正在執行Minecraft版本{0} (v{1}協議) +mcc.unknown_version=§8未知或不受支援的Minecraft版本{0}。\n正在切換至自動檢測模式。 +mcc.forge=檢查伺服器是否正在執行Forge... +mcc.forgeforce=正在強制啟動Forge支援。 +mcc.resolve=正在解析{0}... +mcc.found=§8已找到伺服器{0}:{1},域名:{2} +mcc.not_found=§8無法執行{0}的SRV解析\n{1}:{2} +mcc.retrieve=正在獲取伺服器資訊... +mcc.restart=正在重啟Minecraft Console Client... +mcc.restart_delay=等待 {0} 秒後重啟... +mcc.server_version=伺服器版本: +mcc.disconnected=未連線至任何伺服器。輸入 '{0}help' 獲得幫助。 +mcc.press_exit=或者敲擊回車退出Minecraft Console Client。 +mcc.version_supported=該版本受到支援\n正在登入... +mcc.single_cmd=§7已傳送命令§8 {0} +mcc.joined=已成功加入伺服器。\n輸入 '{0}quit' 離開伺服器。 +mcc.reconnect=等待5秒 (剩餘{0}次嘗試)... +mcc.disconnect.lost=失去連線。 +mcc.disconnect.server=從伺服器斷開連線: +mcc.disconnect.login=連線失敗: +mcc.link=連結:{0} +mcc.player_dead_respawn=你死了!1秒後自動重生。 +mcc.player_dead=你死了!輸入 '{0}respawn' 重生。 +mcc.server_offline=§8伺服器正處於離線模式。 +mcc.session=檢查會話... +mcc.session_fail=檢查會話失敗 +mcc.server_protocol=§8伺服器的Minecraft版本:{0} (協議v{1}) +mcc.with_forge=, 帶有Forge +mcc.handshake=§8握手成功。 (伺服器ID:{0}) +mcc.realms_available==您可以訪問以下Realms世界 +mcc.realms_join=請使用"realms:<序號>"作為伺服器IP加入Realms世界 +mcc.generator.generating=正在從 {1} 生成 {0} 資訊。 +mcc.generator.done=已完成從 {0} 資訊生成,使用 {1} +mcc.invaild_config=解析配置檔案失敗,輸入 "{0}new" 以生成一個新的配置。 +mcc.gen_new_config=已生成新的配置檔案 "{0}" 。 + +[debug] +# Messages from MCC Debug Mode +debug.color_test=顏色測試:終端應該顯示:{0} +debug.session_cache_ok=§8已成功從磁碟中載入會話資料。 +debug.session_cache_fail=§8無法從磁碟載入快取的會話資料。 +debug.keys_cache_ok=§8已成功從磁碟中載入聊天簽名金鑰。 +debug.keys_cache_fail=§8無法從磁碟中載入聊天簽名金鑰。 +debug.session_id=成功!(會話ID:{0}) +debug.crypto=§8金鑰和雜湊值已生成: +debug.request=§8正在請求{0} + +[error] +# Error messages from MCC +error.ping=ping此IP失敗。 +error.unsupported=無法連線到伺服器:不支援此版本! +error.determine=無法確定伺服器版本。 +error.forgeforce=無法為此Minecraft版本強制啟動Forge支援! +error.login=登入失敗: +error.login.migrated=帳戶已遷移,請使用電子郵件作為使用者名稱。 +error.login.server=登入伺服器不可用。請稍後再試。 +error.login.blocked=使用者名稱/密碼錯誤、IP被禁用或登入次數過多。 +error.login.response=伺服器返回了無效的響應。 +error.login.premium=不是Premium使用者。 +error.login.network=網路錯誤。 +error.login.ssl=SSL錯誤。 +error.login.unknown=未知錯誤。 +error.login.cancel=使用者取消。 +error.login_failed=無法登入到此伺服器。 +error.join=加入伺服器時發生錯誤。 +error.connect=無法連線到此IP。 +error.timeout=連線超時 +error.unexpect_response=§8來自伺服器的意外響應(這是Minecraft伺服器嗎?) +error.invalid_response=§8對握手包的響應無效 +error.invalid_encrypt=§8對StartEncryption資料包的響應無效 +error.version_different=§8伺服器報告的版本與手動設定的版本不同。登入可能無法工作。 +error.no_version_report=§8伺服器未報告其協議版本,自動檢測將不起作用。 +error.connection_timeout=§8嘗試連線到此IP時超時。 +error.forge=§8Forge登入握手未成功完成 +error.forge_encrypt=§8Forge StartEncryption握手未成功完成 +error.setting.argument_syntax={0}:無效語法,應為 --argname=value 或 --section.argname=value +error.http_code=§8接收到伺服器錯誤:{0} +error.auth=§8在重新整理身份驗證時接收到伺服器錯誤:{0} +error.realms.ip_error=無法獲取您Realms世界的伺服器IP +error.realms.access_denied=此Realms世界不存在或訪問被拒絕 +error.realms.server_unavailable=Realms伺服器可能需要一些時間來啟動。請稍後再試。 +error.realms.server_id=Realms伺服器ID無效或未知。 +error.realms.disabled=正在嘗試加入Realms世界,但配置中禁用了Realms支援 +error.missing.argument=缺少引數 {0} +error.usage=使用方法: +error.generator.invalid=該生成器命令用法無效! +error.generator.path=提供的資料路徑無效! (路徑不存在或是輸入錯誤) +error.generator.json=提供的路徑必須指向一個.json格式的檔案! + +[internal command] +# MCC internal help command +icmd.help=help <命令名稱> :顯示有關命令的簡要幫助。 +icmd.unknown=未知命令 '{0}'。請使用 'help' 命令來獲取命令列表。 +icmd.list=help <命令名稱>。可用命令:{0}。在伺服器上獲取幫助,請改用 '{1}send /help'。 +icmd.error=OnInternalCommand: 來自{0}的錯誤{1} + +[exception] +# Exception messages threw by MCC +exception.user_logout=使用者發起的登出應該通過呼叫Disconnect()來完成 +exception.unknown_direction=未知方向 +exception.palette.block=請為此Minecraft版本更新方塊型別處理。詳細請參考 Material.cs +exception.palette.entity=請為此Minecraft版本更新實體型別處理。詳細請參考 EntityType.cs +exception.palette.item=請為此Minecraft版本更新物品型別處理。詳細請參考 ItemType.cs +exception.palette.packet=請為此Minecraft版本更新資料包型別調色盤。詳細請參考 PacketTypePalette.cs +exception.packet_process=無法處理傳入的{0}型別的資料包。(資料包ID:{1},協議:{2},登陸階段:{3},內部異常:{4})。 +exception.version_unsupport=版本{0}的協議未被支援。 +exception.chatbot.init=不應在建構函式中呼叫ChatBot的方法,因為作為API處理程式的模組尚未初始化。請重寫 Initialize() 或 AfterGameJoined() 來執行初始化任務。 +exception.csrunner.invalid_head=提供的指令碼沒有有效的MCCScript頭 + +[chatbot] +# ChatBot API +chatbot.reconnect=[{0}] 斷開並重新連線到伺服器 + +[filemonitor] +# FileMonitor +filemonitor.init=§8[{0}] 正在為檔案{1}初始化FileSystemWatcher +filemonitor.fail=§8[{0}] 無法初始化FileSystemWatcher,正在使用輪詢重試 + +[extra] +# Inventory, Terrain & Movements, Entity related messages +# Terrain & Movements +extra.terrainandmovement_enabled=地形和移動處理現在已啟用。 +extra.terrainandmovement_disabled=§c該遊戲版本目前還不支援地形和移動處理。 +extra.terrainandmovement_required=請先在配置檔案中啟用地形和移動處理。 +# Inventory +extra.inventory_enabled=庫存(物品)處理現在已啟用。 +extra.inventory_disabled=§c該MC版本目前未支援處理庫存(物品)。 +extra.inventory_required=請先在配置檔案中啟用"Main.Advanced.inventory_handling"。 +extra.inventory_interact=請使用 /inventory 來與其互動。 +extra.inventory_open=容器# {0}已開啟:{1} +extra.inventory_close=容器# {0}已關閉。 +# Entity +extra.entity_disabled=§c該MC版本當前還不支援處理實體。 +extra.entity_required=請先在配置檔案中啟用"Main.Advanced.entity_handling"。 + + +[forge] +# Messages from Forge handler +forge.version=§8Forge協議版本:{0} +forge.send_mod=§8向伺服器傳送偽造的forge模組列表... +forge.accept=§8接受來自的伺服器模組列表... +forge.registry=§8已接收的登錄檔包含{0}個條目 +forge.registry_2=§8已接收登錄檔{0},包含{1}個條目 +forge.accept_registry=§8接受伺服器登錄檔... +forge.complete=Forge伺服器連線完成! +forge.with_mod=§8伺服器正在執行Forge,有{0}個模組。 +forge.no_mod=§8正在執行的伺服器沒有Forge模組。 +forge.mod_list=§8模組列表: +# FML2 +forge.fml2.mod=§8收到FM2伺服器模組列表 +forge.fml2.mod_send=§8發回FML2客戶端的模組列表 +forge.fml2.registry=§8確認FML2伺服器登錄檔:{0} +forge.fml2.config=§8確認FML2伺服器配置:{0} +forge.fml2.unknown=§8收到未知的FML2握手資訊,編號:{0} +forge.fml2.unknown_channel=§8忽略未知的FML2登入訊息通道:{0} + +[cache] +# Session Cache +cache.loading=§8載入Minecraft配置檔案:{0} +cache.loaded=§8已載入會話:{0}:{1} +cache.converting=§8從磁碟轉換會話快取:{0} +cache.read_fail=§8無法從磁碟讀取序列化會話快取:{0} +cache.malformed=§8從磁碟讀取序列化會話快取時,獲取到格式錯誤的資料:{0} +cache.loading_session=§8從磁碟載入會話快取:{0} +cache.ignore_string=§8忽略會話令牌字串'{0}':{1} +cache.ignore_line=§8忽略無效的會話令牌行:{0} +cache.read_fail_plain=§8無法從磁碟讀取會話快取:{0} +cache.saving=§8將會話快取儲存到磁碟 +cache.save_fail=§8無法將會話快取寫入磁碟:{0} +# Profile Key Cache +cache.loading_keys=§8從磁碟載入聊天簽名金鑰快取: {0} +cache.loaded_keys=§8已載入簽名金鑰,下次重新整理於 {0} +cache.ignore_string_keys=§8忽略聊天簽名金鑰字串 '{0}':{1} +cache.ignore_line_keys=§8忽略無效的聊天簽名金鑰行:{0} +cache.read_fail_plain_keys=§8無法從磁碟讀取聊天簽名金鑰快取:{0} +cache.saving_keys=§8將聊天簽名金鑰儲存到磁碟 +cache.save_fail_keys=§8無法將聊天簽名金鑰寫入磁碟:{0} + +[proxy] +proxy.connected=§8已連線到代理{0}:{1} + +[chat] +# Chat Parser +chat.download=§8正在從Mojang伺服器下載語言檔案 '{0}.lang'... +chat.request=§8正在請求{0}... +chat.done=§8下載完成。檔案另存為 '{0}' +chat.fail=§8下載檔案失敗。 +chat.from_dir=§8預設為你的Minecraft目錄中的en_GB.lang +chat.loaded=§8已載入翻譯檔案。 +chat.not_found=§8找不到翻譯檔案:'{0}'\n如果沒有此檔案,某些資訊將無法正確列印。 +chat.message_chain_broken=玩家 {0} 的訊息簽名鏈已經被破壞。(簽名不在可信) + +[general] +# General message/information (i.e. Done) +general.done=完成 +general.fail=失敗 +general.bot_unload=將會解除安裝此bot。 +general.available_cmd=可用命令:{0} + + +[cmd] +# Commands. Naming style: cmd.. + +# Animation +cmd.animation.desc=揮動你的手臂。 + +# Bots +cmd.bots.desc=列出全部 ChatBot ,載入或解除安裝一個 ChatBot。 +cmd.bots.list=已載入的 ChatBot +cmd.bots.notfound=該 ChatBot 並未載入,請檢查輸入。 +cmd.bots.noloaded=沒有 ChatBot 被載入。 +cmd.bots.unloaded=成功解除安裝 ChatBot:{0} +cmd.bots.unloaded_all=成功解除安裝所有 ChatBot。 + +# ChangeSlot +cmd.changeSlot.desc=變更快捷欄欄位 +cmd.changeSlot.nan=無法變更欄位:不是數字 +cmd.changeSlot.changed=已變更為欄位{0} +cmd.changeSlot.fail=無法變更欄位 + +# Chunk +cmd.chunk.desc=顯示區塊載入狀態。\n如果顯示錯亂竟在設定中更改 EnableEmoji=false 。 +cmd.chunk.current=當前位置:{0},所在區塊:({1}, {2})。 +cmd.chunk.marked=標記的位置: +cmd.chunk.chunk_pos=區塊:({0}, {1})。 +cmd.chunk.outside=§x§0由於被標記的區塊距離玩家太遠,它不會被顯示在圖中§r +cmd.chunk.icon=玩家:{0},標記的區塊:{1},未收到:{2},載入中:{3},已載入:{4} +cmd.chunk.for_debug=§x§0此命令僅用於除錯使用,確保你已經瞭解執行該命令會造成的影響。§r + +# Connect +cmd.connect.desc=連線到指定的伺服器。 +cmd.connect.unknown=未知帳戶 '{0}'。 +cmd.connect.invalid_ip=無效的伺服器IP '{0}'。 + +# Debug +cmd.debug.desc=切換除錯訊息。 +cmd.debug.state_on=除錯訊息現在已開啟 +cmd.debug.state_off=除錯訊息現在已關閉 + +# Dig +cmd.dig.desc=試圖破壞一個方塊 +cmd.dig.too_far=你離這個方塊太遠了。 +cmd.dig.no_block=這個地方沒有方塊 (空氣) +cmd.dig.dig=嘗試挖掘位於({0}, {1}, {2})的方塊({3})。 +cmd.dig.fail=無法開始挖掘方塊。 + +# Entitycmd +cmd.entityCmd.attacked=已攻擊實體 +cmd.entityCmd.used=已使用實體 +cmd.entityCmd.not_found=找不到實體 + +cmd.entityCmd.entity=實體 +cmd.entityCmd.entities=實體集 +cmd.entityCmd.nickname=暱稱 +cmd.entityCmd.customname=自定義名稱 +cmd.entityCmd.latency=延遲 +cmd.entityCmd.item=物品 +cmd.entityCmd.equipment=裝備 +cmd.entityCmd.mainhand=主手 +cmd.entityCmd.offhane=副手 +cmd.entityCmd.helmet=頭盔 +cmd.entityCmd.chestplate=胸甲 +cmd.entityCmd.leggings=護腿 +cmd.entityCmd.boots=靴子 +cmd.entityCmd.pose=姿勢 +cmd.entityCmd.health=生命值 +cmd.entityCmd.distance=距離 +cmd.entityCmd.location=位置 +cmd.entityCmd.type=型別 + +# Exec If +cmd.execif.desc=允許你在某個條件成立時執行一個命令。(你可以使用"MinecraftClient.ini"中的變數和使用"/set"命令定義的變數,以及CSharp表示式)。 +cmd.execif.executed=條件'{0}'滿足,已執行命令'{1}',得到結果'{2}'。 +cmd.execif.executed_no_output=條件'{0}'滿足,已執行命令'{1}',該命令沒有返回任何結果。 +cmd.execif.error_occured=在執行命令 {0} 時出現錯誤。 +cmd.execif.error=錯誤:{0} + +# Exec Multi +cmd.execmulti.desc=依次執行多個命令。 +cmd.execmulti.executed=執行了命令 '{0}' , +cmd.execmulti.result=結果為 '{0}'! +cmd.execmulti.no_result=沒有返回結果! + +# Exit +cmd.exit.desc=斷開與伺服器的連線。 + +# Health +cmd.health.desc=顯示生命值和飽食度。 +cmd.health.response=生命值:{0},飽食度:{1},等級:{2},總經驗值:{3} + +# Inventory +cmd.inventory.desc=容器相關命令 +cmd.inventory.creative_done=向容器#{2}請求{0} x{1} +cmd.inventory.creative_delete=請求清除欄位 #{0} +cmd.inventory.creative_fail=請求創造模式操作失敗 +cmd.inventory.need_creative=你必須在創造模式 +cmd.inventory.container_not_found=找不到容器,請使用顯式ID重試 +cmd.inventory.close=關閉容器 #{0} +cmd.inventory.close_fail=關閉容器失敗 #{0} +cmd.inventory.not_exist=容器#{0}不存在 +cmd.inventory.inventory=容器 +cmd.inventory.inventories=容器 +cmd.inventory.inventories_available=可用容器 +cmd.inventory.hotbar=您選擇的快捷欄是{0} +cmd.inventory.damage=武器傷害值 +cmd.inventory.left=左 +cmd.inventory.right=右 +cmd.inventory.middle=中間 +cmd.inventory.clicking={0}正在點選容器#{2}中的欄位{1} +cmd.inventory.shiftclick=按住Shift鍵點選容器#{1}中的欄位{0} +cmd.inventory.shiftclick_fail=執行Shift點選失敗,這可能是因為該容器型別目前不被支援。 +cmd.inventory.no_item=欄位#{0}中沒有物品 +cmd.inventory.drop=從欄位#{0}中丟棄了1個物品 +cmd.inventory.drop_stack=從欄位#{0}中丟棄了所有堆疊的物品 +# Inventory Help +cmd.inventory.help.basic=基本用法 +cmd.inventory.help.available=可用操作 +cmd.inventory.help.help={0}\n使用 '/inventory help ' 獲取幫助。\n'player' 和 'container' 可以簡化為 'p' 和 'c'。\n請注意,'[]' 中的引數是可選的。 +cmd.inventory.help.usage=用法 +cmd.inventory.help.list=列出所有容器。 +cmd.inventory.help.close=關閉開啟的容器。 +cmd.inventory.help.click=單擊物品。 +cmd.inventory.help.shiftclick=按住Shift鍵點選一個物品。 +cmd.inventory.help.drop=從容器中丟棄物品。 +cmd.inventory.help.creativegive=在創造模式中給予物品。 +cmd.inventory.help.creativedelete=在創造性模式中清除欄位。 +cmd.inventory.help.inventories=列出所有可用的視窗。 +cmd.inventory.help.search=在開啟的所有視窗中搜索物品。 +cmd.inventory.help.unknown=未知操作。 +cmd.inventory.found_items=找到物品 +cmd.inventory.no_found_items=在任何視窗中都沒有找到該物品! + +# Leave bed +cmd.bed.desc=用於右鍵床開始睡覺或離開正在睡覺的床。 +cmd.bed.leaving=已向伺服器傳送離開床的資料包。 +cmd.bed.trying_to_use=試圖在位於 (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) 上的床上睡覺,結果:{3} +cmd.bed.in=成功地躺在了床上! +cmd.bed.not_in=上床睡覺失敗了。(PS: 你必須使用床頭對應的座標) +cmd.bed.not_a_bed=位於 (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) 的方塊不是一個床! +cmd.bed.searching=在半徑為{0}的範圍內尋找床... +cmd.bed.bed_not_found=沒有找到床! +cmd.bed.found_a_bed_at=在 (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) 找到了一個床。 +cmd.bed.cant_reach_safely=無法安全地到達床邊! +cmd.bed.moving=正在移動到床所在的位置: (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}) +cmd.bed.failed_to_reach_in_time=無法在30秒內到達(X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}),放棄本次尋路。 + +# List +cmd.list.desc=獲取玩家列表。 +cmd.list.players=玩家列表:{0} + +# Log +cmd.log.desc=將文字記錄到控制檯。 + +# Look +cmd.look.desc=檢視方向或座標。 +cmd.look.unknown=未知方向 '{0}'。 +cmd.look.at=當前視角 偏航角:{0} 俯仰角:{1}。 +cmd.look.block=正看向位於 {0} 的方塊。 +cmd.look.inspection=與視線相交的第一個方塊是 {0} ({1:0}, {2:0}, {3:0}),距離玩家 {4:0.0}({5:0.0})。 +cmd.look.noinspection=在 {0} 米內沒有任何方塊與視線相交。 + +# Move +cmd.move.desc=移動或開始移動。 +cmd.move.enable=在下次伺服器登入、重生或更換世界時啟用地形和移動。 +cmd.move.disable=禁用地形和移動。 +cmd.move.moving=移動{0} +cmd.move.dir_fail=不能朝此方向移動。 +cmd.move.walk=移動到{0} +cmd.move.fail=無法計算到達{0}的路徑。 +cmd.move.suggestforce=無法計算安全到達{0}的路徑. 請使用 -f 引數允許不安全移動。 +cmd.move.gravity.enabled=重力已開啟。 +cmd.move.gravity.disabled=重力已關閉。 +cmd.move.chunk_loading_status=區塊載入進度:{0:P} - 共{2}個,載入完成了{1}個。 +cmd.move.chunk_not_loaded=目標位置所在的區塊還沒有被載入。你可以使用"/chunk status {0:0.0} {1:0.0} {2:0.0}"來檢視區塊的載入狀態。 + +# Reco +cmd.reco.desc=重新啟動並重新連線到伺服器。 + +# Reload +cmd.reload.started=重新載入設定中... +cmd.reload.warning1=這條命令不會影響在連線到伺服器之前的某些設定。 +cmd.reload.warning2=一些通過命令列引數傳遞的設定可能會被覆蓋! +cmd.reload.warning3=你有可能需要重新連線(/reco)才能使某些設定生效。 +cmd.reload.warning4=由於技術限制,ReplayCapturer 將不會被強制過載! +cmd.reload.finished=重新載入設定完成! +cmd.reload.desc=重新載入MCC的設定。 + +# Respawn +cmd.respawn.desc=如果你死亡了,請用這個來重生。 +cmd.respawn.done=你重生了。 + +# Script +cmd.script.desc=執行指令碼檔案。 + +# Send +cmd.send.desc=傳送聊天資訊或命令。 + +# Set +cmd.set.desc=設定自定義 %variable%。 +cmd.set.format=變數名範圍必須為 A-Za-z0-9。 + +# SetRnd +cmd.setrnd.desc=隨機為自定義 %變數名% 賦值。 +cmd.setrndnum.format=setrnd 變數名 -7to17 +cmd.setrndstr.format=setrnd 變數名 字串1 "\"字串2\" 字串3" + +# Sneak +cmd.sneak.desc=切換到潛行 +cmd.sneak.on=現在你在潛行狀態。 +cmd.sneak.off=你不在潛行狀態了。 + +# DropItem +cmd.dropItem.desc=丟棄玩家容器中的指定型別物品或開啟的容器 +cmd.dropItem.dropped=已從容器#{1}中丟棄所有{0} +cmd.dropItem.unknown_item=未知物品:{0} + +# Tps +cmd.tps.desc=顯示伺服器當前tps (tick per second)。(可能不精確) +cmd.tps.current=當前TPS + +# Useblock +cmd.useblock.desc=放置一個方塊或開啟箱子 +cmd.useblock.use=使用位於 ({0:0.0}, {1:0.0}, {2:0.0}) 的 {3}。 + +# Useitem +cmd.useitem.desc=使用(左鍵單擊)手上的物品 +cmd.useitem.use=使用了一個物品。 + + +[bot] +# ChatBots. Naming style: bot.. + +# Alerts +bot.alerts.start_rain=§c天氣變化:開始下雨了。§r +bot.alerts.end_rain=§c天氣變化:雨停了。§r +bot.alerts.start_thunderstorm=§c天氣變化:現在是雷雨天。§r +bot.alerts.end_thunderstorm=§c天氣變化:現在不再是雷雨天了。§r + +# Anti AFK +bot.antiafk.not_using_terrain_handling=當前未啟用地形處理,如果你想使用相關的特性,請在設定中啟用它。當前將繼續使用替代方法執行(定時傳送命令)。 +bot.antiafk.swapping=最短時間大於最長時間,已將它們交換。 +bot.antiafk.invalid_walk_range=當前提供的行走範圍無效,它必須是一個大於0的正整數,將使用預設值5! + +# AutoAttack +bot.autoAttack.invalidcooldown=攻擊冷卻時間值不能小於0,已使用自動作為預設值 + +# AutoCraft +bot.autoCraft.cmd=自動製作ChatBot命令 +bot.autoCraft.alias=自動製作ChatBot命令的別名 +bot.autoCraft.cmd.list=已載入{0}個配方:{1} +bot.autoCraft.cmd.resetcfg=將配置重置為預設值 +bot.autoCraft.recipe_not_exist=指定的配方名稱不存在。請檢查配置檔案。 +bot.autoCraft.no_recipe_name=請指定要製作的配方名稱。 +bot.autoCraft.stop=AutoCraft已停止 +bot.autoCraft.available_cmd=可用命令:{0}。可使用 /autocraft help <命令名> 瞭解更多資訊。您可以使用 /ac 作為命令別名。 +bot.autoCraft.help.load=載入配置檔案。 +bot.autoCraft.help.list=列出可用配方。 +bot.autoCraft.help.reload=重新載入配置檔案。 +bot.autoCraft.help.resetcfg=將預設示例配置寫入預設位置。 +bot.autoCraft.help.start=開始製作。用法:/autocraft start <配方名稱> +bot.autoCraft.help.stop=停止當前正在進行的製作過程 +bot.autoCraft.help.help=獲取命令描述。用法: /autocraft help <命令名> +bot.autoCraft.loaded=已成功載入 +bot.autoCraft.start=AutoCraft啟動中:{0} +bot.autoCraft.start_fail=無法啟動AutoCraft。請檢查用於製作{0}的可用材料 +bot.autoCraft.table_not_found=找不到工作臺 +bot.autoCraft.close_inventory=容器#{0}被AutoCraft關閉 +bot.autoCraft.missing_material=缺失材料:{0} +bot.autoCraft.aborted=製作被終止!請檢查可用材料。 +bot.autoCraft.craft_fail=製作失敗!等待更多材料 +bot.autoCraft.timeout=動作超時!原因:{0} +bot.autoCraft.error.config=分析配置時出錯:{0} +bot.autoCraft.exception.name_miss=解析配方時缺少配方名稱 +bot.autoCraft.exception.duplicate=指定了重複的配方名稱:{0} +bot.autoCraft.debug.no_config=找不到配置。請新建一個。 +bot.autocraft.invaild_slots=配方的物品數量不匹配,已自動調整。 +bot.autocraft.invaild_invaild_result=無效的輸出物品! + +# AutoDig +bot.autodig.start_delay=將在 {0:0.0} 秒後開始自動挖掘。 +bot.autodig.dig_timeout=挖掘方塊超時,重試。 +bot.autodig.not_allow=當前所看向的方塊不在允許挖掘列表中。 +bot.autodig.cmd=自動挖掘 ChatBot 命令 +bot.autodig.available_cmd=可用命令:{0}。可使用 /digbot help <命令名> 瞭解更多資訊。 +bot.autodig.start=開始自動挖掘。 +bot.autodig.stop=停止自動挖掘。 +bot.autodig.help.start=開始執行自動挖掘。 +bot.autodig.help.stop=停止執行自動挖掘。 +bot.autodig.help.help=獲取命令描述。用法:/digbot help <命令名> + +# AutoDrop +bot.autoDrop.cmd=AutoDrop ChatBot命令 +bot.autoDrop.alias=AutoDrop ChatBot命令別名 +bot.autoDrop.on=已啟用AutoDrop +bot.autoDrop.off=已禁用AutoDrop +bot.autoDrop.added=已新增物品{0} +bot.autoDrop.incorrect_name=物品名稱不正確:{0}。請再試一次。 +bot.autoDrop.removed=已刪除物品{0} +bot.autoDrop.not_in_list=物品不在列表中 +bot.autoDrop.no_item=列表中沒有物品 +bot.autoDrop.list=列表中總計{0}個物品:\n {1} +bot.autoDrop.switched= 切換到{0}模式。 +bot.autoDrop.unknown_mode=未知模式。可用的模式:Include, Exclude, Everything +bot.autoDrop.no_mode=無法從配置中讀取丟棄模式(drop mode)。使用Include模式。 +bot.autoDrop.no_inventory=找不到容器{0}! + +# AutoFish +bot.autoFish.no_inv_handle=未啟用庫存(物品)處理。將不支援檢查魚竿耐久度都和自動切換魚竿。 +bot.autoFish.start=將在 {0:0.0} 秒後自動開始釣魚。 +bot.autoFish.throw=拋竿成功。 +bot.autoFish.caught=釣到了一條魚!(總計 {0} 條) +bot.autoFish.caught_at=在 ({0:0.0},{1:0.0},{2:0.0}) 掉到了一條魚!(總計 {0} 條) +bot.autoFish.no_rod=沒有可使用的釣魚竿了。也許是用壞了或耐久度過低? +bot.autoFish.despawn=魚鉤消失,將會重新拋竿。 +bot.autoFish.fishing_timeout=釣魚超時,將於幾秒鐘後重新拋竿。 +bot.autoFish.cast_timeout=拋竿超時,將在等待一段時間後重試。(超時時間延長至 {0:0.0} 秒) +bot.autoFish.update_lookat=更新當前朝向 偏航角(yaw) = {0:0.00}, 俯仰角(pitch) = {1:0.00}。 +bot.autoFish.switch=切換到位於揹包 {0} 位置的魚竿,剩餘耐用 {1}/64。 + +# AutoRelog +bot.autoRelog.launch=已啟動,嘗試了{0}次重新連線 +bot.autoRelog.no_kick_msg=在沒有kick訊息檔案的情況下初始化 +bot.autoRelog.loading=從檔案{0}載入訊息 +bot.autoRelog.loaded=已載入訊息:{0} +bot.autoRelog.not_found=找不到檔案或目錄:{0} +bot.autoRelog.curr_dir=當前目錄為:{0} +bot.autoRelog.ignore_user_logout=由使用者或MCC bot啟動的斷開連線。忽略。 +bot.autoRelog.disconnect_msg=連線斷開,收到訊息:{0} +bot.autoRelog.reconnect_always=忽略kick訊息,仍要重新連線。 +bot.autoRelog.reconnect=資訊包含 '{0}'。重新連線。 +bot.autoRelog.reconnect_ignore=不包含任何已定義關鍵字的訊息,忽略。 +bot.autoRelog.wait=等待{0}秒後重新連線... + +# AutoRespond +bot.autoRespond.loading=正在從'{0}'載入匹配項 +bot.autoRespond.file_not_found=找不到檔案或目錄: '{0}' +bot.autoRespond.loaded_match=載入匹配項:\n{0} +bot.autoRespond.no_trigger=這個匹配永遠不會觸發:\n{0} +bot.autoRespond.no_action=匹配沒有對應操作:\n{0} +bot.autoRespond.match_run=進行操作:{0} +bot.autoRespond.match=match: {0}\nregex: {1}\naction: {2}\nactionPrivate: {3}\nactionOther: {4}\nownersOnly: {5}\ncooldown: {6} + +# ChatLog +bot.chatLog.invalid_file=路徑'{0}'包含無效字元。 + +# Mailer +bot.mailer.init=使用設定初始化Mailer: +bot.mailer.init.db= - 資料庫檔案:{0} +bot.mailer.init.ignore= - 忽略列表:{0} +bot.mailer.init.public= - 公開互動:{0} +bot.mailer.init.max_mails= - 每位玩家最多郵件數:{0} +bot.mailer.init.db_size= - 最大資料庫大小:{0} +bot.mailer.init.mail_retention= - 郵件保留天數:{0} + +bot.mailer.init_fail.db_size=無法啟用Mailer:最大資料庫大小必須大於0。請檢查設定。 +bot.mailer.init_fail.max_mails=無法啟用Mailer:每個玩家的最大郵件數必須大於0。請檢查設定。 +bot.mailer.init_fail.mail_retention=無法啟用Mailer:郵件保留天數必須大於0。請檢查設定。 + +bot.mailer.create.db=建立新資料庫檔案:{0} +bot.mailer.create.ignore=建立新忽略列表:{0} +bot.mailer.load.db=載入資料庫檔案:{0} +bot.mailer.load.ignore=載入忽略列表: + +bot.mailer.cmd=Mailer 命令 + +bot.mailer.saving=正在儲存郵件:{0} +bot.mailer.user_ignored={0}已被忽略! +bot.mailer.process_mails=正在查詢要傳送的郵件 @ {0} +bot.mailer.delivered=已傳送:{0} + +bot.mailer.cmd.getmails=--- 資料庫中的郵件 ---\n{0} +bot.mailer.cmd.getignored=--- 忽略列表 ---\n{0} +bot.mailer.cmd.ignore.added=新增{0}到忽略列表! +bot.mailer.cmd.ignore.removed={0}已從忽略列表中刪除! +bot.mailer.cmd.ignore.invalid=丟失或無效的名稱。用法:{0}<使用者名稱> +bot.mailer.cmd.help=檢視用法 + +# Maps +bot.map.cmd.desc=渲染物品形式的地圖 +bot.map.cmd.not_found=沒有找到編號為 '{0}' 的地圖! +bot.map.cmd.invalid_id=無效的地圖編號,必須是一個數字。 +bot.map.received=從伺服器接收到的地圖有: +bot.map.no_maps=沒有收到過地圖! +bot.map.received_map=收到一個編號為 {0} 的新地圖。 +bot.map.rendered=成功接收到地圖 '{0}' ,儲存為 '{1}' +bot.map.failed_to_render=無法渲染編號為 '{0}' 的地圖。 +bot.map.list_item=- 地圖編號:{0}(最近更新於:{1}) +bot.map.windows_only=儲存地圖到檔案功能目前僅可用於Windows平臺。 + +# ReplayCapture +bot.replayCapture.cmd=replay 命令 +bot.replayCapture.created=已建立重播檔案。 +bot.replayCapture.stopped=錄製已停止。 +bot.replayCapture.restart=錄製已停止。請重新啟動程式以進行另一段錄製。 + +# Follow player +cmd.follow.desc=讓機器人跟隨指定的玩家 +cmd.follow.usage=follow [-f] (使用 -f 允許機器人途徑不安全的地方) +cmd.follow.already_stopped=已經停止過了 +cmd.follow.stopping=已停止! +cmd.follow.invalid_name=提供的玩家名無效! +cmd.follow.invalid_player=指定的玩家沒有上線或距離太遠! +cmd.follow.cant_reach_player=無法尋路到該玩家,有可能他在沒有載入的區塊中,或是距離太遠,也有可能是間隙或水體等障礙使機器人無法到達。 +cmd.follow.already_following=已經在跟隨 {0} 了! +cmd.follow.switched=切換為跟隨 {0}! +cmd.follow.started=開始跟隨 {0}! +cmd.follow.unsafe_enabled=啟用了允許途徑不安全位置(注意:機器人可能會受傷或死亡!) +cmd.follow.note=注意:此機器人的速度很慢,你需要慢慢地走,而且要保持很近的距離,這樣它才能跟上,有點像拿著食物讓動物跟著你。這是由於尋路演算法的限制,我們正在努力改進它。 +cmd.follow.player_came_to_the_range=玩家 {0} 回到了可尋路範圍之內! +cmd.follow.resuming=繼續跟隨! +cmd.follow.player_left_the_range=玩家 {0} 離開了可尋路範圍! +cmd.follow.pausing=已暫停! +cmd.follow.player_left=玩家 {0} 離開了伺服器! +cmd.follow.stopping=已停止! + +# Script +bot.script.not_found=§8[MCC] [{0}] 找不到指令碼檔案:{1} +bot.script.file_not_found=找不到檔案:'{0}' +bot.script.fail=指令碼'{0}'執行失敗 ({1})。 +bot.script.pm.loaded=指令碼'{0}'載入。 + +# ScriptScheduler +bot.scriptScheduler.loaded_task=已載入任務:\n{0} +bot.scriptScheduler.no_trigger=這個任務永遠不會觸發:\n{0} +bot.scriptScheduler.no_action=任務沒有對應操作:\n{0} +bot.scriptScheduler.running_time=時間 / 執行中的操作:{0} +bot.scriptScheduler.running_inverval=間隔 / 執行中的操作:{0} +bot.scriptScheduler.running_login=登入 / 執行中的操作:{0} +bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n triggerontime: {2}\n triggeroninterval: {3}\n timevalue: {4}\n timeinterval: {5}\n action: {6} + +# TestBot +bot.testBot.told=Bot:{0}對我說:{1} +bot.testBot.said=Bot:{0}說:{1} + + +[config] + +config.load=已從 {0} 載入設定。 +config.load.fail=§c載入設定時出錯:§r +config.write.fail=§儲存備份檔案({0})時出錯:§r +config.backup.fail=§c寫入設定檔案({0})時出錯:§r +config.saving=§a當前設定已儲存至 {0} + +# Head +config.Head=啟動配置檔案\n\n# 對 MCC(Minecraft 命令列客戶端)不熟悉?請看這個文件:https://mccteam.github.io/guide/configuration.html\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(手動在網頁上登入)。 + +# Main.Advanced +config.Main.Advanced=在更改這裏的某項設定之前,請確保你理解了該選項的影響。 +config.Main.Advanced.language=請使用Minecraft的語言程式碼填寫,詳見 https://github.com/MCCTeam/Minecraft-Console-Client/discussions/2239 +config.Main.Advanced.internal_cmd_char=MCC內部命令的字首,可使用 "none", "slash"(/) 或 "backslash"(\)。 +config.Main.Advanced.message_cooldown=控制向伺服器傳送訊息的最小間隔時間(秒)。 +config.Main.Advanced.bot_owners=設定機器人的所有者。/!\伺服器管理員可以偽裝成任何玩家! +config.Main.Advanced.mc_version=遊戲版本,可使用 "auto"(自動) 或類似 "1.X.X" 的值。設定具體版本將跳過從伺服器解析的過程。 +config.Main.Advanced.mc_forge=可使用 "auto"(自動),"no"(禁用) 或是 "force"(強制啟用,僅在 1.13 及更高的版本中可用)。 +config.Main.Advanced.brand_info=客戶端標識,可用 "mcc","vanilla"(原版客戶端) 或 "none"(空標識)。這用於改變MCC向伺服器傳送的客戶端標識內容。 +config.Main.Advanced.chatbot_log_file=留空將禁用 ChatBot 寫入日誌檔案。 +config.Main.Advanced.private_msgs_cmd_name=遠端控制功能將會使用它。 +config.Main.Advanced.show_system_messages=顯示遊戲伺服器的系統訊息(來自管理員或命令方塊等)。 +config.Main.Advanced.show_xpbar_messages=顯示經驗條上方的訊息,如果被此類訊息刷屏請禁用此選項。 +config.Main.Advanced.show_chat_links=解碼聊天資訊裡的連結,並在控制檯單獨顯示。 +config.Main.Advanced.show_inventory_layout=以字元畫形式顯示庫存佈局。 +config.Main.Advanced.terrain_and_movements=開啟地形處理將消耗更多的記憶體、CPU和網路頻寬,但這允許你進行移動以及和方塊互動。 +config.Main.Advanced.inventory_handling=啟用庫存處理(可操作揹包、箱子等容器)。 +config.Main.Advanced.entity_handling=啟用實體處理。 +config.Main.Advanced.session_cache=如何快取會話令牌。可使用 "none"(不快取),"memory"(記憶體快取) 或 "disk"(磁碟快取)。 +config.Main.Advanced.profilekey_cache=如何快取聊天簽名金鑰。可使用 "none"(不快取),"memory"(記憶體快取) 或 "disk"(磁碟快取)。 +config.Main.Advanced.resolve_srv_records=可填寫 "no","fast"(超時時間為五秒鐘)或是 "yes"。加入某些伺服器需要開啟此項。 +config.Main.Advanced.account_list=AccountList:使你可以不用輸入賬號資訊而快速在多個賬號間切換\n# 可用命令示例:"/tell reco Player2","/connect Player1" +config.Main.Advanced.server_list=ServerList:可用使用伺服器別名快速連線到該伺服器\n# 別名不能包含空格和小數點",而且 "localhost" 不能作為別名使用。\n# 可用命令示例:"/tell connect Server1","/connect Server2" +config.Main.Advanced.player_head_icon=使用玩家面板頭像作為視窗圖示,這僅在部分舊版控制檯中有效。 +config.Main.Advanced.exit_on_failure=發生錯誤時是否直接退出,用於在非互動式指令碼中使用MCC。 +config.Main.Advanced.script_cache=快取已編譯的指令碼,以便在低端裝置上更快的載入。 +config.Main.Advanced.timestamps=在聊天資訊頭部新增時間戳。 +config.Main.Advanced.auto_respawn=死亡時自動重生(開啟前請確保你的出生點是安全的) +config.Main.Advanced.minecraft_realms=啟用對加入我的世界領域(Realms)伺服器的支援。 +config.Main.Advanced.move_head_while_walking=在移動時轉向頭部。 +config.Main.Advanced.timeout=與伺服器的TCP連線超時時間(秒)。 +config.Main.Advanced.enable_emoji=如果關閉,Emoji表情符號將被替換成更簡單的字元(用於 "/chunk status" 命令) +config.Main.Advanced.movement_speed=高於 2 的移動速度可能會被檢測為作弊。 +config.Main.Advanced.language.invaild=無效的語言程式碼! + +# Signature +config.Signature=聊天簽名相關設定(影響1.19及以上版本) +config.Signature.LoginWithSecureProfile=僅微軟賬戶可用。如禁用此項,將無法簽名訊息和進入某些的伺服器。 +config.Signature.SignChat=是否簽名傳送的聊天訊息。 +config.Signature.SignMessageInCommand=是否簽名指令中的訊息。例如"/msg"和"/me"中的訊息。 +config.Signature.MarkLegallySignedMsg=是否使用綠色色塊標識擁有合法簽名的聊天。 +config.Signature.MarkModifiedMsg=是否使用黃色色塊標識被伺服器更改過的聊天。 +config.Signature.MarkIllegallySignedMsg=是否使用紅色色塊標識沒有合法簽名的聊天。 +config.Signature.MarkSystemMessage=是否使用灰色色塊標識系統訊息(它們總是不會被簽名)。 +config.Signature.ShowModifiedChat=設定為 true,顯示被伺服器修改過的資訊;設定為 false,顯示經過簽名的原始資訊。 +config.Signature.ShowIllegalSignedChat=是否顯示沒有被正確簽名的聊天訊息。 + +# Logging +config.Logging=此項設定僅會影響到控制檯中的資訊(日誌)。 +config.Logging.DebugMessages=請在提交錯誤報告之前先啟用此項。謝謝! +config.Logging.ChatMessages=是否顯示來自伺服器的聊天訊息。 +config.Logging.InfoMessages=資訊性的訊息。(大部分來自MCC內部) +config.Logging.WarningMessages=顯示警告訊息。 +config.Logging.ErrorMessages=顯示錯誤訊息。 +config.Logging.ChatFilter=過濾聊天訊息所用的正規表示式。 +config.Logging.DebugFilter=過濾除錯訊息所用的正規表示式。 +config.Logging.FilterMode=過濾方式:"disable"(禁用),"blacklist"(隱藏匹配的訊息) 或 "whitelist"(僅顯示匹配的訊息) +config.Logging.LogToFile=是否將日誌資訊寫入到檔案。 +config.Logging.LogFile=日誌檔名稱。 +config.Logging.PrependTimestamp=寫入日誌檔案時是否新增時間戳。 +config.Logging.SaveColorCodes=是否保留訊息中的顏色字元。(例如"§b") + +# AppVars +config.AppVars.Variables=可以在某些欄位中以"%yourvar%"的形式使用。\n# %username% 和 %serverip% 時保留的變數名。 + +# Proxy +config.Proxy=通過代理連線到伺服器。\n# 如果Mojang/微軟登入服務被防火牆阻斷,設定Enabled_Login=true以使用代理進行登入。\n# 如果到Minecraft遊戲伺服器的連線被防火牆阻止,設定Enabled_Ingame=true以使用代理連線遊戲伺服器。\n# /!\ 在啟用代理前,請確保你的伺服器規則允許使用代理或VPN,否則你可能面臨被封禁等風險! +config.Proxy.Enabled_Login=是否使用代理連線Mojang或微軟的登入伺服器。 +config.Proxy.Enabled_Ingame=是否通過代理連線Minecraft遊戲伺服器。 +config.Proxy.Server=代理伺服器必須允許HTTPS登入。 +config.Proxy.Proxy_Type=支援的代理型別:"HTTP","SOCKS4","SOCKS4a","SOCKS5"。 +config.Proxy.Username=只有連線到受密碼保護的代理才需要。 +config.Proxy.Password=只有連線到受密碼保護的代理才需要。 + +# ChatFormat +config.ChatFormat=MCC會盡力檢測聊天資訊,但有些伺服器有不尋常的聊天格式\n# 當這種情況發生時,你需要在下面自定義匹配聊天所用的正規表示式,詳見 https://mccteam.github.io/guide/configuration.html#chat-format +config.ChatFormat.Builtins=是否啟用MCC內建的聊天檢測規則。設定為 false 以避免與自定義格式衝突。 +config.ChatFormat.UserDefined=是否啟用下方的自定義正規表示式進行聊天檢測。 + +# MCSettings +config.MCSettings=下面的設定將會被髮送到遊戲伺服器,隻影響一些伺服器端的東西,比如你的面板。 +config.MCSettings.Enabled=如果禁用,下面的設定就不會被髮送到伺服器上。 +config.MCSettings.Locale=請使用Minecraft的語言程式碼填寫,詳見[Main.Advanced.Language] +config.MCSettings.RenderDistance=渲染距離,取值範圍[0 - 255]。 +config.MCSettings.Difficulty=Minecraft 1.7及更早版本難度。"peaceful","easy","normal","difficult"。 +config.MCSettings.ChatMode=使用 "enabled"(完全啟用聊天)、"commands"(僅限命令)或 "disabled"(完全禁用聊天)。這允許你禁言自己... +config.MCSettings.ChatColors=這允許你在伺服器端禁用聊天顏色。 +config.MCSettings.MainHand=在1.9及更高版本中的主手設定。"left"(左手) 或 "right"(右手)。 + +# ChatBot +config.ChatBot================================ #\n# Minecraft Console Client Bots #\n# =============================== # + +# ChatBot.Alerts +config.ChatBot.Alerts=當檢測到特定聊天訊息或特定事件發生時提醒你\n # 對檢測特定玩家的聊天訊息很有用。 +config.ChatBot.Alerts.Beep_Enabled=除了高亮顯示外,當檢測到一個詞時還會播放類似蜂鳴器的嗶嗶聲。 +config.ChatBot.Alerts.Trigger_By_Words=在收到指定的關鍵詞後觸發提醒。 +config.ChatBot.Alerts.Trigger_By_Rain=在開始下雨和停止下雨時觸發提醒。 +config.ChatBot.Alerts.Trigger_By_Thunderstorm=在雷暴天氣的開始與結束觸發提醒。 +config.ChatBot.Alerts.Matches=觸發提醒的聊天關鍵詞列表。 +config.ChatBot.Alerts.Excludes=出現這些關鍵詞後該條訊息一定不觸發提醒。 +config.ChatBot.Alerts.Log_To_File=是否將提醒訊息寫入到日誌檔案。 +config.ChatBot.Alerts.Log_File=日誌檔案的路徑。 + +# ChatBot.AntiAFK +config.ChatBot.AntiAfk=定期傳送命令,或讓機器人隨機走動,以避免檢測到掛機後被踢出伺服器\n # /!\啟用前請確保你的伺服器規則不禁止反AFK機制!\n# /!\如果啟用隨機移動,請將機器人圍在圍欄裡,以防走失!(建議尺寸5x5x5) +config.ChatBot.AntiAfk.Delay=執行操作的間隔時間。(秒) +config.ChatBot.AntiAfk.Command=傳送給伺服器的指令。 +config.ChatBot.AntiAfk.Use_Sneak=在傳送指令時是否蹲下。 +config.ChatBot.AntiAfk.Use_Terrain_Handling=啟用地形處理,以使機器人能夠四處移動。 +config.ChatBot.AntiAfk.Walk_Range=機器人可以隨機移動的範圍(注意:範圍越大,速度越慢) +config.ChatBot.AntiAfk.Walk_Retries=嘗試移動失敗幾次後在改為傳送命令模式。 + +# ChatBot.AutoAttack +config.ChatBot.AutoAttack=自動攻擊周圍的生物\n# 使用此功能之前,你需要開啟實體處理。\n# /!\確保你的伺服器允許使用自動攻擊。\n# /!\伺服器外掛可能會認為此功能時作弊,並可能會封禁你的賬號,所以請自己檢查伺服器規則是否允許。 +config.ChatBot.AutoAttack.Mode="single"(單目標) 或 "multi"(多目標)。一次攻擊一個生物還是範圍內的所有生物。 +config.ChatBot.AutoAttack.Priority="health"(生命值)或 "distance"(距離)。當使用"single"模式時,以哪一個屬性確定優先順序。 +config.ChatBot.AutoAttack.Cooldown_Time=每次攻擊間的冷卻時間,設定 "Custom = false" 以讓MCC自動計算攻擊速度。 +config.ChatBot.AutoAttack.Interaction=可選項:"Interact"(互動),"Attack"(攻擊) 或 "InteractAt"(互動並攻擊) +config.ChatBot.AutoAttack.Attack_Hostile=是否攻擊敵對生物。 +config.ChatBot.AutoAttack.Attack_Passive=是否攻擊被動生物。 +config.ChatBot.AutoAttack.List_Mode=將實體列表作為 "whitelist"(白名單)還是 "blacklist"(黑名單)。 +config.ChatBot.AutoAttack.Entites_List=所有可用的實體型別可以在這裏找到:https://bit.ly/3Rg68lp + +# ChatBot.AutoCraft +config.ChatBot.AutoCraft=自動使用揹包中的物品進行合成。\n# 請看 https://mccteam.github.io/guide/chat-bots.html#auto-craft\n# 你需要啟用庫存處理來使用這個功能\n# 如果需要使用工作臺,你還需要啟用地形處理。 +config.ChatBot.AutoCraft.CraftingTable=如果你打算使用工作臺,請填寫它所在的位置。(需要啟用地形處理) +config.ChatBot.AutoCraft.OnFailure=合成失敗時應該怎麼處理,"abort"(中止)或 "wait"(等待)。 +config.ChatBot.AutoCraft.Recipes=Recipes.Name:給該配方起一個獨一無二的名字。(不能包含空白字元)Recipes.Type:合成型別,"player"(揹包2x2)或 "table"(工作臺3x3)\n# Recipes.Result:合成的目標物品\n# Recipes.Slots:合成的物品擺放方式,以從左到右、從上到下的格式填寫。需留空請填寫"Null"。\n# 最新的物品命名請看:https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs + +# AutoDig +config.ChatBot.AutoDig=自動挖掘方塊。\n# 你可以使用 "/digbot start" 和 "/digbot stop" 指令來控制 AutoDig 的啟停。\n# 由於MCC目前還不支援精確計算方塊的碰撞體積,在獲取看向的方塊時,視線上所有的方塊都被看作是完整的立方體。\n# 查詢方塊的名字,請訪問 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Mapping/Material.cs +config.ChatBot.AutoDig.Auto_Tool_Switch=自動切換到合適的工具。 +config.ChatBot.AutoDig.Durability_Limit=不會使用低於此耐久度的工具。(需要啟用庫存處理) +config.ChatBot.AutoDig.Drop_Low_Durability_Tools=在當前使用的工具耐久度過低後,是否丟掉它。 +config.ChatBot.AutoDig.Mode="lookat","fixedpos" 或 "both"。挖掘看向的方塊還是固定位置的方塊,或者是兩個條件都滿足的方塊。 +config.ChatBot.AutoDig.Locations=使用 "fixedpos" 或 "both" 模式時,方塊的座標。 +config.ChatBot.AutoDig.Location_Order="distance" 或 "index",當使用 "fixedpos" 模式時,按照到玩家的距離,還是列表中的順序確定挖掘的方塊。 +config.ChatBot.AutoDig.Auto_Start_Delay=進入遊戲後等待多少秒後開始自動挖掘,設定為-1禁用自動開始。 +config.ChatBot.AutoDig.Dig_Timeout=若挖掘一個方塊用時超過這個值,將會重新獲取目標進行挖掘。 +config.ChatBot.AutoDig.Log_Block_Dig=是否輸出挖掘方塊的相關資訊。 +config.ChatBot.AutoDig.List_Type=將方塊列表作為 "whitelist"(白名單)還是 "blacklist"(黑名單)。 + +# ChatBot.AutoDrop +config.ChatBot.AutoDrop=自動從揹包/庫存中丟棄指定的物品\n# 你需要啟用庫存處理來使用這個功能。\n# 可用物品請看 https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs +config.ChatBot.AutoDrop.Mode="include"(丟棄列表中的物品),"exclude"(丟棄列表外的所有物品) 或 "everything"(丟棄所有物品) + +# ChatBot.AutoEat +config.ChatBot.AutoEat=在飽食度較低是自動在揹包中尋找食物食用。\n# 你需要啟用庫存處理來使用這個功能。 + +# ChatBot.AutoFishing +config.ChatBot.AutoFishing=使用魚竿自動釣魚。指南:https://mccteam.github.io/guide/chat-bots.html#auto-fishingn# /!\ 啟用前請確保伺服器允許自動釣魚。 +config.ChatBot.AutoFishing.Antidespawn=如果你之前沒有啟用過這個選項,請保持它為 false 。 +config.ChatBot.AutoFishing.Mainhand=使用主手還是副手拿魚竿。 +config.ChatBot.AutoFishing.Auto_Start=是否在進入伺服器後自動開始釣魚,禁用此功能後,你需要使用"/usehand"手動使用魚竿一次。 +config.ChatBot.AutoFishing.Cast_Delay=釣到魚後多久開始下一次釣魚(拋竿)。 +config.ChatBot.AutoFishing.Fishing_Delay=進入伺服器後多久後開始自動釣魚。(秒) +config.ChatBot.AutoFishing.Fishing_Timeout=多少秒後沒有釣到魚視為超時。超時後會重新拋竿。 +config.ChatBot.AutoFishing.Durability_Limit=不會使用低於此耐久度的魚竿(魚竿耐久度最高為64)。(需要啟用庫存處理) +config.ChatBot.AutoFishing.Auto_Rod_Switch=在當前魚竿不可用後自動切換到揹包中的其他魚竿。(需要啟用庫存處理) +config.ChatBot.AutoFishing.Stationary_Threshold=魚鉤在X軸和Z軸方向上的移動小於這個值將被認為是靜止的,過高的閾值會在拋竿途中觸發收竿。 +config.ChatBot.AutoFishing.Hook_Threshold=一個「靜止」的魚鉤,在Y軸方向上的移動超過這個閾值將被認為釣到了魚。 +config.ChatBot.AutoFishing.Log_Fish_Bobber=用於調整以上兩個閾值,啟用後會在收到魚鉤實體移動資料包後列印其座標變化。 +config.ChatBot.AutoFishing.Enable_Move=這允許玩家在釣到魚後改變其位置或朝向。(需要啟用地形處理) +config.ChatBot.AutoFishing.Movements=會按照 "1->2->3->4->3->2->1->2->..." 的順序執行。每次可用改變位置、朝向或是都改變。推薦只改變朝向。 + +# ChatBot.AutoRelog +config.ChatBot.AutoRelog=在被伺服器斷開連線時自動重連,例如伺服器重啟時。\n# /!\ 謹慎使用Ignore_Kick_Message=true,這會在伺服器管理員將你踢出時依然連回! +config.ChatBot.AutoRelog.Delay=重新加入到伺服器前的延遲時間。(單位:秒) +config.ChatBot.AutoRelog.Retries=重新登入伺服器失敗時的重試次數,使用-1表示無限重試。 +config.ChatBot.AutoRelog.Ignore_Kick_Message=當設定為 true 時,將不考慮踢出的資訊直接重連。 +config.ChatBot.AutoRelog.Kick_Messages=如果踢出資訊與這其中的任何一個字串匹配,那麼將觸發自動重連。 + +# ChatBot.AutoRespond +config.ChatBot.AutoRespond=當聊天訊息與檔案中的規則匹配時,自動執行指定命令。\n# /!\ 伺服器管理員可以以任意玩家的身份傳送任意訊息,記住這一點!\n# 此機器人如果設定的不得當可能會造成刷屏,建議設定一個冷卻時間。 +config.ChatBot.AutoRespond.Match_Colors=不要刪除文字中的顏色程式碼(使用§字元的程式碼)。注意:啟用後你的匹配模板也必須包括顏色程式碼。 + +# ChatBot.ChatLog +config.ChatBot.ChatLog=將聊天資訊寫入到日誌檔案中。 + +# ChatBot.FollowPlayer +config.ChatBot.FollowPlayer=讓機器人跟隨指定玩家\n# 注意這是一個實驗性的功能,目前的尋路速度可能很慢,你可能需要時常等一會機器人來讓它跟上你。\n# 你可以調整"Update_Limit",找到最適合你的速度。(注意不要設定的太低,這樣可能導致反效果或使MCC卡頓)。\n# /!\ 在使用此功能之前,請先確保伺服器規則允許你這樣做。 +config.ChatBot.FollowPlayer.Update_Limit=機器人尋路的間隔時間(以秒為單位) +config.ChatBot.FollowPlayer.Stop_At_Distance=如果玩家在該範圍內,則視為已經接近玩家了。(防止機器人將玩家推開而產生無限迴圈) + +# ChatBot.HangmanGame +config.ChatBot.HangmanGame=一個用於演示聊天互動的小遊戲。玩家可以一次一個字母地猜出神祕的單詞。\n# 你需要正確地使用 ChatFormat,並在 botowners 中新增自己,用/tell start\n# /!\ 這個機器人可能會造成刷屏,如果許多玩家與它互動。 + +# ChatBot.Mailer +config.ChatBot.Mailer=在玩家和伺服器之間中繼訊息,就像一個郵件外掛一樣。\n# 這個機器人可以在收件人離線時儲存訊息,並在他們加入伺服器時傳送訊息。\n# /!\ 伺服器管理員可以以任意玩家的身份傳送任意訊息,請記住這一點。 + +# ChatBot.Map +config.ChatBot.Map=允許你將地圖渲染成.jpg圖片,該圖片會被渲染到Rendered_Maps資料夾中。\n# 注意:這個功能目前只對解決使用地圖的驗證碼有用。\n# 如果一些伺服器解決驗證碼的時間很短,請啟用Auto_Render_On_Update並準備快速開啟該檔案。\n# 在linux上,你可以使用FTP來訪問生成的檔案。 +config.ChatBot.Map.Should_Resize=是否需要調整地圖的大小?(預設大小是128x128) +config.ChatBot.Map.Resize_To=將地圖調整到什麼大小?(注意:大小越大,質量越低) +config.ChatBot.Map.Auto_Render_On_Update=一旦接收到新的地圖或已有地圖被更新,自動渲染該地圖。 +config.ChatBot.Map.Delete_All_On_Unload=在解除安裝/重新載入地圖時刪除所有已渲染的地圖(退出MCC時不會刪除影象) +config.ChatBot.Map.Notify_On_First_Update=當第一次從伺服器上收到一張地圖時,傳送一個通知。 + +# ChatBot.PlayerListLogger +config.ChatBot.PlayerListLogger=定期記錄當前的玩家列表到檔案中。 +config.ChatBot.PlayerListLogger.Delay=(單位:秒) + +# ChatBot.RemoteControl +config.ChatBot.RemoteControl=通過遊戲中的私聊向機器人傳送MCC控制檯命令\n# 你需要先配置好[ChatFormat]章節的設定,並在[Main.Advanced.bot_owners]中新增自己的賬號。\n# /!\ 伺服器管理員可以以任意玩家的身份傳送任意訊息,僅在信任他們時啟用本功能。 + +# ChatBot.ReplayCapture +config.ChatBot.ReplayCapture=使用"/replay start"開始記錄遊戲,並在之後使用 Replay Mod (https://www.replaymod.com/) 進行重放。\n# 請注意,由於技術限制,玩家自身不會顯示在重放檔案中。\n# /!\ 你應該使用"/replay stop"停止記錄或者使用"/quit"退出程式,否則回放檔案可能會損壞。 +config.ChatBot.ReplayCapture.Backup_Interval=每間隔多少秒自動儲存一次回放檔案,以秒為單位。使用-1禁用自動儲存。 + +# ChatBot.ScriptScheduler +config.ChatBot.ScriptScheduler=在加入伺服器時、到達特定時間時或以設定的時間間隔執行命令或指令碼檔案\n# 詳細使用方法請檢視 https://mccteam.github.io/guide/chat-bots.html#script-scheduler + 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..7730d274 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 { @@ -425,6 +426,14 @@ namespace MinecraftClient /// public virtual void OnThunderLevelChange(float level) { } + + /// + /// Called when a block is changed. + /// + /// The location of the block. + /// The block + public virtual void OnBlockChange(Location location, Block block) { } + /* =================================================================== */ /* ToolBox - Methods below might be useful while creating your bot. */ /* You should not need to interact with other classes of the program. */ @@ -521,9 +530,9 @@ namespace MinecraftClient text = GetVerbatim(text); //User-defined regex for private chat messages - if (Settings.ChatFormat_Private != null) + if (Config.ChatFormat.UserDefined && !string.IsNullOrWhiteSpace(Config.ChatFormat.Private)) { - 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 +542,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 +641,9 @@ namespace MinecraftClient text = GetVerbatim(text); //User-defined regex for public chat messages - if (Settings.ChatFormat_Public != null) + if (Config.ChatFormat.UserDefined && !string.IsNullOrWhiteSpace(Config.ChatFormat.Public)) { - 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 +653,7 @@ namespace MinecraftClient } //Built-in detection routine for public messages - if (Settings.ChatFormat_Builtins) + if (Config.ChatFormat.Builtins) { string[] tmp = text.Split(' '); @@ -735,9 +744,9 @@ namespace MinecraftClient text = GetVerbatim(text); //User-defined regex for teleport requests - if (Settings.ChatFormat_TeleportRequest != null) + if (Config.ChatFormat.UserDefined && !string.IsNullOrWhiteSpace(Config.ChatFormat.TeleportRequest)) { - 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 +755,7 @@ namespace MinecraftClient } //Built-in detection routine for teleport requests - if (Settings.ChatFormat_Builtins) + if (Config.ChatFormat.Builtins) { string[] tmp = text.Split(' '); @@ -786,7 +795,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 +836,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 +868,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 +901,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..51041b96 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -1,330 +1,217 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using MinecraftClient.Mapping; 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.HeadCommentHealper; +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; - - //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 = ""; - - //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 = ""; - - //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 - { - get - { - 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) - ); - } - } + private const int CommentsAlignPosition = 45; + private readonly static Regex CommentRegex = new(@"^(.*)\s?#\s\$([\w\.]+)\$\s*$$", RegexOptions.Compiled); //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_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"; - 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; + public const string TranslationDocUrl = "https://mccteam.github.io/guide/contibuting.html#translations"; + public const string GithubReleaseUrl = "https://github.com/MCCTeam/Minecraft-Console-Client/releases"; + public const string GithubLatestReleaseUrl = GithubReleaseUrl + "/latest"; - // 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; + public static GlobalConfig Config = new(); - //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 class InternalConfig { - if (Enum.TryParse(name, true, out Section pMode)) - return pMode; - return Section.Default; + 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; } - /// - /// Load settings from the given INI file - /// - /// File to load - public static void LoadFile(string file) + public class GlobalConfig { - Task.Factory.StartNew(() => ConsoleIO.WriteLogLine("[Settings] Loading Settings from " + Path.GetFullPath(file))); - if (File.Exists(file)) + [TomlPrecedingComment("$config.Head$")] + public HeadComment Head + { + get { return HeadCommentHealper.Config; } + set { HeadCommentHealper.Config = value; HeadCommentHealper.Config.OnSettingUpdate(); } + } + + public MainConfig Main + { + 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; } + } + + } + + public static Tuple LoadFromFile(string filepath) + { + TomlDocument document; + try + { + document = TomlParser.ParseFile(filepath); + Config = TomletMain.To(document); + } + 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); + ConsoleIO.WriteLine(Translations.GetOrNull("mcc.run_with_default_settings") ?? "\nMCC is running with default settings."); + return new(false, true); + } + } + catch { } + ConsoleIO.WriteLineFormatted(Translations.GetOrNull("config.load.fail") ?? "§cFailed to load settings:§r"); + ConsoleIO.WriteLine(ex.GetFullMessage()); + return new(false, false); + } + return new(true, 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 matchComment = CommentRegex.Match(line); + if (matchComment.Success && matchComment.Groups.Count == 3) + { + string config = matchComment.Groups[1].Value, comment = matchComment.Groups[2].Value; + if (config.Length > 0) + newConfig.Append(config).Append(' ', Math.Max(1, CommentsAlignPosition - config.Length) - 1); + newConfig.Append("# ").AppendLine(Translations.TryGet(comment).ReplaceLineEndings()); + } + else + { + newConfig.AppendLine(line); + } + } + + bool needUpdate = true; + byte[] newConfigByte = Encoding.UTF8.GetBytes(newConfig.ToString()); + if (File.Exists(filepath)) + { + try + { + if (new FileInfo(filepath).Length == newConfigByte.Length) + if (File.ReadAllBytes(filepath).SequenceEqual(newConfigByte)) + needUpdate = false; + } + catch { } + } + + if (needUpdate) + { + bool backupSuccessed = true; + if (backupOldFile && File.Exists(filepath)) + { + string backupFilePath = Path.ChangeExtension(filepath, ".backup.ini"); + try { File.Copy(filepath, backupFilePath, true); } + catch (Exception ex) + { + backupSuccessed = false; + ConsoleIO.WriteLineFormatted(Translations.TryGet("config.backup.fail", backupFilePath)); + ConsoleIO.WriteLine(ex.Message); + } + } + + if (backupSuccessed) + { + try { File.WriteAllBytes(filepath, newConfigByte); } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted(Translations.TryGet("config.write.fail", filepath)); + ConsoleIO.WriteLine(ex.Message); } } - catch (IOException) { } } } @@ -344,19 +231,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 +242,865 @@ 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 HeadCommentHealper { - switch (section) + public static HeadComment Config = new(); + + [TomlDoNotInlineObject] + public class HeadComment { - 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; + [TomlProperty("Current Version")] + public string CurrentVersion { get; set; } = Program.BuildInfo ?? "Development Build"; + + [TomlProperty("Latest Version")] + public string LatestVersion { get; set; } = "Unknown"; - 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; - - 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)) + public void OnSettingUpdate() { - if (i == 0) - { - res = new double[locationStrList.Length, curCodLen]; - codLen = curCodLen; - } + CurrentVersion = Program.BuildInfo ?? "Development Build"; + LatestVersion ??= "Unknown"; + } + } + } - for (int j = 0; j < curCodLen; ++j) + public static class MainConfigHealper + { + public static MainConfig Config = new(); + + [TomlDoNotInlineObject] + public class MainConfig + { + public GeneralConfig General = new(); + + [TomlPrecedingComment("$config.Main.Advanced$")] + public AdvancedConfig Advanced = new(); + + + [NonSerialized] + public static readonly string[] AvailableLang = + { + "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" + }; + + /// + /// 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 = ToLowerIfNeed(serverInfo.Host); + 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('-', '_'); + Advanced.Language = ToLowerIfNeed(Advanced.Language); + 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); + + for (int i = 0; i < Advanced.BotOwners.Count; ++i) + Advanced.BotOwners[i] = ToLowerIfNeed(Advanced.BotOwners[i]); + } + + [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 double MessageCooldown = 1.0; + + [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; + + [TomlPrecedingComment("$config.Main.Advanced.account_list$")] + public Dictionary AccountList = new() { + { "AccountNikename1", new AccountInfoConfig("playerone@email.com", "thepassword") }, + { "AccountNikename2", new AccountInfoConfig("TestBot", "-") }, + }; + + [TomlPrecedingComment("$config.Main.Advanced.server_list$")] + public Dictionary ServerList = new() { + { "ServerAlias1", new ServerInfoConfig("mc.awesomeserver.com") }, + { "ServerAlias2", new ServerInfoConfig("192.168.1.27", 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.disable; + + [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 { disable, 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; + + [TomlInlineComment("$config.ChatFormat.UserDefined$")] + public bool UserDefined = false; + + 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.AutoDig$")] + public ChatBots.AutoDig.Configs AutoDig + { + get { return ChatBots.AutoDig.Config; } + set { ChatBots.AutoDig.Config = value; ChatBots.AutoDig.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) @@ -1065,172 +1126,114 @@ namespace MinecraftClient } } - /// - /// Load login/password using an account alias - /// - /// True if the account was found and loaded - public static bool SetAccount(string accountAlias) + public static bool CheckUpdate(string? current, string? latest) { - accountAlias = Settings.ToLowerIfNeed(accountAlias); - if (Accounts.ContainsKey(accountAlias)) + if (current == null || latest == null) + return false; + Regex reg = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s(\d{4})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{2}).*"); + Regex reg2 = new(@"\w+\sbuild\s(\d+),\sbuilt\son\s\w+\s(\d{2})[-\/\.\s]?(\d{2})[-\/\.\s]?(\d{4}).*"); + + DateTime? curTime = null, latestTime = null; + + Match curMatch = reg.Match(current); + if (curMatch.Success && curMatch.Groups.Count == 5) { - Settings.Login = Accounts[accountAlias].Key; - Settings.Password = Accounts[accountAlias].Value; + try { curTime = new(int.Parse(curMatch.Groups[2].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[4].Value)); } + catch { curTime = null; } + } + if (curTime == null) + { + curMatch = reg2.Match(current); + try { curTime = new(int.Parse(curMatch.Groups[4].Value), int.Parse(curMatch.Groups[3].Value), int.Parse(curMatch.Groups[2].Value)); } + catch { curTime = null; } + } + if (curTime == null) + return false; + + Match latestMatch = reg.Match(latest); + if (latestMatch.Success && latestMatch.Groups.Count == 5) + { + try { latestTime = new(int.Parse(latestMatch.Groups[2].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[4].Value)); } + catch { latestTime = null; } + } + if (latestTime == null) + { + latestMatch = reg2.Match(latest); + try { latestTime = new(int.Parse(latestMatch.Groups[4].Value), int.Parse(latestMatch.Groups[3].Value), int.Parse(latestMatch.Groups[2].Value)); } + catch { latestTime = null; } + } + if (latestTime == null) + return false; + + int curBuildId, latestBuildId; + try + { + curBuildId = int.Parse(curMatch.Groups[1].Value); + latestBuildId = int.Parse(latestMatch.Groups[1].Value); + } + catch { return false; } + + if (latestTime > curTime) return true; - } - else return false; - } - - /// - /// 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) - { - server = ToLowerIfNeed(server); - string[] sip = server.Split(':'); - string host = sip[0]; - ushort port = 25565; - - if (sip.Length > 1) - { - 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; + else if (latestTime >= curTime && latestBuildId > curBuildId) return true; - } - else if (Servers.ContainsKey(server)) + else + return false; + } + + public static int DoubleToTick(double time) + { + time = Math.Min(int.MaxValue / 10, time); + return (int)Math.Round(time * 10); + } + } + + public static class InternalCmdCharTypeExtensions + { + public static char ToChar(this InternalCmdCharType type) + { + return type switch { - //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 => ' ', + 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 string ToLogString(this InternalCmdCharType type) { - lock (AppVars) + return type switch { - varName = Settings.ToLowerIfNeed(new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray())); - if (varName.Length > 0) - { - AppVars[varName] = varData; - return true; - } - else return false; - } + InternalCmdCharType.none => string.Empty, + InternalCmdCharType.slash => @"/", + InternalCmdCharType.backslash => @"\", + _ => @"/", + }; } - - /// - /// 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) + } + + public static class BrandInfoTypeExtensions + { + public static string? ToBrandString(this BrandInfoType info) { - 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++) + return info switch { - if (str[i] == '%') - { - bool varname_ok = false; - StringBuilder var_name = new(); + BrandInfoType.mcc => "Minecraft-Console-Client/" + Program.Version, + BrandInfoType.vanilla => "vanilla", + BrandInfoType.empty => null, + _ => null, + }; + } + } - 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(); + public static class ExceptionExtensions + { + public static string GetFullMessage(this Exception ex) + { + return ex.InnerException == null + ? ex.Message + : ex.Message + "\n --> " + ex.InnerException.GetFullMessage(); } } } diff --git a/MinecraftClient/Translations.cs b/MinecraftClient/Translations.cs index 1bcf9c16..c447a51b 100644 --- a/MinecraftClient/Translations.cs +++ b/MinecraftClient/Translations.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -15,9 +14,8 @@ namespace MinecraftClient /// public static class Translations { - private static readonly Dictionary translations; + private static Dictionary translations; 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 +50,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]) /// @@ -81,71 +94,541 @@ namespace MinecraftClient /// static Translations() { - translations = new Dictionary(); - LoadDefaultTranslationsFile(); + string[] engLang = DefaultConfigResource.Translation_en.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); // use embedded translations + translations = ParseTranslationContent(engLang); + } + + public static Tuple GetTranslationPriority() + { + string gameLanguage = "en_gb"; + List name = new(); + + string systemLanguage = string.IsNullOrWhiteSpace(Program.ActualCulture.Name) + ? Program.ActualCulture.Parent.Name + : Program.ActualCulture.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() + public static void LoadTranslationFile(string[] languageList) { - string[] engLang = DefaultConfigResource.TranslationEnglish.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); // use embedded translations - ParseTranslationContent(engLang); - } + translations = new(); - /// - /// 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) - { /* * External translation files * These files are loaded from the installation directory as: * 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,39 +636,77 @@ 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(); - if (line.Length <= 0) + if (line.Length < 3) continue; - if (line.StartsWith("#")) // ignore comment line started with # - continue; - if (line[0] == '[' && line[^1] == ']') // ignore section + if (!char.IsLetterOrDigit(line[0])) // ignore comment line started with # continue; - string translationName = line.Split('=')[0]; - if (line.Length > (translationName.Length + 1)) - { - string translationValue = line[(translationName.Length + 1)..].Replace("\\n", "\n"); - translations[translationName] = translationValue; - } + int index = line.IndexOf('='); + if (index != -1 && line.Length > (index + 1)) + translations[line[..index]] = line[(index + 1)..].Replace("\\n", "\n"); } + return translations; } - /// - /// Write the default translation file (English) to the disk. - /// - private static void WriteDefaultTranslation() + public static void TrimAllTranslations() { - string defaultPath = AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar + translationFilePath + Path.DirectorySeparatorChar + defaultTranslation; + string[] transEn = DefaultConfigResource.ResourceManager.GetString("Translation_en")! + .Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); - if (!Directory.Exists(translationFilePath)) + foreach (string lang in new string[] { "de", "fr", "ru", "vi", "zh_Hans", "zh_Hant" }) { - Directory.CreateDirectory(translationFilePath); + Dictionary trans = ParseTranslationContent( + DefaultConfigResource.ResourceManager.GetString("Translation_" + lang)! + .Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None) + ); + string fileName = AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar + translationFilePath + Path.DirectorySeparatorChar + lang + ".ini"; + if (File.Exists(fileName)) + { + string backupFilePath = Path.ChangeExtension(fileName, ".backup.ini"); + try { File.Copy(fileName, backupFilePath, true); } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted(Translations.TryGet("config.backup.fail", backupFilePath)); + ConsoleIO.WriteLine(ex.Message); + return; + } + } + using FileStream file = File.OpenWrite(fileName); + int total = 0, translated = 0; + for (int i = 0; i < transEn.Length; ++i) + { + string line = transEn[i].Trim(); + int index = transEn[i].IndexOf('='); + if (line.Length < 3 || !char.IsLetterOrDigit(line[0]) || index == -1 || line.Length <= (index + 1)) + { + file.Write(Encoding.UTF8.GetBytes(line)); + } + else + { + string key = line[..index]; + file.Write(Encoding.UTF8.GetBytes(key)); + file.Write(Encoding.UTF8.GetBytes("=")); + if (trans.TryGetValue(key, out string? value)) + { + file.Write(Encoding.UTF8.GetBytes(value.Replace("\n", "\\n"))); + ++total; + ++translated; + } + else + { + ++total; + } + } + file.Write(Encoding.UTF8.GetBytes(Environment.NewLine)); + } + ConsoleIO.WriteLine(string.Format("Language {0}: Translated {1} of {2}, {3:0.00}%", lang, translated, total, 100.0 * (double)translated / total)); } - File.WriteAllText(defaultPath, DefaultConfigResource.TranslationEnglish, Encoding.UTF8); } #region Console writing method wrapper diff --git a/MinecraftClient/WinAPI/ConsoleIcon.cs b/MinecraftClient/WinAPI/ConsoleIcon.cs index 36492f7c..e66eca89 100644 --- a/MinecraftClient/WinAPI/ConsoleIcon.cs +++ b/MinecraftClient/WinAPI/ConsoleIcon.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.IO; using System.Net.Http; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; @@ -42,56 +43,55 @@ namespace MinecraftClient.WinAPI /// /// Asynchronously download the player's skin and set the head as console icon /// + [SupportedOSPlatform("windows")] public static void SetPlayerIconAsync(string playerName) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + Thread t = new(new ThreadStart(delegate { - Thread t = new(new ThreadStart(delegate + HttpClient httpClient = new(); + try { - HttpClient httpClient = new(); + Task httpWebRequest = httpClient.GetStreamAsync("https://minotar.net/helm/" + playerName + "/100.png"); + httpWebRequest.Wait(); + Stream imageStream = httpWebRequest.Result; try { - Task httpWebRequest = httpClient.GetStreamAsync("https://minotar.net/helm/" + playerName + "/100.png"); - httpWebRequest.Wait(); - Stream imageStream = httpWebRequest.Result; - try - { - Bitmap skin = new(Image.FromStream(imageStream)); //Read skin from network - SetWindowIcon(Icon.FromHandle(skin.GetHicon())); // Windows 10+ (New console) - SetConsoleIcon(skin.GetHicon()); // Windows 8 and lower (Older console) - } - catch (ArgumentException) - { - /* Invalid image in HTTP response */ - } - imageStream.Dispose(); - httpWebRequest.Dispose(); + Bitmap skin = new(Image.FromStream(imageStream)); //Read skin from network + SetWindowIcon(Icon.FromHandle(skin.GetHicon())); // Windows 10+ (New console) + SetConsoleIcon(skin.GetHicon()); // Windows 8 and lower (Older console) } - catch (AggregateException ae) + catch (ArgumentException) { - 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(); - } - finally - { - httpClient.Dispose(); + /* Invalid image in HTTP response */ } + imageStream.Dispose(); + httpWebRequest.Dispose(); } - )) + catch (AggregateException ae) { - Name = "Player skin icon setter" - }; - t.Start(); + bool needRevert = false; + foreach (var ex in ae.InnerExceptions) + { + if (ex is HttpRequestException || ex is TaskCanceledException) //Skin not found? Reset to default icon + needRevert = true; + } + if (needRevert) + RevertToMCCIcon(); + } + catch (HttpRequestException) //Skin not found? Reset to default icon + { + RevertToMCCIcon(); + } + finally + { + httpClient.Dispose(); + } } + )) + { + Name = "Player skin icon setter" + }; + t.Start(); } /// diff --git a/MinecraftClient/WinAPI/ExitCleanUp.cs b/MinecraftClient/WinAPI/ExitCleanUp.cs index 1de9864f..98f93fe1 100644 --- a/MinecraftClient/WinAPI/ExitCleanUp.cs +++ b/MinecraftClient/WinAPI/ExitCleanUp.cs @@ -79,9 +79,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 { diff --git a/MinecraftClient/WinAPI/WindowsVersion.cs b/MinecraftClient/WinAPI/WindowsVersion.cs deleted file mode 100644 index 414c7d49..00000000 --- a/MinecraftClient/WinAPI/WindowsVersion.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Globalization; -using System.Runtime.InteropServices; -using Microsoft.Win32; - -namespace MinecraftClient.WinAPI -{ - /// - /// Retrieve information about the current Windows version - /// - /// - /// Environment.OSVersion does not work with Windows 10. - /// It returns 6.2 which is Windows 8 - /// - /// - /// https://stackoverflow.com/a/37755503 - /// - class WindowsVersion - { - /// - /// Returns the Windows major version number for this computer. - /// - public static uint WinMajorVersion - { - get - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10, - // and will most likely (hopefully) be there for some time before MS decides to change this - again... - if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out dynamic? major)) - return (uint)major; - - // When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion' - if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out dynamic? version)) - return 0; - - var versionParts = ((string)version!).Split('.'); - if (versionParts.Length != 2) return 0; - return uint.TryParse(versionParts[0], NumberStyles.Any, CultureInfo.CurrentCulture, out uint majorAsUInt) ? majorAsUInt : 0; - } - - return 0; - } - } - - /// - /// Returns the Windows minor version number for this computer. - /// - public static uint WinMinorVersion - { - get - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10, - // and will most likely (hopefully) be there for some time before MS decides to change this - again... - if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber", out dynamic? minor)) - return (uint)minor; - - // When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion' - if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out dynamic? version)) - return 0; - - var versionParts = ((string)version!).Split('.'); - if (versionParts.Length != 2) return 0; - return uint.TryParse(versionParts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out uint minorAsUInt) ? minorAsUInt : 0; - } - - return 0; - } - } - - /// - /// Try retrieving a registry key - /// - /// key path - /// Key - /// Value (output) - /// TRUE if successfully retrieved - private static bool TryGetRegistryKey(string path, string key, out dynamic? value) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - value = null; - try - { - var rk = Registry.LocalMachine.OpenSubKey(path); - if (rk == null) return false; - value = rk.GetValue(key); - return value != null; - } - catch - { - return false; - } - } - - value = null; - return false; - } - } -} diff --git a/MinecraftClient/config/alerts-exclude.txt b/MinecraftClient/config/alerts-exclude.txt deleted file mode 100644 index 9d4263a5..00000000 --- a/MinecraftClient/config/alerts-exclude.txt +++ /dev/null @@ -1,8 +0,0 @@ -myserver.com -Yourname>: -Player Yourname -Yourname joined -Yourname left -[Lockette] (Admin) - Yourname: -Yourname is \ No newline at end of file diff --git a/MinecraftClient/config/alerts.txt b/MinecraftClient/config/alerts.txt deleted file mode 100644 index 8db71581..00000000 --- a/MinecraftClient/config/alerts.txt +++ /dev/null @@ -1,37 +0,0 @@ -Yourname - whispers --> me -admin -.com -.net -.fr -.us -.uk -!!!! -???? -aaaa -zzzz -eeee -rrrr -tttt -yyyy -uuuu -iiii -oooo -pppp -qqqq -ssss -dddd -ffff -gggg -hhhh -jjjj -kkkk -llll -mmmm -wwww -xxxx -cccc -vvvv -bbbb -nnnn \ No newline at end of file diff --git a/MinecraftClient/config/kickmessages.txt b/MinecraftClient/config/kickmessages.txt deleted file mode 100644 index f9fe3ecc..00000000 --- a/MinecraftClient/config/kickmessages.txt +++ /dev/null @@ -1,4 +0,0 @@ -Connection has been lost -Server is restarting -Server is full -Too Many people \ No newline at end of file diff --git a/MinecraftClient/config/sample-accounts.txt b/MinecraftClient/config/sample-accounts.txt deleted file mode 100644 index f2326621..00000000 --- a/MinecraftClient/config/sample-accounts.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Minecraft Console Client -# Account list file - -# Put account data as comma separated values -# Values are: Alias,Login,Password -# It allows a fast account switching -# without directly using the credentials - -# Usage examples: -# /tell reco Player2 -# /connect Player1 - -Player1,playerone@email.com,thepassword -Player2,TestBot,- \ No newline at end of file diff --git a/MinecraftClient/config/sample-servers.txt b/MinecraftClient/config/sample-servers.txt deleted file mode 100644 index 2657da3b..00000000 --- a/MinecraftClient/config/sample-servers.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Minecraft Console Client -# Server list file - -# Put server data as comma separated values -# Values are: Alias,ServerIP:Port -# Aliases cannot contains dots or spaces -# The name "localhost" cannot be used as an alias -# It allows an easier and faster server switching -# with short aliases instead of full server IP -# It also adds a bit of privacy for remote control - -# Usage examples: -# /tell connect Server1 -# /connect Server2 - -Server1,localhost -Server2,mc.awesomeserver.com:25567 -Server3,192.168.1.27:1348 # Example of LAN server \ No newline at end of file diff --git a/MinecraftClient/config/sample-tasks.ini b/MinecraftClient/config/sample-tasks.ini deleted file mode 100644 index 6681f3a8..00000000 --- a/MinecraftClient/config/sample-tasks.ini +++ /dev/null @@ -1,52 +0,0 @@ -# Minecraft Console Client -# ScriptScheduler Tasks -# Example config file - -# Structure of a task: [Task] Followed by triggers and other settings. -# The example below contains all the possible fields for a task -# Time is HH:mm format, several different hours can be provided -# Action command can be "script" or any other internal command - -[Task] -triggerOnFirstLogin=false -triggerOnLogin=false -triggerOnTime=true -triggerOnInterval=false -timeValue=19:30 -timeValue=08:10 -timeInterval=0 -action=script event.txt - -# Another minimal example: some properties may be omitted -# This is highly recommended for improving task readability - -[Task] -triggerOnFirstLogin=true -action=script startup.txt - -# Intervals can be random -# To define a random interval between 2 numbers, use -, example: 1-100 -[Task] -triggerOnInterval=true -timeInterval=1-15 -action=send I am triggered! - -# Of course, the tasks file can contain as much tasks as you want. -# Another example triggered on logging in and every night at midnight: - -[Task] -triggerOnLogin=true -triggerOnTime=true -timeValue=00:00 -action=log It's midnight! - -# Example of task occuring every 30 seconds -# Could be used for jumping as antiAFK method - -[Task] -triggerOnInterval=true -timeInterval=30 -action=move up - -# Enjoy! -# - ORelio diff --git a/README-zh-Hans.md b/README-zh-Hans.md index a439b51a..57d68fe5 100644 --- a/README-zh-Hans.md +++ b/README-zh-Hans.md @@ -45,11 +45,15 @@ ## 翻译 Minecraft 控制台客户端 (MCC) 🌍 -如果你想将 Minecraft 控制台客户端 (MCC) 翻译成其他语言,请从 [lang 文件夹](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/Resources/lang)下载翻译文件或直接 fork 仓库。一旦你完成了翻译工作,请提交一个 Pull Request。如果你不熟悉 Git 的话,你也可以通过 [Issue](https://github.com/MCCTeam/Minecraft-Console-Client/issues) 将文件发送给我们。 +请查看:[如何为MCC添加或更新翻译](https://mccteam.github.io/guide/contibuting.html#translations)。 -要使用翻译后的语言文件,请将其放在 `lang/mcc/` 文件夹下,并在 `.ini` 配置文件中设置语言。如果该目录不存在,你可以创建它。 - -对于翻译文件的名称,请参阅[此评论](https://github.com/MCCTeam/Minecraft-Console-Client/pull/1282#issuecomment-711150715)。 +MCC 现在支持这些语言(按字母顺序排列): + * `de.ini` : Deutsch - German + * `en.ini` : English - English + * `fr.ini` : Français (France) - French + * `ru.ini` : Русский (Russkiy) - Russian + * `vi.ini` : Tiếng Việt (Việt Nam) - Vietnamese + * `zh-Hans.ini` : 简体中文(中国大陆) - Chinese Simplified (China; Mandarin) ## 从源码构建 🏗️ diff --git a/README.md b/README.md index 5fa01eb9..d5a5bd5b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [Documentation](https://mccteam.github.io/) | [Download](#download) | [Installation](https://mccteam.github.io/guide/installation.html) | [Configuration](https://mccteam.github.io/guide/configuration.html) | [Usage](https://mccteam.github.io/guide/usage.html) +[English](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/README.md) | [Nederlands](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/README-nl.md) | [Српски](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/README-sr.md) | [Türkçe](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/README-tr.md) | [Tiếng Việt](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/README-vi-vn.md) | [简体中文](https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/README-zh-Hans.md) + [![GitHub Actions build status](https://github.com/MCCTeam/Minecraft-Console-Client/actions/workflows/build-and-release.yml/badge.svg)](https://github.com/MCCTeam/Minecraft-Console-Client/releases/latest) Discord server @@ -45,11 +47,16 @@ If you'd like to contribute to Minecraft Console Client, great, just fork the re ## Translating Minecraft Console Client 🌍 -If you would like to translate Minecraft Console Client to a different language, please download the translation file from [the lang folder](https://github.com/MCCTeam/Minecraft-Console-Client/tree/master/MinecraftClient/Resources/lang) or just fork the repository. Once you finished the translation work, submit a pull request or send us the file through an [Issue](https://github.com/MCCTeam/Minecraft-Console-Client/issues) in case you are not familiar with Git. +Check out: [How to update or add translations for MCC](https://mccteam.github.io/guide/contibuting.html#translations). -To use the translated language file, place it under `lang/mcc/` folder and set your language in `.ini` config. You may create the directory if it does not exist. - -For the names of the translation file, please see [this comment](https://github.com/MCCTeam/Minecraft-Console-Client/pull/1282#issuecomment-711150715). +MCC now supports the following languages (Alphabetical order) : + * `de.ini` (57.12% translated) : Deutsch - German + * `en.ini` : English - English + * `fr.ini` (57.12% translated) : Français (France) - French + * `ru.ini` (56.18% translated) : Русский (Russkiy) - Russian + * `vi.ini` (56.18% translated) : Tiếng Việt (Việt Nam) - Vietnamese + * `zh-Hans.ini` (100.00% translated) : 简体中文 - Chinese Simplified + * `zh-Hant.ini` (100.00% translated) : 繁體中文 - Chinese Traditional ## Building from the source 🏗️