From f8aefaf129bf97e57b84176f3eb22123bceb7389 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Wed, 26 Oct 2022 08:54:54 +0800 Subject: [PATCH 01/44] init --- MinecraftClient/Command.cs | 23 +++ MinecraftClient/Commands/Animation.cs | 47 ++++- MinecraftClient/Commands/Bed.cs | 5 + MinecraftClient/Commands/BlockInfo.cs | 5 + MinecraftClient/Commands/Bots.cs | 5 + MinecraftClient/Commands/ChangeSlot.cs | 5 + MinecraftClient/Commands/Chunk.cs | 5 + MinecraftClient/Commands/CommandSource.cs | 13 ++ MinecraftClient/Commands/Connect.cs | 5 + MinecraftClient/Commands/Debug.cs | 5 + MinecraftClient/Commands/Dig.cs | 5 + MinecraftClient/Commands/DropItem.cs | 5 + MinecraftClient/Commands/Enchant.cs | 5 + MinecraftClient/Commands/Entitycmd.cs | 5 + MinecraftClient/Commands/ExecIf.cs | 5 + MinecraftClient/Commands/ExecMulti.cs | 5 + MinecraftClient/Commands/Exit.cs | 5 + MinecraftClient/Commands/Health.cs | 5 + MinecraftClient/Commands/Inventory.cs | 5 + MinecraftClient/Commands/List.cs | 5 + MinecraftClient/Commands/Log.cs | 5 + MinecraftClient/Commands/Look.cs | 5 + MinecraftClient/Commands/Move.cs | 5 + MinecraftClient/Commands/Reco.cs | 5 + MinecraftClient/Commands/Reload.cs | 5 + MinecraftClient/Commands/Respawn.cs | 5 + MinecraftClient/Commands/Script.cs | 5 + MinecraftClient/Commands/Send.cs | 5 + MinecraftClient/Commands/Set.cs | 5 + MinecraftClient/Commands/SetRnd.cs | 5 + MinecraftClient/Commands/Sneak.cs | 5 + MinecraftClient/Commands/Tps.cs | 5 + MinecraftClient/Commands/UseItem.cs | 5 + MinecraftClient/Commands/Useblock.cs | 5 + MinecraftClient/ConsoleIO.cs | 1 + MinecraftClient/McClient.cs | 166 +++++++++++------- MinecraftClient/MinecraftClient.csproj | 1 + .../Protocol/Handlers/Protocol18.cs | 7 + MinecraftClient/Scripting/ChatBot.cs | 6 + 39 files changed, 353 insertions(+), 66 deletions(-) create mode 100644 MinecraftClient/Commands/CommandSource.cs diff --git a/MinecraftClient/Command.cs b/MinecraftClient/Command.cs index 627542c6..467e0cf8 100644 --- a/MinecraftClient/Command.cs +++ b/MinecraftClient/Command.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Text; +using Brigadier.NET; +using Microsoft.Extensions.Logging; +using MinecraftClient.Commands; namespace MinecraftClient { @@ -22,6 +25,8 @@ namespace MinecraftClient /// public abstract string CmdDesc { get; } + public abstract void RegisterCommand(McClient handler, CommandDispatcher dispatcher); + /// /// Get the translated version of command description. /// @@ -32,6 +37,24 @@ namespace MinecraftClient return CmdUsage + s + Translations.TryGet(CmdDesc); } + public void LogUsage(Logger.ILogger logger) + { + logger.Info($"{Translations.Get("error.usage")}: {Settings.Config.Main.Advanced.InternalCmdChar.ToChar()}{CmdUsage}"); + } + + + public static int LogExecuteResult(Logger.ILogger logger, bool result) + { + logger.Info(Translations.Get(result ? "general.done" : "general.fail")); + return result ? 1 : 0; + } + + public static int LogExecuteResult(Logger.ILogger logger, int result) + { + logger.Info(Translations.Get(result > 0 ? "general.done" : "general.fail")); + return result; + } + /// /// Usage message, eg: 'name [args]' /// diff --git a/MinecraftClient/Commands/Animation.cs b/MinecraftClient/Commands/Animation.cs index cdfa8ad7..a7caec2c 100644 --- a/MinecraftClient/Commands/Animation.cs +++ b/MinecraftClient/Commands/Animation.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using Brigadier.NET; +using Brigadier.NET.Builder; namespace MinecraftClient.Commands { @@ -8,6 +11,48 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "animation "; } } public override string CmdDesc { get { return "cmd.animation.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + dispatcher.Register(l => + l.Literal("help").Then(l => + l.Literal(CmdName).Executes(c => { + LogUsage(handler.Log); + return 1; + }) + ) + ); + + dispatcher.Register(l => + l.Literal(CmdName).Then(l => + l.Literal("mainhand") + .Executes(c => { + return LogExecuteResult(handler.Log, handler.DoAnimation(0)); + }) + ) + ); + dispatcher.Register(l => + l.Literal(CmdName).Then(l => + l.Literal("0") + .Redirect(dispatcher.GetRoot().GetChild(CmdName).GetChild("mainhand")) + ) + ); + + dispatcher.Register(l => + l.Literal(CmdName).Then(l => + l.Literal("offhand") + .Executes(c => { + return LogExecuteResult(handler.Log, handler.DoAnimation(1)); + }) + ) + ); + dispatcher.Register(l => + l.Literal(CmdName).Then(l => + l.Literal("1") + .Redirect(dispatcher.GetRoot().GetChild(CmdName).GetChild("offhand")) + ) + ); + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs index ea2b4b1b..9a77b94a 100644 --- a/MinecraftClient/Commands/Bed.cs +++ b/MinecraftClient/Commands/Bed.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -13,6 +14,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bed leave|sleep |sleep "; } } public override string CmdDesc { get { return "cmd.bed.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { string[] args = GetArgs(command); diff --git a/MinecraftClient/Commands/BlockInfo.cs b/MinecraftClient/Commands/BlockInfo.cs index 5aa8ec75..51051c1d 100644 --- a/MinecraftClient/Commands/BlockInfo.cs +++ b/MinecraftClient/Commands/BlockInfo.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Text; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -10,6 +11,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "blockinfo [-s]"; } } public override string CmdDesc { get { return "cmd.blockinfo.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (!handler.GetTerrainEnabled()) diff --git a/MinecraftClient/Commands/Bots.cs b/MinecraftClient/Commands/Bots.cs index 46864dd1..b8b23848 100644 --- a/MinecraftClient/Commands/Bots.cs +++ b/MinecraftClient/Commands/Bots.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -10,6 +11,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bots [list|unload ]"; } } public override string CmdDesc { get { return "cmd.bots.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index f278cac7..5280cb67 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -9,6 +10,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "changeslot <1-9>"; } } public override string CmdDesc { get { return "cmd.changeSlot.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (!handler.GetInventoryEnabled()) diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index 4007d4e2..c6116928 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -12,6 +13,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "chunk status [chunkX chunkZ|locationX locationY locationZ]"; } } public override string CmdDesc { get { return "cmd.chunk.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/CommandSource.cs b/MinecraftClient/Commands/CommandSource.cs new file mode 100644 index 00000000..89f1f1ff --- /dev/null +++ b/MinecraftClient/Commands/CommandSource.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinecraftClient.Commands +{ + public class CommandSource + { + public string UserName { get; set; } = string.Empty; + } +} diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs index 12c94a3f..a58fba48 100644 --- a/MinecraftClient/Commands/Connect.cs +++ b/MinecraftClient/Commands/Connect.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "connect [account]"; } } public override string CmdDesc { get { return "cmd.connect.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient? handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index c2bbda8a..91d925ff 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "debug [on|off]"; } } public override string CmdDesc { get { return "cmd.debug.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index 59b9d79d..1fc408a5 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -10,6 +11,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "dig "; } } public override string CmdDesc { get { return "cmd.dig.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (!handler.GetTerrainEnabled()) diff --git a/MinecraftClient/Commands/DropItem.cs b/MinecraftClient/Commands/DropItem.cs index 54f93254..430ff675 100644 --- a/MinecraftClient/Commands/DropItem.cs +++ b/MinecraftClient/Commands/DropItem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -13,6 +14,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "/dropitem "; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (!handler.GetInventoryEnabled()) diff --git a/MinecraftClient/Commands/Enchant.cs b/MinecraftClient/Commands/Enchant.cs index a75317e2..376c31c5 100644 --- a/MinecraftClient/Commands/Enchant.cs +++ b/MinecraftClient/Commands/Enchant.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Brigadier.NET; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -10,6 +11,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "enchant "; } } public override string CmdDesc { get { return "cmd.enchant.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (!handler.GetInventoryEnabled()) diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 64bfa6c1..229b7fe7 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Text; +using Brigadier.NET; using MinecraftClient.Inventory; using MinecraftClient.Mapping; @@ -13,6 +14,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "entity "; } } public override string CmdDesc { get { return ""; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (handler.GetEntityHandlingEnabled()) diff --git a/MinecraftClient/Commands/ExecIf.cs b/MinecraftClient/Commands/ExecIf.cs index 06b8619f..b05acceb 100644 --- a/MinecraftClient/Commands/ExecIf.cs +++ b/MinecraftClient/Commands/ExecIf.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; using DynamicExpresso; namespace MinecraftClient.Commands @@ -11,6 +12,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "execif ---> "; } } public override string CmdDesc { get { return "cmd.execif.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/ExecMulti.cs b/MinecraftClient/Commands/ExecMulti.cs index a329da10..40804550 100644 --- a/MinecraftClient/Commands/ExecMulti.cs +++ b/MinecraftClient/Commands/ExecMulti.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -10,6 +11,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "execmulti -> -> -> ..."; } } public override string CmdDesc { get { return "cmd.execmulti.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Exit.cs b/MinecraftClient/Commands/Exit.cs index 7bca6496..f189e604 100644 --- a/MinecraftClient/Commands/Exit.cs +++ b/MinecraftClient/Commands/Exit.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "exit"; } } public override string CmdDesc { get { return "cmd.exit.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient? handler, string command, Dictionary? localVars) { Program.Exit(); diff --git a/MinecraftClient/Commands/Health.cs b/MinecraftClient/Commands/Health.cs index 68af9b32..01db69f0 100644 --- a/MinecraftClient/Commands/Health.cs +++ b/MinecraftClient/Commands/Health.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "health"; } } public override string CmdDesc { get { return "cmd.health.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { return Translations.Get("cmd.health.response", handler.GetHealth(), handler.GetSaturation(), handler.GetLevel(), handler.GetTotalExperience()); diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index f5f1a6c2..6f8877eb 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; +using Brigadier.NET; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -13,6 +14,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return GetBasicUsage(); } } public override string CmdDesc { get { return "cmd.inventory.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (handler.GetInventoryEnabled()) diff --git a/MinecraftClient/Commands/List.cs b/MinecraftClient/Commands/List.cs index f261d670..e833e8a9 100644 --- a/MinecraftClient/Commands/List.cs +++ b/MinecraftClient/Commands/List.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -9,6 +10,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "list"; } } public override string CmdDesc { get { return "cmd.list.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { return Translations.Get("cmd.list.players", String.Join(", ", handler.GetOnlinePlayers())); diff --git a/MinecraftClient/Commands/Log.cs b/MinecraftClient/Commands/Log.cs index 412e401b..896e560a 100644 --- a/MinecraftClient/Commands/Log.cs +++ b/MinecraftClient/Commands/Log.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "log "; } } public override string CmdDesc { get { return "cmd.log.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs index 11065f62..0dcc7642 100644 --- a/MinecraftClient/Commands/Look.cs +++ b/MinecraftClient/Commands/Look.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -11,6 +12,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "look "; } } public override string CmdDesc { get { return "cmd.look.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (handler.GetTerrainEnabled()) diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index bbebcded..026dac82 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -11,6 +12,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "move [-f]"; } } public override string CmdDesc { get { return "walk or start walking. \"-f\": force unsafe movements like falling or touching fire"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { List args = GetArgs(command.ToLower()).ToList(); diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs index 5b5b0e0f..206280e3 100644 --- a/MinecraftClient/Commands/Reco.cs +++ b/MinecraftClient/Commands/Reco.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reco [account]"; } } public override string CmdDesc { get { return "cmd.reco.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient? handler, string command, Dictionary? localVars) { string[] args = GetArgs(command); diff --git a/MinecraftClient/Commands/Reload.cs b/MinecraftClient/Commands/Reload.cs index f4063db7..52bd1ab2 100644 --- a/MinecraftClient/Commands/Reload.cs +++ b/MinecraftClient/Commands/Reload.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reload"; } } public override string CmdDesc { get { return "cmd.reload.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { handler.Log.Info(Translations.TryGet("cmd.reload.started")); diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs index 3ff718fd..fd39bc0a 100644 --- a/MinecraftClient/Commands/Respawn.cs +++ b/MinecraftClient/Commands/Respawn.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "respawn"; } } public override string CmdDesc { get { return "cmd.respawn.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { handler.SendRespawnPacket(); diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index e7e233c5..d345b2e2 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "script "; } } public override string CmdDesc { get { return "cmd.script.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Send.cs b/MinecraftClient/Commands/Send.cs index 55977740..92ef28d4 100644 --- a/MinecraftClient/Commands/Send.cs +++ b/MinecraftClient/Commands/Send.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "send "; } } public override string CmdDesc { get { return "cmd.send.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs index 9f8cfe5d..46f1d716 100644 --- a/MinecraftClient/Commands/Set.cs +++ b/MinecraftClient/Commands/Set.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "set varname=value"; } } public override string CmdDesc { get { return "cmd.set.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/SetRnd.cs b/MinecraftClient/Commands/SetRnd.cs index 6e0d00b2..f489bcca 100644 --- a/MinecraftClient/Commands/SetRnd.cs +++ b/MinecraftClient/Commands/SetRnd.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -10,6 +11,10 @@ namespace MinecraftClient.Commands public override string CmdDesc { get { return "cmd.setrnd.desc"; } } private static readonly Random rand = new(); + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (HasArg(command)) diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index 1c8dbf17..2f520b48 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -9,6 +10,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "Sneak"; } } public override string CmdDesc { get { return "cmd.sneak.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (sneaking) diff --git a/MinecraftClient/Commands/Tps.cs b/MinecraftClient/Commands/Tps.cs index 9aaafc5c..cd0f51c1 100644 --- a/MinecraftClient/Commands/Tps.cs +++ b/MinecraftClient/Commands/Tps.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -9,6 +10,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "tps"; } } public override string CmdDesc { get { return "cmd.tps.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { var tps = Math.Round(handler.GetServerTPS(), 2); diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 497633d2..be7eb2b2 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; namespace MinecraftClient.Commands { @@ -8,6 +9,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useitem"; } } public override string CmdDesc { get { return "cmd.useitem.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (handler.GetInventoryEnabled()) diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 5d8bf32e..3669794a 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Brigadier.NET; using MinecraftClient.Mapping; namespace MinecraftClient.Commands @@ -9,6 +10,10 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useblock "; } } public override string CmdDesc { get { return "cmd.useblock.desc"; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { if (!handler.GetTerrainEnabled()) diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs index 13b55989..022ccaf4 100644 --- a/MinecraftClient/ConsoleIO.cs +++ b/MinecraftClient/ConsoleIO.cs @@ -183,6 +183,7 @@ namespace MinecraftClient return; var buffer = ConsoleInteractive.ConsoleReader.GetBufferContent(); + ConsoleIO.WriteLine("AutoComplete " + buffer); autocomplete_engine.AutoComplete(buffer.Text[..buffer.CursorPosition]); } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 16bb2e9d..e60919c7 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2,9 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; +using System.Security.Cryptography.Pkcs; using System.Text; using System.Threading; +using Brigadier.NET; +using Brigadier.NET.Exceptions; using MinecraftClient.ChatBots; +using MinecraftClient.Commands; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; @@ -25,8 +29,10 @@ namespace MinecraftClient { public static int ReconnectionAttemptsLeft = 0; - private static readonly List cmd_names = new(); - private static readonly Dictionary cmds = new(); + public static readonly CommandSource cmd_source = new(); + public static readonly CommandDispatcher dispatcher = new(); + //private static readonly List cmd_names = new(); + //private static readonly Dictionary cmds = new(); private readonly Dictionary onlinePlayers = new(); private static bool commandsLoaded = false; @@ -584,17 +590,18 @@ namespace MinecraftClient /// True if successfully registered public bool RegisterCommand(string cmdName, string cmdDesc, string cmdUsage, ChatBot.CommandRunner callback) { - if (cmds.ContainsKey(cmdName.ToLower())) - { - return false; - } - else - { - Command cmd = new ChatBot.ChatBotCommand(cmdName, cmdDesc, cmdUsage, callback); - cmds.Add(cmdName.ToLower(), cmd); - cmd_names.Add(cmdName.ToLower()); - return true; - } + // if (cmds.ContainsKey(cmdName.ToLower())) + // { + // return false; + // } + // else + // { + // Command cmd = new ChatBot.ChatBotCommand(cmdName, cmdDesc, cmdUsage, callback); + // cmds.Add(cmdName.ToLower(), cmd); + // cmd_names.Add(cmdName.ToLower()); + // return true; + // } + return true; } /// @@ -608,13 +615,14 @@ namespace MinecraftClient /// public bool UnregisterCommand(string cmdName) { - if (cmds.ContainsKey(cmdName.ToLower())) - { - cmds.Remove(cmdName.ToLower()); - cmd_names.Remove(cmdName.ToLower()); - return true; - } - else return false; + // if (cmds.ContainsKey(cmdName.ToLower())) + // { + // cmds.Remove(cmdName.ToLower()); + // cmd_names.Remove(cmdName.ToLower()); + // return true; + // } + // else return false; + return true; } /// @@ -627,52 +635,79 @@ namespace MinecraftClient public bool PerformInternalCommand(string command, ref string? response_msg, Dictionary? localVars = null) { /* Process the provided command */ - - string command_name = command.Split(' ')[0].ToLower(); - if (command_name == "help") + ParseResults parse; + try { - if (Command.HasArg(command)) - { - string help_cmdname = Command.GetArgs(command)[0].ToLower(); - if (help_cmdname == "help") - { - response_msg = Translations.Get("icmd.help"); - } - else if (cmds.ContainsKey(help_cmdname)) - { - response_msg = cmds[help_cmdname].GetCmdDescTranslated(); - } - else response_msg = Translations.Get("icmd.unknown", command_name); - } - else response_msg = Translations.Get("icmd.list", String.Join(", ", cmd_names.ToArray()), Config.Main.Advanced.InternalCmdChar.ToChar()); + parse = dispatcher.Parse(command, cmd_source); } - else if (cmds.ContainsKey(command_name)) + catch (Exception e) { - response_msg = cmds[command_name].Run(this, command, localVars); - - foreach (ChatBot bot in bots.ToArray()) - { - try - { - bot.OnInternalCommand(command_name, string.Join(" ", Command.GetArgs(command)), response_msg); - } - catch (Exception e) - { - if (e is not ThreadAbortException) - { - Log.Warn(Translations.Get("icmd.error", bot.ToString() ?? string.Empty, e.ToString())); - } - else throw; //ThreadAbortException should not be caught - } - } - } - else - { - response_msg = Translations.Get("icmd.unknown", command_name); - return false; + Log.Error(e.GetFullMessage()); + return true; } - return true; + try + { + int res = dispatcher.Execute(parse); + Log.Info("res = " + res); + return true; + } + catch (CommandSyntaxException e) + { + Log.Warn(e.GetFullMessage()); + return true; + } + catch (Exception e) + { + Log.Error(e.GetFullMessage()); + return true; + } + + //string command_name = command.Split(' ')[0].ToLower(); + //if (command_name == "help") + //{ + // if (Command.HasArg(command)) + // { + // string help_cmdname = Command.GetArgs(command)[0].ToLower(); + // if (help_cmdname == "help") + // { + // response_msg = Translations.Get("icmd.help"); + // } + // else if (cmds.ContainsKey(help_cmdname)) + // { + // response_msg = cmds[help_cmdname].GetCmdDescTranslated(); + // } + // else response_msg = Translations.Get("icmd.unknown", command_name); + // } + // else response_msg = Translations.Get("icmd.list", String.Join(", ", cmd_names.ToArray()), Config.Main.Advanced.InternalCmdChar.ToChar()); + //} + //else if (cmds.ContainsKey(command_name)) + //{ + // response_msg = cmds[command_name].Run(this, command, localVars); + + // foreach (ChatBot bot in bots.ToArray()) + // { + // try + // { + // bot.OnInternalCommand(command_name, string.Join(" ", Command.GetArgs(command)), response_msg); + // } + // catch (Exception e) + // { + // if (e is not ThreadAbortException) + // { + // Log.Warn(Translations.Get("icmd.error", bot.ToString() ?? string.Empty, e.ToString())); + // } + // else throw; //ThreadAbortException should not be caught + // } + // } + //} + //else + //{ + // response_msg = Translations.Get("icmd.unknown", command_name); + // return false; + //} + + //return true; } public void LoadCommands() @@ -689,10 +724,11 @@ namespace MinecraftClient try { Command cmd = (Command)Activator.CreateInstance(type)!; - cmds[Settings.ToLowerIfNeed(cmd.CmdName)] = cmd; - cmd_names.Add(Settings.ToLowerIfNeed(cmd.CmdName)); - foreach (string alias in cmd.GetCMDAliases()) - cmds[Settings.ToLowerIfNeed(alias)] = cmd; + cmd.RegisterCommand(this, dispatcher); + // cmds[Settings.ToLowerIfNeed(cmd.CmdName)] = cmd; + // cmd_names.Add(Settings.ToLowerIfNeed(cmd.CmdName)); + // foreach (string alias in cmd.GetCMDAliases()) + // cmds[Settings.ToLowerIfNeed(alias)] = cmd; } catch (Exception e) { diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 013e6801..5f0061cf 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -33,6 +33,7 @@ + diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 70670e4b..4856cb6f 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -2082,6 +2082,13 @@ namespace MinecraftClient.Protocol.Handlers /// Completed text IEnumerable IAutoComplete.AutoComplete(string BehindCursor) { + var sug = McClient.dispatcher.GetCompletionSuggestions(McClient.dispatcher.Parse(BehindCursor[1..], McClient.cmd_source)); + sug.Wait(); + foreach (var hint in sug.Result.List) + { + log.Info(hint); + } + //log.Info(McClient.dispatcher.GetSmartUsage(McClient.dispatcher.Parse(BehindCursor, McClient.cmd_source), McClient.cmd_source)); if (String.IsNullOrEmpty(BehindCursor)) return Array.Empty(); diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 1c8272b1..d8d6a9fb 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -4,6 +4,8 @@ using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Brigadier.NET; +using MinecraftClient.Commands; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using static MinecraftClient.Settings; @@ -1676,6 +1678,10 @@ namespace MinecraftClient public override string CmdUsage { get { return _cmdUsage; } } public override string CmdDesc { get { return _cmdDesc; } } + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + } + public override string Run(McClient handler, string command, Dictionary? localVars) { return Runner(command, GetArgs(command)); From 84cf749344c8958dbc1a1e168835e99db5b8f13c Mon Sep 17 00:00:00 2001 From: BruceChen Date: Tue, 6 Dec 2022 15:50:17 +0800 Subject: [PATCH 02/44] Implement command completion suggestions. --- MinecraftClient/ChatBots/Alerts.cs | 1 + MinecraftClient/ChatBots/AntiAFK.cs | 5 +- MinecraftClient/ChatBots/AutoAttack.cs | 5 +- MinecraftClient/ChatBots/AutoCraft.cs | 151 +++-- MinecraftClient/ChatBots/AutoDig.cs | 112 ++-- MinecraftClient/ChatBots/AutoDrop.cs | 222 +++--- MinecraftClient/ChatBots/AutoEat.cs | 1 + MinecraftClient/ChatBots/AutoFishing.cs | 192 +++--- MinecraftClient/ChatBots/AutoRelog.cs | 12 +- MinecraftClient/ChatBots/AutoRespond.cs | 12 +- MinecraftClient/ChatBots/ChatLog.cs | 4 +- MinecraftClient/ChatBots/DiscordBridge.cs | 115 ++-- MinecraftClient/ChatBots/Farmer.cs | 308 +++++---- MinecraftClient/ChatBots/FollowPlayer.cs | 114 ++-- MinecraftClient/ChatBots/HangmanGame.cs | 1 + MinecraftClient/ChatBots/Mailer.cs | 179 +++-- MinecraftClient/ChatBots/Map.cs | 134 ++-- MinecraftClient/ChatBots/PlayerListLogger.cs | 1 + MinecraftClient/ChatBots/RemoteControl.cs | 14 +- MinecraftClient/ChatBots/ReplayCapture.cs | 109 ++- MinecraftClient/ChatBots/Script.cs | 5 +- MinecraftClient/ChatBots/ScriptScheduler.cs | 1 + MinecraftClient/ChatBots/TelegramBridge.cs | 114 ++-- MinecraftClient/ChatBots/TestBot.cs | 4 +- MinecraftClient/Command.cs | 44 +- .../ArgumentType/AccountNickArgumentType.cs | 25 + .../AutoCraftRecipeNameArgumentType.cs | 25 + .../ArgumentType/BotNameArgumentType.cs | 29 + .../ArgumentType/EntityTypeArgumentType.cs | 31 + .../FarmerCropTypeArgumentType.cs | 31 + .../InventoryActionArgumentType.cs | 41 ++ .../ArgumentType/InventoryIdArgumentType.cs | 34 + .../ArgumentType/ItemTypeArgumentType.cs | 31 + .../ArgumentType/LocationArgumentType.cs | 99 +++ .../ArgumentType/MapBotMapIdArgumentType.cs | 39 ++ .../ArgumentType/PlayerNameArgumentType.cs | 36 + .../ArgumentType/ServerNickArgumentType.cs | 25 + .../ArgumentType/TupleArgumentType.cs | 18 + MinecraftClient/CommandHandler/CmdResult.cs | 96 +++ .../CommandHandler/MccArguments.cs | 104 +++ .../Patch/CommandDispatcherExtensions.cs | 36 + .../Patch/CommandNodeExtensions.cs | 28 + MinecraftClient/Commands/Animation.cs | 95 +-- MinecraftClient/Commands/Bed.cs | 276 ++++---- MinecraftClient/Commands/BlockInfo.cs | 72 +- MinecraftClient/Commands/Bots.cs | 125 ++-- MinecraftClient/Commands/ChangeSlot.cs | 63 +- MinecraftClient/Commands/Chunk.cs | 482 ++++++------- MinecraftClient/Commands/CommandSource.cs | 13 - MinecraftClient/Commands/Connect.cs | 76 ++- MinecraftClient/Commands/Debug.cs | 47 +- MinecraftClient/Commands/Dig.cs | 95 +-- MinecraftClient/Commands/DropItem.cs | 81 ++- MinecraftClient/Commands/Enchant.cs | 149 +++-- MinecraftClient/Commands/Entitycmd.cs | 413 ++++++++---- MinecraftClient/Commands/ExecIf.cs | 131 ++-- MinecraftClient/Commands/ExecMulti.cs | 59 +- MinecraftClient/Commands/Exit.cs | 50 +- MinecraftClient/Commands/Health.cs | 30 +- MinecraftClient/Commands/Help.cs | 33 + MinecraftClient/Commands/Inventory.cs | 632 ++++++++++-------- MinecraftClient/Commands/List.cs | 30 +- MinecraftClient/Commands/Log.cs | 36 +- MinecraftClient/Commands/Look.cs | 153 +++-- MinecraftClient/Commands/Move.cs | 255 ++++--- MinecraftClient/Commands/Reco.cs | 50 +- MinecraftClient/Commands/Reload.cs | 30 +- MinecraftClient/Commands/Respawn.cs | 32 +- MinecraftClient/Commands/Script.cs | 34 +- MinecraftClient/Commands/Send.cs | 35 +- MinecraftClient/Commands/Set.cs | 50 +- MinecraftClient/Commands/SetRnd.cs | 98 ++- MinecraftClient/Commands/Sneak.cs | 44 +- MinecraftClient/Commands/Tps.cs | 32 +- MinecraftClient/Commands/UseItem.cs | 40 +- MinecraftClient/Commands/Useblock.cs | 53 +- MinecraftClient/ConsoleIO.cs | 158 ++++- .../Inventory/EnchantmentMapping.cs | 3 +- MinecraftClient/Inventory/Item.cs | 13 +- MinecraftClient/Inventory/ItemMovingHelper.cs | 1 + MinecraftClient/Inventory/VillagerInfo.cs | 2 +- MinecraftClient/Json.cs | 2 +- MinecraftClient/Logger/FileLogLogger.cs | 1 + MinecraftClient/Mapping/Block.cs | 3 +- MinecraftClient/Mapping/Dimension.cs | 6 +- MinecraftClient/Mapping/Entity.cs | 3 +- MinecraftClient/Mapping/Location.cs | 35 + MinecraftClient/Mapping/RaycastHelper.cs | 4 +- MinecraftClient/Mapping/World.cs | 4 +- MinecraftClient/McClient.cs | 220 +++--- MinecraftClient/MinecraftClient.csproj | 13 +- MinecraftClient/Program.cs | 20 +- .../Handlers/Packet/s2c/DeclareCommands.cs | 19 +- .../Protocol/Handlers/Protocol16.cs | 32 +- .../Protocol/Handlers/Protocol18.cs | 50 +- .../Protocol/Handlers/Protocol18Forge.cs | 2 + MinecraftClient/Protocol/IMinecraftCom.cs | 2 +- .../Protocol/IMinecraftComHandler.cs | 8 + .../Protocol/Message/ChatParser.cs | 17 +- MinecraftClient/Protocol/MojangAPI.cs | 2 +- MinecraftClient/Protocol/PlayerInfo.cs | 2 +- .../Protocol/ProfileKey/KeyUtils.cs | 18 +- .../Protocol/ProfileKey/KeysCache.cs | 14 +- .../Protocol/ProfileKey/PlayerKeyPair.cs | 8 +- .../Protocol/ProfileKey/PrivateKey.cs | 2 +- .../Protocol/ProfileKey/PublicKey.cs | 2 +- .../Protocol/Session/SessionToken.cs | 1 + .../Translations/Translations.Designer.cs | 17 +- .../Resources/Translations/Translations.resx | 17 +- MinecraftClient/Scripting/AssemblyResolver.cs | 40 +- MinecraftClient/Scripting/CSharpRunner.cs | 30 +- MinecraftClient/Scripting/ChatBot.cs | 105 ++- .../DynamicRun/Builder/CompileRunner.cs | 3 +- .../Scripting/DynamicRun/Builder/Compiler.cs | 16 +- .../SimpleUnloadableAssemblyLoadContext.cs | 2 +- 115 files changed, 4684 insertions(+), 2695 deletions(-) create mode 100644 MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/CmdResult.cs create mode 100644 MinecraftClient/CommandHandler/MccArguments.cs create mode 100644 MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs create mode 100644 MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs delete mode 100644 MinecraftClient/Commands/CommandSource.cs create mode 100644 MinecraftClient/Commands/Help.cs diff --git a/MinecraftClient/ChatBots/Alerts.cs b/MinecraftClient/ChatBots/Alerts.cs index fabdc3b4..e503d52f 100644 --- a/MinecraftClient/ChatBots/Alerts.cs +++ b/MinecraftClient/ChatBots/Alerts.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index aac824e4..bbf73637 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -1,5 +1,8 @@ using System; +using Brigadier.NET; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -90,7 +93,7 @@ namespace MinecraftClient.ChatBots count = 0; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (Config.Use_Terrain_Handling) { diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index e6fd19af..f7bed4ed 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using Brigadier.NET; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -109,7 +112,7 @@ namespace MinecraftClient.ChatBots attackPassive = Config.Attack_Passive; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetEntityHandlingEnabled()) { diff --git a/MinecraftClient/ChatBots/AutoCraft.cs b/MinecraftClient/ChatBots/AutoCraft.cs index 13478736..9c700160 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -2,8 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.AutoCraft.Configs; @@ -11,6 +16,8 @@ namespace MinecraftClient.ChatBots { public class AutoCraft : ChatBot { + public const string CommandName = "autocraft"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -40,7 +47,7 @@ namespace MinecraftClient.ChatBots Name: "Recipe-Name-2", Type: CraftTypeConfig.table, Result: ItemType.StoneBricks, - Slots: new ItemType[9] { + Slots: new ItemType[9] { ItemType.Stone, ItemType.Stone, ItemType.Null, ItemType.Stone, ItemType.Stone, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, @@ -117,7 +124,7 @@ namespace MinecraftClient.ChatBots public ItemType Result = ItemType.Air; - public ItemType[] Slots = new ItemType[9] { + public ItemType[] Slots = new ItemType[9] { ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, ItemType.Null, @@ -279,7 +286,7 @@ namespace MinecraftClient.ChatBots } } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetInventoryEnabled()) { @@ -288,81 +295,93 @@ namespace MinecraftClient.ChatBots UnloadBot(); return; } - RegisterChatBotCommand("autocraft", Translations.bot_autoCraft_cmd, GetHelp(), CommandHandler); - RegisterChatBotCommand("ac", Translations.bot_autoCraft_alias, GetHelp(), CommandHandler); + + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => OnCommandHelp(r.Source, "list"))) + .Then(l => l.Literal("start") + .Executes(r => OnCommandHelp(r.Source, "start"))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandHelp(r.Source, "stop"))) + .Then(l => l.Literal("help") + .Executes(r => OnCommandHelp(r.Source, "help"))) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("list") + .Executes(r => OnCommandList(r.Source))) + .Then(l => l.Literal("start") + .Then(l => l.Argument("RecipeName", MccArguments.AutoCraftRecipeName()) + .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "RecipeName"))))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - public string CommandHandler(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) - { - switch (args[0]) - { - case "list": - StringBuilder nameList = new(); - foreach (RecipeConfig recipe in Config.Recipes) - nameList.Append(recipe.Name).Append(", "); - return string.Format(Translations.bot_autoCraft_cmd_list, Config.Recipes.Length, nameList.ToString()); - case "start": - if (args.Length >= 2) - { - string name = args[1]; - - 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(recipe!); - return ""; - } - else - return Translations.bot_autoCraft_recipe_not_exist; - } - else - return Translations.bot_autoCraft_no_recipe_name; - case "stop": - StopCrafting(); - return Translations.bot_autoCraft_stop; - case "help": - return GetCommandHelp(args.Length >= 2 ? args[1] : ""); - default: - return GetHelp(); - } - } - else return GetHelp(); + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } - private static string GetHelp() + private int OnCommandHelp(CmdResult r, string? cmd) { - return string.Format(Translations.bot_autoCraft_available_cmd, "load, list, reload, resetcfg, start, stop, help"); - } - - private string GetCommandHelp(string cmd) - { - return cmd.ToLower() switch + return r.SetAndReturn(cmd switch { #pragma warning disable format // @formatter:off - "load" => Translations.bot_autoCraft_help_load, "list" => Translations.bot_autoCraft_help_list, - "reload" => Translations.bot_autoCraft_help_reload, - "resetcfg" => Translations.bot_autoCraft_help_resetcfg, "start" => Translations.bot_autoCraft_help_start, "stop" => Translations.bot_autoCraft_help_stop, "help" => Translations.bot_autoCraft_help_help, - _ => GetHelp(), + _ => string.Format(Translations.bot_autoCraft_available_cmd, "load, list, reload, resetcfg, start, stop, help") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on - }; + }); + } + + private int OnCommandList(CmdResult r) + { + StringBuilder nameList = new(); + foreach (RecipeConfig recipe in Config.Recipes) + nameList.Append(recipe.Name).Append(", "); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoCraft_cmd_list, Config.Recipes.Length, nameList.ToString())); + } + + private int OnCommandStart(CmdResult r, string 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(recipe!); + return r.SetAndReturn(CmdResult.Status.Done); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_autoCraft_recipe_not_exist); + } + } + + private int OnCommandStop(CmdResult r) + { + StopCrafting(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoCraft_stop); } #region Core part of auto-crafting @@ -442,7 +461,7 @@ namespace MinecraftClient.ChatBots ItemType ResultItem = recipeConfig.Result; - ContainerType CraftingAreaType = + ContainerType CraftingAreaType = (recipeConfig.Type == CraftTypeConfig.player) ? ContainerType.PlayerInventory : ContainerType.Crafting; PrepareCrafting(new Recipe(materials, ResultItem, CraftingAreaType)); diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs index 21f93803..740a9053 100644 --- a/MinecraftClient/ChatBots/AutoDig.cs +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -1,13 +1,20 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class AutoDig : ChatBot { + public const string CommandName = "autodig"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -60,7 +67,7 @@ namespace MinecraftClient.ChatBots { 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); @@ -103,7 +110,7 @@ namespace MinecraftClient.ChatBots Stopping, } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetTerrainEnabled()) { @@ -117,32 +124,67 @@ namespace MinecraftClient.ChatBots if (!inventoryEnabled && Config.Auto_Tool_Switch) LogToConsole(Translations.bot_autodig_no_inv_handle); - RegisterChatBotCommand("digbot", Translations.bot_autodig_cmd, GetHelp(), CommandHandler); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("start") + .Executes(r => OnCommandHelp(r.Source, "start"))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandHelp(r.Source, "stop"))) + .Then(l => l.Literal("help") + .Executes(r => OnCommandHelp(r.Source, "help"))) + ) + ); + + var cmd = dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("start") + .Executes(r => OnCommandStart(r.Source))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); + + dispatcher.Register(l => l.Literal("digbot") + .Redirect(cmd) + ); } - public string CommandHandler(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) + dispatcher.Unregister("digbot"); + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - switch (args[0]) - { - case "start": - lock (stateLock) - { - counter = 0; - state = State.WaitingStart; - } - return Translations.bot_autodig_start; - case "stop": - StopDigging(); - return Translations.bot_autodig_stop; - case "help": - return GetCommandHelp(args.Length >= 2 ? args[1] : ""); - default: - return GetHelp(); - } +#pragma warning disable format // @formatter:off + "start" => Translations.bot_autodig_help_start, + "stop" => Translations.bot_autodig_help_stop, + "help" => Translations.bot_autodig_help_help, + _ => string.Format(Translations.bot_autodig_available_cmd, "start, stop, help") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandStart(CmdResult r) + { + lock (stateLock) + { + counter = 0; + state = State.WaitingStart; } - else return GetHelp(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autodig_start); + } + + private int OnCommandStop(CmdResult r) + { + StopDigging(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autodig_stop); } private void StartDigging() @@ -240,7 +282,7 @@ namespace MinecraftClient.ChatBots 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 || + if (Config.Mode == Configs.ModeType.lookat || (Config.Mode == Configs.ModeType.both && Config._Locations.Contains(blockLoc))) { if (DigBlock(blockLoc, lookAtBlock: false)) @@ -288,8 +330,8 @@ namespace MinecraftClient.ChatBots 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)) || + 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); @@ -401,23 +443,5 @@ namespace MinecraftClient.ChatBots return base.OnDisconnect(reason, message); } - - private static string GetHelp() - { - return string.Format(Translations.bot_autodig_available_cmd, "start, stop, help"); - } - - private string GetCommandHelp(string cmd) - { - return cmd.ToLower() switch - { -#pragma warning disable format // @formatter:off - "start" => Translations.bot_autodig_help_start, - "stop" => Translations.bot_autodig_help_stop, - "help" => Translations.bot_autodig_help_help, - _ => GetHelp(), -#pragma warning restore format // @formatter:on - }; - } } } diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index 1ad79917..5781af9e 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -1,7 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.AutoDrop.Configs; @@ -9,6 +14,8 @@ namespace MinecraftClient.ChatBots { public class AutoDrop : ChatBot { + public const string CommandName = "autodrop"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -38,111 +45,7 @@ namespace MinecraftClient.ChatBots private readonly int updateDebounceValue = 2; private int inventoryUpdated = -1; - public string CommandHandler(string cmd, string[] args) - { - if (args.Length > 0) - { - switch (args[0].ToLower()) - { - case "on": - Config.Enabled = true; - inventoryUpdated = 0; - OnUpdateFinish(); - return Translations.bot_autoDrop_on; - case "off": - Config.Enabled = false; - return Translations.bot_autoDrop_off; - case "add": - if (args.Length >= 2) - { - if (Enum.TryParse(args[1], true, out ItemType item)) - { - Config.Items.Add(item); - return string.Format(Translations.bot_autoDrop_added, item.ToString()); - } - else - { - return string.Format(Translations.bot_autoDrop_incorrect_name, args[1]); - } - } - else - { - return Translations.cmd_inventory_help_usage + ": add "; - } - case "remove": - if (args.Length >= 2) - { - if (Enum.TryParse(args[1], true, out ItemType item)) - { - if (Config.Items.Contains(item)) - { - Config.Items.Remove(item); - return string.Format(Translations.bot_autoDrop_removed, item.ToString()); - } - else - { - return Translations.bot_autoDrop_not_in_list; - } - } - else - { - return string.Format(Translations.bot_autoDrop_incorrect_name, args[1]); - } - } - else - { - return Translations.cmd_inventory_help_usage + ": remove "; - } - case "list": - if (Config.Items.Count > 0) - { - return string.Format(Translations.bot_autoDrop_list, Config.Items.Count, string.Join("\n", Config.Items)); - } - else - { - return Translations.bot_autoDrop_no_item; - } - case "mode": - if (args.Length >= 2) - { - switch (args[1].ToLower()) - { - case "include": - Config.Mode = DropMode.include; - break; - case "exclude": - Config.Mode = DropMode.exclude; - break; - case "everything": - Config.Mode = DropMode.everything; - break; - default: - return Translations.bot_autoDrop_unknown_mode; // Unknwon mode. Available modes: Include, Exclude, Everything - } - inventoryUpdated = 0; - OnUpdateFinish(); - return string.Format(Translations.bot_autoDrop_switched, Config.Mode.ToString()); // Switched to {0} mode. - } - else - { - return Translations.bot_autoDrop_unknown_mode; - } - default: - return GetHelp(); - } - } - else - { - return GetHelp(); - } - } - - private static string GetHelp() - { - return string.Format(Translations.general_available_cmd, "on, off, add, remove, list, mode"); - } - - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetInventoryEnabled()) { @@ -151,8 +54,113 @@ namespace MinecraftClient.ChatBots UnloadBot(); return; } - RegisterChatBotCommand("autodrop", Translations.bot_autoDrop_cmd, GetHelp(), CommandHandler); - RegisterChatBotCommand("ad", Translations.bot_autoDrop_alias, GetHelp(), CommandHandler); + + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("add") + .Executes(r => OnCommandHelp(r.Source, "add"))) + .Then(l => l.Literal("remove") + .Executes(r => OnCommandHelp(r.Source, "remove"))) + .Then(l => l.Literal("mode") + .Executes(r => OnCommandHelp(r.Source, "mode"))) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("on") + .Executes(r => OnCommandEnable(r.Source, true))) + .Then(l => l.Literal("off") + .Executes(r => OnCommandEnable(r.Source, false))) + .Then(l => l.Literal("add") + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => OnCommandAdd(r.Source, MccArguments.GetItemType(r, "ItemType"))))) + .Then(l => l.Literal("remove") + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => OnCommandRemove(r.Source, MccArguments.GetItemType(r, "ItemType"))))) + .Then(l => l.Literal("list") + .Executes(r => OnCommandList(r.Source))) + .Then(l => l.Literal("mode") + .Then(l => l.Literal("include") + .Executes(r => OnCommandMode(r.Source, DropMode.include))) + .Then(l => l.Literal("exclude") + .Executes(r => OnCommandMode(r.Source, DropMode.exclude))) + .Then(l => l.Literal("everything") + .Executes(r => OnCommandMode(r.Source, DropMode.everything)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); + } + + public override void OnUnload(CommandDispatcher dispatcher) + { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + "add" => Translations.cmd_inventory_help_usage + ": add ", + "remove" => Translations.cmd_inventory_help_usage + ": remove ", + "mode" => Translations.bot_autoDrop_unknown_mode, + _ => string.Format(Translations.general_available_cmd, "on, off, add, remove, list, mode") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandEnable(CmdResult r, bool enable) + { + if (enable) + { + Config.Enabled = true; + inventoryUpdated = 0; + OnUpdateFinish(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoDrop_on); + } + else + { + Config.Enabled = false; + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoDrop_off); + } + } + + private int OnCommandAdd(CmdResult r, ItemType item) + { + Config.Items.Add(item); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_added, item.ToString())); + } + + private int OnCommandRemove(CmdResult r, ItemType item) + { + if (Config.Items.Contains(item)) + { + Config.Items.Remove(item); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_removed, item.ToString())); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_autoDrop_not_in_list); + } + } + + private int OnCommandList(CmdResult r) + { + if (Config.Items.Count > 0) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_list, Config.Items.Count, string.Join("\n", Config.Items))); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_autoDrop_no_item); + } + + private int OnCommandMode(CmdResult r, DropMode mode) + { + Config.Mode = mode; + inventoryUpdated = 0; + OnUpdateFinish(); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_autoDrop_switched, Config.Mode.ToString())); } public override void Update() diff --git a/MinecraftClient/ChatBots/AutoEat.cs b/MinecraftClient/ChatBots/AutoEat.cs index eb9c3762..eb9eea07 100644 --- a/MinecraftClient/ChatBots/AutoEat.cs +++ b/MinecraftClient/ChatBots/AutoEat.cs @@ -1,5 +1,6 @@ using System; using MinecraftClient.Inventory; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 73b0af3f..963cc9ad 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -2,8 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.AutoFishing.Configs; @@ -15,6 +20,8 @@ namespace MinecraftClient.ChatBots /// public class AutoFishing : ChatBot { + public const string CommandName = "autofishing"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -169,87 +176,119 @@ namespace MinecraftClient.ChatBots Stopping, } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetEntityHandlingEnabled()) { LogToConsole(Translations.extra_entity_required); state = FishingState.WaitJoinGame; } + inventoryEnabled = GetInventoryEnabled(); if (!inventoryEnabled) LogToConsole(Translations.bot_autoFish_no_inv_handle); - RegisterChatBotCommand("fish", Translations.bot_autoFish_cmd, GetHelp(), CommandHandler); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + .Then(l => l.Literal("start") + .Executes(r => OnCommandHelp(r.Source, "start"))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandHelp(r.Source, "stop"))) + .Then(l => l.Literal("status") + .Executes(r => OnCommandHelp(r.Source, "status"))) + .Then(l => l.Literal("help") + .Executes(r => OnCommandHelp(r.Source, "help"))) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("start") + .Executes(r => OnCommandStart(r.Source))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("status") + .Executes(r => OnCommandStatus(r.Source)) + .Then(l => l.Literal("clear") + .Executes(r => OnCommandStatusClear(r.Source)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - public string CommandHandler(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length >= 1) + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - switch (args[0]) - { - case "start": - isFishing = false; - lock (stateLock) - { - isFishing = false; - counter = 0; - state = FishingState.StartMove; - } - return Translations.bot_autoFish_start; - case "stop": - isFishing = false; - lock (stateLock) - { - isFishing = false; - if (state == FishingState.WaitingFishToBite) - UseFishRod(); - state = FishingState.Stopping; - } - StopFishing(); - return Translations.bot_autoFish_stop; - case "status": - if (args.Length >= 2) - { - if (args[1] == "clear") - { - fishItemCnt = new(); - return Translations.bot_autoFish_status_clear; - } - else - { - return GetCommandHelp("status"); - } - } - else - { - if (fishItemCnt.Count == 0) - return Translations.bot_autoFish_status_info; +#pragma warning disable format // @formatter:off + "start" => Translations.bot_autoFish_help_start, + "stop" => Translations.bot_autoFish_help_stop, + "status" => Translations.bot_autoFish_help_status, + "help" => Translations.bot_autoFish_help_help, + _ => string.Format(Translations.bot_autoFish_available_cmd, "start, stop, status, help") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } - List> orderedList = fishItemCnt.OrderBy(x => x.Value).ToList(); - int maxLen = orderedList[^1].Value.ToString().Length; - StringBuilder sb = new(); - sb.Append(Translations.bot_autoFish_status_info); - foreach ((ItemType type, uint cnt) in orderedList) - { - sb.Append(Environment.NewLine); - - string cntStr = cnt.ToString(); - sb.Append(' ', maxLen - cntStr.Length).Append(cntStr); - sb.Append(" x "); - sb.Append(Item.GetTypeString(type)); - } - return sb.ToString(); - } - case "help": - return GetCommandHelp(args.Length >= 2 ? args[1] : ""); - default: - return GetHelp(); - } + private int OnCommandStart(CmdResult r) + { + isFishing = false; + lock (stateLock) + { + isFishing = false; + counter = 0; + state = FishingState.StartMove; } - else - return GetHelp(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_start); + } + + private int OnCommandStop(CmdResult r) + { + isFishing = false; + lock (stateLock) + { + isFishing = false; + if (state == FishingState.WaitingFishToBite) + UseFishRod(); + state = FishingState.Stopping; + } + StopFishing(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_stop); + } + + private int OnCommandStatus(CmdResult r) + { + if (fishItemCnt.Count == 0) + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_status_info); + + List> orderedList = fishItemCnt.OrderBy(x => x.Value).ToList(); + int maxLen = orderedList[^1].Value.ToString().Length; + StringBuilder sb = new(); + sb.Append(Translations.bot_autoFish_status_info); + foreach ((ItemType type, uint cnt) in orderedList) + { + sb.Append(Environment.NewLine); + + string cntStr = cnt.ToString(); + sb.Append(' ', maxLen - cntStr.Length).Append(cntStr); + sb.Append(" x "); + sb.Append(Item.GetTypeString(type)); + } + LogToConsole(sb.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int OnCommandStatusClear(CmdResult r) + { + fishItemCnt = new(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_autoFish_status_clear); } private void StartFishing() @@ -386,7 +425,7 @@ namespace MinecraftClient.ChatBots public override void OnEntitySpawn(Entity entity) { if (fishItemCounter < 15 && entity.Type == EntityType.Item && Math.Abs(entity.Location.Y - LastPos.Y) < 2.2 && - Math.Abs(entity.Location.X - LastPos.X) < 0.12 && Math.Abs(entity.Location.Z - LastPos.Z) < 0.12) + Math.Abs(entity.Location.X - LastPos.X) < 0.12 && Math.Abs(entity.Location.Z - LastPos.Z) < 0.12) { if (Config.Log_Fish_Bobber) LogToConsole(string.Format("Item ({0}) spawn at {1}, distance = {2:0.00}", entity.ID, entity.Location, entity.Location.Distance(LastPos))); @@ -440,7 +479,7 @@ namespace MinecraftClient.ChatBots public override void OnEntityMove(Entity entity) { - if (isFishing && entity != null && fishingBobber!.ID == entity.ID && + if (isFishing && entity != null && fishingBobber!.ID == entity.ID && (state == FishingState.WaitingFishToBite || state == FishingState.WaitingFishingBobber)) { Location Pos = entity.Location; @@ -620,24 +659,5 @@ namespace MinecraftClient.ChatBots return false; } } - - private static string GetHelp() - { - return string.Format(Translations.bot_autoFish_available_cmd, "start, stop, status, help"); - } - - private string GetCommandHelp(string cmd) - { - return cmd.ToLower() switch - { -#pragma warning disable format // @formatter:off - "start" => Translations.bot_autoFish_help_start, - "stop" => Translations.bot_autoFish_help_stop, - "status" => Translations.bot_autoFish_help_status, - "help" => Translations.bot_autoFish_help_help, - _ => GetHelp(), -#pragma warning restore format // @formatter:on - }; - } } } diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index 2795366e..ae69d76f 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -1,4 +1,7 @@ using System; +using Brigadier.NET; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -43,7 +46,7 @@ namespace MinecraftClient.ChatBots if (Delay.min > Delay.max) (Delay.min, Delay.max) = (Delay.max, Delay.min); - + if (Retries == -1) Retries = int.MaxValue; @@ -82,7 +85,12 @@ namespace MinecraftClient.ChatBots LogDebugToConsole(string.Format(Translations.bot_autoRelog_launch, Config.Retries)); } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) + { + Initialize(); + } + + private void Initialize() { McClient.ReconnectionAttemptsLeft = Config.Retries; if (Config.Ignore_Kick_Message) diff --git a/MinecraftClient/ChatBots/AutoRespond.cs b/MinecraftClient/ChatBots/AutoRespond.cs index 91279191..bb5ff233 100644 --- a/MinecraftClient/ChatBots/AutoRespond.cs +++ b/MinecraftClient/ChatBots/AutoRespond.cs @@ -4,6 +4,10 @@ using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; +using Brigadier.NET; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; +using PInvoke; using Tomlet.Attributes; using static MinecraftClient.Settings; @@ -183,7 +187,7 @@ namespace MinecraftClient.ChatBots /// /// Initialize the AutoRespond bot from the matches file /// - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (File.Exists(Config.Matches_File)) { @@ -304,12 +308,12 @@ namespace MinecraftClient.ChatBots { Dictionary localVars = new(); string? toPerform = rule.Match(sender, message, msgType, localVars); - if (!String.IsNullOrEmpty(toPerform)) + if (!string.IsNullOrEmpty(toPerform)) { - string? response = null; + CmdResult response = new(); LogToConsole(string.Format(Translations.bot_autoRespond_match_run, toPerform)); PerformInternalCommand(toPerform, ref response, localVars); - if (!String.IsNullOrEmpty(response)) + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) LogToConsole(response); } } diff --git a/MinecraftClient/ChatBots/ChatLog.cs b/MinecraftClient/ChatBots/ChatLog.cs index b6ad94ef..37aecd36 100644 --- a/MinecraftClient/ChatBots/ChatLog.cs +++ b/MinecraftClient/ChatBots/ChatLog.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -115,7 +117,7 @@ namespace MinecraftClient.ChatBots } } - public override void OnInternalCommand(string commandName, string commandParams, string result) + public override void OnInternalCommand(string commandName, string commandParams, CmdResult result) { if (saveInternal) { diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index 644be296..848fb86b 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -3,16 +3,23 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.Builder; using DSharpPlus; using DSharpPlus.Entities; using DSharpPlus.Exceptions; using Microsoft.Extensions.Logging; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class DiscordBridge : ChatBot { + public const string CommandName = "dscbridge"; + private enum BridgeDirection { Both = 0, @@ -68,19 +75,75 @@ namespace MinecraftClient.ChatBots instance = this; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { - RegisterChatBotCommand("dscbridge", "bot.DiscordBridge.desc", "dscbridge direction ", OnDscCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("direction") + .Then(l => l.Literal("both") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Both))) + .Then(l => l.Literal("mc") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Minecraft))) + .Then(l => l.Literal("discord") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Discord))) + ) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); Task.Run(async () => await MainAsync()); } - ~DiscordBridge() + public override void OnUnload(CommandDispatcher dispatcher) { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); Disconnect(); } - public override void OnUnload() + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => "dscbridge direction " + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandDirection(CmdResult r, BridgeDirection direction) + { + string bridgeName; + switch (direction) + { + case BridgeDirection.Both: + bridgeName = Translations.bot_DiscordBridge_direction_both; + bridgeDirection = BridgeDirection.Both; + break; + + case BridgeDirection.Minecraft: + bridgeName = Translations.bot_DiscordBridge_direction_minecraft; + bridgeDirection = BridgeDirection.Minecraft; + break; + + case BridgeDirection.Discord: + bridgeName = Translations.bot_DiscordBridge_direction_discord; + bridgeDirection = BridgeDirection.Discord; + break; + + default: + goto case BridgeDirection.Both; + } + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_DiscordBridge_direction, bridgeName)); + } + + ~DiscordBridge() { Disconnect(); } @@ -114,47 +177,6 @@ namespace MinecraftClient.ChatBots return instance; } - private string OnDscCommand(string cmd, string[] args) - { - if (args.Length == 2) - { - if (args[0].ToLower().Equals("direction")) - { - string direction = args[1].ToLower().Trim(); - - string bridgeName; - switch (direction) - { - case "b": - case "both": - bridgeName = Translations.bot_DiscordBridge_direction_both; - bridgeDirection = BridgeDirection.Both; - break; - - case "mc": - case "minecraft": - bridgeName = Translations.bot_DiscordBridge_direction_minecraft; - bridgeDirection = BridgeDirection.Minecraft; - break; - - case "d": - case "dcs": - case "discord": - bridgeName = Translations.bot_DiscordBridge_direction_discord; - bridgeDirection = BridgeDirection.Discord; - break; - - default: - return Translations.bot_DiscordBridge_invalid_direction; - } - - return string.Format(Translations.bot_DiscordBridge_direction, bridgeName); - }; - } - - return "dscbridge direction "; - } - public override void GetText(string text) { if (!CanSendMessages()) @@ -369,9 +391,8 @@ namespace MinecraftClient.ChatBots message = message[1..]; await e.Message.CreateReactionAsync(DiscordEmoji.FromName(discordBotClient, ":gear:")); - string? result = ""; + CmdResult result = new(); PerformInternalCommand(message, ref result); - result = string.IsNullOrEmpty(result) ? "-" : result; await e.Message.DeleteOwnReactionAsync(DiscordEmoji.FromName(discordBotClient, ":gear:")); await e.Message.CreateReactionAsync(DiscordEmoji.FromName(discordBotClient, ":white_check_mark:")); diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index 94af1191..e54e5ed7 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -3,34 +3,22 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { - enum State - { - SearchingForCropsToBreak = 0, - SearchingForFarmlandToPlant, - PlantingCrops, - BonemealingCrops - } - - enum CropType - { - Beetroot, - Carrot, - Melon, - Netherwart, - Pumpkin, - Potato, - Wheat - } - public class Farmer : ChatBot { + public const string CommandName = "farmer"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -42,15 +30,34 @@ namespace MinecraftClient.ChatBots public bool Enabled = false; [TomlInlineComment("$config.ChatBot.Farmer.Delay_Between_Tasks$")] - public int Delay_Between_Tasks = 1; + public double Delay_Between_Tasks = 1.0; public void OnSettingUpdate() { - if (Delay_Between_Tasks <= 0) - Delay_Between_Tasks = 1; + if (Delay_Between_Tasks < 1.0) + Delay_Between_Tasks = 1.0; } } + public enum State + { + SearchingForCropsToBreak = 0, + SearchingForFarmlandToPlant, + PlantingCrops, + BonemealingCrops + } + + public enum CropType + { + Beetroot, + Carrot, + Melon, + Netherwart, + Pumpkin, + Potato, + Wheat + } + private State state = State.SearchingForCropsToBreak; private CropType cropType = CropType.Wheat; private int farmingRadius = 30; @@ -59,9 +66,11 @@ namespace MinecraftClient.ChatBots private bool allowTeleport = false; private bool debugEnabled = false; + public int Delay_Between_Tasks_Millisecond => (int)Math.Round(Config.Delay_Between_Tasks * 1000); + private const string commandDescription = "farmer [radius:] [unsafe:] [teleport:] [debug:]|stop>"; - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (GetProtocolVersion() < Protocol18Handler.MC_1_13_Version) { @@ -81,130 +90,152 @@ namespace MinecraftClient.ChatBots return; } - RegisterChatBotCommand("farmer", "bot.farmer.desc", commandDescription, OnFarmCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("start") + .Then(l => l.Argument("CropType", MccArguments.FarmerCropType()) + .Executes(r => OnCommandStart(r.Source, MccArguments.GetFarmerCropType(r, "CropType"), null)) + .Then(l => l.Argument("OtherArgs", Arguments.GreedyString()) + .Executes(r => OnCommandStart(r.Source, MccArguments.GetFarmerCropType(r, "CropType"), Arguments.GetString(r, "OtherArgs")))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - private string OnFarmCommand(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - if (args[0].Equals("stop", StringComparison.OrdinalIgnoreCase)) - { - if (!running) - return Translations.bot_farmer_already_stopped; +#pragma warning disable format // @formatter:off + _ => Translations.bot_farmer_desc + ": " + commandDescription + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } - running = false; - return Translations.bot_farmer_stopping; - } + private int OnCommandStop(CmdResult r) + { + if (!running) + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_farmer_already_stopped); + } + else + { + running = false; + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_farmer_stopping); + } + } - if (args[0].Equals("start", StringComparison.OrdinalIgnoreCase)) + private int OnCommandStart(CmdResult r, CropType whatToFarm, string? otherArgs) + { + if (running) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_farmer_already_running); + + int radius = 30; + + state = State.SearchingForFarmlandToPlant; + cropType = whatToFarm; + allowUnsafe = false; + allowTeleport = false; + debugEnabled = false; + + if (!string.IsNullOrWhiteSpace(otherArgs)) + { + string[] args = otherArgs.ToLower().Split(' ', StringSplitOptions.TrimEntries); + foreach (string currentArg in args) { - if (args.Length >= 2) + if (!currentArg.Contains(':')) { - if (running) - return Translations.bot_farmer_already_running; + LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); + continue; + } - if (!Enum.TryParse(args[1], true, out CropType whatToFarm)) - return Translations.bot_farmer_invalid_crop_type; + string[] parts = currentArg.Split(":", StringSplitOptions.TrimEntries); - int radius = 30; + if (parts.Length != 2) + { + LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); + continue; + } - state = State.SearchingForFarmlandToPlant; - cropType = whatToFarm; - allowUnsafe = false; - allowTeleport = false; - debugEnabled = false; + switch (parts[0]) + { + case "r": + case "radius": + if (!int.TryParse(parts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out radius)) + LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); - if (args.Length >= 3) - { - for (int i = 2; i < args.Length; i++) + if (radius <= 0) { - string currentArg = args[i].Trim().ToLower(); - - if (!currentArg.Contains(':')) - { - LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); - continue; - } - - string[] parts = currentArg.Split(":", StringSplitOptions.TrimEntries); - - if (parts.Length != 2) - { - LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); - continue; - } - - switch (parts[0]) - { - case "r": - case "radius": - if (!int.TryParse(parts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out radius)) - LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); - - if (radius <= 0) - { - LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); - radius = 30; - } - - break; - - case "f": - case "unsafe": - if (allowUnsafe) - break; - - if (parts[1].Equals("true") || parts[1].Equals("1")) - { - LogToConsole("§x§1§0" + Translations.bot_farmer_warining_force_unsafe); - allowUnsafe = true; - } - else allowUnsafe = false; - - break; - - case "t": - case "teleport": - if (allowTeleport) - break; - - if (parts[1].Equals("true") || parts[1].Equals("1")) - { - LogToConsole("§w§1§f" + Translations.bot_farmer_warining_allow_teleport); - allowTeleport = true; - } - else allowTeleport = false; - - break; - - case "d": - case "debug": - if (debugEnabled) - break; - - if (parts[1].Equals("true") || parts[1].Equals("1")) - { - LogToConsole("Debug enabled!"); - debugEnabled = true; - } - else debugEnabled = false; - - break; - } + LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); + radius = 30; } - } - farmingRadius = radius; - running = true; - new Thread(() => MainPorcess()).Start(); + break; - return ""; + case "f": + case "unsafe": + if (allowUnsafe) + break; + + if (parts[1].Equals("true") || parts[1].Equals("1")) + { + LogToConsole("§x§1§0" + Translations.bot_farmer_warining_force_unsafe); + allowUnsafe = true; + } + else allowUnsafe = false; + + break; + + case "t": + case "teleport": + if (allowTeleport) + break; + + if (parts[1].Equals("true") || parts[1].Equals("1")) + { + LogToConsole("§w§1§f" + Translations.bot_farmer_warining_allow_teleport); + allowTeleport = true; + } + else allowTeleport = false; + + break; + + case "d": + case "debug": + if (debugEnabled) + break; + + if (parts[1].Equals("true") || parts[1].Equals("1")) + { + LogToConsole("Debug enabled!"); + debugEnabled = true; + } + else debugEnabled = false; + + break; } } } - return Translations.bot_farmer_desc + ": " + commandDescription; + farmingRadius = radius; + running = true; + new Thread(() => MainPorcess()).Start(); + + return r.SetAndReturn(CmdResult.Status.Done); } public override void AfterGameJoined() @@ -230,7 +261,7 @@ namespace MinecraftClient.ChatBots if (AutoEat.Eating) { LogDebug("Eating..."); - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -246,7 +277,7 @@ namespace MinecraftClient.ChatBots { LogDebug("No seeds, trying to find some crops to break"); state = State.SearchingForCropsToBreak; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -256,7 +287,7 @@ namespace MinecraftClient.ChatBots { LogDebug("Could not find any farmland, trying to find some crops to break"); state = State.SearchingForCropsToBreak; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -272,7 +303,7 @@ namespace MinecraftClient.ChatBots { LogDebug("Ran out of seeds, looking for crops to break..."); state = State.SearchingForCropsToBreak; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } } @@ -321,7 +352,7 @@ namespace MinecraftClient.ChatBots { LogToConsole("No crops to break, trying to bonemeal ungrown ones"); state = State.BonemealingCrops; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -367,7 +398,7 @@ namespace MinecraftClient.ChatBots if (cropType == CropType.Netherwart) { state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -376,7 +407,7 @@ namespace MinecraftClient.ChatBots { LogDebug("No bonemeal, searching for some farmland to plant seeds on"); state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -386,7 +417,7 @@ namespace MinecraftClient.ChatBots { LogDebug("No crops to bonemeal, searching for farmland to plant seeds on"); state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } @@ -402,7 +433,7 @@ namespace MinecraftClient.ChatBots { LogDebug("Ran out of Bone Meal, looking for farmland to plant on..."); state = State.SearchingForFarmlandToPlant; - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + Thread.Sleep(Delay_Between_Tasks_Millisecond); continue; } } @@ -438,8 +469,8 @@ namespace MinecraftClient.ChatBots break; } - LogDebug("Waiting for " + Config.Delay_Between_Tasks + " seconds for next cycle."); - Thread.Sleep(Config.Delay_Between_Tasks * 1000); + LogDebug(string.Format("Waiting for {0:0.00} seconds for next cycle.", Config.Delay_Between_Tasks)); + Thread.Sleep(Delay_Between_Tasks_Millisecond); } LogToConsole(Translations.bot_farmer_stopped); @@ -815,6 +846,7 @@ namespace MinecraftClient.ChatBots { return GetPlayerInventory().SearchItem(itemType).Length > 0; } + private void LogDebug(object text) { if (debugEnabled) diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index ebbf2c90..231332b4 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -1,12 +1,19 @@ using System; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class FollowPlayer : ChatBot { + public const string CommandName = "follow"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -37,7 +44,7 @@ namespace MinecraftClient.ChatBots private int _updateCounter = 0; private bool _unsafeEnabled = false; - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!GetEntityHandlingEnabled()) { @@ -55,58 +62,85 @@ namespace MinecraftClient.ChatBots return; } - RegisterChatBotCommand("follow", "cmd.follow.desc", "follow ", OnFollowCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("start") + .Then(l => l.Argument("PlayerName", MccArguments.PlayerName()) + .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: false)) + .Then(l => l.Literal("-f") + .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: true))))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - private string OnFollowCommand(string cmd, string[] args) + public override void OnUnload(CommandDispatcher dispatcher) { - if (args.Length > 0) + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch { - if (args[0].Equals("stop", StringComparison.OrdinalIgnoreCase)) - { - if (_playerToFollow == null) - return Translations.cmd_follow_already_stopped; +#pragma warning disable format // @formatter:off + _ => Translations.cmd_follow_desc + ": " + Translations.cmd_follow_usage + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } - _playerToFollow = null; - return Translations.cmd_follow_stopping; - } - else - { - if (!IsValidName(args[0])) - return Translations.cmd_follow_invalid_name; + private int OnCommandStart(CmdResult r, string name, bool takeRisk) + { + if (!IsValidName(name)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_name); - Entity? player = GetEntities().Values.ToList().Find(entity => - entity.Type == EntityType.Player && !string.IsNullOrEmpty(entity.Name) && entity.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase)); + Entity? player = GetEntities().Values.ToList().Find(entity => + entity.Type == EntityType.Player + && !string.IsNullOrEmpty(entity.Name) + && entity.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - if (player == null) - return Translations.cmd_follow_invalid_player; + if (player == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_invalid_player); - if (!CanMoveThere(player.Location)) - return Translations.cmd_follow_cant_reach_player; + if (!CanMoveThere(player.Location)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_cant_reach_player); - if (_playerToFollow != null && _playerToFollow.Equals(args[0], StringComparison.OrdinalIgnoreCase)) - return string.Format(Translations.cmd_follow_already_following, _playerToFollow); + if (_playerToFollow != null && _playerToFollow.Equals(name, StringComparison.OrdinalIgnoreCase)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_follow_already_following, _playerToFollow)); - string result; - if (_playerToFollow != null) - result = string.Format(Translations.cmd_follow_switched, player.Name!); - else - result = string.Format(Translations.cmd_follow_started, player.Name!); - _playerToFollow = args[0].Trim().ToLower(); + string result; + if (_playerToFollow != null) + result = string.Format(Translations.cmd_follow_switched, player.Name!); + else + result = string.Format(Translations.cmd_follow_started, player.Name!); + _playerToFollow = name.ToLower(); - LogToConsole(Translations.cmd_follow_note); - - if (args.Length == 2 && args[1].Equals("-f", StringComparison.OrdinalIgnoreCase)) - { - _unsafeEnabled = true; - LogToConsole(Translations.cmd_follow_unsafe_enabled); - } - - return result; - } + if (takeRisk) + { + _unsafeEnabled = true; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_note + '\n' + Translations.cmd_follow_unsafe_enabled); } + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_note); + } - return Translations.cmd_follow_desc + ": " + Translations.cmd_follow_usage; + private int OnCommandStop(CmdResult r) + { + if (_playerToFollow == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_follow_already_stopped); + + _playerToFollow = null; + + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_follow_stopping); } public override void Update() diff --git a/MinecraftClient/ChatBots/HangmanGame.cs b/MinecraftClient/ChatBots/HangmanGame.cs index 0eb923c9..ac04dd9e 100644 --- a/MinecraftClient/ChatBots/HangmanGame.cs +++ b/MinecraftClient/ChatBots/HangmanGame.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index b6f2ee12..7da1a312 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -3,6 +3,11 @@ using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -12,6 +17,8 @@ namespace MinecraftClient.ChatBots /// public class Mailer : ChatBot { + public const string CommandName = "mailer"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -216,7 +223,7 @@ namespace MinecraftClient.ChatBots /// /// Initialization of the Mailer bot /// - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { LogDebugToConsole(Translations.bot_mailer_init); LogDebugToConsole(Translations.bot_mailer_init_db + Config.DatabaseFile); @@ -251,7 +258,123 @@ namespace MinecraftClient.ChatBots 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.bot_mailer_cmd, "mailer ", ProcessInternalCommand); + dispatcher.Register(l => + l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(string.Empty)) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp(string.Empty))) + ) + ); + + dispatcher.Register(l => + l.Literal(CommandName) + .Executes(r => OnCommandHelp(string.Empty)) + .Then(l => l.Literal("getmails") + .Executes(r => OnCommandGetMails()) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("getmails"))) + ) + .Then(l => l.Literal("getignored") + .Executes(r => OnCommandGetIgnored()) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("getignored"))) + ) + .Then(l => l.Literal("addignored") + .Executes(r => OnCommandHelp("addignored")) + .Then(l => l.Argument("username", Arguments.String()) + .Executes(r => OnCommandAddIgnored(Arguments.GetString(r, "username"))) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("addignored"))) + ) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("addignored"))) + ) + .Then(l => l.Literal("removeignored") + .Executes(r => OnCommandHelp("removeignored")) + .Then(l => l.Argument("username", Arguments.String()) + .Executes(r => OnCommandRemoveIgnored(Arguments.GetString(r, "username"))) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("removeignored"))) + ) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp("removeignored"))) + ) + .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(string.Empty)) + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName)) + ) + .Then(l => l.Argument("any", Arguments.GreedyString()).Executes(r => OnCommandHelp(string.Empty))) + ); + } + + public override void OnUnload(CommandDispatcher dispatcher) + { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(string cmd) + { + LogToConsole(cmd switch + { +#pragma warning disable format // @formatter:off + _ => Translations.bot_mailer_cmd_help + ": /mailer " + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + return 1; + } + + private int OnCommandGetMails() + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_getmails, string.Join("\n", mailDatabase))); + return 1; + } + + private int OnCommandGetIgnored() + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_getignored, string.Join("\n", ignoreList))); + return 1; + } + + private int OnCommandAddIgnored(string username) + { + if (IsValidName(username)) + { + username = username.ToLower(); + lock (readWriteLock) + { + if (!ignoreList.Contains(username)) + { + ignoreList.Add(username); + ignoreList.SaveToFile(Config.IgnoreListFile); + } + } + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_added, username)); + return 1; + } + else + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_invalid, "addignored")); + return 0; + } + } + + private int OnCommandRemoveIgnored(string username) + { + if (IsValidName(username)) + { + username = username.ToLower(); + lock (readWriteLock) + { + if (ignoreList.Contains(username)) + { + ignoreList.Remove(username); + ignoreList.SaveToFile(Config.IgnoreListFile); + } + } + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_removed, username)); + return 1; + } + else + { + LogToConsole(string.Format(Translations.bot_mailer_cmd_ignore_invalid, "removeignored")); + return 0; + } } /// @@ -365,57 +488,5 @@ namespace MinecraftClient.ChatBots ignoreList = IgnoreList.FromFile(Config.IgnoreListFile); } } - - /// - /// Interprets local commands. - /// - private string ProcessInternalCommand(string cmd, string[] args) - { - if (args.Length > 0) - { - string commandName = args[0].ToLower(); - switch (commandName) - { - case "getmails": // Sorry, I (ReinforceZwei) replaced "=" to "-" because it would affect the parsing of translation file (key=value) - return string.Format(Translations.bot_mailer_cmd_getmails, string.Join("\n", mailDatabase)); - - case "getignored": - return string.Format(Translations.bot_mailer_cmd_getignored, string.Join("\n", ignoreList)); - - case "addignored": - case "removeignored": - if (args.Length > 1 && IsValidName(args[1])) - { - string username = args[1].ToLower(); - if (commandName == "addignored") - { - lock (readWriteLock) - { - if (!ignoreList.Contains(username)) - { - ignoreList.Add(username); - ignoreList.SaveToFile(Config.IgnoreListFile); - } - } - return string.Format(Translations.bot_mailer_cmd_ignore_added, args[1]); - } - else - { - lock (readWriteLock) - { - if (ignoreList.Contains(username)) - { - ignoreList.Remove(username); - ignoreList.SaveToFile(Config.IgnoreListFile); - } - } - return string.Format(Translations.bot_mailer_cmd_ignore_removed, args[1]); - } - } - else return string.Format(Translations.bot_mailer_cmd_ignore_invalid, commandName); - } - } - return Translations.bot_mailer_cmd_help + ": /help mailer"; - } } } diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index d0d01a64..fd06c769 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -1,18 +1,24 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Text; using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.Builder; using ImageMagick; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Mapping; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class Map : ChatBot { + public const string CommandName = "maps"; + public static Configs Config = new(); public struct QueuedMap @@ -63,25 +69,89 @@ namespace MinecraftClient.ChatBots private readonly string baseDirectory = @"Rendered_Maps"; - private readonly Dictionary cachedMaps = new(); + internal readonly Dictionary cachedMaps = new(); private readonly Queue discordQueue = new(); - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { if (!Directory.Exists(baseDirectory)) Directory.CreateDirectory(baseDirectory); DeleteRenderedMaps(); - RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render or maps l|r ", OnMapCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Executes(r => OnCommandList(r.Source)) + .Then(l => l.Literal("list") + .Executes(r => OnCommandList(r.Source))) + .Then(l => l.Literal("render") + .Then(l => l.Argument("MapID", MccArguments.MapBotMapId()) + .Executes(r => OnCommandRender(r.Source, Arguments.GetInteger(r, "MapID"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); } - public override void OnUnload() + public override void OnUnload(CommandDispatcher dispatcher) { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); DeleteRenderedMaps(); } + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => Translations.error_usage + ": /maps >" + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandList(CmdResult r) + { + if (cachedMaps.Count == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_map_no_maps); + + LogToConsole(Translations.bot_map_received); + + foreach (var (key, value) in new SortedDictionary(cachedMaps)) + LogToConsole(string.Format(Translations.bot_map_list_item, key, value.LastUpdated)); + + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int OnCommandRender(CmdResult r, int mapId) + { + if (!cachedMaps.ContainsKey(mapId)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.bot_map_cmd_not_found, mapId)); + + try + { + McMap map = cachedMaps[mapId]; + if (Config.Save_To_File) + SaveToFile(map); + + if (Config.Render_In_Console) + RenderInConsole(map); + + return r.SetAndReturn(CmdResult.Status.Done); + } + catch (Exception e) + { + LogDebugToConsole(e.StackTrace!); + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.bot_map_failed_to_render, mapId)); + } + } + private void DeleteRenderedMaps() { if (Config.Delete_All_On_Unload) @@ -94,56 +164,6 @@ namespace MinecraftClient.ChatBots } } - public string OnMapCommand(string command, string[] args) - { - if (args.Length == 0 || (args.Length == 1 && (args[0].ToLower().Equals("list") || args[0].ToLower().Equals("l")))) - { - if (cachedMaps.Count == 0) - return Translations.bot_map_no_maps; - - LogToConsole(Translations.bot_map_received); - - foreach (var (key, value) in new SortedDictionary(cachedMaps)) - LogToConsole(string.Format(Translations.bot_map_list_item, key, value.LastUpdated)); - - return ""; - } - - if (args.Length > 1) - { - if (args[0].ToLower().Equals("render") || args[0].ToLower().Equals("r")) - { - if (args.Length < 2) - return "maps > | maps >"; - - if (int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int mapId)) - { - if (!cachedMaps.ContainsKey(mapId)) - return string.Format(Translations.bot_map_cmd_not_found, mapId); - - try - { - McMap map = cachedMaps[mapId]; - if (Config.Save_To_File) - SaveToFile(map); - - if (Config.Render_In_Console) - RenderInConsole(map); - - return ""; - } - catch (Exception e) - { - LogDebugToConsole(e.StackTrace!); - return string.Format(Translations.bot_map_failed_to_render, mapId); - } - } - return Translations.bot_map_cmd_invalid_id; - } - } - return ""; - } - public override void OnMapData(int mapid, byte scale, bool trackingPosition, bool locked, List icons, byte columnsUpdated, byte rowsUpdated, byte mapCoulmnX, byte mapRowZ, byte[]? colors) { if (columnsUpdated == 0 && cachedMaps.ContainsKey(mapid)) @@ -320,7 +340,7 @@ namespace MinecraftClient.ChatBots } } - private void RenderInConsole(McMap map) + private static void RenderInConsole(McMap map) { StringBuilder sb = new(); @@ -406,7 +426,7 @@ namespace MinecraftClient.ChatBots public DateTime LastUpdated { get; set; } } - class MapColors + internal class MapColors { // When colors are updated in a new update, you can get them using the game code: net\minecraft\world\level\material\MaterialColor.java public static Dictionary Colors = new() diff --git a/MinecraftClient/ChatBots/PlayerListLogger.cs b/MinecraftClient/ChatBots/PlayerListLogger.cs index 8c59af89..4b0ad7d4 100644 --- a/MinecraftClient/ChatBots/PlayerListLogger.cs +++ b/MinecraftClient/ChatBots/PlayerListLogger.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/RemoteControl.cs b/MinecraftClient/ChatBots/RemoteControl.cs index ac1d544e..301e16f8 100644 --- a/MinecraftClient/ChatBots/RemoteControl.cs +++ b/MinecraftClient/ChatBots/RemoteControl.cs @@ -1,4 +1,6 @@ using System; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -32,17 +34,9 @@ namespace MinecraftClient.ChatBots string command = "", sender = ""; if (IsPrivateMessage(text, ref command, ref sender) && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim())) { - string? response = ""; + CmdResult response = new(); PerformInternalCommand(command, ref response); - response = GetVerbatim(response); - foreach (char disallowedChar in McClient.GetDisallowedChatCharacters()) - { - response = response.Replace(disallowedChar.ToString(), String.Empty); - } - if (response.Length > 0) - { - SendPrivateMessage(sender, response); - } + SendPrivateMessage(sender, response.ToString()); } else if (Config.AutoTpaccept && IsTeleportRequest(text, ref sender) diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index 26315ef2..ee0c0a55 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -1,6 +1,11 @@ using System; using System.Collections.Generic; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Protocol; +using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots @@ -10,6 +15,8 @@ namespace MinecraftClient.ChatBots /// public class ReplayCapture : ChatBot { + public const string CommandName = "replay"; + public static Configs Config = new(); [TomlDoNotInlineObject] @@ -33,14 +40,80 @@ namespace MinecraftClient.ChatBots private ReplayHandler? replay; private int backupCounter = -1; - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { SetNetworkPacketEventEnabled(true); replay = new ReplayHandler(GetProtocolVersion()); replay.MetaData.serverName = GetServerHost() + GetServerPort(); backupCounter = Settings.DoubleToTick(Config.Backup_Interval); - RegisterChatBotCommand("replay", Translations.bot_replayCapture_cmd, "replay ", Command); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("save") + .Executes(r => OnCommandSave(r.Source))) + .Then(l => l.Literal("stop") + .Executes(r => OnCommandStop(r.Source))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); + } + + public override void OnUnload(CommandDispatcher dispatcher) + { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + } + + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => string.Format(Translations.general_available_cmd, "save, stop") + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandSave(CmdResult r) + { + try + { + if (replay!.RecordRunning) + { + replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName()); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_created); + } + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_replayCapture_restart); + } + catch (Exception e) + { + return r.SetAndReturn(CmdResult.Status.Fail, e.Message); + } + } + + private int OnCommandStop(CmdResult r) + { + try + { + if (replay!.RecordRunning) + { + replay.OnShutDown(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.bot_replayCapture_stopped); + } + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.bot_replayCapture_restart); + } + catch (Exception e) + { + return r.SetAndReturn(CmdResult.Status.Fail, e.Message); + } } public override void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) @@ -66,37 +139,5 @@ namespace MinecraftClient.ChatBots replay!.OnShutDown(); return base.OnDisconnect(reason, message); } - - public string Command(string cmd, string[] args) - { - try - { - if (replay!.RecordRunning) - { - if (args.Length > 0) - { - switch (args[0].ToLower()) - { - case "save": - { - replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName()); - return Translations.bot_replayCapture_created; - } - case "stop": - { - replay.OnShutDown(); - return Translations.bot_replayCapture_stopped; - } - } - } - return string.Format(Translations.general_available_cmd, "save, stop"); - } - else return Translations.bot_replayCapture_restart; - } - catch (Exception e) - { - return e.Message; - } - } } } diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 11b9d97e..8f706e11 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -5,6 +5,9 @@ using System.IO; using System.Reflection; using System.Text; using System.Threading; +using Brigadier.NET; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; namespace MinecraftClient.ChatBots { @@ -124,7 +127,7 @@ namespace MinecraftClient.ChatBots return false; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { //Load the given file from the startup parameters if (LookForScript(ref file!)) diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index 21e9a977..bf8680a4 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -1,4 +1,5 @@ using System; +using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.ScriptScheduler.Configs; diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index fb1a93ca..8c98d4cf 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -3,6 +3,11 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Scripting; using Telegram.Bot; using Telegram.Bot.Exceptions; using Telegram.Bot.Polling; @@ -16,6 +21,8 @@ namespace MinecraftClient.ChatBots { public class TelegramBridge : ChatBot { + public const string CommandName = "tgbridge"; + private enum BridgeDirection { Both = 0, @@ -68,19 +75,74 @@ namespace MinecraftClient.ChatBots instance = this; } - public override void Initialize() + public override void Initialize(CommandDispatcher dispatcher) { - RegisterChatBotCommand("tgbridge", "bot.TelegramBridge.desc", "tgbridge direction ", OnTgCommand); + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CommandName) + .Executes(r => OnCommandHelp(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CommandName) + .Then(l => l.Literal("direction") + .Then(l => l.Literal("both") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Both))) + .Then(l => l.Literal("mc") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Minecraft))) + .Then(l => l.Literal("telegram") + .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Telegram)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + ); Task.Run(async () => await MainAsync()); } - ~TelegramBridge() + public override void OnUnload(CommandDispatcher dispatcher) { + dispatcher.Unregister(CommandName); + dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); Disconnect(); } - public override void OnUnload() + private int OnCommandHelp(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => Translations.error_usage + ": /tgbridge direction " + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), +#pragma warning restore format // @formatter:on + }); + } + + private int OnCommandDirection(CmdResult r, BridgeDirection direction) + { + string bridgeName; + switch (direction) + { + case BridgeDirection.Both: + bridgeName = Translations.bot_TelegramBridge_direction_both; + bridgeDirection = BridgeDirection.Both; + break; + + case BridgeDirection.Minecraft: + bridgeName = Translations.bot_TelegramBridge_direction_minecraft; + bridgeDirection = BridgeDirection.Minecraft; + break; + + case BridgeDirection.Telegram: + bridgeName = Translations.bot_TelegramBridge_direction_Telegram; + bridgeDirection = BridgeDirection.Telegram; + break; + + default: + goto case BridgeDirection.Both; + } + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.bot_TelegramBridge_direction, bridgeName)); + } + + ~TelegramBridge() { Disconnect(); } @@ -110,47 +172,6 @@ namespace MinecraftClient.ChatBots return instance; } - private string OnTgCommand(string cmd, string[] args) - { - if (args.Length == 2) - { - if (args[0].ToLower().Equals("direction")) - { - string direction = args[1].ToLower().Trim(); - - string bridgeName; - switch (direction) - { - case "b": - case "both": - bridgeName = Translations.bot_TelegramBridge_direction_both; - bridgeDirection = BridgeDirection.Both; - break; - - case "mc": - case "minecraft": - bridgeName = Translations.bot_TelegramBridge_direction_minecraft; - bridgeDirection = BridgeDirection.Minecraft; - break; - - case "t": - case "tg": - case "telegram": - bridgeName = Translations.bot_TelegramBridge_direction_Telegram; - bridgeDirection = BridgeDirection.Telegram; - break; - - default: - return Translations.bot_TelegramBridge_invalid_direction; - } - - return string.Format(Translations.bot_TelegramBridge_direction, bridgeName); - }; - } - - return "dscbridge direction "; - } - public override void GetText(string text) { if (!CanSendMessages()) @@ -324,9 +345,8 @@ namespace MinecraftClient.ChatBots { var command = text[1..]; - string? result = ""; + CmdResult result = new(); PerformInternalCommand(command, ref result); - result = string.IsNullOrEmpty(result) ? "-" : result; await botClient.SendTextMessageAsync( chatId: chatId, diff --git a/MinecraftClient/ChatBots/TestBot.cs b/MinecraftClient/ChatBots/TestBot.cs index f96e9e8a..8b103535 100644 --- a/MinecraftClient/ChatBots/TestBot.cs +++ b/MinecraftClient/ChatBots/TestBot.cs @@ -1,4 +1,6 @@ -namespace MinecraftClient.ChatBots +using MinecraftClient.Scripting; + +namespace MinecraftClient.ChatBots { /// /// Example of message receiving. diff --git a/MinecraftClient/Command.cs b/MinecraftClient/Command.cs index ddb99ac3..6a6e8ae5 100644 --- a/MinecraftClient/Command.cs +++ b/MinecraftClient/Command.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Text; using Brigadier.NET; -using Microsoft.Extensions.Logging; -using MinecraftClient.Commands; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; namespace MinecraftClient { @@ -25,34 +25,19 @@ namespace MinecraftClient /// public abstract string CmdDesc { get; } - public abstract void RegisterCommand(McClient handler, CommandDispatcher dispatcher); - /// /// Get the translated version of command description. /// /// Translated command description public string GetCmdDescTranslated() { - string s = (string.IsNullOrEmpty(CmdUsage) || string.IsNullOrEmpty(CmdDesc)) ? "" : ": "; // If either one is empty, no colon : - return CmdUsage + s + CmdDesc; - } + char cmdChar = Settings.Config.Main.Advanced.InternalCmdChar.ToChar(); - public void LogUsage(Logger.ILogger logger) - { - logger.Info($"{Translations.Get("error.usage")}: {Settings.Config.Main.Advanced.InternalCmdChar.ToChar()}{CmdUsage}"); - } - - - public static int LogExecuteResult(Logger.ILogger logger, bool result) - { - logger.Info(Translations.Get(result ? "general.done" : "general.fail")); - return result ? 1 : 0; - } - - public static int LogExecuteResult(Logger.ILogger logger, int result) - { - logger.Info(Translations.Get(result > 0 ? "general.done" : "general.fail")); - return result; + StringBuilder sb = new(); + string s = (string.IsNullOrEmpty(CmdUsage) || string.IsNullOrEmpty(CmdDesc)) ? string.Empty : ": "; // If either one is empty, no colon : + sb.Append("§e").Append(cmdChar).Append(CmdUsage).Append("§r").Append(s).AppendLine(CmdDesc); + sb.Append(McClient.dispatcher.GetAllUsageString(CmdName, false)); + return sb.ToString(); } /// @@ -61,18 +46,9 @@ namespace MinecraftClient public abstract string CmdUsage { get; } /// - /// Perform the command + /// Register the command. /// - /// The full command, eg: 'mycommand arg1 arg2' - /// Local variables passed along with the command (may be null) - /// A confirmation/error message, or "" if no message - public abstract string Run(McClient handler, string command, Dictionary? localVars); - - /// - /// Return a list of aliases for this command. - /// Override this method if you wish to put aliases to the command - /// - public virtual IEnumerable GetCMDAliases() { return Array.Empty(); } + public abstract void RegisterCommand(McClient handler, CommandDispatcher dispatcher); /// /// Check if at least one argument has been passed to the command diff --git a/MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs new file mode 100644 index 00000000..09d11de1 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/AccountNickArgumentType.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class AccountNickArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + var accountList = Settings.Config.Main.Advanced.AccountList; + foreach (var account in accountList) + builder.Suggest(account.Key); + return builder.BuildFuture(); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs new file mode 100644 index 00000000..0d2bc0eb --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/AutoCraftRecipeNameArgumentType.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class AutoCraftRecipeNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + var recipeList = Settings.Config.ChatBot.AutoCraft.Recipes; + foreach (var recipe in recipeList) + builder.Suggest(recipe.Name); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs new file mode 100644 index 00000000..0681ae0d --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class BotNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var botList = client.GetLoadedChatBots(); + foreach (var bot in botList) + builder.Suggest(bot.GetType().Name); + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs new file mode 100644 index 00000000..9ba6f999 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using MinecraftClient.Mapping; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class EntityTypeArgumentType : ArgumentType + { + public override EntityType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string entity = reader.ReadString(); + if (Enum.TryParse(entity, true, out EntityType entityType)) + return entityType; + else + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, entity); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var result in Enum.GetNames(typeof(EntityType))) + builder.Suggest(result); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs new file mode 100644 index 00000000..16ecdad4 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/FarmerCropTypeArgumentType.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using static MinecraftClient.ChatBots.Farmer; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class FarmerCropTypeArgumentType : ArgumentType + { + public override CropType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string inputStr = reader.ReadString(); + if (Enum.TryParse(inputStr, true, out CropType cropType)) + return cropType; + else + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, inputStr); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var result in Enum.GetNames(typeof(CropType))) + builder.Suggest(result); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs new file mode 100644 index 00000000..89616af6 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using MinecraftClient.Inventory; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class InventoryActionArgumentType : ArgumentType + { + private WindowActionType[] SupportActions = new WindowActionType[] + { + WindowActionType.LeftClick, + WindowActionType.RightClick, + WindowActionType.MiddleClick, + WindowActionType.ShiftClick, + }; + + public override WindowActionType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string inputStr = reader.ReadString(); + foreach (var action in SupportActions) + { + string actionStr = action.ToString(); + if (string.Compare(inputStr, actionStr, true) == 0) + return action; + } + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, inputStr); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var action in SupportActions) + builder.Suggest(action.ToString()); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs new file mode 100644 index 00000000..c697b493 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class InventoryIdArgumentType : ArgumentType + { + public override int Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadInt(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var invList = client.GetInventories(); + foreach (var inv in invList) + { + string invName = inv.Key.ToString(); + if (invName.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) + builder.Suggest(invName); + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs new file mode 100644 index 00000000..28ab93a3 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Exceptions; +using Brigadier.NET.Suggestion; +using MinecraftClient.Inventory; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class ItemTypeArgumentType : ArgumentType + { + public override ItemType Parse(IStringReader reader) + { + reader.SkipWhitespace(); + string entity = reader.ReadString(); + if (Enum.TryParse(entity, true, out ItemType itemType)) + return itemType; + else + throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, entity); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + foreach (var result in Enum.GetNames(typeof(ItemType))) + builder.Suggest(result); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs new file mode 100644 index 00000000..79f51d62 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; +using MinecraftClient.Mapping; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class LocationArgumentType : ArgumentType + { + public override Location Parse(IStringReader reader) + { + int[] status = new int[3]; + double[] coords = new double[3]; + for (int i = 0; i < 3; ++i) + { + reader.SkipWhitespace(); + if (reader.Peek() == '~' || reader.Peek() == '~') + { + status[i] = 1; + reader.Next(); + if (reader.CanRead()) + { + char next = reader.Peek(); + if (char.IsDigit(next) || next == '.' || next == '-') + coords[i] = reader.ReadDouble(); + else if (next == '+') + { + reader.Next(); + coords[i] = reader.ReadDouble(); + } + else coords[i] = 0; + } + else coords[i] = 0; + } + else + { + status[i] = 0; + coords[i] = reader.ReadDouble(); + } + } + + return new Location(coords[0], coords[1], coords[2], (byte)(status[0] | status[1] << 1 | status[2] << 2)); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + string[] args = builder.Remaining.Split(' ', StringSplitOptions.TrimEntries); + if (args.Length == 0 || (args.Length == 1 && string.IsNullOrWhiteSpace(args[0]))) + { + if (client != null) + { + Location current = client.GetCurrentLocation(); + builder.Suggest(string.Format("{0:0.00}", current.X)); + builder.Suggest(string.Format("{0:0.00} {1:0.00}", current.X, current.Y)); + builder.Suggest(string.Format("{0:0.00} {1:0.00} {2:0.00}", current.X, current.Y, current.Z)); + } + else + { + builder.Suggest("~"); + builder.Suggest("~ ~"); + builder.Suggest("~ ~ ~"); + } + } + else if (args.Length == 1 || (args.Length == 2 && string.IsNullOrWhiteSpace(args[1]))) + { + string add = args.Length == 1 ? " " : string.Empty; + if (client != null) + { + Location current = client.GetCurrentLocation(); + builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Y, add)); + builder.Suggest(string.Format("{0}{3}{1:0.00} {2:0.00}", builder.Remaining, current.Y, current.Z, add)); + } + else + { + builder.Suggest(builder.Remaining + add + "~"); + builder.Suggest(builder.Remaining + add + "~ ~"); + } + } + else if (args.Length == 2 || (args.Length == 3 && string.IsNullOrWhiteSpace(args[2]))) + { + string add = args.Length == 2 ? " " : string.Empty; + if (client != null) + { + Location current = client.GetCurrentLocation(); + builder.Suggest(string.Format("{0}{2}{1:0.00}", builder.Remaining, current.Z, add)); + } + else + { + builder.Suggest(builder.Remaining + add + "~"); + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs new file mode 100644 index 00000000..55c05020 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; +using MinecraftClient.ChatBots; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class MapBotMapIdArgumentType : ArgumentType + { + public override int Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadInt(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var bot = (Map?)client.GetLoadedChatBots().Find(bot => bot.GetType().Name == "Map"); + if (bot != null) + { + var mapList = bot.cachedMaps; + foreach (var map in mapList) + { + string mapName = map.Key.ToString(); + if (mapName.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) + builder.Suggest(mapName); + } + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs new file mode 100644 index 00000000..d8b88093 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs @@ -0,0 +1,36 @@ +using System.Linq; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; +using MinecraftClient.Mapping; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class PlayerNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + var entityList = client.GetEntities().Values.ToList(); + foreach (var entity in entityList) + { + if (entity.Type != EntityType.Player || string.IsNullOrWhiteSpace(entity.Name)) + continue; + builder.Suggest(entity.Name); + } + builder.Suggest(client.GetUsername()); + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs new file mode 100644 index 00000000..ae520f7d --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/ServerNickArgumentType.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class ServerNickArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadString(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + var serverList = Settings.Config.Main.Advanced.ServerList; + foreach (var server in serverList) + builder.Suggest(server.Key); + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs new file mode 100644 index 00000000..55157d99 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/TupleArgumentType.cs @@ -0,0 +1,18 @@ +using System; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class TupleArgumentType : ArgumentType> + { + public override Tuple Parse(IStringReader reader) + { + reader.SkipWhitespace(); + int int1 = reader.ReadInt(); + reader.SkipWhitespace(); + int int2 = reader.ReadInt(); + return new(int1, int2); + } + } +} diff --git a/MinecraftClient/CommandHandler/CmdResult.cs b/MinecraftClient/CommandHandler/CmdResult.cs new file mode 100644 index 00000000..b5477763 --- /dev/null +++ b/MinecraftClient/CommandHandler/CmdResult.cs @@ -0,0 +1,96 @@ +using System; + +namespace MinecraftClient.CommandHandler +{ + public class CmdResult + { + public static readonly CmdResult Empty = new(); + + internal static McClient? client; + + public enum Status + { + NotRun = int.MinValue, + FailChunkNotLoad = -4, + FailNeedEntity = -3, + FailNeedInventory = -2, + FailNeedTerrain = -1, + Fail = 0, + Done = 1, + } + + public CmdResult() + { + this.status = Status.NotRun; + this.result = null; + } + + public Status status; + + public string? result; + + public int SetAndReturn(Status status) + { + this.status = status; + this.result = status switch + { +#pragma warning disable format // @formatter:off + Status.NotRun => null, + Status.FailChunkNotLoad => null, + Status.FailNeedEntity => Translations.extra_entity_required, + Status.FailNeedInventory => Translations.extra_inventory_required, + Status.FailNeedTerrain => Translations.extra_terrainandmovement_required, + Status.Fail => Translations.general_fail, + Status.Done => null, + _ => null, +#pragma warning restore format // @formatter:on + }; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(Status status, string? result) + { + this.status = status; + this.result = result; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(int code, string? result) + { + this.status = (Status)Enum.ToObject(typeof(Status), code); + if (!Enum.IsDefined(typeof(Status), status)) + throw new InvalidOperationException($"{code} is not a legal return value."); + this.result = result; + return code; + } + + public int SetAndReturn(bool result) + { + this.status = result ? Status.Done : Status.Fail; + this.result = result ? Translations.general_done : Translations.general_fail; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(string? result) + { + this.status = Status.Done; + this.result = result; + return Convert.ToInt32(this.status); + } + + public int SetAndReturn(string? resultstr, bool result) + { + this.status = result ? Status.Done : Status.Fail; + this.result = resultstr; + return Convert.ToInt32(this.status); + } + + public override string ToString() + { + if (result != null) + return result; + else + return status.ToString(); + } + } +} diff --git a/MinecraftClient/CommandHandler/MccArguments.cs b/MinecraftClient/CommandHandler/MccArguments.cs new file mode 100644 index 00000000..ef6fbf8a --- /dev/null +++ b/MinecraftClient/CommandHandler/MccArguments.cs @@ -0,0 +1,104 @@ +using System; +using Brigadier.NET.Context; +using MinecraftClient.CommandHandler.ArgumentType; + +namespace MinecraftClient.CommandHandler +{ + public static class MccArguments + { + public static LocationArgumentType Location() + { + return new LocationArgumentType(); + } + + public static Mapping.Location GetLocation(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static TupleArgumentType Tuple() + { + return new TupleArgumentType(); + } + + public static Tuple GetTuple(CommandContext context, string name) + { + return context.GetArgument>(name); + } + + public static EntityTypeArgumentType EntityType() + { + return new EntityTypeArgumentType(); + } + + public static Mapping.EntityType GetEntityType(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static ItemTypeArgumentType ItemType() + { + return new ItemTypeArgumentType(); + } + + public static Inventory.ItemType GetItemType(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static BotNameArgumentType BotName() + { + return new BotNameArgumentType(); + } + + public static ServerNickArgumentType ServerNick() + { + return new ServerNickArgumentType(); + } + + public static AccountNickArgumentType AccountNick() + { + return new AccountNickArgumentType(); + } + + public static InventoryIdArgumentType InventoryId() + { + return new InventoryIdArgumentType(); + } + + public static InventoryActionArgumentType InventoryAction() + { + return new InventoryActionArgumentType(); + } + + public static Inventory.WindowActionType GetInventoryAction(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static AutoCraftRecipeNameArgumentType AutoCraftRecipeName() + { + return new AutoCraftRecipeNameArgumentType(); + } + + public static FarmerCropTypeArgumentType FarmerCropType() + { + return new FarmerCropTypeArgumentType(); + } + + public static ChatBots.Farmer.CropType GetFarmerCropType(CommandContext context, string name) + { + return context.GetArgument(name); + } + + public static PlayerNameArgumentType PlayerName() + { + return new PlayerNameArgumentType(); + } + + public static MapBotMapIdArgumentType MapBotMapId() + { + return new MapBotMapIdArgumentType(); + } + } +} diff --git a/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs b/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs new file mode 100644 index 00000000..341fa4ae --- /dev/null +++ b/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs @@ -0,0 +1,36 @@ +using System.Text; +using Brigadier.NET; + +namespace MinecraftClient.CommandHandler.Patch +{ + public static class CommandDispatcherExtensions + { + /** + * This method unregisteres a previously declared command + * + * @param The name of the command to remove + */ + public static void Unregister(this CommandDispatcher commandDispatcher, string commandname) + { + commandDispatcher.GetRoot().RemoveChild(commandname); + } + + public static string GetAllUsageString(this CommandDispatcher commandDispatcher, string commandName, bool restricted) + { + char cmdChar = Settings.Config.Main.Advanced.InternalCmdChar.ToChar(); + string[] usages = commandDispatcher.GetAllUsage(commandDispatcher.GetRoot().GetChild(commandName), new(), restricted); + StringBuilder sb = new(); + sb.AppendLine("All Usages:"); + foreach (var usage in usages) + { + sb.Append(cmdChar).Append(commandName).Append(' '); + if (usage.Length > 0 && usage[0] == '_') + sb.AppendLine(usage.Replace("_help -> ", $"_help -> {cmdChar}help ")); + else + sb.AppendLine(usage); + } + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs b/MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs new file mode 100644 index 00000000..23e2a87f --- /dev/null +++ b/MinecraftClient/CommandHandler/Patch/CommandNodeExtensions.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Reflection; +using Brigadier.NET.Tree; + +namespace MinecraftClient.CommandHandler.Patch +{ + public static class CommandNodeExtensions + { + public static void RemoveChild(this CommandNode commandNode, string name) + { + var children = (IDictionary>) + typeof(CommandNode) + .GetField("_children", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(commandNode)!; + var literals = (IDictionary>) + typeof(CommandNode) + .GetField("_literals", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(commandNode)!; + var arguments = (IDictionary>) + typeof(CommandNode) + .GetField("_arguments", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(commandNode)!; + + children.Remove(name); + literals.Remove(name); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/Commands/Animation.cs b/MinecraftClient/Commands/Animation.cs index 54efe765..41f7affe 100644 --- a/MinecraftClient/Commands/Animation.cs +++ b/MinecraftClient/Commands/Animation.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -11,76 +10,44 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "animation "; } } public override string CmdDesc { get { return Translations.cmd_animation_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { - dispatcher.Register(l => - l.Literal("help").Then(l => - l.Literal(CmdName).Executes(c => { - LogUsage(handler.Log); - return 1; - }) + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("mainhand") + .Executes(r => GetUsage(r.Source, "mainhand"))) + .Then(l => l.Literal("offhand") + .Executes(r => GetUsage(r.Source, "offhand"))) ) ); - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("mainhand") - .Executes(c => { - return LogExecuteResult(handler.Log, handler.DoAnimation(0)); - }) - ) - ); - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("0") - .Redirect(dispatcher.GetRoot().GetChild(CmdName).GetChild("mainhand")) - ) - ); - - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("offhand") - .Executes(c => { - return LogExecuteResult(handler.Log, handler.DoAnimation(1)); - }) - ) - ); - dispatcher.Register(l => - l.Literal(CmdName).Then(l => - l.Literal("1") - .Redirect(dispatcher.GetRoot().GetChild(CmdName).GetChild("offhand")) - ) + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoAnimation(r.Source, handler, mainhand: true)) + .Then(l => l.Literal("mainhand") + .Executes(r => DoAnimation(r.Source, handler, mainhand: true))) + .Then(l => l.Literal("offhand") + .Executes(r => DoAnimation(r.Source, handler, mainhand: false))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length > 0) - { - if (args[0] == "mainhand" || args[0] == "0") - { - handler.DoAnimation(0); - return Translations.general_done; - } - else if (args[0] == "offhand" || args[0] == "1") - { - handler.DoAnimation(1); - return Translations.general_done; - } - else - { - return GetCmdDescTranslated(); - } - } - else - { - return GetCmdDescTranslated(); - } - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + "mainhand" => GetCmdDescTranslated(), + "offhand" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int DoAnimation(CmdResult r, McClient handler, bool mainhand) + { + return r.SetAndReturn(handler.DoAnimation(mainhand ? 1 : 0)); } } } diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs index 6d7e774c..fa6e6958 100644 --- a/MinecraftClient/Commands/Bed.cs +++ b/MinecraftClient/Commands/Bed.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -14,142 +16,162 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bed leave|sleep |sleep "; } } public override string CmdDesc { get { return Translations.cmd_bed_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("leave") + .Executes(r => GetUsage(r.Source, "leave"))) + .Then(l => l.Literal("sleep") + .Executes(r => GetUsage(r.Source, "sleep"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("leave") + .Executes(r => DoLeaveBed(r.Source, handler))) + .Then(l => l.Literal("sleep") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DoSleepBedWithLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Radius", Arguments.Double()) + .Executes(r => DoSleepBedWithRadius(r.Source, handler, Arguments.GetDouble(r, "Radius"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - string[] args = GetArgs(command); - - if (args.Length >= 1) + return r.SetAndReturn(cmd switch { - string subcommand = args[0].ToLower().Trim(); +#pragma warning disable format // @formatter:off + "leave" => GetCmdDescTranslated(), + "sleep" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (subcommand.Equals("leave") || subcommand.Equals("l")) + private static int DoLeaveBed(CmdResult r, McClient handler) + { + return r.SetAndReturn(Translations.cmd_bed_leaving, handler.SendEntityAction(Protocol.EntityActionType.LeaveBed)); + } + + private static int DoSleepBedWithRadius(CmdResult r, McClient handler, double radius) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + handler.Log.Info(string.Format(Translations.cmd_bed_searching, radius)); + + Location current = handler.GetCurrentLocation(); + Location bedLocation = current; + + Material[] bedMaterialList = new Material[] + { + Material.BlackBed, + Material.BlueBed, + Material.BrownBed, + Material.CyanBed, + Material.GrayBed, + Material.GreenBed, + Material.LightBlueBed, + Material.LightGrayBed, + Material.LimeBed, + Material.MagentaBed, + Material.OrangeBed, + Material.PinkBed, + Material.PurpleBed, + Material.RedBed, + Material.WhiteBed, + Material.YellowBed + }; + + bool found = false; + foreach (Material material in bedMaterialList) + { + List beds = handler.GetWorld().FindBlock(current, material, radius); + + if (beds.Count > 0) { - handler.SendEntityAction(Protocol.EntityActionType.LeaveBed); - return Translations.cmd_bed_leaving; - } - - if (subcommand.Equals("sleep") || subcommand.Equals("s")) - { - if (!handler.GetTerrainEnabled()) - return Translations.error_terrain_not_enabled; - - if (args.Length == 2) - { - if (!int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int radius)) - return CmdUsage; - - handler.GetLogger().Info(string.Format(Translations.cmd_bed_searching, radius)); - - Location current = handler.GetCurrentLocation(); - Location bedLocation = current; - - Material[] bedMaterialList = new Material[]{ - Material.BlackBed, - Material.BlueBed, - Material.BrownBed, - Material.CyanBed, - Material.GrayBed, - Material.GreenBed, - Material.LightBlueBed, - Material.LightGrayBed, - Material.LimeBed, - Material.MagentaBed, - Material.OrangeBed, - Material.PinkBed, - Material.PurpleBed, - Material.RedBed, - Material.WhiteBed, - Material.YellowBed - }; - - bool found = false; - foreach (Material material in bedMaterialList) - { - List beds = handler.GetWorld().FindBlock(current, material, radius); - - if (beds.Count > 0) - { - found = true; - bedLocation = beds.First(); - break; - } - } - - if (!found) - return Translations.cmd_bed_bed_not_found; - - handler.Log.Info(string.Format(Translations.cmd_bed_found_a_bed_at, bedLocation.X, bedLocation.Y, bedLocation.Z)); - - if (!Movement.CheckChunkLoading(handler.GetWorld(), current, bedLocation)) - return string.Format(Translations.cmd_move_chunk_not_loaded, bedLocation.X, bedLocation.Y, bedLocation.Z); - - if (handler.MoveTo(bedLocation)) - { - Task.Factory.StartNew(() => - { - bool atTheLocation = false; - DateTime timeout = DateTime.Now.AddSeconds(60); - - while (DateTime.Now < timeout) - { - if (handler.GetCurrentLocation() == bedLocation || handler.GetCurrentLocation().Distance(bedLocation) <= 2.0) - { - atTheLocation = true; - break; - } - } - - if (!atTheLocation) - { - handler.Log.Info(string.Format(Translations.cmd_bed_failed_to_reach_in_time, bedLocation.X, bedLocation.Y, bedLocation.Z)); - return; - } - - handler.Log.Info(string.Format(Translations.cmd_bed_moving, bedLocation.X, bedLocation.Y, bedLocation.Z)); - - bool res = handler.PlaceBlock(bedLocation, Direction.Down); - - handler.Log.Info(string.Format( - Translations.cmd_bed_trying_to_use, - bedLocation.X, - bedLocation.Y, - bedLocation.Z, - res ? Translations.cmd_bed_in : Translations.cmd_bed_not_in - )); - }); - - return ""; - } - - return Translations.cmd_bed_cant_reach_safely; - } - - if (args.Length >= 3) - { - Location block = Location.Parse(handler.GetCurrentLocation(), args[1], args[2], args[3]).ToFloor(); - Location blockCenter = block.ToCenter(); - - if (!handler.GetWorld().GetBlock(block).Type.IsBed()) - return string.Format(Translations.cmd_bed_not_a_bed, blockCenter.X, blockCenter.Y, blockCenter.Z); - - bool res = handler.PlaceBlock(block, Direction.Down); - - return string.Format( - Translations.cmd_bed_trying_to_use, - blockCenter.X, - blockCenter.Y, - blockCenter.Z, - res ? Translations.cmd_bed_in : Translations.cmd_bed_not_in - ); - } + found = true; + bedLocation = beds.First(); + break; } } - return CmdUsage; + if (!found) + return r.SetAndReturn(Status.Fail, Translations.cmd_bed_bed_not_found); + + handler.Log.Info(string.Format(Translations.cmd_bed_found_a_bed_at, bedLocation.X, bedLocation.Y, bedLocation.Z)); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), current, bedLocation)) + return r.SetAndReturn(Status.FailChunkNotLoad, + string.Format(Translations.cmd_move_chunk_not_loaded, bedLocation.X, bedLocation.Y, bedLocation.Z)); + + if (handler.MoveTo(bedLocation)) + { + Task.Factory.StartNew(() => + { + bool atTheLocation = false; + DateTime timeout = DateTime.Now.AddSeconds(60); + + while (DateTime.Now < timeout) + { + if (handler.GetCurrentLocation() == bedLocation || handler.GetCurrentLocation().Distance(bedLocation) <= 2.0) + { + atTheLocation = true; + break; + } + } + + if (!atTheLocation) + { + handler.Log.Info(string.Format(Translations.cmd_bed_failed_to_reach_in_time, bedLocation.X, bedLocation.Y, bedLocation.Z)); + return; + } + + handler.Log.Info(string.Format(Translations.cmd_bed_moving, bedLocation.X, bedLocation.Y, bedLocation.Z)); + + bool res = handler.PlaceBlock(bedLocation, Direction.Down); + + handler.Log.Info(string.Format( + Translations.cmd_bed_trying_to_use, + bedLocation.X, + bedLocation.Y, + bedLocation.Z, + res ? Translations.cmd_bed_in : Translations.cmd_bed_not_in + )); + }); + + return r.SetAndReturn(Status.Done); + } + else + { + return r.SetAndReturn(Status.Fail, Translations.cmd_bed_cant_reach_safely); + } + } + + private static int DoSleepBedWithLocation(CmdResult r, McClient handler, Location block) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + block.ToAbsolute(handler.GetCurrentLocation()); + Location blockCenter = block.ToCenter(); + + if (!handler.GetWorld().GetBlock(block).Type.IsBed()) + return r.SetAndReturn(Status.Fail, + string.Format(Translations.cmd_bed_not_a_bed, blockCenter.X, blockCenter.Y, blockCenter.Z)); + + return r.SetAndReturn(Status.Done, string.Format( + Translations.cmd_bed_trying_to_use, + blockCenter.X, + blockCenter.Y, + blockCenter.Z, + handler.PlaceBlock(block, Direction.Down) ? Translations.cmd_bed_in : Translations.cmd_bed_not_in + )); } } } \ No newline at end of file diff --git a/MinecraftClient/Commands/BlockInfo.cs b/MinecraftClient/Commands/BlockInfo.cs index d2165eee..dcc82d61 100644 --- a/MinecraftClient/Commands/BlockInfo.cs +++ b/MinecraftClient/Commands/BlockInfo.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; -using System.Text; +using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -11,40 +13,60 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "blockinfo [-s]"; } } public override string CmdDesc { get { return Translations.cmd_blockinfo_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("-s") + .Executes(r => GetUsage(r.Source, "-s"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => LogBlockInfo(r.Source, handler, handler.GetCurrentLocation(), false)) + .Then(l => l.Literal("-s") + .Executes(r => LogBlockInfo(r.Source, handler, handler.GetCurrentLocation(), true))) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => LogBlockInfo(r.Source, handler, MccArguments.GetLocation(r, "Location"), false)) + .Then(l => l.Literal("-s") + .Executes(r => LogBlockInfo(r.Source, handler, MccArguments.GetLocation(r, "Location"), true)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + "-s" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int LogBlockInfo(CmdResult r, McClient handler, Location targetBlock, bool reportSurrounding) { if (!handler.GetTerrainEnabled()) - return Translations.error_terrain_not_enabled; + return r.SetAndReturn(Status.FailNeedTerrain); - string[] args = GetArgs(command); - - if (args.Length < 3) - return CmdUsage; - - bool reportSurrounding = args.Length >= 4 && args[3].Equals("-s", System.StringComparison.OrdinalIgnoreCase); - - Location current = handler.GetCurrentLocation(); - Location targetBlockLocation = Location.Parse(current, args[0], args[1], args[2]); - - Block block = handler.GetWorld().GetBlock(targetBlockLocation); + targetBlock.ToAbsolute(handler.GetCurrentLocation()); + Block block = handler.GetWorld().GetBlock(targetBlock); handler.Log.Info($"{Translations.cmd_blockinfo_BlockType}: {block.GetTypeString()}"); - if (reportSurrounding) { StringBuilder sb = new(); sb.AppendLine($"{Translations.cmd_blockinfo_BlocksAround}:"); - Block blockXPositive = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X + 1, targetBlockLocation.Y, targetBlockLocation.Z)); - Block blockXNegative = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X - 1, targetBlockLocation.Y, targetBlockLocation.Z)); - Block blockYPositive = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y + 1, targetBlockLocation.Z)); - Block blockYNegative = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y - 1, targetBlockLocation.Z)); - Block blockZPositive = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y, targetBlockLocation.Z + 1)); - Block blockZNegative = handler.GetWorld().GetBlock(new Location(targetBlockLocation.X, targetBlockLocation.Y, targetBlockLocation.Z - 1)); + Block blockXPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X + 1, targetBlock.Y, targetBlock.Z)); + Block blockXNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X - 1, targetBlock.Y, targetBlock.Z)); + Block blockYPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y + 1, targetBlock.Z)); + Block blockYNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y - 1, targetBlock.Z)); + Block blockZPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y, targetBlock.Z + 1)); + Block blockZNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y, targetBlock.Z - 1)); sb.AppendLine($"[X {Translations.cmd_blockinfo_Positive}] {Translations.cmd_blockinfo_BlockType}: {blockXPositive.GetTypeString()}"); sb.AppendLine($"[X {Translations.cmd_blockinfo_Negative}] {Translations.cmd_blockinfo_BlockType}: {blockXNegative.GetTypeString()}"); @@ -61,9 +83,7 @@ namespace MinecraftClient.Commands handler.Log.Info(sb.ToString()); } - - - return ""; + return r.SetAndReturn(Status.Done); } } } diff --git a/MinecraftClient/Commands/Bots.cs b/MinecraftClient/Commands/Bots.cs index 9d4233f6..58057552 100644 --- a/MinecraftClient/Commands/Bots.cs +++ b/MinecraftClient/Commands/Bots.cs @@ -1,7 +1,9 @@ using System; -using System.Collections.Generic; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; namespace MinecraftClient.Commands { @@ -11,69 +13,82 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bots [list|unload ]"; } } public override string CmdDesc { get { return Translations.cmd_bots_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("unload") + .Executes(r => GetUsage(r.Source, "unload"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoListBot(r.Source, handler)) + .Then(l => l.Literal("list") + .Executes(r => DoListBot(r.Source, handler))) + .Then(l => l.Literal("unload") + .Then(l => l.Argument("BotName", MccArguments.BotName()) + .Executes(r => DoUnloadBot(r.Source, handler, Arguments.GetString(r, "BotName"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); +#pragma warning disable format // @formatter:off + "list" => GetCmdDescTranslated(), + "unload" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (args.Length == 1) - { - if (args[0].Equals("list", StringComparison.OrdinalIgnoreCase)) - { - StringBuilder sb = new(); + private int DoListBot(CmdResult r, McClient handler) + { + int length = handler.GetLoadedChatBots().Count; + if (length == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_bots_noloaded); - int length = handler.GetLoadedChatBots().Count; - - if (length == 0) - return Translations.cmd_bots_noloaded; - - for (int i = 0; i < length; i++) - { - sb.Append(handler.GetLoadedChatBots()[i].GetType().Name); - - if (i != length - 1) - sb.Append(" ,"); - - } - - return Translations.cmd_bots_list + ": " + sb.ToString(); - } - - } - else if (args.Length == 2) - { - if (args[0].Equals("unload", StringComparison.OrdinalIgnoreCase)) - { - string botName = args[1].Trim(); - - if (botName.ToLower().Equals("all", StringComparison.OrdinalIgnoreCase)) - { - if (handler.GetLoadedChatBots().Count == 0) - return Translations.cmd_bots_noloaded; - - handler.UnloadAllBots(); - return Translations.cmd_bots_unloaded_all; - } - else - { - ChatBot? bot = handler.GetLoadedChatBots().Find(bot => bot.GetType().Name.ToLower() == botName.ToLower()); - - if (bot == null) - return string.Format(Translations.cmd_bots_notfound, botName); - - handler.BotUnLoad(bot); - return string.Format(Translations.cmd_bots_unloaded, botName); - } - } - } + StringBuilder sb = new(); + for (int i = 0; i < length; i++) + { + sb.Append(handler.GetLoadedChatBots()[i].GetType().Name); + if (i != length - 1) + sb.Append(" ,"); } - return GetCmdDescTranslated(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_bots_list + ": " + sb.ToString()); + } + + private int DoUnloadBot(CmdResult r, McClient handler, string botName) + { + if (botName.ToLower().Equals("all", StringComparison.OrdinalIgnoreCase)) + { + if (handler.GetLoadedChatBots().Count == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_bots_noloaded); + else + { + handler.UnloadAllBots(); + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_bots_unloaded_all); + } + } + else + { + ChatBot? bot = handler.GetLoadedChatBots().Find(bot => bot.GetType().Name.ToLower() == botName.ToLower()); + if (bot == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_bots_notfound, botName)); + else + { + handler.BotUnLoad(bot); + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_bots_unloaded, botName)); + } + } } } } diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index ae74604f..4a565de8 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -10,39 +11,41 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "changeslot <1-9>"; } } public override string CmdDesc { get { return Translations.cmd_changeSlot_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Slot", Arguments.Integer(1, 9)) + .Executes(r => DoChangeSlot(r.Source, handler, Arguments.GetInteger(r, "Slot")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoChangeSlot(CmdResult r, McClient handler, int slot) { if (!handler.GetInventoryEnabled()) - return Translations.extra_inventory_required; + return r.SetAndReturn(Status.FailNeedInventory); - if (HasArg(command)) - { - short slot; - try - { - slot = Convert.ToInt16(GetArg(command)); - } - catch (FormatException) - { - return Translations.cmd_changeSlot_nan; - } - if (slot >= 1 && slot <= 9) - { - if (handler.ChangeSlot(slot -= 1)) - { - return string.Format(Translations.cmd_changeSlot_changed, (slot += 1)); - } - else - { - return Translations.cmd_changeSlot_fail; - } - } - } - return GetCmdDescTranslated(); + if (handler.ChangeSlot((short)(slot - 1))) + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_changeSlot_changed, slot)); + else + return r.SetAndReturn(Status.Fail, Translations.cmd_changeSlot_fail); } } } diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index a84fea87..5d225db1 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -1,9 +1,10 @@ using System; -using System.Collections.Generic; -using System.Globalization; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -13,252 +14,267 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "chunk status [chunkX chunkZ|locationX locationY locationZ]"; } } public override string CmdDesc { get { return Translations.cmd_chunk_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("status") + .Executes(r => GetUsage(r.Source, "status"))) + .Then(l => l.Literal("_setloading") + .Executes(r => GetUsage(r.Source, "_setloading"))) + .Then(l => l.Literal("_setloaded") + .Executes(r => GetUsage(r.Source, "_setloaded"))) + .Then(l => l.Literal("_delete") + .Executes(r => GetUsage(r.Source, "_delete"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("status") + .Executes(r => LogChunkStatus(r.Source, handler)) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => LogChunkStatus(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => LogChunkStatus(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_setloading") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DebugSetLoading(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => DebugSetLoading(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_setloaded") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DebugSetLoaded(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => DebugSetLoaded(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_delete") + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DebugDelete(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Argument("Chunk", MccArguments.Tuple()) + .Executes(r => DebugDelete(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length > 0) +#pragma warning disable format // @formatter:off + "status" => GetCmdDescTranslated(), + "_setloading" => GetCmdDescTranslated(), + "_setloaded" => GetCmdDescTranslated(), + "_delete" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private static int LogChunkStatus(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + World world = handler.GetWorld(); + Location current = handler.GetCurrentLocation(); + if (pos.HasValue) + pos.Value.ToAbsolute(current); + + (int markChunkX, int markChunkZ) = markedChunkPos ?? + (pos.HasValue ? new(pos.Value.ChunkX, pos.Value.ChunkZ) : new(current.ChunkX, current.ChunkZ)); + + StringBuilder sb = new(); + + sb.Append(World.GetChunkLoadingStatus(handler.GetWorld())); + sb.Append('\n'); + + sb.AppendLine(string.Format(Translations.cmd_chunk_current, current, current.ChunkX, current.ChunkZ)); + if (markedChunkPos != null) + { + sb.Append(Translations.cmd_chunk_marked); + if (pos.HasValue) + sb.Append(string.Format("X:{0:0.00} Y:{1:0.00} Z:{2:0.00}, ", pos.Value.X, pos.Value.Y, pos.Value.Z)); + sb.AppendLine(string.Format(Translations.cmd_chunk_chunk_pos, markChunkX, markChunkZ)); ; + } + + int consoleHeight = Math.Max(Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25); + if (consoleHeight % 2 == 0) + --consoleHeight; + + int consoleWidth = Math.Max(Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17); + if (consoleWidth % 2 == 0) + --consoleWidth; + + int startZ = current.ChunkZ - consoleHeight, endZ = current.ChunkZ + consoleHeight; + int startX = current.ChunkX - consoleWidth, endX = current.ChunkX + consoleWidth; + + int leftMost = endX, rightMost = startX, topMost = endZ, bottomMost = startZ; + for (int z = startZ; z <= endZ; z++) + { + for (int x = startX; x <= endX; ++x) { - if (args[0] == "status") + if (world[x, z] != null) { - World world = handler.GetWorld(); - Location current = handler.GetCurrentLocation(); - - Tuple? markedChunkPos = ParseChunkPos(args); - (int markChunkX, int markChunkZ) = markedChunkPos ?? (new(current.ChunkX, current.ChunkZ)); - - StringBuilder sb = new(); - - sb.Append(World.GetChunkLoadingStatus(handler.GetWorld())); - sb.Append('\n'); - - sb.AppendLine(string.Format(Translations.cmd_chunk_current, current, current.ChunkX, current.ChunkZ)); - if (markedChunkPos != null) - { - sb.Append(Translations.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.AppendLine(string.Format(Translations.cmd_chunk_chunk_pos, markChunkX, markChunkZ));; - } - - int consoleHeight = Math.Max(Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 2, 25); - if (consoleHeight % 2 == 0) - --consoleHeight; - - int consoleWidth = Math.Max(Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2, 17); - if (consoleWidth % 2 == 0) - --consoleWidth; - - int startZ = current.ChunkZ - consoleHeight, endZ = current.ChunkZ + consoleHeight; - int startX = current.ChunkX - consoleWidth, endX = current.ChunkX + consoleWidth; - - int leftMost = endX, rightMost = startX, topMost = endZ, bottomMost = startZ; - for (int z = startZ; z <= endZ; z++) - { - for (int x = startX; x <= endX; ++x) - { - if (world[x, z] != null) - { - leftMost = Math.Min(leftMost, x); - rightMost = Math.Max(rightMost, x); - topMost = Math.Min(topMost, z); - bottomMost = Math.Max(bottomMost, z); - } - } - } - - // Include the player's location - topMost = Math.Min(topMost, current.ChunkZ); - bottomMost = Math.Max(bottomMost, current.ChunkZ); - leftMost = Math.Min(leftMost, current.ChunkX); - rightMost = Math.Max(rightMost, current.ChunkX); - - // Empty one row and one column each - --leftMost; ++rightMost; --topMost; ++bottomMost; - - // Resize according to limitations - if ((bottomMost - topMost + 1) > consoleHeight) - { - int delta = (bottomMost - topMost + 1) - consoleHeight; - if (bottomMost - (delta + 1) / 2 < current.ChunkZ + 1) - { - int bottomReduce = bottomMost - (current.ChunkZ + 1); - bottomMost -= bottomReduce; - topMost += delta - bottomReduce; - } - else if (topMost + delta / 2 > current.ChunkZ - 1) - { - int topAdd = topMost - (current.ChunkZ - 1); - topMost += topAdd; - bottomMost -= delta - topAdd; - } - else - { - topMost += delta / 2; - bottomMost -= (delta + 1) / 2; - } - } - if ((rightMost - leftMost + 1) > consoleWidth) - { - int delta = (rightMost - leftMost + 1) - consoleWidth; - if (rightMost - (delta + 1) / 2 < current.ChunkX + 1) - { - int rightReduce = rightMost - (current.ChunkX + 1); - rightMost -= rightReduce; - leftMost += delta - rightReduce; - } - else if (leftMost + delta / 2 > current.ChunkX - 1) - { - int leftAdd = leftMost - (current.ChunkX - 1); - leftMost += leftAdd; - rightMost -= delta - leftAdd; - } - else - { - leftMost += delta / 2; - rightMost -= (delta + 1) / 2; - } - } - - // Try to include the marker chunk - if (markedChunkPos != null && - (((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) || - ((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth))) - sb.AppendLine(Translations.cmd_chunk_outside); - else - { - topMost = Math.Min(topMost, markChunkZ); - bottomMost = Math.Max(bottomMost, markChunkZ); - leftMost = Math.Min(leftMost, markChunkX); - rightMost = Math.Max(rightMost, markChunkX); - } - - - // \ud83d\udd33: 🔳, \ud83d\udfe8: 🟨, \ud83d\udfe9: 🟩, \u25A1: □, \u25A3: ▣, \u25A0: ■ - string[] chunkStatusStr = Settings.Config.Main.Advanced.EnableEmoji ? - new string[] { "\ud83d\udd33", "\ud83d\udfe8", "\ud83d\udfe9" } : new string[] { "\u25A1", "\u25A3", "\u25A0" }; - - // Output - for (int z = topMost; z <= bottomMost; ++z) - { - for (int x = leftMost; x <= rightMost; ++x) - { - if (z == current.ChunkZ && x == current.ChunkX) - sb.Append("§z"); // Player Location: background gray - else if (z == markChunkZ && x == markChunkX) - sb.Append("§w"); // Marked chunk: background red - - ChunkColumn? chunkColumn = world[x, z]; - if (chunkColumn == null) - sb.Append(chunkStatusStr[0]); - else if (chunkColumn.FullyLoaded) - sb.Append(chunkStatusStr[2]); - else - sb.Append(chunkStatusStr[1]); - - if ((z == current.ChunkZ && x == current.ChunkX) || (z == markChunkZ && x == markChunkX)) - sb.Append("§r"); // Reset background color - } - sb.Append('\n'); - } - - sb.AppendLine(string.Format(Translations.cmd_chunk_icon, "§z §r", "§w §r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); - return sb.ToString(); + leftMost = Math.Min(leftMost, x); + rightMost = Math.Max(rightMost, x); + topMost = Math.Min(topMost, z); + bottomMost = Math.Max(bottomMost, z); } - else if (args[0] == "setloading") // For debugging - { - Tuple? chunkPos = ParseChunkPos(args); - if (chunkPos != null) - { - handler.Log.Info(Translations.cmd_chunk_for_debug); - World world = handler.GetWorld(); - (int chunkX, int chunkZ) = chunkPos; - ChunkColumn? chunkColumn = world[chunkX, chunkZ]; - if (chunkColumn != null) - chunkColumn.FullyLoaded = false; - return (chunkColumn == null) ? "Fail: chunk dosen't exist!" : - String.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ); - } - else - return GetCmdDescTranslated(); - } - else if (args[0] == "setloaded") // For debugging - { - Tuple? chunkPos = ParseChunkPos(args); - if (chunkPos != null) - { - handler.Log.Info(Translations.cmd_chunk_for_debug); - World world = handler.GetWorld(); - (int chunkX, int chunkZ) = chunkPos; - ChunkColumn? chunkColumn = world[chunkX, chunkZ]; - if (chunkColumn != null) - chunkColumn.FullyLoaded = true; - return (chunkColumn == null) ? "Fail: chunk dosen't exist!" : - String.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ); - } - else - return GetCmdDescTranslated(); - } - else if (args[0] == "delete") // For debugging - { - Tuple? chunkPos = ParseChunkPos(args); - if (chunkPos != null) - { - handler.Log.Info(Translations.cmd_chunk_for_debug); - World world = handler.GetWorld(); - (int chunkX, int chunkZ) = chunkPos; - world[chunkX, chunkZ] = null; - return String.Format("Successfully deleted chunk ({0}, {1}).", chunkX, chunkZ); - } - else - return GetCmdDescTranslated(); - } - else - return GetCmdDescTranslated(); + } + } + + // Include the player's location + topMost = Math.Min(topMost, current.ChunkZ); + bottomMost = Math.Max(bottomMost, current.ChunkZ); + leftMost = Math.Min(leftMost, current.ChunkX); + rightMost = Math.Max(rightMost, current.ChunkX); + + // Empty one row and one column each + --leftMost; ++rightMost; --topMost; ++bottomMost; + + // Resize according to limitations + if ((bottomMost - topMost + 1) > consoleHeight) + { + int delta = (bottomMost - topMost + 1) - consoleHeight; + if (bottomMost - (delta + 1) / 2 < current.ChunkZ + 1) + { + int bottomReduce = bottomMost - (current.ChunkZ + 1); + bottomMost -= bottomReduce; + topMost += delta - bottomReduce; + } + else if (topMost + delta / 2 > current.ChunkZ - 1) + { + int topAdd = topMost - (current.ChunkZ - 1); + topMost += topAdd; + bottomMost -= delta - topAdd; } else - return GetCmdDescTranslated(); + { + topMost += delta / 2; + bottomMost -= (delta + 1) / 2; + } } + if ((rightMost - leftMost + 1) > consoleWidth) + { + int delta = (rightMost - leftMost + 1) - consoleWidth; + if (rightMost - (delta + 1) / 2 < current.ChunkX + 1) + { + int rightReduce = rightMost - (current.ChunkX + 1); + rightMost -= rightReduce; + leftMost += delta - rightReduce; + } + else if (leftMost + delta / 2 > current.ChunkX - 1) + { + int leftAdd = leftMost - (current.ChunkX - 1); + leftMost += leftAdd; + rightMost -= delta - leftAdd; + } + else + { + leftMost += delta / 2; + rightMost -= (delta + 1) / 2; + } + } + + // Try to include the marker chunk + if (markedChunkPos != null && + (((Math.Max(bottomMost, markChunkZ) - Math.Min(topMost, markChunkZ) + 1) > consoleHeight) || + ((Math.Max(rightMost, markChunkX) - Math.Min(leftMost, markChunkX) + 1) > consoleWidth))) + sb.AppendLine(Translations.cmd_chunk_outside); else - return GetCmdDescTranslated(); + { + topMost = Math.Min(topMost, markChunkZ); + bottomMost = Math.Max(bottomMost, markChunkZ); + leftMost = Math.Min(leftMost, markChunkX); + rightMost = Math.Max(rightMost, markChunkX); + } + + + // \ud83d\udd33: 🔳, \ud83d\udfe8: 🟨, \ud83d\udfe9: 🟩, \u25A1: □, \u25A3: ▣, \u25A0: ■ + string[] chunkStatusStr = Settings.Config.Main.Advanced.EnableEmoji ? + new string[] { "\ud83d\udd33", "\ud83d\udfe8", "\ud83d\udfe9" } : new string[] { "\u25A1", "\u25A3", "\u25A0" }; + + // Output + for (int z = topMost; z <= bottomMost; ++z) + { + for (int x = leftMost; x <= rightMost; ++x) + { + if (z == current.ChunkZ && x == current.ChunkX) + sb.Append("§z"); // Player Location: background gray + else if (z == markChunkZ && x == markChunkX) + sb.Append("§w"); // Marked chunk: background red + + ChunkColumn? chunkColumn = world[x, z]; + if (chunkColumn == null) + sb.Append(chunkStatusStr[0]); + else if (chunkColumn.FullyLoaded) + sb.Append(chunkStatusStr[2]); + else + sb.Append(chunkStatusStr[1]); + + if ((z == current.ChunkZ && x == current.ChunkX) || (z == markChunkZ && x == markChunkX)) + sb.Append("§r"); // Reset background color + } + sb.Append('\n'); + } + + sb.Append(string.Format(Translations.cmd_chunk_icon, "§z §r", "§w §r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); + handler.Log.Info(sb.ToString()); + + return r.SetAndReturn(Status.Done); } - private static Tuple? ParseChunkPos(string[] args) + private static int DebugSetLoading(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) { - try - { - int chunkX, chunkZ; - if (args.Length == 1 + 3) - { - Location pos = new( - double.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture), - double.Parse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture), - double.Parse(args[3], NumberStyles.Any, CultureInfo.CurrentCulture) - ); - chunkX = pos.ChunkX; - chunkZ = pos.ChunkZ; - } - else if (args.Length == 1 + 2) - { - chunkX = int.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture); - chunkZ = int.Parse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture); - } - else - return null; - return new(chunkX, chunkZ); - } - catch (FormatException) - { - return null; - } + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + if (pos.HasValue) + pos.Value.ToAbsolute(handler.GetCurrentLocation()); + handler.Log.Info(Translations.cmd_chunk_for_debug); + (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); + ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ]; + if (chunkColumn != null) + chunkColumn.FullyLoaded = false; + + if (chunkColumn == null) + return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!"); + else + return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ)); + } + + private static int DebugSetLoaded(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + if (pos.HasValue) + pos.Value.ToAbsolute(handler.GetCurrentLocation()); + handler.Log.Info(Translations.cmd_chunk_for_debug); + (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); + ChunkColumn? chunkColumn = handler.GetWorld()[chunkX, chunkZ]; + if (chunkColumn != null) + chunkColumn.FullyLoaded = false; + + if (chunkColumn == null) + return r.SetAndReturn(Status.Fail, "Fail: chunk dosen't exist!"); + else + return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ)); + } + + private static int DebugDelete(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + if (pos.HasValue) + pos.Value.ToAbsolute(handler.GetCurrentLocation()); + handler.Log.Info(Translations.cmd_chunk_for_debug); + (int chunkX, int chunkZ) = markedChunkPos ?? new(pos!.Value.ChunkX, pos!.Value.ChunkZ); + handler.GetWorld()[chunkX, chunkZ] = null; + + return r.SetAndReturn(Status.Done, string.Format("Successfully deleted chunk ({0}, {1}).", chunkX, chunkZ)); } } } diff --git a/MinecraftClient/Commands/CommandSource.cs b/MinecraftClient/Commands/CommandSource.cs deleted file mode 100644 index 89f1f1ff..00000000 --- a/MinecraftClient/Commands/CommandSource.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MinecraftClient.Commands -{ - public class CommandSource - { - public string UserName { get; set; } = string.Empty; - } -} diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs index 9640eb9d..79892fc4 100644 --- a/MinecraftClient/Commands/Connect.cs +++ b/MinecraftClient/Commands/Connect.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -9,31 +11,65 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "connect [account]"; } } public override string CmdDesc { get { return Translations.cmd_connect_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("ServerNick", MccArguments.ServerNick()) + .Executes(r => DoConnect(r.Source, handler, Arguments.GetString(r, "ServerNick"), string.Empty)) + .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) + .Executes(r => DoConnect(r.Source, handler, Arguments.GetString(r, "ServerNick"), Arguments.GetString(r, "AccountNick"))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient? handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length > 1) - { - if (!Settings.Config.Main.Advanced.SetAccount(args[1])) - { - return string.Format(Translations.cmd_connect_unknown, args[1]); - } - } +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(args[0]), true)) - { - Program.Restart(keepAccountAndServerSettings: true); - return ""; - } - else return string.Format(Translations.cmd_connect_invalid_ip, args[0]); + private int DoConnect(CmdResult r, McClient handler, string server, string account) + { + if (!string.IsNullOrWhiteSpace(account) && !Settings.Config.Main.Advanced.SetAccount(account)) + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_connect_unknown, account)); + + if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(server), true)) + { + Program.Restart(keepAccountAndServerSettings: true); + return r.SetAndReturn(Status.Done); + } + else + { + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_connect_invalid_ip, server)); + } + } + + internal static string DoConnect(string command) + { + string[] args = GetArgs(command); + if (args.Length > 1 && !Settings.Config.Main.Advanced.SetAccount(args[1])) + return string.Format(Translations.cmd_connect_unknown, args[1]); + + if (Settings.Config.Main.SetServerIP(new Settings.MainConfigHealper.MainConfig.ServerInfoConfig(args[0]), true)) + { + Program.Restart(keepAccountAndServerSettings: true); + return string.Empty; + } + else + { + return string.Format(Translations.cmd_connect_invalid_ip, args[0]); } - else return GetCmdDescTranslated(); } } } diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index 18122daa..acb6f7e9 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,20 +10,46 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "debug [on|off]"; } } public override string CmdDesc { get { return Translations.cmd_debug_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => SetDebugMode(r.Source, true)) + .Then(l => l.Literal("on") + .Executes(r => SetDebugMode(r.Source, false, true))) + .Then(l => l.Literal("off") + .Executes(r => SetDebugMode(r.Source, false, false))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) - Settings.Config.Logging.DebugMessages = (GetArg(command).ToLower() == "on"); - else + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int SetDebugMode(CmdResult r, bool flip, bool mode = false) + { + if (flip) Settings.Config.Logging.DebugMessages = !Settings.Config.Logging.DebugMessages; - if (Settings.Config.Logging.DebugMessages) - return Translations.cmd_debug_state_on; else - return Translations.cmd_debug_state_off; + Settings.Config.Logging.DebugMessages = mode; + + if (Settings.Config.Logging.DebugMessages) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_on); + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_debug_state_off); } } } diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index bf68743c..2903a817 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -1,7 +1,9 @@ using System; -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -11,53 +13,68 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "dig "; } } public override string CmdDesc { get { return Translations.cmd_dig_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DigLookAt(r.Source, handler)) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => DigAt(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DigAt(CmdResult r, McClient handler, Location blockToBreak) { if (!handler.GetTerrainEnabled()) - return Translations.extra_terrainandmovement_required; + return r.SetAndReturn(Status.FailNeedTerrain); - string[] args = GetArgs(command); - if (args.Length == 0) + Location current = handler.GetCurrentLocation(); + blockToBreak = blockToBreak.ToAbsolute(current); + if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far); + Block block = handler.GetWorld().GetBlock(blockToBreak); + if (block.Type == Material.Air) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); + else if (handler.DigBlock(blockToBreak)) { - (bool hasBlock, Location blockLoc, Block block) = RaycastHelper.RaycastBlock(handler, 4.5, false); - if (!hasBlock) - return Translations.cmd_dig_too_far; - else if (block.Type == Material.Air) - return Translations.cmd_dig_no_block; - else if (handler.DigBlock(blockLoc, lookAtBlock: false)) - return string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString()); - else - return Translations.cmd_dig_fail; - } - else if (args.Length == 3) - { - try - { - Location current = handler.GetCurrentLocation(); - Location blockToBreak = Location.Parse(current.ToFloor(), args[0], args[1], args[2]); - if (blockToBreak.DistanceSquared(current.EyesLocation()) > 25) - return Translations.cmd_dig_too_far; - Block block = handler.GetWorld().GetBlock(blockToBreak); - if (block.Type == Material.Air) - return Translations.cmd_dig_no_block; - else if (handler.DigBlock(blockToBreak)) - { - blockToBreak = blockToBreak.ToCenter(); - return string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString()); - } - else - return Translations.cmd_dig_fail; - } - catch (FormatException) { return GetCmdDescTranslated(); } + blockToBreak = blockToBreak.ToCenter(); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString())); } else - { - return GetCmdDescTranslated(); - } + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); + } + + private int DigLookAt(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + (bool hasBlock, Location blockLoc, Block block) = RaycastHelper.RaycastBlock(handler, 4.5, false); + if (!hasBlock) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far); + else if (block.Type == Material.Air) + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); + else if (handler.DigBlock(blockLoc, lookAtBlock: false)) + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString())); + else + return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); } } } diff --git a/MinecraftClient/Commands/DropItem.cs b/MinecraftClient/Commands/DropItem.cs index fa085d47..01e9ecb1 100644 --- a/MinecraftClient/Commands/DropItem.cs +++ b/MinecraftClient/Commands/DropItem.cs @@ -1,57 +1,64 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { class DropItem : Command { public override string CmdName { get { return "dropitem"; } } - public override string CmdUsage { get { return "/dropitem "; } } + public override string CmdUsage { get { return "dropitem "; } } public override string CmdDesc { get { return Translations.cmd_dropItem_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => DoDropItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoDropItem(CmdResult r, McClient handler, ItemType itemType) { if (!handler.GetInventoryEnabled()) - { - return Translations.extra_inventory_required; - } - if (HasArg(command)) - { - string arg = GetArg(command); - if (Enum.TryParse(arg, true, out ItemType itemType)) - { - int inventoryId; - var inventories = handler.GetInventories(); - List availableIds = inventories.Keys.ToList(); - availableIds.Remove(0); // remove player inventory ID from list - if (availableIds.Count == 1) - inventoryId = availableIds[0]; // one container, use it - else - inventoryId = 0; - var p = inventories[inventoryId]; - int[] targetItems = p.SearchItem(itemType); - foreach (int slot in targetItems) - { - handler.DoWindowAction(inventoryId, slot, WindowActionType.DropItemStack); - } - return string.Format(Translations.cmd_dropItem_dropped, itemType.ToString(), inventoryId); - } - else - { - return string.Format(Translations.cmd_dropItem_unknown_item, arg); - } - } + return r.SetAndReturn(Status.FailNeedTerrain); + + int inventoryId; + var inventories = handler.GetInventories(); + List availableIds = inventories.Keys.ToList(); + availableIds.Remove(0); // remove player inventory ID from list + if (availableIds.Count == 1) + inventoryId = availableIds[0]; // one container, use it else - { - return CmdUsage; - } + inventoryId = 0; + var p = inventories[inventoryId]; + int[] targetItems = p.SearchItem(itemType); + foreach (int slot in targetItems) + handler.DoWindowAction(inventoryId, slot, WindowActionType.DropItemStack); + + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dropItem_dropped, Item.GetTypeString(itemType), inventoryId)); } } } diff --git a/MinecraftClient/Commands/Enchant.cs b/MinecraftClient/Commands/Enchant.cs index 108a8b95..bc7c7e10 100644 --- a/MinecraftClient/Commands/Enchant.cs +++ b/MinecraftClient/Commands/Enchant.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -11,80 +12,96 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "enchant "; } } public override string CmdDesc { get { return Translations.cmd_enchant_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("top") + .Executes(r => GetUsage(r.Source, "top"))) + .Then(l => l.Literal("middle") + .Executes(r => GetUsage(r.Source, "middle"))) + .Then(l => l.Literal("bottom") + .Executes(r => GetUsage(r.Source, "bottom"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("top") + .Executes(r => DoEnchant(r.Source, handler, slotId: 0))) + .Then(l => l.Literal("middle") + .Executes(r => DoEnchant(r.Source, handler, slotId: 1))) + .Then(l => l.Literal("bottom") + .Executes(r => DoEnchant(r.Source, handler, slotId: 2))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (!handler.GetInventoryEnabled()) - return Translations.error_inventoryhandling_not_enabled; - - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string slot = GetArg(command).ToLower().Trim(); +#pragma warning disable format // @formatter:off + "top" => GetCmdDescTranslated(), + "middle" => GetCmdDescTranslated(), + "bottom" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - int slotId = slot switch + private int DoEnchant(CmdResult r, McClient handler, int slotId) + { + Container? enchantingTable = null; + + foreach (var (id, container) in handler.GetInventories()) + { + if (container.Type == ContainerType.Enchantment) { - "top" => 0, - "middle" => 1, - "bottom" => 2, - _ => -1 - }; - - if (slotId == -1) - return Translations.cmd_enchant_invalid_slot; - - Container? enchantingTable = null; - - foreach (var (id, container) in handler.GetInventories()) - { - if (container.Type == ContainerType.Enchantment) - { - enchantingTable = container; - break; - } + enchantingTable = container; + break; } - - if (enchantingTable == null) - return Translations.cmd_enchant_enchanting_table_not_opened; - - int[] emptySlots = enchantingTable.GetEmpytSlots(); - - if (emptySlots.Contains(0)) - return Translations.cmd_enchant_enchanting_no_item; - - if (emptySlots.Contains(1)) - return Translations.cmd_enchant_enchanting_no_lapis; - - Item lapisSlot = enchantingTable.Items[1]; - - if (lapisSlot.Type != ItemType.LapisLazuli) - return Translations.cmd_enchant_enchanting_no_lapis; - - if (lapisSlot.Count < 3) - return Translations.cmd_enchant_enchanting_no_lapis; - - EnchantmentData? enchantment = handler.GetLastEnchantments(); - - if (enchantment == null) - return Translations.cmd_enchant_no_enchantments; - - short requiredLevel = slotId switch - { - 0 => enchantment.TopEnchantmentLevelRequirement, - 1 => enchantment.MiddleEnchantmentLevelRequirement, - 2 => enchantment.BottomEnchantmentLevelRequirement, - _ => 9999 - }; - - if (handler.GetLevel() < requiredLevel) - return string.Format(Translations.cmd_enchant_no_levels, handler.GetLevel(), requiredLevel); - - return handler.ClickContainerButton(enchantingTable.ID, slotId) ? Translations.cmd_enchant_clicked : Translations.cmd_enchant_not_clicked; } - return GetCmdDescTranslated(); + if (enchantingTable == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_table_not_opened); + + int[] emptySlots = enchantingTable.GetEmpytSlots(); + + if (emptySlots.Contains(0)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_no_item); + + if (emptySlots.Contains(1)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_no_lapis); + + Item lapisSlot = enchantingTable.Items[1]; + + if (lapisSlot.Type != ItemType.LapisLazuli || lapisSlot.Count < 3) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_enchanting_no_lapis); + + EnchantmentData? enchantment = handler.GetLastEnchantments(); + + if (enchantment == null) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_enchant_no_enchantments); + + short requiredLevel = slotId switch + { + 0 => enchantment.TopEnchantmentLevelRequirement, + 1 => enchantment.MiddleEnchantmentLevelRequirement, + 2 => enchantment.BottomEnchantmentLevelRequirement, + _ => 9999 + }; + + if (handler.GetLevel() < requiredLevel) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_enchant_no_levels, handler.GetLevel(), requiredLevel)); + else + { + if (handler.ClickContainerButton(enchantingTable.ID, slotId)) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_enchant_clicked); + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_enchant_not_clicked); + } } } } diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 6fc21bb4..ad9b8c7e 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -1,172 +1,321 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics.CodeAnalysis; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { class Entitycmd : Command { public override string CmdName { get { return "entity"; } } - public override string CmdUsage { get { return "entity "; } } + public override string CmdUsage { get { return "entity [near] "; } } public override string CmdDesc { get { return string.Empty; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + private enum ActionType { Attack, Use, List }; + + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("near") + .Executes(r => GetUsage(r.Source, "near"))) + .Then(l => l.Literal("attack") + .Executes(r => GetUsage(r.Source, "attack"))) + .Then(l => l.Literal("use") + .Executes(r => GetUsage(r.Source, "use"))) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => GetFullEntityList(r.Source, handler)) + .Then(l => l.Literal("near") + .Executes(r => GetClosetEntityList(r.Source, handler)) + .Then(l => l.Argument("EntityID", Arguments.Integer()) + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) + .Then(l => l.Argument("EntityType", MccArguments.EntityType()) + .Executes(r => OperateWithType(r.Source, handler, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List))))) + .Then(l => l.Argument("EntityID", Arguments.Integer()) + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) + .Then(l => l.Argument("EntityType", MccArguments.EntityType()) + .Executes(r => OperateWithType(r.Source, handler, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) + .Then(l => l.Literal("attack") + .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) + .Then(l => l.Literal("use") + .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) + .Then(l => l.Literal("list") + .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length >= 1) +#pragma warning disable format // @formatter:off + "near" => GetCmdDescTranslated(), + "attack" => GetCmdDescTranslated(), + "use" => GetCmdDescTranslated(), + "list" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int GetFullEntityList(CmdResult r, McClient handler) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + Dictionary entities = handler.GetEntities(); + StringBuilder response = new(); + response.AppendLine(Translations.cmd_entityCmd_entities); + foreach (var entity2 in entities) + response.AppendLine(GetEntityInfoShort(entity2.Value)); + response.Append(GetCmdDescTranslated()); + handler.Log.Info(response.ToString()); + + return r.SetAndReturn(Status.Done); + } + + private int GetClosetEntityList(CmdResult r, McClient handler) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + if (TryGetClosetEntity(handler.GetEntities(), handler.GetCurrentLocation(), null, out Entity? closest)) + { + handler.Log.Info(GetEntityInfoDetailed(handler, closest)); + return r.SetAndReturn(Status.Done); + } + else + return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); + } + + private int OperateWithId(CmdResult r, McClient handler, int entityID, ActionType action) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + if (handler.GetEntities().TryGetValue(entityID, out Entity? entity)) + { + handler.Log.Info(InteractionWithEntity(handler, entity, action)); + return r.SetAndReturn(Status.Done); + } + else + return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); + } + + private int OperateWithType(CmdResult r, McClient handler, bool near, EntityType entityType, ActionType action) + { + if (!handler.GetEntityHandlingEnabled()) + return r.SetAndReturn(Status.FailNeedEntity); + + if (near) + { + if (TryGetClosetEntity(handler.GetEntities(), handler.GetCurrentLocation(), entityType, out Entity? closest)) { - try + handler.Log.Info(InteractionWithEntity(handler, closest, action)); + return r.SetAndReturn(Status.Done); + } + else + return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); + } + else + { + if (action == ActionType.Attack || action == ActionType.Use) + { + string actionst = Translations.cmd_entityCmd_attacked; + int actioncount = 0; + foreach (var entity2 in handler.GetEntities()) { - int entityID = int.Parse(args[0], NumberStyles.Any, CultureInfo.CurrentCulture); - if (entityID != 0) + if (entity2.Value.Type == entityType) { - if (handler.GetEntities().ContainsKey(entityID)) + if (action == ActionType.Attack) { - string action = args.Length > 1 - ? args[1].ToLower() - : "list"; - switch (action) - { - case "attack": - handler.InteractEntity(entityID, InteractType.Attack); - return Translations.cmd_entityCmd_attacked; - case "use": - handler.InteractEntity(entityID, InteractType.Interact); - return Translations.cmd_entityCmd_used; - default: - Entity entity = handler.GetEntities()[entityID]; - int id = entity.ID; - float health = entity.Health; - int latency = entity.Latency; - Item item = entity.Item; - string? nickname = entity.Name; - string? customname = entity.CustomName; - EntityPose pose = entity.Pose; - EntityType type = entity.Type; - double distance = Math.Round(entity.Location.Distance(handler.GetCurrentLocation()), 2); - - string color = "§a"; // Green - if (health < 10) - color = "§c"; // Red - else if (health < 15) - color = "§e"; // Yellow - - string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; - StringBuilder done = new(); - done.Append($"{Translations.cmd_entityCmd_entity}: {id}\n [MCC] Type: {entity.GetTypeString()}"); - if (!string.IsNullOrEmpty(nickname)) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_nickname}: {nickname}"); - else if (!string.IsNullOrEmpty(customname)) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_customname}: {customname.Replace("&", "§")}§8"); - if (type == EntityType.Player) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_latency}: {latency}"); - else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket) - { - string? displayName = item.DisplayName; - if (string.IsNullOrEmpty(displayName)) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count}"); - else - done.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count} - {displayName}§8"); - } - - if (entity.Equipment.Count >= 1 && entity.Equipment != null) - { - done.Append($"\n [MCC] {Translations.cmd_entityCmd_equipment}:"); - if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_mainhand}: {entity.Equipment[0].GetTypeString()} x{entity.Equipment[0].Count}"); - if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_offhand}: {entity.Equipment[1].GetTypeString()} x{entity.Equipment[1].Count}"); - if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_helmet}: {entity.Equipment[5].GetTypeString()} x{entity.Equipment[5].Count}"); - if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_chestplate}: {entity.Equipment[4].GetTypeString()} x{entity.Equipment[4].Count}"); - if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_leggings}: {entity.Equipment[3].GetTypeString()} x{entity.Equipment[3].Count}"); - if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] != null) - done.Append($"\n [MCC] {Translations.cmd_entityCmd_boots}: {entity.Equipment[2].GetTypeString()} x{entity.Equipment[2].Count}"); - } - done.Append($"\n [MCC] {Translations.cmd_entityCmd_pose}: {pose}"); - done.Append($"\n [MCC] {Translations.cmd_entityCmd_health}: {color}{health}§8"); - done.Append($"\n [MCC] {Translations.cmd_entityCmd_distance}: {distance}"); - done.Append($"\n [MCC] {Translations.cmd_entityCmd_location}: {location}"); - return done.ToString(); - } + handler.InteractEntity(entity2.Key, InteractType.Attack); + actionst = Translations.cmd_entityCmd_attacked; } - else return Translations.cmd_entityCmd_not_found; - } - else - { - EntityType interacttype = Enum.Parse(args[0]); - string actionst = Translations.cmd_entityCmd_attacked; - int actioncount = 0; - foreach (var entity2 in handler.GetEntities()) + else if (action == ActionType.Use) { - if (entity2.Value.Type == interacttype) - { - string action = args.Length > 1 - ? args[1].ToLower() - : "list"; - if (action == "attack") - { - handler.InteractEntity(entity2.Key, InteractType.Attack); - actionst = Translations.cmd_entityCmd_attacked; - actioncount++; - } - else if (action == "use") - { - handler.InteractEntity(entity2.Key, InteractType.Interact); - actionst = Translations.cmd_entityCmd_used; - actioncount++; - } - else return GetCmdDescTranslated(); - } + handler.InteractEntity(entity2.Key, InteractType.Interact); + actionst = Translations.cmd_entityCmd_used; } - return actioncount + " " + actionst; + actioncount++; } } - catch (FormatException) { return GetCmdDescTranslated(); } + handler.Log.Info(actioncount + " " + actionst); + return r.SetAndReturn(Status.Done); } else { - Dictionary entities = handler.GetEntities(); StringBuilder response = new(); response.AppendLine(Translations.cmd_entityCmd_entities); - foreach (var entity2 in entities) + foreach (var entity2 in handler.GetEntities()) { - int id = entity2.Key; - float health = entity2.Value.Health; - int latency = entity2.Value.Latency; - string? nickname = entity2.Value.Name; - string? customname = entity2.Value.CustomName; - EntityPose pose = entity2.Value.Pose; - EntityType type = entity2.Value.Type; - Item item = entity2.Value.Item; - string location = $"X:{Math.Round(entity2.Value.Location.X, 2)}, Y:{Math.Round(entity2.Value.Location.Y, 2)}, Z:{Math.Round(entity2.Value.Location.Z, 2)}"; - - if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.Fireball || type == EntityType.FireworkRocket) - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_item}: {item.GetTypeString()}, {Translations.cmd_entityCmd_location}: {location}"); - else if (type == EntityType.Player && !string.IsNullOrEmpty(nickname)) - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_nickname}: §8{nickname}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"); - else if (type == EntityType.Player && !string.IsNullOrEmpty(customname)) - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_customname}: §8{customname.Replace("&", "§")}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"); - else - response.AppendLine($" #{id}: {Translations.cmd_entityCmd_type}: {entity2.Value.GetTypeString()}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_location}: {location}"); + if (entity2.Value.Type == entityType) + { + response.AppendLine(GetEntityInfoShort(entity2.Value)); + } } response.Append(GetCmdDescTranslated()); - return response.ToString(); + handler.Log.Info(response.ToString()); + return r.SetAndReturn(Status.Done); } } - else return Translations.extra_entity_required; + } + + private static string GetEntityInfoShort(Entity entity) + { + int id = entity.ID; + float health = entity.Health; + int latency = entity.Latency; + string? nickname = entity.Name; + string? customname = entity.CustomName; + EntityPose pose = entity.Pose; + EntityType type = entity.Type; + Item item = entity.Item; + string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; + + if (type == EntityType.Item || type == EntityType.ItemFrame || type == EntityType.EyeOfEnder || type == EntityType.Egg || type == EntityType.EnderPearl || type == EntityType.Potion || type == EntityType.Fireball || type == EntityType.FireworkRocket) + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_item}: {item.GetTypeString()}, {Translations.cmd_entityCmd_location}: {location}"; + else if (type == EntityType.Player && !string.IsNullOrEmpty(nickname)) + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_nickname}: §8{nickname}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"; + else if (type == EntityType.Player && !string.IsNullOrEmpty(customname)) + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_customname}: §8{customname.Replace("&", "§")}§8, {Translations.cmd_entityCmd_latency}: {latency}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_pose}: {pose}, {Translations.cmd_entityCmd_location}: {location}"; + else + return $" #{id}: {Translations.cmd_entityCmd_type}: {entity.GetTypeString()}, {Translations.cmd_entityCmd_health}: {health}, {Translations.cmd_entityCmd_location}: {location}"; + } + + private static string GetEntityInfoDetailed(McClient handler, Entity entity) + { + StringBuilder sb = new(); + int id = entity.ID; + float health = entity.Health; + int latency = entity.Latency; + Item item = entity.Item; + string? nickname = entity.Name; + string? customname = entity.CustomName; + EntityPose pose = entity.Pose; + EntityType type = entity.Type; + double distance = Math.Round(entity.Location.Distance(handler.GetCurrentLocation()), 2); + string location = $"X:{Math.Round(entity.Location.X, 2)}, Y:{Math.Round(entity.Location.Y, 2)}, Z:{Math.Round(entity.Location.Z, 2)}"; + + string color = "§a"; // Green + if (health < 10) + color = "§c"; // Red + else if (health < 15) + color = "§e"; // Yellow + + sb.Append($"{Translations.cmd_entityCmd_entity}: {id}\n [MCC] Type: {entity.GetTypeString()}"); + + if (!string.IsNullOrEmpty(nickname)) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_nickname}: {nickname}"); + else if (!string.IsNullOrEmpty(customname)) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_customname}: {customname.Replace("&", "§")}§8"); + + if (type == EntityType.Player) + { + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_latency}: {latency}"); + } + else if (type == EntityType.Item || type == EntityType.ItemFrame || type == Mapping.EntityType.EyeOfEnder || type == Mapping.EntityType.Egg || type == Mapping.EntityType.EnderPearl || type == Mapping.EntityType.Potion || type == Mapping.EntityType.Fireball || type == Mapping.EntityType.FireworkRocket) + { + string? displayName = item.DisplayName; + if (string.IsNullOrEmpty(displayName)) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count}"); + else + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_item}: {item.GetTypeString()} x{item.Count} - {displayName}§8"); + } + + if (entity.Equipment.Count >= 1 && entity.Equipment != null) + { + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_equipment}:"); + if (entity.Equipment.ContainsKey(0) && entity.Equipment[0] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_mainhand}: {entity.Equipment[0].GetTypeString()} x{entity.Equipment[0].Count}"); + if (entity.Equipment.ContainsKey(1) && entity.Equipment[1] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_offhand}: {entity.Equipment[1].GetTypeString()} x{entity.Equipment[1].Count}"); + if (entity.Equipment.ContainsKey(5) && entity.Equipment[5] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_helmet}: {entity.Equipment[5].GetTypeString()} x{entity.Equipment[5].Count}"); + if (entity.Equipment.ContainsKey(4) && entity.Equipment[4] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_chestplate}: {entity.Equipment[4].GetTypeString()} x{entity.Equipment[4].Count}"); + if (entity.Equipment.ContainsKey(3) && entity.Equipment[3] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_leggings}: {entity.Equipment[3].GetTypeString()} x{entity.Equipment[3].Count}"); + if (entity.Equipment.ContainsKey(2) && entity.Equipment[2] != null) + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_boots}: {entity.Equipment[2].GetTypeString()} x{entity.Equipment[2].Count}"); + } + + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_pose}: {pose}"); + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_health}: {color}{health}§8"); + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_distance}: {distance}"); + sb.Append($"\n [MCC] {Translations.cmd_entityCmd_location}: {location}"); + + return sb.ToString(); + } + + private static bool TryGetClosetEntity(Dictionary entities, Location location, EntityType? entityType, [NotNullWhen(true)] out Entity? closest) + { + closest = null; + bool find = false; + double closest_distance = double.PositiveInfinity; + foreach (var entity in entities) + { + if (entityType.HasValue && entity.Value.Type != entityType) + continue; + + double distance = Math.Round(entity.Value.Location.Distance(location), 2); + if (distance < closest_distance) + { + find = true; + closest = entity.Value; + closest_distance = distance; + } + } + return find; + } + + private static string InteractionWithEntity(McClient handler, Entity entity, ActionType action) + { + switch (action) + { + case ActionType.Attack: + handler.InteractEntity(entity.ID, InteractType.Attack); + return Translations.cmd_entityCmd_attacked; + case ActionType.Use: + handler.InteractEntity(entity.ID, InteractType.Interact); + return Translations.cmd_entityCmd_used; + case ActionType.List: + return GetEntityInfoDetailed(handler, entity); + default: + goto case ActionType.List; + } } } -} +} \ No newline at end of file diff --git a/MinecraftClient/Commands/ExecIf.cs b/MinecraftClient/Commands/ExecIf.cs index e19e5bc3..3557e200 100644 --- a/MinecraftClient/Commands/ExecIf.cs +++ b/MinecraftClient/Commands/ExecIf.cs @@ -1,99 +1,94 @@ using System; using System.Collections.Generic; -using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; using DynamicExpresso; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { class ExecIf : Command { public override string CmdName { get { return "execif"; } } - public override string CmdUsage { get { return "execif ---> "; } } + public override string CmdUsage { get { return "execif \"\" \"\""; } } public override string CmdDesc { get { return Translations.cmd_execif_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Condition", Arguments.String()) + .Then(l => l.Argument("Command", Arguments.String()) + .Executes(r => HandleCommand(r.Source, handler, Arguments.GetString(r, "Condition"), Arguments.GetString(r, "Command"))))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string commandsString = GetArg(command); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (!commandsString.Contains("--->")) - return GetCmdDescTranslated(); + private int HandleCommand(CmdResult r, McClient handler, string expressionText, string resultCommand) + { + try + { + var interpreter = new Interpreter(); + interpreter.SetVariable("MCC", handler); - string[] parts = commandsString.Split("--->", StringSplitOptions.TrimEntries) - .ToList() - .ConvertAll(command => command.Trim()) - .ToArray(); + foreach (KeyValuePair entry in Settings.Config.AppVar.GetVariables()) + interpreter.SetVariable(entry.Key, entry.Value); - if (parts.Length == 0) - return GetCmdDescTranslated(); + var result = interpreter.Eval(expressionText); - string expressionText = parts[0]; - string resultCommand = parts[1]; + bool shouldExec = result; - try + /*if (result is bool) + shouldExec = (bool)result; + else if (result is string) + shouldExec = !string.IsNullOrEmpty((string)result) && ((string)result).Trim().Contains("true", StringComparison.OrdinalIgnoreCase); + else if (result is int) + shouldExec = (int)result > 0; + else if (result is double) + shouldExec = (double)result > 0; + else if (result is float) + shouldExec = (float)result > 0; + else if (result is Int16) + shouldExec = (Int16)result > 0; + else if (result is Int32) + shouldExec = (Int32)result > 0; + else if (result is Int64) + shouldExec = (Int64)result > 0; + */ + + handler.Log.Debug("[Execif] Result Type: " + result.GetType().Name); + + if (shouldExec) { - var interpreter = new Interpreter(); - interpreter.SetVariable("MCC", handler); + CmdResult output = new(); + handler.PerformInternalCommand(resultCommand, ref output); - foreach (KeyValuePair entry in Settings.Config.AppVar.GetVariables()) - interpreter.SetVariable(entry.Key, entry.Value); - - var result = interpreter.Eval(expressionText); - - bool shouldExec = result; - - /*if (result is bool) - shouldExec = (bool)result; - else if (result is string) - shouldExec = !string.IsNullOrEmpty((string)result) && ((string)result).Trim().Contains("true", StringComparison.OrdinalIgnoreCase); - else if (result is int) - shouldExec = (int)result > 0; - else if (result is double) - shouldExec = (double)result > 0; - else if (result is float) - shouldExec = (float)result > 0; - else if (result is Int16) - shouldExec = (Int16)result > 0; - else if (result is Int32) - shouldExec = (Int32)result > 0; - else if (result is Int64) - shouldExec = (Int64)result > 0; - */ - - handler.Log.Debug("[Execif] Result Type: " + result.GetType().Name); - - if (shouldExec) - { - string? output = ""; - handler.PerformInternalCommand(resultCommand, ref output); - - if (string.IsNullOrEmpty(output)) - handler.Log.Debug(string.Format(Translations.cmd_execif_executed_no_output, expressionText, resultCommand)); - else - handler.Log.Debug(string.Format(Translations.cmd_execif_executed, expressionText, resultCommand, output)); - - return ""; - } - - return ""; + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_execif_executed, expressionText, resultCommand, output)); } - catch (Exception e) + else { - handler.Log.Error(string.Format(Translations.cmd_execif_error_occured, command)); - handler.Log.Error(string.Format(Translations.cmd_execif_error, e.Message)); - return ""; + return r.SetAndReturn(CmdResult.Status.Done); } } - - return GetCmdDescTranslated(); + catch (Exception e) + { + handler.Log.Error(string.Format(Translations.cmd_execif_error, e.Message)); + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_execif_error_occured, expressionText + " ---> " + resultCommand)); + } } - - } } diff --git a/MinecraftClient/Commands/ExecMulti.cs b/MinecraftClient/Commands/ExecMulti.cs index 983e5f85..bebff1a1 100644 --- a/MinecraftClient/Commands/ExecMulti.cs +++ b/MinecraftClient/Commands/ExecMulti.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -11,42 +13,47 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "execmulti -> -> -> ..."; } } public override string CmdDesc { get { return Translations.cmd_execmulti_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Commands", Arguments.GreedyString()) + .Executes(r => HandleCommand(r.Source, handler, Arguments.GetString(r, "Commands")))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string commandsString = GetArg(command); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (commandsString.Contains("execmulti", StringComparison.OrdinalIgnoreCase) || commandsString.Contains("execif", StringComparison.OrdinalIgnoreCase)) - return Translations.cmd_execmulti_prevent; + private int HandleCommand(CmdResult r, McClient handler, string commandsString) + { + if (commandsString.Contains("execmulti", StringComparison.OrdinalIgnoreCase) || commandsString.Contains("execif", StringComparison.OrdinalIgnoreCase)) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_execmulti_prevent); - IEnumerable commands = commandsString.Split("->", StringSplitOptions.TrimEntries) - .ToList() - .FindAll(command => !string.IsNullOrEmpty(command)); + IEnumerable commands = commandsString.Split("->", StringSplitOptions.TrimEntries) + .ToList() + .FindAll(command => !string.IsNullOrEmpty(command)); - foreach (string cmd in commands) - { - string? output = ""; - handler.PerformInternalCommand(cmd, ref output); - - string log = string.Format( - Translations.cmd_execmulti_executed, cmd, - string.IsNullOrEmpty(output) ? Translations.cmd_execmulti_no_result : string.Format(Translations.cmd_execmulti_result, output)); - - if (output != null && output.Contains("unknown command", StringComparison.OrdinalIgnoreCase)) - handler.Log.Error(log); - else - handler.Log.Info(log); - } - - return ""; + foreach (string cmd in commands) + { + CmdResult output = new(); + handler.PerformInternalCommand(cmd, ref output); + handler.Log.Info(string.Format(Translations.cmd_execmulti_executed, cmd, string.Format(Translations.cmd_execmulti_result, output))); } - return GetCmdDescTranslated(); + return r.SetAndReturn(CmdResult.Status.Done); } } } diff --git a/MinecraftClient/Commands/Exit.cs b/MinecraftClient/Commands/Exit.cs index 9a8ade0d..5618b248 100644 --- a/MinecraftClient/Commands/Exit.cs +++ b/MinecraftClient/Commands/Exit.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,19 +10,48 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "exit"; } } public override string CmdDesc { get { return Translations.cmd_exit_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + var exit = dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoExit(r.Source, 0)) + .Then(l => l.Argument("ExitCode", Arguments.Integer()) + .Executes(r => DoExit(r.Source, Arguments.GetInteger(r, "ExitCode")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); + + dispatcher.Register(l => l.Literal("quit") + .Executes(r => DoExit(r.Source, 0)) + .Redirect(exit) + ); } - public override string Run(McClient? handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoExit(CmdResult r, int code = 0) + { + Program.Exit(code); + return r.SetAndReturn(CmdResult.Status.Done); + } + + internal static string DoExit(string command) { Program.Exit(); - return ""; - } - - public override IEnumerable GetCMDAliases() - { - return new string[] { "quit" }; + return string.Empty; } } } diff --git a/MinecraftClient/Commands/Health.cs b/MinecraftClient/Commands/Health.cs index 18f0f67c..29c95598 100644 --- a/MinecraftClient/Commands/Health.cs +++ b/MinecraftClient/Commands/Health.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,13 +10,34 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "health"; } } public override string CmdDesc { get { return Translations.cmd_health_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => LogHealth(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - return string.Format(Translations.cmd_health_response, handler.GetHealth(), handler.GetSaturation(), handler.GetLevel(), handler.GetTotalExperience()); + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int LogHealth(CmdResult r, McClient handler) + { + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_health_response, handler.GetHealth(), handler.GetSaturation(), handler.GetLevel(), handler.GetTotalExperience())); } } } diff --git a/MinecraftClient/Commands/Help.cs b/MinecraftClient/Commands/Help.cs new file mode 100644 index 00000000..4727f2bb --- /dev/null +++ b/MinecraftClient/Commands/Help.cs @@ -0,0 +1,33 @@ +using System; +using System.Text; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; + +namespace MinecraftClient.Commands +{ + internal class Help : Command + { + public override string CmdName => throw new NotImplementedException(); + public override string CmdDesc => throw new NotImplementedException(); + public override string CmdUsage => throw new NotImplementedException(); + + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { + dispatcher.Register(l => + l.Literal("help") + .Executes(r => LogHelp(r.Source, handler, dispatcher)) + ); + } + + private int LogHelp(CmdResult r, McClient handler, CommandDispatcher dispatcher) + { + var usage = dispatcher.GetSmartUsage(dispatcher.GetRoot(), CmdResult.Empty); + StringBuilder sb = new(); + foreach (var item in usage) + sb.AppendLine(Settings.Config.Main.Advanced.InternalCmdChar.ToChar() + item.Value); + handler.Log.Info(string.Format(Translations.icmd_list, sb.ToString(), Settings.Config.Main.Advanced.InternalCmdChar.ToChar())); + return r.SetAndReturn(CmdResult.Status.Done); + } + } +} diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index 17f45c7d..89f48c6e 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; namespace MinecraftClient.Commands @@ -14,270 +15,374 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return GetBasicUsage(); } } public override string CmdDesc { get { return Translations.cmd_inventory_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("list") + .Executes(r => GetUsage(r.Source, "list"))) + .Then(l => l.Literal("close") + .Executes(r => GetUsage(r.Source, "close"))) + .Then(l => l.Literal("click") + .Executes(r => GetUsage(r.Source, "click"))) + .Then(l => l.Literal("drop") + .Executes(r => GetUsage(r.Source, "drop"))) + .Then(l => l.Literal("creativegive") + .Executes(r => GetUsage(r.Source, "creativegive"))) + .Then(l => l.Literal("creativedelete") + .Executes(r => GetUsage(r.Source, "creativedelete"))) + .Then(l => l.Literal("inventories") + .Executes(r => GetUsage(r.Source, "inventories"))) + .Then(l => l.Literal("search") + .Executes(r => GetUsage(r.Source, "search"))) + .Then(l => l.Literal("help") + .Executes(r => GetUsage(r.Source, "help"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => ListAllInventories(r.Source, handler)) + .Then(l => l.Literal("creativegive") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Then(l => l.Argument("Count", Arguments.Integer(min: 1)) + .Executes(r => DoCreativeGive(r.Source, handler, Arguments.GetInteger(r, "Slot"), MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count"))))))) + .Then(l => l.Literal("creativedelete") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoCreativeDelete(r.Source, handler, Arguments.GetInteger(r, "Slot"))))) + .Then(l => l.Literal("inventories") + .Executes(r => ListAvailableInventories(r.Source, handler))) + .Then(l => l.Literal("search") + .Then(l => l.Argument("ItemType", MccArguments.ItemType()) + .Executes(r => SearchItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType"), null)) + .Then(l => l.Argument("Count", Arguments.Integer(0, 64)) + .Executes(r => SearchItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count")))))) + .Then(l => l.Argument("InventoryId", MccArguments.InventoryId()) + .Then(l => l.Literal("close") + .Executes(r => DoCloseAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) + .Then(l => l.Literal("list") + .Executes(r => DoListAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Then(l => l.Argument("Action", MccArguments.InventoryAction()) + .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Then(l => l.Literal("drop") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Then(l => l.Literal("all") + .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Then(l => l.Literal("player") + .Then(l => l.Literal("list") + .Executes(r => DoListAction(r.Source, handler, inventoryId: 0))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 45)) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Then(l => l.Argument("Action", MccArguments.InventoryAction()) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Then(l => l.Literal("drop") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 45)) + .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Then(l => l.Literal("all") + .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Then(l => l.Literal("container") + .Then(l => l.Literal("close") + .Executes(r => DoCloseAction(r.Source, handler, inventoryId: null))) + .Then(l => l.Literal("list") + .Executes(r => DoListAction(r.Source, handler, inventoryId: null))) + .Then(l => l.Literal("click") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Then(l => l.Argument("Action", MccArguments.InventoryAction()) + .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Then(l => l.Literal("drop") + .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Then(l => l.Literal("all") + .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetInventoryEnabled()) + string usageStr = ' ' + Translations.cmd_inventory_help_usage + ": "; + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length >= 1) - { - int inventoryId; - if (args[0].ToLower() == "creativegive") - { - if (args.Length >= 4) - { - if (!int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + "list" => Translations.cmd_inventory_help_list + usageStr + "/inventory > list", + "close" => Translations.cmd_inventory_help_close + usageStr + "/inventory > close", + "click" => Translations.cmd_inventory_help_click + usageStr + "/inventory > click [left|right|middle|shift]\nDefault is left click", + "drop" => Translations.cmd_inventory_help_drop + usageStr + "/inventory > drop [all]\nAll means drop full stack", + "creativegive" => Translations.cmd_inventory_help_creativegive + usageStr + "/inventory creativegive ", + "creativedelete" => Translations.cmd_inventory_help_creativedelete + usageStr + "/inventory creativedelete ", + "inventories" => Translations.cmd_inventory_help_inventories + usageStr + "/inventory inventories", + "search" => Translations.cmd_inventory_help_search + usageStr + "/inventory search [count]", + "help" => GetCmdDescTranslated(), + "action" => Translations.cmd_inventory_help_unknown + GetAvailableActions(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (Enum.TryParse(args[2], true, out ItemType itemType)) - { - if (handler.GetGamemode() == 1) - { - if (!int.TryParse(args[3], NumberStyles.Any, CultureInfo.CurrentCulture, out int count)) - return GetCmdDescTranslated(); + private int GetMaximumInventoryId(McClient handler) + { + List availableIds = handler.GetInventories().Keys.ToList(); + return availableIds.Max(); // use foreground container + } - if (handler.DoCreativeGive(slot, itemType, count, null)) - return string.Format(Translations.cmd_inventory_creative_done, itemType, count, slot); - else - return Translations.cmd_inventory_creative_fail; - } - else - return Translations.cmd_inventory_need_creative; - } - else - return GetCmdDescTranslated(); - } - else - return GetCmdDescTranslated(); - } - else if (args[0].ToLower() == "creativedelete") - { - if (args.Length >= 2) - { - if (!int.TryParse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); + private int ListAllInventories(CmdResult r, McClient handler) + { + if (!handler.GetInventoryEnabled()) + { + handler.Log.Info(Translations.extra_inventory_required); + return -1; + } + StringBuilder response = new(); + response.Append(Translations.cmd_inventory_inventories).Append(":\n"); + foreach ((int invId, Container inv) in handler.GetInventories()) + response.AppendLine(String.Format(" #{0}: {1}§8", invId, inv.Title)); + response.Append(CmdUsage); + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } - if (handler.GetGamemode() == 1) - { - if (handler.DoCreativeGive(slot, ItemType.Null, 0, null)) - return string.Format(Translations.cmd_inventory_creative_delete, slot); - else - return Translations.cmd_inventory_creative_fail; - } - else - return Translations.cmd_inventory_need_creative; - } - else - return GetCmdDescTranslated(); - } - else if (args[0].ToLower().StartsWith("p")) - { - // player inventory is always ID 0 - inventoryId = 0; - } - else if (args[0].ToLower().StartsWith("c")) - { - List availableIds = handler.GetInventories().Keys.ToList(); - availableIds.Remove(0); // remove player inventory ID from list - if (availableIds.Count > 0) - inventoryId = availableIds.Max(); // use foreground container - else - return Translations.cmd_inventory_container_not_found; - } - else if (args[0].ToLower().StartsWith("inventories") || args[0].ToLower().StartsWith("i")) - { - Dictionary inventories = handler.GetInventories(); - List availableIds = inventories.Keys.ToList(); - StringBuilder response = new(); - response.AppendLine(Translations.cmd_inventory_inventories_available); + private int DoCreativeGive(CmdResult r, McClient handler, int slot, ItemType itemType, int count) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); - foreach (int id in availableIds) - response.AppendLine(String.Format(" #{0} - {1}§8", id, inventories[id].Title)); - - return response.ToString(); - } - else if (args[0].ToLower().StartsWith("search") || args[0].ToLower().StartsWith("s")) - { - if (args.Length < 2) - return GetCmdDescTranslated(); - - if (!Enum.TryParse(args[1], true, out ItemType parsedItemType)) - return GetCmdDescTranslated(); - - bool shouldUseItemCount = args.Length >= 3; - int itemCount = 0; - - if (shouldUseItemCount && !int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out itemCount)) - return GetCmdDescTranslated(); - - Dictionary inventories = handler.GetInventories(); - Dictionary> foundItems = new(); - - List availableInventories = inventories.Values.ToList(); - - availableInventories.ForEach(inventory => - { - inventory.Items.Values - .ToList() - .FindAll(item => item.Type == parsedItemType && (!shouldUseItemCount || item.Count == itemCount)) - .ForEach(item => - { - if (!foundItems.ContainsKey(inventory.ID)) - { - foundItems.Add(inventory.ID, new List() { item }); - return; - } - - List invItems = foundItems[inventory.ID]; - invItems.Add(item); - foundItems.Remove(inventory.ID); - foundItems.Add(inventory.ID, invItems); - }); - }); - - if (foundItems.Count == 0) - return Translations.cmd_inventory_no_found_items; - - StringBuilder response = new(); - - response.AppendLine(Translations.cmd_inventory_found_items + ":"); - - foreach ((int invId, List itemsList) in new SortedDictionary>(foundItems)) - { - if (itemsList.Count > 0) - { - response.AppendLine(String.Format("{0} (#{1}):", inventories[invId].Title, invId)); - - foreach (Item item in itemsList) - response.AppendLine(String.Format("\t- {0}", item.ToFullString())); - - response.AppendLine(" "); - } - } - - return response.ToString(); - } - else if (args[0].ToLower() == "help") - { - if (args.Length >= 2) - return GetSubCommandHelp(args[1]); - else - return GetHelp(); - } - else if (!int.TryParse(args[0], NumberStyles.Any, CultureInfo.CurrentCulture, out inventoryId)) - return GetCmdDescTranslated(); - - Container? inventory = handler.GetInventory(inventoryId); - if (inventory == null) - return string.Format(Translations.cmd_inventory_not_exist, inventoryId); - - string action = args.Length > 1 ? args[1].ToLower() : "list"; - if (action == "close") - { - if (handler.CloseInventory(inventoryId)) - return string.Format(Translations.cmd_inventory_close, inventoryId); - else - return string.Format(Translations.cmd_inventory_close_fail, inventoryId); - } - else if (action == "list") - { - StringBuilder response = new(); - response.Append(Translations.cmd_inventory_inventory); - response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title)); - - string? asciiArt = inventory.Type.GetAsciiArt(); - if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout) - response.AppendLine(asciiArt); - - int selectedHotbar = handler.GetCurrentSlot() + 1; - foreach ((int itemId, Item item) in new SortedDictionary(inventory.Items)) - { - bool isHotbar = inventory.IsHotbar(itemId, out int hotbar); - string hotbarString = isHotbar ? (hotbar + 1).ToString() : " "; - if ((hotbar + 1) == selectedHotbar) - hotbarString = ">" + hotbarString; - response.AppendLine(String.Format("{0,2} | #{1,-2}: {2}", hotbarString, itemId, item.ToFullString())); - } - - if (inventoryId == 0) - response.AppendLine(string.Format(Translations.cmd_inventory_hotbar, (handler.GetCurrentSlot() + 1))); - - response.Remove(response.Length - 1, 1); // Remove last '\n' - return response.ToString(); - } - else if (action == "click" && args.Length >= 3) - { - if (!int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); - - WindowActionType actionType = WindowActionType.LeftClick; - string keyName = Translations.cmd_inventory_left; - if (args.Length >= 4) - { - string b = args[3]; - if (b.ToLower()[0] == 'r') - (actionType, keyName) = (WindowActionType.RightClick, Translations.cmd_inventory_right); - else if (b.ToLower()[0] == 'm') - (actionType, keyName) = (WindowActionType.MiddleClick, Translations.cmd_inventory_middle); - } - - handler.DoWindowAction(inventoryId, slot, actionType); - return string.Format(Translations.cmd_inventory_clicking, keyName, slot, inventoryId); - } - else if (action == "shiftclick" && args.Length >= 3) - { - if (!int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); - - if (!handler.DoWindowAction(inventoryId, slot, WindowActionType.ShiftClick)) - return Translations.cmd_inventory_shiftclick_fail; - - return string.Format(Translations.cmd_inventory_shiftclick, slot, inventoryId); - } - else if (action == "drop" && args.Length >= 3) - { - if (!int.TryParse(args[2], NumberStyles.Any, CultureInfo.CurrentCulture, out int slot)) - return GetCmdDescTranslated(); - - // check item exist - if (!inventory.Items.ContainsKey(slot)) - return string.Format(Translations.cmd_inventory_no_item, slot); - - WindowActionType actionType = WindowActionType.DropItem; - if (args.Length >= 4 && args[3].ToLower() == "all") - actionType = WindowActionType.DropItemStack; - - if (handler.DoWindowAction(inventoryId, slot, actionType)) - { - if (actionType == WindowActionType.DropItemStack) - return string.Format(Translations.cmd_inventory_drop_stack, slot); - else - return string.Format(Translations.cmd_inventory_drop, slot); - } - else - return "Failed"; - } - else - return GetCmdDescTranslated(); - } + if (handler.GetGamemode() == 1) + { + if (handler.DoCreativeGive(slot, itemType, count, null)) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_creative_done, itemType, count, slot)); else - { - StringBuilder response = new(); - response.Append(Translations.cmd_inventory_inventories).Append(":\n"); - foreach ((int invId, Container inv) in handler.GetInventories()) - response.AppendLine(String.Format(" #{0}: {1}§8", invId, inv.Title)); - response.Append(CmdUsage); - return response.ToString(); - } + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_creative_fail); } else - return Translations.extra_inventory_required; + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_need_creative); + } } + private int DoCreativeDelete(CmdResult r, McClient handler, int slot) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (handler.GetGamemode() == 1) + { + if (handler.DoCreativeGive(slot, ItemType.Null, 0, null)) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_creative_delete, slot)); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_creative_fail); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_need_creative); + } + } + + private int ListAvailableInventories(CmdResult r, McClient handler) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + Dictionary inventories = handler.GetInventories(); + List availableIds = inventories.Keys.ToList(); + StringBuilder response = new(); + response.AppendLine(Translations.cmd_inventory_inventories_available); + + foreach (int id in availableIds) + response.AppendLine(String.Format(" #{0} - {1}§8", id, inventories[id].Title)); + + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int SearchItem(CmdResult r, McClient handler, ItemType itemType, int? itemCount) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + Dictionary inventories = handler.GetInventories(); + Dictionary> foundItems = new(); + + List availableInventories = inventories.Values.ToList(); + + availableInventories.ForEach(inventory => + { + inventory.Items.Values + .ToList() + .FindAll(item => item.Type == itemType && (!itemCount.HasValue || item.Count == itemCount)) + .ForEach(item => + { + if (!foundItems.ContainsKey(inventory.ID)) + { + foundItems.Add(inventory.ID, new List() { item }); + return; + } + + List invItems = foundItems[inventory.ID]; + invItems.Add(item); + foundItems.Remove(inventory.ID); + foundItems.Add(inventory.ID, invItems); + }); + }); + + if (foundItems.Count == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_no_found_items); + + StringBuilder response = new(); + + response.AppendLine(Translations.cmd_inventory_found_items + ":"); + + foreach ((int invId, List itemsList) in new SortedDictionary>(foundItems)) + { + if (itemsList.Count > 0) + { + response.AppendLine(String.Format("{0} (#{1}):", inventories[invId].Title, invId)); + + foreach (Item item in itemsList) + response.AppendLine(String.Format("\t- {0}", item.ToFullString())); + + response.AppendLine(" "); + } + } + + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int DoCloseAction(CmdResult r, McClient handler, int? inventoryId) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + if (handler.CloseInventory(inventoryId.Value)) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_close, inventoryId)); + else + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_close_fail, inventoryId)); + } + + private int DoListAction(CmdResult r, McClient handler, int? inventoryId) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + StringBuilder response = new(); + response.Append(Translations.cmd_inventory_inventory); + response.AppendLine(String.Format(" #{0} - {1}§8", inventoryId, inventory.Title)); + + string? asciiArt = inventory.Type.GetAsciiArt(); + if (asciiArt != null && Settings.Config.Main.Advanced.ShowInventoryLayout) + response.AppendLine(asciiArt); + + int selectedHotbar = handler.GetCurrentSlot() + 1; + foreach ((int itemId, Item item) in new SortedDictionary(inventory.Items)) + { + bool isHotbar = inventory.IsHotbar(itemId, out int hotbar); + string hotbarString = isHotbar ? (hotbar + 1).ToString() : " "; + if ((hotbar + 1) == selectedHotbar) + hotbarString = ">" + hotbarString; + response.AppendLine(String.Format("{0,2} | #{1,-2}: {2}", hotbarString, itemId, item.ToFullString())); + } + + if (inventoryId == 0) + response.AppendLine(string.Format(Translations.cmd_inventory_hotbar, (handler.GetCurrentSlot() + 1))); + + response.Remove(response.Length - 1, 1); // Remove last '\n' + handler.Log.Info(response.ToString()); + return r.SetAndReturn(CmdResult.Status.Done); + } + + private int DoClickAction(CmdResult r, McClient handler, int? inventoryId, int slot, WindowActionType actionType) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + string keyName = actionType switch + { + WindowActionType.LeftClick => Translations.cmd_inventory_left, + WindowActionType.RightClick => Translations.cmd_inventory_right, + WindowActionType.MiddleClick => Translations.cmd_inventory_middle, + WindowActionType.ShiftClick => Translations.cmd_inventory_shiftclick, + _ => "unknown", + }; + + handler.Log.Info(string.Format(Translations.cmd_inventory_clicking, keyName, slot, inventoryId)); + return r.SetAndReturn(handler.DoWindowAction(inventoryId.Value, slot, actionType)); + } + + private int DoDropAction(CmdResult r, McClient handler, int? inventoryId, int slot, WindowActionType actionType) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(CmdResult.Status.FailNeedInventory); + + if (!inventoryId.HasValue) + { + inventoryId = GetMaximumInventoryId(handler); + if (inventoryId == 0) + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_container_not_found); + } + + Container? inventory = handler.GetInventory(inventoryId.Value); + if (inventory == null) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); + + // check item exist + if (!inventory.Items.ContainsKey(slot)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_no_item, slot)); + + if (handler.DoWindowAction(inventoryId.Value, slot, actionType)) + { + if (actionType == WindowActionType.DropItemStack) + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_drop_stack, slot)); + else + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_drop, slot)); + } + else + { + return r.SetAndReturn(CmdResult.Status.Fail, "Drop Failed"); + } + } + + #region Methods for commands help private static string GetAvailableActions() @@ -290,31 +395,6 @@ namespace MinecraftClient.Commands return Translations.cmd_inventory_help_basic + ": /inventory > ."; } - private static string GetHelp() - { - return string.Format(Translations.cmd_inventory_help_help, GetAvailableActions()); - } - - private static string GetSubCommandHelp(string cmd) - { - string usageStr = ' ' + Translations.cmd_inventory_help_usage + ": "; - return cmd switch - { -#pragma warning disable format // @formatter:off - "list" => Translations.cmd_inventory_help_list + usageStr + "/inventory > list", - "close" => Translations.cmd_inventory_help_close + usageStr + "/inventory > close", - "click" => Translations.cmd_inventory_help_click + usageStr + "/inventory > click [left|right|middle]\nDefault is left click", - "shiftclick" => Translations.cmd_inventory_help_shiftclick + usageStr + "/inventory > shiftclick ", - "drop" => Translations.cmd_inventory_help_drop + usageStr + "/inventory > drop [all]\nAll means drop full stack", - "creativegive" => Translations.cmd_inventory_help_creativegive + usageStr + "/inventory creativegive ", - "creativedelete" => Translations.cmd_inventory_help_creativedelete + usageStr + "/inventory creativedelete ", - "inventories" => Translations.cmd_inventory_help_inventories + usageStr + "/inventory inventories", - "search" => Translations.cmd_inventory_help_search + usageStr + "/inventory search [count]", - "help" => GetHelp(), - _ => Translations.cmd_inventory_help_unknown + GetAvailableActions(), -#pragma warning restore format // @formatter:on - }; - } #endregion } } diff --git a/MinecraftClient/Commands/List.cs b/MinecraftClient/Commands/List.cs index 64273a89..78b3c9d2 100644 --- a/MinecraftClient/Commands/List.cs +++ b/MinecraftClient/Commands/List.cs @@ -1,6 +1,7 @@ using System; -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -10,13 +11,34 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "list"; } } public override string CmdDesc { get { return Translations.cmd_list_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoListPlayers(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - return string.Format(Translations.cmd_list_players, String.Join(", ", handler.GetOnlinePlayers())); + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoListPlayers(CmdResult r, McClient handler) + { + return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_list_players, String.Join(", ", handler.GetOnlinePlayers()))); } } } diff --git a/MinecraftClient/Commands/Log.cs b/MinecraftClient/Commands/Log.cs index 4b03e93f..e9181ed3 100644 --- a/MinecraftClient/Commands/Log.cs +++ b/MinecraftClient/Commands/Log.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,18 +10,35 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "log "; } } public override string CmdDesc { get { return Translations.cmd_log_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("String", Arguments.GreedyString()) + .Executes(r => DoLog(r.Source, Arguments.GetString(r, "String")))) + .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - ConsoleIO.WriteLogLine(GetArg(command)); - return ""; - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoLog(CmdResult r, string command) + { + return r.SetAndReturn(CmdResult.Status.Done, command); } } } diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs index f03e69d3..2376bbc1 100644 --- a/MinecraftClient/Commands/Look.cs +++ b/MinecraftClient/Commands/Look.cs @@ -1,8 +1,9 @@ using System; -using System.Collections.Generic; -using System.Globalization; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -12,74 +13,102 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "look "; } } public override string CmdDesc { get { return Translations.cmd_look_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("direction") + .Executes(r => GetUsage(r.Source, "direction"))) + .Then(l => l.Literal("angle") + .Executes(r => GetUsage(r.Source, "angle"))) + .Then(l => l.Literal("location") + .Executes(r => GetUsage(r.Source, "location"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => LogCurrentLooking(r.Source, handler)) + .Then(l => l.Literal("up") + .Executes(r => LookAtDirection(r.Source, handler, Direction.Up))) + .Then(l => l.Literal("down") + .Executes(r => LookAtDirection(r.Source, handler, Direction.Down))) + .Then(l => l.Literal("east") + .Executes(r => LookAtDirection(r.Source, handler, Direction.East))) + .Then(l => l.Literal("west") + .Executes(r => LookAtDirection(r.Source, handler, Direction.West))) + .Then(l => l.Literal("north") + .Executes(r => LookAtDirection(r.Source, handler, Direction.North))) + .Then(l => l.Literal("south") + .Executes(r => LookAtDirection(r.Source, handler, Direction.South))) + .Then(l => l.Argument("Yaw", Arguments.Float()) + .Then(l => l.Argument("Pitch", Arguments.Float()) + .Executes(r => LookAtAngle(r.Source, handler, Arguments.GetFloat(r, "Yaw"), Arguments.GetFloat(r, "Pitch"))))) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => LookAtLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetTerrainEnabled()) + return r.SetAndReturn(cmd switch { - string[] args = GetArgs(command); - if (args.Length == 0) - { - const double maxDistance = 8.0; - (bool hasBlock, Location target, Block block) = RaycastHelper.RaycastBlock(handler, maxDistance, false); - if (!hasBlock) - return string.Format(Translations.cmd_look_noinspection, maxDistance); - else - { - Location current = handler.GetCurrentLocation(), target_center = target.ToCenter(); - return string.Format(Translations.cmd_look_inspection, block.Type, target.X, target.Y, target.Z, - current.Distance(target_center), current.EyesLocation().Distance(target_center)); - } - } - else if (args.Length == 1) - { - string dirStr = GetArg(command).Trim().ToLower(); - Direction direction; - switch (dirStr) - { - case "up": direction = Direction.Up; break; - case "down": direction = Direction.Down; break; - case "east": direction = Direction.East; break; - case "west": direction = Direction.West; break; - case "north": direction = Direction.North; break; - case "south": direction = Direction.South; break; - default: return string.Format(Translations.cmd_look_unknown, dirStr); - } +#pragma warning disable format // @formatter:off + "direction" => GetCmdDescTranslated(), + "angle" => GetCmdDescTranslated(), + "location" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - handler.UpdateLocation(handler.GetCurrentLocation(), direction); - return "Looking " + dirStr; - } - else if (args.Length == 2) - { - try - { - float yaw = float.Parse(args[0], NumberStyles.Any, CultureInfo.CurrentCulture); - float pitch = float.Parse(args[1], NumberStyles.Any, CultureInfo.CurrentCulture); + private int LogCurrentLooking(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); - handler.UpdateLocation(handler.GetCurrentLocation(), yaw, pitch); - return string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00")); - } - catch (FormatException) { return GetCmdDescTranslated(); } - } - else if (args.Length == 3) - { - try - { - Location current = handler.GetCurrentLocation(); - Location block = Location.Parse(current, args[0], args[1], args[2]); - handler.UpdateLocation(current, block); - - return string.Format(Translations.cmd_look_block, block); - } - catch (FormatException) { return CmdUsage; } - - } - else return GetCmdDescTranslated(); + const double maxDistance = 8.0; + (bool hasBlock, Location target, Block block) = RaycastHelper.RaycastBlock(handler, maxDistance, false); + if (!hasBlock) + { + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_look_noinspection, maxDistance)); } - else return Translations.extra_terrainandmovement_required; + else + { + Location current = handler.GetCurrentLocation(), target_center = target.ToCenter(); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_inspection, block.Type, target.X, target.Y, target.Z, + current.Distance(target_center), current.EyesLocation().Distance(target_center))); + } + } + + private int LookAtDirection(CmdResult r, McClient handler, Direction direction) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + handler.UpdateLocation(handler.GetCurrentLocation(), direction); + return r.SetAndReturn(Status.Done, "Looking " + direction.ToString()); + } + + private int LookAtAngle(CmdResult r, McClient handler, float yaw, float pitch) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + handler.UpdateLocation(handler.GetCurrentLocation(), yaw, pitch); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00"))); + } + + private int LookAtLocation(CmdResult r, McClient handler, Location location) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + handler.UpdateLocation(current, location); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_block, location)); } } } diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index 679f51f6..d3b0897b 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -1,8 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -12,111 +13,183 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "move [-f]"; } } public override string CmdDesc { get { return Translations.cmd_move_desc + " \"-f\": " + Translations.cmd_move_desc_force; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("enable") + .Executes(r => GetUsage(r.Source, "enable"))) + .Then(l => l.Literal("gravity") + .Executes(r => GetUsage(r.Source, "gravity"))) + .Then(l => l.Literal("direction") + .Executes(r => GetUsage(r.Source, "direction"))) + .Then(l => l.Literal("center") + .Executes(r => GetUsage(r.Source, "center"))) + .Then(l => l.Literal("get") + .Executes(r => GetUsage(r.Source, "get"))) + .Then(l => l.Literal("location") + .Executes(r => GetUsage(r.Source, "location"))) + .Then(l => l.Literal("-f") + .Executes(r => GetUsage(r.Source, "-f"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Literal("on") + .Executes(r => SetMovementEnable(r.Source, handler, enable: true))) + .Then(l => l.Literal("off") + .Executes(r => SetMovementEnable(r.Source, handler, enable: false))) + .Then(l => l.Literal("gravity") + .Executes(r => SetGravityEnable(r.Source, handler, enable: null)) + .Then(l => l.Literal("on") + .Executes(r => SetGravityEnable(r.Source, handler, enable: true))) + .Then(l => l.Literal("off") + .Executes(r => SetGravityEnable(r.Source, handler, enable: false)))) + .Then(l => l.Literal("up") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Up, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Up, true)))) + .Then(l => l.Literal("down") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Down, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.Down, true)))) + .Then(l => l.Literal("east") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.East, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.East, true)))) + .Then(l => l.Literal("west") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.West, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.West, true)))) + .Then(l => l.Literal("north") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.North, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.North, true)))) + .Then(l => l.Literal("south") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.South, false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveOnDirection(r.Source, handler, Direction.South, true)))) + .Then(l => l.Literal("center") + .Executes(r => MoveToCenter(r.Source, handler))) + .Then(l => l.Literal("get") + .Executes(r => GetCurrentLocation(r.Source, handler))) + .Then(l => l.Argument("location", MccArguments.Location()) + .Executes(r => MoveToLocation(r.Source, handler, MccArguments.GetLocation(r, "location"), false)) + .Then(l => l.Literal("-f") + .Executes(r => MoveToLocation(r.Source, handler, MccArguments.GetLocation(r, "location"), true)))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - List args = GetArgs(command.ToLower()).ToList(); - bool takeRisk = false; - - if (args.Count < 1) + return r.SetAndReturn(cmd switch { - string desc = GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + "enable" => GetCmdDescTranslated(), + "gravity" => GetCmdDescTranslated(), + "direction" => GetCmdDescTranslated(), + "center" => GetCmdDescTranslated(), + "get" => GetCmdDescTranslated(), + "location" => GetCmdDescTranslated(), + "-f" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (handler.GetTerrainEnabled()) - handler.Log.Info(World.GetChunkLoadingStatus(handler.GetWorld())); - - return desc; - } - - if (args.Contains("-f")) - { - takeRisk = true; - args.Remove("-f"); - } - - if (args[0] == "on") + private int SetMovementEnable(CmdResult r, McClient handler, bool enable) + { + if (enable) { handler.SetTerrainEnabled(true); - return Translations.cmd_move_enable; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_enable); } - else if (args[0] == "off") + else { handler.SetTerrainEnabled(false); - return Translations.cmd_move_disable; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_disable); } - else if (args[0] == "gravity") + } + + private int SetGravityEnable(CmdResult r, McClient handler, bool? enable) + { + if (enable.HasValue) + Settings.InternalConfig.GravityEnabled = enable.Value; + + if (Settings.InternalConfig.GravityEnabled) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_gravity_enabled); + else + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_gravity_disabled); + } + + private int GetCurrentLocation(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + return r.SetAndReturn(Status.Done, handler.GetCurrentLocation().ToString()); + } + + private int MoveToCenter(CmdResult r, McClient handler) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + Location currentCenter = new(Math.Floor(current.X) + 0.5, current.Y, Math.Floor(current.Z) + 0.5); + handler.MoveTo(currentCenter, allowDirectTeleport: true); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_walk, currentCenter, current)); + } + + private int MoveOnDirection(CmdResult r, McClient handler, Direction direction, bool takeRisk) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location goal = Movement.Move(handler.GetCurrentLocation(), direction); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), handler.GetCurrentLocation(), goal)) + return r.SetAndReturn(Status.FailChunkNotLoad, string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z)); + + if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction)) { - if (args.Count >= 2) - Settings.InternalConfig.GravityEnabled = (args[1] == "on"); - if (Settings.InternalConfig.GravityEnabled) - return Translations.cmd_move_gravity_enabled; - else return Translations.cmd_move_gravity_disabled; + if (handler.MoveTo(goal, allowUnsafe: takeRisk)) + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_moving, direction.ToString())); + else + return r.SetAndReturn(Status.Fail, takeRisk ? Translations.cmd_move_dir_fail : Translations.cmd_move_suggestforce); } - else if (handler.GetTerrainEnabled()) + else { - if (args.Count == 1) - { - Direction direction; - switch (args[0]) - { - case "up": direction = Direction.Up; break; - case "down": direction = Direction.Down; break; - case "east": direction = Direction.East; break; - case "west": direction = Direction.West; break; - case "north": direction = Direction.North; break; - case "south": direction = Direction.South; break; - case "center": - Location current = handler.GetCurrentLocation(); - Location currentCenter = new(Math.Floor(current.X) + 0.5, current.Y, Math.Floor(current.Z) + 0.5); - handler.MoveTo(currentCenter, allowDirectTeleport: true); - return string.Format(Translations.cmd_move_walk, currentCenter, current); - case "get": return handler.GetCurrentLocation().ToString(); - default: return string.Format(Translations.cmd_look_unknown, args[0]); - } - - Location goal = Movement.Move(handler.GetCurrentLocation(), direction); - - if (!Movement.CheckChunkLoading(handler.GetWorld(), handler.GetCurrentLocation(), goal)) - return string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z); - - if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction)) - { - if (handler.MoveTo(goal, allowUnsafe: takeRisk)) - return string.Format(Translations.cmd_move_moving, args[0]); - else - return takeRisk ? Translations.cmd_move_dir_fail : Translations.cmd_move_suggestforce; - } - else return Translations.cmd_move_dir_fail; - } - else if (args.Count == 3) - { - try - { - Location current = handler.GetCurrentLocation(), currentCenter = current.ToCenter(); - Location goal = Location.Parse(current, args[0], args[1], args[2]); - - if (!Movement.CheckChunkLoading(handler.GetWorld(), current, goal)) - return string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z); - - if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal)) - { - if (current.ToFloor() == goal.ToFloor()) - handler.MoveTo(goal, allowDirectTeleport: true); - else if (!handler.MoveTo(goal, allowUnsafe: takeRisk)) - return takeRisk ? string.Format(Translations.cmd_move_fail, goal) : string.Format(Translations.cmd_move_suggestforce, goal); - return string.Format(Translations.cmd_move_walk, goal, current); - } - else - return string.Format(Translations.cmd_move_suggestforce, goal); - } - catch (FormatException) { return GetCmdDescTranslated(); } - } - else return GetCmdDescTranslated(); + return r.SetAndReturn(Status.Fail, Translations.cmd_move_dir_fail); + } + } + + private int MoveToLocation(CmdResult r, McClient handler, Location goal, bool takeRisk) + { + if (!handler.GetTerrainEnabled()) + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(), currentCenter = current.ToCenter(); + goal.ToAbsolute(current); + + if (!Movement.CheckChunkLoading(handler.GetWorld(), current, goal)) + return r.SetAndReturn(Status.FailChunkNotLoad, string.Format(Translations.cmd_move_chunk_not_loaded, goal.X, goal.Y, goal.Z)); + + if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal)) + { + if (current.ToFloor() == goal.ToFloor()) + handler.MoveTo(goal, allowDirectTeleport: true); + else if (!handler.MoveTo(goal, allowUnsafe: takeRisk)) + return r.SetAndReturn(Status.Fail, takeRisk ? string.Format(Translations.cmd_move_fail, goal) : string.Format(Translations.cmd_move_suggestforce, goal)); + return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_walk, goal, current)); + } + else + { + return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_move_suggestforce, goal)); } - else return Translations.extra_terrainandmovement_required; } } } diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs index b92b4fcb..c2ef3081 100644 --- a/MinecraftClient/Commands/Reco.cs +++ b/MinecraftClient/Commands/Reco.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,22 +11,58 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reco [account]"; } } public override string CmdDesc { get { return Translations.cmd_reco_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoReconnect(r.Source, string.Empty)) + .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) + .Executes(r => DoReconnect(r.Source, Arguments.GetString(r, "AccountNick")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient? handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoReconnect(CmdResult r, string account) + { + if (string.IsNullOrWhiteSpace(account)) + { + account = account.Trim(); + if (!Settings.Config.Main.Advanced.SetAccount(account)) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_connect_unknown, account)); + } + Program.Restart(keepAccountAndServerSettings: true); + return r.SetAndReturn(CmdResult.Status.Done); + } + + internal static string DoReconnect(string command) { string[] args = GetArgs(command); if (args.Length > 0) { - if (!Settings.Config.Main.Advanced.SetAccount(args[0])) + string account = args[0].Trim(); + if (!Settings.Config.Main.Advanced.SetAccount(account)) { - return string.Format(Translations.cmd_connect_unknown, args[0]); + return string.Format(Translations.cmd_connect_unknown, account); } } Program.Restart(keepAccountAndServerSettings: true); - return ""; + return String.Empty; } } } diff --git a/MinecraftClient/Commands/Reload.cs b/MinecraftClient/Commands/Reload.cs index 2409827e..6ab4cfaf 100644 --- a/MinecraftClient/Commands/Reload.cs +++ b/MinecraftClient/Commands/Reload.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,11 +10,32 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reload"; } } public override string CmdDesc { get { return Translations.cmd_reload_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoReload(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoReload(CmdResult r, McClient handler) { handler.Log.Info(Translations.cmd_reload_started); handler.ReloadSettings(); @@ -22,7 +44,7 @@ namespace MinecraftClient.Commands handler.Log.Warn(Translations.cmd_reload_warning3); handler.Log.Warn(Translations.cmd_reload_warning4); - return Translations.cmd_reload_finished; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_reload_finished); } } } diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs index 2eedc088..d9bfbcbf 100644 --- a/MinecraftClient/Commands/Respawn.cs +++ b/MinecraftClient/Commands/Respawn.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,14 +10,35 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "respawn"; } } public override string CmdDesc { get { return Translations.cmd_respawn_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoRespawn(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoRespawn(CmdResult r, McClient handler) { handler.SendRespawnPacket(); - return Translations.cmd_respawn_done; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_respawn_done); } } } diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index 239f7737..f7f850f2 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,18 +11,36 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "script "; } } public override string CmdDesc { get { return Translations.cmd_script_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Script", Arguments.GreedyString()) + .Executes(r => DoExecuteScript(r.Source, handler, Arguments.GetString(r, "Script"), null))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - handler.BotLoad(new ChatBots.Script(GetArg(command), null, localVars)); - return ""; - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoExecuteScript(CmdResult r, McClient handler, string command, Dictionary? localVars) + { + handler.BotLoad(new ChatBots.Script(command.Trim(), null, localVars)); + return r.SetAndReturn(CmdResult.Status.Done); } } } diff --git a/MinecraftClient/Commands/Send.cs b/MinecraftClient/Commands/Send.cs index 2e868904..d17bc3b5 100644 --- a/MinecraftClient/Commands/Send.cs +++ b/MinecraftClient/Commands/Send.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,18 +10,34 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "send "; } } public override string CmdDesc { get { return Translations.cmd_send_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("any", Arguments.GreedyString()) + .Executes(r => DoSendText(r.Source, handler, Arguments.GetString(r, "any")))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - handler.SendText(GetArg(command)); - return ""; - } - else return GetCmdDescTranslated(); +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoSendText(CmdResult r, McClient handler, string command) + { + handler.SendText(command); + return r.SetAndReturn(CmdResult.Status.Done); } } } diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs index 09db64e7..3bf6d86f 100644 --- a/MinecraftClient/Commands/Set.cs +++ b/MinecraftClient/Commands/Set.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -9,27 +10,50 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "set varname=value"; } } public override string CmdDesc { get { return Translations.cmd_set_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Expression", Arguments.GreedyString()) + .Executes(r => DoSetVar(r.Source, Arguments.GetString(r, "Expression")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] temp = GetArg(command).Split('='); - if (temp.Length > 1) +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoSetVar(CmdResult r, string command) + { + string[] temp = command.Trim().Split('='); + if (temp.Length > 1) + { + if (Settings.Config.AppVar.SetVar(temp[0], command[(temp[0].Length + 1)..])) { - if (Settings.Config.AppVar.SetVar(temp[0], GetArg(command).Substring(temp[0].Length + 1))) - return ""; //Success - else - return Translations.cmd_set_format; + return r.SetAndReturn(CmdResult.Status.Done); //Success } else - return GetCmdDescTranslated(); + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_set_format); + } } else - return GetCmdDescTranslated(); + { + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_set_format); + } } } } diff --git a/MinecraftClient/Commands/SetRnd.cs b/MinecraftClient/Commands/SetRnd.cs index d6f23b7c..73e8673b 100644 --- a/MinecraftClient/Commands/SetRnd.cs +++ b/MinecraftClient/Commands/SetRnd.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -11,65 +13,61 @@ namespace MinecraftClient.Commands public override string CmdDesc { get { return Translations.cmd_setrnd_desc; } } private static readonly Random rand = new(); - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + .Then(l => l.Literal("range") + .Executes(r => GetUsage(r.Source, "range"))) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("VarName", Arguments.String()) + .Then(l => l.Argument("Min", Arguments.Long()) + .Then(l => l.Literal("to") + .Then(l => l.Argument("Max", Arguments.Long()) + .Executes(r => DoSetRnd(r.Source, Arguments.GetString(r, "VarName"), Arguments.GetLong(r, "Min"), Arguments.GetLong(r, "Max")))))) + .Then(l => l.Argument("Expression", Arguments.GreedyString()) + .Executes(r => DoSetRnd(r.Source, Arguments.GetString(r, "VarName"), Arguments.GetString(r, "Expression"))))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (HasArg(command)) + return r.SetAndReturn(cmd switch { - string[] args = GetArg(command).Split(' '); +#pragma warning disable format // @formatter:off + "range" => GetCmdDescTranslated(), + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } - if (args.Length > 1) - { - // detect "to" keyword in string - if (args.Length == 2 && args[1].Contains("to")) - { - int num1; - int num2; + private int DoSetRnd(CmdResult r, string var, string argString) + { + // process all arguments similar to regular terminals with quotes and escaping + List values = ParseCommandLine(argString); - // try to extract the two numbers from the string - try - { - num1 = Convert.ToInt32(args[1][..args[1].IndexOf('t')]); - num2 = Convert.ToInt32(args[1].Substring(args[1].IndexOf('o') + 1, args[1].Length - 1 - args[1].IndexOf('o'))); - } - catch (Exception) - { - return Translations.cmd_setrndnum_format; - } + // create a variable or set it to one of the values + if (values.Count > 0 && Settings.Config.AppVar.SetVar(var, values[rand.Next(0, values.Count)])) + return r.SetAndReturn(CmdResult.Status.Done, string.Format("Set %{0}% to {1}.", var, Settings.Config.AppVar.GetVar(var))); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_setrndstr_format); + } - // switch the values if they were entered in the wrong way - if (num2 < num1) - (num2, num1) = (num1, num2); + private int DoSetRnd(CmdResult r, string var, long min, long max) + { + // switch the values if they were entered in the wrong way + if (max < min) + (max, min) = (min, max); - // create a variable or set it to num1 <= varlue < num2 - if (Settings.Config.AppVar.SetVar(args[0], rand.Next(num1, num2))) - { - return string.Format("Set %{0}% to {1}.", args[0], Settings.Config.AppVar.GetVar(args[0])); //Success - } - else return Translations.cmd_setrndnum_format; - } - else - { - // extract all arguments of the command - string argString = command[(8 + command.Split(' ')[1].Length)..]; - - // process all arguments similar to regular terminals with quotes and escaping - List values = ParseCommandLine(argString); - - // create a variable or set it to one of the values - 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.Config.AppVar.GetVar(args[0])); //Success - } - else return Translations.cmd_setrndstr_format; - } - } - else return GetCmdDescTranslated(); - } - else return GetCmdDescTranslated(); + // create a variable or set it to num1 <= varlue < num2 + if (Settings.Config.AppVar.SetVar(var, rand.NextInt64(min, max))) + return r.SetAndReturn(CmdResult.Status.Fail, string.Format("Set %{0}% to {1}.", var, Settings.Config.AppVar.GetVar(var))); + else + return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_setrndstr_format); } } } diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index eac0a7cd..d21bea8a 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -1,34 +1,62 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { public class Sneak : Command { private bool sneaking = false; - public override string CmdName { get { return "Sneak"; } } - public override string CmdUsage { get { return "Sneak"; } } + public override string CmdName { get { return "sneak"; } } + public override string CmdUsage { get { return "sneak"; } } public override string CmdDesc { get { return Translations.cmd_sneak_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoSneak(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoSneak(CmdResult r, McClient handler) { if (sneaking) { var result = handler.SendEntityAction(Protocol.EntityActionType.StopSneaking); if (result) sneaking = false; - return result ? Translations.cmd_sneak_off : Translations.general_fail; + if (result) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_off); + else + return r.SetAndReturn(CmdResult.Status.Fail); } else { var result = handler.SendEntityAction(Protocol.EntityActionType.StartSneaking); if (result) sneaking = true; - return result ? Translations.cmd_sneak_on : Translations.general_fail; + if (result) + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_on); + else + return r.SetAndReturn(CmdResult.Status.Fail); } } } diff --git a/MinecraftClient/Commands/Tps.cs b/MinecraftClient/Commands/Tps.cs index 029fe22d..92fb0924 100644 --- a/MinecraftClient/Commands/Tps.cs +++ b/MinecraftClient/Commands/Tps.cs @@ -1,6 +1,7 @@ using System; -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; namespace MinecraftClient.Commands { @@ -10,11 +11,32 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "tps"; } } public override string CmdDesc { get { return Translations.cmd_tps_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoLogTps(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoLogTps(CmdResult r, McClient handler) { var tps = Math.Round(handler.GetServerTPS(), 2); string color; @@ -22,9 +44,9 @@ namespace MinecraftClient.Commands color = "§c"; // Red else if (tps < 15) color = "§e"; // Yellow - else + else color = "§a"; // Green - return Translations.cmd_tps_current + ": " + color + tps; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_tps_current + ": " + color + tps); } } } diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 52ea97c3..41f056da 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -9,18 +11,38 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useitem"; } } public override string CmdDesc { get { return Translations.cmd_useitem_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Executes(r => DoUseItem(r.Source, handler)) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) { - if (handler.GetInventoryEnabled()) + return r.SetAndReturn(cmd switch { - handler.UseItemOnHand(); - return Translations.cmd_useitem_use; - } - else return Translations.extra_inventory_required; +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int DoUseItem(CmdResult r, McClient handler) + { + if (!handler.GetInventoryEnabled()) + return r.SetAndReturn(Status.FailNeedInventory); + + handler.UseItemOnHand(); + return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use); } } } diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 10125f43..784185ee 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; using Brigadier.NET; +using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; +using static MinecraftClient.CommandHandler.CmdResult; namespace MinecraftClient.Commands { @@ -10,29 +12,42 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useblock "; } } public override string CmdDesc { get { return Translations.cmd_useblock_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + dispatcher.Register(l => l.Literal("help") + .Then(l => l.Literal(CmdName) + .Executes(r => GetUsage(r.Source, string.Empty)) + ) + ); + + dispatcher.Register(l => l.Literal(CmdName) + .Then(l => l.Argument("Location", MccArguments.Location()) + .Executes(r => UseBlockAtLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Then(l => l.Literal("_help") + .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) + ); } - public override string Run(McClient handler, string command, Dictionary? localVars) + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { +#pragma warning disable format // @formatter:off + _ => GetCmdDescTranslated(), +#pragma warning restore format // @formatter:on + }); + } + + private int UseBlockAtLocation(CmdResult r, McClient handler, Location block) { if (!handler.GetTerrainEnabled()) - return Translations.extra_terrainandmovement_required; - else if (HasArg(command)) - { - string[] args = GetArgs(command); - if (args.Length >= 3) - { - 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 string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"); - } - else - return GetCmdDescTranslated(); - } - else - return GetCmdDescTranslated(); + return r.SetAndReturn(Status.FailNeedTerrain); + + Location current = handler.GetCurrentLocation(); + block = block.ToAbsolute(current).ToFloor(); + Location blockCenter = block.ToCenter(); + bool res = handler.PlaceBlock(block, Direction.Down); + return r.SetAndReturn(string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"), res); } } } diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs index 022ccaf4..5561e003 100644 --- a/MinecraftClient/ConsoleIO.cs +++ b/MinecraftClient/ConsoleIO.cs @@ -1,6 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using FuzzySharp; +using MinecraftClient.CommandHandler; +using MinecraftClient.Scripting; +using static MinecraftClient.Settings; namespace MinecraftClient { @@ -175,16 +182,151 @@ namespace MinecraftClient #endregion - public static void AutocompleteHandler(object? sender, ConsoleKey e) + + internal static bool AutoCompleteDone = false; + internal static string[] AutoCompleteResult = Array.Empty(); + + private static HashSet Commands = new(); + private static string[] CommandsFromAutoComplete = Array.Empty(); + private static string[] CommandsFromDeclareCommands = Array.Empty(); + + private static Task _latestTask = Task.CompletedTask; + private static CancellationTokenSource? _cancellationTokenSource; + + private static void MccAutocompleteHandler(ConsoleInteractive.ConsoleReader.Buffer buffer) { - if (e != ConsoleKey.Tab) return; - - if (autocomplete_engine == null) + string fullCommand = buffer.Text; + if (string.IsNullOrEmpty(fullCommand)) + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); return; + } - var buffer = ConsoleInteractive.ConsoleReader.GetBufferContent(); - ConsoleIO.WriteLine("AutoComplete " + buffer); - autocomplete_engine.AutoComplete(buffer.Text[..buffer.CursorPosition]); + var InternalCmdChar = Config.Main.Advanced.InternalCmdChar; + if (InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none || fullCommand[0] == InternalCmdChar.ToChar()) + { + int offset = InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none ? 0 : 1; + if (buffer.CursorPosition - offset < 0) + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + return; + } + _cancellationTokenSource?.Cancel(); + using var cts = new CancellationTokenSource(); + _cancellationTokenSource = cts; + var previousTask = _latestTask; + var newTask = new Task(async () => + { + string command = fullCommand[offset..]; + if (command.Length == 0) + { + var childs = McClient.dispatcher.GetRoot().Children; + int index = 0; + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[childs.Count + Commands.Count + 1]; + + sugList[index++] = new("/"); + foreach (var child in childs) + sugList[index++] = new(child.Name); + foreach (var cmd in Commands) + sugList[index++] = new(cmd); + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset)); + } + else if (command.Length > 0 && command[0] == '/' && !command.Contains(' ')) + { + var sorted = Process.ExtractSorted(command[1..], Commands); + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[sorted.Count()]; + + int index = 0; + foreach (var sug in sorted) + sugList[index++] = new(sug.Value); + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset + command.Length)); + } + else + { + var parse = McClient.dispatcher.Parse(command, CmdResult.Empty); + + var suggestion = await McClient.dispatcher.GetCompletionSuggestions(parse, buffer.CursorPosition - offset); + + int sugLen = suggestion.List.Count; + if (sugLen == 0) + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + return; + } + + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[sugLen]; + if (cts.IsCancellationRequested) + return; + + Tuple range = new(suggestion.Range.Start + offset, suggestion.Range.End + offset); + var sorted = Process.ExtractSorted(fullCommand[range.Item1..range.Item2], suggestion.List.Select(_ => _.Text).ToList()); + if (cts.IsCancellationRequested) + return; + + int index = 0; + foreach (var sug in sorted) + sugList[index++] = new(sug.Value); + + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, range); + } + }, cts.Token); + _latestTask = newTask; + try { newTask.Start(); } catch { } + if (_cancellationTokenSource == cts) _cancellationTokenSource = null; + } + else + { + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + return; + } + } + + public static void AutocompleteHandler(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer) + { + MccAutocompleteHandler(buffer); + } + + public static void CancelAutocomplete() + { + _cancellationTokenSource?.Cancel(); + _latestTask = Task.CompletedTask; + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); + + AutoCompleteDone = false; + AutoCompleteResult = Array.Empty(); + CommandsFromAutoComplete = Array.Empty(); + CommandsFromDeclareCommands = Array.Empty(); + } + + private static void MergeCommands() + { + Commands.Clear(); + foreach (string cmd in CommandsFromAutoComplete) + Commands.Add('/' + cmd); + foreach (string cmd in CommandsFromDeclareCommands) + Commands.Add('/' + cmd); + } + + public static void OnAutoCompleteDone(int transactionId, string[] result) + { + AutoCompleteResult = result; + if (transactionId == 0) + { + CommandsFromAutoComplete = result; + MergeCommands(); + } + AutoCompleteDone = true; + } + + public static void OnDeclareMinecraftCommand(string[] rootCommands) + { + CommandsFromDeclareCommands = rootCommands; + MergeCommands(); + } + + public static void InitAutocomplete() + { + autocomplete_engine!.AutoComplete("/"); } } @@ -199,6 +341,6 @@ namespace MinecraftClient /// /// Text behind the cursor, e.g. "my input comm" /// List of auto-complete words, e.g. ["command", "comment"] - IEnumerable AutoComplete(string BehindCursor); + int AutoComplete(string BehindCursor); } } diff --git a/MinecraftClient/Inventory/EnchantmentMapping.cs b/MinecraftClient/Inventory/EnchantmentMapping.cs index d6ffba32..badadb99 100644 --- a/MinecraftClient/Inventory/EnchantmentMapping.cs +++ b/MinecraftClient/Inventory/EnchantmentMapping.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Inventory { @@ -158,7 +159,7 @@ namespace MinecraftClient.Inventory public static string GetEnchantmentName(Enchantment enchantment) { - string? trans = Protocol.ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase()); + string? trans = ChatParser.TranslateString("enchantment.minecraft." + enchantment.ToString().ToUnderscoreCase()); if (string.IsNullOrEmpty(trans)) return "Unknown Enchantment with ID: " + ((short)enchantment) + " (Probably not named in the code yet)"; else diff --git a/MinecraftClient/Inventory/Item.cs b/MinecraftClient/Inventory/Item.cs index 28a5754e..d0c523b3 100644 --- a/MinecraftClient/Inventory/Item.cs +++ b/MinecraftClient/Inventory/Item.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Inventory { @@ -64,7 +65,7 @@ namespace MinecraftClient.Inventory { string? displayName = displayProperties["Name"] as string; if (!String.IsNullOrEmpty(displayName)) - return MinecraftClient.Protocol.ChatParser.ParseText(displayProperties["Name"].ToString() ?? string.Empty); + return ChatParser.ParseText(displayProperties["Name"].ToString() ?? string.Empty); } } return null; @@ -85,7 +86,7 @@ namespace MinecraftClient.Inventory { object[] displayName = (object[])displayProperties["Lore"]; lores.AddRange(from string st in displayName - let str = MinecraftClient.Protocol.ChatParser.ParseText(st.ToString()) + let str = ChatParser.ParseText(st.ToString()) select str); return lores.ToArray(); } @@ -117,10 +118,10 @@ namespace MinecraftClient.Inventory { string type_str = type.ToString(); string type_renamed = type_str.ToUnderscoreCase(); - string? res1 = Protocol.ChatParser.TranslateString("item.minecraft." + type_renamed); + string? res1 = ChatParser.TranslateString("item.minecraft." + type_renamed); if (!string.IsNullOrEmpty(res1)) return res1; - string? res2 = Protocol.ChatParser.TranslateString("block.minecraft." + type_renamed); + string? res2 = ChatParser.TranslateString("block.minecraft." + type_renamed); if (!string.IsNullOrEmpty(res2)) return res2; return type_str; @@ -145,8 +146,8 @@ namespace MinecraftClient.Inventory short level = (short)enchantment["lvl"]; string id = ((string)enchantment["id"]).Replace(':', '.'); sb.AppendFormat(" | {0} {1}", - Protocol.ChatParser.TranslateString("enchantment." + id) ?? id, - Protocol.ChatParser.TranslateString("enchantment.level." + level) ?? level.ToString()); + ChatParser.TranslateString("enchantment." + id) ?? id, + ChatParser.TranslateString("enchantment.level." + level) ?? level.ToString()); } } } diff --git a/MinecraftClient/Inventory/ItemMovingHelper.cs b/MinecraftClient/Inventory/ItemMovingHelper.cs index 07b1e686..da9a0097 100644 --- a/MinecraftClient/Inventory/ItemMovingHelper.cs +++ b/MinecraftClient/Inventory/ItemMovingHelper.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using MinecraftClient.Scripting; namespace MinecraftClient.Inventory { diff --git a/MinecraftClient/Inventory/VillagerInfo.cs b/MinecraftClient/Inventory/VillagerInfo.cs index 28f793af..c93781e8 100644 --- a/MinecraftClient/Inventory/VillagerInfo.cs +++ b/MinecraftClient/Inventory/VillagerInfo.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.Inventory { /// /// Properties of a villager diff --git a/MinecraftClient/Json.cs b/MinecraftClient/Json.cs index e77e32bf..be70b48e 100644 --- a/MinecraftClient/Json.cs +++ b/MinecraftClient/Json.cs @@ -103,7 +103,7 @@ namespace MinecraftClient && IsHex(toparse[cursorpos + 5])) { //"abc\u0123abc" => "0123" => 0123 => Unicode char n°0123 => Add char to string - data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4), + data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4), System.Globalization.NumberStyles.HexNumber)); cursorpos += 6; continue; } diff --git a/MinecraftClient/Logger/FileLogLogger.cs b/MinecraftClient/Logger/FileLogLogger.cs index 8418ec6c..9614a5c4 100644 --- a/MinecraftClient/Logger/FileLogLogger.cs +++ b/MinecraftClient/Logger/FileLogLogger.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using MinecraftClient.Scripting; namespace MinecraftClient.Logger { diff --git a/MinecraftClient/Mapping/Block.cs b/MinecraftClient/Mapping/Block.cs index 065699a7..ffbe499c 100644 --- a/MinecraftClient/Mapping/Block.cs +++ b/MinecraftClient/Mapping/Block.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using MinecraftClient.Mapping.BlockPalettes; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Mapping { @@ -115,7 +116,7 @@ namespace MinecraftClient.Mapping public string GetTypeString() { string typeStr = Type.ToString(); - string? trans = Protocol.ChatParser.TranslateString("block.minecraft." + typeStr.ToUnderscoreCase()); + string? trans = ChatParser.TranslateString("block.minecraft." + typeStr.ToUnderscoreCase()); return string.IsNullOrEmpty(trans) ? typeStr : trans; } diff --git a/MinecraftClient/Mapping/Dimension.cs b/MinecraftClient/Mapping/Dimension.cs index 3aea6744..fe52b0e0 100644 --- a/MinecraftClient/Mapping/Dimension.cs +++ b/MinecraftClient/Mapping/Dimension.cs @@ -139,9 +139,9 @@ namespace MinecraftClient.Mapping try { var monsterSpawnLightLevelObj = nbt["monster_spawn_light_level"]; - try - { - monsterSpawnMinLightLevel = monsterSpawnMaxLightLevel = Convert.ToInt32(monsterSpawnLightLevelObj); + try + { + monsterSpawnMinLightLevel = monsterSpawnMaxLightLevel = Convert.ToInt32(monsterSpawnLightLevelObj); } catch (Exception) { diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index fa7646b7..8d11ba27 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using MinecraftClient.Inventory; +using MinecraftClient.Protocol.Message; namespace MinecraftClient.Mapping { @@ -158,7 +159,7 @@ namespace MinecraftClient.Mapping public string GetTypeString() { string typeStr = Type.ToString(); - string? trans = Protocol.ChatParser.TranslateString("entity.minecraft." + typeStr.ToUnderscoreCase()); + string? trans = ChatParser.TranslateString("entity.minecraft." + typeStr.ToUnderscoreCase()); return string.IsNullOrEmpty(trans) ? typeStr : trans; } } diff --git a/MinecraftClient/Mapping/Location.cs b/MinecraftClient/Mapping/Location.cs index 5094648f..4a6a7320 100644 --- a/MinecraftClient/Mapping/Location.cs +++ b/MinecraftClient/Mapping/Location.cs @@ -26,6 +26,15 @@ namespace MinecraftClient.Mapping /// public double Z; + /// + /// Identifies whether the coordinates are absolute or relative. + /// true for relative coordinates, false for absolute coordinates. + /// X-axis: ((Status & (1 << 0)) > 0) + /// Y-axis: ((Status & (1 << 1)) > 0) + /// Z-axis: ((Status & (1 << 2)) > 0) + /// + public byte Status; + /// /// Create a new location /// @@ -34,6 +43,18 @@ namespace MinecraftClient.Mapping X = x; Y = y; Z = z; + Status = 0; + } + + /// + /// Create a new location + /// + public Location(double x, double y, double z, byte status) + { + X = x; + Y = y; + Z = z; + Status = status; } /// @@ -44,6 +65,7 @@ namespace MinecraftClient.Mapping X = loc.X; Y = loc.Y; Z = loc.Z; + Status = loc.Status; } /// @@ -60,6 +82,19 @@ namespace MinecraftClient.Mapping X = chunkX * Chunk.SizeX + blockX; Y = blockY; Z = chunkZ * Chunk.SizeZ + blockZ; + Status = 0; + } + + public Location ToAbsolute(Location based) + { + if ((Status & (1 << 0)) > 0) + X += based.X; + if ((Status & (1 << 1)) > 0) + Y += based.Y; + if ((Status & (1 << 2)) > 0) + Z += based.Z; + Status = 0; + return this; } /// diff --git a/MinecraftClient/Mapping/RaycastHelper.cs b/MinecraftClient/Mapping/RaycastHelper.cs index a1afecef..483b0b86 100644 --- a/MinecraftClient/Mapping/RaycastHelper.cs +++ b/MinecraftClient/Mapping/RaycastHelper.cs @@ -36,7 +36,7 @@ namespace MinecraftClient.Mapping { if (start == end) return new(false, Location.Zero, Block.Air); - + double start_x = MathHelper.Lerp(-1.0E-7, start.X, end.X); double start_y = MathHelper.Lerp(-1.0E-7, start.Y, end.Y); double start_z = MathHelper.Lerp(-1.0E-7, start.Z, end.Z); @@ -87,7 +87,7 @@ namespace MinecraftClient.Mapping res_location.Z += dz_sign; z_frac += z_step; } - + block = CheckRaycastResult(world, res_location, includeFluids); if (block.Type != Material.Air) return new(true, res_location, block); diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index 97792cfe..0b2e02f9 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -153,7 +153,7 @@ namespace MinecraftClient.Mapping /// Block type /// Search radius - larger is slower: O^3 complexity /// Block matching the specified block type - public List FindBlock(Location from, Material block, int radius) + public List FindBlock(Location from, Material block, double radius) { return FindBlock(from, block, radius, radius, radius); } @@ -167,7 +167,7 @@ namespace MinecraftClient.Mapping /// Search radius on the Y axis /// Search radius on the Z axis /// Block matching the specified block type - public List FindBlock(Location from, Material block, int radiusx, int radiusy, int radiusz) + public List FindBlock(Location from, Material block, double radiusx, double radiusy, double radiusz) { Location minPoint = new Location(from.X - radiusx, from.Y - radiusy, from.Z - radiusz); Location maxPoint = new Location(from.X + radiusx, from.Y + radiusy, from.Z + radiusz); diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index f33e6456..a33f0fae 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2,22 +2,23 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; -using System.Security.Cryptography.Pkcs; using System.Text; using System.Threading; using Brigadier.NET; using Brigadier.NET.Exceptions; using MinecraftClient.ChatBots; -using MinecraftClient.Commands; +using MinecraftClient.CommandHandler; +using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; using MinecraftClient.Protocol; using MinecraftClient.Protocol.Handlers.Forge; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using MinecraftClient.Scripting; using static MinecraftClient.Settings; namespace MinecraftClient @@ -29,10 +30,7 @@ namespace MinecraftClient { public static int ReconnectionAttemptsLeft = 0; - public static readonly CommandSource cmd_source = new(); - public static readonly CommandDispatcher dispatcher = new(); - //private static readonly List cmd_names = new(); - //private static readonly Dictionary cmds = new(); + public static CommandDispatcher dispatcher = new(); private readonly Dictionary onlinePlayers = new(); private static bool commandsLoaded = false; @@ -152,6 +150,8 @@ namespace MinecraftClient /// ForgeInfo item stating that Forge is enabled public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo) { + dispatcher = new(); + CmdResult.client = this; terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; entityHandlingEnabled = Config.Main.Advanced.EntityHandling; @@ -206,7 +206,7 @@ namespace MinecraftClient cmdprompt = new CancellationTokenSource(); ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt); ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput += ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.OnInputChange += ConsoleIO.AutocompleteHandler; } else { @@ -230,7 +230,7 @@ namespace MinecraftClient return; - Retry: + Retry: if (timeoutdetector != null) { timeoutdetector.Item2.Cancel(); @@ -247,7 +247,7 @@ namespace MinecraftClient { ConsoleInteractive.ConsoleReader.StopReadThread(); ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput -= ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; Program.HandleFailure(); } @@ -456,6 +456,8 @@ namespace MinecraftClient /// public void OnConnectionLost(ChatBot.DisconnectReason reason, string message) { + ConsoleIO.CancelAutocomplete(); + handler.Dispose(); world.Clear(); @@ -514,7 +516,7 @@ namespace MinecraftClient { ConsoleInteractive.ConsoleReader.StopReadThread(); ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; - ConsoleInteractive.ConsoleReader.OnKeyInput -= ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; Program.HandleFailure(); } } @@ -553,7 +555,9 @@ namespace MinecraftClient switch (command[0].ToLower()) { case "autocomplete": - if (command.Length > 1) { ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00 + handler.AutoComplete(command[1])); } + int id = handler.AutoComplete(command[1]); + while (!ConsoleIO.AutoCompleteDone) { Thread.Sleep(100); } + if (command.Length > 1) { ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00 + ConsoleIO.AutoCompleteResult); } else ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00); break; } @@ -561,70 +565,44 @@ namespace MinecraftClient else { text = text.Trim(); - if (text.Length > 0) + + if (text.Length > 1 + && Config.Main.Advanced.InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none + && text[0] == '/') { - if (Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' || text[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) + SendText(text); + } + else if (text.Length > 2 + && Config.Main.Advanced.InternalCmdChar != MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none + && text[0] == Config.Main.Advanced.InternalCmdChar.ToChar() + && text[1] == '/') + { + SendText(text[1..]); + } + else if (text.Length > 0) + { + if (Config.Main.Advanced.InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none + || text[0] == Config.Main.Advanced.InternalCmdChar.ToChar()) { - string? response_msg = ""; + CmdResult result = new(); 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() == '/') + if (!PerformInternalCommand(Config.AppVar.ExpandVars(command), ref result, Settings.Config.AppVar.GetVariables()) && Config.Main.Advanced.InternalCmdChar.ToChar() == '/') { SendText(text); } - else if (!String.IsNullOrEmpty(response_msg)) + else if (result.status != CmdResult.Status.NotRun && (result.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(result.result))) { - Log.Info(response_msg); + Log.Info(result); } } - else SendText(text); + else + { + SendText(text); + } } } } - /// - /// Register a custom console command - /// - /// Name of the command - /// Description/usage of the command - /// Method for handling the command - /// True if successfully registered - public bool RegisterCommand(string cmdName, string cmdDesc, string cmdUsage, ChatBot.CommandRunner callback) - { - // if (cmds.ContainsKey(cmdName.ToLower())) - // { - // return false; - // } - // else - // { - // Command cmd = new ChatBot.ChatBotCommand(cmdName, cmdDesc, cmdUsage, callback); - // cmds.Add(cmdName.ToLower(), cmd); - // cmd_names.Add(cmdName.ToLower()); - // return true; - // } - return true; - } - - /// - /// Unregister a console command - /// - /// - /// There is no check for the command is registered by above method or is embedded command. - /// Which mean this can unload any command - /// - /// The name of command to be unregistered - /// - public bool UnregisterCommand(string cmdName) - { - // if (cmds.ContainsKey(cmdName.ToLower())) - // { - // cmds.Remove(cmdName.ToLower()); - // cmd_names.Remove(cmdName.ToLower()); - // return true; - // } - // else return false; - return true; - } - /// /// Perform an internal MCC command (not a server command, use SendText() instead for that!) /// @@ -632,82 +610,55 @@ namespace MinecraftClient /// May contain a confirmation or error message after processing the command, or "" otherwise. /// Local variables passed along with the command /// TRUE if the command was indeed an internal MCC command - public bool PerformInternalCommand(string command, ref string? response_msg, Dictionary? localVars = null) + public bool PerformInternalCommand(string command, ref CmdResult result, Dictionary? localVars = null) { /* Process the provided command */ - ParseResults parse; + ParseResults parse; try { - parse = dispatcher.Parse(command, cmd_source); + parse = dispatcher.Parse(command, result); } catch (Exception e) { - Log.Error(e.GetFullMessage()); - return true; + Log.Debug("Parse fail: " + e.GetFullMessage()); + return false; } try { - int res = dispatcher.Execute(parse); - Log.Info("res = " + res); + dispatcher.Execute(parse); + + foreach (ChatBot bot in bots.ToArray()) + { + try + { + bot.OnInternalCommand(command, string.Join(" ", Command.GetArgs(command)), result); + } + catch (Exception e) + { + if (e is not ThreadAbortException) + { + Log.Warn(string.Format(Translations.icmd_error, bot.ToString() ?? string.Empty, e.ToString())); + } + else throw; //ThreadAbortException should not be caught + } + } + return true; } catch (CommandSyntaxException e) { - Log.Warn(e.GetFullMessage()); - return true; + if (parse.Context.Nodes.Count == 0) + { + return false; + } + else + { + Log.Info("§e" + e.Message ?? e.StackTrace ?? "Incorrect argument."); + Log.Info(dispatcher.GetAllUsageString(parse.Context.Nodes[0].Node.Name, false)); + return true; + } } - catch (Exception e) - { - Log.Error(e.GetFullMessage()); - return true; - } - - //string command_name = command.Split(' ')[0].ToLower(); - //if (command_name == "help") - //{ - // if (Command.HasArg(command)) - // { - // string help_cmdname = Command.GetArgs(command)[0].ToLower(); - // if (help_cmdname == "help") - // { - // response_msg = Translations.icmd_help; - // } - // else if (cmds.ContainsKey(help_cmdname)) - // { - // response_msg = cmds[help_cmdname].GetCmdDescTranslated(); - // } - // else response_msg = string.Format(Translations.icmd_unknown, command_name); - // } - // else response_msg = string.Format(Translations.icmd_list, String.Join(", ", cmd_names.ToArray()), Config.Main.Advanced.InternalCmdChar.ToChar()); - //} - //else if (cmds.ContainsKey(command_name)) - //{ - // response_msg = cmds[command_name].Run(this, command, localVars); - - // foreach (ChatBot bot in bots.ToArray()) - // { - // try - // { - // bot.OnInternalCommand(command_name, string.Join(" ", Command.GetArgs(command)), response_msg); - // } - // catch (Exception e) - // { - // if (e is not ThreadAbortException) - // { - // Log.Warn(string.Format(Translations.icmd_error, bot.ToString() ?? string.Empty, e.ToString())); - // } - // else throw; //ThreadAbortException should not be caught - // } - // } - //} - //else - //{ - // response_msg = string.Format(Translations.icmd_unknown, command_name); - // return false; - //} - - //return true; } public void LoadCommands() @@ -725,10 +676,6 @@ namespace MinecraftClient { Command cmd = (Command)Activator.CreateInstance(type)!; cmd.RegisterCommand(this, dispatcher); - // cmds[Settings.ToLowerIfNeed(cmd.CmdName)] = cmd; - // cmd_names.Add(Settings.ToLowerIfNeed(cmd.CmdName)); - // foreach (string alias in cmd.GetCMDAliases()) - // cmds[Settings.ToLowerIfNeed(alias)] = cmd; } catch (Exception e) { @@ -863,7 +810,7 @@ namespace MinecraftClient b.SetHandler(this); bots.Add(b); if (init) - DispatchBotEvent(bot => bot.Initialize(), new ChatBot[] { b }); + DispatchBotEvent(bot => bot.Initialize(dispatcher), new ChatBot[] { b }); if (handler != null) DispatchBotEvent(bot => bot.AfterGameJoined(), new ChatBot[] { b }); } @@ -879,8 +826,9 @@ namespace MinecraftClient return; } - b.OnUnload(); - bots.RemoveAll(item => object.ReferenceEquals(item, b)); + b.OnUnload(dispatcher); + + bots.RemoveAll(item => ReferenceEquals(item, b)); // ToList is needed to avoid an InvalidOperationException from modfiying the list while it's being iterated upon. var botRegistrations = registeredBotPluginChannels.Where(entry => entry.Value.Contains(b)).ToList(); @@ -2449,6 +2397,8 @@ namespace MinecraftClient ClearInventories(); DispatchBotEvent(bot => bot.AfterGameJoined()); + + ConsoleIO.InitAutocomplete(); } /// @@ -3471,6 +3421,16 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.OnBlockChange(location, block)); } + /// + /// Called when "AutoComplete" completes. + /// + /// The number of this result. + /// All commands. + public void OnAutoCompleteDone(int transactionId, string[] result) + { + ConsoleIO.OnAutoCompleteDone(transactionId, result); + } + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index e6f2477c..f0a01aaa 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -28,20 +28,21 @@ - + - - + + + - - + + NU1701 - + diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index d8ae2250..52aaf0f4 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -14,8 +14,9 @@ using MinecraftClient.Mapping.BlockPalettes; using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Protocol; using MinecraftClient.Protocol.Handlers.Forge; -using MinecraftClient.Protocol.Keys; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; +using MinecraftClient.Scripting; using MinecraftClient.WinAPI; using Tomlet; using static MinecraftClient.Settings; @@ -573,7 +574,7 @@ namespace MinecraftClient } //Retrieve server info if version is not manually set OR if need to retrieve Forge information - if (!isRealms && (protocolversion == 0 || (Config.Main.Advanced.EnableForge == ForgeConfigType.auto) || + if (!isRealms && (protocolversion == 0 || (Config.Main.Advanced.EnableForge == ForgeConfigType.auto) || ((Config.Main.Advanced.EnableForge == ForgeConfigType.force) && !ProtocolHandler.ProtocolMayForceForge(protocolversion)))) { if (protocolversion != 0) @@ -587,9 +588,9 @@ namespace MinecraftClient } } - if (Config.Main.General.AccountType == LoginType.microsoft + if (Config.Main.General.AccountType == LoginType.microsoft && (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser) - && Config.Signature.LoginWithSecureProfile + && Config.Signature.LoginWithSecureProfile && protocolversion >= 759 /* 1.19 and above */) { // Load cached profile key from disk if necessary @@ -683,7 +684,7 @@ namespace MinecraftClient /// public static void ReloadSettings(bool keepAccountAndServerSettings = false) { - if(Settings.LoadFromFile(settingsIniPath, keepAccountAndServerSettings).Item1) + if (Settings.LoadFromFile(settingsIniPath, keepAccountAndServerSettings).Item1) ConsoleIO.WriteLine(string.Format(Translations.config_load, settingsIniPath)); } @@ -720,6 +721,7 @@ namespace MinecraftClient public static void DoExit(int exitcode = 0) { WriteBackSettings(true); + ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); ConsoleIO.WriteLineFormatted(string.Format(Translations.config_saving, settingsIniPath)); if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } @@ -807,7 +809,7 @@ namespace MinecraftClient if (command.StartsWith("reco")) { - message = new Commands.Reco().Run(null, Config.AppVar.ExpandVars(command), null); + message = Commands.Reco.DoReconnect(Config.AppVar.ExpandVars(command)); if (message == "") { exitThread = true; @@ -816,7 +818,7 @@ namespace MinecraftClient } else if (command.StartsWith("connect")) { - message = new Commands.Connect().Run(null, Config.AppVar.ExpandVars(command), null); + message = Commands.Connect.DoConnect(Config.AppVar.ExpandVars(command)); if (message == "") { exitThread = true; @@ -825,7 +827,7 @@ namespace MinecraftClient } else if (command.StartsWith("exit") || command.StartsWith("quit")) { - message = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null); + message = Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); } else if (command.StartsWith("help")) { @@ -844,7 +846,7 @@ namespace MinecraftClient } else { - _ = new Commands.Exit().Run(null, Config.AppVar.ExpandVars(command), null); + Commands.Exit.DoExit(Config.AppVar.ExpandVars(command)); } } diff --git a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs index a850dd2a..22835b61 100644 --- a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs +++ b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs @@ -54,7 +54,22 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c Nodes[i] = new(flags, childs, redirectNode, name, paser, suggestionsType); } - RootIdx = dataTypes.ReadNextVarInt(packetData); + RootIdx = dataTypes.ReadNextVarInt(packetData); + + ConsoleIO.OnDeclareMinecraftCommand(ExtractRootCommand()); + } + + private static string[] ExtractRootCommand() + { + List commands = new(); + CommandNode root = Nodes[RootIdx]; + foreach (var child in root.Clildren) + { + string? childName = Nodes[child].Name; + if (childName != null) + commands.Add(childName); + } + return commands.ToArray(); } public static List> CollectSignArguments(string command) @@ -113,7 +128,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c public string? Name; public Paser? Paser; public string? SuggestionsType; - + public CommandNode(byte Flags, int[] Clildren, diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 9db07050..1bf99fdc 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -9,10 +9,11 @@ using System.Threading; using MinecraftClient.Crypto; using MinecraftClient.Inventory; using MinecraftClient.Mapping; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using MinecraftClient.Scripting; using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Handlers @@ -24,8 +25,6 @@ namespace MinecraftClient.Protocol.Handlers class Protocol16Handler : IMinecraftCom { readonly IMinecraftComHandler handler; - private bool autocomplete_received = false; - private string autocomplete_result = ""; private bool encrypted = false; private readonly int protocolversion; private Tuple? netRead = null; @@ -193,7 +192,14 @@ namespace MinecraftClient.Protocol.Handlers if (online) { handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); } else { handler.OnPlayerLeave(FakeUUID); } break; case 0xCA: if (protocolversion >= 72) { ReadData(9); } else ReadData(3); break; - case 0xCB: autocomplete_result = ReadNextString(); autocomplete_received = true; break; + case 0xCB: + string resultString = ReadNextString(); + if (!string.IsNullOrEmpty(resultString)) + { + string[] result = resultString.Split((char)0x00); + handler.OnAutoCompleteDone(0, result); + } + break; case 0xCC: ReadNextString(); ReadData(4); break; case 0xCD: ReadData(1); break; case 0xCE: if (protocolversion > 51) { ReadNextString(); ReadNextString(); ReadData(1); } break; @@ -820,27 +826,21 @@ namespace MinecraftClient.Protocol.Handlers catch (System.IO.IOException) { return false; } } - IEnumerable IAutoComplete.AutoComplete(string BehindCursor) + int IAutoComplete.AutoComplete(string BehindCursor) { if (String.IsNullOrEmpty(BehindCursor)) - return Array.Empty(); + return -1; byte[] autocomplete = new byte[3 + (BehindCursor.Length * 2)]; autocomplete[0] = 0xCB; byte[] msglen = BitConverter.GetBytes((short)BehindCursor.Length); - Array.Reverse(msglen); msglen.CopyTo(autocomplete, 1); + Array.Reverse(msglen); + msglen.CopyTo(autocomplete, 1); byte[] msg = Encoding.BigEndianUnicode.GetBytes(BehindCursor); msg.CopyTo(autocomplete, 3); - - autocomplete_received = false; - autocomplete_result = BehindCursor; + ConsoleIO.AutoCompleteDone = false; Send(autocomplete); - - int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) - while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } - if (!String.IsNullOrEmpty(autocomplete_result) && autocomplete_received) - ConsoleIO.WriteLineFormatted("§8" + autocomplete_result.Replace((char)0x00, ' '), false); - return autocomplete_result.Split((char)0x00); + return 0; } private static byte[] ConcatBytes(params byte[][] bytes) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 5f0f855d..de33417e 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -19,10 +19,11 @@ using MinecraftClient.Mapping.EntityPalettes; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Handlers.packet.s2c; using MinecraftClient.Protocol.Handlers.PacketPalettes; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using MinecraftClient.Scripting; using static MinecraftClient.Settings; namespace MinecraftClient.Protocol.Handlers @@ -64,9 +65,7 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_19_2_Version = 760; private int compression_treshold = 0; - private bool autocomplete_received = false; private int autocomplete_transaction_id = 0; - private readonly List autocomplete_result = new(); private readonly Dictionary window_actions = new(); private bool login_phase = true; private readonly int protocolVersion; @@ -1325,6 +1324,7 @@ namespace MinecraftClient.Protocol.Handlers } break; case PacketTypesIn.TabComplete: + int old_transaction_id = autocomplete_transaction_id; if (protocolVersion >= MC_1_13_Version) { autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); @@ -1333,20 +1333,19 @@ namespace MinecraftClient.Protocol.Handlers } int autocomplete_count = dataTypes.ReadNextVarInt(packetData); - autocomplete_result.Clear(); + string[] autocomplete_result = new string[autocomplete_count]; for (int i = 0; i < autocomplete_count; i++) { - autocomplete_result.Add(dataTypes.ReadNextString(packetData)); + autocomplete_result[i] = dataTypes.ReadNextString(packetData); if (protocolVersion >= MC_1_13_Version) { - // Skip optional tooltip for each tab-complete result + // Skip optional tooltip for each tab-complete resul`t if (dataTypes.ReadNextBool(packetData)) dataTypes.SkipNextString(packetData); } } - - autocomplete_received = true; + handler.OnAutoCompleteDone(old_transaction_id, autocomplete_result); break; case PacketTypesIn.PluginMessage: String channel = dataTypes.ReadNextString(packetData); @@ -2110,18 +2109,10 @@ namespace MinecraftClient.Protocol.Handlers /// /// Text behind cursor /// Completed text - IEnumerable IAutoComplete.AutoComplete(string BehindCursor) + int IAutoComplete.AutoComplete(string BehindCursor) { - var sug = McClient.dispatcher.GetCompletionSuggestions(McClient.dispatcher.Parse(BehindCursor[1..], McClient.cmd_source)); - sug.Wait(); - foreach (var hint in sug.Result.List) - { - log.Info(hint); - } - //log.Info(McClient.dispatcher.GetSmartUsage(McClient.dispatcher.Parse(BehindCursor, McClient.cmd_source), McClient.cmd_source)); - - if (String.IsNullOrEmpty(BehindCursor)) - return Array.Empty(); + if (string.IsNullOrEmpty(BehindCursor)) + return -1; byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id); byte[] assume_command = new byte[] { 0x00 }; @@ -2134,16 +2125,14 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_13_Version) { tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, transaction_id); - tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); + tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor.Replace(' ', (char)0x00))); } else { tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, dataTypes.GetString(BehindCursor)); if (protocolVersion >= MC_1_9_Version) - { tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, assume_command); - } tabcomplete_packet = dataTypes.ConcatBytes(tabcomplete_packet, has_position); } @@ -2152,22 +2141,9 @@ namespace MinecraftClient.Protocol.Handlers { tabcomplete_packet = dataTypes.ConcatBytes(dataTypes.GetString(BehindCursor)); } - - autocomplete_received = false; - autocomplete_result.Clear(); - autocomplete_result.Add(BehindCursor); + ConsoleIO.AutoCompleteDone = false; SendPacket(PacketTypesOut.TabComplete, tabcomplete_packet); - - int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) - ThreadStart start = new(delegate - { - while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } - if (autocomplete_result.Count > 0) - ConsoleIO.WriteLineFormatted("§8" + String.Join(" ", autocomplete_result), false); - }); - Thread t1 = new(start); - t1.Start(); - return autocomplete_result; + return autocomplete_transaction_id; } /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index 382ba5c5..922496d1 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using System.Threading; using MinecraftClient.Protocol.Handlers.Forge; +using MinecraftClient.Protocol.Message; +using MinecraftClient.Scripting; namespace MinecraftClient.Protocol.Handlers { diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index afe5b1ba..5597dafb 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using MinecraftClient.Inventory; using MinecraftClient.Mapping; -using MinecraftClient.Protocol.Keys; +using MinecraftClient.Protocol.ProfileKey; namespace MinecraftClient.Protocol { diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 1ac38a39..2e8986bd 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -4,6 +4,7 @@ using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; using MinecraftClient.Protocol.Message; +using MinecraftClient.Scripting; namespace MinecraftClient.Protocol { @@ -456,6 +457,13 @@ namespace MinecraftClient.Protocol /// The block public void OnBlockChange(Location location, Block block); + /// + /// Called when "AutoComplete" completes. + /// + /// The number of this result. + /// All commands. + public void OnAutoCompleteDone(int transactionId, string[] result); + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 47e02256..5255e957 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -4,10 +4,9 @@ using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using MinecraftClient.Protocol.Message; using static MinecraftClient.Settings; -namespace MinecraftClient.Protocol +namespace MinecraftClient.Protocol.Message { /// /// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed. @@ -223,7 +222,7 @@ namespace MinecraftClient.Protocol HttpClient httpClient = new(); try { - Task fetch_index = httpClient.GetStringAsync(Settings.TranslationsFile_Website_Index); + Task fetch_index = httpClient.GetStringAsync(TranslationsFile_Website_Index); fetch_index.Wait(); string assets_index = fetch_index.Result; fetch_index.Dispose(); @@ -231,8 +230,8 @@ namespace MinecraftClient.Protocol 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.Config.Logging.DebugMessages) + string translation_file_location = TranslationsFile_Website_Download + '/' + hash[..2] + '/' + hash; + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_request, translation_file_location)); Task fetch_file = httpClient.GetStringAsync(translation_file_location); @@ -242,7 +241,7 @@ namespace MinecraftClient.Protocol StringBuilder stringBuilder = new(); foreach (KeyValuePair entry in Json.ParseJson(translation_file).Properties) - stringBuilder.Append(entry.Key).Append('=').Append(entry.Value.StringValue.Replace("\n", "\\n").Replace("\r", String.Empty)).Append(Environment.NewLine); + stringBuilder.Append(entry.Key).Append('=').Append(entry.Value.StringValue.Replace("\n", "\\n").Replace("\r", string.Empty)).Append(Environment.NewLine); File.WriteAllText(Language_File, stringBuilder.ToString()); ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_done, Language_File)); @@ -256,9 +255,9 @@ namespace MinecraftClient.Protocol //Download Failed? Defaulting to en_GB.lang if the game is installed if (!File.Exists(Language_File) //Try en_GB.lang - && File.Exists(Settings.TranslationsFile_FromMCDir)) + && File.Exists(TranslationsFile_FromMCDir)) { - Language_File = Settings.TranslationsFile_FromMCDir; + Language_File = TranslationsFile_FromMCDir; ConsoleIO.WriteLineFormatted(Translations.chat_from_dir, acceptnewlines: true); } @@ -277,7 +276,7 @@ namespace MinecraftClient.Protocol } } - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(Translations.chat_loaded, acceptnewlines: true); } else //No external dictionnary found. diff --git a/MinecraftClient/Protocol/MojangAPI.cs b/MinecraftClient/Protocol/MojangAPI.cs index e0dade07..8ee28b31 100644 --- a/MinecraftClient/Protocol/MojangAPI.cs +++ b/MinecraftClient/Protocol/MojangAPI.cs @@ -214,7 +214,7 @@ namespace MinecraftClient.Protocol fetchTask.Dispose(); } catch (Exception) - { + { return new MojangServiceStatus(); } diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index b77aea58..31e4300e 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -1,7 +1,7 @@ using System; using System.Linq; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; namespace MinecraftClient.Protocol { diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 94cce827..fdd68560 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -4,7 +4,7 @@ using System.Security.Cryptography; using System.Text; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { static class KeyUtils { @@ -45,7 +45,7 @@ namespace MinecraftClient.Protocol.Keys } catch (Exception e) { - int code = (response == null) ? 0 : response.StatusCode; + int code = response == null ? 0 : response.StatusCode; ConsoleIO.WriteLineFormatted("§cFetch profile key failed: HttpCode = " + code + ", Error = " + e.Message); if (Settings.Config.Logging.DebugMessages) { @@ -55,7 +55,7 @@ namespace MinecraftClient.Protocol.Keys } } - public static byte[] DecodePemKey(String key, String prefix, String suffix) + public static byte[] DecodePemKey(string key, string prefix, string suffix) { int i = key.IndexOf(prefix); if (i != -1) @@ -64,8 +64,8 @@ namespace MinecraftClient.Protocol.Keys int j = key.IndexOf(suffix, i); key = key[i..j]; } - key = key.Replace("\r", String.Empty); - key = key.Replace("\n", String.Empty); + key = key.Replace("\r", string.Empty); + key = key.Replace("\n", string.Empty); return Convert.FromBase64String(key); } @@ -135,11 +135,11 @@ namespace MinecraftClient.Protocol.Keys char c = src[i]; bool needEscape = c < 32 || c == '"' || c == '\\'; // Broken lead surrogate - needEscape = needEscape || (c >= '\uD800' && c <= '\uDBFF' && - (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF')); + needEscape = needEscape || c >= '\uD800' && c <= '\uDBFF' && + (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'); // Broken tail surrogate - needEscape = needEscape || (c >= '\uDC00' && c <= '\uDFFF' && - (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF')); + needEscape = needEscape || c >= '\uDC00' && c <= '\uDFFF' && + (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'); // To produce valid JavaScript needEscape = needEscape || c == '\u2028' || c == '\u2029'; diff --git a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs index 9962e2aa..4b052f96 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeysCache.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeysCache.cs @@ -6,7 +6,7 @@ using System.Timers; using static MinecraftClient.Settings; using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { /// /// Handle keys caching and storage. @@ -115,7 +115,7 @@ namespace MinecraftClient.Protocol.Keys //User-editable keys cache file in text format if (File.Exists(KeysCacheFilePlaintext)) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loading_keys, KeysCacheFilePlaintext)); try @@ -134,27 +134,27 @@ namespace MinecraftClient.Protocol.Keys { PlayerKeyPair playerKeyPair = PlayerKeyPair.FromString(value); keys[login] = playerKeyPair; - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_loaded_keys, playerKeyPair.ExpiresAt.ToString())); } catch (InvalidDataException e) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message)); } catch (FormatException e) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message)); } catch (ArgumentNullException e) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_string_keys, value, e.Message)); } } - else if (Settings.Config.Logging.DebugMessages) + else if (Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted(string.Format(Translations.cache_ignore_line_keys, line)); } diff --git a/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs b/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs index d7b0fe39..572b0d06 100644 --- a/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs +++ b/MinecraftClient/Protocol/ProfileKey/PlayerKeyPair.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { public class PlayerKeyPair { @@ -74,17 +74,17 @@ namespace MinecraftClient.Protocol.Keys List datas = new(); datas.Add(Convert.ToBase64String(PublicKey.Key)); if (PublicKey.Signature == null) - datas.Add(String.Empty); + datas.Add(string.Empty); else datas.Add(Convert.ToBase64String(PublicKey.Signature)); if (PublicKey.SignatureV2 == null) - datas.Add(String.Empty); + datas.Add(string.Empty); else datas.Add(Convert.ToBase64String(PublicKey.SignatureV2)); datas.Add(Convert.ToBase64String(PrivateKey.Key)); datas.Add(ExpiresAt.ToString(DataTimeFormat)); datas.Add(RefreshedAfter.ToString(DataTimeFormat)); - return String.Join(",", datas.ToArray()); + return string.Join(",", datas.ToArray()); } } } diff --git a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs index b3dd4093..6cb8103b 100644 --- a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { public class PrivateKey { diff --git a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs index 429c3962..2f01fa84 100644 --- a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Protocol.Keys +namespace MinecraftClient.Protocol.ProfileKey { public class PublicKey { diff --git a/MinecraftClient/Protocol/Session/SessionToken.cs b/MinecraftClient/Protocol/Session/SessionToken.cs index e48567d9..c3c0aee9 100644 --- a/MinecraftClient/Protocol/Session/SessionToken.cs +++ b/MinecraftClient/Protocol/Session/SessionToken.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; +using MinecraftClient.Scripting; namespace MinecraftClient.Protocol.Session { diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index 3f5208f1..fd93a259 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -151,7 +151,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Available commands: {0}. Use /autocraft help <cmd name> for more information. You may use /ac as command alias.. + /// Looks up a localized string similar to Available commands: {0}. Use /autocraft help <cmd name> for more information.. /// internal static string bot_autoCraft_available_cmd { get { @@ -403,7 +403,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Available commands: {0}. Use /digbot help <cmd name> for more information.. + /// Looks up a localized string similar to Available commands: {0}. Use /autodig help <cmd name> for more information.. /// internal static string bot_autodig_available_cmd { get { @@ -430,7 +430,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Get the command description. Usage: /digbot help <command name>. + /// Looks up a localized string similar to Get the command description. Usage: /autodig help <command name>. /// internal static string bot_autodig_help_help { get { @@ -2488,7 +2488,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Searching for a bed in radius of {0}.... + /// Looks up a localized string similar to Searching for a bed in radius of {0:0.00}.... /// internal static string cmd_bed_searching { get { @@ -3599,7 +3599,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Unknown action. . + /// Looks up a localized string similar to Unknown action.. /// internal static string cmd_inventory_help_unknown { get { @@ -4068,7 +4068,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to setrnd variable -7to17 OR setrnd variable string1 "\"string2\" string3". + /// Looks up a localized string similar to setrnd variable -7 to 17 OR setrnd variable string1 "\"string2\" string3". /// internal static string cmd_setrnd_format { get { @@ -4077,7 +4077,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to setrnd variable -7to17. + /// Looks up a localized string similar to setrnd variable -7 to 17. /// internal static string cmd_setrndnum_format { get { @@ -6875,7 +6875,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to help <cmdname>. Available commands: {0}. For server help, use '{1}send /help' instead.. + /// Looks up a localized string similar to help <cmdname>. Available commands: + ///{0}For server help, use '{1}send /help' instead.. /// internal static string icmd_list { get { diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index 8e041df0..2beab3d2 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -148,7 +148,7 @@ Auto-crafting ChatBot command alias - Available commands: {0}. Use /autocraft help <cmd name> for more information. You may use /ac as command alias. + Available commands: {0}. Use /autocraft help <cmd name> for more information. Inventory #{0} was closed by AutoCraft @@ -232,7 +232,7 @@ Action timeout! Reason: {0} - Available commands: {0}. Use /digbot help <cmd name> for more information. + Available commands: {0}. Use /autodig help <cmd name> for more information. Auto-digging ChatBot command @@ -241,7 +241,7 @@ Digging block timeout, retry. - Get the command description. Usage: /digbot help <command name> + Get the command description. Usage: /autodig help <command name> Start the automatic digging bot. @@ -937,7 +937,7 @@ Some messages won't be properly printed without this file. Could not lay in bed. Are you trying to sleep in a bed? (PS: You must use the head block coordinates of the bed) - Searching for a bed in radius of {0}... + Searching for a bed in radius of {0:0.00}... Trying to sleep in a bed on location (X: {0:0.0}, Y: {1:0.0}, Z: {2:0.0}). Result: {3} @@ -1307,7 +1307,7 @@ Note that parameters in '[]' are optional. Shift click an item. - Unknown action. + Unknown action. Usage @@ -1461,7 +1461,7 @@ You can use "/chunk status {0:0.0} {1:0.0} {2:0.0}" to check the chunk loading s set a custom %variable% randomly to a given value. - setrnd variable -7to17 + setrnd variable -7 to 17 setrnd variable string1 "\"string2\" string3" @@ -2471,7 +2471,8 @@ If the connection to the Minecraft game server is blocked by the firewall, set E help <cmdname>: show brief help about a command. - help <cmdname>. Available commands: {0}. For server help, use '{1}send /help' instead. + help <cmdname>. Available commands: +{0}For server help, use '{1}send /help' instead. Unknown command '{0}'. Use 'help' for command list. @@ -2644,7 +2645,7 @@ Logging in... Load translations applied to MCC when available, turn it off to use English only. - setrnd variable -7to17 OR setrnd variable string1 "\"string2\" string3" + setrnd variable -7 to 17 OR setrnd variable string1 "\"string2\" string3" Please update health field handling for this Minecraft version. diff --git a/MinecraftClient/Scripting/AssemblyResolver.cs b/MinecraftClient/Scripting/AssemblyResolver.cs index f9f970ea..85b16405 100644 --- a/MinecraftClient/Scripting/AssemblyResolver.cs +++ b/MinecraftClient/Scripting/AssemblyResolver.cs @@ -4,34 +4,36 @@ using System.Reflection; namespace MinecraftClient.Scripting; -public static class AssemblyResolver { +public static class AssemblyResolver +{ private static Dictionary ScriptAssemblies = new(); - static AssemblyResolver() { + static AssemblyResolver() + { // Manually resolve assemblies that .NET can't resolve automatically. - AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => + AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { - var asmReqName = new AssemblyName(args.Name); - - // Check the script-referenced assemblies if we have the DLL that is required. - foreach (var dll in ScriptAssemblies) - { - // If we have the assembly, load it. - if (asmReqName.FullName == dll.Key) - { - return Assembly.LoadFile(dll.Value); - } - } + var asmReqName = new AssemblyName(args.Name); - ConsoleIO.WriteLogLine($"[Script Error] Failed to resolve assembly {args.Name} (are you missing a DLL file?)"); - return null; - }; + // Check the script-referenced assemblies if we have the DLL that is required. + foreach (var dll in ScriptAssemblies) + { + // If we have the assembly, load it. + if (asmReqName.FullName == dll.Key) + { + return Assembly.LoadFile(dll.Value); + } + } + + ConsoleIO.WriteLogLine($"[Script Error] Failed to resolve assembly {args.Name} (are you missing a DLL file?)"); + return null; + }; } - internal static void AddAssembly(string AssemblyFullName, string AssemblyPath) + internal static void AddAssembly(string AssemblyFullName, string AssemblyPath) { if (ScriptAssemblies.ContainsKey(AssemblyFullName)) return; - + ScriptAssemblies.Add(AssemblyFullName, AssemblyPath); } } \ No newline at end of file diff --git a/MinecraftClient/Scripting/CSharpRunner.cs b/MinecraftClient/Scripting/CSharpRunner.cs index 026a29fa..1d6f258a 100644 --- a/MinecraftClient/Scripting/CSharpRunner.cs +++ b/MinecraftClient/Scripting/CSharpRunner.cs @@ -4,10 +4,10 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Text; -using DynamicRun.Builder; +using MinecraftClient.Scripting.DynamicRun.Builder; using static MinecraftClient.Settings; -namespace MinecraftClient +namespace MinecraftClient.Scripting { /// /// C# Script runner - Compile on-the-fly and run C# scripts @@ -77,7 +77,7 @@ namespace MinecraftClient script.Add("return null;"); //Generate a class from the given script - string code = String.Join("\n", new string[] + string code = string.Join("\n", new string[] { "using System;", "using System.Collections.Generic;", @@ -90,15 +90,15 @@ namespace MinecraftClient "using MinecraftClient;", "using MinecraftClient.Mapping;", "using MinecraftClient.Inventory;", - String.Join("\n", libs), + string.Join("\n", libs), "namespace ScriptLoader {", "public class Script {", "public CSharpAPI MCC;", "public object __run(CSharpAPI __apiHandler, string[] args) {", "this.MCC = __apiHandler;", - String.Join("\n", script), + string.Join("\n", script), "}", - String.Join("\n", extensions), + string.Join("\n", extensions), "}}", }); @@ -108,11 +108,13 @@ namespace MinecraftClient var result = compiler.Compile(code, Guid.NewGuid().ToString(), dlls); //Process compile warnings and errors - if (result.Failures != null) { - + if (result.Failures != null) + { + ConsoleIO.WriteLogLine("[Script] Compilation failed with error(s):"); - foreach (var failure in result.Failures) { + foreach (var failure in result.Failures) + { ConsoleIO.WriteLogLine($"[Script] Error in {scriptName}, line:col{failure.Location.GetMappedLineSpan()}: [{failure.Id}] {failure.GetMessage()}"); } @@ -226,7 +228,7 @@ namespace MinecraftClient /// TRUE if successfully sent (Deprectated, always returns TRUE for compatibility purposes with existing scripts) public bool SendText(object text) { - return base.SendText(text is string str ? str : (text.ToString() ?? string.Empty)); + return base.SendText(text is string str ? str : text.ToString() ?? string.Empty); } /// @@ -305,7 +307,7 @@ namespace MinecraftClient if (localVars != null && localVars.ContainsKey(varName)) return localVars[varName]; else - return Settings.Config.AppVar.GetVar(varName); + return Config.AppVar.GetVar(varName); } /// @@ -317,7 +319,7 @@ namespace MinecraftClient { if (localVars != null && localVars.ContainsKey(varName)) localVars.Remove(varName); - return Settings.Config.AppVar.SetVar(varName, varValue); + return Config.AppVar.SetVar(varName, varValue); } /// @@ -359,7 +361,7 @@ namespace MinecraftClient /// True if the account was found and loaded public bool SetAccount(string accountAlias, bool andReconnect = false) { - bool result = Settings.Config.Main.Advanced.SetAccount(accountAlias); + bool result = Config.Main.Advanced.SetAccount(accountAlias); if (result && andReconnect) ReconnectToTheServer(keepAccountAndServerSettings: true); return result; @@ -372,7 +374,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.Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(server), true); + bool result = Config.Main.SetServerIP(new MainConfigHealper.MainConfig.ServerInfoConfig(server), true); if (result && andReconnect) ReconnectToTheServer(keepAccountAndServerSettings: true); return result; diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 61cd85c7..245a1a4a 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -5,12 +5,12 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using Brigadier.NET; -using MinecraftClient.Commands; +using MinecraftClient.CommandHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using static MinecraftClient.Settings; -namespace MinecraftClient +namespace MinecraftClient.Scripting { /// /// Welcome to the Bot API file ! @@ -42,7 +42,6 @@ namespace MinecraftClient private McClient? _handler = null; private ChatBot? master = null; private readonly List registeredPluginChannels = new(); - private readonly List registeredCommands = new(); private readonly object delayTasksLock = new(); private readonly List delayedTasks = new(); protected McClient Handler @@ -101,12 +100,12 @@ namespace MinecraftClient /// NOTE: Chat messages cannot be sent at this point in the login process. /// If you want to send a message when the bot is loaded, use AfterGameJoined. /// - public virtual void Initialize() { } + public virtual void Initialize(CommandDispatcher dispatcher) { } /// /// This method is called when the bot is being unloaded, you can use it to free up resources like DB connections /// - public virtual void OnUnload() { } + public virtual void OnUnload(CommandDispatcher dispatcher) { } /// /// Called after the server has been joined successfully and chat messages are able to be sent. @@ -175,13 +174,13 @@ namespace MinecraftClient /// Called when properties for the Player entity are received from the server /// /// Dictionary of player properties - public virtual void OnPlayerProperty(Dictionary prop) { } + public virtual void OnPlayerProperty(Dictionary prop) { } /// /// Called when server TPS are recalculated by MCC based on world time updates /// /// New estimated server TPS (between 0 and 20) - public virtual void OnServerTpsUpdate(Double tps) { } + public virtual void OnServerTpsUpdate(double tps) { } /// /// Called when a time changed @@ -194,7 +193,7 @@ namespace MinecraftClient /// Called when an entity moved nearby /// /// Entity with updated location - public virtual void OnEntityMove(Mapping.Entity entity) { } + public virtual void OnEntityMove(Entity entity) { } /// /// Called after an internal MCC command has been performed @@ -202,19 +201,19 @@ namespace MinecraftClient /// MCC Command Name /// MCC Command Parameters /// MCC command result - public virtual void OnInternalCommand(string commandName, string commandParams, string Result) { } + public virtual void OnInternalCommand(string commandName, string commandParams, CmdResult Result) { } /// /// Called when an entity spawned nearby /// /// New Entity - public virtual void OnEntitySpawn(Mapping.Entity entity) { } + public virtual void OnEntitySpawn(Entity entity) { } /// /// Called when an entity despawns/dies nearby /// /// Entity wich has just disappeared - public virtual void OnEntityDespawn(Mapping.Entity entity) { } + public virtual void OnEntityDespawn(Entity entity) { } /// /// Called when the player held item has changed @@ -515,7 +514,7 @@ namespace MinecraftClient /// TRUE if the command was indeed an internal MCC command protected bool PerformInternalCommand(string command, Dictionary? localVars = null) { - string? temp = ""; + CmdResult temp = new(); return Handler.PerformInternalCommand(command, ref temp, localVars); } @@ -526,9 +525,9 @@ namespace MinecraftClient /// May contain a confirmation or error message after processing the command, or "" otherwise. /// Local variables passed along with the command /// TRUE if the command was indeed an internal MCC command - protected bool PerformInternalCommand(string command, ref string? response_msg, Dictionary? localVars = null) + protected bool PerformInternalCommand(string command, ref CmdResult result, Dictionary? localVars = null) { - return Handler.PerformInternalCommand(command, ref response_msg, localVars); + return Handler.PerformInternalCommand(command, ref result, localVars); } /// @@ -536,8 +535,8 @@ namespace MinecraftClient /// public static string GetVerbatim(string? text) { - if (String.IsNullOrEmpty(text)) - return String.Empty; + if (string.IsNullOrEmpty(text)) + return string.Empty; int idx = 0; var data = new char[text.Length]; @@ -556,13 +555,13 @@ namespace MinecraftClient /// public static bool IsValidName(string username) { - if (String.IsNullOrEmpty(username)) + if (string.IsNullOrEmpty(username)) return false; foreach (char c in username) - if (!((c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') + if (!(c >= 'a' && c <= 'z' + || c >= 'A' && c <= 'Z' + || c >= '0' && c <= '9' || c == '_')) return false; @@ -578,7 +577,7 @@ namespace MinecraftClient /// Returns true if the text is a private message protected static bool IsPrivateMessage(string text, ref string message, ref string sender) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); @@ -689,7 +688,7 @@ namespace MinecraftClient /// Returns true if the text is a chat message protected static bool IsChatMessage(string text, ref string message, ref string sender) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); @@ -792,7 +791,7 @@ namespace MinecraftClient /// Returns true if the text is a teleport request protected static bool IsTeleportRequest(string text, ref string sender) { - if (String.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); @@ -820,8 +819,8 @@ namespace MinecraftClient { // Username has requested... //[Rank] Username has requested... - if (((tmp[0].StartsWith("<") && tmp[0].EndsWith(">")) - || (tmp[0].StartsWith("[") && tmp[0].EndsWith("]"))) + if ((tmp[0].StartsWith("<") && tmp[0].EndsWith(">") + || tmp[0].StartsWith("[") && tmp[0].EndsWith("]")) && tmp.Length > 1) sender = tmp[1]; else //Username has requested.. @@ -847,12 +846,12 @@ namespace MinecraftClient { string botName = Translations.ResourceManager.GetString("botname." + GetType().Name) ?? GetType().Name; if (_handler == null || master == null) - ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text)); + ConsoleIO.WriteLogLine(string.Format("[{0}] {1}", botName, text)); else - Handler.Log.Info(String.Format("[{0}] {1}", botName, text)); - string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); + Handler.Log.Info(string.Format("[{0}] {1}", botName, text)); + string logfile = Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); - if (!String.IsNullOrEmpty(logfile)) + if (!string.IsNullOrEmpty(logfile)) { if (!File.Exists(logfile)) { @@ -869,10 +868,10 @@ namespace MinecraftClient protected static void LogToConsole(string originBotName, object? text) { string botName = Translations.ResourceManager.GetString(originBotName) ?? originBotName; - ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text)); - string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); + ConsoleIO.WriteLogLine(string.Format("[{0}] {1}", botName, text)); + string logfile = Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile); - if (!String.IsNullOrEmpty(logfile)) + if (!string.IsNullOrEmpty(logfile)) { if (!File.Exists(logfile)) { @@ -892,7 +891,7 @@ namespace MinecraftClient /// Debug log text to write protected void LogDebugToConsole(object text) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) LogToConsole(text); } @@ -924,7 +923,7 @@ namespace MinecraftClient /// Optional delay, in seconds, before restarting protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0, bool keepAccountAndServerSettings = false) { - if (Settings.Config.Logging.DebugMessages) + if (Config.Logging.DebugMessages) { string botName = Translations.ResourceManager.GetString("botname." + GetType().Name) ?? GetType().Name; ConsoleIO.WriteLogLine(string.Format(Translations.chatbot_reconnect, botName)); @@ -946,10 +945,6 @@ namespace MinecraftClient /// protected void UnloadBot() { - foreach (string cmdName in registeredCommands) - { - Handler.UnregisterCommand(cmdName); - } Handler.BotUnLoad(this); } @@ -960,7 +955,7 @@ namespace MinecraftClient /// Message protected void SendPrivateMessage(string player, string message) { - SendText(String.Format("/{0} {1} {2}", Config.Main.Advanced.PrivateMsgsCmdName, player, message)); + SendText(string.Format("/{0} {1} {2}", Config.Main.Advanced.PrivateMsgsCmdName, player, message)); } /// @@ -1079,7 +1074,7 @@ namespace MinecraftClient /// Get the current location of the player (Feet location) /// /// Minecraft world or null if associated setting is disabled - protected Mapping.Location GetCurrentLocation() + protected Location GetCurrentLocation() { return Handler.GetCurrentLocation(); } @@ -1095,7 +1090,7 @@ namespace MinecraftClient /// How long to wait before stopping computation (default: 5 seconds) /// When location is unreachable, computation will reach timeout, then optionally fallback to a close location within maxOffset /// True if a path has been found - protected bool MoveToLocation(Mapping.Location location, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null) + protected bool MoveToLocation(Location location, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null) { return Handler.MoveTo(location, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeout); } @@ -1113,7 +1108,7 @@ namespace MinecraftClient /// Look at the specified location /// /// Location to look at - protected void LookAtLocation(Mapping.Location location) + protected void LookAtLocation(Location location) { Handler.UpdateLocation(Handler.GetCurrentLocation(), location); } @@ -1167,13 +1162,13 @@ namespace MinecraftClient //Read all lines from file, remove lines with no text, convert to lowercase, //remove duplicate entries, convert to a string array, and return the result. return File.ReadAllLines(file, Encoding.UTF8) - .Where(line => !String.IsNullOrWhiteSpace(line)) + .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => line.ToLower()) .Distinct().ToArray(); } else { - LogToConsole("File not found: " + System.IO.Path.GetFullPath(file)); + LogToConsole("File not found: " + Path.GetFullPath(file)); return Array.Empty(); } } @@ -1315,7 +1310,7 @@ namespace MinecraftClient /// Get server current TPS (tick per second) /// /// tps - protected Double GetServerTPS() + protected double GetServerTPS() { return Handler.GetServerTPS(); } @@ -1541,21 +1536,6 @@ namespace MinecraftClient return Handler.UpdateCommandBlock(location, command, mode, flags); } - /// - /// Register a command in command prompt. Command will be automatically unregistered when unloading ChatBot - /// - /// Name of the command - /// Description/usage of the command - /// Method for handling the command - /// True if successfully registered - protected bool RegisterChatBotCommand(string cmdName, string cmdDesc, string cmdUsage, CommandRunner callback) - { - bool result = Handler.RegisterCommand(cmdName, cmdDesc, cmdUsage, callback); - if (result) - registeredCommands.Add(cmdName.ToLower()); - return result; - } - /// /// Close a opened inventory /// @@ -1687,11 +1667,12 @@ namespace MinecraftClient public override string CmdUsage { get { return _cmdUsage; } } public override string CmdDesc { get { return _cmdDesc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + } - public override string Run(McClient handler, string command, Dictionary? localVars) + public string Run(McClient handler, string command, Dictionary? localVars) { return Runner(command, GetArgs(command)); } diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs b/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs index 8a1e492c..ee5bf460 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs @@ -8,9 +8,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; -using MinecraftClient; -namespace DynamicRun.Builder +namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class CompileRunner { diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs index 4bb02b07..078f97a0 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs @@ -14,11 +14,9 @@ using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; -using MinecraftClient; -using MinecraftClient.Scripting; using SingleFileExtractor.Core; -namespace DynamicRun.Builder +namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class Compiler { @@ -38,7 +36,7 @@ namespace DynamicRun.Builder Failures = failures.ToList() }; } - + peStream.Seek(0, SeekOrigin.Begin); return new CompileResult() @@ -55,20 +53,20 @@ namespace DynamicRun.Builder var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options); - + var references = new List(); // Find if any additional assembly DLL exists in the base directory where the .exe exists. - foreach (var assembly in additionalAssemblies) + foreach (var assembly in additionalAssemblies) { var dllPath = Path.Combine(AppContext.BaseDirectory, assembly); - if (File.Exists(dllPath)) + if (File.Exists(dllPath)) { references.Add(MetadataReference.CreateFromFile(dllPath)); // Store the reference in our Assembly Resolver for future reference. AssemblyResolver.AddAssembly(Assembly.LoadFile(dllPath).FullName!, dllPath); } - else + else { ConsoleIO.WriteLogLine($"[Script Error] {assembly} is defined in script, but cannot find DLL! Script may not run."); } @@ -79,7 +77,7 @@ namespace DynamicRun.Builder var SystemPrivateCoreLib = typeof(object).Assembly.Location; // System.Private.CoreLib var SystemConsole = typeof(Console).Assembly.Location; // System.Console var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll - + // We're on a self-contained binary, so we need to extract the executable to get the assemblies. if (string.IsNullOrEmpty(MinecraftClientDll)) { diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs b/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs index de44652c..e210bcac 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/SimpleUnloadableAssemblyLoadContext.cs @@ -7,7 +7,7 @@ https://github.com/laurentkempe/DynamicRun/blob/master/LICENSE using System.Reflection; using System.Runtime.Loader; -namespace DynamicRun.Builder +namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class SimpleUnloadableAssemblyLoadContext : AssemblyLoadContext { From 5e11ed38962b56d92c6e09cdca0457e9f271e54e Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 11 Dec 2022 13:00:19 +0800 Subject: [PATCH 03/44] Tooltip support & Bug fix --- MinecraftClient/ChatBots/AntiAFK.cs | 2 - MinecraftClient/ChatBots/AutoAttack.cs | 2 - MinecraftClient/ChatBots/AutoDig.cs | 1 - MinecraftClient/ChatBots/AutoDrop.cs | 1 - MinecraftClient/ChatBots/AutoFishing.cs | 1 - MinecraftClient/ChatBots/AutoRelog.cs | 2 - MinecraftClient/ChatBots/AutoRespond.cs | 2 - MinecraftClient/ChatBots/DiscordBridge.cs | 1 - MinecraftClient/ChatBots/Mailer.cs | 1 - MinecraftClient/ChatBots/ReplayCapture.cs | 1 - MinecraftClient/ChatBots/Script.cs | 2 - MinecraftClient/ChatBots/TelegramBridge.cs | 1 - .../ArgumentType/EntityTypeArgumentType.cs | 23 +- .../ArgumentType/HotbarSlotArgumentType.cs | 43 + .../ArgumentType/InventoryIdArgumentType.cs | 8 +- .../ArgumentType/InventorySlotArgumentType.cs | 55 + .../ArgumentType/ItemTypeArgumentType.cs | 25 +- .../CommandHandler/MccArguments.cs | 10 + .../Patch/CommandDispatcherExtensions.cs | 29 +- .../CommandHandler/SuggestionTooltip.cs | 14 + MinecraftClient/Commands/ChangeSlot.cs | 2 +- MinecraftClient/Commands/Inventory.cs | 16 +- MinecraftClient/Commands/Upgrade.cs | 12 +- MinecraftClient/ConsoleIO.cs | 42 +- MinecraftClient/Mapping/Entity.cs | 9 +- MinecraftClient/McClient.cs | 8 +- MinecraftClient/MinecraftClient.csproj | 10 + MinecraftClient/Program.cs | 2 +- .../Protocol/Message/ChatParser.cs | 120 +- MinecraftClient/Protocol/ProxiedWebRequest.cs | 4 +- MinecraftClient/Proxy/ProxyHandler.cs | 2 +- .../ConfigComments/ConfigComments.Designer.cs | 3680 ++++++----- .../ConfigComments/ConfigComments.resx | 15 + .../Resources/MinecraftAssets.Designer.cs | 73 + .../Resources/MinecraftAssets.resx | 124 + .../Translations/Translations.Designer.cs | 36 +- .../Resources/Translations/Translations.resx | 19 +- MinecraftClient/Resources/en_us.json | 5822 +++++++++++++++++ MinecraftClient/Settings.cs | 172 +- MinecraftClient/UpgradeHelper.cs | 4 +- 40 files changed, 8409 insertions(+), 1987 deletions(-) create mode 100644 MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs create mode 100644 MinecraftClient/CommandHandler/SuggestionTooltip.cs create mode 100644 MinecraftClient/Resources/MinecraftAssets.Designer.cs create mode 100644 MinecraftClient/Resources/MinecraftAssets.resx create mode 100644 MinecraftClient/Resources/en_us.json diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs index a6fd308b..bb43045d 100644 --- a/MinecraftClient/ChatBots/AntiAFK.cs +++ b/MinecraftClient/ChatBots/AntiAFK.cs @@ -1,6 +1,4 @@ using System; -using Brigadier.NET; -using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; using MinecraftClient.Scripting; using Tomlet.Attributes; diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 771d5769..8cd8ee2f 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Brigadier.NET; -using MinecraftClient.CommandHandler; using MinecraftClient.Mapping; using MinecraftClient.Scripting; using Tomlet.Attributes; diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs index 8c2180e6..49b9b44e 100644 --- a/MinecraftClient/ChatBots/AutoDig.cs +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index 00854a67..7dea2c18 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 652c7b3f..28886bfd 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs index 4f461d83..c384eb7c 100644 --- a/MinecraftClient/ChatBots/AutoRelog.cs +++ b/MinecraftClient/ChatBots/AutoRelog.cs @@ -1,6 +1,4 @@ using System; -using Brigadier.NET; -using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; using Tomlet.Attributes; diff --git a/MinecraftClient/ChatBots/AutoRespond.cs b/MinecraftClient/ChatBots/AutoRespond.cs index 823dfa16..3b5a2991 100644 --- a/MinecraftClient/ChatBots/AutoRespond.cs +++ b/MinecraftClient/ChatBots/AutoRespond.cs @@ -4,10 +4,8 @@ using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; -using Brigadier.NET; using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; -using PInvoke; using Tomlet.Attributes; using static MinecraftClient.Settings; diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index 528805b5..c9aa29a2 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using Brigadier.NET; using Brigadier.NET.Builder; using DSharpPlus; using DSharpPlus.Entities; diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index 1ca5f034..8ef5c1d1 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using Brigadier.NET; using Brigadier.NET.Builder; -using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Scripting; using Tomlet.Attributes; diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index 651431f6..9db42ec2 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 194a1f48..8ed7cb42 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -5,8 +5,6 @@ using System.IO; using System.Reflection; using System.Text; using System.Threading; -using Brigadier.NET; -using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; namespace MinecraftClient.ChatBots diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index f83b60a3..392bc1cf 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; diff --git a/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs index 9ba6f999..fc3ce124 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs @@ -23,8 +23,27 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - foreach (var result in Enum.GetNames(typeof(EntityType))) - builder.Suggest(result); + foreach (EntityType result in Enum.GetValues()) + { + string name = result.ToString(); + string localName = Entity.GetTypeString(result); + bool same = true; + for (int i = 0, j = 0; i < name.Length; ++i, ++j) + { + while (j < localName.Length && localName[j] == ' ') + ++j; + if (j >= localName.Length || name[i] != localName[j]) + { + same = false; + break; + } + } + if (same) + builder.Suggest(name); + else + builder.Suggest(name, new SuggestionTooltip(localName)); + } + return builder.BuildFuture(); } } diff --git a/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs new file mode 100644 index 00000000..16d07611 --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class HotbarSlotArgumentType : ArgumentType + { + public override int Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadInt(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null) + { + Inventory.Container? inventory = client.GetInventory(0); + if (inventory != null) + { + for (int i = 1; i <= 9; ++i) + { + if (inventory.Items.TryGetValue(i - 1 + 36, out Inventory.Item? item)) + { + string slotStr = i.ToString(); + if (slotStr.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) + { + string itemDesc = item.Count == 1 ? item.GetTypeString() : string.Format("{0}x{1}", item.Count, item.GetTypeString()); + builder.Suggest(slotStr, new SuggestionTooltip(itemDesc)); + } + } + } + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs index c697b493..6c7ae2af 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs @@ -25,7 +25,13 @@ namespace MinecraftClient.CommandHandler.ArgumentType { string invName = inv.Key.ToString(); if (invName.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) - builder.Suggest(invName); + { + string? invTitle = inv.Value.Title; + if (!string.IsNullOrWhiteSpace(invTitle)) + builder.Suggest(invName, new SuggestionTooltip(invTitle)); + else + builder.Suggest(invName); + } } } return builder.BuildFuture(); diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs new file mode 100644 index 00000000..ecc796ca --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class InventorySlotArgumentType : ArgumentType + { + public override int Parse(IStringReader reader) + { + reader.SkipWhitespace(); + return reader.ReadInt(); + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + McClient? client = CmdResult.client; + if (client != null && context.Nodes.Count >= 2) + { + string invName = context.Nodes[1].Range.Get(builder.Input); + if (!int.TryParse(invName, out int invId)) + invId = invName switch + { + "creativegive" => 0, + "creativedelete" => 0, + "player" => 0, + "container" => client.GetInventories().Keys.ToList().Max(), + _ => -1, + }; + + Inventory.Container? inventory = client.GetInventory(invId); + if (inventory != null) + { + foreach ((int slot, Inventory.Item item) in inventory.Items) + { + if (item != null && item.Count > 0) + { + string slotStr = slot.ToString(); + if (slotStr.StartsWith(builder.RemainingLowerCase, StringComparison.InvariantCultureIgnoreCase)) + { + string itemDesc = item.Count == 1 ? item.GetTypeString() : string.Format("{0}x{1}", item.Count, item.GetTypeString()); + builder.Suggest(slotStr, new SuggestionTooltip(itemDesc)); + } + } + } + } + } + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs index 28ab93a3..fcd270bf 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/ItemTypeArgumentType.cs @@ -23,8 +23,29 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - foreach (var result in Enum.GetNames(typeof(ItemType))) - builder.Suggest(result); + foreach (ItemType result in Enum.GetValues()) + { + if (result == ItemType.Unknown || result == ItemType.Null) + continue; + + string name = result.ToString(); + string localName = Item.GetTypeString(result); + bool same = true; + for (int i = 0, j = 0; i < name.Length; ++i, ++j) + { + while (j < localName.Length && localName[j] == ' ') + ++j; + if (j >= localName.Length || name[i] != localName[j]) + { + same = false; + break; + } + } + if (same) + builder.Suggest(name); + else + builder.Suggest(name, new SuggestionTooltip(localName)); + } return builder.BuildFuture(); } } diff --git a/MinecraftClient/CommandHandler/MccArguments.cs b/MinecraftClient/CommandHandler/MccArguments.cs index ef6fbf8a..c659ee3e 100644 --- a/MinecraftClient/CommandHandler/MccArguments.cs +++ b/MinecraftClient/CommandHandler/MccArguments.cs @@ -71,6 +71,11 @@ namespace MinecraftClient.CommandHandler return new InventoryActionArgumentType(); } + public static InventorySlotArgumentType InventorySlot() + { + return new InventorySlotArgumentType(); + } + public static Inventory.WindowActionType GetInventoryAction(CommandContext context, string name) { return context.GetArgument(name); @@ -100,5 +105,10 @@ namespace MinecraftClient.CommandHandler { return new MapBotMapIdArgumentType(); } + + public static HotbarSlotArgumentType HotbarSlot() + { + return new HotbarSlotArgumentType(); + } } } diff --git a/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs b/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs index 341fa4ae..37ef9cc4 100644 --- a/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs +++ b/MinecraftClient/CommandHandler/Patch/CommandDispatcherExtensions.cs @@ -18,19 +18,26 @@ namespace MinecraftClient.CommandHandler.Patch public static string GetAllUsageString(this CommandDispatcher commandDispatcher, string commandName, bool restricted) { char cmdChar = Settings.Config.Main.Advanced.InternalCmdChar.ToChar(); - string[] usages = commandDispatcher.GetAllUsage(commandDispatcher.GetRoot().GetChild(commandName), new(), restricted); - StringBuilder sb = new(); - sb.AppendLine("All Usages:"); - foreach (var usage in usages) + try { - sb.Append(cmdChar).Append(commandName).Append(' '); - if (usage.Length > 0 && usage[0] == '_') - sb.AppendLine(usage.Replace("_help -> ", $"_help -> {cmdChar}help ")); - else - sb.AppendLine(usage); + string[] usages = commandDispatcher.GetAllUsage(commandDispatcher.GetRoot().GetChild(commandName), new(), restricted); + StringBuilder sb = new(); + sb.AppendLine("All Usages:"); + foreach (var usage in usages) + { + sb.Append(cmdChar).Append(commandName).Append(' '); + if (usage.Length > 0 && usage[0] == '_') + sb.AppendLine(usage.Replace("_help -> ", $"_help -> {cmdChar}help ")); + else + sb.AppendLine(usage); + } + sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + catch + { + return string.Empty; } - sb.Remove(sb.Length - 1, 1); - return sb.ToString(); } } } \ No newline at end of file diff --git a/MinecraftClient/CommandHandler/SuggestionTooltip.cs b/MinecraftClient/CommandHandler/SuggestionTooltip.cs new file mode 100644 index 00000000..c235f061 --- /dev/null +++ b/MinecraftClient/CommandHandler/SuggestionTooltip.cs @@ -0,0 +1,14 @@ +using Brigadier.NET; + +namespace MinecraftClient.CommandHandler +{ + internal class SuggestionTooltip : IMessage + { + public SuggestionTooltip(string tooltip) + { + String = tooltip; + } + + public string String { get; set; } + } +} diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index 4a565de8..0353fe1e 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Then(l => l.Argument("Slot", Arguments.Integer(1, 9)) + .Then(l => l.Argument("Slot", MccArguments.HotbarSlot()) .Executes(r => DoChangeSlot(r.Source, handler, Arguments.GetInteger(r, "Slot")))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index 2fde9d2d..cc674cc3 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -44,12 +44,12 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => ListAllInventories(r.Source, handler)) .Then(l => l.Literal("creativegive") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Then(l => l.Argument("ItemType", MccArguments.ItemType()) .Then(l => l.Argument("Count", Arguments.Integer(min: 1)) .Executes(r => DoCreativeGive(r.Source, handler, Arguments.GetInteger(r, "Slot"), MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count"))))))) .Then(l => l.Literal("creativedelete") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoCreativeDelete(r.Source, handler, Arguments.GetInteger(r, "Slot"))))) .Then(l => l.Literal("inventories") .Executes(r => ListAvailableInventories(r.Source, handler))) @@ -64,12 +64,12 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("list") .Executes(r => DoListAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) .Then(l => l.Literal("click") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) .Then(l => l.Argument("Action", MccArguments.InventoryAction()) .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) .Then(l => l.Literal("drop") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) .Then(l => l.Literal("all") .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) @@ -77,12 +77,12 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("list") .Executes(r => DoListAction(r.Source, handler, inventoryId: 0))) .Then(l => l.Literal("click") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 45)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) .Then(l => l.Argument("Action", MccArguments.InventoryAction()) .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) .Then(l => l.Literal("drop") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 45)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) .Then(l => l.Literal("all") .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) @@ -92,12 +92,12 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("list") .Executes(r => DoListAction(r.Source, handler, inventoryId: null))) .Then(l => l.Literal("click") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) .Then(l => l.Argument("Action", MccArguments.InventoryAction()) .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) .Then(l => l.Literal("drop") - .Then(l => l.Argument("Slot", Arguments.Integer(0, 89)) + .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) .Then(l => l.Literal("all") .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) diff --git a/MinecraftClient/Commands/Upgrade.cs b/MinecraftClient/Commands/Upgrade.cs index 227cb995..625b5d5b 100644 --- a/MinecraftClient/Commands/Upgrade.cs +++ b/MinecraftClient/Commands/Upgrade.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Brigadier.NET; +using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; @@ -11,7 +10,8 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "upgrade [-f|check|cancel|download]"; } } public override string CmdDesc { get { return string.Empty; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) { + public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) .Executes(r => GetUsage(r.Source, string.Empty)) @@ -41,8 +41,10 @@ namespace MinecraftClient.Commands ); } - private int GetUsage(CmdResult r, string? cmd) { - return r.SetAndReturn(cmd switch { + private int GetUsage(CmdResult r, string? cmd) + { + return r.SetAndReturn(cmd switch + { #pragma warning disable format // @formatter:off "cancel" => GetCmdDescTranslated(), "check" => GetCmdDescTranslated(), diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs index 099fb84b..db2f849b 100644 --- a/MinecraftClient/ConsoleIO.cs +++ b/MinecraftClient/ConsoleIO.cs @@ -220,16 +220,19 @@ namespace MinecraftClient string command = fullCommand[offset..]; if (command.Length == 0) { - var childs = CmdResult.client!.dispatcher.GetRoot().Children; - int index = 0; - var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[childs.Count + Commands.Count + 1]; + List sugList = new(); + + sugList.Add(new("/")); + + var childs = CmdResult.client?.dispatcher.GetRoot().Children; + if (childs != null) + foreach (var child in childs) + sugList.Add(new(child.Name)); - sugList[index++] = new("/"); - foreach (var child in childs) - sugList[index++] = new(child.Name); foreach (var cmd in Commands) - sugList[index++] = new(cmd); - ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, new(offset, offset)); + sugList.Add(new(cmd)); + + ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList.ToArray(), new(offset, offset)); } else if (command.Length > 0 && command[0] == '/' && !command.Contains(' ')) { @@ -243,29 +246,37 @@ namespace MinecraftClient } else { - var parse = CmdResult.client!.dispatcher.Parse(command, CmdResult.Empty); + CommandDispatcher? dispatcher = CmdResult.client?.dispatcher; + if (dispatcher == null) + return; - var suggestion = await CmdResult.client!.dispatcher.GetCompletionSuggestions(parse, buffer.CursorPosition - offset); + ParseResults parse = dispatcher.Parse(command, CmdResult.Empty); - int sugLen = suggestion.List.Count; + Brigadier.NET.Suggestion.Suggestions suggestions = await dispatcher.GetCompletionSuggestions(parse, buffer.CursorPosition - offset); + + int sugLen = suggestions.List.Count; if (sugLen == 0) { ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); return; } + Dictionary dictionary = new(); + foreach (var sug in suggestions.List) + dictionary.Add(sug.Text, sug.Tooltip?.String); + var sugList = new ConsoleInteractive.ConsoleSuggestion.Suggestion[sugLen]; if (cts.IsCancellationRequested) return; - Tuple range = new(suggestion.Range.Start + offset, suggestion.Range.End + offset); - var sorted = Process.ExtractSorted(fullCommand[range.Item1..range.Item2], suggestion.List.Select(_ => _.Text).ToList()); + Tuple range = new(suggestions.Range.Start + offset, suggestions.Range.End + offset); + var sorted = Process.ExtractSorted(fullCommand[range.Item1..range.Item2], dictionary.Keys); if (cts.IsCancellationRequested) return; int index = 0; foreach (var sug in sorted) - sugList[index++] = new(sug.Value); + sugList[index++] = new(sug.Value, dictionary[sug.Value] ?? string.Empty); ConsoleInteractive.ConsoleSuggestion.UpdateSuggestions(sugList, range); } @@ -283,7 +294,8 @@ namespace MinecraftClient public static void AutocompleteHandler(object? sender, ConsoleInteractive.ConsoleReader.Buffer buffer) { - MccAutocompleteHandler(buffer); + if (Settings.Config.Console.CommandSuggestion.Enable) + MccAutocompleteHandler(buffer); } public static void CancelAutocomplete() diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/Mapping/Entity.cs index 60364780..ad9fbf7b 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/Mapping/Entity.cs @@ -156,11 +156,16 @@ namespace MinecraftClient.Mapping Pitch = pitch * (1 / 256) * 360; } - public string GetTypeString() + public static string GetTypeString(EntityType type) { - string typeStr = Type.ToString(); + string typeStr = type.ToString(); string? trans = ChatParser.TranslateString("entity.minecraft." + typeStr.ToUnderscoreCase()); return string.IsNullOrEmpty(trans) ? typeStr : trans; } + + public string GetTypeString() + { + return GetTypeString(Type); + } } } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 668d656f..2b38ab50 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -150,7 +150,6 @@ namespace MinecraftClient /// ForgeInfo item stating that Forge is enabled public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo) { - dispatcher = new(); CmdResult.client = this; terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; @@ -1048,9 +1047,10 @@ namespace MinecraftClient if (InvokeRequired) return InvokeOnMainThread(() => GetInventory(inventoryID)); - if (inventories.ContainsKey(inventoryID)) - return inventories[inventoryID]; - return null; + if (inventories.TryGetValue(inventoryID, out Container? inv)) + return inv; + else + return null; } /// diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index d824a836..95597b42 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -85,6 +85,11 @@ True ConfigComments.resx + + True + True + MinecraftAssets.resx + True True @@ -102,6 +107,11 @@ ConfigComments.Designer.cs MinecraftClient + + ResXFileCodeGenerator + MinecraftAssets.Designer.cs + MinecraftClient + ResXFileCodeGenerator Translations.Designer.cs diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index ea6af5eb..cd525fb4 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -692,7 +692,7 @@ namespace MinecraftClient { WriteBackSettings(true); ConsoleInteractive.ConsoleSuggestion.ClearSuggestions(); - ConsoleIO.WriteLineFormatted(string.Format(Translations.config_saving, settingsIniPath)); + ConsoleIO.WriteLineFormatted("§a" + string.Format(Translations.config_saving, settingsIniPath)); if (client != null) { client.Disconnect(); ConsoleIO.Reset(); } if (offlinePrompt != null) { offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); } diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 55a1999f..3df06572 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -1,8 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading.Tasks; using static MinecraftClient.Settings; @@ -186,7 +187,7 @@ namespace MinecraftClient.Protocol.Message /// /// Set of translation rules for formatting text /// - private static readonly Dictionary TranslationRules = new(); + private static Dictionary TranslationRules = new(); /// /// Initialize translation rules. @@ -199,90 +200,85 @@ namespace MinecraftClient.Protocol.Message /// private static void InitRules() { - //Small default dictionnary of translation rules - TranslationRules["chat.type.admin"] = "[%s: %s]"; - TranslationRules["chat.type.announcement"] = "§d[%s] %s"; - TranslationRules["chat.type.emote"] = " * %s %s"; - TranslationRules["chat.type.text"] = "<%s> %s"; - TranslationRules["multiplayer.player.joined"] = "§e%s joined the game."; - TranslationRules["multiplayer.player.left"] = "§e%s left the game."; - TranslationRules["commands.message.display.incoming"] = "§7%s whispers to you: %s"; - TranslationRules["commands.message.display.outgoing"] = "§7You whisper to %s: %s"; + if (Config.Main.Advanced.Language == "en_us") + { + TranslationRules = JsonSerializer.Deserialize>((byte[])MinecraftAssets.ResourceManager.GetObject("en_us.json")!)!; + return; + } //Language file in a subfolder, depending on the language setting if (!Directory.Exists("lang")) Directory.CreateDirectory("lang"); - string Language_File = "lang" + Path.DirectorySeparatorChar + Config.Main.Advanced.Language + ".lang"; + string languageFilePath = "lang" + Path.DirectorySeparatorChar + Config.Main.Advanced.Language + ".json"; - //File not found? Try downloading language file from Mojang's servers? - if (!File.Exists(Language_File)) + // Load the external dictionnary of translation rules or display an error message + if (File.Exists(languageFilePath)) { - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_download, Config.Main.Advanced.Language)); - HttpClient httpClient = new(); try { - Task fetch_index = httpClient.GetStringAsync(TranslationsFile_Website_Index); - fetch_index.Wait(); - string assets_index = fetch_index.Result; - fetch_index.Dispose(); + TranslationRules = JsonSerializer.Deserialize>(File.OpenRead(languageFilePath))!; + } + catch (IOException) { } + catch (JsonException) { } + } - 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 + if (TranslationRules.TryGetValue("Version", out string? version) && version == Settings.TranslationsFile_Version) + { + if (Config.Logging.DebugMessages) + ConsoleIO.WriteLineFormatted(Translations.chat_loaded, acceptnewlines: true); + return; + } + + // Try downloading language file from Mojang's servers? + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_download, Config.Main.Advanced.Language)); + HttpClient httpClient = new(); + try + { + Task fetch_index = httpClient.GetStringAsync(TranslationsFile_Website_Index); + fetch_index.Wait(); + Match match = Regex.Match(fetch_index.Result, $"minecraft/lang/{Config.Main.Advanced.Language}.json" + @""":\s\{""hash"":\s""([\d\w]{40})"""); + fetch_index.Dispose(); + if (match.Success && match.Groups.Count == 2) + { + string hash = match.Groups[1].Value; string translation_file_location = TranslationsFile_Website_Download + '/' + hash[..2] + '/' + hash; if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_request, translation_file_location)); - Task fetch_file = httpClient.GetStringAsync(translation_file_location); + Task fetch_file = httpClient.GetStreamAsync(translation_file_location); fetch_file.Wait(); - string translation_file = fetch_file.Result; + TranslationRules = JsonSerializer.Deserialize>(fetch_file.Result)!; fetch_file.Dispose(); - StringBuilder stringBuilder = new(); - foreach (KeyValuePair entry in Json.ParseJson(translation_file).Properties) - stringBuilder.Append(entry.Key).Append('=').Append(entry.Value.StringValue.Replace("\n", "\\n").Replace("\r", string.Empty)).Append(Environment.NewLine); - File.WriteAllText(Language_File, stringBuilder.ToString()); + TranslationRules["Version"] = TranslationsFile_Version; - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_done, Language_File)); + File.WriteAllText(languageFilePath, JsonSerializer.Serialize(TranslationRules, typeof(Dictionary)), Encoding.UTF8); + + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_done, languageFilePath)); + + return; } - catch + else { ConsoleIO.WriteLineFormatted("§8" + Translations.chat_fail, acceptnewlines: true); } + } + catch (HttpRequestException) + { + ConsoleIO.WriteLineFormatted("§8" + Translations.chat_fail, acceptnewlines: true); + } + catch (IOException) + { + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_save_fail, languageFilePath), acceptnewlines: true); + } + finally + { httpClient.Dispose(); } - //Download Failed? Defaulting to en_GB.lang if the game is installed - if (!File.Exists(Language_File) //Try en_GB.lang - && File.Exists(TranslationsFile_FromMCDir)) - { - Language_File = TranslationsFile_FromMCDir; - ConsoleIO.WriteLineFormatted(Translations.chat_from_dir, acceptnewlines: true); - } - - //Load the external dictionnary of translation rules or display an error message - if (File.Exists(Language_File)) - { - foreach (var line in File.ReadLines(Language_File)) - { - if (line.Length > 0) - { - string[] splitted = line.Split('='); - if (splitted.Length == 2) - { - TranslationRules[splitted[0]] = splitted[1]; - } - } - } - - if (Config.Logging.DebugMessages) - ConsoleIO.WriteLineFormatted(Translations.chat_loaded, acceptnewlines: true); - } - else //No external dictionnary found. - { - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_not_found, Language_File)); - } + TranslationRules = JsonSerializer.Deserialize>((byte[])MinecraftAssets.ResourceManager.GetObject("en_us.json")!)!; + ConsoleIO.WriteLine(Translations.chat_use_default); } public static string? TranslateString(string rulename) diff --git a/MinecraftClient/Protocol/ProxiedWebRequest.cs b/MinecraftClient/Protocol/ProxiedWebRequest.cs index 1e028abb..06e69a9d 100644 --- a/MinecraftClient/Protocol/ProxiedWebRequest.cs +++ b/MinecraftClient/Protocol/ProxiedWebRequest.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Linq; using System.Globalization; using System.IO; +using System.Linq; using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; @@ -193,7 +193,7 @@ namespace MinecraftClient.Protocol response.Body = rbody ?? ""; response.StatusCode = statusCode; response.Headers = headers; - + try { stream.Close(); diff --git a/MinecraftClient/Proxy/ProxyHandler.cs b/MinecraftClient/Proxy/ProxyHandler.cs index fca377bd..1cd55e40 100644 --- a/MinecraftClient/Proxy/ProxyHandler.cs +++ b/MinecraftClient/Proxy/ProxyHandler.cs @@ -82,7 +82,7 @@ namespace MinecraftClient.Proxy case Configs.ProxyType.SOCKS5: innerProxytype = ProxyType.Socks5; break; } - if (!string.IsNullOrWhiteSpace(Config.Username)&& !string.IsNullOrWhiteSpace(Config.Password)) + if (!string.IsNullOrWhiteSpace(Config.Username) && !string.IsNullOrWhiteSpace(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); diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index 5dd4bfbb..c27a20e8 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1,1818 +1,1862 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace MinecraftClient { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // 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", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class ConfigComments { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal ConfigComments() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.Resources.ConfigComments.ConfigComments", typeof(ConfigComments).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)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to can be used in some other fields as %yourvar% - ///%username% and %serverip% are reserved variables.. - /// - internal static string AppVars_Variables { - get { - return ResourceManager.GetString("AppVars.Variables", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to =============================== # - /// Minecraft Console Client Bots # - ///=============================== #. - /// - internal static string ChatBot { - get { - return ResourceManager.GetString("ChatBot", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Get alerted when specified words are detected in chat - ///Useful for moderating your server or detecting when someone is talking to you. - /// - internal static string ChatBot_Alerts { - get { - return ResourceManager.GetString("ChatBot.Alerts", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Play a beep sound when a word is detected in addition to highlighting.. - /// - internal static string ChatBot_Alerts_Beep_Enabled { - get { - return ResourceManager.GetString("ChatBot.Alerts.Beep_Enabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to List of words/strings to NOT alert you on.. - /// - internal static string ChatBot_Alerts_Excludes { - get { - return ResourceManager.GetString("ChatBot.Alerts.Excludes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The name of a file where alers logs will be written.. - /// - internal static string ChatBot_Alerts_Log_File { - get { - return ResourceManager.GetString("ChatBot.Alerts.Log_File", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Log alerts info a file.. - /// - internal static string ChatBot_Alerts_Log_To_File { - get { - return ResourceManager.GetString("ChatBot.Alerts.Log_To_File", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to List of words/strings to alert you on.. - /// - internal static string ChatBot_Alerts_Matches { - get { - return ResourceManager.GetString("ChatBot.Alerts.Matches", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Trigger alerts when it rains and when it stops.. - /// - internal static string ChatBot_Alerts_Trigger_By_Rain { - get { - return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Rain", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Triggers alerts at the beginning and end of thunderstorms.. - /// - internal static string ChatBot_Alerts_Trigger_By_Thunderstorm { - get { - return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Thunderstorm", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Triggers an alert after receiving a specified keyword.. - /// - internal static string ChatBot_Alerts_Trigger_By_Words { - get { - return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Words", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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). - /// - internal static string ChatBot_AntiAfk { - get { - return ResourceManager.GetString("ChatBot.AntiAfk", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Command to send to the server.. - /// - internal static string ChatBot_AntiAfk_Command { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Command", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The time interval for execution. (in seconds). - /// - internal static string ChatBot_AntiAfk_Delay { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to sneak when sending the command.. - /// - internal static string ChatBot_AntiAfk_Use_Sneak { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Use_Sneak", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use terrain handling to enable the bot to move around.. - /// - internal static string ChatBot_AntiAfk_Use_Terrain_Handling { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Use_Terrain_Handling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be). - /// - internal static string ChatBot_AntiAfk_Walk_Range { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Range", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How many times can the bot fail trying to move before using the command method.. - /// - internal static string ChatBot_AntiAfk_Walk_Retries { - get { - return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Retries", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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!. - /// - internal static string ChatBot_AutoAttack { - get { - return ResourceManager.GetString("ChatBot.AutoAttack", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allow attacking hostile mobs.. - /// - internal static string ChatBot_AutoAttack_Attack_Hostile { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Hostile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allow attacking passive mobs.. - /// - internal static string ChatBot_AutoAttack_Attack_Passive { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Passive", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long to wait between each attack. Set "Custom = false" to let MCC calculate it.. - /// - internal static string ChatBot_AutoAttack_Cooldown_Time { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Cooldown_Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to All entity types can be found here: https://mccteam.github.io/r/entity/#L15. - /// - internal static string ChatBot_AutoAttack_Entites_List { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Entites_List", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack).. - /// - internal static string ChatBot_AutoAttack_Interaction { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Interaction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wether to treat the entities list as a "whitelist" or as a "blacklist".. - /// - internal static string ChatBot_AutoAttack_List_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.List_Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "single" or "multi". single target one mob per attack. multi target all mobs in range per attack. - /// - internal static string ChatBot_AutoAttack_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "health" or "distance". Only needed when using single mode. - /// - internal static string ChatBot_AutoAttack_Priority { - get { - return ResourceManager.GetString("ChatBot.AutoAttack.Priority", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically craft items in your inventory - ///See https://mccteam.github.io/g/bots/#auto-craft 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. - /// - internal static string ChatBot_AutoCraft { - get { - return ResourceManager.GetString("ChatBot.AutoCraft", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Location of the crafting table if you intended to use it. Terrain and movements must be enabled.. - /// - internal static string ChatBot_AutoCraft_CraftingTable { - get { - return ResourceManager.GetString("ChatBot.AutoCraft.CraftingTable", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to What to do on crafting failure, "abort" or "wait".. - /// - internal static string ChatBot_AutoCraft_OnFailure { - get { - return ResourceManager.GetString("ChatBot.AutoCraft.OnFailure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Recipes.Name: The name can be whatever you like and it is used to represent the recipe. - ///Recipes.Type: crafting table type: "player" or "table" - ///Recipes.Result: the resulting item - ///Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots. - ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12. - /// - internal static string ChatBot_AutoCraft_Recipes { - get { - return ResourceManager.GetString("ChatBot.AutoCraft.Recipes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Auto-digging blocks. - ///You need to enable Terrain Handling to use this bot - ///You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig. - ///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. - ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15. - /// - internal static string ChatBot_AutoDig { - get { - return ResourceManager.GetString("ChatBot.AutoDig", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start.. - /// - internal static string ChatBot_AutoDig_Auto_Start_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Auto_Start_Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically switch to the appropriate tool.. - /// - internal static string ChatBot_AutoDig_Auto_Tool_Switch { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Auto_Tool_Switch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mining a block for more than "Dig_Timeout" seconds will be considered a timeout.. - /// - internal static string ChatBot_AutoDig_Dig_Timeout { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Dig_Timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to drop the current tool when its durability is too low.. - /// - internal static string ChatBot_AutoDig_Drop_Low_Durability_Tools { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Drop_Low_Durability_Tools", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Will not use tools with less durability than this. Set to zero to disable this feature.. - /// - internal static string ChatBot_AutoDig_Durability_Limit { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Durability_Limit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wether to treat the blocks list as a "whitelist" or as a "blacklist".. - /// - internal static string ChatBot_AutoDig_List_Type { - get { - return ResourceManager.GetString("ChatBot.AutoDig.List_Type", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list.. - /// - internal static string ChatBot_AutoDig_Location_Order { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Location_Order", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The position of the blocks when using "fixedpos" or "both" mode.. - /// - internal static string ChatBot_AutoDig_Locations { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Locations", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to output logs when digging blocks.. - /// - internal static string ChatBot_AutoDig_Log_Block_Dig { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Log_Block_Dig", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "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.. - /// - internal static string ChatBot_AutoDig_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoDig.Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically drop items in inventory - ///You need to enable Inventory Handling to use this bot - ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12. - /// - internal static string ChatBot_AutoDrop { - get { - return ResourceManager.GetString("ChatBot.AutoDrop", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list. - /// - internal static string ChatBot_AutoDrop_Mode { - get { - return ResourceManager.GetString("ChatBot.AutoDrop.Mode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically eat food when your Hunger value is low - ///You need to enable Inventory Handling to use this bot. - /// - internal static string ChatBot_AutoEat { - get { - return ResourceManager.GetString("ChatBot.AutoEat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically catch fish using a fishing rod - ///Guide: https://mccteam.github.io/g/bots/#auto-fishing - ///You can use "/fish" to control the bot manually. - ////!\ Make sure server rules allow automated farming before using this bot. - /// - internal static string ChatBot_AutoFishing { - get { - return ResourceManager.GetString("ChatBot.AutoFishing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Keep it as false if you have not changed it before.. - /// - internal static string ChatBot_AutoFishing_Antidespawn { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Antidespawn", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Switch to a new rod from inventory after the current rod is unavailable.. - /// - internal static string ChatBot_AutoFishing_Auto_Rod_Switch { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Rod_Switch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to start fishing automatically after entering a world.. - /// - internal static string ChatBot_AutoFishing_Auto_Start { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Start", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How soon to re-cast after successful fishing.. - /// - internal static string ChatBot_AutoFishing_Cast_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Cast_Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.. - /// - internal static string ChatBot_AutoFishing_Durability_Limit { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Durability_Limit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This allows the player to change position/facing after each fish caught.. - /// - internal static string ChatBot_AutoFishing_Enable_Move { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Enable_Move", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long after entering the game to start fishing (seconds).. - /// - internal static string ChatBot_AutoFishing_Fishing_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fishing timeout (seconds). Timeout will trigger a re-cast.. - /// - internal static string ChatBot_AutoFishing_Fishing_Timeout { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish.. - /// - internal static string ChatBot_AutoFishing_Hook_Threshold { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Hook_Threshold", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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.. - /// - internal static string ChatBot_AutoFishing_Log_Fish_Bobber { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Log_Fish_Bobber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use the mainhand or the offhand to hold the rod.. - /// - internal static string ChatBot_AutoFishing_Mainhand { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Mainhand", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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.. - /// - internal static string ChatBot_AutoFishing_Movements { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Movements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hook movement in the X and Z axis less than this value will be considered stationary.. - /// - internal static string ChatBot_AutoFishing_Stationary_Threshold { - get { - return ResourceManager.GetString("ChatBot.AutoFishing.Stationary_Threshold", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically relog when disconnected by server, for example because the server is restating - ////!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks. - /// - internal static string ChatBot_AutoRelog { - get { - return ResourceManager.GetString("ChatBot.AutoRelog", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The delay time before joining the server. (in seconds). - /// - internal static string ChatBot_AutoRelog_Delay { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to When set to true, autorelog will reconnect regardless of kick messages.. - /// - internal static string ChatBot_AutoRelog_Ignore_Kick_Message { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Ignore_Kick_Message", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If the kickout message matches any of the strings, then autorelog will be triggered.. - /// - internal static string ChatBot_AutoRelog_Kick_Messages { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Kick_Messages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Retries when failing to relog to the server. use -1 for unlimited retries.. - /// - internal static string ChatBot_AutoRelog_Retries { - get { - return ResourceManager.GetString("ChatBot.AutoRelog.Retries", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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. - /// - internal static string ChatBot_AutoRespond { - get { - return ResourceManager.GetString("ChatBot.AutoRespond", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work). - /// - internal static string ChatBot_AutoRespond_Match_Colors { - get { - return ResourceManager.GetString("ChatBot.AutoRespond.Match_Colors", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logs chat messages in a file on disk.. - /// - internal static string ChatBot_ChatLog { - get { - return ResourceManager.GetString("ChatBot.ChatLog", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This bot allows you to send and recieve messages and commands via a Discord channel. - ///For Setup you can either use the documentation or read here (Documentation has images). - ///Documentation: https://mccteam.github.io/g/bots/#discord-bridge - ///Setup: - ///First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA . - ////!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [rest of string was truncated]";. - /// - internal static string ChatBot_DiscordBridge { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The ID of a channel where you want to interact with the MCC using the bot.. - /// - internal static string ChatBot_DiscordBridge_ChannelId { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.ChannelId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message formats - ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! - ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html. - /// - internal static string ChatBot_DiscordBridge_Formats { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.Formats", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The ID of a server/guild where you have invited the bot to.. - /// - internal static string ChatBot_DiscordBridge_GuildId { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.GuildId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).. - /// - internal static string ChatBot_DiscordBridge_MessageSendTimeout { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.MessageSendTimeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A list of IDs of people you want to be able to interact with the MCC using the bot.. - /// - internal static string ChatBot_DiscordBridge_OwnersIds { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.OwnersIds", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Your Discord Bot token.. - /// - internal static string ChatBot_DiscordBridge_Token { - get { - return ResourceManager.GetString("ChatBot.DiscordBridge.Token", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically farms crops for you (plants, breaks and bonemeals them). - ///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat. - ///Usage: "/farmer start" command and "/farmer stop" command. - ///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes. - ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [rest of string was truncated]";. - /// - internal static string ChatBot_Farmer { - get { - return ResourceManager.GetString("ChatBot.Farmer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delay between tasks in seconds (Minimum 1 second). - /// - internal static string ChatBot_Farmer_Delay_Between_Tasks { - get { - return ResourceManager.GetString("ChatBot.Farmer.Delay_Between_Tasks", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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, - /// [rest of string was truncated]";. - /// - internal static string ChatBot_FollowPlayer { - get { - return ResourceManager.GetString("ChatBot.FollowPlayer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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). - /// - internal static string ChatBot_FollowPlayer_Stop_At_Distance { - get { - return ResourceManager.GetString("ChatBot.FollowPlayer.Stop_At_Distance", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow). - /// - internal static string ChatBot_FollowPlayer_Update_Limit { - get { - return ResourceManager.GetString("ChatBot.FollowPlayer.Update_Limit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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 <bot username> start - ////!\ This bot may get a bit spammy if many players are interacting with it. - /// - internal static string ChatBot_HangmanGame { - get { - return ResourceManager.GetString("ChatBot.HangmanGame", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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. - /// - internal static string ChatBot_Mailer { - get { - return ResourceManager.GetString("ChatBot.Mailer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) - ///This is useful for solving captchas which use maps - ///The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled. - ///NOTE: - ///If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console. - ////!\ Make sure server rules allow bots to be used on the server, or you risk being punished.. - /// - internal static string ChatBot_Map { - get { - return ResourceManager.GetString("ChatBot.Map", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Automatically render the map once it is received or updated from/by the server. - /// - internal static string ChatBot_Map_Auto_Render_On_Update { - get { - return ResourceManager.GetString("ChatBot.Map.Auto_Render_On_Update", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delete all rendered maps on unload/reload or when you launch the MCC again.. - /// - internal static string ChatBot_Map_Delete_All_On_Unload { - get { - return ResourceManager.GetString("ChatBot.Map.Delete_All_On_Unload", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Get a notification when you have gotten a map from the server for the first time. - /// - internal static string ChatBot_Map_Notify_On_First_Update { - get { - return ResourceManager.GetString("ChatBot.Map.Notify_On_First_Update", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord.. - /// - internal static string ChatBot_Map_Rasize_Rendered_Image { - get { - return ResourceManager.GetString("ChatBot.Map.Rasize_Rendered_Image", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to render the map in the console.. - /// - internal static string ChatBot_Map_Render_In_Console { - get { - return ResourceManager.GetString("ChatBot.Map.Render_In_Console", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The size that a rendered image should be resized to, in pixels (eg. 512).. - /// - internal static string ChatBot_Map_Resize_To { - get { - return ResourceManager.GetString("ChatBot.Map.Resize_To", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge).. - /// - internal static string ChatBot_Map_Save_To_File { - get { - return ResourceManager.GetString("ChatBot.Map.Save_To_File", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) - ///You need to enable Save_To_File in order for this to work. - ///We also recommend turning on resizing.. - /// - internal static string ChatBot_Map_Send_Rendered_To_Bridges { - get { - return ResourceManager.GetString("ChatBot.Map.Send_Rendered_To_Bridges", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Log the list of players periodically into a textual file.. - /// - internal static string ChatBot_PlayerListLogger { - get { - return ResourceManager.GetString("ChatBot.PlayerListLogger", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to (In seconds). - /// - internal static string ChatBot_PlayerListLogger_Delay { - get { - return ResourceManager.GetString("ChatBot.PlayerListLogger.Delay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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. - /// - internal static string ChatBot_RemoteControl { - get { - return ResourceManager.GetString("ChatBot.RemoteControl", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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!. - /// - internal static string ChatBot_ReplayCapture { - get { - return ResourceManager.GetString("ChatBot.ReplayCapture", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long should replay file be auto-saved, in seconds. Use -1 to disable.. - /// - internal static string ChatBot_ReplayCapture_Backup_Interval { - get { - return ResourceManager.GetString("ChatBot.ReplayCapture.Backup_Interval", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Schedule commands and scripts to launch on various events such as server join, date/time or time interval - ///See https://mccteam.github.io/g/bots/#script-scheduler for more info. - /// - internal static string ChatBot_ScriptScheduler { - get { - return ResourceManager.GetString("ChatBot.ScriptScheduler", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. - ////!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel. - ///----------------------------------------------------------- - ///Setup: - ///First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather - ///Click on "Start" button and re [rest of string was truncated]";. - /// - internal static string ChatBot_TelegramBridge { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.. - /// - internal static string ChatBot_TelegramBridge_Authorized_Chat_Ids { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.Authorized_Chat_Ids", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An ID of a channel where you want to interact with the MCC using the bot.. - /// - internal static string ChatBot_TelegramBridge_ChannelId { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.ChannelId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message formats - ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! - ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html. - /// - internal static string ChatBot_TelegramBridge_Formats { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.Formats", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).. - /// - internal static string ChatBot_TelegramBridge_MessageSendTimeout { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.MessageSendTimeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Your Telegram Bot token.. - /// - internal static string ChatBot_TelegramBridge_Token { - get { - return ResourceManager.GetString("ChatBot.TelegramBridge.Token", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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 https://mccteam.github.io/g/conf/#chat-format-section. - /// - internal static string ChatFormat { - get { - return ResourceManager.GetString("ChatFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MCC support for common message formats. Set "false" to avoid conflicts with custom formats.. - /// - internal static string ChatFormat_Builtins { - get { - return ResourceManager.GetString("ChatFormat.Builtins", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to use the custom regular expressions below for detection.. - /// - internal static string ChatFormat_UserDefined { - get { - return ResourceManager.GetString("ChatFormat.UserDefined", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Startup Config File - ///Please do not record extraneous data in this file as it will be overwritten by MCC. - /// - ///New to Minecraft Console Client? Check out this document: https://mccteam.github.io/g/conf.html - ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download. - /// - internal static string Head { - get { - return ResourceManager.GetString("Head", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This setting affects only the messages in the console.. - /// - internal static string Logging { - get { - return ResourceManager.GetString("Logging", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Regex for filtering chat message.. - /// - internal static string Logging_ChatFilter { - get { - return ResourceManager.GetString("Logging.ChatFilter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show server chat messages.. - /// - internal static string Logging_ChatMessages { - get { - return ResourceManager.GetString("Logging.ChatMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Regex for filtering debug message.. - /// - internal static string Logging_DebugFilter { - get { - return ResourceManager.GetString("Logging.DebugFilter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Please enable this before submitting bug reports. Thanks!. - /// - internal static string Logging_DebugMessages { - get { - return ResourceManager.GetString("Logging.DebugMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show error messages.. - /// - internal static string Logging_ErrorMessages { - get { - return ResourceManager.GetString("Logging.ErrorMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex.. - /// - internal static string Logging_FilterMode { - get { - return ResourceManager.GetString("Logging.FilterMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Informative messages. (i.e Most of the message from MCC). - /// - internal static string Logging_InfoMessages { - get { - return ResourceManager.GetString("Logging.InfoMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Log file name.. - /// - internal static string Logging_LogFile { - get { - return ResourceManager.GetString("Logging.LogFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Write log messages to file.. - /// - internal static string Logging_LogToFile { - get { - return ResourceManager.GetString("Logging.LogToFile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prepend timestamp to messages in log file.. - /// - internal static string Logging_PrependTimestamp { - get { - return ResourceManager.GetString("Logging.PrependTimestamp", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Keep color codes in the saved text.(look like "§b"). - /// - internal static string Logging_SaveColorCodes { - get { - return ResourceManager.GetString("Logging.SaveColorCodes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show warning messages.. - /// - internal static string Logging_WarningMessages { - get { - return ResourceManager.GetString("Logging.WarningMessages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Make sure you understand what each setting does before changing anything!. - /// - internal static string Main_Advanced { - get { - return ResourceManager.GetString("Main.Advanced", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to AccountList: It allows a fast account switching without directly using the credentials - ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1". - /// - internal static string Main_Advanced_account_list { - get { - return ResourceManager.GetString("Main.Advanced.account_list", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle auto respawn if client player was dead (make sure your spawn point is safe).. - /// - internal static string Main_Advanced_auto_respawn { - get { - return ResourceManager.GetString("Main.Advanced.auto_respawn", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Set the owner of the bot. /!\ Server admins can impersonate owners!. - /// - internal static string Main_Advanced_bot_owners { - get { - return ResourceManager.GetString("Main.Advanced.bot_owners", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server.. - /// - internal static string Main_Advanced_brand_info { - get { - return ResourceManager.GetString("Main.Advanced.brand_info", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Leave empty for no logfile.. - /// - internal static string Main_Advanced_chatbot_log_file { - get { - return ResourceManager.GetString("Main.Advanced.chatbot_log_file", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If turned off, the emoji will be replaced with a simpler character (for /chunk status).. - /// - internal static string Main_Advanced_enable_emoji { - get { - return ResourceManager.GetString("Main.Advanced.enable_emoji", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle entity handling.. - /// - internal static string Main_Advanced_entity_handling { - get { - return ResourceManager.GetString("Main.Advanced.entity_handling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to exit directly when an error occurs, for using MCC in non-interactive scripts.. - /// - internal static string Main_Advanced_exit_on_failure { - get { - return ResourceManager.GetString("Main.Advanced.exit_on_failure", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "none", "slash"(/) or "backslash"(\).. - /// - internal static string Main_Advanced_internal_cmd_char { - get { - return ResourceManager.GetString("Main.Advanced.internal_cmd_char", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Toggle inventory handling.. - /// - internal static string Main_Advanced_inventory_handling { - get { - return ResourceManager.GetString("Main.Advanced.inventory_handling", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html. - /// - internal static string Main_Advanced_language { - get { - return ResourceManager.GetString("Main.Advanced.language", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Load translations applied to MCC when available, turn it off to use English only.. - /// - internal static string Main_Advanced_LoadMccTrans { - get { - return ResourceManager.GetString("Main.Advanced.LoadMccTrans", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "auto", "no" or "force". Force-enabling only works for MC 1.13+.. - /// - internal static string Main_Advanced_mc_forge { - get { - return ResourceManager.GetString("Main.Advanced.mc_forge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "auto" or "1.X.X" values. Allows to skip server info retrieval.. - /// - internal static string Main_Advanced_mc_version { - get { - return ResourceManager.GetString("Main.Advanced.mc_version", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Controls the minimum interval (in seconds) between sending each message to the server.. - /// - internal static string Main_Advanced_message_cooldown { - get { - return ResourceManager.GetString("Main.Advanced.message_cooldown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable support for joining Minecraft Realms worlds.. - /// - internal static string Main_Advanced_minecraft_realms { - get { - return ResourceManager.GetString("Main.Advanced.minecraft_realms", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The minimum height to use when calculating the image size from the height of the terminal.. - /// - internal static string Main_Advanced_MinTerminalHeight { - get { - return ResourceManager.GetString("Main.Advanced.MinTerminalHeight", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The minimum width used when calculating the image size from the width of the terminal.. - /// - internal static string Main_Advanced_MinTerminalWidth { - get { - return ResourceManager.GetString("Main.Advanced.MinTerminalWidth", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enable head movement while walking to avoid anti-cheat triggers.. - /// - internal static string Main_Advanced_move_head_while_walking { - get { - return ResourceManager.GetString("Main.Advanced.move_head_while_walking", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A movement speed higher than 2 may be considered cheating.. - /// - internal static string Main_Advanced_movement_speed { - get { - return ResourceManager.GetString("Main.Advanced.movement_speed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only works on Windows XP-8 or Windows 10 with old console.. - /// - internal static string Main_Advanced_player_head_icon { - get { - return ResourceManager.GetString("Main.Advanced.player_head_icon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to For remote control of the bot.. - /// - internal static string Main_Advanced_private_msgs_cmd_name { - get { - return ResourceManager.GetString("Main.Advanced.private_msgs_cmd_name", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How to retain profile key. Use "none", "memory" or "disk".. - /// - internal static string Main_Advanced_profilekey_cache { - get { - return ResourceManager.GetString("Main.Advanced.profilekey_cache", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "no", "fast" (5s timeout), or "yes". Required for joining some servers.. - /// - internal static string Main_Advanced_resolve_srv_records { - get { - return ResourceManager.GetString("Main.Advanced.resolve_srv_records", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cache compiled scripts for faster load on low-end devices.. - /// - internal static string Main_Advanced_script_cache { - get { - return ResourceManager.GetString("Main.Advanced.script_cache", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ServerList: It allows an easier and faster server switching with short aliases instead of full server IP - ///Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. - ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2". - /// - internal static string Main_Advanced_server_list { - get { - return ResourceManager.GetString("Main.Advanced.server_list", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to How to retain session tokens. Use "none", "memory" or "disk".. - /// - internal static string Main_Advanced_session_cache { - get { - return ResourceManager.GetString("Main.Advanced.session_cache", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Decode links embedded in chat messages and show them in console.. - /// - internal static string Main_Advanced_show_chat_links { - get { - return ResourceManager.GetString("Main.Advanced.show_chat_links", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show inventory layout as ASCII art in inventory command.. - /// - internal static string Main_Advanced_show_inventory_layout { - get { - return ResourceManager.GetString("Main.Advanced.show_inventory_layout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to System messages for server ops.. - /// - internal static string Main_Advanced_show_system_messages { - get { - return ResourceManager.GetString("Main.Advanced.show_system_messages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Messages displayed above xp bar, set this to false in case of xp bar spam.. - /// - internal static string Main_Advanced_show_xpbar_messages { - get { - return ResourceManager.GetString("Main.Advanced.show_xpbar_messages", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers.. - /// - internal static string Main_Advanced_temporary_fix_badpacket { - get { - return ResourceManager.GetString("Main.Advanced.temporary_fix_badpacket", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log.. - /// - internal static string Main_Advanced_TerminalColorDepth { - get { - return ResourceManager.GetString("Main.Advanced.TerminalColorDepth", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. - /// - internal static string Main_Advanced_terrain_and_movements { - get { - return ResourceManager.GetString("Main.Advanced.terrain_and_movements", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Customize the TCP connection timeout with the server. (in seconds). - /// - internal static string Main_Advanced_timeout { - get { - return ResourceManager.GetString("Main.Advanced.timeout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prepend timestamps to chat messages.. - /// - internal static string Main_Advanced_timestamps { - get { - return ResourceManager.GetString("Main.Advanced.timestamps", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.. - /// - internal static string Main_General_account { - get { - return ResourceManager.GetString("Main.General.account", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 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). - /// - internal static string Main_General_login { - get { - return ResourceManager.GetString("Main.General.login", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once.. - /// - internal static string Main_General_method { - get { - return ResourceManager.GetString("Main.General.method", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Account type: "mojang" OR "microsoft". Also affects interactive login in console.. - /// - internal static string Main_General_server_info { - get { - return ResourceManager.GetString("Main.General.server_info", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Settings below are sent to the server and only affect server-side things like your skin.. - /// - internal static string MCSettings { - get { - return ResourceManager.GetString("MCSettings", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Allows disabling chat colors server-side.. - /// - internal static string MCSettings_ChatColors { - get { - return ResourceManager.GetString("MCSettings.ChatColors", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use "enabled", "commands", or "disabled". Allows to mute yourself.... - /// - internal static string MCSettings_ChatMode { - get { - return ResourceManager.GetString("MCSettings.ChatMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult".. - /// - internal static string MCSettings_Difficulty { - get { - return ResourceManager.GetString("MCSettings.Difficulty", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to If disabled, settings below are not sent to the server.. - /// - internal static string MCSettings_Enabled { - get { - return ResourceManager.GetString("MCSettings.Enabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use any language implemented in Minecraft.. - /// - internal static string MCSettings_Locale { - get { - return ResourceManager.GetString("MCSettings.Locale", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to MC 1.9+ main hand. "left" or "right".. - /// - internal static string MCSettings_MainHand { - get { - return ResourceManager.GetString("MCSettings.MainHand", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Value range: [0 - 255].. - /// - internal static string MCSettings_RenderDistance { - get { - return ResourceManager.GetString("MCSettings.RenderDistance", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connect to a server via a proxy instead of connecting directly - ///If Mojang session services are blocked on your network, set Enabled_Login=true to login using proxy. - ///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. - ////!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!. - /// - internal static string Proxy { - get { - return ResourceManager.GetString("Proxy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to connect to the game server through a proxy.. - /// - internal static string Proxy_Enabled_Ingame { - get { - return ResourceManager.GetString("Proxy.Enabled_Ingame", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to connect to the login server through a proxy.. - /// - internal static string Proxy_Enabled_Login { - get { - return ResourceManager.GetString("Proxy.Enabled_Login", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to download MCC updates via proxy.. - /// - internal static string Proxy_Enabled_Update { - get { - return ResourceManager.GetString("Proxy.Enabled_Update", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only required for password-protected proxies.. - /// - internal static string Proxy_Password { - get { - return ResourceManager.GetString("Proxy.Password", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5".. - /// - internal static string Proxy_Proxy_Type { - get { - return ResourceManager.GetString("Proxy.Proxy_Type", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Proxy server must allow HTTPS for login, and non-443 ports for playing.. - /// - internal static string Proxy_Server { - get { - return ResourceManager.GetString("Proxy.Server", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only required for password-protected proxies.. - /// - internal static string Proxy_Username { - get { - return ResourceManager.GetString("Proxy.Username", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chat signature related settings (affects minecraft 1.19+). - /// - internal static string Signature { - get { - return ResourceManager.GetString("Signature", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true". - /// - internal static string Signature_LoginWithSecureProfile { - get { - return ResourceManager.GetString("Signature.LoginWithSecureProfile", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use red    color block to mark chat without legitimate signature. - /// - internal static string Signature_MarkIllegallySignedMsg { - get { - return ResourceManager.GetString("Signature.MarkIllegallySignedMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use green  color block to mark chat with legitimate signatures. - /// - internal static string Signature_MarkLegallySignedMsg { - get { - return ResourceManager.GetString("Signature.MarkLegallySignedMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use yellow color block to mark chat that have been modified by the server.. - /// - internal static string Signature_MarkModifiedMsg { - get { - return ResourceManager.GetString("Signature.MarkModifiedMsg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use gray   color block to mark system message (always without signature). - /// - internal static string Signature_MarkSystemMessage { - get { - return ResourceManager.GetString("Signature.MarkSystemMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to display chat and messages in commands without legal signatures. - /// - internal static string Signature_ShowIllegalSignedChat { - get { - return ResourceManager.GetString("Signature.ShowIllegalSignedChat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Set to true to display messages modified by the server, false to display the original signed messages. - /// - internal static string Signature_ShowModifiedChat { - get { - return ResourceManager.GetString("Signature.ShowModifiedChat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to sign the chat send from MCC. - /// - internal static string Signature_SignChat { - get { - return ResourceManager.GetString("Signature.SignChat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me". - /// - internal static string Signature_SignMessageInCommand { - get { - return ResourceManager.GetString("Signature.SignMessageInCommand", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MinecraftClient { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // 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", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ConfigComments { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ConfigComments() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.Resources.ConfigComments.ConfigComments", typeof(ConfigComments).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)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to can be used in some other fields as %yourvar% + ///%username% and %serverip% are reserved variables.. + /// + internal static string AppVars_Variables { + get { + return ResourceManager.GetString("AppVars.Variables", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to =============================== # + /// Minecraft Console Client Bots # + ///=============================== #. + /// + internal static string ChatBot { + get { + return ResourceManager.GetString("ChatBot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Get alerted when specified words are detected in chat + ///Useful for moderating your server or detecting when someone is talking to you. + /// + internal static string ChatBot_Alerts { + get { + return ResourceManager.GetString("ChatBot.Alerts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Play a beep sound when a word is detected in addition to highlighting.. + /// + internal static string ChatBot_Alerts_Beep_Enabled { + get { + return ResourceManager.GetString("ChatBot.Alerts.Beep_Enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of words/strings to NOT alert you on.. + /// + internal static string ChatBot_Alerts_Excludes { + get { + return ResourceManager.GetString("ChatBot.Alerts.Excludes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name of a file where alers logs will be written.. + /// + internal static string ChatBot_Alerts_Log_File { + get { + return ResourceManager.GetString("ChatBot.Alerts.Log_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log alerts info a file.. + /// + internal static string ChatBot_Alerts_Log_To_File { + get { + return ResourceManager.GetString("ChatBot.Alerts.Log_To_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of words/strings to alert you on.. + /// + internal static string ChatBot_Alerts_Matches { + get { + return ResourceManager.GetString("ChatBot.Alerts.Matches", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trigger alerts when it rains and when it stops.. + /// + internal static string ChatBot_Alerts_Trigger_By_Rain { + get { + return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Rain", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Triggers alerts at the beginning and end of thunderstorms.. + /// + internal static string ChatBot_Alerts_Trigger_By_Thunderstorm { + get { + return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Thunderstorm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Triggers an alert after receiving a specified keyword.. + /// + internal static string ChatBot_Alerts_Trigger_By_Words { + get { + return ResourceManager.GetString("ChatBot.Alerts.Trigger_By_Words", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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). + /// + internal static string ChatBot_AntiAfk { + get { + return ResourceManager.GetString("ChatBot.AntiAfk", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Command to send to the server.. + /// + internal static string ChatBot_AntiAfk_Command { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Command", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The time interval for execution. (in seconds). + /// + internal static string ChatBot_AntiAfk_Delay { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to sneak when sending the command.. + /// + internal static string ChatBot_AntiAfk_Use_Sneak { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Use_Sneak", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use terrain handling to enable the bot to move around.. + /// + internal static string ChatBot_AntiAfk_Use_Terrain_Handling { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Use_Terrain_Handling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be). + /// + internal static string ChatBot_AntiAfk_Walk_Range { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Range", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How many times can the bot fail trying to move before using the command method.. + /// + internal static string ChatBot_AntiAfk_Walk_Retries { + get { + return ResourceManager.GetString("ChatBot.AntiAfk.Walk_Retries", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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!. + /// + internal static string ChatBot_AutoAttack { + get { + return ResourceManager.GetString("ChatBot.AutoAttack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow attacking hostile mobs.. + /// + internal static string ChatBot_AutoAttack_Attack_Hostile { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Hostile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow attacking passive mobs.. + /// + internal static string ChatBot_AutoAttack_Attack_Passive { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Passive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long to wait between each attack. Set "Custom = false" to let MCC calculate it.. + /// + internal static string ChatBot_AutoAttack_Cooldown_Time { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Cooldown_Time", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All entity types can be found here: https://mccteam.github.io/r/entity/#L15. + /// + internal static string ChatBot_AutoAttack_Entites_List { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Entites_List", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack).. + /// + internal static string ChatBot_AutoAttack_Interaction { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Interaction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wether to treat the entities list as a "whitelist" or as a "blacklist".. + /// + internal static string ChatBot_AutoAttack_List_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.List_Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "single" or "multi". single target one mob per attack. multi target all mobs in range per attack. + /// + internal static string ChatBot_AutoAttack_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "health" or "distance". Only needed when using single mode. + /// + internal static string ChatBot_AutoAttack_Priority { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Priority", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically craft items in your inventory + ///See https://mccteam.github.io/g/bots/#auto-craft 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. + /// + internal static string ChatBot_AutoCraft { + get { + return ResourceManager.GetString("ChatBot.AutoCraft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Location of the crafting table if you intended to use it. Terrain and movements must be enabled.. + /// + internal static string ChatBot_AutoCraft_CraftingTable { + get { + return ResourceManager.GetString("ChatBot.AutoCraft.CraftingTable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to What to do on crafting failure, "abort" or "wait".. + /// + internal static string ChatBot_AutoCraft_OnFailure { + get { + return ResourceManager.GetString("ChatBot.AutoCraft.OnFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Recipes.Name: The name can be whatever you like and it is used to represent the recipe. + ///Recipes.Type: crafting table type: "player" or "table" + ///Recipes.Result: the resulting item + ///Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots. + ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12. + /// + internal static string ChatBot_AutoCraft_Recipes { + get { + return ResourceManager.GetString("ChatBot.AutoCraft.Recipes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auto-digging blocks. + ///You need to enable Terrain Handling to use this bot + ///You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig. + ///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. + ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15. + /// + internal static string ChatBot_AutoDig { + get { + return ResourceManager.GetString("ChatBot.AutoDig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start.. + /// + internal static string ChatBot_AutoDig_Auto_Start_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Auto_Start_Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically switch to the appropriate tool.. + /// + internal static string ChatBot_AutoDig_Auto_Tool_Switch { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Auto_Tool_Switch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mining a block for more than "Dig_Timeout" seconds will be considered a timeout.. + /// + internal static string ChatBot_AutoDig_Dig_Timeout { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Dig_Timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to drop the current tool when its durability is too low.. + /// + internal static string ChatBot_AutoDig_Drop_Low_Durability_Tools { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Drop_Low_Durability_Tools", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Will not use tools with less durability than this. Set to zero to disable this feature.. + /// + internal static string ChatBot_AutoDig_Durability_Limit { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Durability_Limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wether to treat the blocks list as a "whitelist" or as a "blacklist".. + /// + internal static string ChatBot_AutoDig_List_Type { + get { + return ResourceManager.GetString("ChatBot.AutoDig.List_Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list.. + /// + internal static string ChatBot_AutoDig_Location_Order { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Location_Order", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The position of the blocks when using "fixedpos" or "both" mode.. + /// + internal static string ChatBot_AutoDig_Locations { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Locations", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to output logs when digging blocks.. + /// + internal static string ChatBot_AutoDig_Log_Block_Dig { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Log_Block_Dig", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "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.. + /// + internal static string ChatBot_AutoDig_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoDig.Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically drop items in inventory + ///You need to enable Inventory Handling to use this bot + ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12. + /// + internal static string ChatBot_AutoDrop { + get { + return ResourceManager.GetString("ChatBot.AutoDrop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list. + /// + internal static string ChatBot_AutoDrop_Mode { + get { + return ResourceManager.GetString("ChatBot.AutoDrop.Mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically eat food when your Hunger value is low + ///You need to enable Inventory Handling to use this bot. + /// + internal static string ChatBot_AutoEat { + get { + return ResourceManager.GetString("ChatBot.AutoEat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically catch fish using a fishing rod + ///Guide: https://mccteam.github.io/g/bots/#auto-fishing + ///You can use "/fish" to control the bot manually. + ////!\ Make sure server rules allow automated farming before using this bot. + /// + internal static string ChatBot_AutoFishing { + get { + return ResourceManager.GetString("ChatBot.AutoFishing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep it as false if you have not changed it before.. + /// + internal static string ChatBot_AutoFishing_Antidespawn { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Antidespawn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to a new rod from inventory after the current rod is unavailable.. + /// + internal static string ChatBot_AutoFishing_Auto_Rod_Switch { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Rod_Switch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to start fishing automatically after entering a world.. + /// + internal static string ChatBot_AutoFishing_Auto_Start { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Auto_Start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How soon to re-cast after successful fishing.. + /// + internal static string ChatBot_AutoFishing_Cast_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Cast_Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.. + /// + internal static string ChatBot_AutoFishing_Durability_Limit { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Durability_Limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This allows the player to change position/facing after each fish caught.. + /// + internal static string ChatBot_AutoFishing_Enable_Move { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Enable_Move", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long after entering the game to start fishing (seconds).. + /// + internal static string ChatBot_AutoFishing_Fishing_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fishing timeout (seconds). Timeout will trigger a re-cast.. + /// + internal static string ChatBot_AutoFishing_Fishing_Timeout { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Fishing_Timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish.. + /// + internal static string ChatBot_AutoFishing_Hook_Threshold { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Hook_Threshold", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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.. + /// + internal static string ChatBot_AutoFishing_Log_Fish_Bobber { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Log_Fish_Bobber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the mainhand or the offhand to hold the rod.. + /// + internal static string ChatBot_AutoFishing_Mainhand { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Mainhand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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.. + /// + internal static string ChatBot_AutoFishing_Movements { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Movements", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hook movement in the X and Z axis less than this value will be considered stationary.. + /// + internal static string ChatBot_AutoFishing_Stationary_Threshold { + get { + return ResourceManager.GetString("ChatBot.AutoFishing.Stationary_Threshold", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically relog when disconnected by server, for example because the server is restating + ////!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks. + /// + internal static string ChatBot_AutoRelog { + get { + return ResourceManager.GetString("ChatBot.AutoRelog", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The delay time before joining the server. (in seconds). + /// + internal static string ChatBot_AutoRelog_Delay { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When set to true, autorelog will reconnect regardless of kick messages.. + /// + internal static string ChatBot_AutoRelog_Ignore_Kick_Message { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Ignore_Kick_Message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If the kickout message matches any of the strings, then autorelog will be triggered.. + /// + internal static string ChatBot_AutoRelog_Kick_Messages { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Kick_Messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Retries when failing to relog to the server. use -1 for unlimited retries.. + /// + internal static string ChatBot_AutoRelog_Retries { + get { + return ResourceManager.GetString("ChatBot.AutoRelog.Retries", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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. + /// + internal static string ChatBot_AutoRespond { + get { + return ResourceManager.GetString("ChatBot.AutoRespond", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work). + /// + internal static string ChatBot_AutoRespond_Match_Colors { + get { + return ResourceManager.GetString("ChatBot.AutoRespond.Match_Colors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logs chat messages in a file on disk.. + /// + internal static string ChatBot_ChatLog { + get { + return ResourceManager.GetString("ChatBot.ChatLog", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This bot allows you to send and recieve messages and commands via a Discord channel. + ///For Setup you can either use the documentation or read here (Documentation has images). + ///Documentation: https://mccteam.github.io/g/bots/#discord-bridge + ///Setup: + ///First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA . + ////!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [rest of string was truncated]";. + /// + internal static string ChatBot_DiscordBridge { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The ID of a channel where you want to interact with the MCC using the bot.. + /// + internal static string ChatBot_DiscordBridge_ChannelId { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.ChannelId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message formats + ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! + ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. + ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html. + /// + internal static string ChatBot_DiscordBridge_Formats { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.Formats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The ID of a server/guild where you have invited the bot to.. + /// + internal static string ChatBot_DiscordBridge_GuildId { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.GuildId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).. + /// + internal static string ChatBot_DiscordBridge_MessageSendTimeout { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.MessageSendTimeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A list of IDs of people you want to be able to interact with the MCC using the bot.. + /// + internal static string ChatBot_DiscordBridge_OwnersIds { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.OwnersIds", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your Discord Bot token.. + /// + internal static string ChatBot_DiscordBridge_Token { + get { + return ResourceManager.GetString("ChatBot.DiscordBridge.Token", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically farms crops for you (plants, breaks and bonemeals them). + ///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat. + ///Usage: "/farmer start" command and "/farmer stop" command. + ///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes. + ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [rest of string was truncated]";. + /// + internal static string ChatBot_Farmer { + get { + return ResourceManager.GetString("ChatBot.Farmer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delay between tasks in seconds (Minimum 1 second). + /// + internal static string ChatBot_Farmer_Delay_Between_Tasks { + get { + return ResourceManager.GetString("ChatBot.Farmer.Delay_Between_Tasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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, /// [rest of string was truncated]";. + /// + internal static string ChatBot_FollowPlayer { + get { + return ResourceManager.GetString("ChatBot.FollowPlayer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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). + /// + internal static string ChatBot_FollowPlayer_Stop_At_Distance { + get { + return ResourceManager.GetString("ChatBot.FollowPlayer.Stop_At_Distance", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow). + /// + internal static string ChatBot_FollowPlayer_Update_Limit { + get { + return ResourceManager.GetString("ChatBot.FollowPlayer.Update_Limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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 <bot username> start + ////!\ This bot may get a bit spammy if many players are interacting with it. + /// + internal static string ChatBot_HangmanGame { + get { + return ResourceManager.GetString("ChatBot.HangmanGame", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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. + /// + internal static string ChatBot_Mailer { + get { + return ResourceManager.GetString("ChatBot.Mailer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) + ///This is useful for solving captchas which use maps + ///The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled. + ///NOTE: + ///If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console. + ////!\ Make sure server rules allow bots to be used on the server, or you risk being punished.. + /// + internal static string ChatBot_Map { + get { + return ResourceManager.GetString("ChatBot.Map", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatically render the map once it is received or updated from/by the server. + /// + internal static string ChatBot_Map_Auto_Render_On_Update { + get { + return ResourceManager.GetString("ChatBot.Map.Auto_Render_On_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete all rendered maps on unload/reload or when you launch the MCC again.. + /// + internal static string ChatBot_Map_Delete_All_On_Unload { + get { + return ResourceManager.GetString("ChatBot.Map.Delete_All_On_Unload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Get a notification when you have gotten a map from the server for the first time. + /// + internal static string ChatBot_Map_Notify_On_First_Update { + get { + return ResourceManager.GetString("ChatBot.Map.Notify_On_First_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord.. + /// + internal static string ChatBot_Map_Rasize_Rendered_Image { + get { + return ResourceManager.GetString("ChatBot.Map.Rasize_Rendered_Image", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to render the map in the console.. + /// + internal static string ChatBot_Map_Render_In_Console { + get { + return ResourceManager.GetString("ChatBot.Map.Render_In_Console", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The size that a rendered image should be resized to, in pixels (eg. 512).. + /// + internal static string ChatBot_Map_Resize_To { + get { + return ResourceManager.GetString("ChatBot.Map.Resize_To", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge).. + /// + internal static string ChatBot_Map_Save_To_File { + get { + return ResourceManager.GetString("ChatBot.Map.Save_To_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) + ///You need to enable Save_To_File in order for this to work. + ///We also recommend turning on resizing.. + /// + internal static string ChatBot_Map_Send_Rendered_To_Bridges { + get { + return ResourceManager.GetString("ChatBot.Map.Send_Rendered_To_Bridges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log the list of players periodically into a textual file.. + /// + internal static string ChatBot_PlayerListLogger { + get { + return ResourceManager.GetString("ChatBot.PlayerListLogger", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (In seconds). + /// + internal static string ChatBot_PlayerListLogger_Delay { + get { + return ResourceManager.GetString("ChatBot.PlayerListLogger.Delay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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. + /// + internal static string ChatBot_RemoteControl { + get { + return ResourceManager.GetString("ChatBot.RemoteControl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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!. + /// + internal static string ChatBot_ReplayCapture { + get { + return ResourceManager.GetString("ChatBot.ReplayCapture", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long should replay file be auto-saved, in seconds. Use -1 to disable.. + /// + internal static string ChatBot_ReplayCapture_Backup_Interval { + get { + return ResourceManager.GetString("ChatBot.ReplayCapture.Backup_Interval", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Schedule commands and scripts to launch on various events such as server join, date/time or time interval + ///See https://mccteam.github.io/g/bots/#script-scheduler for more info. + /// + internal static string ChatBot_ScriptScheduler { + get { + return ResourceManager.GetString("ChatBot.ScriptScheduler", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. + ////!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel. + ///----------------------------------------------------------- + ///Setup: + ///First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather + ///Click on "Start" button and re [rest of string was truncated]";. + /// + internal static string ChatBot_TelegramBridge { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.. + /// + internal static string ChatBot_TelegramBridge_Authorized_Chat_Ids { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.Authorized_Chat_Ids", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An ID of a channel where you want to interact with the MCC using the bot.. + /// + internal static string ChatBot_TelegramBridge_ChannelId { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.ChannelId", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message formats + ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! + ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. + ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html. + /// + internal static string ChatBot_TelegramBridge_Formats { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.Formats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).. + /// + internal static string ChatBot_TelegramBridge_MessageSendTimeout { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.MessageSendTimeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your Telegram Bot token.. + /// + internal static string ChatBot_TelegramBridge_Token { + get { + return ResourceManager.GetString("ChatBot.TelegramBridge.Token", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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 https://mccteam.github.io/g/conf/#chat-format-section. + /// + internal static string ChatFormat { + get { + return ResourceManager.GetString("ChatFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MCC support for common message formats. Set "false" to avoid conflicts with custom formats.. + /// + internal static string ChatFormat_Builtins { + get { + return ResourceManager.GetString("ChatFormat.Builtins", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to use the custom regular expressions below for detection.. + /// + internal static string ChatFormat_UserDefined { + get { + return ResourceManager.GetString("ChatFormat.UserDefined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Console-related settings.. + /// + internal static string Console { + get { + return ResourceManager.GetString("Console", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The settings for command completion suggestions.. + /// + internal static string Console_CommandSuggestion { + get { + return ResourceManager.GetString("Console.CommandSuggestion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to display command suggestions in the console.. + /// + internal static string Console_CommandSuggestion_Enable { + get { + return ResourceManager.GetString("Console.CommandSuggestion.Enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If a garbled code like "←[0m" appears on the terminal, you can turn off this.. + /// + internal static string Console_Enable_Color { + get { + return ResourceManager.GetString("Console.Enable_Color", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can use "Ctrl+P" to print out the current input and cursor position.. + /// + internal static string Console_General_Display_Uesr_Input { + get { + return ResourceManager.GetString("Console.General.Display_Uesr_Input", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Startup Config File + ///Please do not record extraneous data in this file as it will be overwritten by MCC. + /// + ///New to Minecraft Console Client? Check out this document: https://mccteam.github.io/g/conf.html + ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download. + /// + internal static string Head { + get { + return ResourceManager.GetString("Head", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This setting affects only the messages in the console.. + /// + internal static string Logging { + get { + return ResourceManager.GetString("Logging", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Regex for filtering chat message.. + /// + internal static string Logging_ChatFilter { + get { + return ResourceManager.GetString("Logging.ChatFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show server chat messages.. + /// + internal static string Logging_ChatMessages { + get { + return ResourceManager.GetString("Logging.ChatMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Regex for filtering debug message.. + /// + internal static string Logging_DebugFilter { + get { + return ResourceManager.GetString("Logging.DebugFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please enable this before submitting bug reports. Thanks!. + /// + internal static string Logging_DebugMessages { + get { + return ResourceManager.GetString("Logging.DebugMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show error messages.. + /// + internal static string Logging_ErrorMessages { + get { + return ResourceManager.GetString("Logging.ErrorMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex.. + /// + internal static string Logging_FilterMode { + get { + return ResourceManager.GetString("Logging.FilterMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Informative messages. (i.e Most of the message from MCC). + /// + internal static string Logging_InfoMessages { + get { + return ResourceManager.GetString("Logging.InfoMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log file name.. + /// + internal static string Logging_LogFile { + get { + return ResourceManager.GetString("Logging.LogFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Write log messages to file.. + /// + internal static string Logging_LogToFile { + get { + return ResourceManager.GetString("Logging.LogToFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prepend timestamp to messages in log file.. + /// + internal static string Logging_PrependTimestamp { + get { + return ResourceManager.GetString("Logging.PrependTimestamp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep color codes in the saved text.(look like "§b"). + /// + internal static string Logging_SaveColorCodes { + get { + return ResourceManager.GetString("Logging.SaveColorCodes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show warning messages.. + /// + internal static string Logging_WarningMessages { + get { + return ResourceManager.GetString("Logging.WarningMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make sure you understand what each setting does before changing anything!. + /// + internal static string Main_Advanced { + get { + return ResourceManager.GetString("Main.Advanced", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to AccountList: It allows a fast account switching without directly using the credentials + ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1". + /// + internal static string Main_Advanced_account_list { + get { + return ResourceManager.GetString("Main.Advanced.account_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle auto respawn if client player was dead (make sure your spawn point is safe).. + /// + internal static string Main_Advanced_auto_respawn { + get { + return ResourceManager.GetString("Main.Advanced.auto_respawn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set the owner of the bot. /!\ Server admins can impersonate owners!. + /// + internal static string Main_Advanced_bot_owners { + get { + return ResourceManager.GetString("Main.Advanced.bot_owners", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server.. + /// + internal static string Main_Advanced_brand_info { + get { + return ResourceManager.GetString("Main.Advanced.brand_info", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leave empty for no logfile.. + /// + internal static string Main_Advanced_chatbot_log_file { + get { + return ResourceManager.GetString("Main.Advanced.chatbot_log_file", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If turned off, the emoji will be replaced with a simpler character (for /chunk status).. + /// + internal static string Main_Advanced_enable_emoji { + get { + return ResourceManager.GetString("Main.Advanced.enable_emoji", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle entity handling.. + /// + internal static string Main_Advanced_entity_handling { + get { + return ResourceManager.GetString("Main.Advanced.entity_handling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to exit directly when an error occurs, for using MCC in non-interactive scripts.. + /// + internal static string Main_Advanced_exit_on_failure { + get { + return ResourceManager.GetString("Main.Advanced.exit_on_failure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "none", "slash"(/) or "backslash"(\).. + /// + internal static string Main_Advanced_internal_cmd_char { + get { + return ResourceManager.GetString("Main.Advanced.internal_cmd_char", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle inventory handling.. + /// + internal static string Main_Advanced_inventory_handling { + get { + return ResourceManager.GetString("Main.Advanced.inventory_handling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html. + /// + internal static string Main_Advanced_language { + get { + return ResourceManager.GetString("Main.Advanced.language", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Load translations applied to MCC when available, turn it off to use English only.. + /// + internal static string Main_Advanced_LoadMccTrans { + get { + return ResourceManager.GetString("Main.Advanced.LoadMccTrans", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "auto", "no" or "force". Force-enabling only works for MC 1.13+.. + /// + internal static string Main_Advanced_mc_forge { + get { + return ResourceManager.GetString("Main.Advanced.mc_forge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "auto" or "1.X.X" values. Allows to skip server info retrieval.. + /// + internal static string Main_Advanced_mc_version { + get { + return ResourceManager.GetString("Main.Advanced.mc_version", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Controls the minimum interval (in seconds) between sending each message to the server.. + /// + internal static string Main_Advanced_message_cooldown { + get { + return ResourceManager.GetString("Main.Advanced.message_cooldown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable support for joining Minecraft Realms worlds.. + /// + internal static string Main_Advanced_minecraft_realms { + get { + return ResourceManager.GetString("Main.Advanced.minecraft_realms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The minimum height to use when calculating the image size from the height of the terminal.. + /// + internal static string Main_Advanced_MinTerminalHeight { + get { + return ResourceManager.GetString("Main.Advanced.MinTerminalHeight", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The minimum width used when calculating the image size from the width of the terminal.. + /// + internal static string Main_Advanced_MinTerminalWidth { + get { + return ResourceManager.GetString("Main.Advanced.MinTerminalWidth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable head movement while walking to avoid anti-cheat triggers.. + /// + internal static string Main_Advanced_move_head_while_walking { + get { + return ResourceManager.GetString("Main.Advanced.move_head_while_walking", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A movement speed higher than 2 may be considered cheating.. + /// + internal static string Main_Advanced_movement_speed { + get { + return ResourceManager.GetString("Main.Advanced.movement_speed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only works on Windows XP-8 or Windows 10 with old console.. + /// + internal static string Main_Advanced_player_head_icon { + get { + return ResourceManager.GetString("Main.Advanced.player_head_icon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to For remote control of the bot.. + /// + internal static string Main_Advanced_private_msgs_cmd_name { + get { + return ResourceManager.GetString("Main.Advanced.private_msgs_cmd_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How to retain profile key. Use "none", "memory" or "disk".. + /// + internal static string Main_Advanced_profilekey_cache { + get { + return ResourceManager.GetString("Main.Advanced.profilekey_cache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "no", "fast" (5s timeout), or "yes". Required for joining some servers.. + /// + internal static string Main_Advanced_resolve_srv_records { + get { + return ResourceManager.GetString("Main.Advanced.resolve_srv_records", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cache compiled scripts for faster load on low-end devices.. + /// + internal static string Main_Advanced_script_cache { + get { + return ResourceManager.GetString("Main.Advanced.script_cache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ServerList: It allows an easier and faster server switching with short aliases instead of full server IP + ///Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. + ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2". + /// + internal static string Main_Advanced_server_list { + get { + return ResourceManager.GetString("Main.Advanced.server_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How to retain session tokens. Use "none", "memory" or "disk".. + /// + internal static string Main_Advanced_session_cache { + get { + return ResourceManager.GetString("Main.Advanced.session_cache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Decode links embedded in chat messages and show them in console.. + /// + internal static string Main_Advanced_show_chat_links { + get { + return ResourceManager.GetString("Main.Advanced.show_chat_links", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show inventory layout as ASCII art in inventory command.. + /// + internal static string Main_Advanced_show_inventory_layout { + get { + return ResourceManager.GetString("Main.Advanced.show_inventory_layout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to System messages for server ops.. + /// + internal static string Main_Advanced_show_system_messages { + get { + return ResourceManager.GetString("Main.Advanced.show_system_messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Messages displayed above xp bar, set this to false in case of xp bar spam.. + /// + internal static string Main_Advanced_show_xpbar_messages { + get { + return ResourceManager.GetString("Main.Advanced.show_xpbar_messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers.. + /// + internal static string Main_Advanced_temporary_fix_badpacket { + get { + return ResourceManager.GetString("Main.Advanced.temporary_fix_badpacket", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log.. + /// + internal static string Main_Advanced_TerminalColorDepth { + get { + return ResourceManager.GetString("Main.Advanced.TerminalColorDepth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. + /// + internal static string Main_Advanced_terrain_and_movements { + get { + return ResourceManager.GetString("Main.Advanced.terrain_and_movements", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customize the TCP connection timeout with the server. (in seconds). + /// + internal static string Main_Advanced_timeout { + get { + return ResourceManager.GetString("Main.Advanced.timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prepend timestamps to chat messages.. + /// + internal static string Main_Advanced_timestamps { + get { + return ResourceManager.GetString("Main.Advanced.timestamps", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.. + /// + internal static string Main_General_account { + get { + return ResourceManager.GetString("Main.General.account", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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). + /// + internal static string Main_General_login { + get { + return ResourceManager.GetString("Main.General.login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once.. + /// + internal static string Main_General_method { + get { + return ResourceManager.GetString("Main.General.method", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Account type: "mojang" OR "microsoft". Also affects interactive login in console.. + /// + internal static string Main_General_server_info { + get { + return ResourceManager.GetString("Main.General.server_info", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings below are sent to the server and only affect server-side things like your skin.. + /// + internal static string MCSettings { + get { + return ResourceManager.GetString("MCSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allows disabling chat colors server-side.. + /// + internal static string MCSettings_ChatColors { + get { + return ResourceManager.GetString("MCSettings.ChatColors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "enabled", "commands", or "disabled". Allows to mute yourself.... + /// + internal static string MCSettings_ChatMode { + get { + return ResourceManager.GetString("MCSettings.ChatMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult".. + /// + internal static string MCSettings_Difficulty { + get { + return ResourceManager.GetString("MCSettings.Difficulty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If disabled, settings below are not sent to the server.. + /// + internal static string MCSettings_Enabled { + get { + return ResourceManager.GetString("MCSettings.Enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use any language implemented in Minecraft.. + /// + internal static string MCSettings_Locale { + get { + return ResourceManager.GetString("MCSettings.Locale", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MC 1.9+ main hand. "left" or "right".. + /// + internal static string MCSettings_MainHand { + get { + return ResourceManager.GetString("MCSettings.MainHand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value range: [0 - 255].. + /// + internal static string MCSettings_RenderDistance { + get { + return ResourceManager.GetString("MCSettings.RenderDistance", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect to a server via a proxy instead of connecting directly + ///If Mojang session services are blocked on your network, set Enabled_Login=true to login using proxy. + ///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. + ////!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!. + /// + internal static string Proxy { + get { + return ResourceManager.GetString("Proxy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to connect to the game server through a proxy.. + /// + internal static string Proxy_Enabled_Ingame { + get { + return ResourceManager.GetString("Proxy.Enabled_Ingame", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to connect to the login server through a proxy.. + /// + internal static string Proxy_Enabled_Login { + get { + return ResourceManager.GetString("Proxy.Enabled_Login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to download MCC updates via proxy.. + /// + internal static string Proxy_Enabled_Update { + get { + return ResourceManager.GetString("Proxy.Enabled_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only required for password-protected proxies.. + /// + internal static string Proxy_Password { + get { + return ResourceManager.GetString("Proxy.Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5".. + /// + internal static string Proxy_Proxy_Type { + get { + return ResourceManager.GetString("Proxy.Proxy_Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Proxy server must allow HTTPS for login, and non-443 ports for playing.. + /// + internal static string Proxy_Server { + get { + return ResourceManager.GetString("Proxy.Server", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only required for password-protected proxies.. + /// + internal static string Proxy_Username { + get { + return ResourceManager.GetString("Proxy.Username", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chat signature related settings (affects minecraft 1.19+). + /// + internal static string Signature { + get { + return ResourceManager.GetString("Signature", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true". + /// + internal static string Signature_LoginWithSecureProfile { + get { + return ResourceManager.GetString("Signature.LoginWithSecureProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use red    color block to mark chat without legitimate signature. + /// + internal static string Signature_MarkIllegallySignedMsg { + get { + return ResourceManager.GetString("Signature.MarkIllegallySignedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use green  color block to mark chat with legitimate signatures. + /// + internal static string Signature_MarkLegallySignedMsg { + get { + return ResourceManager.GetString("Signature.MarkLegallySignedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use yellow color block to mark chat that have been modified by the server.. + /// + internal static string Signature_MarkModifiedMsg { + get { + return ResourceManager.GetString("Signature.MarkModifiedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use gray   color block to mark system message (always without signature). + /// + internal static string Signature_MarkSystemMessage { + get { + return ResourceManager.GetString("Signature.MarkSystemMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to display chat and messages in commands without legal signatures. + /// + internal static string Signature_ShowIllegalSignedChat { + get { + return ResourceManager.GetString("Signature.ShowIllegalSignedChat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set to true to display messages modified by the server, false to display the original signed messages. + /// + internal static string Signature_ShowModifiedChat { + get { + return ResourceManager.GetString("Signature.ShowModifiedChat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to sign the chat send from MCC. + /// + internal static string Signature_SignChat { + get { + return ResourceManager.GetString("Signature.SignChat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me". + /// + internal static string Signature_SignMessageInCommand { + get { + return ResourceManager.GetString("Signature.SignMessageInCommand", resourceCulture); + } + } + } +} diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index da56bedc..3413fd87 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -535,6 +535,21 @@ When this happens, you'll need to configure chat format below, see https://mccte Whether to use the custom regular expressions below for detection. + + Console-related settings. + + + The settings for command completion suggestions. + + + Whether to display command suggestions in the console. + + + If a garbled code like "←[0m" appears on the terminal, you can turn off this. + + + You can use "Ctrl+P" to print out the current input and cursor position. + Startup Config File Please do not record extraneous data in this file as it will be overwritten by MCC. diff --git a/MinecraftClient/Resources/MinecraftAssets.Designer.cs b/MinecraftClient/Resources/MinecraftAssets.Designer.cs new file mode 100644 index 00000000..052c11b7 --- /dev/null +++ b/MinecraftClient/Resources/MinecraftAssets.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MinecraftClient { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // 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", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MinecraftAssets { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MinecraftAssets() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClient.Resources.MinecraftAssets", typeof(MinecraftAssets).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)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] en_us_json { + get { + object obj = ResourceManager.GetObject("en_us.json", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/MinecraftClient/Resources/MinecraftAssets.resx b/MinecraftClient/Resources/MinecraftAssets.resx new file mode 100644 index 00000000..0e01945f --- /dev/null +++ b/MinecraftClient/Resources/MinecraftAssets.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + en_us.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index 64fe6379..7af00b17 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -2270,15 +2270,6 @@ namespace MinecraftClient { } } - /// - /// Looks up a localized string similar to Defaulting to en_GB.lang from your Minecraft directory.. - /// - internal static string chat_from_dir { - get { - return ResourceManager.GetString("chat.from_dir", resourceCulture); - } - } - /// /// Looks up a localized string similar to Translations file loaded.. /// @@ -2316,6 +2307,24 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to Failed to save the file {0}.. + /// + internal static string chat_save_fail { + get { + return ResourceManager.GetString("chat.save_fail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to use Minecraft's default language resource "en_us.json".. + /// + internal static string chat_use_default { + get { + return ResourceManager.GetString("chat.use_default", resourceCulture); + } + } + /// /// Looks up a localized string similar to [{0}] Disconnecting and Reconnecting to the Server. /// @@ -4112,6 +4121,15 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to The color code {1} in {0} is in illegal format and the default value has been restored.. + /// + internal static string config_commandsuggestion_illegal_color { + get { + return ResourceManager.GetString("config.commandsuggestion.illegal_color", resourceCulture); + } + } + /// /// Looks up a localized string similar to Settings have been loaded from {0}. /// diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index ed73e510..1888597a 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -869,9 +869,6 @@ Add the ID of this chat to "Authorized_Chat_Ids" field in the configuration file Failed to download the file. - - Defaulting to en_GB.lang from your Minecraft directory. - Translations file loaded. @@ -885,6 +882,12 @@ Some messages won't be properly printed without this file. Performing request to {0} + + Failed to save the file {0}. + + + Switch to use Minecraft's default language resource "en_us.json". + [{0}] Disconnecting and Reconnecting to the Server @@ -1449,7 +1452,7 @@ You can use "/chunk status {0:0.0} {1:0.0} {2:0.0}" to check the chunk loading s set a custom %variable% randomly to a given value. - setrnd variable -7to17 OR setrnd variable string1 "\"string2\" string3" + setrnd variable -7 to 17 OR setrnd variable string1 "\"string2\" string3" setrnd variable -7 to 17 @@ -1487,6 +1490,9 @@ You can use "/chunk status {0:0.0} {1:0.0} {2:0.0}" to check the chunk loading s Failed to write to backup file {0} + + The color code {1} in {0} is in illegal format and the default value has been restored. + Settings have been loaded from {0} @@ -1991,9 +1997,6 @@ Switching to autodetection mode. Self-updating: {0:00.00}%, ETA {4}, Downloaded {1:00.0}MB of {2:00.0}MB, Avg {3:0.0}KB/s - - setrnd variable -7 to 17 OR setrnd variable string1 "\"string2\" string3" - Self-updating: Downloaded {0:00.0}MB, Avg {1:0.0}KB/s @@ -2025,4 +2028,4 @@ Logging in... Connected to proxy {0}:{1} - + \ No newline at end of file diff --git a/MinecraftClient/Resources/en_us.json b/MinecraftClient/Resources/en_us.json new file mode 100644 index 00000000..f02b7bf1 --- /dev/null +++ b/MinecraftClient/Resources/en_us.json @@ -0,0 +1,5822 @@ +{ + "language.name": "English", + "language.region": "United States", + "language.code": "en_us", + "narrator.button.accessibility": "Accessibility", + "narrator.button.language": "Language", + "narrator.button.difficulty_lock": "Difficulty lock", + "narrator.button.difficulty_lock.unlocked": "Unlocked", + "narrator.button.difficulty_lock.locked": "Locked", + "narrator.screen.title": "Title Screen", + "narrator.controls.reset": "Reset %s button", + "narrator.controls.bound": "%s is bound to %s", + "narrator.controls.unbound": "%s is not bound", + "narrator.select": "Selected: %s", + "narrator.select.world": "Selected %s, last played: %s, %s, %s, version: %s", + "narrator.loading": "Loading: %s", + "narrator.loading.done": "Done", + "narrator.joining": "Joining", + "narrator.position.screen": "Screen element %s out of %s", + "narrator.screen.usage": "Use mouse cursor or Tab button to select element", + "narrator.position.list": "Selected list row %s out of %s", + "narrator.position.object_list": "Selected row element %s out of %s", + "narration.suggestion.tooltip": "Selected suggestion %d out of %d: %s (%s)", + "narration.suggestion": "Selected suggestion %d out of %d: %s", + "narration.button": "Button: %s", + "narration.button.usage.focused": "Press Enter to activate", + "narration.button.usage.hovered": "Left click to activate", + "narration.cycle_button.usage.focused": "Press Enter to switch to %s", + "narration.cycle_button.usage.hovered": "Left click to switch to %s", + "narration.checkbox": "Checkbox: %s", + "narration.checkbox.usage.focused": "Press Enter to toggle", + "narration.checkbox.usage.hovered": "Left click to toggle", + "narration.recipe": "Recipe for %s", + "narration.recipe.usage": "Left click to select", + "narration.recipe.usage.more": "Right click to show more recipes", + "narration.selection.usage": "Press up and down buttons to move to another entry", + "narration.component_list.usage": "Press Tab to navigate to next element", + "narration.slider.usage.focused": "Press left or right keyboard buttons to change value", + "narration.slider.usage.hovered": "Drag slider to change value", + "narration.edit_box": "Edit box: %s", + "chat_screen.title": "Chat screen", + "chat_screen.usage": "Input message and press Enter to send", + "chat_screen.message": "Message to send: %s", + "gui.done": "Done", + "gui.cancel": "Cancel", + "gui.back": "Back", + "gui.toTitle": "Back to Title Screen", + "gui.toMenu": "Back to Server List", + "gui.up": "Up", + "gui.down": "Down", + "gui.yes": "Yes", + "gui.no": "No", + "gui.none": "None", + "gui.all": "All", + "gui.ok": "Ok", + "gui.proceed": "Proceed", + "gui.acknowledge": "Acknowledge", + "gui.recipebook.moreRecipes": "Right Click for More", + "gui.recipebook.search_hint": "Search...", + "gui.recipebook.toggleRecipes.all": "Showing All", + "gui.recipebook.toggleRecipes.craftable": "Showing Craftable", + "gui.recipebook.toggleRecipes.smeltable": "Showing Smeltable", + "gui.recipebook.toggleRecipes.blastable": "Showing Blastable", + "gui.recipebook.toggleRecipes.smokable": "Showing Smokable", + "gui.socialInteractions.title": "Social Interactions", + "gui.socialInteractions.tab_all": "All", + "gui.socialInteractions.tab_hidden": "Hidden", + "gui.socialInteractions.tab_blocked": "Blocked", + "gui.socialInteractions.blocking_hint": "Manage with Microsoft account", + "gui.socialInteractions.status_hidden": "Hidden", + "gui.socialInteractions.status_blocked": "Blocked", + "gui.socialInteractions.status_offline": "Offline", + "gui.socialInteractions.status_hidden_offline": "Hidden - Offline", + "gui.socialInteractions.status_blocked_offline": "Blocked - Offline", + "gui.socialInteractions.server_label.single": "%s - %s player", + "gui.socialInteractions.server_label.multiple": "%s - %s players", + "gui.socialInteractions.search_hint": "Search...", + "gui.socialInteractions.search_empty": "Couldn't find any players with that name", + "gui.socialInteractions.empty_hidden": "No players hidden in chat", + "gui.socialInteractions.empty_blocked": "No blocked players in chat", + "gui.socialInteractions.hide": "Hide in Chat", + "gui.socialInteractions.show": "Show in Chat", + "gui.socialInteractions.report": "Report", + "gui.socialInteractions.hidden_in_chat": "Chat messages from %s will be hidden", + "gui.socialInteractions.shown_in_chat": "Chat messages from %s will be shown", + "gui.socialInteractions.tooltip.hide": "Hide messages", + "gui.socialInteractions.tooltip.show": "Show messages", + "gui.socialInteractions.tooltip.report": "Report player", + "gui.socialInteractions.tooltip.report.disabled": "The reporting service is unavailable", + "gui.socialInteractions.tooltip.report.no_messages": "No reportable messages from player %s", + "gui.socialInteractions.tooltip.report.not_reportable": "This player can't be reported, because their chat messages can't be verified on this server", + "gui.socialInteractions.narration.hide": "Hide messages from %s", + "gui.socialInteractions.narration.show": "Show messages from %s", + "gui.socialInteractions.narration.report": "Report player %s", + "gui.narrate.button": "%s button", + "gui.narrate.slider": "%s slider", + "gui.narrate.editBox": "%s edit box: %s", + "gui.chatReport.title": "Report Player", + "gui.chatReport.send": "Send Report", + "gui.chatReport.send.comments_too_long": "Please shorten the comment", + "gui.chatReport.send.no_reason": "Please select a report category", + "gui.chatReport.send.no_reported_messages": "Please select at least one chat message to report", + "gui.chatReport.send.too_many_messages": "Trying to include too many messages in the report", + "gui.chatReport.observed_what": "Why are you reporting this?", + "gui.chatReport.select_reason": "Select Report Category", + "gui.chatReport.more_comments": "Please describe what happened:", + "gui.chatReport.describe": "Sharing details will help us make a well-informed decision.", + "gui.chatReport.comments": "Comments", + "gui.chatReport.read_info": "Learn About Reporting", + "gui.chatReport.select_chat": "Select Chat Messages to Report", + "gui.chatReport.selected_chat": "%s Chat Message(s) Selected to Report", + "gui.chatReport.report_sent_msg": "We’ve successfully received your report. Thank you!\n\nOur team will review it as soon as possible.", + "gui.chatReport.discard.title": "Discard report and comments?", + "gui.chatReport.discard.content": "If you leave, you'll lose this report and your comments.\nAre you sure you want to leave?", + "gui.chatReport.discard.discard": "Leave and Discard Report", + "gui.chatReport.discard.draft": "Save as Draft", + "gui.chatReport.discard.return": "Continue Editing", + "gui.chatReport.draft.title": "Edit draft chat report?", + "gui.chatReport.draft.content": "Would you like to continue editing the existing report or discard it and create a new one?", + "gui.chatReport.draft.quittotitle.title": "You have a draft chat report that will be lost if you quit", + "gui.chatReport.draft.quittotitle.content": "Would you like to continue editing it or discard it?", + "gui.chatReport.draft.discard": "Discard", + "gui.chatReport.draft.edit": "Continue Editing", + "gui.abuseReport.reason.title": "Select Report Category", + "gui.abuseReport.reason.description": "Description:", + "gui.abuseReport.reason.narration": "%s: %s", + "gui.abuseReport.reason.false_reporting": "False Reporting", + "gui.abuseReport.reason.child_sexual_exploitation_or_abuse": "Child sexual exploitation or abuse", + "gui.abuseReport.reason.child_sexual_exploitation_or_abuse.description": "Someone is talking about or otherwise promoting indecent behavior involving children.", + "gui.abuseReport.reason.terrorism_or_violent_extremism": "Terrorism or violent extremism", + "gui.abuseReport.reason.terrorism_or_violent_extremism.description": "Someone is talking about, promoting, or threatening to commit acts of terrorism or violent extremism for political, religious, ideological, or other reasons.", + "gui.abuseReport.reason.hate_speech": "Hate speech", + "gui.abuseReport.reason.hate_speech.description": "Someone is attacking you or another player based on characteristics of their identity, like religion, race, or sexuality.", + "gui.abuseReport.reason.harassment_or_bullying": "Harassment or bullying", + "gui.abuseReport.reason.harassment_or_bullying.description": "Someone is shaming, attacking, or bullying you or someone else. This includes when someone is repeatedly trying to contact you or someone else without consent or posting private personal information about you or someone else without consent (\"doxing\").", + "gui.abuseReport.reason.imminent_harm": "Imminent harm - Threat to harm others", + "gui.abuseReport.reason.imminent_harm.description": "Someone is threatening to harm you or someone else in real life.", + "gui.abuseReport.reason.defamation_impersonation_false_information": "Defamation, impersonation, or false information", + "gui.abuseReport.reason.defamation_impersonation_false_information.description": "Someone is damaging someone else's reputation, pretending to be someone they're not, or sharing false information with the aim to exploit or mislead others.", + "gui.abuseReport.reason.self_harm_or_suicide": "Imminent harm - Self-harm or suicide", + "gui.abuseReport.reason.self_harm_or_suicide.description": "Someone is threatening to harm themselves in real life or talking about harming themselves in real life.", + "gui.abuseReport.reason.alcohol_tobacco_drugs": "Drugs or alcohol", + "gui.abuseReport.reason.alcohol_tobacco_drugs.description": "Someone is encouraging others to partake in illegal drug related activities or encouraging underage drinking.", + "gui.abuseReport.reason.non_consensual_intimate_imagery": "Non-consensual intimate imagery", + "gui.abuseReport.reason.non_consensual_intimate_imagery.description": "Someone is talking about, sharing, or otherwise promoting private and intimate images.", + "gui.abuseReport.sending.title": "Sending your report...", + "gui.abuseReport.sent.title": "Report sent", + "gui.abuseReport.error.title": "Problem sending your report", + "gui.abuseReport.send.generic_error": "Encountered an unexpected error while sending your report.", + "gui.abuseReport.send.error_message": "An error was returned while sending your report:\n'%s'", + "gui.abuseReport.send.service_unavailable": "Unable to reach the Abuse Reporting service. Please make sure you are connected to the internet and try again.", + "gui.abuseReport.send.http_error": "An unexpected HTTP error occurred while sending your report.", + "gui.abuseReport.send.json_error": "Encountered malformed payload while sending your report.", + "gui.chatSelection.title": "Select Chat Messages to Report", + "gui.chatSelection.context": "Messages surrounding this selection will be included to provide additional context", + "gui.chatSelection.selected": "%s/%s message(s) selected", + "gui.chatSelection.heading": "%s %s", + "gui.chatSelection.message.narrate": "%s said: %s at %s", + "gui.chatSelection.fold": "%s message(s) hidden", + "gui.chatSelection.join": "%s joined the chat", + "gui.multiLineEditBox.character_limit": "%s/%s", + "gui.banned.title.temporary": "Account temporarily suspended", + "gui.banned.title.permanent": "Account permanently banned", + "gui.banned.description": "%s\n\n%s\n\nLearn more at the following link: %s", + "gui.banned.description.reason": "We recently received a report for bad behavior by your account. Our moderators have now reviewed your case and identified it as %s, which goes against the Minecraft Community Standards.", + "gui.banned.description.reason_id": "Code: %s", + "gui.banned.description.reason_id_message": "Code: %s - %s", + "gui.banned.description.unknownreason": "We recently received a report for bad behavior by your account. Our moderators have now reviewed your case and identified that it goes against the Minecraft Community Standards.", + "gui.banned.description.temporary.duration": "Your account is temporarily suspended and will be reactivated in %s.", + "gui.banned.description.temporary": "%s Until then, you can’t play online or join Realms.", + "gui.banned.description.permanent": "Your account is permanently banned, which means you can’t play online or join Realms.", + "translation.test.none": "Hello, world!", + "translation.test.complex": "Prefix, %s%2$s again %s and %1$s lastly %s and also %1$s again!", + "translation.test.escape": "%%s %%%s %%%%s %%%%%s", + "translation.test.invalid": "hi %", + "translation.test.invalid2": "hi % s", + "translation.test.args": "%s %s", + "translation.test.world": "world", + "menu.game": "Game Menu", + "menu.singleplayer": "Singleplayer", + "menu.multiplayer": "Multiplayer", + "menu.online": "Minecraft Realms", + "menu.options": "Options...", + "menu.quit": "Quit Game", + "menu.returnToMenu": "Save and Quit to Title", + "menu.disconnect": "Disconnect", + "menu.returnToGame": "Back to Game", + "menu.generatingLevel": "Generating world", + "menu.loadingLevel": "Loading world", + "menu.savingLevel": "Saving world", + "menu.working": "Working...", + "menu.savingChunks": "Saving chunks", + "menu.preparingSpawn": "Preparing spawn area: %s%%", + "menu.loadingForcedChunks": "Loading forced chunks for dimension %s", + "menu.generatingTerrain": "Building terrain", + "menu.convertingLevel": "Converting world", + "menu.respawning": "Respawning", + "menu.shareToLan": "Open to LAN", + "menu.sendFeedback": "Give Feedback", + "menu.reportBugs": "Report Bugs", + "menu.playerReporting": "Player Reporting", + "menu.paused": "Game Paused", + "menu.modded": " (Modded)", + "optimizeWorld.confirm.title": "Optimize World", + "optimizeWorld.confirm.description": "This will attempt to optimize your world by making sure all data is stored in the most recent game format. This can take a very long time, depending on your world. Once done, your world may play faster but will no longer be compatible with older versions of the game. Are you sure you wish to proceed?", + "optimizeWorld.title": "Optimizing World '%s'", + "optimizeWorld.stage.counting": "Counting chunks...", + "optimizeWorld.stage.upgrading": "Upgrading all chunks...", + "optimizeWorld.stage.finished": "Finishing up...", + "optimizeWorld.stage.failed": "Failed! :(", + "optimizeWorld.info.converted": "Upgraded chunks: %s", + "optimizeWorld.info.skipped": "Skipped chunks: %s", + "optimizeWorld.info.total": "Total chunks: %s", + "selectWorld.title": "Select World", + "selectWorld.search": "search for worlds", + "selectWorld.world": "World", + "selectWorld.select": "Play Selected World", + "selectWorld.create": "Create New World", + "selectWorld.recreate": "Re-Create", + "selectWorld.createDemo": "Play New Demo World", + "selectWorld.delete": "Delete", + "selectWorld.edit": "Edit", + "selectWorld.edit.title": "Edit World", + "selectWorld.edit.resetIcon": "Reset Icon", + "selectWorld.edit.openFolder": "Open World Folder", + "selectWorld.edit.save": "Save", + "selectWorld.edit.backup": "Make Backup", + "selectWorld.edit.backupFolder": "Open Backups Folder", + "selectWorld.edit.backupFailed": "Backup failed", + "selectWorld.edit.backupCreated": "Backed up: %s", + "selectWorld.edit.backupSize": "size: %s MB", + "selectWorld.edit.optimize": "Optimize World", + "selectWorld.edit.export_worldgen_settings": "Export World Generation Settings", + "selectWorld.edit.export_worldgen_settings.success": "Exported", + "selectWorld.edit.export_worldgen_settings.failure": "Export failed", + "selectWorld.deleteQuestion": "Are you sure you want to delete this world?", + "selectWorld.deleteWarning": "'%s' will be lost forever! (A long time!)", + "selectWorld.deleteButton": "Delete", + "selectWorld.conversion": "Must be converted!", + "selectWorld.conversion.tooltip": "This world must be opened in an older version (like 1.6.4) to be safely converted", + "selectWorld.locked": "Locked by another running instance of Minecraft", + "selectWorld.incompatible_series": "Created by an incompatible version", + "selectWorld.newWorld": "New World", + "selectWorld.enterName": "World Name", + "selectWorld.resultFolder": "Will be saved in:", + "selectWorld.enterSeed": "Seed for the world generator", + "selectWorld.seedInfo": "Leave blank for a random seed", + "selectWorld.cheats": "Cheats", + "selectWorld.experimental": "Experimental", + "selectWorld.customizeType": "Customize", + "selectWorld.version": "Version:", + "selectWorld.versionUnknown": "unknown", + "selectWorld.versionQuestion": "Do you really want to load this world?", + "selectWorld.versionWarning": "This world was last played in version %s and loading it in this version could cause corruption!", + "selectWorld.versionJoinButton": "Load Anyway", + "selectWorld.backupQuestion.snapshot": "Do you really want to load this world?", + "selectWorld.backupWarning.snapshot": "This world was last played in version %s; you are on version %s. Please make a backup in case you experience world corruptions!", + "selectWorld.backupQuestion.downgrade": "Downgrading a world is not supported", + "selectWorld.backupWarning.downgrade": "This world was last played in version %s; you are on version %s. Downgrading a world could cause corruption - we cannot guarantee that it will load or work. If you still want to continue, please make a backup!", + "selectWorld.backupQuestion.customized": "Customized worlds are no longer supported", + "selectWorld.backupWarning.customized": "Unfortunately, we do not support customized worlds in this version of Minecraft. We can still load this world and keep everything the way it was, but any newly generated terrain will no longer be customized. We're sorry for the inconvenience!", + "selectWorld.backupQuestion.experimental": "Worlds using Experimental Settings are not supported", + "selectWorld.backupWarning.experimental": "This world uses experimental settings that could stop working at any time. We cannot guarantee it will load or work. Here be dragons!", + "selectWorld.backupEraseCache": "Erase Cached Data", + "selectWorld.backupJoinConfirmButton": "Create Backup and Load", + "selectWorld.backupJoinSkipButton": "I know what I'm doing!", + "selectWorld.tooltip.fromNewerVersion1": "World was saved in a newer version,", + "selectWorld.tooltip.fromNewerVersion2": "loading this world could cause problems!", + "selectWorld.tooltip.snapshot1": "Don't forget to back up this world", + "selectWorld.tooltip.snapshot2": "before you load it in this snapshot.", + "selectWorld.unable_to_load": "Unable to load worlds", + "selectWorld.futureworld.error.title": "An error occurred!", + "selectWorld.futureworld.error.text": "Something went wrong while trying to load a world from a future version. This was a risky operation to begin with; sorry it didn't work.", + "selectWorld.recreate.error.title": "An error occurred!", + "selectWorld.recreate.error.text": "Something went wrong while trying to recreate a world.", + "selectWorld.recreate.customized.title": "Customized worlds are no longer supported", + "selectWorld.recreate.customized.text": "Customized worlds are no longer supported in this version of Minecraft. We can try to recreate it with the same seed and properties, but any terrain customizations will be lost. We're sorry for the inconvenience!", + "selectWorld.load_folder_access": "Unable to read or access folder where game worlds are saved!", + "selectWorld.access_failure": "Failed to access world", + "selectWorld.delete_failure": "Failed to delete world", + "selectWorld.data_read": "Reading world data...", + "selectWorld.loading_list": "Loading world list", + "createWorld.customize.presets": "Presets", + "createWorld.customize.presets.title": "Select a Preset", + "createWorld.customize.presets.select": "Use Preset", + "createWorld.customize.presets.share": "Want to share your preset with someone? Use the box below!", + "createWorld.customize.presets.list": "Alternatively, here's some we made earlier!", + "createWorld.customize.flat.title": "Superflat Customization", + "createWorld.customize.flat.tile": "Layer Material", + "createWorld.customize.flat.height": "Height", + "createWorld.customize.flat.removeLayer": "Remove Layer", + "createWorld.customize.flat.layer.top": "Top - %s", + "createWorld.customize.flat.layer": "%s", + "createWorld.customize.flat.layer.bottom": "Bottom - %s", + "createWorld.customize.buffet.title": "Buffet world customization", + "createWorld.customize.buffet.biome": "Please select a biome", + "flat_world_preset.unknown": "???", + "flat_world_preset.minecraft.classic_flat": "Classic Flat", + "flat_world_preset.minecraft.tunnelers_dream": "Tunnelers' Dream", + "flat_world_preset.minecraft.water_world": "Water World", + "flat_world_preset.minecraft.overworld": "Overworld", + "flat_world_preset.minecraft.snowy_kingdom": "Snowy Kingdom", + "flat_world_preset.minecraft.bottomless_pit": "Bottomless Pit", + "flat_world_preset.minecraft.desert": "Desert", + "flat_world_preset.minecraft.redstone_ready": "Redstone Ready", + "flat_world_preset.minecraft.the_void": "The Void", + "createWorld.customize.custom.page0": "Basic Settings", + "createWorld.customize.custom.page1": "Ore Settings", + "createWorld.customize.custom.page2": "Advanced Settings (Expert Users Only!)", + "createWorld.customize.custom.page3": "Extra Advanced Settings (Expert Users Only!)", + "createWorld.customize.custom.randomize": "Randomize", + "createWorld.customize.custom.prev": "Previous Page", + "createWorld.customize.custom.next": "Next Page", + "createWorld.customize.custom.defaults": "Defaults", + "createWorld.customize.custom.confirm1": "This will overwrite your current", + "createWorld.customize.custom.confirm2": "settings and cannot be undone.", + "createWorld.customize.custom.confirmTitle": "Warning!", + "createWorld.customize.custom.mainNoiseScaleX": "Main Noise Scale X", + "createWorld.customize.custom.mainNoiseScaleY": "Main Noise Scale Y", + "createWorld.customize.custom.mainNoiseScaleZ": "Main Noise Scale Z", + "createWorld.customize.custom.depthNoiseScaleX": "Depth Noise Scale X", + "createWorld.customize.custom.depthNoiseScaleZ": "Depth Noise Scale Z", + "createWorld.customize.custom.depthNoiseScaleExponent": "Depth Noise Exponent", + "createWorld.customize.custom.baseSize": "Depth Base Size", + "createWorld.customize.custom.coordinateScale": "Coordinate Scale", + "createWorld.customize.custom.heightScale": "Height Scale", + "createWorld.customize.custom.stretchY": "Height Stretch", + "createWorld.customize.custom.upperLimitScale": "Upper Limit Scale", + "createWorld.customize.custom.lowerLimitScale": "Lower Limit Scale", + "createWorld.customize.custom.biomeDepthWeight": "Biome Depth Weight", + "createWorld.customize.custom.biomeDepthOffset": "Biome Depth Offset", + "createWorld.customize.custom.biomeScaleWeight": "Biome Scale Weight", + "createWorld.customize.custom.biomeScaleOffset": "Biome Scale Offset", + "createWorld.customize.custom.seaLevel": "Sea Level", + "createWorld.customize.custom.useCaves": "Caves", + "createWorld.customize.custom.useStrongholds": "Strongholds", + "createWorld.customize.custom.useVillages": "Villages", + "createWorld.customize.custom.useMineShafts": "Mineshafts", + "createWorld.customize.custom.useTemples": "Temples", + "createWorld.customize.custom.useOceanRuins": "Ocean Ruins", + "createWorld.customize.custom.useMonuments": "Ocean Monuments", + "createWorld.customize.custom.useMansions": "Woodland Mansions", + "createWorld.customize.custom.useRavines": "Ravines", + "createWorld.customize.custom.useDungeons": "Dungeons", + "createWorld.customize.custom.dungeonChance": "Dungeon Count", + "createWorld.customize.custom.useWaterLakes": "Water Lakes", + "createWorld.customize.custom.waterLakeChance": "Water Lake Rarity", + "createWorld.customize.custom.useLavaLakes": "Lava Lakes", + "createWorld.customize.custom.lavaLakeChance": "Lava Lake Rarity", + "createWorld.customize.custom.useLavaOceans": "Lava Oceans", + "createWorld.customize.custom.fixedBiome": "Biome", + "createWorld.customize.custom.biomeSize": "Biome Size", + "createWorld.customize.custom.riverSize": "River Size", + "createWorld.customize.custom.size": "Spawn Size", + "createWorld.customize.custom.count": "Spawn Tries", + "createWorld.customize.custom.minHeight": "Min. Height", + "createWorld.customize.custom.maxHeight": "Max. Height", + "createWorld.customize.custom.center": "Center Height", + "createWorld.customize.custom.spread": "Spread Height", + "createWorld.customize.custom.presets.title": "Customize World Presets", + "createWorld.customize.custom.presets": "Presets", + "createWorld.customize.custom.preset.waterWorld": "Water World", + "createWorld.customize.custom.preset.isleLand": "Isle Land", + "createWorld.customize.custom.preset.caveDelight": "Caver's Delight", + "createWorld.customize.custom.preset.mountains": "Mountain Madness", + "createWorld.customize.custom.preset.drought": "Drought", + "createWorld.customize.custom.preset.caveChaos": "Caves of Chaos", + "createWorld.customize.custom.preset.goodLuck": "Good Luck", + "createWorld.preparing": "Preparing for world creation...", + "datapackFailure.title": "Errors in currently selected datapacks prevented the world from loading.\nYou can either try to load it with only the vanilla data pack (\"safe mode\"), or go back to the title screen and fix it manually.", + "datapackFailure.safeMode": "Safe Mode", + "editGamerule.title": "Edit Game Rules", + "editGamerule.default": "Default: %s", + "gameMode.survival": "Survival Mode", + "gameMode.creative": "Creative Mode", + "gameMode.adventure": "Adventure Mode", + "gameMode.spectator": "Spectator Mode", + "gameMode.hardcore": "Hardcore Mode!", + "gameMode.changed": "Your game mode has been updated to %s", + "spectatorMenu.previous_page": "Previous Page", + "spectatorMenu.next_page": "Next Page", + "spectatorMenu.close": "Close Menu", + "spectatorMenu.teleport": "Teleport to Player", + "spectatorMenu.teleport.prompt": "Select a player to teleport to", + "spectatorMenu.team_teleport": "Teleport to Team Member", + "spectatorMenu.team_teleport.prompt": "Select a team to teleport to", + "spectatorMenu.root.prompt": "Press a key to select a command, and again to use it.", + "selectWorld.gameMode": "Game Mode", + "selectWorld.gameMode.survival": "Survival", + "selectWorld.gameMode.survival.line1": "Search for resources, craft, gain", + "selectWorld.gameMode.survival.line2": "levels, health and hunger", + "selectWorld.gameMode.creative": "Creative", + "selectWorld.gameMode.creative.line1": "Unlimited resources, free flying and", + "selectWorld.gameMode.creative.line2": "destroy blocks instantly", + "selectWorld.gameMode.spectator": "Spectator", + "selectWorld.gameMode.spectator.line1": "You can look but don't touch", + "selectWorld.gameMode.spectator.line2": "", + "selectWorld.gameMode.hardcore": "Hardcore", + "selectWorld.gameMode.hardcore.line1": "Same as Survival Mode, locked at hardest", + "selectWorld.gameMode.hardcore.line2": "difficulty, and one life only", + "selectWorld.gameMode.adventure": "Adventure", + "selectWorld.gameMode.adventure.line1": "Same as Survival Mode, but blocks can't", + "selectWorld.gameMode.adventure.line2": "be added or removed", + "selectWorld.moreWorldOptions": "More World Options...", + "selectWorld.gameRules": "Game Rules", + "selectWorld.mapFeatures": "Generate Structures", + "selectWorld.mapFeatures.info": "Villages, dungeons etc.", + "selectWorld.mapType": "World Type", + "selectWorld.mapType.normal": "Normal", + "selectWorld.allowCommands": "Allow Cheats", + "selectWorld.allowCommands.info": "Commands like /gamemode, /experience", + "selectWorld.dataPacks": "Data Packs", + "selectWorld.bonusItems": "Bonus Chest", + "selectWorld.import_worldgen_settings": "Import Settings", + "selectWorld.import_worldgen_settings.select_file": "Select settings file (.json)", + "selectWorld.import_worldgen_settings.failure": "Error importing settings", + "selectWorld.warning.experimental.title": "Warning! These settings are using experimental features", + "selectWorld.warning.experimental.question": "These settings are experimental and could one day stop working. Do you wish to proceed?", + "selectWorld.warning.deprecated.title": "Warning! These settings are using deprecated features", + "selectWorld.warning.deprecated.question": "Some features used are deprecated and will stop working in the future. Do you wish to proceed?", + "selectWorld.experimental.title": "Experimental Features Warning", + "selectWorld.experimental.message": "Be careful!\nSome of the selected packs require features that are still under development. Your world might crash, break or not work with future updates.", + "selectWorld.experimental.details": "Details", + "selectWorld.experimental.details.title": "Experimental feature requirements", + "selectWorld.experimental.details.entry": "Required experimental features: %s", + "generator.custom": "Custom", + "generator.minecraft.normal": "Default", + "generator.minecraft.flat": "Superflat", + "generator.minecraft.large_biomes": "Large Biomes", + "generator.minecraft.amplified": "AMPLIFIED", + "generator.minecraft.amplified.info": "Notice: Just for fun! Requires a beefy computer.", + "generator.minecraft.debug_all_block_states": "Debug Mode", + "generator.minecraft.single_biome_surface": "Single Biome", + "generator.customized": "Old Customized", + "generator.single_biome_caves": "Caves", + "generator.single_biome_floating_islands": "Floating Islands", + "selectServer.title": "Select Server", + "selectServer.select": "Join Server", + "selectServer.direct": "Direct Connection", + "selectServer.edit": "Edit", + "selectServer.delete": "Delete", + "selectServer.add": "Add Server", + "selectServer.defaultName": "Minecraft Server", + "selectServer.deleteQuestion": "Are you sure you want to remove this server?", + "selectServer.deleteWarning": "'%s' will be lost forever! (A long time!)", + "selectServer.deleteButton": "Delete", + "selectServer.refresh": "Refresh", + "selectServer.hiddenAddress": "(Hidden)", + "addServer.title": "Edit Server Info", + "addServer.enterName": "Server Name", + "addServer.enterIp": "Server Address", + "addServer.add": "Done", + "addServer.hideAddress": "Hide Address", + "addServer.resourcePack": "Server Resource Packs", + "addServer.resourcePack.enabled": "Enabled", + "addServer.resourcePack.disabled": "Disabled", + "addServer.resourcePack.prompt": "Prompt", + "lanServer.title": "LAN World", + "lanServer.scanning": "Scanning for games on your local network", + "lanServer.start": "Start LAN World", + "lanServer.otherPlayers": "Settings for Other Players", + "lanServer.port": "Port Number", + "lanServer.port.unavailable": "Port not available.\nLeave the edit box empty or enter a different number between 1024 and 65535.", + "lanServer.port.unavailable.new": "Port not available.\nLeave the edit box empty or enter a different number between %s and %s.", + "lanServer.port.invalid": "Not a valid port.\nLeave the edit box empty or enter a number between 1024 and 65535.", + "lanServer.port.invalid.new": "Not a valid port.\nLeave the edit box empty or enter a number between %s and %s.", + "multiplayerWarning.header": "Caution: Third-Party Online Play", + "multiplayerWarning.message": "Caution: Online play is offered by third-party servers that are not owned, operated, or supervised by Mojang Studios or Microsoft. During online play, you may be exposed to unmoderated chat messages or other types of user-generated content that may not be suitable for everyone.", + "multiplayerWarning.check": "Do not show this screen again", + "multiplayer.title": "Play Multiplayer", + "multiplayer.texturePrompt.line1": "This server recommends the use of a custom resource pack.", + "multiplayer.texturePrompt.line2": "Would you like to download and install it automagically?", + "multiplayer.requiredTexturePrompt.line1": "This server requires the use of a custom resource pack.", + "multiplayer.requiredTexturePrompt.line2": "Rejecting this custom resource pack will disconnect you from this server.", + "multiplayer.requiredTexturePrompt.disconnect": "Server requires a custom resource pack", + "multiplayer.texturePrompt.failure.line1": "Server resource pack couldn't be applied", + "multiplayer.texturePrompt.failure.line2": "Any functionality that requires custom resources might not work as expected", + "multiplayer.texturePrompt.serverPrompt": "%s\n\nMessage from server:\n%s", + "multiplayer.applyingPack": "Applying resource pack", + "multiplayer.downloadingTerrain": "Loading terrain...", + "multiplayer.downloadingStats": "Retrieving statistics...", + "multiplayer.stopSleeping": "Leave Bed", + "multiplayer.message_not_delivered": "Can't deliver chat message, check server logs: %s", + "multiplayer.player.joined": "%s joined the game", + "multiplayer.player.joined.renamed": "%s (formerly known as %s) joined the game", + "multiplayer.player.left": "%s left the game", + "multiplayer.status.and_more": "... and %s more ...", + "multiplayer.status.cancelled": "Cancelled", + "multiplayer.status.cannot_connect": "Can't connect to server", + "multiplayer.status.cannot_resolve": "Can't resolve hostname", + "multiplayer.status.finished": "Finished", + "multiplayer.status.incompatible": "Incompatible version!", + "multiplayer.status.no_connection": "(no connection)", + "multiplayer.status.ping": "%s ms", + "multiplayer.status.old": "Old", + "multiplayer.status.pinging": "Pinging...", + "multiplayer.status.quitting": "Quitting", + "multiplayer.status.unknown": "???", + "multiplayer.status.unrequested": "Received unrequested status", + "multiplayer.status.request_handled": "Status request has been handled", + "multiplayer.disconnect.authservers_down": "Authentication servers are down. Please try again later, sorry!", + "multiplayer.disconnect.banned": "You are banned from this server", + "multiplayer.disconnect.banned.reason": "You are banned from this server.\nReason: %s", + "multiplayer.disconnect.banned.expiration": "\nYour ban will be removed on %s", + "multiplayer.disconnect.banned_ip.reason": "Your IP address is banned from this server.\nReason: %s", + "multiplayer.disconnect.banned_ip.expiration": "\nYour ban will be removed on %s", + "multiplayer.disconnect.duplicate_login": "You logged in from another location", + "multiplayer.disconnect.flying": "Flying is not enabled on this server", + "multiplayer.disconnect.generic": "Disconnected", + "multiplayer.disconnect.idling": "You have been idle for too long!", + "multiplayer.disconnect.illegal_characters": "Illegal characters in chat", + "multiplayer.disconnect.invalid_entity_attacked": "Attempting to attack an invalid entity", + "multiplayer.disconnect.invalid_packet": "Server sent an invalid packet", + "multiplayer.disconnect.invalid_player_data": "Invalid player data", + "multiplayer.disconnect.invalid_player_movement": "Invalid move player packet received", + "multiplayer.disconnect.invalid_vehicle_movement": "Invalid move vehicle packet received", + "multiplayer.disconnect.ip_banned": "You have been IP banned from this server", + "multiplayer.disconnect.kicked": "Kicked by an operator", + "multiplayer.disconnect.incompatible": "Incompatible client! Please use %s", + "multiplayer.disconnect.outdated_client": "Incompatible client! Please use %s", + "multiplayer.disconnect.outdated_server": "Incompatible client! Please use %s", + "multiplayer.disconnect.server_shutdown": "Server closed", + "multiplayer.disconnect.slow_login": "Took too long to log in", + "multiplayer.disconnect.unverified_username": "Failed to verify username!", + "multiplayer.disconnect.not_whitelisted": "You are not white-listed on this server!", + "multiplayer.disconnect.server_full": "The server is full!", + "multiplayer.disconnect.name_taken": "That name is already taken", + "multiplayer.disconnect.unexpected_query_response": "Unexpected custom data from client", + "multiplayer.disconnect.missing_tags": "Incomplete set of tags received from server.\nPlease contact server operator.", + "multiplayer.disconnect.expired_public_key": "Expired profile public key. Check that your system time is synchronized, and try restarting your game.", + "multiplayer.disconnect.invalid_public_key_signature": "Invalid signature for profile public key.\nTry restarting your game.", + "multiplayer.disconnect.out_of_order_chat": "Out-of-order chat packet received. Did your system time change?", + "multiplayer.disconnect.unsigned_chat": "Received chat packet with missing or invalid signature.", + "multiplayer.disconnect.too_many_pending_chats": "Too many unacknowledged chat messages", + "multiplayer.disconnect.chat_validation_failed": "Chat message validation failure", + "multiplayer.socialInteractions.not_available": "Social Interactions are only available in Multiplayer worlds", + "multiplayer.unsecureserver.toast.title": "Chat messages can't be verified", + "multiplayer.unsecureserver.toast": "Messages sent on this server may be modified and might not reflect the original message", + "chat.editBox": "chat", + "chat.cannotSend": "Cannot send chat message", + "chat.disabled.options": "Chat disabled in client options", + "chat.disabled.launcher": "Chat disabled by launcher option. Cannot send message", + "chat.disabled.profile": "Chat not allowed by account settings. Press '%s' again for more information", + "chat.disabled.profile.moreInfo": "Chat not allowed by account settings. Cannot send or view messages.", + "chat.disabled.expiredProfileKey": "Chat disabled due to expired profile public key. Please try reconnecting.", + "chat.disabled.chain_broken": "Chat disabled due to broken chain. Please try reconnecting.", + "chat.disabled.missingProfileKey": "Chat disabled due to missing profile public key. Please try reconnecting.", + "chat.type.text": "<%s> %s", + "chat.type.text.narrate": "%s says %s", + "chat.type.emote": "* %s %s", + "chat.type.announcement": "[%s] %s", + "chat.type.admin": "[%s: %s]", + "chat.type.advancement.task": "%s has made the advancement %s", + "chat.type.advancement.challenge": "%s has completed the challenge %s", + "chat.type.advancement.goal": "%s has reached the goal %s", + "chat.type.team.text": "%s <%s> %s", + "chat.type.team.sent": "-> %s <%s> %s", + "chat.type.team.hover": "Message Team", + "chat.link.confirm": "Are you sure you want to open the following website?", + "chat.link.warning": "Never open links from people that you don't trust!", + "chat.copy": "Copy to Clipboard", + "chat.copy.click": "Click to Copy to Clipboard", + "chat.link.confirmTrusted": "Do you want to open this link or copy it to your clipboard?", + "chat.link.open": "Open in Browser", + "chat.coordinates": "%s, %s, %s", + "chat.coordinates.tooltip": "Click to teleport", + "chat.queue": "[+%s pending lines]", + "chat.square_brackets": "[%s]", + "chat.tag.system": "Server message. Cannot be reported.", + "chat.tag.system_single_player": "Server message.", + "chat.tag.not_secure": "Unverified message. Cannot be reported.", + "chat.tag.modified": "Message modified by the server. Original:", + "chat.filtered_full": "The server has hidden your message for some players.", + "chat.filtered": "Filtered by the server.", + "chat.deleted_marker": "This chat message has been deleted by the server.", + "menu.playdemo": "Play Demo World", + "menu.resetdemo": "Reset Demo World", + "demo.day.1": "This demo will last five game days. Do your best!", + "demo.day.2": "Day Two", + "demo.day.3": "Day Three", + "demo.day.4": "Day Four", + "demo.day.5": "This is your last day!", + "demo.day.warning": "Your time is almost up!", + "demo.day.6": "You have passed your fifth day. Use %s to save a screenshot of your creation.", + "demo.reminder": "The demo time has expired. Buy the game to continue or start a new world!", + "demo.remainingTime": "Remaining time: %s", + "demo.demoExpired": "Demo time's up!", + "demo.help.movement": "Use the %1$s, %2$s, %3$s, %4$s keys and the mouse to move around", + "demo.help.movementShort": "Move by pressing the %1$s, %2$s, %3$s, %4$s keys", + "demo.help.movementMouse": "Look around using the mouse", + "demo.help.jump": "Jump by pressing the %1$s key", + "demo.help.inventory": "Use the %1$s key to open your inventory", + "demo.help.title": "Minecraft Demo Mode", + "demo.help.fullWrapped": "This demo will last 5 in-game days (about 1 hour and 40 minutes of real time). Check the advancements for hints! Have fun!", + "demo.help.buy": "Purchase Now!", + "demo.help.later": "Continue Playing!", + "connect.connecting": "Connecting to the server...", + "connect.aborted": "Aborted", + "connect.authorizing": "Logging in...", + "connect.negotiating": "Negotiating...", + "connect.encrypting": "Encrypting...", + "connect.joining": "Joining world...", + "connect.failed": "Failed to connect to the server", + "disconnect.genericReason": "%s", + "disconnect.unknownHost": "Unknown host", + "disconnect.disconnected": "Disconnected by Server", + "disconnect.lost": "Connection Lost", + "disconnect.kicked": "Was kicked from the game", + "disconnect.timeout": "Timed out", + "disconnect.closed": "Connection closed", + "disconnect.loginFailed": "Failed to log in", + "disconnect.loginFailedInfo": "Failed to log in: %s", + "disconnect.loginFailedInfo.serversUnavailable": "The authentication servers are currently not reachable. Please try again.", + "disconnect.loginFailedInfo.invalidSession": "Invalid session (Try restarting your game and the launcher)", + "disconnect.loginFailedInfo.insufficientPrivileges": "Multiplayer is disabled. Please check your Microsoft account settings.", + "disconnect.loginFailedInfo.userBanned": "You are banned from playing online", + "disconnect.quitting": "Quitting", + "disconnect.endOfStream": "End of stream", + "disconnect.overflow": "Buffer overflow", + "disconnect.spam": "Kicked for spamming", + "disconnect.exceeded_packet_rate": "Kicked for exceeding packet rate limit", + "soundCategory.master": "Master Volume", + "soundCategory.music": "Music", + "soundCategory.record": "Jukebox/Note Blocks", + "soundCategory.weather": "Weather", + "soundCategory.hostile": "Hostile Creatures", + "soundCategory.neutral": "Friendly Creatures", + "soundCategory.player": "Players", + "soundCategory.block": "Blocks", + "soundCategory.ambient": "Ambient/Environment", + "soundCategory.voice": "Voice/Speech", + "record.nowPlaying": "Now Playing: %s", + "options.off": "OFF", + "options.on": "ON", + "options.off.composed": "%s: OFF", + "options.on.composed": "%s: ON", + "options.generic_value": "%s: %s", + "options.pixel_value": "%s: %spx", + "options.percent_value": "%s: %s%%", + "options.percent_add_value": "%s: +%s%%", + "options.visible": "Shown", + "options.hidden": "Hidden", + "options.title": "Options", + "options.controls": "Controls...", + "options.video": "Video Settings...", + "options.language": "Language...", + "options.sounds": "Music & Sounds...", + "options.sounds.title": "Music & Sound Options", + "options.languageWarning": "Language translations may not be 100%% accurate", + "options.videoTitle": "Video Settings", + "options.mouse_settings": "Mouse Settings...", + "options.mouse_settings.title": "Mouse Settings", + "options.customizeTitle": "Customize World Settings", + "options.invertMouse": "Invert Mouse", + "options.fov": "FOV", + "options.fov.min": "Normal", + "options.fov.max": "Quake Pro", + "options.screenEffectScale": "Distortion Effects", + "options.screenEffectScale.tooltip": "Strength of nausea and Nether portal screen distortion effects.\nAt lower values, the nausea effect is replaced with a green overlay.", + "options.fovEffectScale": "FOV Effects", + "options.fovEffectScale.tooltip": "Controls how much the field of view can change with gameplay effects.", + "options.darknessEffectScale": "Darkness Pulsing", + "options.darknessEffectScale.tooltip": "Controls how much the Darkness effect pulses when a Warden or Sculk Shrieker gives it to you.", + "options.biomeBlendRadius": "Biome Blend", + "options.biomeBlendRadius.1": "OFF (Fastest)", + "options.biomeBlendRadius.3": "3x3 (Fast)", + "options.biomeBlendRadius.5": "5x5 (Normal)", + "options.biomeBlendRadius.7": "7x7 (High)", + "options.biomeBlendRadius.9": "9x9 (Very High)", + "options.biomeBlendRadius.11": "11x11 (Extreme)", + "options.biomeBlendRadius.13": "13x13 (Showoff)", + "options.biomeBlendRadius.15": "15x15 (Maximum)", + "options.gamma": "Brightness", + "options.gamma.min": "Moody", + "options.gamma.default": "Default", + "options.gamma.max": "Bright", + "options.sensitivity": "Sensitivity", + "options.sensitivity.min": "*yawn*", + "options.sensitivity.max": "HYPERSPEED!!!", + "options.renderDistance": "Render Distance", + "options.simulationDistance": "Simulation Distance", + "options.entityDistanceScaling": "Entity Distance", + "options.viewBobbing": "View Bobbing", + "options.ao": "Smooth Lighting", + "options.ao.off": "OFF", + "options.ao.min": "Minimum", + "options.ao.max": "Maximum", + "options.prioritizeChunkUpdates": "Chunk Builder", + "options.prioritizeChunkUpdates.none": "Threaded", + "options.prioritizeChunkUpdates.byPlayer": "Semi Blocking", + "options.prioritizeChunkUpdates.nearby": "Fully Blocking", + "options.prioritizeChunkUpdates.none.tooltip": "Nearby chunks are compiled in parallel threads. This may result in brief visual holes when blocks are destroyed.", + "options.prioritizeChunkUpdates.byPlayer.tooltip": "Some actions within a chunk will recompile the chunk immediately. This includes block placing & destroying.", + "options.prioritizeChunkUpdates.nearby.tooltip": "Nearby chunks are always compiled immediately. This may impact game performance when blocks are placed or destroyed.", + "options.chunks": "%s chunks", + "options.framerate": "%s fps", + "options.framerateLimit": "Max Framerate", + "options.framerateLimit.max": "Unlimited", + "options.difficulty": "Difficulty", + "options.difficulty.online": "Server Difficulty", + "options.difficulty.peaceful": "Peaceful", + "options.difficulty.easy": "Easy", + "options.difficulty.normal": "Normal", + "options.difficulty.hard": "Hard", + "options.difficulty.hardcore": "Hardcore", + "options.graphics": "Graphics", + "options.graphics.fabulous.tooltip": "%s graphics uses screen shaders for drawing weather, clouds, and particles behind translucent blocks and water.\nThis may severely impact performance for portable devices and 4K displays.", + "options.graphics.fabulous": "Fabulous!", + "options.graphics.fancy.tooltip": "Fancy graphics balances performance and quality for the majority of machines.\nWeather, clouds, and particles may not appear behind translucent blocks or water.", + "options.graphics.fancy": "Fancy", + "options.graphics.fast.tooltip": "Fast graphics reduces the amount of visible rain and snow.\nTransparency effects are disabled for various blocks such as leaves.", + "options.graphics.fast": "Fast", + "options.graphics.warning.title": "Graphics Device Unsupported", + "options.graphics.warning.message": "Your graphics device is detected as unsupported for the %s graphics option.\n\nYou may ignore this and continue, however support will not be provided for your device if you choose to use %s graphics.", + "options.graphics.warning.renderer": "Renderer detected: [%s]", + "options.graphics.warning.vendor": "Vendor detected: [%s]", + "options.graphics.warning.version": "OpenGL Version detected: [%s]", + "options.graphics.warning.accept": "Continue without Support", + "options.graphics.warning.cancel": "Take me Back", + "options.clouds.fancy": "Fancy", + "options.clouds.fast": "Fast", + "options.guiScale": "GUI Scale", + "options.guiScale.auto": "Auto", + "options.renderClouds": "Clouds", + "options.particles": "Particles", + "options.particles.all": "All", + "options.particles.decreased": "Decreased", + "options.particles.minimal": "Minimal", + "options.multiplayer.title": "Multiplayer Settings...", + "options.chat.title": "Chat Settings...", + "options.chat.visibility": "Chat", + "options.chat.visibility.full": "Shown", + "options.chat.visibility.system": "Commands Only", + "options.chat.visibility.hidden": "Hidden", + "options.chat.color": "Colors", + "options.chat.opacity": "Chat Text Opacity", + "options.chat.line_spacing": "Line Spacing", + "options.chat.links": "Web Links", + "options.chat.links.prompt": "Prompt on Links", + "options.chat.delay_none": "Chat Delay: None", + "options.chat.delay": "Chat Delay: %s seconds", + "options.chat.scale": "Chat Text Size", + "options.chat.width": "Width", + "options.chat.height.focused": "Focused Height", + "options.chat.height.unfocused": "Unfocused Height", + "options.onlyShowSecureChat": "Only Show Secure Chat", + "options.onlyShowSecureChat.tooltip": "Only display messages from other players that can be verified to have been sent by that player, and have not been modified.", + "options.accessibility.title": "Accessibility Settings...", + "options.accessibility.text_background": "Text Background", + "options.accessibility.text_background.chat": "Chat", + "options.accessibility.text_background.everywhere": "Everywhere", + "options.accessibility.text_background_opacity": "Text Background Opacity", + "options.accessibility.panorama_speed": "Panorama Scroll Speed", + "options.accessibility.link": "Accessibility Guide", + "options.telemetry": "Telemetry Data", + "options.telemetry.button": "Data Collection", + "options.telemetry.state.none": "None", + "options.telemetry.state.minimal": "Minimal", + "options.telemetry.state.all": "All", + "options.telemetry.button.tooltip": "\"%s\" includes only the required data.\n\"%s\" includes optional, as well as the required data.", + "options.audioDevice": "Device", + "options.audioDevice.default": "System Default", + "options.key.toggle": "Toggle", + "options.key.hold": "Hold", + "options.skinCustomisation": "Skin Customization...", + "options.skinCustomisation.title": "Skin Customization", + "options.modelPart.cape": "Cape", + "options.modelPart.hat": "Hat", + "options.modelPart.jacket": "Jacket", + "options.modelPart.left_sleeve": "Left Sleeve", + "options.modelPart.right_sleeve": "Right Sleeve", + "options.modelPart.left_pants_leg": "Left Pants Leg", + "options.modelPart.right_pants_leg": "Right Pants Leg", + "options.resourcepack": "Resource Packs...", + "options.fullscreen": "Fullscreen", + "options.vsync": "VSync", + "options.touchscreen": "Touchscreen Mode", + "options.reducedDebugInfo": "Reduced Debug Info", + "options.entityShadows": "Entity Shadows", + "options.mainHand": "Main Hand", + "options.mainHand.left": "Left", + "options.mainHand.right": "Right", + "options.attackIndicator": "Attack Indicator", + "options.attack.crosshair": "Crosshair", + "options.attack.hotbar": "Hotbar", + "options.showSubtitles": "Show Subtitles", + "options.directionalAudio": "Directional Audio", + "options.directionalAudio.on.tooltip": "Uses HRTF-based directional audio to improve the simulation of 3D sound. Requires HRTF compatible audio hardware, and is best experienced with headphones.", + "options.directionalAudio.off.tooltip": "Classic Stereo sound", + "options.online": "Online...", + "options.online.title": "Online Options", + "options.allowServerListing": "Allow Server Listings", + "options.allowServerListing.tooltip": "Servers may list online players as part of their public status.\nWith this option off your name will not show up in such lists.", + "options.realmsNotifications": "Realms Notifications", + "options.autoJump": "Auto-Jump", + "options.operatorItemsTab": "Operator Items Tab", + "options.autoSuggestCommands": "Command Suggestions", + "options.autosaveIndicator": "Autosave Indicator", + "options.discrete_mouse_scroll": "Discrete Scrolling", + "options.mouseWheelSensitivity": "Scroll Sensitivity", + "options.rawMouseInput": "Raw Input", + "options.narrator": "Narrator", + "options.narrator.off": "OFF", + "options.narrator.all": "Narrates All", + "options.narrator.chat": "Narrates Chat", + "options.narrator.system": "Narrates System", + "options.narrator.notavailable": "Not Available", + "options.fullscreen.resolution": "Fullscreen Resolution", + "options.fullscreen.unavailable": "Setting unavailable", + "options.fullscreen.current": "Current", + "options.mipmapLevels": "Mipmap Levels", + "options.forceUnicodeFont": "Force Unicode Font", + "options.hideMatchedNames": "Hide Matched Names", + "options.hideMatchedNames.tooltip": "3rd-party Servers may send chat messages in non-standard formats.\nWith this option on: hidden players will be matched based on chat sender names.", + "options.darkMojangStudiosBackgroundColor": "Monochrome Logo", + "options.darkMojangStudiosBackgroundColor.tooltip": "Changes the Mojang Studios loading screen background color to black.", + "options.hideLightningFlashes": "Hide Lightning Flashes", + "options.hideLightningFlashes.tooltip": "Prevents lightning bolts from making the sky flash. The bolts themselves will still be visible.", + "narrator.toast.disabled": "Narrator Disabled", + "narrator.toast.enabled": "Narrator Enabled", + "difficulty.lock.title": "Lock World Difficulty", + "difficulty.lock.question": "Are you sure you want to lock the difficulty of this world? This will set this world to always be %1$s, and you will never be able to change that again.", + "title.32bit.deprecation": "32-bit system detected: this may prevent you from playing in the future as a 64-bit system will be required!", + "title.32bit.deprecation.realms.header": "32-bit system detected", + "title.32bit.deprecation.realms": "Minecraft will soon require a 64-bit system, which will prevent you from playing or using Realms on this device. You will need to manually cancel any Realms subscription.", + "title.32bit.deprecation.realms.check": "Do not show this screen again", + "title.multiplayer.disabled": "Multiplayer is disabled. Please check your Microsoft account settings.", + "title.multiplayer.disabled.banned.temporary": "Your account is temporarily suspended from online play", + "title.multiplayer.disabled.banned.permanent": "Your account is permanently suspended from online play", + "controls.title": "Controls", + "controls.reset": "Reset", + "controls.resetAll": "Reset Keys", + "controls.keybinds": "Key Binds...", + "controls.keybinds.title": "Key Binds", + "key.sprint": "Sprint", + "key.forward": "Walk Forwards", + "key.left": "Strafe Left", + "key.back": "Walk Backwards", + "key.right": "Strafe Right", + "key.jump": "Jump", + "key.inventory": "Open/Close Inventory", + "key.drop": "Drop Selected Item", + "key.swapOffhand": "Swap Item With Offhand", + "key.chat": "Open Chat", + "key.sneak": "Sneak", + "key.playerlist": "List Players", + "key.attack": "Attack/Destroy", + "key.use": "Use Item/Place Block", + "key.pickItem": "Pick Block", + "key.command": "Open Command", + "key.socialInteractions": "Social Interactions Screen", + "key.screenshot": "Take Screenshot", + "key.togglePerspective": "Toggle Perspective", + "key.smoothCamera": "Toggle Cinematic Camera", + "key.fullscreen": "Toggle Fullscreen", + "key.spectatorOutlines": "Highlight Players (Spectators)", + "key.hotbar.1": "Hotbar Slot 1", + "key.hotbar.2": "Hotbar Slot 2", + "key.hotbar.3": "Hotbar Slot 3", + "key.hotbar.4": "Hotbar Slot 4", + "key.hotbar.5": "Hotbar Slot 5", + "key.hotbar.6": "Hotbar Slot 6", + "key.hotbar.7": "Hotbar Slot 7", + "key.hotbar.8": "Hotbar Slot 8", + "key.hotbar.9": "Hotbar Slot 9", + "key.saveToolbarActivator": "Save Hotbar Activator", + "key.loadToolbarActivator": "Load Hotbar Activator", + "key.advancements": "Advancements", + "key.categories.movement": "Movement", + "key.categories.misc": "Miscellaneous", + "key.categories.multiplayer": "Multiplayer", + "key.categories.gameplay": "Gameplay", + "key.categories.ui": "Game Interface", + "key.categories.inventory": "Inventory", + "key.categories.creative": "Creative Mode", + "key.mouse.left": "Left Button", + "key.mouse.right": "Right Button", + "key.mouse.middle": "Middle Button", + "key.mouse": "Button %1$s", + "key.keyboard.unknown": "Not bound", + "key.keyboard.apostrophe": "'", + "key.keyboard.backslash": "\\", + "key.keyboard.backspace": "Backspace", + "key.keyboard.comma": ",", + "key.keyboard.delete": "Delete", + "key.keyboard.end": "End", + "key.keyboard.enter": "Enter", + "key.keyboard.equal": "=", + "key.keyboard.escape": "Escape", + "key.keyboard.f1": "F1", + "key.keyboard.f2": "F2", + "key.keyboard.f3": "F3", + "key.keyboard.f4": "F4", + "key.keyboard.f5": "F5", + "key.keyboard.f6": "F6", + "key.keyboard.f7": "F7", + "key.keyboard.f8": "F8", + "key.keyboard.f9": "F9", + "key.keyboard.f10": "F10", + "key.keyboard.f11": "F11", + "key.keyboard.f12": "F12", + "key.keyboard.f13": "F13", + "key.keyboard.f14": "F14", + "key.keyboard.f15": "F15", + "key.keyboard.f16": "F16", + "key.keyboard.f17": "F17", + "key.keyboard.f18": "F18", + "key.keyboard.f19": "F19", + "key.keyboard.f20": "F20", + "key.keyboard.f21": "F21", + "key.keyboard.f22": "F22", + "key.keyboard.f23": "F23", + "key.keyboard.f24": "F24", + "key.keyboard.f25": "F25", + "key.keyboard.grave.accent": "`", + "key.keyboard.home": "Home", + "key.keyboard.insert": "Insert", + "key.keyboard.keypad.0": "Keypad 0", + "key.keyboard.keypad.1": "Keypad 1", + "key.keyboard.keypad.2": "Keypad 2", + "key.keyboard.keypad.3": "Keypad 3", + "key.keyboard.keypad.4": "Keypad 4", + "key.keyboard.keypad.5": "Keypad 5", + "key.keyboard.keypad.6": "Keypad 6", + "key.keyboard.keypad.7": "Keypad 7", + "key.keyboard.keypad.8": "Keypad 8", + "key.keyboard.keypad.9": "Keypad 9", + "key.keyboard.keypad.add": "Keypad +", + "key.keyboard.keypad.decimal": "Keypad Decimal", + "key.keyboard.keypad.enter": "Keypad Enter", + "key.keyboard.keypad.equal": "Keypad =", + "key.keyboard.keypad.multiply": "Keypad *", + "key.keyboard.keypad.divide": "Keypad /", + "key.keyboard.keypad.subtract": "Keypad -", + "key.keyboard.left.bracket": "[", + "key.keyboard.right.bracket": "]", + "key.keyboard.minus": "-", + "key.keyboard.num.lock": "Num Lock", + "key.keyboard.caps.lock": "Caps Lock", + "key.keyboard.scroll.lock": "Scroll Lock", + "key.keyboard.page.down": "Page Down", + "key.keyboard.page.up": "Page Up", + "key.keyboard.pause": "Pause", + "key.keyboard.period": ".", + "key.keyboard.left.control": "Left Control", + "key.keyboard.right.control": "Right Control", + "key.keyboard.left.alt": "Left Alt", + "key.keyboard.right.alt": "Right Alt", + "key.keyboard.left.shift": "Left Shift", + "key.keyboard.right.shift": "Right Shift", + "key.keyboard.left.win": "Left Win", + "key.keyboard.right.win": "Right Win", + "key.keyboard.semicolon": ";", + "key.keyboard.slash": "/", + "key.keyboard.space": "Space", + "key.keyboard.tab": "Tab", + "key.keyboard.up": "Up Arrow", + "key.keyboard.down": "Down Arrow", + "key.keyboard.left": "Left Arrow", + "key.keyboard.right": "Right Arrow", + "key.keyboard.menu": "Menu", + "key.keyboard.print.screen": "Print Screen", + "key.keyboard.world.1": "World 1", + "key.keyboard.world.2": "World 2", + "pack.available.title": "Available", + "pack.selected.title": "Selected", + "pack.incompatible": "Incompatible", + "pack.incompatible.old": "(Made for an older version of Minecraft)", + "pack.incompatible.new": "(Made for a newer version of Minecraft)", + "pack.incompatible.confirm.title": "Are you sure you want to load this pack?", + "pack.incompatible.confirm.old": "This pack was made for an older version of Minecraft and may no longer work correctly.", + "pack.incompatible.confirm.new": "This pack was made for a newer version of Minecraft and may not work correctly.", + "pack.dropInfo": "Drag and drop files into this window to add packs", + "pack.dropConfirm": "Do you want to add the following packs to Minecraft?", + "pack.copyFailure": "Failed to copy packs", + "pack.nameAndSource": "%s (%s)", + "pack.openFolder": "Open Pack Folder", + "pack.folderInfo": "(Place pack files here)", + "resourcePack.title": "Select Resource Packs", + "resourcePack.server.name": "World Specific Resources", + "resourcePack.programmer_art.name": "Programmer Art", + "resourcePack.broken_assets": "BROKEN ASSETS DETECTED", + "resourcePack.vanilla.name": "Default", + "resourcePack.vanilla.description": "The default look and feel of Minecraft", + "resourcePack.load_fail": "Resource reload failed", + "dataPack.title": "Select Data Packs", + "dataPack.validation.working": "Validating selected data packs...", + "dataPack.validation.failed": "Data pack validation failed!", + "dataPack.validation.back": "Go Back", + "dataPack.validation.reset": "Reset to Default", + "dataPack.vanilla.name": "Default", + "dataPack.vanilla.description": "The default data for Minecraft", + "dataPack.bundle.description": "Enables experimental Bundle item", + "dataPack.update_1_20.description": "New features and content for Minecraft 1.20", + "sign.edit": "Edit Sign Message", + "hanging_sign.edit": "Edit Hanging Sign Message", + "book.pageIndicator": "Page %1$s of %2$s", + "book.byAuthor": "by %1$s", + "book.signButton": "Sign", + "book.editTitle": "Enter Book Title:", + "book.finalizeButton": "Sign and Close", + "book.finalizeWarning": "Note! When you sign the book, it will no longer be editable.", + "book.generation.0": "Original", + "book.generation.1": "Copy of original", + "book.generation.2": "Copy of a copy", + "book.generation.3": "Tattered", + "book.invalid.tag": "* Invalid book tag *", + "merchant.deprecated": "Villagers restock up to two times per day.", + "merchant.current_level": "Trader's current level", + "merchant.next_level": "Trader's next level", + "merchant.level.1": "Novice", + "merchant.level.2": "Apprentice", + "merchant.level.3": "Journeyman", + "merchant.level.4": "Expert", + "merchant.level.5": "Master", + "merchant.trades": "Trades", + "block.minecraft.air": "Air", + "block.minecraft.barrier": "Barrier", + "block.minecraft.light": "Light", + "block.minecraft.stone": "Stone", + "block.minecraft.granite": "Granite", + "block.minecraft.polished_granite": "Polished Granite", + "block.minecraft.diorite": "Diorite", + "block.minecraft.polished_diorite": "Polished Diorite", + "block.minecraft.andesite": "Andesite", + "block.minecraft.polished_andesite": "Polished Andesite", + "block.minecraft.hay_block": "Hay Bale", + "block.minecraft.grass_block": "Grass Block", + "block.minecraft.dirt": "Dirt", + "block.minecraft.coarse_dirt": "Coarse Dirt", + "block.minecraft.podzol": "Podzol", + "block.minecraft.cobblestone": "Cobblestone", + "block.minecraft.oak_planks": "Oak Planks", + "block.minecraft.spruce_planks": "Spruce Planks", + "block.minecraft.birch_planks": "Birch Planks", + "block.minecraft.jungle_planks": "Jungle Planks", + "block.minecraft.acacia_planks": "Acacia Planks", + "block.minecraft.dark_oak_planks": "Dark Oak Planks", + "block.minecraft.mangrove_planks": "Mangrove Planks", + "block.minecraft.bamboo_planks": "Bamboo Planks", + "block.minecraft.bamboo_mosaic": "Bamboo Mosaic", + "block.minecraft.oak_sapling": "Oak Sapling", + "block.minecraft.spruce_sapling": "Spruce Sapling", + "block.minecraft.birch_sapling": "Birch Sapling", + "block.minecraft.jungle_sapling": "Jungle Sapling", + "block.minecraft.acacia_sapling": "Acacia Sapling", + "block.minecraft.dark_oak_sapling": "Dark Oak Sapling", + "block.minecraft.mangrove_propagule": "Mangrove Propagule", + "block.minecraft.oak_door": "Oak Door", + "block.minecraft.spruce_door": "Spruce Door", + "block.minecraft.birch_door": "Birch Door", + "block.minecraft.jungle_door": "Jungle Door", + "block.minecraft.acacia_door": "Acacia Door", + "block.minecraft.dark_oak_door": "Dark Oak Door", + "block.minecraft.mangrove_door": "Mangrove Door", + "block.minecraft.bamboo_door": "Bamboo Door", + "block.minecraft.bedrock": "Bedrock", + "block.minecraft.water": "Water", + "block.minecraft.lava": "Lava", + "block.minecraft.sand": "Sand", + "block.minecraft.red_sand": "Red Sand", + "block.minecraft.sandstone": "Sandstone", + "block.minecraft.chiseled_sandstone": "Chiseled Sandstone", + "block.minecraft.cut_sandstone": "Cut Sandstone", + "block.minecraft.red_sandstone": "Red Sandstone", + "block.minecraft.chiseled_red_sandstone": "Chiseled Red Sandstone", + "block.minecraft.cut_red_sandstone": "Cut Red Sandstone", + "block.minecraft.gravel": "Gravel", + "block.minecraft.gold_ore": "Gold Ore", + "block.minecraft.deepslate_gold_ore": "Deepslate Gold Ore", + "block.minecraft.nether_gold_ore": "Nether Gold Ore", + "block.minecraft.iron_ore": "Iron Ore", + "block.minecraft.deepslate_iron_ore": "Deepslate Iron Ore", + "block.minecraft.coal_ore": "Coal Ore", + "block.minecraft.deepslate_coal_ore": "Deepslate Coal Ore", + "block.minecraft.oak_wood": "Oak Wood", + "block.minecraft.spruce_wood": "Spruce Wood", + "block.minecraft.birch_wood": "Birch Wood", + "block.minecraft.jungle_wood": "Jungle Wood", + "block.minecraft.acacia_wood": "Acacia Wood", + "block.minecraft.dark_oak_wood": "Dark Oak Wood", + "block.minecraft.mangrove_wood": "Mangrove Wood", + "block.minecraft.oak_log": "Oak Log", + "block.minecraft.spruce_log": "Spruce Log", + "block.minecraft.birch_log": "Birch Log", + "block.minecraft.jungle_log": "Jungle Log", + "block.minecraft.acacia_log": "Acacia Log", + "block.minecraft.dark_oak_log": "Dark Oak Log", + "block.minecraft.mangrove_log": "Mangrove Log", + "block.minecraft.mangrove_roots": "Mangrove Roots", + "block.minecraft.muddy_mangrove_roots": "Muddy Mangrove Roots", + "block.minecraft.bamboo_block": "Block of Bamboo", + "block.minecraft.stripped_oak_log": "Stripped Oak Log", + "block.minecraft.stripped_spruce_log": "Stripped Spruce Log", + "block.minecraft.stripped_birch_log": "Stripped Birch Log", + "block.minecraft.stripped_jungle_log": "Stripped Jungle Log", + "block.minecraft.stripped_acacia_log": "Stripped Acacia Log", + "block.minecraft.stripped_dark_oak_log": "Stripped Dark Oak Log", + "block.minecraft.stripped_mangrove_log": "Stripped Mangrove Log", + "block.minecraft.stripped_bamboo_block": "Block of Stripped Bamboo", + "block.minecraft.stripped_oak_wood": "Stripped Oak Wood", + "block.minecraft.stripped_spruce_wood": "Stripped Spruce Wood", + "block.minecraft.stripped_birch_wood": "Stripped Birch Wood", + "block.minecraft.stripped_jungle_wood": "Stripped Jungle Wood", + "block.minecraft.stripped_acacia_wood": "Stripped Acacia Wood", + "block.minecraft.stripped_dark_oak_wood": "Stripped Dark Oak Wood", + "block.minecraft.stripped_mangrove_wood": "Stripped Mangrove Wood", + "block.minecraft.oak_leaves": "Oak Leaves", + "block.minecraft.spruce_leaves": "Spruce Leaves", + "block.minecraft.birch_leaves": "Birch Leaves", + "block.minecraft.jungle_leaves": "Jungle Leaves", + "block.minecraft.acacia_leaves": "Acacia Leaves", + "block.minecraft.dark_oak_leaves": "Dark Oak Leaves", + "block.minecraft.mangrove_leaves": "Mangrove Leaves", + "block.minecraft.dead_bush": "Dead Bush", + "block.minecraft.grass": "Grass", + "block.minecraft.fern": "Fern", + "block.minecraft.sponge": "Sponge", + "block.minecraft.wet_sponge": "Wet Sponge", + "block.minecraft.glass": "Glass", + "block.minecraft.kelp_plant": "Kelp Plant", + "block.minecraft.kelp": "Kelp", + "block.minecraft.dried_kelp_block": "Dried Kelp Block", + "block.minecraft.white_stained_glass": "White Stained Glass", + "block.minecraft.orange_stained_glass": "Orange Stained Glass", + "block.minecraft.magenta_stained_glass": "Magenta Stained Glass", + "block.minecraft.light_blue_stained_glass": "Light Blue Stained Glass", + "block.minecraft.yellow_stained_glass": "Yellow Stained Glass", + "block.minecraft.lime_stained_glass": "Lime Stained Glass", + "block.minecraft.pink_stained_glass": "Pink Stained Glass", + "block.minecraft.gray_stained_glass": "Gray Stained Glass", + "block.minecraft.light_gray_stained_glass": "Light Gray Stained Glass", + "block.minecraft.cyan_stained_glass": "Cyan Stained Glass", + "block.minecraft.purple_stained_glass": "Purple Stained Glass", + "block.minecraft.blue_stained_glass": "Blue Stained Glass", + "block.minecraft.brown_stained_glass": "Brown Stained Glass", + "block.minecraft.green_stained_glass": "Green Stained Glass", + "block.minecraft.red_stained_glass": "Red Stained Glass", + "block.minecraft.black_stained_glass": "Black Stained Glass", + "block.minecraft.white_stained_glass_pane": "White Stained Glass Pane", + "block.minecraft.orange_stained_glass_pane": "Orange Stained Glass Pane", + "block.minecraft.magenta_stained_glass_pane": "Magenta Stained Glass Pane", + "block.minecraft.light_blue_stained_glass_pane": "Light Blue Stained Glass Pane", + "block.minecraft.yellow_stained_glass_pane": "Yellow Stained Glass Pane", + "block.minecraft.lime_stained_glass_pane": "Lime Stained Glass Pane", + "block.minecraft.pink_stained_glass_pane": "Pink Stained Glass Pane", + "block.minecraft.gray_stained_glass_pane": "Gray Stained Glass Pane", + "block.minecraft.light_gray_stained_glass_pane": "Light Gray Stained Glass Pane", + "block.minecraft.cyan_stained_glass_pane": "Cyan Stained Glass Pane", + "block.minecraft.purple_stained_glass_pane": "Purple Stained Glass Pane", + "block.minecraft.blue_stained_glass_pane": "Blue Stained Glass Pane", + "block.minecraft.brown_stained_glass_pane": "Brown Stained Glass Pane", + "block.minecraft.green_stained_glass_pane": "Green Stained Glass Pane", + "block.minecraft.red_stained_glass_pane": "Red Stained Glass Pane", + "block.minecraft.black_stained_glass_pane": "Black Stained Glass Pane", + "block.minecraft.glass_pane": "Glass Pane", + "block.minecraft.dandelion": "Dandelion", + "block.minecraft.poppy": "Poppy", + "block.minecraft.blue_orchid": "Blue Orchid", + "block.minecraft.allium": "Allium", + "block.minecraft.azure_bluet": "Azure Bluet", + "block.minecraft.red_tulip": "Red Tulip", + "block.minecraft.orange_tulip": "Orange Tulip", + "block.minecraft.white_tulip": "White Tulip", + "block.minecraft.pink_tulip": "Pink Tulip", + "block.minecraft.oxeye_daisy": "Oxeye Daisy", + "block.minecraft.cornflower": "Cornflower", + "block.minecraft.lily_of_the_valley": "Lily of the Valley", + "block.minecraft.wither_rose": "Wither Rose", + "block.minecraft.sunflower": "Sunflower", + "block.minecraft.lilac": "Lilac", + "block.minecraft.tall_grass": "Tall Grass", + "block.minecraft.tall_seagrass": "Tall Seagrass", + "block.minecraft.large_fern": "Large Fern", + "block.minecraft.rose_bush": "Rose Bush", + "block.minecraft.peony": "Peony", + "block.minecraft.seagrass": "Seagrass", + "block.minecraft.sea_pickle": "Sea Pickle", + "block.minecraft.brown_mushroom": "Brown Mushroom", + "block.minecraft.red_mushroom_block": "Red Mushroom Block", + "block.minecraft.brown_mushroom_block": "Brown Mushroom Block", + "block.minecraft.mushroom_stem": "Mushroom Stem", + "block.minecraft.gold_block": "Block of Gold", + "block.minecraft.iron_block": "Block of Iron", + "block.minecraft.smooth_stone": "Smooth Stone", + "block.minecraft.smooth_sandstone": "Smooth Sandstone", + "block.minecraft.smooth_red_sandstone": "Smooth Red Sandstone", + "block.minecraft.smooth_quartz": "Smooth Quartz Block", + "block.minecraft.stone_slab": "Stone Slab", + "block.minecraft.smooth_stone_slab": "Smooth Stone Slab", + "block.minecraft.sandstone_slab": "Sandstone Slab", + "block.minecraft.red_sandstone_slab": "Red Sandstone Slab", + "block.minecraft.cut_sandstone_slab": "Cut Sandstone Slab", + "block.minecraft.cut_red_sandstone_slab": "Cut Red Sandstone Slab", + "block.minecraft.petrified_oak_slab": "Petrified Oak Slab", + "block.minecraft.cobblestone_slab": "Cobblestone Slab", + "block.minecraft.brick_slab": "Brick Slab", + "block.minecraft.stone_brick_slab": "Stone Brick Slab", + "block.minecraft.mud_brick_slab": "Mud Brick Slab", + "block.minecraft.nether_brick_slab": "Nether Brick Slab", + "block.minecraft.quartz_slab": "Quartz Slab", + "block.minecraft.oak_slab": "Oak Slab", + "block.minecraft.spruce_slab": "Spruce Slab", + "block.minecraft.birch_slab": "Birch Slab", + "block.minecraft.jungle_slab": "Jungle Slab", + "block.minecraft.acacia_slab": "Acacia Slab", + "block.minecraft.dark_oak_slab": "Dark Oak Slab", + "block.minecraft.mangrove_slab": "Mangrove Slab", + "block.minecraft.bamboo_slab": "Bamboo Slab", + "block.minecraft.bamboo_mosaic_slab": "Bamboo Mosaic Slab", + "block.minecraft.dark_prismarine_slab": "Dark Prismarine Slab", + "block.minecraft.prismarine_slab": "Prismarine Slab", + "block.minecraft.prismarine_brick_slab": "Prismarine Brick Slab", + "block.minecraft.bricks": "Bricks", + "block.minecraft.tnt": "TNT", + "block.minecraft.bookshelf": "Bookshelf", + "block.minecraft.chiseled_bookshelf": "Chiseled Bookshelf", + "block.minecraft.mossy_cobblestone": "Mossy Cobblestone", + "block.minecraft.obsidian": "Obsidian", + "block.minecraft.torch": "Torch", + "block.minecraft.wall_torch": "Wall Torch", + "block.minecraft.soul_torch": "Soul Torch", + "block.minecraft.soul_wall_torch": "Soul Wall Torch", + "block.minecraft.fire": "Fire", + "block.minecraft.spawner": "Monster Spawner", + "block.minecraft.spawner.desc1": "Interact with Spawn Egg:", + "block.minecraft.spawner.desc2": "Sets Mob Type", + "block.minecraft.respawn_anchor": "Respawn Anchor", + "block.minecraft.oak_stairs": "Oak Stairs", + "block.minecraft.spruce_stairs": "Spruce Stairs", + "block.minecraft.birch_stairs": "Birch Stairs", + "block.minecraft.jungle_stairs": "Jungle Stairs", + "block.minecraft.acacia_stairs": "Acacia Stairs", + "block.minecraft.dark_oak_stairs": "Dark Oak Stairs", + "block.minecraft.mangrove_stairs": "Mangrove Stairs", + "block.minecraft.bamboo_stairs": "Bamboo Stairs", + "block.minecraft.bamboo_mosaic_stairs": "Bamboo Mosaic Stairs", + "block.minecraft.dark_prismarine_stairs": "Dark Prismarine Stairs", + "block.minecraft.prismarine_stairs": "Prismarine Stairs", + "block.minecraft.prismarine_brick_stairs": "Prismarine Brick Stairs", + "block.minecraft.chest": "Chest", + "block.minecraft.trapped_chest": "Trapped Chest", + "block.minecraft.redstone_wire": "Redstone Wire", + "block.minecraft.diamond_ore": "Diamond Ore", + "block.minecraft.deepslate_diamond_ore": "Deepslate Diamond Ore", + "block.minecraft.coal_block": "Block of Coal", + "block.minecraft.diamond_block": "Block of Diamond", + "block.minecraft.crafting_table": "Crafting Table", + "block.minecraft.wheat": "Wheat Crops", + "block.minecraft.farmland": "Farmland", + "block.minecraft.furnace": "Furnace", + "block.minecraft.oak_sign": "Oak Sign", + "block.minecraft.spruce_sign": "Spruce Sign", + "block.minecraft.birch_sign": "Birch Sign", + "block.minecraft.acacia_sign": "Acacia Sign", + "block.minecraft.jungle_sign": "Jungle Sign", + "block.minecraft.dark_oak_sign": "Dark Oak Sign", + "block.minecraft.mangrove_sign": "Mangrove Sign", + "block.minecraft.bamboo_sign": "Bamboo Sign", + "block.minecraft.oak_wall_sign": "Oak Wall Sign", + "block.minecraft.spruce_wall_sign": "Spruce Wall Sign", + "block.minecraft.birch_wall_sign": "Birch Wall Sign", + "block.minecraft.acacia_wall_sign": "Acacia Wall Sign", + "block.minecraft.jungle_wall_sign": "Jungle Wall Sign", + "block.minecraft.dark_oak_wall_sign": "Dark Oak Wall Sign", + "block.minecraft.mangrove_wall_sign": "Mangrove Wall Sign", + "block.minecraft.bamboo_wall_sign": "Bamboo Wall Sign", + "block.minecraft.oak_hanging_sign": "Oak Hanging Sign", + "block.minecraft.spruce_hanging_sign": "Spruce Hanging Sign", + "block.minecraft.birch_hanging_sign": "Birch Hanging Sign", + "block.minecraft.acacia_hanging_sign": "Acacia Hanging Sign", + "block.minecraft.jungle_hanging_sign": "Jungle Hanging Sign", + "block.minecraft.crimson_hanging_sign": "Crimson Hanging Sign", + "block.minecraft.warped_hanging_sign": "Warped Hanging Sign", + "block.minecraft.dark_oak_hanging_sign": "Dark Oak Hanging Sign", + "block.minecraft.mangrove_hanging_sign": "Mangrove Hanging Sign", + "block.minecraft.bamboo_hanging_sign": "Bamboo Hanging Sign", + "block.minecraft.oak_wall_hanging_sign": "Oak Wall Hanging Sign", + "block.minecraft.spruce_wall_hanging_sign": "Spruce Wall Hanging Sign", + "block.minecraft.birch_wall_hanging_sign": "Birch Wall Hanging Sign", + "block.minecraft.acacia_wall_hanging_sign": "Acacia Wall Hanging Sign", + "block.minecraft.jungle_wall_hanging_sign": "Jungle Wall Hanging Sign", + "block.minecraft.dark_oak_wall_hanging_sign": "Dark Oak Wall Hanging Sign", + "block.minecraft.mangrove_wall_hanging_sign": "Mangrove Wall Hanging Sign", + "block.minecraft.crimson_wall_hanging_sign": "Crimson Wall Hanging Sign", + "block.minecraft.warped_wall_hanging_sign": "Warped Wall Hanging Sign", + "block.minecraft.bamboo_wall_hanging_sign": "Bamboo Wall Hanging Sign", + "block.minecraft.ladder": "Ladder", + "block.minecraft.scaffolding": "Scaffolding", + "block.minecraft.rail": "Rail", + "block.minecraft.powered_rail": "Powered Rail", + "block.minecraft.activator_rail": "Activator Rail", + "block.minecraft.detector_rail": "Detector Rail", + "block.minecraft.cobblestone_stairs": "Cobblestone Stairs", + "block.minecraft.sandstone_stairs": "Sandstone Stairs", + "block.minecraft.red_sandstone_stairs": "Red Sandstone Stairs", + "block.minecraft.lever": "Lever", + "block.minecraft.stone_pressure_plate": "Stone Pressure Plate", + "block.minecraft.oak_pressure_plate": "Oak Pressure Plate", + "block.minecraft.spruce_pressure_plate": "Spruce Pressure Plate", + "block.minecraft.birch_pressure_plate": "Birch Pressure Plate", + "block.minecraft.jungle_pressure_plate": "Jungle Pressure Plate", + "block.minecraft.acacia_pressure_plate": "Acacia Pressure Plate", + "block.minecraft.dark_oak_pressure_plate": "Dark Oak Pressure Plate", + "block.minecraft.mangrove_pressure_plate": "Mangrove Pressure Plate", + "block.minecraft.bamboo_pressure_plate": "Bamboo Pressure Plate", + "block.minecraft.light_weighted_pressure_plate": "Light Weighted Pressure Plate", + "block.minecraft.heavy_weighted_pressure_plate": "Heavy Weighted Pressure Plate", + "block.minecraft.iron_door": "Iron Door", + "block.minecraft.redstone_ore": "Redstone Ore", + "block.minecraft.deepslate_redstone_ore": "Deepslate Redstone Ore", + "block.minecraft.redstone_torch": "Redstone Torch", + "block.minecraft.redstone_wall_torch": "Redstone Wall Torch", + "block.minecraft.stone_button": "Stone Button", + "block.minecraft.oak_button": "Oak Button", + "block.minecraft.spruce_button": "Spruce Button", + "block.minecraft.birch_button": "Birch Button", + "block.minecraft.jungle_button": "Jungle Button", + "block.minecraft.acacia_button": "Acacia Button", + "block.minecraft.dark_oak_button": "Dark Oak Button", + "block.minecraft.mangrove_button": "Mangrove Button", + "block.minecraft.bamboo_button": "Bamboo Button", + "block.minecraft.snow": "Snow", + "block.minecraft.white_carpet": "White Carpet", + "block.minecraft.orange_carpet": "Orange Carpet", + "block.minecraft.magenta_carpet": "Magenta Carpet", + "block.minecraft.light_blue_carpet": "Light Blue Carpet", + "block.minecraft.yellow_carpet": "Yellow Carpet", + "block.minecraft.lime_carpet": "Lime Carpet", + "block.minecraft.pink_carpet": "Pink Carpet", + "block.minecraft.gray_carpet": "Gray Carpet", + "block.minecraft.light_gray_carpet": "Light Gray Carpet", + "block.minecraft.cyan_carpet": "Cyan Carpet", + "block.minecraft.purple_carpet": "Purple Carpet", + "block.minecraft.blue_carpet": "Blue Carpet", + "block.minecraft.brown_carpet": "Brown Carpet", + "block.minecraft.green_carpet": "Green Carpet", + "block.minecraft.red_carpet": "Red Carpet", + "block.minecraft.black_carpet": "Black Carpet", + "block.minecraft.ice": "Ice", + "block.minecraft.frosted_ice": "Frosted Ice", + "block.minecraft.packed_ice": "Packed Ice", + "block.minecraft.blue_ice": "Blue Ice", + "block.minecraft.cactus": "Cactus", + "block.minecraft.clay": "Clay", + "block.minecraft.white_terracotta": "White Terracotta", + "block.minecraft.orange_terracotta": "Orange Terracotta", + "block.minecraft.magenta_terracotta": "Magenta Terracotta", + "block.minecraft.light_blue_terracotta": "Light Blue Terracotta", + "block.minecraft.yellow_terracotta": "Yellow Terracotta", + "block.minecraft.lime_terracotta": "Lime Terracotta", + "block.minecraft.pink_terracotta": "Pink Terracotta", + "block.minecraft.gray_terracotta": "Gray Terracotta", + "block.minecraft.light_gray_terracotta": "Light Gray Terracotta", + "block.minecraft.cyan_terracotta": "Cyan Terracotta", + "block.minecraft.purple_terracotta": "Purple Terracotta", + "block.minecraft.blue_terracotta": "Blue Terracotta", + "block.minecraft.brown_terracotta": "Brown Terracotta", + "block.minecraft.green_terracotta": "Green Terracotta", + "block.minecraft.red_terracotta": "Red Terracotta", + "block.minecraft.black_terracotta": "Black Terracotta", + "block.minecraft.terracotta": "Terracotta", + "block.minecraft.sugar_cane": "Sugar Cane", + "block.minecraft.jukebox": "Jukebox", + "block.minecraft.oak_fence": "Oak Fence", + "block.minecraft.spruce_fence": "Spruce Fence", + "block.minecraft.birch_fence": "Birch Fence", + "block.minecraft.jungle_fence": "Jungle Fence", + "block.minecraft.acacia_fence": "Acacia Fence", + "block.minecraft.dark_oak_fence": "Dark Oak Fence", + "block.minecraft.mangrove_fence": "Mangrove Fence", + "block.minecraft.bamboo_fence": "Bamboo Fence", + "block.minecraft.oak_fence_gate": "Oak Fence Gate", + "block.minecraft.spruce_fence_gate": "Spruce Fence Gate", + "block.minecraft.birch_fence_gate": "Birch Fence Gate", + "block.minecraft.jungle_fence_gate": "Jungle Fence Gate", + "block.minecraft.acacia_fence_gate": "Acacia Fence Gate", + "block.minecraft.dark_oak_fence_gate": "Dark Oak Fence Gate", + "block.minecraft.mangrove_fence_gate": "Mangrove Fence Gate", + "block.minecraft.bamboo_fence_gate": "Bamboo Fence Gate", + "block.minecraft.pumpkin_stem": "Pumpkin Stem", + "block.minecraft.attached_pumpkin_stem": "Attached Pumpkin Stem", + "block.minecraft.pumpkin": "Pumpkin", + "block.minecraft.carved_pumpkin": "Carved Pumpkin", + "block.minecraft.jack_o_lantern": "Jack o'Lantern", + "block.minecraft.netherrack": "Netherrack", + "block.minecraft.soul_sand": "Soul Sand", + "block.minecraft.glowstone": "Glowstone", + "block.minecraft.nether_portal": "Nether Portal", + "block.minecraft.white_wool": "White Wool", + "block.minecraft.orange_wool": "Orange Wool", + "block.minecraft.magenta_wool": "Magenta Wool", + "block.minecraft.light_blue_wool": "Light Blue Wool", + "block.minecraft.yellow_wool": "Yellow Wool", + "block.minecraft.lime_wool": "Lime Wool", + "block.minecraft.pink_wool": "Pink Wool", + "block.minecraft.gray_wool": "Gray Wool", + "block.minecraft.light_gray_wool": "Light Gray Wool", + "block.minecraft.cyan_wool": "Cyan Wool", + "block.minecraft.purple_wool": "Purple Wool", + "block.minecraft.blue_wool": "Blue Wool", + "block.minecraft.brown_wool": "Brown Wool", + "block.minecraft.green_wool": "Green Wool", + "block.minecraft.red_wool": "Red Wool", + "block.minecraft.black_wool": "Black Wool", + "block.minecraft.lapis_ore": "Lapis Lazuli Ore", + "block.minecraft.deepslate_lapis_ore": "Deepslate Lapis Lazuli Ore", + "block.minecraft.lapis_block": "Block of Lapis Lazuli", + "block.minecraft.dispenser": "Dispenser", + "block.minecraft.dropper": "Dropper", + "block.minecraft.note_block": "Note Block", + "block.minecraft.cake": "Cake", + "block.minecraft.bed.occupied": "This bed is occupied", + "block.minecraft.bed.obstructed": "This bed is obstructed", + "block.minecraft.bed.no_sleep": "You can sleep only at night or during thunderstorms", + "block.minecraft.bed.too_far_away": "You may not rest now; the bed is too far away", + "block.minecraft.bed.not_safe": "You may not rest now; there are monsters nearby", + "block.minecraft.spawn.not_valid": "You have no home bed or charged respawn anchor, or it was obstructed", + "block.minecraft.set_spawn": "Respawn point set", + "block.minecraft.oak_trapdoor": "Oak Trapdoor", + "block.minecraft.spruce_trapdoor": "Spruce Trapdoor", + "block.minecraft.birch_trapdoor": "Birch Trapdoor", + "block.minecraft.jungle_trapdoor": "Jungle Trapdoor", + "block.minecraft.acacia_trapdoor": "Acacia Trapdoor", + "block.minecraft.dark_oak_trapdoor": "Dark Oak Trapdoor", + "block.minecraft.mangrove_trapdoor": "Mangrove Trapdoor", + "block.minecraft.bamboo_trapdoor": "Bamboo Trapdoor", + "block.minecraft.iron_trapdoor": "Iron Trapdoor", + "block.minecraft.cobweb": "Cobweb", + "block.minecraft.stone_bricks": "Stone Bricks", + "block.minecraft.mossy_stone_bricks": "Mossy Stone Bricks", + "block.minecraft.cracked_stone_bricks": "Cracked Stone Bricks", + "block.minecraft.chiseled_stone_bricks": "Chiseled Stone Bricks", + "block.minecraft.packed_mud": "Packed Mud", + "block.minecraft.mud_bricks": "Mud Bricks", + "block.minecraft.infested_stone": "Infested Stone", + "block.minecraft.infested_cobblestone": "Infested Cobblestone", + "block.minecraft.infested_stone_bricks": "Infested Stone Bricks", + "block.minecraft.infested_mossy_stone_bricks": "Infested Mossy Stone Bricks", + "block.minecraft.infested_cracked_stone_bricks": "Infested Cracked Stone Bricks", + "block.minecraft.infested_chiseled_stone_bricks": "Infested Chiseled Stone Bricks", + "block.minecraft.piston": "Piston", + "block.minecraft.sticky_piston": "Sticky Piston", + "block.minecraft.iron_bars": "Iron Bars", + "block.minecraft.melon": "Melon", + "block.minecraft.brick_stairs": "Brick Stairs", + "block.minecraft.stone_brick_stairs": "Stone Brick Stairs", + "block.minecraft.mud_brick_stairs": "Mud Brick Stairs", + "block.minecraft.vine": "Vines", + "block.minecraft.nether_bricks": "Nether Bricks", + "block.minecraft.nether_brick_fence": "Nether Brick Fence", + "block.minecraft.nether_brick_stairs": "Nether Brick Stairs", + "block.minecraft.nether_wart": "Nether Wart", + "block.minecraft.warped_wart_block": "Warped Wart Block", + "block.minecraft.warped_stem": "Warped Stem", + "block.minecraft.stripped_warped_stem": "Stripped Warped Stem", + "block.minecraft.warped_hyphae": "Warped Hyphae", + "block.minecraft.stripped_warped_hyphae": "Stripped Warped Hyphae", + "block.minecraft.crimson_stem": "Crimson Stem", + "block.minecraft.stripped_crimson_stem": "Stripped Crimson Stem", + "block.minecraft.crimson_hyphae": "Crimson Hyphae", + "block.minecraft.stripped_crimson_hyphae": "Stripped Crimson Hyphae", + "block.minecraft.warped_nylium": "Warped Nylium", + "block.minecraft.crimson_nylium": "Crimson Nylium", + "block.minecraft.warped_fungus": "Warped Fungus", + "block.minecraft.crimson_fungus": "Crimson Fungus", + "block.minecraft.crimson_roots": "Crimson Roots", + "block.minecraft.warped_roots": "Warped Roots", + "block.minecraft.nether_sprouts": "Nether Sprouts", + "block.minecraft.shroomlight": "Shroomlight", + "block.minecraft.weeping_vines": "Weeping Vines", + "block.minecraft.weeping_vines_plant": "Weeping Vines Plant", + "block.minecraft.twisting_vines": "Twisting Vines", + "block.minecraft.twisting_vines_plant": "Twisting Vines Plant", + "block.minecraft.soul_soil": "Soul Soil", + "block.minecraft.basalt": "Basalt", + "block.minecraft.polished_basalt": "Polished Basalt", + "block.minecraft.warped_planks": "Warped Planks", + "block.minecraft.warped_slab": "Warped Slab", + "block.minecraft.warped_pressure_plate": "Warped Pressure Plate", + "block.minecraft.warped_fence": "Warped Fence", + "block.minecraft.warped_trapdoor": "Warped Trapdoor", + "block.minecraft.warped_fence_gate": "Warped Fence Gate", + "block.minecraft.warped_stairs": "Warped Stairs", + "block.minecraft.warped_button": "Warped Button", + "block.minecraft.warped_door": "Warped Door", + "block.minecraft.warped_sign": "Warped Sign", + "block.minecraft.warped_wall_sign": "Warped Wall Sign", + "block.minecraft.crimson_planks": "Crimson Planks", + "block.minecraft.crimson_slab": "Crimson Slab", + "block.minecraft.crimson_pressure_plate": "Crimson Pressure Plate", + "block.minecraft.crimson_fence": "Crimson Fence", + "block.minecraft.crimson_trapdoor": "Crimson Trapdoor", + "block.minecraft.crimson_fence_gate": "Crimson Fence Gate", + "block.minecraft.crimson_stairs": "Crimson Stairs", + "block.minecraft.crimson_button": "Crimson Button", + "block.minecraft.crimson_door": "Crimson Door", + "block.minecraft.crimson_sign": "Crimson Sign", + "block.minecraft.crimson_wall_sign": "Crimson Wall Sign", + "block.minecraft.soul_fire": "Soul Fire", + "block.minecraft.cauldron": "Cauldron", + "block.minecraft.water_cauldron": "Water Cauldron", + "block.minecraft.lava_cauldron": "Lava Cauldron", + "block.minecraft.powder_snow_cauldron": "Powder Snow Cauldron", + "block.minecraft.enchanting_table": "Enchanting Table", + "block.minecraft.anvil": "Anvil", + "block.minecraft.chipped_anvil": "Chipped Anvil", + "block.minecraft.damaged_anvil": "Damaged Anvil", + "block.minecraft.end_stone": "End Stone", + "block.minecraft.end_portal_frame": "End Portal Frame", + "block.minecraft.mycelium": "Mycelium", + "block.minecraft.lily_pad": "Lily Pad", + "block.minecraft.dragon_egg": "Dragon Egg", + "block.minecraft.redstone_lamp": "Redstone Lamp", + "block.minecraft.cocoa": "Cocoa", + "block.minecraft.ender_chest": "Ender Chest", + "block.minecraft.emerald_ore": "Emerald Ore", + "block.minecraft.deepslate_emerald_ore": "Deepslate Emerald Ore", + "block.minecraft.emerald_block": "Block of Emerald", + "block.minecraft.redstone_block": "Block of Redstone", + "block.minecraft.tripwire": "Tripwire", + "block.minecraft.tripwire_hook": "Tripwire Hook", + "block.minecraft.command_block": "Command Block", + "block.minecraft.repeating_command_block": "Repeating Command Block", + "block.minecraft.chain_command_block": "Chain Command Block", + "block.minecraft.beacon": "Beacon", + "block.minecraft.beacon.primary": "Primary Power", + "block.minecraft.beacon.secondary": "Secondary Power", + "block.minecraft.cobblestone_wall": "Cobblestone Wall", + "block.minecraft.mossy_cobblestone_wall": "Mossy Cobblestone Wall", + "block.minecraft.carrots": "Carrots", + "block.minecraft.potatoes": "Potatoes", + "block.minecraft.daylight_detector": "Daylight Detector", + "block.minecraft.nether_quartz_ore": "Nether Quartz Ore", + "block.minecraft.hopper": "Hopper", + "block.minecraft.quartz_block": "Block of Quartz", + "block.minecraft.chiseled_quartz_block": "Chiseled Quartz Block", + "block.minecraft.quartz_pillar": "Quartz Pillar", + "block.minecraft.quartz_stairs": "Quartz Stairs", + "block.minecraft.slime_block": "Slime Block", + "block.minecraft.prismarine": "Prismarine", + "block.minecraft.prismarine_bricks": "Prismarine Bricks", + "block.minecraft.dark_prismarine": "Dark Prismarine", + "block.minecraft.sea_lantern": "Sea Lantern", + "block.minecraft.end_rod": "End Rod", + "block.minecraft.chorus_plant": "Chorus Plant", + "block.minecraft.chorus_flower": "Chorus Flower", + "block.minecraft.purpur_block": "Purpur Block", + "block.minecraft.purpur_pillar": "Purpur Pillar", + "block.minecraft.purpur_stairs": "Purpur Stairs", + "block.minecraft.purpur_slab": "Purpur Slab", + "block.minecraft.end_stone_bricks": "End Stone Bricks", + "block.minecraft.beetroots": "Beetroots", + "block.minecraft.dirt_path": "Dirt Path", + "block.minecraft.magma_block": "Magma Block", + "block.minecraft.nether_wart_block": "Nether Wart Block", + "block.minecraft.red_nether_bricks": "Red Nether Bricks", + "block.minecraft.bone_block": "Bone Block", + "block.minecraft.observer": "Observer", + "block.minecraft.shulker_box": "Shulker Box", + "block.minecraft.white_shulker_box": "White Shulker Box", + "block.minecraft.orange_shulker_box": "Orange Shulker Box", + "block.minecraft.magenta_shulker_box": "Magenta Shulker Box", + "block.minecraft.light_blue_shulker_box": "Light Blue Shulker Box", + "block.minecraft.yellow_shulker_box": "Yellow Shulker Box", + "block.minecraft.lime_shulker_box": "Lime Shulker Box", + "block.minecraft.pink_shulker_box": "Pink Shulker Box", + "block.minecraft.gray_shulker_box": "Gray Shulker Box", + "block.minecraft.light_gray_shulker_box": "Light Gray Shulker Box", + "block.minecraft.cyan_shulker_box": "Cyan Shulker Box", + "block.minecraft.purple_shulker_box": "Purple Shulker Box", + "block.minecraft.blue_shulker_box": "Blue Shulker Box", + "block.minecraft.brown_shulker_box": "Brown Shulker Box", + "block.minecraft.green_shulker_box": "Green Shulker Box", + "block.minecraft.red_shulker_box": "Red Shulker Box", + "block.minecraft.black_shulker_box": "Black Shulker Box", + "block.minecraft.white_glazed_terracotta": "White Glazed Terracotta", + "block.minecraft.orange_glazed_terracotta": "Orange Glazed Terracotta", + "block.minecraft.magenta_glazed_terracotta": "Magenta Glazed Terracotta", + "block.minecraft.light_blue_glazed_terracotta": "Light Blue Glazed Terracotta", + "block.minecraft.yellow_glazed_terracotta": "Yellow Glazed Terracotta", + "block.minecraft.lime_glazed_terracotta": "Lime Glazed Terracotta", + "block.minecraft.pink_glazed_terracotta": "Pink Glazed Terracotta", + "block.minecraft.gray_glazed_terracotta": "Gray Glazed Terracotta", + "block.minecraft.light_gray_glazed_terracotta": "Light Gray Glazed Terracotta", + "block.minecraft.cyan_glazed_terracotta": "Cyan Glazed Terracotta", + "block.minecraft.purple_glazed_terracotta": "Purple Glazed Terracotta", + "block.minecraft.blue_glazed_terracotta": "Blue Glazed Terracotta", + "block.minecraft.brown_glazed_terracotta": "Brown Glazed Terracotta", + "block.minecraft.green_glazed_terracotta": "Green Glazed Terracotta", + "block.minecraft.red_glazed_terracotta": "Red Glazed Terracotta", + "block.minecraft.black_glazed_terracotta": "Black Glazed Terracotta", + "block.minecraft.black_concrete": "Black Concrete", + "block.minecraft.red_concrete": "Red Concrete", + "block.minecraft.green_concrete": "Green Concrete", + "block.minecraft.brown_concrete": "Brown Concrete", + "block.minecraft.blue_concrete": "Blue Concrete", + "block.minecraft.purple_concrete": "Purple Concrete", + "block.minecraft.cyan_concrete": "Cyan Concrete", + "block.minecraft.light_gray_concrete": "Light Gray Concrete", + "block.minecraft.gray_concrete": "Gray Concrete", + "block.minecraft.pink_concrete": "Pink Concrete", + "block.minecraft.lime_concrete": "Lime Concrete", + "block.minecraft.yellow_concrete": "Yellow Concrete", + "block.minecraft.light_blue_concrete": "Light Blue Concrete", + "block.minecraft.magenta_concrete": "Magenta Concrete", + "block.minecraft.orange_concrete": "Orange Concrete", + "block.minecraft.white_concrete": "White Concrete", + "block.minecraft.black_concrete_powder": "Black Concrete Powder", + "block.minecraft.red_concrete_powder": "Red Concrete Powder", + "block.minecraft.green_concrete_powder": "Green Concrete Powder", + "block.minecraft.brown_concrete_powder": "Brown Concrete Powder", + "block.minecraft.blue_concrete_powder": "Blue Concrete Powder", + "block.minecraft.purple_concrete_powder": "Purple Concrete Powder", + "block.minecraft.cyan_concrete_powder": "Cyan Concrete Powder", + "block.minecraft.light_gray_concrete_powder": "Light Gray Concrete Powder", + "block.minecraft.gray_concrete_powder": "Gray Concrete Powder", + "block.minecraft.pink_concrete_powder": "Pink Concrete Powder", + "block.minecraft.lime_concrete_powder": "Lime Concrete Powder", + "block.minecraft.yellow_concrete_powder": "Yellow Concrete Powder", + "block.minecraft.light_blue_concrete_powder": "Light Blue Concrete Powder", + "block.minecraft.magenta_concrete_powder": "Magenta Concrete Powder", + "block.minecraft.orange_concrete_powder": "Orange Concrete Powder", + "block.minecraft.white_concrete_powder": "White Concrete Powder", + "block.minecraft.turtle_egg": "Turtle Egg", + "block.minecraft.piston_head": "Piston Head", + "block.minecraft.moving_piston": "Moving Piston", + "block.minecraft.red_mushroom": "Red Mushroom", + "block.minecraft.snow_block": "Snow Block", + "block.minecraft.attached_melon_stem": "Attached Melon Stem", + "block.minecraft.melon_stem": "Melon Stem", + "block.minecraft.brewing_stand": "Brewing Stand", + "block.minecraft.end_portal": "End Portal", + "block.minecraft.flower_pot": "Flower Pot", + "block.minecraft.potted_oak_sapling": "Potted Oak Sapling", + "block.minecraft.potted_spruce_sapling": "Potted Spruce Sapling", + "block.minecraft.potted_birch_sapling": "Potted Birch Sapling", + "block.minecraft.potted_jungle_sapling": "Potted Jungle Sapling", + "block.minecraft.potted_acacia_sapling": "Potted Acacia Sapling", + "block.minecraft.potted_dark_oak_sapling": "Potted Dark Oak Sapling", + "block.minecraft.potted_mangrove_propagule": "Potted Mangrove Propagule", + "block.minecraft.potted_fern": "Potted Fern", + "block.minecraft.potted_dandelion": "Potted Dandelion", + "block.minecraft.potted_poppy": "Potted Poppy", + "block.minecraft.potted_blue_orchid": "Potted Blue Orchid", + "block.minecraft.potted_allium": "Potted Allium", + "block.minecraft.potted_azure_bluet": "Potted Azure Bluet", + "block.minecraft.potted_red_tulip": "Potted Red Tulip", + "block.minecraft.potted_orange_tulip": "Potted Orange Tulip", + "block.minecraft.potted_white_tulip": "Potted White Tulip", + "block.minecraft.potted_pink_tulip": "Potted Pink Tulip", + "block.minecraft.potted_oxeye_daisy": "Potted Oxeye Daisy", + "block.minecraft.potted_cornflower": "Potted Cornflower", + "block.minecraft.potted_lily_of_the_valley": "Potted Lily of the Valley", + "block.minecraft.potted_wither_rose": "Potted Wither Rose", + "block.minecraft.potted_red_mushroom": "Potted Red Mushroom", + "block.minecraft.potted_brown_mushroom": "Potted Brown Mushroom", + "block.minecraft.potted_dead_bush": "Potted Dead Bush", + "block.minecraft.potted_cactus": "Potted Cactus", + "block.minecraft.potted_bamboo": "Potted Bamboo", + "block.minecraft.potted_crimson_fungus": "Potted Crimson Fungus", + "block.minecraft.potted_warped_fungus": "Potted Warped Fungus", + "block.minecraft.potted_crimson_roots": "Potted Crimson Roots", + "block.minecraft.potted_warped_roots": "Potted Warped Roots", + "block.minecraft.potted_azalea_bush": "Potted Azalea", + "block.minecraft.potted_flowering_azalea_bush": "Potted Flowering Azalea", + "block.minecraft.skeleton_wall_skull": "Skeleton Wall Skull", + "block.minecraft.skeleton_skull": "Skeleton Skull", + "block.minecraft.wither_skeleton_wall_skull": "Wither Skeleton Wall Skull", + "block.minecraft.wither_skeleton_skull": "Wither Skeleton Skull", + "block.minecraft.zombie_wall_head": "Zombie Wall Head", + "block.minecraft.zombie_head": "Zombie Head", + "block.minecraft.player_wall_head": "Player Wall Head", + "block.minecraft.player_head": "Player Head", + "block.minecraft.player_head.named": "%s's Head", + "block.minecraft.creeper_wall_head": "Creeper Wall Head", + "block.minecraft.creeper_head": "Creeper Head", + "block.minecraft.dragon_wall_head": "Dragon Wall Head", + "block.minecraft.dragon_head": "Dragon Head", + "block.minecraft.piglin_wall_head": "Piglin Wall Head", + "block.minecraft.piglin_head": "Piglin Head", + "block.minecraft.end_gateway": "End Gateway", + "block.minecraft.structure_void": "Structure Void", + "block.minecraft.structure_block": "Structure Block", + "block.minecraft.void_air": "Void Air", + "block.minecraft.cave_air": "Cave Air", + "block.minecraft.bubble_column": "Bubble Column", + "block.minecraft.dead_tube_coral_block": "Dead Tube Coral Block", + "block.minecraft.dead_brain_coral_block": "Dead Brain Coral Block", + "block.minecraft.dead_bubble_coral_block": "Dead Bubble Coral Block", + "block.minecraft.dead_fire_coral_block": "Dead Fire Coral Block", + "block.minecraft.dead_horn_coral_block": "Dead Horn Coral Block", + "block.minecraft.tube_coral_block": "Tube Coral Block", + "block.minecraft.brain_coral_block": "Brain Coral Block", + "block.minecraft.bubble_coral_block": "Bubble Coral Block", + "block.minecraft.fire_coral_block": "Fire Coral Block", + "block.minecraft.horn_coral_block": "Horn Coral Block", + "block.minecraft.tube_coral": "Tube Coral", + "block.minecraft.brain_coral": "Brain Coral", + "block.minecraft.bubble_coral": "Bubble Coral", + "block.minecraft.fire_coral": "Fire Coral", + "block.minecraft.horn_coral": "Horn Coral", + "block.minecraft.dead_tube_coral": "Dead Tube Coral", + "block.minecraft.dead_brain_coral": "Dead Brain Coral", + "block.minecraft.dead_bubble_coral": "Dead Bubble Coral", + "block.minecraft.dead_fire_coral": "Dead Fire Coral", + "block.minecraft.dead_horn_coral": "Dead Horn Coral", + "block.minecraft.tube_coral_fan": "Tube Coral Fan", + "block.minecraft.brain_coral_fan": "Brain Coral Fan", + "block.minecraft.bubble_coral_fan": "Bubble Coral Fan", + "block.minecraft.fire_coral_fan": "Fire Coral Fan", + "block.minecraft.horn_coral_fan": "Horn Coral Fan", + "block.minecraft.dead_tube_coral_fan": "Dead Tube Coral Fan", + "block.minecraft.dead_brain_coral_fan": "Dead Brain Coral Fan", + "block.minecraft.dead_bubble_coral_fan": "Dead Bubble Coral Fan", + "block.minecraft.dead_fire_coral_fan": "Dead Fire Coral Fan", + "block.minecraft.dead_horn_coral_fan": "Dead Horn Coral Fan", + "block.minecraft.tube_coral_wall_fan": "Tube Coral Wall Fan", + "block.minecraft.brain_coral_wall_fan": "Brain Coral Wall Fan", + "block.minecraft.bubble_coral_wall_fan": "Bubble Coral Wall Fan", + "block.minecraft.fire_coral_wall_fan": "Fire Coral Wall Fan", + "block.minecraft.horn_coral_wall_fan": "Horn Coral Wall Fan", + "block.minecraft.dead_tube_coral_wall_fan": "Dead Tube Coral Wall Fan", + "block.minecraft.dead_brain_coral_wall_fan": "Dead Brain Coral Wall Fan", + "block.minecraft.dead_bubble_coral_wall_fan": "Dead Bubble Coral Wall Fan", + "block.minecraft.dead_fire_coral_wall_fan": "Dead Fire Coral Wall Fan", + "block.minecraft.dead_horn_coral_wall_fan": "Dead Horn Coral Wall Fan", + "block.minecraft.loom": "Loom", + "block.minecraft.conduit": "Conduit", + "block.minecraft.bamboo": "Bamboo", + "block.minecraft.bamboo_sapling": "Bamboo Shoot", + "block.minecraft.jigsaw": "Jigsaw Block", + "block.minecraft.composter": "Composter", + "block.minecraft.target": "Target", + "block.minecraft.polished_granite_stairs": "Polished Granite Stairs", + "block.minecraft.smooth_red_sandstone_stairs": "Smooth Red Sandstone Stairs", + "block.minecraft.mossy_stone_brick_stairs": "Mossy Stone Brick Stairs", + "block.minecraft.polished_diorite_stairs": "Polished Diorite Stairs", + "block.minecraft.mossy_cobblestone_stairs": "Mossy Cobblestone Stairs", + "block.minecraft.end_stone_brick_stairs": "End Stone Brick Stairs", + "block.minecraft.stone_stairs": "Stone Stairs", + "block.minecraft.smooth_sandstone_stairs": "Smooth Sandstone Stairs", + "block.minecraft.smooth_quartz_stairs": "Smooth Quartz Stairs", + "block.minecraft.granite_stairs": "Granite Stairs", + "block.minecraft.andesite_stairs": "Andesite Stairs", + "block.minecraft.red_nether_brick_stairs": "Red Nether Brick Stairs", + "block.minecraft.polished_andesite_stairs": "Polished Andesite Stairs", + "block.minecraft.diorite_stairs": "Diorite Stairs", + "block.minecraft.polished_granite_slab": "Polished Granite Slab", + "block.minecraft.smooth_red_sandstone_slab": "Smooth Red Sandstone Slab", + "block.minecraft.mossy_stone_brick_slab": "Mossy Stone Brick Slab", + "block.minecraft.polished_diorite_slab": "Polished Diorite Slab", + "block.minecraft.mossy_cobblestone_slab": "Mossy Cobblestone Slab", + "block.minecraft.end_stone_brick_slab": "End Stone Brick Slab", + "block.minecraft.smooth_sandstone_slab": "Smooth Sandstone Slab", + "block.minecraft.smooth_quartz_slab": "Smooth Quartz Slab", + "block.minecraft.granite_slab": "Granite Slab", + "block.minecraft.andesite_slab": "Andesite Slab", + "block.minecraft.red_nether_brick_slab": "Red Nether Brick Slab", + "block.minecraft.polished_andesite_slab": "Polished Andesite Slab", + "block.minecraft.diorite_slab": "Diorite Slab", + "block.minecraft.brick_wall": "Brick Wall", + "block.minecraft.prismarine_wall": "Prismarine Wall", + "block.minecraft.red_sandstone_wall": "Red Sandstone Wall", + "block.minecraft.mossy_stone_brick_wall": "Mossy Stone Brick Wall", + "block.minecraft.granite_wall": "Granite Wall", + "block.minecraft.stone_brick_wall": "Stone Brick Wall", + "block.minecraft.mud_brick_wall": "Mud Brick Wall", + "block.minecraft.nether_brick_wall": "Nether Brick Wall", + "block.minecraft.andesite_wall": "Andesite Wall", + "block.minecraft.red_nether_brick_wall": "Red Nether Brick Wall", + "block.minecraft.sandstone_wall": "Sandstone Wall", + "block.minecraft.end_stone_brick_wall": "End Stone Brick Wall", + "block.minecraft.diorite_wall": "Diorite Wall", + "block.minecraft.barrel": "Barrel", + "block.minecraft.smoker": "Smoker", + "block.minecraft.blast_furnace": "Blast Furnace", + "block.minecraft.cartography_table": "Cartography Table", + "block.minecraft.fletching_table": "Fletching Table", + "block.minecraft.smithing_table": "Smithing Table", + "block.minecraft.grindstone": "Grindstone", + "block.minecraft.lectern": "Lectern", + "block.minecraft.stonecutter": "Stonecutter", + "block.minecraft.bell": "Bell", + "block.minecraft.ominous_banner": "Ominous Banner", + "block.minecraft.lantern": "Lantern", + "block.minecraft.soul_lantern": "Soul Lantern", + "block.minecraft.sweet_berry_bush": "Sweet Berry Bush", + "block.minecraft.campfire": "Campfire", + "block.minecraft.soul_campfire": "Soul Campfire", + "block.minecraft.beehive": "Beehive", + "block.minecraft.bee_nest": "Bee Nest", + "block.minecraft.honey_block": "Honey Block", + "block.minecraft.honeycomb_block": "Honeycomb Block", + "block.minecraft.lodestone": "Lodestone", + "block.minecraft.netherite_block": "Block of Netherite", + "block.minecraft.ancient_debris": "Ancient Debris", + "block.minecraft.crying_obsidian": "Crying Obsidian", + "block.minecraft.blackstone": "Blackstone", + "block.minecraft.blackstone_slab": "Blackstone Slab", + "block.minecraft.blackstone_stairs": "Blackstone Stairs", + "block.minecraft.blackstone_wall": "Blackstone Wall", + "block.minecraft.polished_blackstone_bricks": "Polished Blackstone Bricks", + "block.minecraft.polished_blackstone_brick_slab": "Polished Blackstone Brick Slab", + "block.minecraft.polished_blackstone_brick_stairs": "Polished Blackstone Brick Stairs", + "block.minecraft.polished_blackstone_brick_wall": "Polished Blackstone Brick Wall", + "block.minecraft.chiseled_polished_blackstone": "Chiseled Polished Blackstone", + "block.minecraft.cracked_polished_blackstone_bricks": "Cracked Polished Blackstone Bricks", + "block.minecraft.gilded_blackstone": "Gilded Blackstone", + "block.minecraft.polished_blackstone": "Polished Blackstone", + "block.minecraft.polished_blackstone_wall": "Polished Blackstone Wall", + "block.minecraft.polished_blackstone_slab": "Polished Blackstone Slab", + "block.minecraft.polished_blackstone_stairs": "Polished Blackstone Stairs", + "block.minecraft.polished_blackstone_pressure_plate": "Polished Blackstone Pressure Plate", + "block.minecraft.polished_blackstone_button": "Polished Blackstone Button", + "block.minecraft.cracked_nether_bricks": "Cracked Nether Bricks", + "block.minecraft.chiseled_nether_bricks": "Chiseled Nether Bricks", + "block.minecraft.quartz_bricks": "Quartz Bricks", + "block.minecraft.chain": "Chain", + "block.minecraft.candle": "Candle", + "block.minecraft.white_candle": "White Candle", + "block.minecraft.orange_candle": "Orange Candle", + "block.minecraft.magenta_candle": "Magenta Candle", + "block.minecraft.light_blue_candle": "Light Blue Candle", + "block.minecraft.yellow_candle": "Yellow Candle", + "block.minecraft.lime_candle": "Lime Candle", + "block.minecraft.pink_candle": "Pink Candle", + "block.minecraft.gray_candle": "Gray Candle", + "block.minecraft.light_gray_candle": "Light Gray Candle", + "block.minecraft.cyan_candle": "Cyan Candle", + "block.minecraft.purple_candle": "Purple Candle", + "block.minecraft.blue_candle": "Blue Candle", + "block.minecraft.brown_candle": "Brown Candle", + "block.minecraft.green_candle": "Green Candle", + "block.minecraft.red_candle": "Red Candle", + "block.minecraft.black_candle": "Black Candle", + "block.minecraft.candle_cake": "Cake with Candle", + "block.minecraft.white_candle_cake": "Cake with White Candle", + "block.minecraft.orange_candle_cake": "Cake with Orange Candle", + "block.minecraft.magenta_candle_cake": "Cake with Magenta Candle", + "block.minecraft.light_blue_candle_cake": "Cake with Light Blue Candle", + "block.minecraft.yellow_candle_cake": "Cake with Yellow Candle", + "block.minecraft.lime_candle_cake": "Cake with Lime Candle", + "block.minecraft.pink_candle_cake": "Cake with Pink Candle", + "block.minecraft.gray_candle_cake": "Cake with Gray Candle", + "block.minecraft.light_gray_candle_cake": "Cake with Light Gray Candle", + "block.minecraft.cyan_candle_cake": "Cake with Cyan Candle", + "block.minecraft.purple_candle_cake": "Cake with Purple Candle", + "block.minecraft.blue_candle_cake": "Cake with Blue Candle", + "block.minecraft.brown_candle_cake": "Cake with Brown Candle", + "block.minecraft.green_candle_cake": "Cake with Green Candle", + "block.minecraft.red_candle_cake": "Cake with Red Candle", + "block.minecraft.black_candle_cake": "Cake with Black Candle", + "block.minecraft.amethyst_block": "Block of Amethyst", + "block.minecraft.small_amethyst_bud": "Small Amethyst Bud", + "block.minecraft.medium_amethyst_bud": "Medium Amethyst Bud", + "block.minecraft.large_amethyst_bud": "Large Amethyst Bud", + "block.minecraft.amethyst_cluster": "Amethyst Cluster", + "block.minecraft.budding_amethyst": "Budding Amethyst", + "block.minecraft.calcite": "Calcite", + "block.minecraft.tuff": "Tuff", + "block.minecraft.tinted_glass": "Tinted Glass", + "block.minecraft.dripstone_block": "Dripstone Block", + "block.minecraft.pointed_dripstone": "Pointed Dripstone", + "block.minecraft.copper_ore": "Copper Ore", + "block.minecraft.deepslate_copper_ore": "Deepslate Copper Ore", + "block.minecraft.copper_block": "Block of Copper", + "block.minecraft.exposed_copper": "Exposed Copper", + "block.minecraft.weathered_copper": "Weathered Copper", + "block.minecraft.oxidized_copper": "Oxidized Copper", + "block.minecraft.cut_copper": "Cut Copper", + "block.minecraft.exposed_cut_copper": "Exposed Cut Copper", + "block.minecraft.weathered_cut_copper": "Weathered Cut Copper", + "block.minecraft.oxidized_cut_copper": "Oxidized Cut Copper", + "block.minecraft.cut_copper_stairs": "Cut Copper Stairs", + "block.minecraft.exposed_cut_copper_stairs": "Exposed Cut Copper Stairs", + "block.minecraft.weathered_cut_copper_stairs": "Weathered Cut Copper Stairs", + "block.minecraft.oxidized_cut_copper_stairs": "Oxidized Cut Copper Stairs", + "block.minecraft.cut_copper_slab": "Cut Copper Slab", + "block.minecraft.exposed_cut_copper_slab": "Exposed Cut Copper Slab", + "block.minecraft.weathered_cut_copper_slab": "Weathered Cut Copper Slab", + "block.minecraft.oxidized_cut_copper_slab": "Oxidized Cut Copper Slab", + "block.minecraft.waxed_copper_block": "Waxed Block of Copper", + "block.minecraft.waxed_exposed_copper": "Waxed Exposed Copper", + "block.minecraft.waxed_weathered_copper": "Waxed Weathered Copper", + "block.minecraft.waxed_oxidized_copper": "Waxed Oxidized Copper", + "block.minecraft.waxed_cut_copper": "Waxed Cut Copper", + "block.minecraft.waxed_exposed_cut_copper": "Waxed Exposed Cut Copper", + "block.minecraft.waxed_weathered_cut_copper": "Waxed Weathered Cut Copper", + "block.minecraft.waxed_oxidized_cut_copper": "Waxed Oxidized Cut Copper", + "block.minecraft.waxed_cut_copper_stairs": "Waxed Cut Copper Stairs", + "block.minecraft.waxed_exposed_cut_copper_stairs": "Waxed Exposed Cut Copper Stairs", + "block.minecraft.waxed_weathered_cut_copper_stairs": "Waxed Weathered Cut Copper Stairs", + "block.minecraft.waxed_oxidized_cut_copper_stairs": "Waxed Oxidized Cut Copper Stairs", + "block.minecraft.waxed_cut_copper_slab": "Waxed Cut Copper Slab", + "block.minecraft.waxed_exposed_cut_copper_slab": "Waxed Exposed Cut Copper Slab", + "block.minecraft.waxed_weathered_cut_copper_slab": "Waxed Weathered Cut Copper Slab", + "block.minecraft.waxed_oxidized_cut_copper_slab": "Waxed Oxidized Cut Copper Slab", + "block.minecraft.lightning_rod": "Lightning Rod", + "block.minecraft.cave_vines": "Cave Vines", + "block.minecraft.cave_vines_plant": "Cave Vines Plant", + "block.minecraft.spore_blossom": "Spore Blossom", + "block.minecraft.azalea": "Azalea", + "block.minecraft.flowering_azalea": "Flowering Azalea", + "block.minecraft.azalea_leaves": "Azalea Leaves", + "block.minecraft.flowering_azalea_leaves": "Flowering Azalea Leaves", + "block.minecraft.moss_carpet": "Moss Carpet", + "block.minecraft.moss_block": "Moss Block", + "block.minecraft.big_dripleaf": "Big Dripleaf", + "block.minecraft.big_dripleaf_stem": "Big Dripleaf Stem", + "block.minecraft.small_dripleaf": "Small Dripleaf", + "block.minecraft.rooted_dirt": "Rooted Dirt", + "block.minecraft.mud": "Mud", + "block.minecraft.hanging_roots": "Hanging Roots", + "block.minecraft.powder_snow": "Powder Snow", + "block.minecraft.glow_lichen": "Glow Lichen", + "block.minecraft.sculk_sensor": "Sculk Sensor", + "block.minecraft.deepslate": "Deepslate", + "block.minecraft.cobbled_deepslate": "Cobbled Deepslate", + "block.minecraft.cobbled_deepslate_slab": "Cobbled Deepslate Slab", + "block.minecraft.cobbled_deepslate_stairs": "Cobbled Deepslate Stairs", + "block.minecraft.cobbled_deepslate_wall": "Cobbled Deepslate Wall", + "block.minecraft.chiseled_deepslate": "Chiseled Deepslate", + "block.minecraft.polished_deepslate": "Polished Deepslate", + "block.minecraft.polished_deepslate_slab": "Polished Deepslate Slab", + "block.minecraft.polished_deepslate_stairs": "Polished Deepslate Stairs", + "block.minecraft.polished_deepslate_wall": "Polished Deepslate Wall", + "block.minecraft.deepslate_bricks": "Deepslate Bricks", + "block.minecraft.deepslate_brick_slab": "Deepslate Brick Slab", + "block.minecraft.deepslate_brick_stairs": "Deepslate Brick Stairs", + "block.minecraft.deepslate_brick_wall": "Deepslate Brick Wall", + "block.minecraft.deepslate_tiles": "Deepslate Tiles", + "block.minecraft.deepslate_tile_slab": "Deepslate Tile Slab", + "block.minecraft.deepslate_tile_stairs": "Deepslate Tile Stairs", + "block.minecraft.deepslate_tile_wall": "Deepslate Tile Wall", + "block.minecraft.cracked_deepslate_bricks": "Cracked Deepslate Bricks", + "block.minecraft.cracked_deepslate_tiles": "Cracked Deepslate Tiles", + "block.minecraft.infested_deepslate": "Infested Deepslate", + "block.minecraft.smooth_basalt": "Smooth Basalt", + "block.minecraft.raw_iron_block": "Block of Raw Iron", + "block.minecraft.raw_copper_block": "Block of Raw Copper", + "block.minecraft.raw_gold_block": "Block of Raw Gold", + "block.minecraft.sculk": "Sculk", + "block.minecraft.sculk_catalyst": "Sculk Catalyst", + "block.minecraft.sculk_shrieker": "Sculk Shrieker", + "block.minecraft.sculk_vein": "Sculk Vein", + "block.minecraft.ochre_froglight": "Ochre Froglight", + "block.minecraft.verdant_froglight": "Verdant Froglight", + "block.minecraft.pearlescent_froglight": "Pearlescent Froglight", + "block.minecraft.frogspawn": "Frogspawn", + "block.minecraft.reinforced_deepslate": "Reinforced Deepslate", + "item.minecraft.name_tag": "Name Tag", + "item.minecraft.lead": "Lead", + "item.minecraft.iron_shovel": "Iron Shovel", + "item.minecraft.iron_pickaxe": "Iron Pickaxe", + "item.minecraft.iron_axe": "Iron Axe", + "item.minecraft.flint_and_steel": "Flint and Steel", + "item.minecraft.apple": "Apple", + "item.minecraft.cookie": "Cookie", + "item.minecraft.bow": "Bow", + "item.minecraft.bundle": "Bundle", + "item.minecraft.bundle.fullness": "%s/%s", + "item.minecraft.arrow": "Arrow", + "item.minecraft.spectral_arrow": "Spectral Arrow", + "item.minecraft.tipped_arrow": "Tipped Arrow", + "item.minecraft.dried_kelp": "Dried Kelp", + "item.minecraft.coal": "Coal", + "item.minecraft.charcoal": "Charcoal", + "item.minecraft.raw_copper": "Raw Copper", + "item.minecraft.raw_iron": "Raw Iron", + "item.minecraft.raw_gold": "Raw Gold", + "item.minecraft.diamond": "Diamond", + "item.minecraft.emerald": "Emerald", + "item.minecraft.iron_ingot": "Iron Ingot", + "item.minecraft.copper_ingot": "Copper Ingot", + "item.minecraft.gold_ingot": "Gold Ingot", + "item.minecraft.iron_sword": "Iron Sword", + "item.minecraft.wooden_sword": "Wooden Sword", + "item.minecraft.wooden_shovel": "Wooden Shovel", + "item.minecraft.wooden_pickaxe": "Wooden Pickaxe", + "item.minecraft.wooden_axe": "Wooden Axe", + "item.minecraft.stone_sword": "Stone Sword", + "item.minecraft.stone_shovel": "Stone Shovel", + "item.minecraft.stone_pickaxe": "Stone Pickaxe", + "item.minecraft.stone_axe": "Stone Axe", + "item.minecraft.diamond_sword": "Diamond Sword", + "item.minecraft.diamond_shovel": "Diamond Shovel", + "item.minecraft.diamond_pickaxe": "Diamond Pickaxe", + "item.minecraft.diamond_axe": "Diamond Axe", + "item.minecraft.stick": "Stick", + "item.minecraft.bowl": "Bowl", + "item.minecraft.mushroom_stew": "Mushroom Stew", + "item.minecraft.golden_sword": "Golden Sword", + "item.minecraft.golden_shovel": "Golden Shovel", + "item.minecraft.golden_pickaxe": "Golden Pickaxe", + "item.minecraft.golden_axe": "Golden Axe", + "item.minecraft.string": "String", + "item.minecraft.feather": "Feather", + "item.minecraft.gunpowder": "Gunpowder", + "item.minecraft.wooden_hoe": "Wooden Hoe", + "item.minecraft.stone_hoe": "Stone Hoe", + "item.minecraft.iron_hoe": "Iron Hoe", + "item.minecraft.diamond_hoe": "Diamond Hoe", + "item.minecraft.golden_hoe": "Golden Hoe", + "item.minecraft.wheat_seeds": "Wheat Seeds", + "item.minecraft.pumpkin_seeds": "Pumpkin Seeds", + "item.minecraft.melon_seeds": "Melon Seeds", + "item.minecraft.melon_slice": "Melon Slice", + "item.minecraft.wheat": "Wheat", + "item.minecraft.bread": "Bread", + "item.minecraft.leather_helmet": "Leather Cap", + "item.minecraft.leather_chestplate": "Leather Tunic", + "item.minecraft.leather_leggings": "Leather Pants", + "item.minecraft.leather_boots": "Leather Boots", + "item.minecraft.chainmail_helmet": "Chainmail Helmet", + "item.minecraft.chainmail_chestplate": "Chainmail Chestplate", + "item.minecraft.chainmail_leggings": "Chainmail Leggings", + "item.minecraft.chainmail_boots": "Chainmail Boots", + "item.minecraft.iron_helmet": "Iron Helmet", + "item.minecraft.iron_chestplate": "Iron Chestplate", + "item.minecraft.iron_leggings": "Iron Leggings", + "item.minecraft.iron_boots": "Iron Boots", + "item.minecraft.diamond_helmet": "Diamond Helmet", + "item.minecraft.diamond_chestplate": "Diamond Chestplate", + "item.minecraft.diamond_leggings": "Diamond Leggings", + "item.minecraft.diamond_boots": "Diamond Boots", + "item.minecraft.golden_helmet": "Golden Helmet", + "item.minecraft.golden_chestplate": "Golden Chestplate", + "item.minecraft.golden_leggings": "Golden Leggings", + "item.minecraft.golden_boots": "Golden Boots", + "item.minecraft.flint": "Flint", + "item.minecraft.porkchop": "Raw Porkchop", + "item.minecraft.cooked_porkchop": "Cooked Porkchop", + "item.minecraft.chicken": "Raw Chicken", + "item.minecraft.cooked_chicken": "Cooked Chicken", + "item.minecraft.mutton": "Raw Mutton", + "item.minecraft.cooked_mutton": "Cooked Mutton", + "item.minecraft.rabbit": "Raw Rabbit", + "item.minecraft.cooked_rabbit": "Cooked Rabbit", + "item.minecraft.rabbit_stew": "Rabbit Stew", + "item.minecraft.rabbit_foot": "Rabbit's Foot", + "item.minecraft.rabbit_hide": "Rabbit Hide", + "item.minecraft.beef": "Raw Beef", + "item.minecraft.cooked_beef": "Steak", + "item.minecraft.painting": "Painting", + "item.minecraft.item_frame": "Item Frame", + "item.minecraft.golden_apple": "Golden Apple", + "item.minecraft.enchanted_golden_apple": "Enchanted Golden Apple", + "item.minecraft.sign": "Sign", + "item.minecraft.bucket": "Bucket", + "item.minecraft.water_bucket": "Water Bucket", + "item.minecraft.lava_bucket": "Lava Bucket", + "item.minecraft.pufferfish_bucket": "Bucket of Pufferfish", + "item.minecraft.salmon_bucket": "Bucket of Salmon", + "item.minecraft.cod_bucket": "Bucket of Cod", + "item.minecraft.tropical_fish_bucket": "Bucket of Tropical Fish", + "item.minecraft.powder_snow_bucket": "Powder Snow Bucket", + "item.minecraft.axolotl_bucket": "Bucket of Axolotl", + "item.minecraft.tadpole_bucket": "Bucket of Tadpole", + "item.minecraft.minecart": "Minecart", + "item.minecraft.saddle": "Saddle", + "item.minecraft.redstone": "Redstone Dust", + "item.minecraft.snowball": "Snowball", + "item.minecraft.oak_boat": "Oak Boat", + "item.minecraft.oak_chest_boat": "Oak Boat with Chest", + "item.minecraft.spruce_boat": "Spruce Boat", + "item.minecraft.spruce_chest_boat": "Spruce Boat with Chest", + "item.minecraft.birch_boat": "Birch Boat", + "item.minecraft.birch_chest_boat": "Birch Boat with Chest", + "item.minecraft.jungle_boat": "Jungle Boat", + "item.minecraft.jungle_chest_boat": "Jungle Boat with Chest", + "item.minecraft.acacia_boat": "Acacia Boat", + "item.minecraft.acacia_chest_boat": "Acacia Boat with Chest", + "item.minecraft.dark_oak_boat": "Dark Oak Boat", + "item.minecraft.dark_oak_chest_boat": "Dark Oak Boat with Chest", + "item.minecraft.mangrove_boat": "Mangrove Boat", + "item.minecraft.mangrove_chest_boat": "Mangrove Boat with Chest", + "item.minecraft.bamboo_raft": "Bamboo Raft", + "item.minecraft.bamboo_chest_raft": "Bamboo Raft with Chest", + "item.minecraft.leather": "Leather", + "item.minecraft.milk_bucket": "Milk Bucket", + "item.minecraft.brick": "Brick", + "item.minecraft.clay_ball": "Clay Ball", + "item.minecraft.paper": "Paper", + "item.minecraft.book": "Book", + "item.minecraft.slime_ball": "Slimeball", + "item.minecraft.chest_minecart": "Minecart with Chest", + "item.minecraft.furnace_minecart": "Minecart with Furnace", + "item.minecraft.tnt_minecart": "Minecart with TNT", + "item.minecraft.hopper_minecart": "Minecart with Hopper", + "item.minecraft.command_block_minecart": "Minecart with Command Block", + "item.minecraft.egg": "Egg", + "item.minecraft.compass": "Compass", + "item.minecraft.recovery_compass": "Recovery Compass", + "item.minecraft.fishing_rod": "Fishing Rod", + "item.minecraft.clock": "Clock", + "item.minecraft.glowstone_dust": "Glowstone Dust", + "item.minecraft.cod": "Raw Cod", + "item.minecraft.salmon": "Raw Salmon", + "item.minecraft.pufferfish": "Pufferfish", + "item.minecraft.tropical_fish": "Tropical Fish", + "item.minecraft.cooked_cod": "Cooked Cod", + "item.minecraft.cooked_salmon": "Cooked Salmon", + "item.minecraft.music_disc_13": "Music Disc", + "item.minecraft.music_disc_cat": "Music Disc", + "item.minecraft.music_disc_blocks": "Music Disc", + "item.minecraft.music_disc_chirp": "Music Disc", + "item.minecraft.music_disc_far": "Music Disc", + "item.minecraft.music_disc_mall": "Music Disc", + "item.minecraft.music_disc_mellohi": "Music Disc", + "item.minecraft.music_disc_stal": "Music Disc", + "item.minecraft.music_disc_strad": "Music Disc", + "item.minecraft.music_disc_ward": "Music Disc", + "item.minecraft.music_disc_11": "Music Disc", + "item.minecraft.music_disc_wait": "Music Disc", + "item.minecraft.music_disc_pigstep": "Music Disc", + "item.minecraft.music_disc_otherside": "Music Disc", + "item.minecraft.music_disc_5": "Music Disc", + "item.minecraft.music_disc_13.desc": "C418 - 13", + "item.minecraft.music_disc_cat.desc": "C418 - cat", + "item.minecraft.music_disc_blocks.desc": "C418 - blocks", + "item.minecraft.music_disc_chirp.desc": "C418 - chirp", + "item.minecraft.music_disc_far.desc": "C418 - far", + "item.minecraft.music_disc_mall.desc": "C418 - mall", + "item.minecraft.music_disc_mellohi.desc": "C418 - mellohi", + "item.minecraft.music_disc_stal.desc": "C418 - stal", + "item.minecraft.music_disc_strad.desc": "C418 - strad", + "item.minecraft.music_disc_ward.desc": "C418 - ward", + "item.minecraft.music_disc_11.desc": "C418 - 11", + "item.minecraft.music_disc_wait.desc": "C418 - wait", + "item.minecraft.music_disc_pigstep.desc": "Lena Raine - Pigstep", + "item.minecraft.music_disc_otherside.desc": "Lena Raine - otherside", + "item.minecraft.music_disc_5.desc": "Samuel Åberg - 5", + "item.minecraft.bone": "Bone", + "item.minecraft.ink_sac": "Ink Sac", + "item.minecraft.red_dye": "Red Dye", + "item.minecraft.green_dye": "Green Dye", + "item.minecraft.cocoa_beans": "Cocoa Beans", + "item.minecraft.lapis_lazuli": "Lapis Lazuli", + "item.minecraft.purple_dye": "Purple Dye", + "item.minecraft.cyan_dye": "Cyan Dye", + "item.minecraft.light_gray_dye": "Light Gray Dye", + "item.minecraft.gray_dye": "Gray Dye", + "item.minecraft.pink_dye": "Pink Dye", + "item.minecraft.lime_dye": "Lime Dye", + "item.minecraft.yellow_dye": "Yellow Dye", + "item.minecraft.light_blue_dye": "Light Blue Dye", + "item.minecraft.magenta_dye": "Magenta Dye", + "item.minecraft.orange_dye": "Orange Dye", + "item.minecraft.bone_meal": "Bone Meal", + "item.minecraft.blue_dye": "Blue Dye", + "item.minecraft.black_dye": "Black Dye", + "item.minecraft.brown_dye": "Brown Dye", + "item.minecraft.white_dye": "White Dye", + "item.minecraft.sugar": "Sugar", + "item.minecraft.amethyst_shard": "Amethyst Shard", + "item.minecraft.spyglass": "Spyglass", + "item.minecraft.glow_berries": "Glow Berries", + "item.minecraft.disc_fragment_5": "Disc Fragment", + "item.minecraft.disc_fragment_5.desc": "Music Disc - 5", + "block.minecraft.black_bed": "Black Bed", + "block.minecraft.red_bed": "Red Bed", + "block.minecraft.green_bed": "Green Bed", + "block.minecraft.brown_bed": "Brown Bed", + "block.minecraft.blue_bed": "Blue Bed", + "block.minecraft.purple_bed": "Purple Bed", + "block.minecraft.cyan_bed": "Cyan Bed", + "block.minecraft.light_gray_bed": "Light Gray Bed", + "block.minecraft.gray_bed": "Gray Bed", + "block.minecraft.pink_bed": "Pink Bed", + "block.minecraft.lime_bed": "Lime Bed", + "block.minecraft.yellow_bed": "Yellow Bed", + "block.minecraft.light_blue_bed": "Light Blue Bed", + "block.minecraft.magenta_bed": "Magenta Bed", + "block.minecraft.orange_bed": "Orange Bed", + "block.minecraft.white_bed": "White Bed", + "block.minecraft.repeater": "Redstone Repeater", + "block.minecraft.comparator": "Redstone Comparator", + "item.minecraft.filled_map": "Map", + "item.minecraft.shears": "Shears", + "item.minecraft.rotten_flesh": "Rotten Flesh", + "item.minecraft.ender_pearl": "Ender Pearl", + "item.minecraft.blaze_rod": "Blaze Rod", + "item.minecraft.ghast_tear": "Ghast Tear", + "item.minecraft.nether_wart": "Nether Wart", + "item.minecraft.potion": "Potion", + "item.minecraft.splash_potion": "Splash Potion", + "item.minecraft.lingering_potion": "Lingering Potion", + "item.minecraft.end_crystal": "End Crystal", + "item.minecraft.gold_nugget": "Gold Nugget", + "item.minecraft.glass_bottle": "Glass Bottle", + "item.minecraft.spider_eye": "Spider Eye", + "item.minecraft.fermented_spider_eye": "Fermented Spider Eye", + "item.minecraft.blaze_powder": "Blaze Powder", + "item.minecraft.magma_cream": "Magma Cream", + "item.minecraft.cauldron": "Cauldron", + "item.minecraft.brewing_stand": "Brewing Stand", + "item.minecraft.ender_eye": "Eye of Ender", + "item.minecraft.glistering_melon_slice": "Glistering Melon Slice", + "item.minecraft.allay_spawn_egg": "Allay Spawn Egg", + "item.minecraft.axolotl_spawn_egg": "Axolotl Spawn Egg", + "item.minecraft.bat_spawn_egg": "Bat Spawn Egg", + "item.minecraft.bee_spawn_egg": "Bee Spawn Egg", + "item.minecraft.blaze_spawn_egg": "Blaze Spawn Egg", + "item.minecraft.cat_spawn_egg": "Cat Spawn Egg", + "item.minecraft.camel_spawn_egg": "Camel Spawn Egg", + "item.minecraft.cave_spider_spawn_egg": "Cave Spider Spawn Egg", + "item.minecraft.chicken_spawn_egg": "Chicken Spawn Egg", + "item.minecraft.cod_spawn_egg": "Cod Spawn Egg", + "item.minecraft.cow_spawn_egg": "Cow Spawn Egg", + "item.minecraft.creeper_spawn_egg": "Creeper Spawn Egg", + "item.minecraft.dolphin_spawn_egg": "Dolphin Spawn Egg", + "item.minecraft.donkey_spawn_egg": "Donkey Spawn Egg", + "item.minecraft.drowned_spawn_egg": "Drowned Spawn Egg", + "item.minecraft.elder_guardian_spawn_egg": "Elder Guardian Spawn Egg", + "item.minecraft.ender_dragon_spawn_egg": "Ender Dragon Spawn Egg", + "item.minecraft.enderman_spawn_egg": "Enderman Spawn Egg", + "item.minecraft.endermite_spawn_egg": "Endermite Spawn Egg", + "item.minecraft.evoker_spawn_egg": "Evoker Spawn Egg", + "item.minecraft.ghast_spawn_egg": "Ghast Spawn Egg", + "item.minecraft.glow_squid_spawn_egg": "Glow Squid Spawn Egg", + "item.minecraft.guardian_spawn_egg": "Guardian Spawn Egg", + "item.minecraft.hoglin_spawn_egg": "Hoglin Spawn Egg", + "item.minecraft.horse_spawn_egg": "Horse Spawn Egg", + "item.minecraft.husk_spawn_egg": "Husk Spawn Egg", + "item.minecraft.iron_golem_spawn_egg": "Iron Golem Spawn Egg", + "item.minecraft.ravager_spawn_egg": "Ravager Spawn Egg", + "item.minecraft.llama_spawn_egg": "Llama Spawn Egg", + "item.minecraft.magma_cube_spawn_egg": "Magma Cube Spawn Egg", + "item.minecraft.mooshroom_spawn_egg": "Mooshroom Spawn Egg", + "item.minecraft.mule_spawn_egg": "Mule Spawn Egg", + "item.minecraft.ocelot_spawn_egg": "Ocelot Spawn Egg", + "item.minecraft.panda_spawn_egg": "Panda Spawn Egg", + "item.minecraft.parrot_spawn_egg": "Parrot Spawn Egg", + "item.minecraft.pig_spawn_egg": "Pig Spawn Egg", + "item.minecraft.piglin_spawn_egg": "Piglin Spawn Egg", + "item.minecraft.piglin_brute_spawn_egg": "Piglin Brute Spawn Egg", + "item.minecraft.pillager_spawn_egg": "Pillager Spawn Egg", + "item.minecraft.phantom_spawn_egg": "Phantom Spawn Egg", + "item.minecraft.polar_bear_spawn_egg": "Polar Bear Spawn Egg", + "item.minecraft.pufferfish_spawn_egg": "Pufferfish Spawn Egg", + "item.minecraft.rabbit_spawn_egg": "Rabbit Spawn Egg", + "item.minecraft.fox_spawn_egg": "Fox Spawn Egg", + "item.minecraft.frog_spawn_egg": "Frog Spawn Egg", + "item.minecraft.salmon_spawn_egg": "Salmon Spawn Egg", + "item.minecraft.sheep_spawn_egg": "Sheep Spawn Egg", + "item.minecraft.shulker_spawn_egg": "Shulker Spawn Egg", + "item.minecraft.silverfish_spawn_egg": "Silverfish Spawn Egg", + "item.minecraft.skeleton_spawn_egg": "Skeleton Spawn Egg", + "item.minecraft.skeleton_horse_spawn_egg": "Skeleton Horse Spawn Egg", + "item.minecraft.slime_spawn_egg": "Slime Spawn Egg", + "item.minecraft.snow_golem_spawn_egg": "Snow Golem Spawn Egg", + "item.minecraft.spider_spawn_egg": "Spider Spawn Egg", + "item.minecraft.squid_spawn_egg": "Squid Spawn Egg", + "item.minecraft.stray_spawn_egg": "Stray Spawn Egg", + "item.minecraft.strider_spawn_egg": "Strider Spawn Egg", + "item.minecraft.tadpole_spawn_egg": "Tadpole Spawn Egg", + "item.minecraft.trader_llama_spawn_egg": "Trader Llama Spawn Egg", + "item.minecraft.tropical_fish_spawn_egg": "Tropical Fish Spawn Egg", + "item.minecraft.turtle_spawn_egg": "Turtle Spawn Egg", + "item.minecraft.vex_spawn_egg": "Vex Spawn Egg", + "item.minecraft.villager_spawn_egg": "Villager Spawn Egg", + "item.minecraft.wandering_trader_spawn_egg": "Wandering Trader Spawn Egg", + "item.minecraft.vindicator_spawn_egg": "Vindicator Spawn Egg", + "item.minecraft.warden_spawn_egg": "Warden Spawn Egg", + "item.minecraft.witch_spawn_egg": "Witch Spawn Egg", + "item.minecraft.wither_spawn_egg": "Wither Spawn Egg", + "item.minecraft.wither_skeleton_spawn_egg": "Wither Skeleton Spawn Egg", + "item.minecraft.wolf_spawn_egg": "Wolf Spawn Egg", + "item.minecraft.zoglin_spawn_egg": "Zoglin Spawn Egg", + "item.minecraft.zombie_spawn_egg": "Zombie Spawn Egg", + "item.minecraft.zombie_horse_spawn_egg": "Zombie Horse Spawn Egg", + "item.minecraft.zombified_piglin_spawn_egg": "Zombified Piglin Spawn Egg", + "item.minecraft.zombie_villager_spawn_egg": "Zombie Villager Spawn Egg", + "item.minecraft.goat_spawn_egg": "Goat Spawn Egg", + "item.minecraft.experience_bottle": "Bottle o' Enchanting", + "item.minecraft.fire_charge": "Fire Charge", + "item.minecraft.writable_book": "Book and Quill", + "item.minecraft.written_book": "Written Book", + "item.minecraft.flower_pot": "Flower Pot", + "item.minecraft.map": "Empty Map", + "item.minecraft.carrot": "Carrot", + "item.minecraft.golden_carrot": "Golden Carrot", + "item.minecraft.potato": "Potato", + "item.minecraft.baked_potato": "Baked Potato", + "item.minecraft.poisonous_potato": "Poisonous Potato", + "item.minecraft.carrot_on_a_stick": "Carrot on a Stick", + "item.minecraft.nether_star": "Nether Star", + "item.minecraft.pumpkin_pie": "Pumpkin Pie", + "item.minecraft.enchanted_book": "Enchanted Book", + "item.minecraft.firework_rocket": "Firework Rocket", + "item.minecraft.firework_rocket.flight": "Flight Duration:", + "item.minecraft.firework_star": "Firework Star", + "item.minecraft.firework_star.black": "Black", + "item.minecraft.firework_star.red": "Red", + "item.minecraft.firework_star.green": "Green", + "item.minecraft.firework_star.brown": "Brown", + "item.minecraft.firework_star.blue": "Blue", + "item.minecraft.firework_star.purple": "Purple", + "item.minecraft.firework_star.cyan": "Cyan", + "item.minecraft.firework_star.light_gray": "Light Gray", + "item.minecraft.firework_star.gray": "Gray", + "item.minecraft.firework_star.pink": "Pink", + "item.minecraft.firework_star.lime": "Lime", + "item.minecraft.firework_star.yellow": "Yellow", + "item.minecraft.firework_star.light_blue": "Light Blue", + "item.minecraft.firework_star.magenta": "Magenta", + "item.minecraft.firework_star.orange": "Orange", + "item.minecraft.firework_star.white": "White", + "item.minecraft.firework_star.custom_color": "Custom", + "item.minecraft.firework_star.fade_to": "Fade to", + "item.minecraft.firework_star.flicker": "Twinkle", + "item.minecraft.firework_star.trail": "Trail", + "item.minecraft.firework_star.shape.small_ball": "Small Ball", + "item.minecraft.firework_star.shape.large_ball": "Large Ball", + "item.minecraft.firework_star.shape.star": "Star-shaped", + "item.minecraft.firework_star.shape.creeper": "Creeper-shaped", + "item.minecraft.firework_star.shape.burst": "Burst", + "item.minecraft.firework_star.shape": "Unknown Shape", + "item.minecraft.nether_brick": "Nether Brick", + "item.minecraft.quartz": "Nether Quartz", + "item.minecraft.armor_stand": "Armor Stand", + "item.minecraft.iron_horse_armor": "Iron Horse Armor", + "item.minecraft.golden_horse_armor": "Golden Horse Armor", + "item.minecraft.diamond_horse_armor": "Diamond Horse Armor", + "item.minecraft.leather_horse_armor": "Leather Horse Armor", + "item.minecraft.prismarine_shard": "Prismarine Shard", + "item.minecraft.prismarine_crystals": "Prismarine Crystals", + "item.minecraft.chorus_fruit": "Chorus Fruit", + "item.minecraft.popped_chorus_fruit": "Popped Chorus Fruit", + "item.minecraft.beetroot": "Beetroot", + "item.minecraft.beetroot_seeds": "Beetroot Seeds", + "item.minecraft.beetroot_soup": "Beetroot Soup", + "item.minecraft.dragon_breath": "Dragon's Breath", + "item.minecraft.elytra": "Elytra", + "item.minecraft.totem_of_undying": "Totem of Undying", + "item.minecraft.shulker_shell": "Shulker Shell", + "item.minecraft.iron_nugget": "Iron Nugget", + "item.minecraft.knowledge_book": "Knowledge Book", + "item.minecraft.debug_stick": "Debug Stick", + "item.minecraft.debug_stick.empty": "%s has no properties", + "item.minecraft.debug_stick.update": "\"%s\" to %s", + "item.minecraft.debug_stick.select": "selected \"%s\" (%s)", + "item.minecraft.trident": "Trident", + "item.minecraft.scute": "Scute", + "item.minecraft.turtle_helmet": "Turtle Shell", + "item.minecraft.phantom_membrane": "Phantom Membrane", + "item.minecraft.nautilus_shell": "Nautilus Shell", + "item.minecraft.heart_of_the_sea": "Heart of the Sea", + "item.minecraft.crossbow": "Crossbow", + "item.minecraft.crossbow.projectile": "Projectile:", + "item.minecraft.suspicious_stew": "Suspicious Stew", + "item.minecraft.creeper_banner_pattern": "Banner Pattern", + "item.minecraft.skull_banner_pattern": "Banner Pattern", + "item.minecraft.flower_banner_pattern": "Banner Pattern", + "item.minecraft.mojang_banner_pattern": "Banner Pattern", + "item.minecraft.globe_banner_pattern": "Banner Pattern", + "item.minecraft.creeper_banner_pattern.desc": "Creeper Charge", + "item.minecraft.skull_banner_pattern.desc": "Skull Charge", + "item.minecraft.flower_banner_pattern.desc": "Flower Charge", + "item.minecraft.mojang_banner_pattern.desc": "Thing", + "item.minecraft.globe_banner_pattern.desc": "Globe", + "item.minecraft.piglin_banner_pattern": "Banner Pattern", + "item.minecraft.piglin_banner_pattern.desc": "Snout", + "item.minecraft.sweet_berries": "Sweet Berries", + "item.minecraft.honey_bottle": "Honey Bottle", + "item.minecraft.honeycomb": "Honeycomb", + "item.minecraft.lodestone_compass": "Lodestone Compass", + "item.minecraft.netherite_scrap": "Netherite Scrap", + "item.minecraft.netherite_ingot": "Netherite Ingot", + "item.minecraft.netherite_helmet": "Netherite Helmet", + "item.minecraft.netherite_chestplate": "Netherite Chestplate", + "item.minecraft.netherite_leggings": "Netherite Leggings", + "item.minecraft.netherite_boots": "Netherite Boots", + "item.minecraft.netherite_axe": "Netherite Axe", + "item.minecraft.netherite_pickaxe": "Netherite Pickaxe", + "item.minecraft.netherite_hoe": "Netherite Hoe", + "item.minecraft.netherite_shovel": "Netherite Shovel", + "item.minecraft.netherite_sword": "Netherite Sword", + "item.minecraft.warped_fungus_on_a_stick": "Warped Fungus on a Stick", + "item.minecraft.glow_ink_sac": "Glow Ink Sac", + "item.minecraft.glow_item_frame": "Glow Item Frame", + "item.minecraft.echo_shard": "Echo Shard", + "item.minecraft.goat_horn": "Goat Horn", + "instrument.minecraft.ponder_goat_horn": "Ponder", + "instrument.minecraft.sing_goat_horn": "Sing", + "instrument.minecraft.seek_goat_horn": "Seek", + "instrument.minecraft.feel_goat_horn": "Feel", + "instrument.minecraft.admire_goat_horn": "Admire", + "instrument.minecraft.call_goat_horn": "Call", + "instrument.minecraft.yearn_goat_horn": "Yearn", + "instrument.minecraft.dream_goat_horn": "Dream", + "container.inventory": "Inventory", + "container.hopper": "Item Hopper", + "container.crafting": "Crafting", + "container.dispenser": "Dispenser", + "container.dropper": "Dropper", + "container.furnace": "Furnace", + "container.enchant": "Enchant", + "container.smoker": "Smoker", + "container.lectern": "Lectern", + "container.blast_furnace": "Blast Furnace", + "container.enchant.lapis.one": "1 Lapis Lazuli", + "container.enchant.lapis.many": "%s Lapis Lazuli", + "container.enchant.level.one": "1 Enchantment Level", + "container.enchant.level.many": "%s Enchantment Levels", + "container.enchant.level.requirement": "Level Requirement: %s", + "container.enchant.clue": "%s . . . ?", + "container.repair": "Repair & Name", + "container.repair.cost": "Enchantment Cost: %1$s", + "container.repair.expensive": "Too Expensive!", + "container.creative": "Item Selection", + "container.brewing": "Brewing Stand", + "container.chest": "Chest", + "container.chestDouble": "Large Chest", + "container.enderchest": "Ender Chest", + "container.beacon": "Beacon", + "container.shulkerBox": "Shulker Box", + "container.shulkerBox.more": "and %s more...", + "container.barrel": "Barrel", + "container.spectatorCantOpen": "Unable to open. Loot not generated yet.", + "container.isLocked": "%s is locked!", + "container.loom": "Loom", + "container.grindstone_title": "Repair & Disenchant", + "container.cartography_table": "Cartography Table", + "container.stonecutter": "Stonecutter", + "container.upgrade": "Upgrade Gear", + "structure_block.invalid_structure_name": "Invalid structure name '%s'", + "structure_block.save_success": "Structure saved as '%s'", + "structure_block.save_failure": "Unable to save structure '%s'", + "structure_block.load_success": "Structure loaded from '%s'", + "structure_block.load_prepare": "Structure '%s' position prepared", + "structure_block.load_not_found": "Structure '%s' is not available", + "structure_block.size_success": "Size successfully detected for '%s'", + "structure_block.size_failure": "Unable to detect structure size. Add corners with matching structure names", + "structure_block.mode.save": "Save", + "structure_block.mode.load": "Load", + "structure_block.mode.data": "Data", + "structure_block.mode.corner": "Corner", + "structure_block.hover.save": "Save: %s", + "structure_block.hover.load": "Load: %s", + "structure_block.hover.data": "Data: %s", + "structure_block.hover.corner": "Corner: %s", + "structure_block.mode_info.save": "Save Mode - Write to File", + "structure_block.mode_info.load": "Load Mode - Load from File", + "structure_block.mode_info.data": "Data Mode - Game Logic Marker", + "structure_block.mode_info.corner": "Corner Mode - Placement and Size Marker", + "structure_block.structure_name": "Structure Name", + "structure_block.custom_data": "Custom Data Tag Name", + "structure_block.position": "Relative Position", + "structure_block.position.x": "relative Position x", + "structure_block.position.y": "relative position y", + "structure_block.position.z": "relative position z", + "structure_block.size": "Structure Size", + "structure_block.size.x": "structure size x", + "structure_block.size.y": "structure size y", + "structure_block.size.z": "structure size z", + "structure_block.integrity": "Structure Integrity and Seed", + "structure_block.integrity.integrity": "Structure Integrity", + "structure_block.integrity.seed": "Structure Seed", + "structure_block.include_entities": "Include entities:", + "structure_block.detect_size": "Detect structure size and position:", + "structure_block.button.detect_size": "DETECT", + "structure_block.button.save": "SAVE", + "structure_block.button.load": "LOAD", + "structure_block.show_air": "Show Invisible Blocks:", + "structure_block.show_boundingbox": "Show Bounding Box:", + "jigsaw_block.pool": "Target Pool:", + "jigsaw_block.name": "Name:", + "jigsaw_block.target": "Target Name:", + "jigsaw_block.final_state": "Turns into:", + "jigsaw_block.levels": "Levels: %s", + "jigsaw_block.keep_jigsaws": "Keep Jigsaws", + "jigsaw_block.generate": "Generate", + "jigsaw_block.joint_label": "Joint Type:", + "jigsaw_block.joint.rollable": "Rollable", + "jigsaw_block.joint.aligned": "Aligned", + "item.dyed": "Dyed", + "item.unbreakable": "Unbreakable", + "item.canBreak": "Can break:", + "item.canPlace": "Can be placed on:", + "item.color": "Color: %s", + "item.nbt_tags": "NBT: %s tag(s)", + "item.durability": "Durability: %s / %s", + "item.disabled": "Disabled item", + "filled_map.mansion": "Woodland Explorer Map", + "filled_map.monument": "Ocean Explorer Map", + "filled_map.buried_treasure": "Buried Treasure Map", + "filled_map.unknown": "Unknown Map", + "filled_map.id": "Id #%s", + "filled_map.level": "(Level %s/%s)", + "filled_map.scale": "Scaling at 1:%s", + "filled_map.locked": "Locked", + "entity.minecraft.allay": "Allay", + "entity.minecraft.area_effect_cloud": "Area Effect Cloud", + "entity.minecraft.armor_stand": "Armor Stand", + "entity.minecraft.arrow": "Arrow", + "entity.minecraft.axolotl": "Axolotl", + "entity.minecraft.bat": "Bat", + "entity.minecraft.bee": "Bee", + "entity.minecraft.blaze": "Blaze", + "entity.minecraft.boat": "Boat", + "entity.minecraft.chest_boat": "Boat with Chest", + "entity.minecraft.cat": "Cat", + "entity.minecraft.camel": "Camel", + "entity.minecraft.cave_spider": "Cave Spider", + "entity.minecraft.chest_minecart": "Minecart with Chest", + "entity.minecraft.chicken": "Chicken", + "entity.minecraft.command_block_minecart": "Minecart with Command Block", + "entity.minecraft.cod": "Cod", + "entity.minecraft.cow": "Cow", + "entity.minecraft.creeper": "Creeper", + "entity.minecraft.dolphin": "Dolphin", + "entity.minecraft.donkey": "Donkey", + "entity.minecraft.drowned": "Drowned", + "entity.minecraft.dragon_fireball": "Dragon Fireball", + "entity.minecraft.egg": "Thrown Egg", + "entity.minecraft.elder_guardian": "Elder Guardian", + "entity.minecraft.end_crystal": "End Crystal", + "entity.minecraft.ender_dragon": "Ender Dragon", + "entity.minecraft.ender_pearl": "Thrown Ender Pearl", + "entity.minecraft.enderman": "Enderman", + "entity.minecraft.endermite": "Endermite", + "entity.minecraft.evoker_fangs": "Evoker Fangs", + "entity.minecraft.evoker": "Evoker", + "entity.minecraft.eye_of_ender": "Eye of Ender", + "entity.minecraft.falling_block": "Falling Block", + "entity.minecraft.fireball": "Fireball", + "entity.minecraft.firework_rocket": "Firework Rocket", + "entity.minecraft.fishing_bobber": "Fishing Bobber", + "entity.minecraft.fox": "Fox", + "entity.minecraft.frog": "Frog", + "entity.minecraft.furnace_minecart": "Minecart with Furnace", + "entity.minecraft.ghast": "Ghast", + "entity.minecraft.giant": "Giant", + "entity.minecraft.glow_item_frame": "Glow Item Frame", + "entity.minecraft.glow_squid": "Glow Squid", + "entity.minecraft.goat": "Goat", + "entity.minecraft.guardian": "Guardian", + "entity.minecraft.hoglin": "Hoglin", + "entity.minecraft.hopper_minecart": "Minecart with Hopper", + "entity.minecraft.horse": "Horse", + "entity.minecraft.husk": "Husk", + "entity.minecraft.ravager": "Ravager", + "entity.minecraft.illusioner": "Illusioner", + "entity.minecraft.item": "Item", + "entity.minecraft.item_frame": "Item Frame", + "entity.minecraft.killer_bunny": "The Killer Bunny", + "entity.minecraft.leash_knot": "Leash Knot", + "entity.minecraft.lightning_bolt": "Lightning Bolt", + "entity.minecraft.llama": "Llama", + "entity.minecraft.llama_spit": "Llama Spit", + "entity.minecraft.magma_cube": "Magma Cube", + "entity.minecraft.marker": "Marker", + "entity.minecraft.minecart": "Minecart", + "entity.minecraft.mooshroom": "Mooshroom", + "entity.minecraft.mule": "Mule", + "entity.minecraft.ocelot": "Ocelot", + "entity.minecraft.painting": "Painting", + "entity.minecraft.panda": "Panda", + "entity.minecraft.parrot": "Parrot", + "entity.minecraft.phantom": "Phantom", + "entity.minecraft.pig": "Pig", + "entity.minecraft.piglin": "Piglin", + "entity.minecraft.piglin_brute": "Piglin Brute", + "entity.minecraft.pillager": "Pillager", + "entity.minecraft.player": "Player", + "entity.minecraft.polar_bear": "Polar Bear", + "entity.minecraft.potion": "Potion", + "entity.minecraft.pufferfish": "Pufferfish", + "entity.minecraft.rabbit": "Rabbit", + "entity.minecraft.salmon": "Salmon", + "entity.minecraft.sheep": "Sheep", + "entity.minecraft.shulker": "Shulker", + "entity.minecraft.shulker_bullet": "Shulker Bullet", + "entity.minecraft.silverfish": "Silverfish", + "entity.minecraft.skeleton": "Skeleton", + "entity.minecraft.skeleton_horse": "Skeleton Horse", + "entity.minecraft.slime": "Slime", + "entity.minecraft.small_fireball": "Small Fireball", + "entity.minecraft.snowball": "Snowball", + "entity.minecraft.snow_golem": "Snow Golem", + "entity.minecraft.spawner_minecart": "Minecart with Monster Spawner", + "entity.minecraft.spectral_arrow": "Spectral Arrow", + "entity.minecraft.spider": "Spider", + "entity.minecraft.squid": "Squid", + "entity.minecraft.stray": "Stray", + "entity.minecraft.strider": "Strider", + "entity.minecraft.tadpole": "Tadpole", + "entity.minecraft.tnt": "Primed TNT", + "entity.minecraft.tnt_minecart": "Minecart with TNT", + "entity.minecraft.trader_llama": "Trader Llama", + "entity.minecraft.trident": "Trident", + "entity.minecraft.tropical_fish": "Tropical Fish", + "entity.minecraft.tropical_fish.predefined.0": "Anemone", + "entity.minecraft.tropical_fish.predefined.1": "Black Tang", + "entity.minecraft.tropical_fish.predefined.2": "Blue Tang", + "entity.minecraft.tropical_fish.predefined.3": "Butterflyfish", + "entity.minecraft.tropical_fish.predefined.4": "Cichlid", + "entity.minecraft.tropical_fish.predefined.5": "Clownfish", + "entity.minecraft.tropical_fish.predefined.6": "Cotton Candy Betta", + "entity.minecraft.tropical_fish.predefined.7": "Dottyback", + "entity.minecraft.tropical_fish.predefined.8": "Emperor Red Snapper", + "entity.minecraft.tropical_fish.predefined.9": "Goatfish", + "entity.minecraft.tropical_fish.predefined.10": "Moorish Idol", + "entity.minecraft.tropical_fish.predefined.11": "Ornate Butterflyfish", + "entity.minecraft.tropical_fish.predefined.12": "Parrotfish", + "entity.minecraft.tropical_fish.predefined.13": "Queen Angelfish", + "entity.minecraft.tropical_fish.predefined.14": "Red Cichlid", + "entity.minecraft.tropical_fish.predefined.15": "Red Lipped Blenny", + "entity.minecraft.tropical_fish.predefined.16": "Red Snapper", + "entity.minecraft.tropical_fish.predefined.17": "Threadfin", + "entity.minecraft.tropical_fish.predefined.18": "Tomato Clownfish", + "entity.minecraft.tropical_fish.predefined.19": "Triggerfish", + "entity.minecraft.tropical_fish.predefined.20": "Yellowtail Parrotfish", + "entity.minecraft.tropical_fish.predefined.21": "Yellow Tang", + "entity.minecraft.tropical_fish.type.flopper": "Flopper", + "entity.minecraft.tropical_fish.type.stripey": "Stripey", + "entity.minecraft.tropical_fish.type.glitter": "Glitter", + "entity.minecraft.tropical_fish.type.blockfish": "Blockfish", + "entity.minecraft.tropical_fish.type.betty": "Betty", + "entity.minecraft.tropical_fish.type.clayfish": "Clayfish", + "entity.minecraft.tropical_fish.type.kob": "Kob", + "entity.minecraft.tropical_fish.type.sunstreak": "Sunstreak", + "entity.minecraft.tropical_fish.type.snooper": "Snooper", + "entity.minecraft.tropical_fish.type.dasher": "Dasher", + "entity.minecraft.tropical_fish.type.brinely": "Brinely", + "entity.minecraft.tropical_fish.type.spotty": "Spotty", + "entity.minecraft.turtle": "Turtle", + "entity.minecraft.vex": "Vex", + "entity.minecraft.villager.armorer": "Armorer", + "entity.minecraft.villager.butcher": "Butcher", + "entity.minecraft.villager.cartographer": "Cartographer", + "entity.minecraft.villager.cleric": "Cleric", + "entity.minecraft.villager.farmer": "Farmer", + "entity.minecraft.villager.fisherman": "Fisherman", + "entity.minecraft.villager.fletcher": "Fletcher", + "entity.minecraft.villager.leatherworker": "Leatherworker", + "entity.minecraft.villager.librarian": "Librarian", + "entity.minecraft.villager.mason": "Mason", + "entity.minecraft.villager.none": "Villager", + "entity.minecraft.villager.nitwit": "Nitwit", + "entity.minecraft.villager.shepherd": "Shepherd", + "entity.minecraft.villager.toolsmith": "Toolsmith", + "entity.minecraft.villager.weaponsmith": "Weaponsmith", + "entity.minecraft.villager": "Villager", + "entity.minecraft.wandering_trader": "Wandering Trader", + "entity.minecraft.iron_golem": "Iron Golem", + "entity.minecraft.vindicator": "Vindicator", + "entity.minecraft.warden": "Warden", + "entity.minecraft.witch": "Witch", + "entity.minecraft.wither": "Wither", + "entity.minecraft.wither_skeleton": "Wither Skeleton", + "entity.minecraft.wither_skull": "Wither Skull", + "entity.minecraft.wolf": "Wolf", + "entity.minecraft.experience_bottle": "Thrown Bottle o' Enchanting", + "entity.minecraft.experience_orb": "Experience Orb", + "entity.minecraft.zoglin": "Zoglin", + "entity.minecraft.zombie": "Zombie", + "entity.minecraft.zombie_horse": "Zombie Horse", + "entity.minecraft.zombified_piglin": "Zombified Piglin", + "entity.minecraft.zombie_villager": "Zombie Villager", + "death.fell.accident.ladder": "%1$s fell off a ladder", + "death.fell.accident.vines": "%1$s fell off some vines", + "death.fell.accident.weeping_vines": "%1$s fell off some weeping vines", + "death.fell.accident.twisting_vines": "%1$s fell off some twisting vines", + "death.fell.accident.scaffolding": "%1$s fell off scaffolding", + "death.fell.accident.other_climbable": "%1$s fell while climbing", + "death.fell.accident.generic": "%1$s fell from a high place", + "death.fell.killer": "%1$s was doomed to fall", + "death.fell.assist": "%1$s was doomed to fall by %2$s", + "death.fell.assist.item": "%1$s was doomed to fall by %2$s using %3$s", + "death.fell.finish": "%1$s fell too far and was finished by %2$s", + "death.fell.finish.item": "%1$s fell too far and was finished by %2$s using %3$s", + "death.attack.lightningBolt": "%1$s was struck by lightning", + "death.attack.lightningBolt.player": "%1$s was struck by lightning whilst fighting %2$s", + "death.attack.inFire": "%1$s went up in flames", + "death.attack.inFire.player": "%1$s walked into fire whilst fighting %2$s", + "death.attack.onFire": "%1$s burned to death", + "death.attack.onFire.item": "%1$s was burnt to a crisp whilst fighting %2$s wielding %3$s", + "death.attack.onFire.player": "%1$s was burnt to a crisp whilst fighting %2$s", + "death.attack.lava": "%1$s tried to swim in lava", + "death.attack.lava.player": "%1$s tried to swim in lava to escape %2$s", + "death.attack.hotFloor": "%1$s discovered the floor was lava", + "death.attack.hotFloor.player": "%1$s walked into danger zone due to %2$s", + "death.attack.inWall": "%1$s suffocated in a wall", + "death.attack.inWall.player": "%1$s suffocated in a wall whilst fighting %2$s", + "death.attack.cramming": "%1$s was squished too much", + "death.attack.cramming.player": "%1$s was squashed by %2$s", + "death.attack.drown": "%1$s drowned", + "death.attack.drown.player": "%1$s drowned whilst trying to escape %2$s", + "death.attack.dryout": "%1$s died from dehydration", + "death.attack.dryout.player": "%1$s died from dehydration whilst trying to escape %2$s", + "death.attack.starve": "%1$s starved to death", + "death.attack.starve.player": "%1$s starved to death whilst fighting %2$s", + "death.attack.cactus": "%1$s was pricked to death", + "death.attack.cactus.player": "%1$s walked into a cactus whilst trying to escape %2$s", + "death.attack.generic": "%1$s died", + "death.attack.generic.player": "%1$s died because of %2$s", + "death.attack.explosion": "%1$s blew up", + "death.attack.explosion.player": "%1$s was blown up by %2$s", + "death.attack.explosion.player.item": "%1$s was blown up by %2$s using %3$s", + "death.attack.magic": "%1$s was killed by magic", + "death.attack.magic.player": "%1$s was killed by magic whilst trying to escape %2$s", + "death.attack.even_more_magic": "%1$s was killed by even more magic", + "death.attack.message_too_long": "Actually, message was too long to deliver fully. Sorry! Here's stripped version: %s", + "death.attack.wither": "%1$s withered away", + "death.attack.wither.player": "%1$s withered away whilst fighting %2$s", + "death.attack.witherSkull": "%1$s was shot by a skull from %2$s", + "death.attack.witherSkull.item": "%1$s was shot by a skull from %2$s using %3$s", + "death.attack.anvil": "%1$s was squashed by a falling anvil", + "death.attack.anvil.player": "%1$s was squashed by a falling anvil whilst fighting %2$s", + "death.attack.fallingBlock": "%1$s was squashed by a falling block", + "death.attack.fallingBlock.player": "%1$s was squashed by a falling block whilst fighting %2$s", + "death.attack.stalagmite": "%1$s was impaled on a stalagmite", + "death.attack.stalagmite.player": "%1$s was impaled on a stalagmite whilst fighting %2$s", + "death.attack.fallingStalactite": "%1$s was skewered by a falling stalactite", + "death.attack.fallingStalactite.player": "%1$s was skewered by a falling stalactite whilst fighting %2$s", + "death.attack.sonic_boom": "%1$s was obliterated by a sonically-charged shriek", + "death.attack.sonic_boom.item": "%1$s was obliterated by a sonically-charged shriek whilst trying to escape %2$s wielding %3$s", + "death.attack.sonic_boom.player": "%1$s was obliterated by a sonically-charged shriek whilst trying to escape %2$s", + "death.attack.mob": "%1$s was slain by %2$s", + "death.attack.mob.item": "%1$s was slain by %2$s using %3$s", + "death.attack.player": "%1$s was slain by %2$s", + "death.attack.player.item": "%1$s was slain by %2$s using %3$s", + "death.attack.arrow": "%1$s was shot by %2$s", + "death.attack.arrow.item": "%1$s was shot by %2$s using %3$s", + "death.attack.fireball": "%1$s was fireballed by %2$s", + "death.attack.fireball.item": "%1$s was fireballed by %2$s using %3$s", + "death.attack.thrown": "%1$s was pummeled by %2$s", + "death.attack.thrown.item": "%1$s was pummeled by %2$s using %3$s", + "death.attack.indirectMagic": "%1$s was killed by %2$s using magic", + "death.attack.indirectMagic.item": "%1$s was killed by %2$s using %3$s", + "death.attack.thorns": "%1$s was killed trying to hurt %2$s", + "death.attack.thorns.item": "%1$s was killed by %3$s trying to hurt %2$s", + "death.attack.trident": "%1$s was impaled by %2$s", + "death.attack.trident.item": "%1$s was impaled by %2$s with %3$s", + "death.attack.fall": "%1$s hit the ground too hard", + "death.attack.fall.player": "%1$s hit the ground too hard whilst trying to escape %2$s", + "death.attack.outOfWorld": "%1$s fell out of the world", + "death.attack.outOfWorld.player": "%1$s didn't want to live in the same world as %2$s", + "death.attack.dragonBreath": "%1$s was roasted in dragon breath", + "death.attack.dragonBreath.player": "%1$s was roasted in dragon breath by %2$s", + "death.attack.flyIntoWall": "%1$s experienced kinetic energy", + "death.attack.flyIntoWall.player": "%1$s experienced kinetic energy whilst trying to escape %2$s", + "death.attack.fireworks": "%1$s went off with a bang", + "death.attack.fireworks.player": "%1$s went off with a bang whilst fighting %2$s", + "death.attack.fireworks.item": "%1$s went off with a bang due to a firework fired from %3$s by %2$s", + "death.attack.badRespawnPoint.message": "%1$s was killed by %2$s", + "death.attack.badRespawnPoint.link": "Intentional Game Design", + "death.attack.sweetBerryBush": "%1$s was poked to death by a sweet berry bush", + "death.attack.sweetBerryBush.player": "%1$s was poked to death by a sweet berry bush whilst trying to escape %2$s", + "death.attack.sting": "%1$s was stung to death", + "death.attack.sting.item": "%1$s was stung to death by %2$s using %3$s", + "death.attack.sting.player": "%1$s was stung to death by %2$s", + "death.attack.freeze": "%1$s froze to death", + "death.attack.freeze.player": "%1$s was frozen to death by %2$s", + "deathScreen.respawn": "Respawn", + "deathScreen.spectate": "Spectate World", + "deathScreen.titleScreen": "Title Screen", + "deathScreen.score": "Score", + "deathScreen.title.hardcore": "Game Over!", + "deathScreen.title": "You Died!", + "deathScreen.quit.confirm": "Are you sure you want to quit?", + "effect.none": "No Effects", + "effect.minecraft.speed": "Speed", + "effect.minecraft.slowness": "Slowness", + "effect.minecraft.haste": "Haste", + "effect.minecraft.mining_fatigue": "Mining Fatigue", + "effect.minecraft.strength": "Strength", + "effect.minecraft.instant_health": "Instant Health", + "effect.minecraft.instant_damage": "Instant Damage", + "effect.minecraft.jump_boost": "Jump Boost", + "effect.minecraft.nausea": "Nausea", + "effect.minecraft.regeneration": "Regeneration", + "effect.minecraft.resistance": "Resistance", + "effect.minecraft.fire_resistance": "Fire Resistance", + "effect.minecraft.water_breathing": "Water Breathing", + "effect.minecraft.invisibility": "Invisibility", + "effect.minecraft.blindness": "Blindness", + "effect.minecraft.night_vision": "Night Vision", + "effect.minecraft.hunger": "Hunger", + "effect.minecraft.weakness": "Weakness", + "effect.minecraft.poison": "Poison", + "effect.minecraft.wither": "Wither", + "effect.minecraft.health_boost": "Health Boost", + "effect.minecraft.absorption": "Absorption", + "effect.minecraft.saturation": "Saturation", + "effect.minecraft.glowing": "Glowing", + "effect.minecraft.luck": "Luck", + "effect.minecraft.unluck": "Bad Luck", + "effect.minecraft.levitation": "Levitation", + "effect.minecraft.slow_falling": "Slow Falling", + "effect.minecraft.conduit_power": "Conduit Power", + "effect.minecraft.dolphins_grace": "Dolphin's Grace", + "effect.minecraft.bad_omen": "Bad Omen", + "effect.minecraft.hero_of_the_village": "Hero of the Village", + "effect.minecraft.darkness": "Darkness", + "event.minecraft.raid": "Raid", + "event.minecraft.raid.raiders_remaining": "Raiders Remaining: %s", + "event.minecraft.raid.victory": "Victory", + "event.minecraft.raid.defeat": "Defeat", + "item.minecraft.tipped_arrow.effect.empty": "Uncraftable Tipped Arrow", + "item.minecraft.tipped_arrow.effect.water": "Arrow of Splashing", + "item.minecraft.tipped_arrow.effect.mundane": "Tipped Arrow", + "item.minecraft.tipped_arrow.effect.thick": "Tipped Arrow", + "item.minecraft.tipped_arrow.effect.awkward": "Tipped Arrow", + "item.minecraft.tipped_arrow.effect.night_vision": "Arrow of Night Vision", + "item.minecraft.tipped_arrow.effect.invisibility": "Arrow of Invisibility", + "item.minecraft.tipped_arrow.effect.leaping": "Arrow of Leaping", + "item.minecraft.tipped_arrow.effect.fire_resistance": "Arrow of Fire Resistance", + "item.minecraft.tipped_arrow.effect.swiftness": "Arrow of Swiftness", + "item.minecraft.tipped_arrow.effect.slowness": "Arrow of Slowness", + "item.minecraft.tipped_arrow.effect.water_breathing": "Arrow of Water Breathing", + "item.minecraft.tipped_arrow.effect.healing": "Arrow of Healing", + "item.minecraft.tipped_arrow.effect.harming": "Arrow of Harming", + "item.minecraft.tipped_arrow.effect.poison": "Arrow of Poison", + "item.minecraft.tipped_arrow.effect.regeneration": "Arrow of Regeneration", + "item.minecraft.tipped_arrow.effect.strength": "Arrow of Strength", + "item.minecraft.tipped_arrow.effect.weakness": "Arrow of Weakness", + "item.minecraft.tipped_arrow.effect.levitation": "Arrow of Levitation", + "item.minecraft.tipped_arrow.effect.luck": "Arrow of Luck", + "item.minecraft.tipped_arrow.effect.turtle_master": "Arrow of the Turtle Master", + "item.minecraft.tipped_arrow.effect.slow_falling": "Arrow of Slow Falling", + "potion.whenDrank": "When Applied:", + "potion.withAmplifier": "%s %s", + "potion.withDuration": "%s (%s)", + "item.minecraft.potion.effect.empty": "Uncraftable Potion", + "item.minecraft.potion.effect.water": "Water Bottle", + "item.minecraft.potion.effect.mundane": "Mundane Potion", + "item.minecraft.potion.effect.thick": "Thick Potion", + "item.minecraft.potion.effect.awkward": "Awkward Potion", + "item.minecraft.potion.effect.night_vision": "Potion of Night Vision", + "item.minecraft.potion.effect.invisibility": "Potion of Invisibility", + "item.minecraft.potion.effect.leaping": "Potion of Leaping", + "item.minecraft.potion.effect.fire_resistance": "Potion of Fire Resistance", + "item.minecraft.potion.effect.swiftness": "Potion of Swiftness", + "item.minecraft.potion.effect.slowness": "Potion of Slowness", + "item.minecraft.potion.effect.water_breathing": "Potion of Water Breathing", + "item.minecraft.potion.effect.healing": "Potion of Healing", + "item.minecraft.potion.effect.harming": "Potion of Harming", + "item.minecraft.potion.effect.poison": "Potion of Poison", + "item.minecraft.potion.effect.regeneration": "Potion of Regeneration", + "item.minecraft.potion.effect.strength": "Potion of Strength", + "item.minecraft.potion.effect.weakness": "Potion of Weakness", + "item.minecraft.potion.effect.levitation": "Potion of Levitation", + "item.minecraft.potion.effect.luck": "Potion of Luck", + "item.minecraft.potion.effect.turtle_master": "Potion of the Turtle Master", + "item.minecraft.potion.effect.slow_falling": "Potion of Slow Falling", + "item.minecraft.splash_potion.effect.empty": "Splash Uncraftable Potion", + "item.minecraft.splash_potion.effect.water": "Splash Water Bottle", + "item.minecraft.splash_potion.effect.mundane": "Mundane Splash Potion", + "item.minecraft.splash_potion.effect.thick": "Thick Splash Potion", + "item.minecraft.splash_potion.effect.awkward": "Awkward Splash Potion", + "item.minecraft.splash_potion.effect.night_vision": "Splash Potion of Night Vision", + "item.minecraft.splash_potion.effect.invisibility": "Splash Potion of Invisibility", + "item.minecraft.splash_potion.effect.leaping": "Splash Potion of Leaping", + "item.minecraft.splash_potion.effect.fire_resistance": "Splash Potion of Fire Resistance", + "item.minecraft.splash_potion.effect.swiftness": "Splash Potion of Swiftness", + "item.minecraft.splash_potion.effect.slowness": "Splash Potion of Slowness", + "item.minecraft.splash_potion.effect.water_breathing": "Splash Potion of Water Breathing", + "item.minecraft.splash_potion.effect.healing": "Splash Potion of Healing", + "item.minecraft.splash_potion.effect.harming": "Splash Potion of Harming", + "item.minecraft.splash_potion.effect.poison": "Splash Potion of Poison", + "item.minecraft.splash_potion.effect.regeneration": "Splash Potion of Regeneration", + "item.minecraft.splash_potion.effect.strength": "Splash Potion of Strength", + "item.minecraft.splash_potion.effect.weakness": "Splash Potion of Weakness", + "item.minecraft.splash_potion.effect.levitation": "Splash Potion of Levitation", + "item.minecraft.splash_potion.effect.luck": "Splash Potion of Luck", + "item.minecraft.splash_potion.effect.turtle_master": "Splash Potion of the Turtle Master", + "item.minecraft.splash_potion.effect.slow_falling": "Splash Potion of Slow Falling", + "item.minecraft.lingering_potion.effect.empty": "Lingering Uncraftable Potion", + "item.minecraft.lingering_potion.effect.water": "Lingering Water Bottle", + "item.minecraft.lingering_potion.effect.mundane": "Mundane Lingering Potion", + "item.minecraft.lingering_potion.effect.thick": "Thick Lingering Potion", + "item.minecraft.lingering_potion.effect.awkward": "Awkward Lingering Potion", + "item.minecraft.lingering_potion.effect.night_vision": "Lingering Potion of Night Vision", + "item.minecraft.lingering_potion.effect.invisibility": "Lingering Potion of Invisibility", + "item.minecraft.lingering_potion.effect.leaping": "Lingering Potion of Leaping", + "item.minecraft.lingering_potion.effect.fire_resistance": "Lingering Potion of Fire Resistance", + "item.minecraft.lingering_potion.effect.swiftness": "Lingering Potion of Swiftness", + "item.minecraft.lingering_potion.effect.slowness": "Lingering Potion of Slowness", + "item.minecraft.lingering_potion.effect.water_breathing": "Lingering Potion of Water Breathing", + "item.minecraft.lingering_potion.effect.healing": "Lingering Potion of Healing", + "item.minecraft.lingering_potion.effect.harming": "Lingering Potion of Harming", + "item.minecraft.lingering_potion.effect.poison": "Lingering Potion of Poison", + "item.minecraft.lingering_potion.effect.regeneration": "Lingering Potion of Regeneration", + "item.minecraft.lingering_potion.effect.strength": "Lingering Potion of Strength", + "item.minecraft.lingering_potion.effect.weakness": "Lingering Potion of Weakness", + "item.minecraft.lingering_potion.effect.levitation": "Lingering Potion of Levitation", + "item.minecraft.lingering_potion.effect.luck": "Lingering Potion of Luck", + "item.minecraft.lingering_potion.effect.turtle_master": "Lingering Potion of the Turtle Master", + "item.minecraft.lingering_potion.effect.slow_falling": "Lingering Potion of Slow Falling", + "potion.potency.0": "", + "potion.potency.1": "II", + "potion.potency.2": "III", + "potion.potency.3": "IV", + "potion.potency.4": "V", + "potion.potency.5": "VI", + "enchantment.minecraft.sharpness": "Sharpness", + "enchantment.minecraft.smite": "Smite", + "enchantment.minecraft.bane_of_arthropods": "Bane of Arthropods", + "enchantment.minecraft.knockback": "Knockback", + "enchantment.minecraft.fire_aspect": "Fire Aspect", + "enchantment.minecraft.sweeping": "Sweeping Edge", + "enchantment.minecraft.protection": "Protection", + "enchantment.minecraft.fire_protection": "Fire Protection", + "enchantment.minecraft.feather_falling": "Feather Falling", + "enchantment.minecraft.blast_protection": "Blast Protection", + "enchantment.minecraft.projectile_protection": "Projectile Protection", + "enchantment.minecraft.respiration": "Respiration", + "enchantment.minecraft.aqua_affinity": "Aqua Affinity", + "enchantment.minecraft.depth_strider": "Depth Strider", + "enchantment.minecraft.frost_walker": "Frost Walker", + "enchantment.minecraft.soul_speed": "Soul Speed", + "enchantment.minecraft.swift_sneak": "Swift Sneak", + "enchantment.minecraft.efficiency": "Efficiency", + "enchantment.minecraft.silk_touch": "Silk Touch", + "enchantment.minecraft.unbreaking": "Unbreaking", + "enchantment.minecraft.looting": "Looting", + "enchantment.minecraft.fortune": "Fortune", + "enchantment.minecraft.luck_of_the_sea": "Luck of the Sea", + "enchantment.minecraft.lure": "Lure", + "enchantment.minecraft.power": "Power", + "enchantment.minecraft.flame": "Flame", + "enchantment.minecraft.punch": "Punch", + "enchantment.minecraft.infinity": "Infinity", + "enchantment.minecraft.thorns": "Thorns", + "enchantment.minecraft.mending": "Mending", + "enchantment.minecraft.binding_curse": "Curse of Binding", + "enchantment.minecraft.vanishing_curse": "Curse of Vanishing", + "enchantment.minecraft.loyalty": "Loyalty", + "enchantment.minecraft.impaling": "Impaling", + "enchantment.minecraft.riptide": "Riptide", + "enchantment.minecraft.channeling": "Channeling", + "enchantment.minecraft.multishot": "Multishot", + "enchantment.minecraft.quick_charge": "Quick Charge", + "enchantment.minecraft.piercing": "Piercing", + "enchantment.level.1": "I", + "enchantment.level.2": "II", + "enchantment.level.3": "III", + "enchantment.level.4": "IV", + "enchantment.level.5": "V", + "enchantment.level.6": "VI", + "enchantment.level.7": "VII", + "enchantment.level.8": "VIII", + "enchantment.level.9": "IX", + "enchantment.level.10": "X", + "gui.minutes": "%s minute(s)", + "gui.hours": "%s hour(s)", + "gui.days": "%s day(s)", + "gui.advancements": "Advancements", + "gui.stats": "Statistics", + "gui.entity_tooltip.type": "Type: %s", + "advancements.empty": "There doesn't seem to be anything here...", + "advancements.sad_label": ":(", + "advancements.toast.task": "Advancement Made!", + "advancements.toast.challenge": "Challenge Complete!", + "advancements.toast.goal": "Goal Reached!", + "stats.tooltip.type.statistic": "Statistic", + "stat.generalButton": "General", + "stat.itemsButton": "Items", + "stat.mobsButton": "Mobs", + "stat_type.minecraft.mined": "Times Mined", + "stat_type.minecraft.crafted": "Times Crafted", + "stat_type.minecraft.used": "Times Used", + "stat_type.minecraft.broken": "Times Broken", + "stat_type.minecraft.picked_up": "Picked Up", + "stat_type.minecraft.dropped": "Dropped", + "stat_type.minecraft.killed": "You killed %s %s", + "stat_type.minecraft.killed.none": "You have never killed %s", + "stat_type.minecraft.killed_by": "%s killed you %s time(s)", + "stat_type.minecraft.killed_by.none": "You have never been killed by %s", + "stat.minecraft.animals_bred": "Animals Bred", + "stat.minecraft.aviate_one_cm": "Distance by Elytra", + "stat.minecraft.clean_armor": "Armor Pieces Cleaned", + "stat.minecraft.clean_banner": "Banners Cleaned", + "stat.minecraft.clean_shulker_box": "Shulker Boxes Cleaned", + "stat.minecraft.climb_one_cm": "Distance Climbed", + "stat.minecraft.bell_ring": "Bells Rung", + "stat.minecraft.target_hit": "Targets Hit", + "stat.minecraft.boat_one_cm": "Distance by Boat", + "stat.minecraft.crouch_one_cm": "Distance Crouched", + "stat.minecraft.damage_dealt": "Damage Dealt", + "stat.minecraft.damage_dealt_absorbed": "Damage Dealt (Absorbed)", + "stat.minecraft.damage_dealt_resisted": "Damage Dealt (Resisted)", + "stat.minecraft.damage_taken": "Damage Taken", + "stat.minecraft.damage_blocked_by_shield": "Damage Blocked by Shield", + "stat.minecraft.damage_absorbed": "Damage Absorbed", + "stat.minecraft.damage_resisted": "Damage Resisted", + "stat.minecraft.deaths": "Number of Deaths", + "stat.minecraft.walk_under_water_one_cm": "Distance Walked under Water", + "stat.minecraft.drop": "Items Dropped", + "stat.minecraft.eat_cake_slice": "Cake Slices Eaten", + "stat.minecraft.enchant_item": "Items Enchanted", + "stat.minecraft.fall_one_cm": "Distance Fallen", + "stat.minecraft.fill_cauldron": "Cauldrons Filled", + "stat.minecraft.fish_caught": "Fish Caught", + "stat.minecraft.fly_one_cm": "Distance Flown", + "stat.minecraft.horse_one_cm": "Distance by Horse", + "stat.minecraft.inspect_dispenser": "Dispensers Searched", + "stat.minecraft.inspect_dropper": "Droppers Searched", + "stat.minecraft.inspect_hopper": "Hoppers Searched", + "stat.minecraft.interact_with_anvil": "Interactions with Anvil", + "stat.minecraft.interact_with_beacon": "Interactions with Beacon", + "stat.minecraft.interact_with_brewingstand": "Interactions with Brewing Stand", + "stat.minecraft.interact_with_campfire": "Interactions with Campfire", + "stat.minecraft.interact_with_cartography_table": "Interactions with Cartography Table", + "stat.minecraft.interact_with_crafting_table": "Interactions with Crafting Table", + "stat.minecraft.interact_with_furnace": "Interactions with Furnace", + "stat.minecraft.interact_with_grindstone": "Interactions with Grindstone", + "stat.minecraft.interact_with_lectern": "Interactions with Lectern", + "stat.minecraft.interact_with_loom": "Interactions with Loom", + "stat.minecraft.interact_with_blast_furnace": "Interactions with Blast Furnace", + "stat.minecraft.interact_with_smithing_table": "Interactions with Smithing Table", + "stat.minecraft.interact_with_smoker": "Interactions with Smoker", + "stat.minecraft.interact_with_stonecutter": "Interactions with Stonecutter", + "stat.minecraft.jump": "Jumps", + "stat.minecraft.junk_fished": "Junk Fished", + "stat.minecraft.leave_game": "Games Quit", + "stat.minecraft.minecart_one_cm": "Distance by Minecart", + "stat.minecraft.mob_kills": "Mob Kills", + "stat.minecraft.open_barrel": "Barrels Opened", + "stat.minecraft.open_chest": "Chests Opened", + "stat.minecraft.open_enderchest": "Ender Chests Opened", + "stat.minecraft.open_shulker_box": "Shulker Boxes Opened", + "stat.minecraft.pig_one_cm": "Distance by Pig", + "stat.minecraft.strider_one_cm": "Distance by Strider", + "stat.minecraft.player_kills": "Player Kills", + "stat.minecraft.play_noteblock": "Note Blocks Played", + "stat.minecraft.play_time": "Time Played", + "stat.minecraft.play_record": "Music Discs Played", + "stat.minecraft.pot_flower": "Plants Potted", + "stat.minecraft.raid_trigger": "Raids Triggered", + "stat.minecraft.raid_win": "Raids Won", + "stat.minecraft.ring_bell": "Bells Rung", + "stat.minecraft.sleep_in_bed": "Times Slept in a Bed", + "stat.minecraft.sneak_time": "Sneak Time", + "stat.minecraft.sprint_one_cm": "Distance Sprinted", + "stat.minecraft.walk_on_water_one_cm": "Distance Walked on Water", + "stat.minecraft.swim_one_cm": "Distance Swum", + "stat.minecraft.talked_to_villager": "Talked to Villagers", + "stat.minecraft.time_since_rest": "Time Since Last Rest", + "stat.minecraft.time_since_death": "Time Since Last Death", + "stat.minecraft.total_world_time": "Time with World Open", + "stat.minecraft.traded_with_villager": "Traded with Villagers", + "stat.minecraft.treasure_fished": "Treasure Fished", + "stat.minecraft.trigger_trapped_chest": "Trapped Chests Triggered", + "stat.minecraft.tune_noteblock": "Note Blocks Tuned", + "stat.minecraft.use_cauldron": "Water Taken from Cauldron", + "stat.minecraft.walk_one_cm": "Distance Walked", + "recipe.toast.title": "New Recipes Unlocked!", + "recipe.toast.description": "Check your recipe book", + "itemGroup.buildingBlocks": "Building Blocks", + "itemGroup.coloredBlocks": "Colored Blocks", + "itemGroup.natural": "Natural Blocks", + "itemGroup.functional": "Functional Blocks", + "itemGroup.redstone": "Redstone Blocks", + "itemGroup.op": "Operator Utilities", + "itemGroup.spawnEggs": "Spawn Eggs", + "itemGroup.search": "Search Items", + "itemGroup.consumables": "Consumables", + "itemGroup.foodAndDrink": "Food & Drinks", + "itemGroup.tools": "Tools & Utilities", + "itemGroup.combat": "Combat", + "itemGroup.crafting": "Crafting", + "itemGroup.ingredients": "Ingredients", + "itemGroup.inventory": "Survival Inventory", + "itemGroup.hotbar": "Saved Hotbars", + "inventory.binSlot": "Destroy Item", + "inventory.hotbarSaved": "Item hotbar saved (restore with %1$s+%2$s)", + "inventory.hotbarInfo": "Save hotbar with %1$s+%2$s", + "advMode.setCommand": "Set Console Command for Block", + "advMode.setCommand.success": "Command set: %s", + "advMode.command": "Console Command", + "advMode.nearestPlayer": "Use \"@p\" to target nearest player", + "advMode.randomPlayer": "Use \"@r\" to target random player", + "advMode.allPlayers": "Use \"@a\" to target all players", + "advMode.allEntities": "Use \"@e\" to target all entities", + "advMode.self": "Use \"@s\" to target the executing entity", + "advMode.previousOutput": "Previous Output", + "advMode.mode": "Mode", + "advMode.mode.sequence": "Chain", + "advMode.mode.auto": "Repeat", + "advMode.mode.redstone": "Impulse", + "advMode.type": "Type", + "advMode.mode.conditional": "Conditional", + "advMode.mode.unconditional": "Unconditional", + "advMode.triggering": "Triggering", + "advMode.mode.redstoneTriggered": "Needs Redstone", + "advMode.mode.autoexec.bat": "Always Active", + "advMode.notEnabled": "Command blocks are not enabled on this server", + "advMode.notAllowed": "Must be an opped player in creative mode", + "advMode.trackOutput": "Track output", + "mount.onboard": "Press %1$s to Dismount", + "build.tooHigh": "Height limit for building is %s", + "item.modifiers.mainhand": "When in Main Hand:", + "item.modifiers.offhand": "When in Off Hand:", + "item.modifiers.feet": "When on Feet:", + "item.modifiers.legs": "When on Legs:", + "item.modifiers.chest": "When on Body:", + "item.modifiers.head": "When on Head:", + "attribute.modifier.plus.0": "+%s %s", + "attribute.modifier.plus.1": "+%s%% %s", + "attribute.modifier.plus.2": "+%s%% %s", + "attribute.modifier.take.0": "-%s %s", + "attribute.modifier.take.1": "-%s%% %s", + "attribute.modifier.take.2": "-%s%% %s", + "attribute.modifier.equals.0": "%s %s", + "attribute.modifier.equals.1": "%s%% %s", + "attribute.modifier.equals.2": "%s%% %s", + "attribute.name.horse.jump_strength": "Horse Jump Strength", + "attribute.name.zombie.spawn_reinforcements": "Zombie Reinforcements", + "attribute.name.generic.max_health": "Max Health", + "attribute.name.generic.follow_range": "Mob Follow Range", + "attribute.name.generic.knockback_resistance": "Knockback Resistance", + "attribute.name.generic.movement_speed": "Speed", + "attribute.name.generic.flying_speed": "Flying Speed", + "attribute.name.generic.attack_damage": "Attack Damage", + "attribute.name.generic.attack_knockback": "Attack Knockback", + "attribute.name.generic.attack_speed": "Attack Speed", + "attribute.name.generic.luck": "Luck", + "attribute.name.generic.armor": "Armor", + "attribute.name.generic.armor_toughness": "Armor Toughness", + "screenshot.success": "Saved screenshot as %s", + "screenshot.failure": "Couldn't save screenshot: %s", + "block.minecraft.black_banner": "Black Banner", + "block.minecraft.red_banner": "Red Banner", + "block.minecraft.green_banner": "Green Banner", + "block.minecraft.brown_banner": "Brown Banner", + "block.minecraft.blue_banner": "Blue Banner", + "block.minecraft.purple_banner": "Purple Banner", + "block.minecraft.cyan_banner": "Cyan Banner", + "block.minecraft.light_gray_banner": "Light Gray Banner", + "block.minecraft.gray_banner": "Gray Banner", + "block.minecraft.pink_banner": "Pink Banner", + "block.minecraft.lime_banner": "Lime Banner", + "block.minecraft.yellow_banner": "Yellow Banner", + "block.minecraft.light_blue_banner": "Light Blue Banner", + "block.minecraft.magenta_banner": "Magenta Banner", + "block.minecraft.orange_banner": "Orange Banner", + "block.minecraft.white_banner": "White Banner", + "item.minecraft.shield": "Shield", + "item.minecraft.shield.black": "Black Shield", + "item.minecraft.shield.red": "Red Shield", + "item.minecraft.shield.green": "Green Shield", + "item.minecraft.shield.brown": "Brown Shield", + "item.minecraft.shield.blue": "Blue Shield", + "item.minecraft.shield.purple": "Purple Shield", + "item.minecraft.shield.cyan": "Cyan Shield", + "item.minecraft.shield.light_gray": "Light Gray Shield", + "item.minecraft.shield.gray": "Gray Shield", + "item.minecraft.shield.pink": "Pink Shield", + "item.minecraft.shield.lime": "Lime Shield", + "item.minecraft.shield.yellow": "Yellow Shield", + "item.minecraft.shield.light_blue": "Light Blue Shield", + "item.minecraft.shield.magenta": "Magenta Shield", + "item.minecraft.shield.orange": "Orange Shield", + "item.minecraft.shield.white": "White Shield", + "block.minecraft.banner.base.black": "Fully Black Field", + "block.minecraft.banner.base.red": "Fully Red Field", + "block.minecraft.banner.base.green": "Fully Green Field", + "block.minecraft.banner.base.brown": "Fully Brown Field", + "block.minecraft.banner.base.blue": "Fully Blue Field", + "block.minecraft.banner.base.purple": "Fully Purple Field", + "block.minecraft.banner.base.cyan": "Fully Cyan Field", + "block.minecraft.banner.base.light_gray": "Fully Light Gray Field", + "block.minecraft.banner.base.gray": "Fully Gray Field", + "block.minecraft.banner.base.pink": "Fully Pink Field", + "block.minecraft.banner.base.lime": "Fully Lime Field", + "block.minecraft.banner.base.yellow": "Fully Yellow Field", + "block.minecraft.banner.base.light_blue": "Fully Light Blue Field", + "block.minecraft.banner.base.magenta": "Fully Magenta Field", + "block.minecraft.banner.base.orange": "Fully Orange Field", + "block.minecraft.banner.base.white": "Fully White Field", + "block.minecraft.banner.square_bottom_left.black": "Black Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.red": "Red Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.green": "Green Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.brown": "Brown Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.blue": "Blue Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.purple": "Purple Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.cyan": "Cyan Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.light_gray": "Light Gray Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.gray": "Gray Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.pink": "Pink Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.lime": "Lime Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.yellow": "Yellow Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.light_blue": "Light Blue Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.magenta": "Magenta Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.orange": "Orange Base Dexter Canton", + "block.minecraft.banner.square_bottom_left.white": "White Base Dexter Canton", + "block.minecraft.banner.square_bottom_right.black": "Black Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.red": "Red Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.green": "Green Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.brown": "Brown Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.blue": "Blue Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.purple": "Purple Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.cyan": "Cyan Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.light_gray": "Light Gray Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.gray": "Gray Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.pink": "Pink Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.lime": "Lime Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.yellow": "Yellow Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.light_blue": "Light Blue Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.magenta": "Magenta Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.orange": "Orange Base Sinister Canton", + "block.minecraft.banner.square_bottom_right.white": "White Base Sinister Canton", + "block.minecraft.banner.square_top_left.black": "Black Chief Dexter Canton", + "block.minecraft.banner.square_top_left.red": "Red Chief Dexter Canton", + "block.minecraft.banner.square_top_left.green": "Green Chief Dexter Canton", + "block.minecraft.banner.square_top_left.brown": "Brown Chief Dexter Canton", + "block.minecraft.banner.square_top_left.blue": "Blue Chief Dexter Canton", + "block.minecraft.banner.square_top_left.purple": "Purple Chief Dexter Canton", + "block.minecraft.banner.square_top_left.cyan": "Cyan Chief Dexter Canton", + "block.minecraft.banner.square_top_left.light_gray": "Light Gray Chief Dexter Canton", + "block.minecraft.banner.square_top_left.gray": "Gray Chief Dexter Canton", + "block.minecraft.banner.square_top_left.pink": "Pink Chief Dexter Canton", + "block.minecraft.banner.square_top_left.lime": "Lime Chief Dexter Canton", + "block.minecraft.banner.square_top_left.yellow": "Yellow Chief Dexter Canton", + "block.minecraft.banner.square_top_left.light_blue": "Light Blue Chief Dexter Canton", + "block.minecraft.banner.square_top_left.magenta": "Magenta Chief Dexter Canton", + "block.minecraft.banner.square_top_left.orange": "Orange Chief Dexter Canton", + "block.minecraft.banner.square_top_left.white": "White Chief Dexter Canton", + "block.minecraft.banner.square_top_right.black": "Black Chief Sinister Canton", + "block.minecraft.banner.square_top_right.red": "Red Chief Sinister Canton", + "block.minecraft.banner.square_top_right.green": "Green Chief Sinister Canton", + "block.minecraft.banner.square_top_right.brown": "Brown Chief Sinister Canton", + "block.minecraft.banner.square_top_right.blue": "Blue Chief Sinister Canton", + "block.minecraft.banner.square_top_right.purple": "Purple Chief Sinister Canton", + "block.minecraft.banner.square_top_right.cyan": "Cyan Chief Sinister Canton", + "block.minecraft.banner.square_top_right.light_gray": "Light Gray Chief Sinister Canton", + "block.minecraft.banner.square_top_right.gray": "Gray Chief Sinister Canton", + "block.minecraft.banner.square_top_right.pink": "Pink Chief Sinister Canton", + "block.minecraft.banner.square_top_right.lime": "Lime Chief Sinister Canton", + "block.minecraft.banner.square_top_right.yellow": "Yellow Chief Sinister Canton", + "block.minecraft.banner.square_top_right.light_blue": "Light Blue Chief Sinister Canton", + "block.minecraft.banner.square_top_right.magenta": "Magenta Chief Sinister Canton", + "block.minecraft.banner.square_top_right.orange": "Orange Chief Sinister Canton", + "block.minecraft.banner.square_top_right.white": "White Chief Sinister Canton", + "block.minecraft.banner.stripe_bottom.black": "Black Base", + "block.minecraft.banner.stripe_bottom.red": "Red Base", + "block.minecraft.banner.stripe_bottom.green": "Green Base", + "block.minecraft.banner.stripe_bottom.brown": "Brown Base", + "block.minecraft.banner.stripe_bottom.blue": "Blue Base", + "block.minecraft.banner.stripe_bottom.purple": "Purple Base", + "block.minecraft.banner.stripe_bottom.cyan": "Cyan Base", + "block.minecraft.banner.stripe_bottom.light_gray": "Light Gray Base", + "block.minecraft.banner.stripe_bottom.gray": "Gray Base", + "block.minecraft.banner.stripe_bottom.pink": "Pink Base", + "block.minecraft.banner.stripe_bottom.lime": "Lime Base", + "block.minecraft.banner.stripe_bottom.yellow": "Yellow Base", + "block.minecraft.banner.stripe_bottom.light_blue": "Light Blue Base", + "block.minecraft.banner.stripe_bottom.magenta": "Magenta Base", + "block.minecraft.banner.stripe_bottom.orange": "Orange Base", + "block.minecraft.banner.stripe_bottom.white": "White Base", + "block.minecraft.banner.stripe_top.black": "Black Chief", + "block.minecraft.banner.stripe_top.red": "Red Chief", + "block.minecraft.banner.stripe_top.green": "Green Chief", + "block.minecraft.banner.stripe_top.brown": "Brown Chief", + "block.minecraft.banner.stripe_top.blue": "Blue Chief", + "block.minecraft.banner.stripe_top.purple": "Purple Chief", + "block.minecraft.banner.stripe_top.cyan": "Cyan Chief", + "block.minecraft.banner.stripe_top.light_gray": "Light Gray Chief", + "block.minecraft.banner.stripe_top.gray": "Gray Chief", + "block.minecraft.banner.stripe_top.pink": "Pink Chief", + "block.minecraft.banner.stripe_top.lime": "Lime Chief", + "block.minecraft.banner.stripe_top.yellow": "Yellow Chief", + "block.minecraft.banner.stripe_top.light_blue": "Light Blue Chief", + "block.minecraft.banner.stripe_top.magenta": "Magenta Chief", + "block.minecraft.banner.stripe_top.orange": "Orange Chief", + "block.minecraft.banner.stripe_top.white": "White Chief", + "block.minecraft.banner.stripe_left.black": "Black Pale Dexter", + "block.minecraft.banner.stripe_left.red": "Red Pale Dexter", + "block.minecraft.banner.stripe_left.green": "Green Pale Dexter", + "block.minecraft.banner.stripe_left.brown": "Brown Pale Dexter", + "block.minecraft.banner.stripe_left.blue": "Blue Pale Dexter", + "block.minecraft.banner.stripe_left.purple": "Purple Pale Dexter", + "block.minecraft.banner.stripe_left.cyan": "Cyan Pale Dexter", + "block.minecraft.banner.stripe_left.light_gray": "Light Gray Pale Dexter", + "block.minecraft.banner.stripe_left.gray": "Gray Pale Dexter", + "block.minecraft.banner.stripe_left.pink": "Pink Pale Dexter", + "block.minecraft.banner.stripe_left.lime": "Lime Pale Dexter", + "block.minecraft.banner.stripe_left.yellow": "Yellow Pale Dexter", + "block.minecraft.banner.stripe_left.light_blue": "Light Blue Pale Dexter", + "block.minecraft.banner.stripe_left.magenta": "Magenta Pale Dexter", + "block.minecraft.banner.stripe_left.orange": "Orange Pale Dexter", + "block.minecraft.banner.stripe_left.white": "White Pale Dexter", + "block.minecraft.banner.stripe_right.black": "Black Pale Sinister", + "block.minecraft.banner.stripe_right.red": "Red Pale Sinister", + "block.minecraft.banner.stripe_right.green": "Green Pale Sinister", + "block.minecraft.banner.stripe_right.brown": "Brown Pale Sinister", + "block.minecraft.banner.stripe_right.blue": "Blue Pale Sinister", + "block.minecraft.banner.stripe_right.purple": "Purple Pale Sinister", + "block.minecraft.banner.stripe_right.cyan": "Cyan Pale Sinister", + "block.minecraft.banner.stripe_right.light_gray": "Light Gray Pale Sinister", + "block.minecraft.banner.stripe_right.gray": "Gray Pale Sinister", + "block.minecraft.banner.stripe_right.pink": "Pink Pale Sinister", + "block.minecraft.banner.stripe_right.lime": "Lime Pale Sinister", + "block.minecraft.banner.stripe_right.yellow": "Yellow Pale Sinister", + "block.minecraft.banner.stripe_right.light_blue": "Light Blue Pale Sinister", + "block.minecraft.banner.stripe_right.magenta": "Magenta Pale Sinister", + "block.minecraft.banner.stripe_right.orange": "Orange Pale Sinister", + "block.minecraft.banner.stripe_right.white": "White Pale Sinister", + "block.minecraft.banner.stripe_center.black": "Black Pale", + "block.minecraft.banner.stripe_center.red": "Red Pale", + "block.minecraft.banner.stripe_center.green": "Green Pale", + "block.minecraft.banner.stripe_center.brown": "Brown Pale", + "block.minecraft.banner.stripe_center.blue": "Blue Pale", + "block.minecraft.banner.stripe_center.purple": "Purple Pale", + "block.minecraft.banner.stripe_center.cyan": "Cyan Pale", + "block.minecraft.banner.stripe_center.light_gray": "Light Gray Pale", + "block.minecraft.banner.stripe_center.gray": "Gray Pale", + "block.minecraft.banner.stripe_center.pink": "Pink Pale", + "block.minecraft.banner.stripe_center.lime": "Lime Pale", + "block.minecraft.banner.stripe_center.yellow": "Yellow Pale", + "block.minecraft.banner.stripe_center.light_blue": "Light Blue Pale", + "block.minecraft.banner.stripe_center.magenta": "Magenta Pale", + "block.minecraft.banner.stripe_center.orange": "Orange Pale", + "block.minecraft.banner.stripe_center.white": "White Pale", + "block.minecraft.banner.stripe_middle.black": "Black Fess", + "block.minecraft.banner.stripe_middle.red": "Red Fess", + "block.minecraft.banner.stripe_middle.green": "Green Fess", + "block.minecraft.banner.stripe_middle.brown": "Brown Fess", + "block.minecraft.banner.stripe_middle.blue": "Blue Fess", + "block.minecraft.banner.stripe_middle.purple": "Purple Fess", + "block.minecraft.banner.stripe_middle.cyan": "Cyan Fess", + "block.minecraft.banner.stripe_middle.light_gray": "Light Gray Fess", + "block.minecraft.banner.stripe_middle.gray": "Gray Fess", + "block.minecraft.banner.stripe_middle.pink": "Pink Fess", + "block.minecraft.banner.stripe_middle.lime": "Lime Fess", + "block.minecraft.banner.stripe_middle.yellow": "Yellow Fess", + "block.minecraft.banner.stripe_middle.light_blue": "Light Blue Fess", + "block.minecraft.banner.stripe_middle.magenta": "Magenta Fess", + "block.minecraft.banner.stripe_middle.orange": "Orange Fess", + "block.minecraft.banner.stripe_middle.white": "White Fess", + "block.minecraft.banner.stripe_downright.black": "Black Bend", + "block.minecraft.banner.stripe_downright.red": "Red Bend", + "block.minecraft.banner.stripe_downright.green": "Green Bend", + "block.minecraft.banner.stripe_downright.brown": "Brown Bend", + "block.minecraft.banner.stripe_downright.blue": "Blue Bend", + "block.minecraft.banner.stripe_downright.purple": "Purple Bend", + "block.minecraft.banner.stripe_downright.cyan": "Cyan Bend", + "block.minecraft.banner.stripe_downright.light_gray": "Light Gray Bend", + "block.minecraft.banner.stripe_downright.gray": "Gray Bend", + "block.minecraft.banner.stripe_downright.pink": "Pink Bend", + "block.minecraft.banner.stripe_downright.lime": "Lime Bend", + "block.minecraft.banner.stripe_downright.yellow": "Yellow Bend", + "block.minecraft.banner.stripe_downright.light_blue": "Light Blue Bend", + "block.minecraft.banner.stripe_downright.magenta": "Magenta Bend", + "block.minecraft.banner.stripe_downright.orange": "Orange Bend", + "block.minecraft.banner.stripe_downright.white": "White Bend", + "block.minecraft.banner.stripe_downleft.black": "Black Bend Sinister", + "block.minecraft.banner.stripe_downleft.red": "Red Bend Sinister", + "block.minecraft.banner.stripe_downleft.green": "Green Bend Sinister", + "block.minecraft.banner.stripe_downleft.brown": "Brown Bend Sinister", + "block.minecraft.banner.stripe_downleft.blue": "Blue Bend Sinister", + "block.minecraft.banner.stripe_downleft.purple": "Purple Bend Sinister", + "block.minecraft.banner.stripe_downleft.cyan": "Cyan Bend Sinister", + "block.minecraft.banner.stripe_downleft.light_gray": "Light Gray Bend Sinister", + "block.minecraft.banner.stripe_downleft.gray": "Gray Bend Sinister", + "block.minecraft.banner.stripe_downleft.pink": "Pink Bend Sinister", + "block.minecraft.banner.stripe_downleft.lime": "Lime Bend Sinister", + "block.minecraft.banner.stripe_downleft.yellow": "Yellow Bend Sinister", + "block.minecraft.banner.stripe_downleft.light_blue": "Light Blue Bend Sinister", + "block.minecraft.banner.stripe_downleft.magenta": "Magenta Bend Sinister", + "block.minecraft.banner.stripe_downleft.orange": "Orange Bend Sinister", + "block.minecraft.banner.stripe_downleft.white": "White Bend Sinister", + "block.minecraft.banner.small_stripes.black": "Black Paly", + "block.minecraft.banner.small_stripes.red": "Red Paly", + "block.minecraft.banner.small_stripes.green": "Green Paly", + "block.minecraft.banner.small_stripes.brown": "Brown Paly", + "block.minecraft.banner.small_stripes.blue": "Blue Paly", + "block.minecraft.banner.small_stripes.purple": "Purple Paly", + "block.minecraft.banner.small_stripes.cyan": "Cyan Paly", + "block.minecraft.banner.small_stripes.light_gray": "Light Gray Paly", + "block.minecraft.banner.small_stripes.gray": "Gray Paly", + "block.minecraft.banner.small_stripes.pink": "Pink Paly", + "block.minecraft.banner.small_stripes.lime": "Lime Paly", + "block.minecraft.banner.small_stripes.yellow": "Yellow Paly", + "block.minecraft.banner.small_stripes.light_blue": "Light Blue Paly", + "block.minecraft.banner.small_stripes.magenta": "Magenta Paly", + "block.minecraft.banner.small_stripes.orange": "Orange Paly", + "block.minecraft.banner.small_stripes.white": "White Paly", + "block.minecraft.banner.cross.black": "Black Saltire", + "block.minecraft.banner.cross.red": "Red Saltire", + "block.minecraft.banner.cross.green": "Green Saltire", + "block.minecraft.banner.cross.brown": "Brown Saltire", + "block.minecraft.banner.cross.blue": "Blue Saltire", + "block.minecraft.banner.cross.purple": "Purple Saltire", + "block.minecraft.banner.cross.cyan": "Cyan Saltire", + "block.minecraft.banner.cross.light_gray": "Light Gray Saltire", + "block.minecraft.banner.cross.gray": "Gray Saltire", + "block.minecraft.banner.cross.pink": "Pink Saltire", + "block.minecraft.banner.cross.lime": "Lime Saltire", + "block.minecraft.banner.cross.yellow": "Yellow Saltire", + "block.minecraft.banner.cross.light_blue": "Light Blue Saltire", + "block.minecraft.banner.cross.magenta": "Magenta Saltire", + "block.minecraft.banner.cross.orange": "Orange Saltire", + "block.minecraft.banner.cross.white": "White Saltire", + "block.minecraft.banner.triangle_bottom.black": "Black Chevron", + "block.minecraft.banner.triangle_bottom.red": "Red Chevron", + "block.minecraft.banner.triangle_bottom.green": "Green Chevron", + "block.minecraft.banner.triangle_bottom.brown": "Brown Chevron", + "block.minecraft.banner.triangle_bottom.blue": "Blue Chevron", + "block.minecraft.banner.triangle_bottom.purple": "Purple Chevron", + "block.minecraft.banner.triangle_bottom.cyan": "Cyan Chevron", + "block.minecraft.banner.triangle_bottom.light_gray": "Light Gray Chevron", + "block.minecraft.banner.triangle_bottom.gray": "Gray Chevron", + "block.minecraft.banner.triangle_bottom.pink": "Pink Chevron", + "block.minecraft.banner.triangle_bottom.lime": "Lime Chevron", + "block.minecraft.banner.triangle_bottom.yellow": "Yellow Chevron", + "block.minecraft.banner.triangle_bottom.light_blue": "Light Blue Chevron", + "block.minecraft.banner.triangle_bottom.magenta": "Magenta Chevron", + "block.minecraft.banner.triangle_bottom.orange": "Orange Chevron", + "block.minecraft.banner.triangle_bottom.white": "White Chevron", + "block.minecraft.banner.triangle_top.black": "Black Inverted Chevron", + "block.minecraft.banner.triangle_top.red": "Red Inverted Chevron", + "block.minecraft.banner.triangle_top.green": "Green Inverted Chevron", + "block.minecraft.banner.triangle_top.brown": "Brown Inverted Chevron", + "block.minecraft.banner.triangle_top.blue": "Blue Inverted Chevron", + "block.minecraft.banner.triangle_top.purple": "Purple Inverted Chevron", + "block.minecraft.banner.triangle_top.cyan": "Cyan Inverted Chevron", + "block.minecraft.banner.triangle_top.light_gray": "Light Gray Inverted Chevron", + "block.minecraft.banner.triangle_top.gray": "Gray Inverted Chevron", + "block.minecraft.banner.triangle_top.pink": "Pink Inverted Chevron", + "block.minecraft.banner.triangle_top.lime": "Lime Inverted Chevron", + "block.minecraft.banner.triangle_top.yellow": "Yellow Inverted Chevron", + "block.minecraft.banner.triangle_top.light_blue": "Light Blue Inverted Chevron", + "block.minecraft.banner.triangle_top.magenta": "Magenta Inverted Chevron", + "block.minecraft.banner.triangle_top.orange": "Orange Inverted Chevron", + "block.minecraft.banner.triangle_top.white": "White Inverted Chevron", + "block.minecraft.banner.triangles_bottom.black": "Black Base Indented", + "block.minecraft.banner.triangles_bottom.red": "Red Base Indented", + "block.minecraft.banner.triangles_bottom.green": "Green Base Indented", + "block.minecraft.banner.triangles_bottom.brown": "Brown Base Indented", + "block.minecraft.banner.triangles_bottom.blue": "Blue Base Indented", + "block.minecraft.banner.triangles_bottom.purple": "Purple Base Indented", + "block.minecraft.banner.triangles_bottom.cyan": "Cyan Base Indented", + "block.minecraft.banner.triangles_bottom.light_gray": "Light Gray Base Indented", + "block.minecraft.banner.triangles_bottom.gray": "Gray Base Indented", + "block.minecraft.banner.triangles_bottom.pink": "Pink Base Indented", + "block.minecraft.banner.triangles_bottom.lime": "Lime Base Indented", + "block.minecraft.banner.triangles_bottom.yellow": "Yellow Base Indented", + "block.minecraft.banner.triangles_bottom.light_blue": "Light Blue Base Indented", + "block.minecraft.banner.triangles_bottom.magenta": "Magenta Base Indented", + "block.minecraft.banner.triangles_bottom.orange": "Orange Base Indented", + "block.minecraft.banner.triangles_bottom.white": "White Base Indented", + "block.minecraft.banner.triangles_top.black": "Black Chief Indented", + "block.minecraft.banner.triangles_top.red": "Red Chief Indented", + "block.minecraft.banner.triangles_top.green": "Green Chief Indented", + "block.minecraft.banner.triangles_top.brown": "Brown Chief Indented", + "block.minecraft.banner.triangles_top.blue": "Blue Chief Indented", + "block.minecraft.banner.triangles_top.purple": "Purple Chief Indented", + "block.minecraft.banner.triangles_top.cyan": "Cyan Chief Indented", + "block.minecraft.banner.triangles_top.light_gray": "Light Gray Chief Indented", + "block.minecraft.banner.triangles_top.gray": "Gray Chief Indented", + "block.minecraft.banner.triangles_top.pink": "Pink Chief Indented", + "block.minecraft.banner.triangles_top.lime": "Lime Chief Indented", + "block.minecraft.banner.triangles_top.yellow": "Yellow Chief Indented", + "block.minecraft.banner.triangles_top.light_blue": "Light Blue Chief Indented", + "block.minecraft.banner.triangles_top.magenta": "Magenta Chief Indented", + "block.minecraft.banner.triangles_top.orange": "Orange Chief Indented", + "block.minecraft.banner.triangles_top.white": "White Chief Indented", + "block.minecraft.banner.diagonal_left.black": "Black Per Bend Sinister", + "block.minecraft.banner.diagonal_left.red": "Red Per Bend Sinister", + "block.minecraft.banner.diagonal_left.green": "Green Per Bend Sinister", + "block.minecraft.banner.diagonal_left.brown": "Brown Per Bend Sinister", + "block.minecraft.banner.diagonal_left.blue": "Blue Per Bend Sinister", + "block.minecraft.banner.diagonal_left.purple": "Purple Per Bend Sinister", + "block.minecraft.banner.diagonal_left.cyan": "Cyan Per Bend Sinister", + "block.minecraft.banner.diagonal_left.light_gray": "Light Gray Per Bend Sinister", + "block.minecraft.banner.diagonal_left.gray": "Gray Per Bend Sinister", + "block.minecraft.banner.diagonal_left.pink": "Pink Per Bend Sinister", + "block.minecraft.banner.diagonal_left.lime": "Lime Per Bend Sinister", + "block.minecraft.banner.diagonal_left.yellow": "Yellow Per Bend Sinister", + "block.minecraft.banner.diagonal_left.light_blue": "Light Blue Per Bend Sinister", + "block.minecraft.banner.diagonal_left.magenta": "Magenta Per Bend Sinister", + "block.minecraft.banner.diagonal_left.orange": "Orange Per Bend Sinister", + "block.minecraft.banner.diagonal_left.white": "White Per Bend Sinister", + "block.minecraft.banner.diagonal_right.black": "Black Per Bend", + "block.minecraft.banner.diagonal_right.red": "Red Per Bend", + "block.minecraft.banner.diagonal_right.green": "Green Per Bend", + "block.minecraft.banner.diagonal_right.brown": "Brown Per Bend", + "block.minecraft.banner.diagonal_right.blue": "Blue Per Bend", + "block.minecraft.banner.diagonal_right.purple": "Purple Per Bend", + "block.minecraft.banner.diagonal_right.cyan": "Cyan Per Bend", + "block.minecraft.banner.diagonal_right.light_gray": "Light Gray Per Bend", + "block.minecraft.banner.diagonal_right.gray": "Gray Per Bend", + "block.minecraft.banner.diagonal_right.pink": "Pink Per Bend", + "block.minecraft.banner.diagonal_right.lime": "Lime Per Bend", + "block.minecraft.banner.diagonal_right.yellow": "Yellow Per Bend", + "block.minecraft.banner.diagonal_right.light_blue": "Light Blue Per Bend", + "block.minecraft.banner.diagonal_right.magenta": "Magenta Per Bend", + "block.minecraft.banner.diagonal_right.orange": "Orange Per Bend", + "block.minecraft.banner.diagonal_right.white": "White Per Bend", + "block.minecraft.banner.diagonal_up_left.black": "Black Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.red": "Red Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.green": "Green Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.brown": "Brown Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.blue": "Blue Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.purple": "Purple Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.cyan": "Cyan Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.light_gray": "Light Gray Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.gray": "Gray Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.pink": "Pink Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.lime": "Lime Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.yellow": "Yellow Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.light_blue": "Light Blue Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.magenta": "Magenta Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.orange": "Orange Per Bend Inverted", + "block.minecraft.banner.diagonal_up_left.white": "White Per Bend Inverted", + "block.minecraft.banner.diagonal_up_right.black": "Black Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.red": "Red Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.green": "Green Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.brown": "Brown Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.blue": "Blue Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.purple": "Purple Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.cyan": "Cyan Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.light_gray": "Light Gray Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.gray": "Gray Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.pink": "Pink Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.lime": "Lime Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.yellow": "Yellow Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.light_blue": "Light Blue Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.magenta": "Magenta Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.orange": "Orange Per Bend Sinister Inverted", + "block.minecraft.banner.diagonal_up_right.white": "White Per Bend Sinister Inverted", + "block.minecraft.banner.circle.black": "Black Roundel", + "block.minecraft.banner.circle.red": "Red Roundel", + "block.minecraft.banner.circle.green": "Green Roundel", + "block.minecraft.banner.circle.brown": "Brown Roundel", + "block.minecraft.banner.circle.blue": "Blue Roundel", + "block.minecraft.banner.circle.purple": "Purple Roundel", + "block.minecraft.banner.circle.cyan": "Cyan Roundel", + "block.minecraft.banner.circle.light_gray": "Light Gray Roundel", + "block.minecraft.banner.circle.gray": "Gray Roundel", + "block.minecraft.banner.circle.pink": "Pink Roundel", + "block.minecraft.banner.circle.lime": "Lime Roundel", + "block.minecraft.banner.circle.yellow": "Yellow Roundel", + "block.minecraft.banner.circle.light_blue": "Light Blue Roundel", + "block.minecraft.banner.circle.magenta": "Magenta Roundel", + "block.minecraft.banner.circle.orange": "Orange Roundel", + "block.minecraft.banner.circle.white": "White Roundel", + "block.minecraft.banner.rhombus.black": "Black Lozenge", + "block.minecraft.banner.rhombus.red": "Red Lozenge", + "block.minecraft.banner.rhombus.green": "Green Lozenge", + "block.minecraft.banner.rhombus.brown": "Brown Lozenge", + "block.minecraft.banner.rhombus.blue": "Blue Lozenge", + "block.minecraft.banner.rhombus.purple": "Purple Lozenge", + "block.minecraft.banner.rhombus.cyan": "Cyan Lozenge", + "block.minecraft.banner.rhombus.light_gray": "Light Gray Lozenge", + "block.minecraft.banner.rhombus.gray": "Gray Lozenge", + "block.minecraft.banner.rhombus.pink": "Pink Lozenge", + "block.minecraft.banner.rhombus.lime": "Lime Lozenge", + "block.minecraft.banner.rhombus.yellow": "Yellow Lozenge", + "block.minecraft.banner.rhombus.light_blue": "Light Blue Lozenge", + "block.minecraft.banner.rhombus.magenta": "Magenta Lozenge", + "block.minecraft.banner.rhombus.orange": "Orange Lozenge", + "block.minecraft.banner.rhombus.white": "White Lozenge", + "block.minecraft.banner.half_vertical.black": "Black Per Pale", + "block.minecraft.banner.half_vertical.red": "Red Per Pale", + "block.minecraft.banner.half_vertical.green": "Green Per Pale", + "block.minecraft.banner.half_vertical.brown": "Brown Per Pale", + "block.minecraft.banner.half_vertical.blue": "Blue Per Pale", + "block.minecraft.banner.half_vertical.purple": "Purple Per Pale", + "block.minecraft.banner.half_vertical.cyan": "Cyan Per Pale", + "block.minecraft.banner.half_vertical.light_gray": "Light Gray Per Pale", + "block.minecraft.banner.half_vertical.gray": "Gray Per Pale", + "block.minecraft.banner.half_vertical.pink": "Pink Per Pale", + "block.minecraft.banner.half_vertical.lime": "Lime Per Pale", + "block.minecraft.banner.half_vertical.yellow": "Yellow Per Pale", + "block.minecraft.banner.half_vertical.light_blue": "Light Blue Per Pale", + "block.minecraft.banner.half_vertical.magenta": "Magenta Per Pale", + "block.minecraft.banner.half_vertical.orange": "Orange Per Pale", + "block.minecraft.banner.half_vertical.white": "White Per Pale", + "block.minecraft.banner.half_horizontal.black": "Black Per Fess", + "block.minecraft.banner.half_horizontal.red": "Red Per Fess", + "block.minecraft.banner.half_horizontal.green": "Green Per Fess", + "block.minecraft.banner.half_horizontal.brown": "Brown Per Fess", + "block.minecraft.banner.half_horizontal.blue": "Blue Per Fess", + "block.minecraft.banner.half_horizontal.purple": "Purple Per Fess", + "block.minecraft.banner.half_horizontal.cyan": "Cyan Per Fess", + "block.minecraft.banner.half_horizontal.light_gray": "Light Gray Per Fess", + "block.minecraft.banner.half_horizontal.gray": "Gray Per Fess", + "block.minecraft.banner.half_horizontal.pink": "Pink Per Fess", + "block.minecraft.banner.half_horizontal.lime": "Lime Per Fess", + "block.minecraft.banner.half_horizontal.yellow": "Yellow Per Fess", + "block.minecraft.banner.half_horizontal.light_blue": "Light Blue Per Fess", + "block.minecraft.banner.half_horizontal.magenta": "Magenta Per Fess", + "block.minecraft.banner.half_horizontal.orange": "Orange Per Fess", + "block.minecraft.banner.half_horizontal.white": "White Per Fess", + "block.minecraft.banner.half_vertical_right.black": "Black Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.red": "Red Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.green": "Green Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.brown": "Brown Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.blue": "Blue Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.purple": "Purple Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.cyan": "Cyan Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.light_gray": "Light Gray Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.gray": "Gray Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.pink": "Pink Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.lime": "Lime Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.yellow": "Yellow Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.light_blue": "Light Blue Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.magenta": "Magenta Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.orange": "Orange Per Pale Inverted", + "block.minecraft.banner.half_vertical_right.white": "White Per Pale Inverted", + "block.minecraft.banner.half_horizontal_bottom.black": "Black Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.red": "Red Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.green": "Green Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.brown": "Brown Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.blue": "Blue Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.purple": "Purple Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.cyan": "Cyan Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.light_gray": "Light Gray Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.gray": "Gray Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.pink": "Pink Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.lime": "Lime Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.yellow": "Yellow Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.light_blue": "Light Blue Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.magenta": "Magenta Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.orange": "Orange Per Fess Inverted", + "block.minecraft.banner.half_horizontal_bottom.white": "White Per Fess Inverted", + "block.minecraft.banner.creeper.black": "Black Creeper Charge", + "block.minecraft.banner.creeper.red": "Red Creeper Charge", + "block.minecraft.banner.creeper.green": "Green Creeper Charge", + "block.minecraft.banner.creeper.brown": "Brown Creeper Charge", + "block.minecraft.banner.creeper.blue": "Blue Creeper Charge", + "block.minecraft.banner.creeper.purple": "Purple Creeper Charge", + "block.minecraft.banner.creeper.cyan": "Cyan Creeper Charge", + "block.minecraft.banner.creeper.light_gray": "Light Gray Creeper Charge", + "block.minecraft.banner.creeper.gray": "Gray Creeper Charge", + "block.minecraft.banner.creeper.pink": "Pink Creeper Charge", + "block.minecraft.banner.creeper.lime": "Lime Creeper Charge", + "block.minecraft.banner.creeper.yellow": "Yellow Creeper Charge", + "block.minecraft.banner.creeper.light_blue": "Light Blue Creeper Charge", + "block.minecraft.banner.creeper.magenta": "Magenta Creeper Charge", + "block.minecraft.banner.creeper.orange": "Orange Creeper Charge", + "block.minecraft.banner.creeper.white": "White Creeper Charge", + "block.minecraft.banner.bricks.black": "Black Field Masoned", + "block.minecraft.banner.bricks.red": "Red Field Masoned", + "block.minecraft.banner.bricks.green": "Green Field Masoned", + "block.minecraft.banner.bricks.brown": "Brown Field Masoned", + "block.minecraft.banner.bricks.blue": "Blue Field Masoned", + "block.minecraft.banner.bricks.purple": "Purple Field Masoned", + "block.minecraft.banner.bricks.cyan": "Cyan Field Masoned", + "block.minecraft.banner.bricks.light_gray": "Light Gray Field Masoned", + "block.minecraft.banner.bricks.gray": "Gray Field Masoned", + "block.minecraft.banner.bricks.pink": "Pink Field Masoned", + "block.minecraft.banner.bricks.lime": "Lime Field Masoned", + "block.minecraft.banner.bricks.yellow": "Yellow Field Masoned", + "block.minecraft.banner.bricks.light_blue": "Light Blue Field Masoned", + "block.minecraft.banner.bricks.magenta": "Magenta Field Masoned", + "block.minecraft.banner.bricks.orange": "Orange Field Masoned", + "block.minecraft.banner.bricks.white": "White Field Masoned", + "block.minecraft.banner.gradient.black": "Black Gradient", + "block.minecraft.banner.gradient.red": "Red Gradient", + "block.minecraft.banner.gradient.green": "Green Gradient", + "block.minecraft.banner.gradient.brown": "Brown Gradient", + "block.minecraft.banner.gradient.blue": "Blue Gradient", + "block.minecraft.banner.gradient.purple": "Purple Gradient", + "block.minecraft.banner.gradient.cyan": "Cyan Gradient", + "block.minecraft.banner.gradient.light_gray": "Light Gray Gradient", + "block.minecraft.banner.gradient.gray": "Gray Gradient", + "block.minecraft.banner.gradient.pink": "Pink Gradient", + "block.minecraft.banner.gradient.lime": "Lime Gradient", + "block.minecraft.banner.gradient.yellow": "Yellow Gradient", + "block.minecraft.banner.gradient.light_blue": "Light Blue Gradient", + "block.minecraft.banner.gradient.magenta": "Magenta Gradient", + "block.minecraft.banner.gradient.orange": "Orange Gradient", + "block.minecraft.banner.gradient.white": "White Gradient", + "block.minecraft.banner.gradient_up.black": "Black Base Gradient", + "block.minecraft.banner.gradient_up.red": "Red Base Gradient", + "block.minecraft.banner.gradient_up.green": "Green Base Gradient", + "block.minecraft.banner.gradient_up.brown": "Brown Base Gradient", + "block.minecraft.banner.gradient_up.blue": "Blue Base Gradient", + "block.minecraft.banner.gradient_up.purple": "Purple Base Gradient", + "block.minecraft.banner.gradient_up.cyan": "Cyan Base Gradient", + "block.minecraft.banner.gradient_up.light_gray": "Light Gray Base Gradient", + "block.minecraft.banner.gradient_up.gray": "Gray Base Gradient", + "block.minecraft.banner.gradient_up.pink": "Pink Base Gradient", + "block.minecraft.banner.gradient_up.lime": "Lime Base Gradient", + "block.minecraft.banner.gradient_up.yellow": "Yellow Base Gradient", + "block.minecraft.banner.gradient_up.light_blue": "Light Blue Base Gradient", + "block.minecraft.banner.gradient_up.magenta": "Magenta Base Gradient", + "block.minecraft.banner.gradient_up.orange": "Orange Base Gradient", + "block.minecraft.banner.gradient_up.white": "White Base Gradient", + "block.minecraft.banner.skull.black": "Black Skull Charge", + "block.minecraft.banner.skull.red": "Red Skull Charge", + "block.minecraft.banner.skull.green": "Green Skull Charge", + "block.minecraft.banner.skull.brown": "Brown Skull Charge", + "block.minecraft.banner.skull.blue": "Blue Skull Charge", + "block.minecraft.banner.skull.purple": "Purple Skull Charge", + "block.minecraft.banner.skull.cyan": "Cyan Skull Charge", + "block.minecraft.banner.skull.light_gray": "Light Gray Skull Charge", + "block.minecraft.banner.skull.gray": "Gray Skull Charge", + "block.minecraft.banner.skull.pink": "Pink Skull Charge", + "block.minecraft.banner.skull.lime": "Lime Skull Charge", + "block.minecraft.banner.skull.yellow": "Yellow Skull Charge", + "block.minecraft.banner.skull.light_blue": "Light Blue Skull Charge", + "block.minecraft.banner.skull.magenta": "Magenta Skull Charge", + "block.minecraft.banner.skull.orange": "Orange Skull Charge", + "block.minecraft.banner.skull.white": "White Skull Charge", + "block.minecraft.banner.flower.black": "Black Flower Charge", + "block.minecraft.banner.flower.red": "Red Flower Charge", + "block.minecraft.banner.flower.green": "Green Flower Charge", + "block.minecraft.banner.flower.brown": "Brown Flower Charge", + "block.minecraft.banner.flower.blue": "Blue Flower Charge", + "block.minecraft.banner.flower.purple": "Purple Flower Charge", + "block.minecraft.banner.flower.cyan": "Cyan Flower Charge", + "block.minecraft.banner.flower.light_gray": "Light Gray Flower Charge", + "block.minecraft.banner.flower.gray": "Gray Flower Charge", + "block.minecraft.banner.flower.pink": "Pink Flower Charge", + "block.minecraft.banner.flower.lime": "Lime Flower Charge", + "block.minecraft.banner.flower.yellow": "Yellow Flower Charge", + "block.minecraft.banner.flower.light_blue": "Light Blue Flower Charge", + "block.minecraft.banner.flower.magenta": "Magenta Flower Charge", + "block.minecraft.banner.flower.orange": "Orange Flower Charge", + "block.minecraft.banner.flower.white": "White Flower Charge", + "block.minecraft.banner.border.black": "Black Bordure", + "block.minecraft.banner.border.red": "Red Bordure", + "block.minecraft.banner.border.green": "Green Bordure", + "block.minecraft.banner.border.brown": "Brown Bordure", + "block.minecraft.banner.border.blue": "Blue Bordure", + "block.minecraft.banner.border.purple": "Purple Bordure", + "block.minecraft.banner.border.cyan": "Cyan Bordure", + "block.minecraft.banner.border.light_gray": "Light Gray Bordure", + "block.minecraft.banner.border.gray": "Gray Bordure", + "block.minecraft.banner.border.pink": "Pink Bordure", + "block.minecraft.banner.border.lime": "Lime Bordure", + "block.minecraft.banner.border.yellow": "Yellow Bordure", + "block.minecraft.banner.border.light_blue": "Light Blue Bordure", + "block.minecraft.banner.border.magenta": "Magenta Bordure", + "block.minecraft.banner.border.orange": "Orange Bordure", + "block.minecraft.banner.border.white": "White Bordure", + "block.minecraft.banner.curly_border.black": "Black Bordure Indented", + "block.minecraft.banner.curly_border.red": "Red Bordure Indented", + "block.minecraft.banner.curly_border.green": "Green Bordure Indented", + "block.minecraft.banner.curly_border.brown": "Brown Bordure Indented", + "block.minecraft.banner.curly_border.blue": "Blue Bordure Indented", + "block.minecraft.banner.curly_border.purple": "Purple Bordure Indented", + "block.minecraft.banner.curly_border.cyan": "Cyan Bordure Indented", + "block.minecraft.banner.curly_border.light_gray": "Light Gray Bordure Indented", + "block.minecraft.banner.curly_border.gray": "Gray Bordure Indented", + "block.minecraft.banner.curly_border.pink": "Pink Bordure Indented", + "block.minecraft.banner.curly_border.lime": "Lime Bordure Indented", + "block.minecraft.banner.curly_border.yellow": "Yellow Bordure Indented", + "block.minecraft.banner.curly_border.light_blue": "Light Blue Bordure Indented", + "block.minecraft.banner.curly_border.magenta": "Magenta Bordure Indented", + "block.minecraft.banner.curly_border.orange": "Orange Bordure Indented", + "block.minecraft.banner.curly_border.white": "White Bordure Indented", + "block.minecraft.banner.mojang.black": "Black Thing", + "block.minecraft.banner.mojang.red": "Red Thing", + "block.minecraft.banner.mojang.green": "Green Thing", + "block.minecraft.banner.mojang.brown": "Brown Thing", + "block.minecraft.banner.mojang.blue": "Blue Thing", + "block.minecraft.banner.mojang.purple": "Purple Thing", + "block.minecraft.banner.mojang.cyan": "Cyan Thing", + "block.minecraft.banner.mojang.light_gray": "Light Gray Thing", + "block.minecraft.banner.mojang.gray": "Gray Thing", + "block.minecraft.banner.mojang.pink": "Pink Thing", + "block.minecraft.banner.mojang.lime": "Lime Thing", + "block.minecraft.banner.mojang.yellow": "Yellow Thing", + "block.minecraft.banner.mojang.light_blue": "Light Blue Thing", + "block.minecraft.banner.mojang.magenta": "Magenta Thing", + "block.minecraft.banner.mojang.orange": "Orange Thing", + "block.minecraft.banner.mojang.white": "White Thing", + "block.minecraft.banner.straight_cross.black": "Black Cross", + "block.minecraft.banner.straight_cross.red": "Red Cross", + "block.minecraft.banner.straight_cross.green": "Green Cross", + "block.minecraft.banner.straight_cross.brown": "Brown Cross", + "block.minecraft.banner.straight_cross.blue": "Blue Cross", + "block.minecraft.banner.straight_cross.purple": "Purple Cross", + "block.minecraft.banner.straight_cross.cyan": "Cyan Cross", + "block.minecraft.banner.straight_cross.light_gray": "Light Gray Cross", + "block.minecraft.banner.straight_cross.gray": "Gray Cross", + "block.minecraft.banner.straight_cross.pink": "Pink Cross", + "block.minecraft.banner.straight_cross.lime": "Lime Cross", + "block.minecraft.banner.straight_cross.yellow": "Yellow Cross", + "block.minecraft.banner.straight_cross.light_blue": "Light Blue Cross", + "block.minecraft.banner.straight_cross.magenta": "Magenta Cross", + "block.minecraft.banner.straight_cross.orange": "Orange Cross", + "block.minecraft.banner.straight_cross.white": "White Cross", + "block.minecraft.banner.globe.black": "Black Globe", + "block.minecraft.banner.globe.red": "Red Globe", + "block.minecraft.banner.globe.green": "Green Globe", + "block.minecraft.banner.globe.brown": "Brown Globe", + "block.minecraft.banner.globe.blue": "Blue Globe", + "block.minecraft.banner.globe.purple": "Purple Globe", + "block.minecraft.banner.globe.cyan": "Cyan Globe", + "block.minecraft.banner.globe.light_gray": "Light Gray Globe", + "block.minecraft.banner.globe.gray": "Gray Globe", + "block.minecraft.banner.globe.pink": "Pink Globe", + "block.minecraft.banner.globe.lime": "Lime Globe", + "block.minecraft.banner.globe.yellow": "Yellow Globe", + "block.minecraft.banner.globe.light_blue": "Light Blue Globe", + "block.minecraft.banner.globe.magenta": "Magenta Globe", + "block.minecraft.banner.globe.orange": "Orange Globe", + "block.minecraft.banner.globe.white": "White Globe", + "block.minecraft.banner.piglin.black": "Black Snout", + "block.minecraft.banner.piglin.red": "Red Snout", + "block.minecraft.banner.piglin.green": "Green Snout", + "block.minecraft.banner.piglin.brown": "Brown Snout", + "block.minecraft.banner.piglin.blue": "Blue Snout", + "block.minecraft.banner.piglin.purple": "Purple Snout", + "block.minecraft.banner.piglin.cyan": "Cyan Snout", + "block.minecraft.banner.piglin.light_gray": "Light Gray Snout", + "block.minecraft.banner.piglin.gray": "Gray Snout", + "block.minecraft.banner.piglin.pink": "Pink Snout", + "block.minecraft.banner.piglin.lime": "Lime Snout", + "block.minecraft.banner.piglin.yellow": "Yellow Snout", + "block.minecraft.banner.piglin.light_blue": "Light Blue Snout", + "block.minecraft.banner.piglin.magenta": "Magenta Snout", + "block.minecraft.banner.piglin.orange": "Orange Snout", + "block.minecraft.banner.piglin.white": "White Snout", + "subtitles.ambient.cave": "Eerie noise", + "subtitles.block.amethyst_block.chime": "Amethyst chimes", + "subtitles.block.anvil.destroy": "Anvil destroyed", + "subtitles.block.anvil.land": "Anvil landed", + "subtitles.block.anvil.use": "Anvil used", + "subtitles.block.barrel.close": "Barrel closes", + "subtitles.block.barrel.open": "Barrel opens", + "subtitles.block.beacon.activate": "Beacon activates", + "subtitles.block.beacon.ambient": "Beacon hums", + "subtitles.block.beacon.deactivate": "Beacon deactivates", + "subtitles.block.beacon.power_select": "Beacon power selected", + "subtitles.block.beehive.drip": "Honey drips", + "subtitles.block.beehive.enter": "Bee enters hive", + "subtitles.block.beehive.exit": "Bee leaves hive", + "subtitles.block.beehive.shear": "Shears scrape", + "subtitles.block.beehive.work": "Bees work", + "subtitles.block.bell.resonate": "Bell resonates", + "subtitles.block.bell.use": "Bell rings", + "subtitles.block.big_dripleaf.tilt_down": "Dripleaf tilts down", + "subtitles.block.big_dripleaf.tilt_up": "Dripleaf tilts up", + "subtitles.block.blastfurnace.fire_crackle": "Blast Furnace crackles", + "subtitles.block.brewing_stand.brew": "Brewing Stand bubbles", + "subtitles.block.bubble_column.bubble_pop": "Bubbles pop", + "subtitles.block.bubble_column.upwards_ambient": "Bubbles flow", + "subtitles.block.bubble_column.upwards_inside": "Bubbles woosh", + "subtitles.block.bubble_column.whirlpool_ambient": "Bubbles whirl", + "subtitles.block.bubble_column.whirlpool_inside": "Bubbles zoom", + "subtitles.block.button.click": "Button clicks", + "subtitles.block.campfire.crackle": "Campfire crackles", + "subtitles.block.candle.crackle": "Candle crackles", + "subtitles.block.cake.add_candle": "Cake squishes", + "subtitles.block.chest.close": "Chest closes", + "subtitles.block.chest.locked": "Chest locked", + "subtitles.block.chest.open": "Chest opens", + "subtitles.chiseled_bookshelf.insert": "Book placed", + "subtitles.chiseled_bookshelf.insert_enchanted": "Enchanted book placed", + "subtitles.chiseled_bookshelf.take": "Book taken", + "subtitles.chiseled_bookshelf.take_enchanted": "Enchanted book taken", + "subtitles.block.chorus_flower.death": "Chorus Flower withers", + "subtitles.block.chorus_flower.grow": "Chorus Flower grows", + "subtitles.block.comparator.click": "Comparator clicks", + "subtitles.block.composter.empty": "Composter emptied", + "subtitles.block.composter.fill": "Composter filled", + "subtitles.block.composter.ready": "Composter composts", + "subtitles.block.conduit.activate": "Conduit activates", + "subtitles.block.conduit.ambient": "Conduit pulses", + "subtitles.block.conduit.attack.target": "Conduit attacks", + "subtitles.block.conduit.deactivate": "Conduit deactivates", + "subtitles.block.dispenser.dispense": "Dispensed item", + "subtitles.block.dispenser.fail": "Dispenser failed", + "subtitles.block.door.toggle": "Door creaks", + "subtitles.block.enchantment_table.use": "Enchanting Table used", + "subtitles.block.end_portal.spawn": "End Portal opens", + "subtitles.block.end_portal_frame.fill": "Eye of Ender attaches", + "subtitles.block.fence_gate.toggle": "Fence Gate creaks", + "subtitles.block.fire.ambient": "Fire crackles", + "subtitles.block.fire.extinguish": "Fire extinguished", + "subtitles.block.frogspawn.hatch": "Tadpole hatches", + "subtitles.block.furnace.fire_crackle": "Furnace crackles", + "subtitles.block.generic.break": "Block broken", + "subtitles.block.generic.footsteps": "Footsteps", + "subtitles.block.generic.hit": "Block breaking", + "subtitles.block.generic.place": "Block placed", + "subtitles.block.grindstone.use": "Grindstone used", + "subtitles.block.growing_plant.crop": "Plant cropped", + "subtitles.block.honey_block.slide": "Sliding down a honey block", + "subtitles.item.honeycomb.wax_on": "Wax on", + "subtitles.block.iron_trapdoor.close": "Trapdoor closes", + "subtitles.block.iron_trapdoor.open": "Trapdoor opens", + "subtitles.block.lava.ambient": "Lava pops", + "subtitles.block.lava.extinguish": "Lava hisses", + "subtitles.block.lever.click": "Lever clicks", + "subtitles.block.note_block.note": "Note Block plays", + "subtitles.block.piston.move": "Piston moves", + "subtitles.block.pointed_dripstone.land": "Stalactite crashes down", + "subtitles.block.pointed_dripstone.drip_lava": "Lava drips", + "subtitles.block.pointed_dripstone.drip_water": "Water drips", + "subtitles.block.pointed_dripstone.drip_lava_into_cauldron": "Lava drips into Cauldron", + "subtitles.block.pointed_dripstone.drip_water_into_cauldron": "Water drips into Cauldron", + "subtitles.block.portal.ambient": "Portal whooshes", + "subtitles.block.portal.travel": "Portal noise fades", + "subtitles.block.portal.trigger": "Portal noise intensifies", + "subtitles.block.pressure_plate.click": "Pressure Plate clicks", + "subtitles.block.pumpkin.carve": "Shears carve", + "subtitles.block.redstone_torch.burnout": "Torch fizzes", + "subtitles.block.respawn_anchor.ambient": "Portal whooshes", + "subtitles.block.respawn_anchor.charge": "Respawn Anchor is charged", + "subtitles.block.respawn_anchor.deplete": "Respawn Anchor depletes", + "subtitles.block.respawn_anchor.set_spawn": "Respawn Anchor sets spawn", + "subtitles.block.sculk.charge": "Sculk bubbles", + "subtitles.block.sculk.spread": "Sculk spreads", + "subtitles.block.sculk_catalyst.bloom": "Sculk Catalyst blooms", + "subtitles.block.sculk_sensor.clicking": "Sculk Sensor starts clicking", + "subtitles.block.sculk_sensor.clicking_stop": "Sculk Sensor stops clicking", + "subtitles.block.sculk_shrieker.shriek": "Sculk Shrieker shrieks", + "subtitles.block.shulker_box.close": "Shulker closes", + "subtitles.block.shulker_box.open": "Shulker opens", + "subtitles.block.smithing_table.use": "Smithing Table used", + "subtitles.block.smoker.smoke": "Smoker smokes", + "subtitles.block.sweet_berry_bush.pick_berries": "Berries pop", + "subtitles.block.trapdoor.toggle": "Trapdoor creaks", + "subtitles.block.tripwire.attach": "Tripwire attaches", + "subtitles.block.tripwire.click": "Tripwire clicks", + "subtitles.block.tripwire.detach": "Tripwire detaches", + "subtitles.block.water.ambient": "Water flows", + "subtitles.enchant.thorns.hit": "Thorns prick", + "subtitles.entity.allay.death": "Allay dies", + "subtitles.entity.allay.hurt": "Allay hurts", + "subtitles.entity.allay.ambient_with_item": "Allay seeks", + "subtitles.entity.allay.ambient_without_item": "Allay yearns", + "subtitles.entity.allay.item_given": "Allay chortles", + "subtitles.entity.allay.item_taken": "Allay allays", + "subtitles.entity.allay.item_thrown": "Allay tosses", + "subtitles.entity.armor_stand.fall": "Something fell", + "subtitles.entity.arrow.hit": "Arrow hits", + "subtitles.entity.arrow.hit_player": "Player hit", + "subtitles.entity.arrow.shoot": "Arrow fired", + "subtitles.entity.axolotl.attack": "Axolotl attacks", + "subtitles.entity.axolotl.death": "Axolotl dies", + "subtitles.entity.axolotl.hurt": "Axolotl hurts", + "subtitles.entity.axolotl.idle_air": "Axolotl chirps", + "subtitles.entity.axolotl.idle_water": "Axolotl chirps", + "subtitles.entity.axolotl.splash": "Axolotl splashes", + "subtitles.entity.axolotl.swim": "Axolotl swims", + "subtitles.entity.bat.ambient": "Bat screeches", + "subtitles.entity.bat.death": "Bat dies", + "subtitles.entity.bat.hurt": "Bat hurts", + "subtitles.entity.bat.takeoff": "Bat takes off", + "subtitles.entity.bee.ambient": "Bee buzzes", + "subtitles.entity.bee.death": "Bee dies", + "subtitles.entity.bee.hurt": "Bee hurts", + "subtitles.entity.bee.loop": "Bee buzzes", + "subtitles.entity.bee.loop_aggressive": "Bee buzzes angrily", + "subtitles.entity.bee.pollinate": "Bee buzzes happily", + "subtitles.entity.bee.sting": "Bee stings", + "subtitles.entity.blaze.ambient": "Blaze breathes", + "subtitles.entity.blaze.burn": "Blaze crackles", + "subtitles.entity.blaze.death": "Blaze dies", + "subtitles.entity.blaze.hurt": "Blaze hurts", + "subtitles.entity.blaze.shoot": "Blaze shoots", + "subtitles.entity.boat.paddle_land": "Rowing", + "subtitles.entity.boat.paddle_water": "Rowing", + "subtitles.entity.camel.ambient": "Camel grunts", + "subtitles.entity.camel.dash": "Camel yeets", + "subtitles.entity.camel.dash_ready": "Camel recovers", + "subtitles.entity.camel.death": "Camel dies", + "subtitles.entity.camel.eat": "Camel eats", + "subtitles.entity.camel.hurt": "Camel hurts", + "subtitles.entity.camel.saddle": "Saddle equips", + "subtitles.entity.camel.sit": "Camel sits down", + "subtitles.entity.camel.stand": "Camel stands up", + "subtitles.entity.camel.step": "Camel steps", + "subtitles.entity.camel.step_sand": "Camel sands", + "subtitles.entity.cat.ambient": "Cat meows", + "subtitles.entity.cat.beg_for_food": "Cat begs", + "subtitles.entity.cat.death": "Cat dies", + "subtitles.entity.cat.eat": "Cat eats", + "subtitles.entity.cat.hiss": "Cat hisses", + "subtitles.entity.cat.hurt": "Cat hurts", + "subtitles.entity.cat.purr": "Cat purrs", + "subtitles.entity.chicken.ambient": "Chicken clucks", + "subtitles.entity.chicken.death": "Chicken dies", + "subtitles.entity.chicken.egg": "Chicken plops", + "subtitles.entity.chicken.hurt": "Chicken hurts", + "subtitles.entity.cod.death": "Cod dies", + "subtitles.entity.cod.flop": "Cod flops", + "subtitles.entity.cod.hurt": "Cod hurts", + "subtitles.entity.cow.ambient": "Cow moos", + "subtitles.entity.cow.death": "Cow dies", + "subtitles.entity.cow.hurt": "Cow hurts", + "subtitles.entity.cow.milk": "Cow gets milked", + "subtitles.entity.creeper.death": "Creeper dies", + "subtitles.entity.creeper.hurt": "Creeper hurts", + "subtitles.entity.creeper.primed": "Creeper hisses", + "subtitles.entity.dolphin.ambient": "Dolphin chirps", + "subtitles.entity.dolphin.ambient_water": "Dolphin whistles", + "subtitles.entity.dolphin.attack": "Dolphin attacks", + "subtitles.entity.dolphin.death": "Dolphin dies", + "subtitles.entity.dolphin.eat": "Dolphin eats", + "subtitles.entity.dolphin.hurt": "Dolphin hurts", + "subtitles.entity.dolphin.jump": "Dolphin jumps", + "subtitles.entity.dolphin.play": "Dolphin plays", + "subtitles.entity.dolphin.splash": "Dolphin splashes", + "subtitles.entity.dolphin.swim": "Dolphin swims", + "subtitles.entity.donkey.ambient": "Donkey hee-haws", + "subtitles.entity.donkey.angry": "Donkey neighs", + "subtitles.entity.donkey.chest": "Donkey Chest equips", + "subtitles.entity.donkey.death": "Donkey dies", + "subtitles.entity.donkey.eat": "Donkey eats", + "subtitles.entity.donkey.hurt": "Donkey hurts", + "subtitles.entity.drowned.ambient": "Drowned gurgles", + "subtitles.entity.drowned.ambient_water": "Drowned gurgles", + "subtitles.entity.drowned.death": "Drowned dies", + "subtitles.entity.drowned.hurt": "Drowned hurts", + "subtitles.entity.drowned.shoot": "Drowned throws Trident", + "subtitles.entity.drowned.step": "Drowned steps", + "subtitles.entity.drowned.swim": "Drowned swims", + "subtitles.entity.egg.throw": "Egg flies", + "subtitles.entity.elder_guardian.ambient": "Elder Guardian moans", + "subtitles.entity.elder_guardian.ambient_land": "Elder Guardian flaps", + "subtitles.entity.elder_guardian.curse": "Elder Guardian curses", + "subtitles.entity.elder_guardian.death": "Elder Guardian dies", + "subtitles.entity.elder_guardian.flop": "Elder Guardian flops", + "subtitles.entity.elder_guardian.hurt": "Elder Guardian hurts", + "subtitles.entity.ender_dragon.ambient": "Dragon roars", + "subtitles.entity.ender_dragon.death": "Dragon dies", + "subtitles.entity.ender_dragon.flap": "Dragon flaps", + "subtitles.entity.ender_dragon.growl": "Dragon growls", + "subtitles.entity.ender_dragon.hurt": "Dragon hurts", + "subtitles.entity.ender_dragon.shoot": "Dragon shoots", + "subtitles.entity.ender_eye.death": "Eye of Ender falls", + "subtitles.entity.ender_eye.launch": "Eye of Ender shoots", + "subtitles.entity.ender_pearl.throw": "Ender Pearl flies", + "subtitles.entity.enderman.ambient": "Enderman vwoops", + "subtitles.entity.enderman.death": "Enderman dies", + "subtitles.entity.enderman.hurt": "Enderman hurts", + "subtitles.entity.enderman.scream": "Enderman screams", + "subtitles.entity.enderman.stare": "Enderman cries out", + "subtitles.entity.enderman.teleport": "Enderman teleports", + "subtitles.entity.endermite.ambient": "Endermite scuttles", + "subtitles.entity.endermite.death": "Endermite dies", + "subtitles.entity.endermite.hurt": "Endermite hurts", + "subtitles.entity.evoker.ambient": "Evoker murmurs", + "subtitles.entity.evoker.cast_spell": "Evoker casts spell", + "subtitles.entity.evoker.celebrate": "Evoker cheers", + "subtitles.entity.evoker.death": "Evoker dies", + "subtitles.entity.evoker.hurt": "Evoker hurts", + "subtitles.entity.evoker.prepare_attack": "Evoker prepares attack", + "subtitles.entity.evoker.prepare_summon": "Evoker prepares summoning", + "subtitles.entity.evoker.prepare_wololo": "Evoker prepares charming", + "subtitles.entity.evoker_fangs.attack": "Fangs snap", + "subtitles.entity.experience_orb.pickup": "Experience gained", + "subtitles.entity.firework_rocket.blast": "Firework blasts", + "subtitles.entity.firework_rocket.launch": "Firework launches", + "subtitles.entity.firework_rocket.twinkle": "Firework twinkles", + "subtitles.entity.fishing_bobber.retrieve": "Bobber retrieved", + "subtitles.entity.fishing_bobber.splash": "Fishing Bobber splashes", + "subtitles.entity.fishing_bobber.throw": "Bobber thrown", + "subtitles.entity.fox.aggro": "Fox angers", + "subtitles.entity.fox.ambient": "Fox squeaks", + "subtitles.entity.fox.bite": "Fox bites", + "subtitles.entity.fox.death": "Fox dies", + "subtitles.entity.fox.eat": "Fox eats", + "subtitles.entity.fox.hurt": "Fox hurts", + "subtitles.entity.fox.screech": "Fox screeches", + "subtitles.entity.fox.sleep": "Fox snores", + "subtitles.entity.fox.sniff": "Fox sniffs", + "subtitles.entity.fox.spit": "Fox spits", + "subtitles.entity.fox.teleport": "Fox teleports", + "subtitles.entity.frog.ambient": "Frog croaks", + "subtitles.entity.frog.death": "Frog dies", + "subtitles.entity.frog.eat": "Frog eats", + "subtitles.entity.frog.hurt": "Frog hurts", + "subtitles.entity.frog.lay_spawn": "Frog lays spawn", + "subtitles.entity.frog.long_jump": "Frog jumps", + "subtitles.entity.generic.big_fall": "Something fell", + "subtitles.entity.generic.burn": "Burning", + "subtitles.entity.generic.death": "Dying", + "subtitles.entity.generic.drink": "Sipping", + "subtitles.entity.generic.eat": "Eating", + "subtitles.entity.generic.explode": "Explosion", + "subtitles.entity.generic.extinguish_fire": "Fire extinguishes", + "subtitles.entity.generic.hurt": "Something hurts", + "subtitles.entity.generic.small_fall": "Something trips", + "subtitles.entity.generic.splash": "Splashing", + "subtitles.entity.generic.swim": "Swimming", + "subtitles.entity.ghast.ambient": "Ghast cries", + "subtitles.entity.ghast.death": "Ghast dies", + "subtitles.entity.ghast.hurt": "Ghast hurts", + "subtitles.entity.ghast.shoot": "Ghast shoots", + "subtitles.entity.glow_item_frame.add_item": "Glow Item Frame fills", + "subtitles.entity.glow_item_frame.break": "Glow Item Frame breaks", + "subtitles.entity.glow_item_frame.place": "Glow Item Frame placed", + "subtitles.entity.glow_item_frame.remove_item": "Glow Item Frame empties", + "subtitles.entity.glow_item_frame.rotate_item": "Glow Item Frame clicks", + "subtitles.entity.glow_squid.ambient": "Glow Squid swims", + "subtitles.entity.glow_squid.death": "Glow Squid dies", + "subtitles.entity.glow_squid.hurt": "Glow Squid hurts", + "subtitles.entity.glow_squid.squirt": "Glow Squid shoots ink", + "subtitles.entity.goat.ambient": "Goat bleats", + "subtitles.entity.goat.screaming.ambient": "Goat bellows", + "subtitles.entity.goat.death": "Goat dies", + "subtitles.entity.goat.eat": "Goat eats", + "subtitles.entity.goat.horn_break": "Goat Horn breaks off", + "subtitles.entity.goat.hurt": "Goat hurts", + "subtitles.entity.goat.long_jump": "Goat leaps", + "subtitles.entity.goat.milk": "Goat gets milked", + "subtitles.entity.goat.prepare_ram": "Goat stomps", + "subtitles.entity.goat.ram_impact": "Goat rams", + "subtitles.entity.goat.step": "Goat steps", + "subtitles.entity.guardian.ambient": "Guardian moans", + "subtitles.entity.guardian.ambient_land": "Guardian flaps", + "subtitles.entity.guardian.attack": "Guardian shoots", + "subtitles.entity.guardian.death": "Guardian dies", + "subtitles.entity.guardian.flop": "Guardian flops", + "subtitles.entity.guardian.hurt": "Guardian hurts", + "subtitles.entity.hoglin.ambient": "Hoglin growls", + "subtitles.entity.hoglin.angry": "Hoglin growls angrily", + "subtitles.entity.hoglin.attack": "Hoglin attacks", + "subtitles.entity.hoglin.converted_to_zombified": "Hoglin converts to Zoglin", + "subtitles.entity.hoglin.death": "Hoglin dies", + "subtitles.entity.hoglin.hurt": "Hoglin hurts", + "subtitles.entity.hoglin.retreat": "Hoglin retreats", + "subtitles.entity.hoglin.step": "Hoglin steps", + "subtitles.entity.horse.ambient": "Horse neighs", + "subtitles.entity.horse.angry": "Horse neighs", + "subtitles.entity.horse.armor": "Horse armor equips", + "subtitles.entity.horse.breathe": "Horse breathes", + "subtitles.entity.horse.death": "Horse dies", + "subtitles.entity.horse.eat": "Horse eats", + "subtitles.entity.horse.gallop": "Horse gallops", + "subtitles.entity.horse.hurt": "Horse hurts", + "subtitles.entity.horse.jump": "Horse jumps", + "subtitles.entity.horse.saddle": "Saddle equips", + "subtitles.entity.husk.ambient": "Husk groans", + "subtitles.entity.husk.converted_to_zombie": "Husk converts to Zombie", + "subtitles.entity.husk.death": "Husk dies", + "subtitles.entity.husk.hurt": "Husk hurts", + "subtitles.entity.illusioner.ambient": "Illusioner murmurs", + "subtitles.entity.illusioner.cast_spell": "Illusioner casts spell", + "subtitles.entity.illusioner.death": "Illusioner dies", + "subtitles.entity.illusioner.hurt": "Illusioner hurts", + "subtitles.entity.illusioner.mirror_move": "Illusioner displaces", + "subtitles.entity.illusioner.prepare_blindness": "Illusioner prepares blindness", + "subtitles.entity.illusioner.prepare_mirror": "Illusioner prepares mirror image", + "subtitles.entity.iron_golem.attack": "Iron Golem attacks", + "subtitles.entity.iron_golem.damage": "Iron Golem breaks", + "subtitles.entity.iron_golem.death": "Iron Golem dies", + "subtitles.entity.iron_golem.hurt": "Iron Golem hurts", + "subtitles.entity.iron_golem.repair": "Iron Golem repaired", + "subtitles.entity.item.break": "Item breaks", + "subtitles.entity.item.pickup": "Item plops", + "subtitles.entity.item_frame.add_item": "Item Frame fills", + "subtitles.entity.item_frame.break": "Item Frame breaks", + "subtitles.entity.item_frame.place": "Item Frame placed", + "subtitles.entity.item_frame.remove_item": "Item Frame empties", + "subtitles.entity.item_frame.rotate_item": "Item Frame clicks", + "subtitles.entity.leash_knot.break": "Leash knot breaks", + "subtitles.entity.leash_knot.place": "Leash knot tied", + "subtitles.entity.lightning_bolt.impact": "Lightning strikes", + "subtitles.entity.lightning_bolt.thunder": "Thunder roars", + "subtitles.entity.llama.ambient": "Llama bleats", + "subtitles.entity.llama.angry": "Llama bleats angrily", + "subtitles.entity.llama.chest": "Llama Chest equips", + "subtitles.entity.llama.death": "Llama dies", + "subtitles.entity.llama.eat": "Llama eats", + "subtitles.entity.llama.hurt": "Llama hurts", + "subtitles.entity.llama.spit": "Llama spits", + "subtitles.entity.llama.step": "Llama steps", + "subtitles.entity.llama.swag": "Llama is decorated", + "subtitles.entity.magma_cube.death": "Magma Cube dies", + "subtitles.entity.magma_cube.hurt": "Magma Cube hurts", + "subtitles.entity.magma_cube.squish": "Magma Cube squishes", + "subtitles.entity.minecart.riding": "Minecart rolls", + "subtitles.entity.mooshroom.convert": "Mooshroom transforms", + "subtitles.entity.mooshroom.eat": "Mooshroom eats", + "subtitles.entity.mooshroom.milk": "Mooshroom gets milked", + "subtitles.entity.mooshroom.suspicious_milk": "Mooshroom gets milked suspiciously", + "subtitles.entity.mule.ambient": "Mule hee-haws", + "subtitles.entity.mule.angry": "Mule neighs", + "subtitles.entity.mule.chest": "Mule Chest equips", + "subtitles.entity.mule.death": "Mule dies", + "subtitles.entity.mule.eat": "Mule eats", + "subtitles.entity.mule.hurt": "Mule hurts", + "subtitles.entity.painting.break": "Painting breaks", + "subtitles.entity.painting.place": "Painting placed", + "subtitles.entity.panda.aggressive_ambient": "Panda huffs", + "subtitles.entity.panda.ambient": "Panda pants", + "subtitles.entity.panda.bite": "Panda bites", + "subtitles.entity.panda.cant_breed": "Panda bleats", + "subtitles.entity.panda.death": "Panda dies", + "subtitles.entity.panda.eat": "Panda eats", + "subtitles.entity.panda.hurt": "Panda hurts", + "subtitles.entity.panda.pre_sneeze": "Panda's nose tickles", + "subtitles.entity.panda.sneeze": "Panda sneezes", + "subtitles.entity.panda.step": "Panda steps", + "subtitles.entity.panda.worried_ambient": "Panda whimpers", + "subtitles.entity.parrot.ambient": "Parrot talks", + "subtitles.entity.parrot.death": "Parrot dies", + "subtitles.entity.parrot.eats": "Parrot eats", + "subtitles.entity.parrot.fly": "Parrot flutters", + "subtitles.entity.parrot.hurts": "Parrot hurts", + "subtitles.entity.parrot.imitate.blaze": "Parrot breathes", + "subtitles.entity.parrot.imitate.creeper": "Parrot hisses", + "subtitles.entity.parrot.imitate.drowned": "Parrot gurgles", + "subtitles.entity.parrot.imitate.elder_guardian": "Parrot moans", + "subtitles.entity.parrot.imitate.ender_dragon": "Parrot roars", + "subtitles.entity.parrot.imitate.endermite": "Parrot scuttles", + "subtitles.entity.parrot.imitate.evoker": "Parrot murmurs", + "subtitles.entity.parrot.imitate.ghast": "Parrot cries", + "subtitles.entity.parrot.imitate.guardian": "Parrot moans", + "subtitles.entity.parrot.imitate.hoglin": "Parrot growls", + "subtitles.entity.parrot.imitate.husk": "Parrot groans", + "subtitles.entity.parrot.imitate.illusioner": "Parrot murmurs", + "subtitles.entity.parrot.imitate.magma_cube": "Parrot squishes", + "subtitles.entity.parrot.imitate.phantom": "Parrot screeches", + "subtitles.entity.parrot.imitate.piglin": "Parrot snorts", + "subtitles.entity.parrot.imitate.piglin_brute": "Parrot snorts", + "subtitles.entity.parrot.imitate.pillager": "Parrot murmurs", + "subtitles.entity.parrot.imitate.ravager": "Parrot grunts", + "subtitles.entity.parrot.imitate.shulker": "Parrot lurks", + "subtitles.entity.parrot.imitate.silverfish": "Parrot hisses", + "subtitles.entity.parrot.imitate.skeleton": "Parrot rattles", + "subtitles.entity.parrot.imitate.slime": "Parrot squishes", + "subtitles.entity.parrot.imitate.spider": "Parrot hisses", + "subtitles.entity.parrot.imitate.stray": "Parrot rattles", + "subtitles.entity.parrot.imitate.vex": "Parrot vexes", + "subtitles.entity.parrot.imitate.vindicator": "Parrot mutters", + "subtitles.entity.parrot.imitate.warden": "Parrot whines", + "subtitles.entity.parrot.imitate.witch": "Parrot giggles", + "subtitles.entity.parrot.imitate.wither": "Parrot angers", + "subtitles.entity.parrot.imitate.wither_skeleton": "Parrot rattles", + "subtitles.entity.parrot.imitate.zoglin": "Parrot growls", + "subtitles.entity.parrot.imitate.zombie": "Parrot groans", + "subtitles.entity.parrot.imitate.zombie_villager": "Parrot groans", + "subtitles.entity.phantom.ambient": "Phantom screeches", + "subtitles.entity.phantom.bite": "Phantom bites", + "subtitles.entity.phantom.death": "Phantom dies", + "subtitles.entity.phantom.flap": "Phantom flaps", + "subtitles.entity.phantom.hurt": "Phantom hurts", + "subtitles.entity.phantom.swoop": "Phantom swoops", + "subtitles.entity.pig.ambient": "Pig oinks", + "subtitles.entity.pig.death": "Pig dies", + "subtitles.entity.pig.hurt": "Pig hurts", + "subtitles.entity.pig.saddle": "Saddle equips", + "subtitles.entity.piglin.admiring_item": "Piglin admires item", + "subtitles.entity.piglin.ambient": "Piglin snorts", + "subtitles.entity.piglin.angry": "Piglin snorts angrily", + "subtitles.entity.piglin.celebrate": "Piglin celebrates", + "subtitles.entity.piglin.converted_to_zombified": "Piglin converts to Zombified Piglin", + "subtitles.entity.piglin.death": "Piglin dies", + "subtitles.entity.piglin.hurt": "Piglin hurts", + "subtitles.entity.piglin.jealous": "Piglin snorts enviously", + "subtitles.entity.piglin.retreat": "Piglin retreats", + "subtitles.entity.piglin.step": "Piglin steps", + "subtitles.entity.piglin_brute.ambient": "Piglin Brute snorts", + "subtitles.entity.piglin_brute.angry": "Piglin Brute snorts angrily", + "subtitles.entity.piglin_brute.death": "Piglin Brute dies", + "subtitles.entity.piglin_brute.hurt": "Piglin Brute hurts", + "subtitles.entity.piglin_brute.step": "Piglin Brute steps", + "subtitles.entity.piglin_brute.converted_to_zombified": "Piglin Brute converts to Zombified Piglin", + "subtitles.entity.pillager.ambient": "Pillager murmurs", + "subtitles.entity.pillager.celebrate": "Pillager cheers", + "subtitles.entity.pillager.death": "Pillager dies", + "subtitles.entity.pillager.hurt": "Pillager hurts", + "subtitles.entity.player.attack.crit": "Critical attack", + "subtitles.entity.player.attack.knockback": "Knockback attack", + "subtitles.entity.player.attack.strong": "Strong attack", + "subtitles.entity.player.attack.sweep": "Sweeping attack", + "subtitles.entity.player.attack.weak": "Weak attack", + "subtitles.entity.player.burp": "Burp", + "subtitles.entity.player.death": "Player dies", + "subtitles.entity.player.hurt": "Player hurts", + "subtitles.entity.player.hurt_drown": "Player drowning", + "subtitles.entity.player.hurt_on_fire": "Player burns", + "subtitles.entity.player.levelup": "Player dings", + "subtitles.entity.player.freeze_hurt": "Player freezes", + "subtitles.entity.polar_bear.ambient": "Polar Bear groans", + "subtitles.entity.polar_bear.ambient_baby": "Polar Bear hums", + "subtitles.entity.polar_bear.death": "Polar Bear dies", + "subtitles.entity.polar_bear.hurt": "Polar Bear hurts", + "subtitles.entity.polar_bear.warning": "Polar Bear roars", + "subtitles.entity.potion.splash": "Bottle smashes", + "subtitles.entity.potion.throw": "Bottle thrown", + "subtitles.entity.puffer_fish.blow_out": "Pufferfish deflates", + "subtitles.entity.puffer_fish.blow_up": "Pufferfish inflates", + "subtitles.entity.puffer_fish.death": "Pufferfish dies", + "subtitles.entity.puffer_fish.flop": "Pufferfish flops", + "subtitles.entity.puffer_fish.hurt": "Pufferfish hurts", + "subtitles.entity.puffer_fish.sting": "Pufferfish stings", + "subtitles.entity.rabbit.ambient": "Rabbit squeaks", + "subtitles.entity.rabbit.attack": "Rabbit attacks", + "subtitles.entity.rabbit.death": "Rabbit dies", + "subtitles.entity.rabbit.hurt": "Rabbit hurts", + "subtitles.entity.rabbit.jump": "Rabbit hops", + "subtitles.entity.ravager.ambient": "Ravager grunts", + "subtitles.entity.ravager.attack": "Ravager bites", + "subtitles.entity.ravager.celebrate": "Ravager cheers", + "subtitles.entity.ravager.death": "Ravager dies", + "subtitles.entity.ravager.hurt": "Ravager hurts", + "subtitles.entity.ravager.roar": "Ravager roars", + "subtitles.entity.ravager.step": "Ravager steps", + "subtitles.entity.ravager.stunned": "Ravager stunned", + "subtitles.entity.salmon.death": "Salmon dies", + "subtitles.entity.salmon.flop": "Salmon flops", + "subtitles.entity.salmon.hurt": "Salmon hurts", + "subtitles.entity.sheep.ambient": "Sheep baahs", + "subtitles.entity.sheep.death": "Sheep dies", + "subtitles.entity.sheep.hurt": "Sheep hurts", + "subtitles.entity.shulker.ambient": "Shulker lurks", + "subtitles.entity.shulker.close": "Shulker closes", + "subtitles.entity.shulker.death": "Shulker dies", + "subtitles.entity.shulker.hurt": "Shulker hurts", + "subtitles.entity.shulker.open": "Shulker opens", + "subtitles.entity.shulker.shoot": "Shulker shoots", + "subtitles.entity.shulker.teleport": "Shulker teleports", + "subtitles.entity.shulker_bullet.hit": "Shulker Bullet explodes", + "subtitles.entity.shulker_bullet.hurt": "Shulker Bullet breaks", + "subtitles.entity.silverfish.ambient": "Silverfish hisses", + "subtitles.entity.silverfish.death": "Silverfish dies", + "subtitles.entity.silverfish.hurt": "Silverfish hurts", + "subtitles.entity.skeleton.ambient": "Skeleton rattles", + "subtitles.entity.skeleton.converted_to_stray": "Skeleton converts to Stray", + "subtitles.entity.skeleton.death": "Skeleton dies", + "subtitles.entity.skeleton.hurt": "Skeleton hurts", + "subtitles.entity.skeleton.shoot": "Skeleton shoots", + "subtitles.entity.skeleton_horse.ambient": "Skeleton Horse cries", + "subtitles.entity.skeleton_horse.death": "Skeleton Horse dies", + "subtitles.entity.skeleton_horse.hurt": "Skeleton Horse hurts", + "subtitles.entity.skeleton_horse.swim": "Skeleton Horse swims", + "subtitles.entity.slime.attack": "Slime attacks", + "subtitles.entity.slime.death": "Slime dies", + "subtitles.entity.slime.hurt": "Slime hurts", + "subtitles.entity.slime.squish": "Slime squishes", + "subtitles.entity.snow_golem.death": "Snow Golem dies", + "subtitles.entity.snow_golem.hurt": "Snow Golem hurts", + "subtitles.entity.snowball.throw": "Snowball flies", + "subtitles.entity.spider.ambient": "Spider hisses", + "subtitles.entity.spider.death": "Spider dies", + "subtitles.entity.spider.hurt": "Spider hurts", + "subtitles.entity.squid.ambient": "Squid swims", + "subtitles.entity.squid.death": "Squid dies", + "subtitles.entity.squid.hurt": "Squid hurts", + "subtitles.entity.squid.squirt": "Squid shoots ink", + "subtitles.entity.stray.ambient": "Stray rattles", + "subtitles.entity.stray.death": "Stray dies", + "subtitles.entity.stray.hurt": "Stray hurts", + "subtitles.entity.strider.death": "Strider dies", + "subtitles.entity.strider.eat": "Strider eats", + "subtitles.entity.strider.happy": "Strider warbles", + "subtitles.entity.strider.hurt": "Strider hurts", + "subtitles.entity.strider.idle": "Strider chirps", + "subtitles.entity.strider.retreat": "Strider retreats", + "subtitles.entity.tadpole.death": "Tadpole dies", + "subtitles.entity.tadpole.flop": "Tadpole flops", + "subtitles.entity.tadpole.grow_up": "Tadpole grows up", + "subtitles.entity.tadpole.hurt": "Tadpole hurts", + "subtitles.entity.tnt.primed": "TNT fizzes", + "subtitles.entity.tropical_fish.death": "Tropical Fish dies", + "subtitles.entity.tropical_fish.flop": "Tropical Fish flops", + "subtitles.entity.tropical_fish.hurt": "Tropical Fish hurts", + "subtitles.entity.turtle.ambient_land": "Turtle chirps", + "subtitles.entity.turtle.death": "Turtle dies", + "subtitles.entity.turtle.death_baby": "Turtle baby dies", + "subtitles.entity.turtle.egg_break": "Turtle Egg breaks", + "subtitles.entity.turtle.egg_crack": "Turtle Egg cracks", + "subtitles.entity.turtle.egg_hatch": "Turtle Egg hatches", + "subtitles.entity.turtle.hurt": "Turtle hurts", + "subtitles.entity.turtle.hurt_baby": "Turtle baby hurts", + "subtitles.entity.turtle.lay_egg": "Turtle lays egg", + "subtitles.entity.turtle.shamble": "Turtle shambles", + "subtitles.entity.turtle.shamble_baby": "Turtle baby shambles", + "subtitles.entity.turtle.swim": "Turtle swims", + "subtitles.entity.vex.ambient": "Vex vexes", + "subtitles.entity.vex.charge": "Vex shrieks", + "subtitles.entity.vex.death": "Vex dies", + "subtitles.entity.vex.hurt": "Vex hurts", + "subtitles.entity.villager.ambient": "Villager mumbles", + "subtitles.entity.villager.celebrate": "Villager cheers", + "subtitles.entity.villager.death": "Villager dies", + "subtitles.entity.villager.hurt": "Villager hurts", + "subtitles.entity.villager.no": "Villager disagrees", + "subtitles.entity.villager.trade": "Villager trades", + "subtitles.entity.villager.work_armorer": "Armorer works", + "subtitles.entity.villager.work_butcher": "Butcher works", + "subtitles.entity.villager.work_cartographer": "Cartographer works", + "subtitles.entity.villager.work_cleric": "Cleric works", + "subtitles.entity.villager.work_farmer": "Farmer works", + "subtitles.entity.villager.work_fisherman": "Fisherman works", + "subtitles.entity.villager.work_fletcher": "Fletcher works", + "subtitles.entity.villager.work_leatherworker": "Leatherworker works", + "subtitles.entity.villager.work_librarian": "Librarian works", + "subtitles.entity.villager.work_mason": "Mason works", + "subtitles.entity.villager.work_shepherd": "Shepherd works", + "subtitles.entity.villager.work_toolsmith": "Toolsmith works", + "subtitles.entity.villager.work_weaponsmith": "Weaponsmith works", + "subtitles.entity.villager.yes": "Villager agrees", + "subtitles.entity.vindicator.ambient": "Vindicator mutters", + "subtitles.entity.vindicator.celebrate": "Vindicator cheers", + "subtitles.entity.vindicator.death": "Vindicator dies", + "subtitles.entity.vindicator.hurt": "Vindicator hurts", + "subtitles.entity.wandering_trader.ambient": "Wandering Trader mumbles", + "subtitles.entity.wandering_trader.death": "Wandering Trader dies", + "subtitles.entity.wandering_trader.disappeared": "Wandering Trader disappears", + "subtitles.entity.wandering_trader.drink_milk": "Wandering Trader drinks milk", + "subtitles.entity.wandering_trader.drink_potion": "Wandering Trader drinks potion", + "subtitles.entity.wandering_trader.hurt": "Wandering Trader hurts", + "subtitles.entity.wandering_trader.no": "Wandering Trader disagrees", + "subtitles.entity.wandering_trader.reappeared": "Wandering Trader appears", + "subtitles.entity.wandering_trader.trade": "Wandering Trader trades", + "subtitles.entity.wandering_trader.yes": "Wandering Trader agrees", + "subtitles.entity.warden.roar": "Warden roars", + "subtitles.entity.warden.sniff": "Warden sniffs", + "subtitles.entity.warden.emerge": "Warden emerges", + "subtitles.entity.warden.dig": "Warden digs", + "subtitles.entity.warden.hurt": "Warden hurts", + "subtitles.entity.warden.death": "Warden dies", + "subtitles.entity.warden.step": "Warden steps", + "subtitles.entity.warden.listening": "Warden takes notice", + "subtitles.entity.warden.listening_angry": "Warden takes notice angrily", + "subtitles.entity.warden.heartbeat": "Warden's heart beats", + "subtitles.entity.warden.attack_impact": "Warden lands hit", + "subtitles.entity.warden.tendril_clicks": "Warden's tendrils click", + "subtitles.entity.warden.angry": "Warden rages", + "subtitles.entity.warden.agitated": "Warden groans angrily", + "subtitles.entity.warden.ambient": "Warden whines", + "subtitles.entity.warden.nearby_close": "Warden approaches", + "subtitles.entity.warden.nearby_closer": "Warden advances", + "subtitles.entity.warden.nearby_closest": "Warden draws close", + "subtitles.entity.warden.sonic_charge": "Warden charges", + "subtitles.entity.warden.sonic_boom": "Warden booms", + "subtitles.entity.witch.ambient": "Witch giggles", + "subtitles.entity.witch.celebrate": "Witch cheers", + "subtitles.entity.witch.death": "Witch dies", + "subtitles.entity.witch.drink": "Witch drinks", + "subtitles.entity.witch.hurt": "Witch hurts", + "subtitles.entity.witch.throw": "Witch throws", + "subtitles.entity.wither.ambient": "Wither angers", + "subtitles.entity.wither.death": "Wither dies", + "subtitles.entity.wither.hurt": "Wither hurts", + "subtitles.entity.wither.shoot": "Wither attacks", + "subtitles.entity.wither.spawn": "Wither released", + "subtitles.entity.wither_skeleton.ambient": "Wither Skeleton rattles", + "subtitles.entity.wither_skeleton.death": "Wither Skeleton dies", + "subtitles.entity.wither_skeleton.hurt": "Wither Skeleton hurts", + "subtitles.entity.wolf.ambient": "Wolf pants", + "subtitles.entity.wolf.death": "Wolf dies", + "subtitles.entity.wolf.growl": "Wolf growls", + "subtitles.entity.wolf.hurt": "Wolf hurts", + "subtitles.entity.wolf.shake": "Wolf shakes", + "subtitles.entity.zoglin.ambient": "Zoglin growls", + "subtitles.entity.zoglin.angry": "Zoglin growls angrily", + "subtitles.entity.zoglin.attack": "Zoglin attacks", + "subtitles.entity.zoglin.death": "Zoglin dies", + "subtitles.entity.zoglin.hurt": "Zoglin hurts", + "subtitles.entity.zoglin.step": "Zoglin steps", + "subtitles.entity.zombie.ambient": "Zombie groans", + "subtitles.entity.zombie.attack_wooden_door": "Door shakes", + "subtitles.entity.zombie.converted_to_drowned": "Zombie converts to Drowned", + "subtitles.entity.zombie.break_wooden_door": "Door breaks", + "subtitles.entity.zombie.death": "Zombie dies", + "subtitles.entity.zombie.destroy_egg": "Turtle Egg stomped", + "subtitles.entity.zombie.hurt": "Zombie hurts", + "subtitles.entity.zombie.infect": "Zombie infects", + "subtitles.entity.zombie_horse.ambient": "Zombie Horse cries", + "subtitles.entity.zombie_horse.death": "Zombie Horse dies", + "subtitles.entity.zombie_horse.hurt": "Zombie Horse hurts", + "subtitles.entity.zombie_villager.ambient": "Zombie Villager groans", + "subtitles.entity.zombie_villager.converted": "Zombie Villager vociferates", + "subtitles.entity.zombie_villager.cure": "Zombie Villager snuffles", + "subtitles.entity.zombie_villager.death": "Zombie Villager dies", + "subtitles.entity.zombie_villager.hurt": "Zombie Villager hurts", + "subtitles.entity.zombified_piglin.ambient": "Zombified Piglin grunts", + "subtitles.entity.zombified_piglin.angry": "Zombified Piglin grunts angrily", + "subtitles.entity.zombified_piglin.death": "Zombified Piglin dies", + "subtitles.entity.zombified_piglin.hurt": "Zombified Piglin hurts", + "subtitles.event.raid.horn": "Ominous horn blares", + "subtitles.item.armor.equip": "Gear equips", + "subtitles.item.armor.equip_chain": "Chain armor jingles", + "subtitles.item.armor.equip_diamond": "Diamond armor clangs", + "subtitles.item.armor.equip_elytra": "Elytra rustle", + "subtitles.item.armor.equip_gold": "Gold armor clinks", + "subtitles.item.armor.equip_iron": "Iron armor clanks", + "subtitles.item.armor.equip_leather": "Leather armor rustles", + "subtitles.item.armor.equip_netherite": "Netherite armor clanks", + "subtitles.item.armor.equip_turtle": "Turtle Shell thunks", + "subtitles.item.axe.strip": "Axe strips", + "subtitles.item.axe.scrape": "Axe scrapes", + "subtitles.item.axe.wax_off": "Wax off", + "subtitles.item.bone_meal.use": "Bone Meal crinkles", + "subtitles.item.book.page_turn": "Page rustles", + "subtitles.item.book.put": "Book thumps", + "subtitles.item.bottle.empty": "Bottle empties", + "subtitles.item.bottle.fill": "Bottle fills", + "subtitles.item.bucket.empty": "Bucket empties", + "subtitles.item.bucket.fill": "Bucket fills", + "subtitles.item.bucket.fill_axolotl": "Axolotl scooped", + "subtitles.item.bucket.fill_fish": "Fish captured", + "subtitles.item.bucket.fill_tadpole": "Tadpole captured", + "subtitles.item.bundle.drop_contents": "Bundle empties", + "subtitles.item.bundle.insert": "Item packed", + "subtitles.item.bundle.remove_one": "Item unpacked", + "subtitles.item.chorus_fruit.teleport": "Player teleports", + "subtitles.item.crop.plant": "Crop planted", + "subtitles.item.crossbow.charge": "Crossbow charges up", + "subtitles.item.crossbow.hit": "Arrow hits", + "subtitles.item.crossbow.load": "Crossbow loads", + "subtitles.item.crossbow.shoot": "Crossbow fires", + "subtitles.item.firecharge.use": "Fireball whooshes", + "subtitles.item.flintandsteel.use": "Flint and Steel click", + "subtitles.item.goat_horn.play": "Goat Horn plays", + "subtitles.item.hoe.till": "Hoe tills", + "subtitles.item.honey_bottle.drink": "Gulping", + "subtitles.item.lodestone_compass.lock": "Lodestone Compass locks onto Lodestone", + "subtitles.item.nether_wart.plant": "Crop planted", + "subtitles.item.shears.shear": "Shears click", + "subtitles.item.shield.block": "Shield blocks", + "subtitles.item.shovel.flatten": "Shovel flattens", + "subtitles.item.totem.use": "Totem activates", + "subtitles.item.trident.hit": "Trident stabs", + "subtitles.item.trident.hit_ground": "Trident vibrates", + "subtitles.item.trident.return": "Trident returns", + "subtitles.item.trident.riptide": "Trident zooms", + "subtitles.item.trident.throw": "Trident clangs", + "subtitles.item.trident.thunder": "Trident thunder cracks", + "subtitles.item.spyglass.use": "Spyglass expands", + "subtitles.item.spyglass.stop_using": "Spyglass retracts", + "subtitles.item.ink_sac.use": "Ink Sac splotches", + "subtitles.item.glow_ink_sac.use": "Glow Ink Sac splotches", + "subtitles.item.dye.use": "Dye stains", + "subtitles.particle.soul_escape": "Soul escapes", + "subtitles.ui.cartography_table.take_result": "Map drawn", + "subtitles.ui.loom.take_result": "Loom used", + "subtitles.ui.stonecutter.take_result": "Stonecutter used", + "subtitles.weather.rain": "Rain falls", + "telemetry_info.screen.title": "Telemetry Data Collection", + "telemetry_info.screen.description": "Collecting this data helps us improve Minecraft by guiding us in directions that are relevant to our players.\nYou can also send in additional feedback to help us keep improving Minecraft.", + "telemetry_info.button.show_data": "Open My Data", + "telemetry_info.button.give_feedback": "Give Feedback", + "telemetry_info.property_title": "Included Data", + "telemetry.property.user_id.title": "User ID", + "telemetry.property.client_id.title": "Client ID", + "telemetry.property.minecraft_session_id.title": "Minecraft Session ID", + "telemetry.property.game_version.title": "Game Version", + "telemetry.property.operating_system.title": "Operating System", + "telemetry.property.platform.title": "Platform", + "telemetry.property.client_modded.title": "Client Modded", + "telemetry.property.event_timestamp_utc.title": "Event Timestamp (UTC)", + "telemetry.property.opt_in.title": "Opt-In", + "telemetry.property.world_session_id.title": "World Session ID", + "telemetry.property.server_modded.title": "Server Modded", + "telemetry.property.server_type.title": "Server Type", + "telemetry.property.frame_rate_samples.title": "Frame Rate Samples (FPS)", + "telemetry.property.render_time_samples.title": "Render Time Samples", + "telemetry.property.used_memory_samples.title": "Used Random Access Memory", + "telemetry.property.number_of_samples.title": "Sample Count", + "telemetry.property.render_distance.title": "Render Distance", + "telemetry.property.dedicated_memory_kb.title": "Dedicated Memory (kB)", + "telemetry.property.game_mode.title": "Game Mode", + "telemetry.property.seconds_since_load.title": "Time Since Load (Seconds)", + "telemetry.property.ticks_since_load.title": "Time Since Load (Ticks)", + "telemetry.property.world_load_time_ms.title": "World Load Time (Milliseconds)", + "telemetry.property.new_world.title": "New World", + "telemetry.event.required": "%s (Required)", + "telemetry.event.optional": "%s (Optional)", + "telemetry.event.world_loaded.title": "World Loaded", + "telemetry.event.world_loaded.description": "Knowing how players play Minecraft (such as Game Mode, client or server modded, and game version) allows us to focus game updates to improve the areas that players care about most.\nThe World Loaded event is paired with the World Unloaded event to calculate how long the play session has lasted.", + "telemetry.event.world_unloaded.title": "World Unloaded", + "telemetry.event.world_unloaded.description": "This event is paired with the World Loaded event to calculate how long the world session has lasted.\nThe duration (in seconds and ticks) is measured when a world session has ended (quitting to title, disconnecting from a server).", + "telemetry.event.performance_metrics.title": "Performance Metrics", + "telemetry.event.performance_metrics.description": "Knowing the overall performance profile of Minecraft helps us tune and optimize the game for a wide range of machine specifications and operating systems. \nGame version is included to help us compare the performance profile for new versions of Minecraft.", + "telemetry.event.world_load_times.title": "World Load Times", + "telemetry.event.world_load_times.description": "It’s important for us to understand how long it takes to join a world, and how that changes over time. For example, when we add new features or do larger technical changes, we need to see what impact that had on load times.", + "debug.prefix": "[Debug]:", + "debug.reload_chunks.help": "F3 + A = Reload chunks", + "debug.show_hitboxes.help": "F3 + B = Show hitboxes", + "debug.clear_chat.help": "F3 + D = Clear chat", + "debug.chunk_boundaries.help": "F3 + G = Show chunk boundaries", + "debug.advanced_tooltips.help": "F3 + H = Advanced tooltips", + "debug.creative_spectator.help": "F3 + N = Cycle previous gamemode <-> spectator", + "debug.pause_focus.help": "F3 + P = Pause on lost focus", + "debug.help.help": "F3 + Q = Show this list", + "debug.reload_resourcepacks.help": "F3 + T = Reload resource packs", + "debug.pause.help": "F3 + Esc = Pause without pause menu (if pausing is possible)", + "debug.copy_location.help": "F3 + C = Copy location as /tp command, hold F3 + C to crash the game", + "debug.inspect.help": "F3 + I = Copy entity or block data to clipboard", + "debug.gamemodes.help": "F3 + F4 = Open game mode switcher", + "debug.profiling.help": "F3 + L = Start/stop profiling", + "debug.copy_location.message": "Copied location to clipboard", + "debug.inspect.server.block": "Copied server-side block data to clipboard", + "debug.inspect.server.entity": "Copied server-side entity data to clipboard", + "debug.inspect.client.block": "Copied client-side block data to clipboard", + "debug.inspect.client.entity": "Copied client-side entity data to clipboard", + "debug.reload_chunks.message": "Reloading all chunks", + "debug.show_hitboxes.on": "Hitboxes: shown", + "debug.show_hitboxes.off": "Hitboxes: hidden", + "debug.chunk_boundaries.on": "Chunk borders: shown", + "debug.chunk_boundaries.off": "Chunk borders: hidden", + "debug.advanced_tooltips.on": "Advanced tooltips: shown", + "debug.advanced_tooltips.off": "Advanced tooltips: hidden", + "debug.creative_spectator.error": "Unable to switch gamemode; no permission", + "debug.gamemodes.error": "Unable to open game mode switcher; no permission", + "debug.pause_focus.on": "Pause on lost focus: enabled", + "debug.pause_focus.off": "Pause on lost focus: disabled", + "debug.help.message": "Key bindings:", + "debug.reload_resourcepacks.message": "Reloaded resource packs", + "debug.crash.message": "F3 + C is held down. This will crash the game unless released.", + "debug.crash.warning": "Crashing in %s...", + "debug.gamemodes.press_f4": "[ F4 ]", + "debug.gamemodes.select_next": "%s Next", + "debug.profiling.start": "Profiling started for %s seconds. Use F3 + L to stop early", + "debug.profiling.stop": "Profiling ended. Saved results to %s", + "resourcepack.downloading": "Downloading Resource Pack", + "resourcepack.requesting": "Making Request...", + "resourcepack.progress": "Downloading file (%s MB)...", + "tutorial.bundleInsert.title": "Use a Bundle", + "tutorial.bundleInsert.description": "Right Click to add items", + "tutorial.move.title": "Move with %s, %s, %s and %s", + "tutorial.move.description": "Jump with %s", + "tutorial.look.title": "Look around", + "tutorial.look.description": "Use your mouse to turn", + "tutorial.find_tree.title": "Find a tree", + "tutorial.find_tree.description": "Punch it to collect wood", + "tutorial.punch_tree.title": "Destroy the tree", + "tutorial.punch_tree.description": "Hold down %s", + "tutorial.open_inventory.title": "Open your inventory", + "tutorial.open_inventory.description": "Press %s", + "tutorial.craft_planks.title": "Craft wooden planks", + "tutorial.craft_planks.description": "The recipe book can help", + "tutorial.socialInteractions.title": "Social Interactions", + "tutorial.socialInteractions.description": "Press %s to open", + "advancements.adventure.adventuring_time.title": "Adventuring Time", + "advancements.adventure.adventuring_time.description": "Discover every biome", + "advancements.adventure.arbalistic.title": "Arbalistic", + "advancements.adventure.arbalistic.description": "Kill five unique mobs with one crossbow shot", + "advancements.adventure.avoid_vibration.title": "Sneak 100", + "advancements.adventure.avoid_vibration.description": "Sneak near a Sculk Sensor or Warden to prevent it from detecting you", + "advancements.adventure.bullseye.title": "Bullseye", + "advancements.adventure.bullseye.description": "Hit the bullseye of a Target block from at least 30 meters away", + "advancements.adventure.fall_from_world_height.title": "Caves & Cliffs", + "advancements.adventure.fall_from_world_height.description": "Free fall from the top of the world (build limit) to the bottom of the world and survive", + "advancements.adventure.kill_mob_near_sculk_catalyst.title": "It Spreads", + "advancements.adventure.kill_mob_near_sculk_catalyst.description": "Kill a mob near a Sculk Catalyst", + "advancements.adventure.walk_on_powder_snow_with_leather_boots.title": "Light as a Rabbit", + "advancements.adventure.walk_on_powder_snow_with_leather_boots.description": "Walk on Powder Snow...without sinking in it", + "advancements.adventure.lightning_rod_with_villager_no_fire.title": "Surge Protector", + "advancements.adventure.lightning_rod_with_villager_no_fire.description": "Protect a Villager from an undesired shock without starting a fire", + "advancements.adventure.spyglass_at_parrot.title": "Is It a Bird?", + "advancements.adventure.spyglass_at_parrot.description": "Look at a Parrot through a Spyglass", + "advancements.adventure.spyglass_at_ghast.title": "Is It a Balloon?", + "advancements.adventure.spyglass_at_ghast.description": "Look at a Ghast through a Spyglass", + "advancements.adventure.spyglass_at_dragon.title": "Is It a Plane?", + "advancements.adventure.spyglass_at_dragon.description": "Look at the Ender Dragon through a Spyglass", + "advancements.adventure.hero_of_the_village.title": "Hero of the Village", + "advancements.adventure.hero_of_the_village.description": "Successfully defend a village from a raid", + "advancements.adventure.honey_block_slide.title": "Sticky Situation", + "advancements.adventure.honey_block_slide.description": "Jump into a Honey Block to break your fall", + "advancements.adventure.kill_all_mobs.title": "Monsters Hunted", + "advancements.adventure.kill_all_mobs.description": "Kill one of every hostile monster", + "advancements.adventure.kill_a_mob.title": "Monster Hunter", + "advancements.adventure.kill_a_mob.description": "Kill any hostile monster", + "advancements.adventure.ol_betsy.title": "Ol' Betsy", + "advancements.adventure.ol_betsy.description": "Shoot a Crossbow", + "advancements.adventure.play_jukebox_in_meadows.title": "Sound of Music", + "advancements.adventure.play_jukebox_in_meadows.description": "Make the Meadows come alive with the sound of music from a Jukebox", + "advancements.adventure.root.title": "Adventure", + "advancements.adventure.root.description": "Adventure, exploration and combat", + "advancements.adventure.shoot_arrow.title": "Take Aim", + "advancements.adventure.shoot_arrow.description": "Shoot something with an Arrow", + "advancements.adventure.sleep_in_bed.title": "Sweet Dreams", + "advancements.adventure.sleep_in_bed.description": "Sleep in a Bed to change your respawn point", + "advancements.adventure.sniper_duel.title": "Sniper Duel", + "advancements.adventure.sniper_duel.description": "Kill a Skeleton from at least 50 meters away", + "advancements.adventure.summon_iron_golem.title": "Hired Help", + "advancements.adventure.summon_iron_golem.description": "Summon an Iron Golem to help defend a village", + "advancements.adventure.totem_of_undying.title": "Postmortal", + "advancements.adventure.totem_of_undying.description": "Use a Totem of Undying to cheat death", + "advancements.adventure.trade.title": "What a Deal!", + "advancements.adventure.trade.description": "Successfully trade with a Villager", + "advancements.adventure.trade_at_world_height.title": "Star Trader", + "advancements.adventure.trade_at_world_height.description": "Trade with a Villager at the build height limit", + "advancements.adventure.throw_trident.title": "A Throwaway Joke", + "advancements.adventure.throw_trident.description": "Throw a Trident at something.\nNote: Throwing away your only weapon is not a good idea.", + "advancements.adventure.two_birds_one_arrow.title": "Two Birds, One Arrow", + "advancements.adventure.two_birds_one_arrow.description": "Kill two Phantoms with a piercing Arrow", + "advancements.adventure.very_very_frightening.title": "Very Very Frightening", + "advancements.adventure.very_very_frightening.description": "Strike a Villager with lightning", + "advancements.adventure.voluntary_exile.title": "Voluntary Exile", + "advancements.adventure.voluntary_exile.description": "Kill a raid captain.\nMaybe consider staying away from villages for the time being...", + "advancements.adventure.whos_the_pillager_now.title": "Who's the Pillager Now?", + "advancements.adventure.whos_the_pillager_now.description": "Give a Pillager a taste of their own medicine", + "advancements.husbandry.root.title": "Husbandry", + "advancements.husbandry.root.description": "The world is full of friends and food", + "advancements.husbandry.breed_an_animal.title": "The Parrots and the Bats", + "advancements.husbandry.breed_an_animal.description": "Breed two animals together", + "advancements.husbandry.fishy_business.title": "Fishy Business", + "advancements.husbandry.fishy_business.description": "Catch a fish", + "advancements.husbandry.make_a_sign_glow.title": "Glow and Behold!", + "advancements.husbandry.make_a_sign_glow.description": "Make the text of any kind of sign glow", + "advancements.husbandry.ride_a_boat_with_a_goat.title": "Whatever Floats Your Goat!", + "advancements.husbandry.ride_a_boat_with_a_goat.description": "Get in a Boat and float with a Goat", + "advancements.husbandry.tactical_fishing.title": "Tactical Fishing", + "advancements.husbandry.tactical_fishing.description": "Catch a Fish... without a Fishing Rod!", + "advancements.husbandry.axolotl_in_a_bucket.title": "The Cutest Predator", + "advancements.husbandry.axolotl_in_a_bucket.description": "Catch an Axolotl in a Bucket", + "advancements.husbandry.froglights.title": "With Our Powers Combined!", + "advancements.husbandry.froglights.description": "Have all Froglights in your inventory", + "advancements.husbandry.tadpole_in_a_bucket.title": "Bukkit Bukkit", + "advancements.husbandry.tadpole_in_a_bucket.description": "Catch a Tadpole in a Bucket", + "advancements.husbandry.leash_all_frog_variants.title": "When the Squad Hops into Town", + "advancements.husbandry.leash_all_frog_variants.description": "Get each Frog variant on a Lead", + "advancements.husbandry.kill_axolotl_target.title": "The Healing Power of Friendship!", + "advancements.husbandry.kill_axolotl_target.description": "Team up with an Axolotl and win a fight", + "advancements.husbandry.breed_all_animals.title": "Two by Two", + "advancements.husbandry.breed_all_animals.description": "Breed all the animals!", + "advancements.husbandry.tame_an_animal.title": "Best Friends Forever", + "advancements.husbandry.tame_an_animal.description": "Tame an animal", + "advancements.husbandry.plant_seed.title": "A Seedy Place", + "advancements.husbandry.plant_seed.description": "Plant a seed and watch it grow", + "advancements.husbandry.netherite_hoe.title": "Serious Dedication", + "advancements.husbandry.netherite_hoe.description": "Use a Netherite Ingot to upgrade a Hoe, and then reevaluate your life choices", + "advancements.husbandry.balanced_diet.title": "A Balanced Diet", + "advancements.husbandry.balanced_diet.description": "Eat everything that is edible, even if it's not good for you", + "advancements.husbandry.complete_catalogue.title": "A Complete Catalogue", + "advancements.husbandry.complete_catalogue.description": "Tame all Cat variants!", + "advancements.husbandry.safely_harvest_honey.title": "Bee Our Guest", + "advancements.husbandry.safely_harvest_honey.description": "Use a Campfire to collect Honey from a Beehive using a Bottle without aggravating the Bees", + "advancements.husbandry.silk_touch_nest.title": "Total Beelocation", + "advancements.husbandry.silk_touch_nest.description": "Move a Bee Nest, with 3 Bees inside, using Silk Touch", + "advancements.husbandry.wax_on.title": "Wax On", + "advancements.husbandry.wax_on.description": "Apply Honeycomb to a Copper block!", + "advancements.husbandry.wax_off.title": "Wax Off", + "advancements.husbandry.wax_off.description": "Scrape Wax off of a Copper block!", + "advancements.husbandry.allay_deliver_item_to_player.title": "You've Got a Friend in Me", + "advancements.husbandry.allay_deliver_item_to_player.description": "Have an Allay deliver items to you", + "advancements.husbandry.allay_deliver_cake_to_note_block.title": "Birthday Song", + "advancements.husbandry.allay_deliver_cake_to_note_block.description": "Have an Allay drop a Cake at a Note Block", + "advancements.end.dragon_breath.title": "You Need a Mint", + "advancements.end.dragon_breath.description": "Collect Dragon's Breath in a Glass Bottle", + "advancements.end.dragon_egg.title": "The Next Generation", + "advancements.end.dragon_egg.description": "Hold the Dragon Egg", + "advancements.end.elytra.title": "Sky's the Limit", + "advancements.end.elytra.description": "Find Elytra", + "advancements.end.enter_end_gateway.title": "Remote Getaway", + "advancements.end.enter_end_gateway.description": "Escape the island", + "advancements.end.find_end_city.title": "The City at the End of the Game", + "advancements.end.find_end_city.description": "Go on in, what could happen?", + "advancements.end.kill_dragon.title": "Free the End", + "advancements.end.kill_dragon.description": "Good luck", + "advancements.end.levitate.title": "Great View From Up Here", + "advancements.end.levitate.description": "Levitate up 50 blocks from the attacks of a Shulker", + "advancements.end.respawn_dragon.title": "The End... Again...", + "advancements.end.respawn_dragon.description": "Respawn the Ender Dragon", + "advancements.end.root.title": "The End", + "advancements.end.root.description": "Or the beginning?", + "advancements.nether.brew_potion.title": "Local Brewery", + "advancements.nether.brew_potion.description": "Brew a Potion", + "advancements.nether.all_potions.title": "A Furious Cocktail", + "advancements.nether.all_potions.description": "Have every potion effect applied at the same time", + "advancements.nether.all_effects.title": "How Did We Get Here?", + "advancements.nether.all_effects.description": "Have every effect applied at the same time", + "advancements.nether.create_beacon.title": "Bring Home the Beacon", + "advancements.nether.create_beacon.description": "Construct and place a Beacon", + "advancements.nether.create_full_beacon.title": "Beaconator", + "advancements.nether.create_full_beacon.description": "Bring a Beacon to full power", + "advancements.nether.find_fortress.title": "A Terrible Fortress", + "advancements.nether.find_fortress.description": "Break your way into a Nether Fortress", + "advancements.nether.get_wither_skull.title": "Spooky Scary Skeleton", + "advancements.nether.get_wither_skull.description": "Obtain a Wither Skeleton's skull", + "advancements.nether.obtain_blaze_rod.title": "Into Fire", + "advancements.nether.obtain_blaze_rod.description": "Relieve a Blaze of its rod", + "advancements.nether.return_to_sender.title": "Return to Sender", + "advancements.nether.return_to_sender.description": "Destroy a Ghast with a fireball", + "advancements.nether.root.title": "Nether", + "advancements.nether.root.description": "Bring summer clothes", + "advancements.nether.summon_wither.title": "Withering Heights", + "advancements.nether.summon_wither.description": "Summon the Wither", + "advancements.nether.fast_travel.title": "Subspace Bubble", + "advancements.nether.fast_travel.description": "Use the Nether to travel 7 km in the Overworld", + "advancements.nether.uneasy_alliance.title": "Uneasy Alliance", + "advancements.nether.uneasy_alliance.description": "Rescue a Ghast from the Nether, bring it safely home to the Overworld... and then kill it", + "advancements.nether.obtain_ancient_debris.title": "Hidden in the Depths", + "advancements.nether.obtain_ancient_debris.description": "Obtain Ancient Debris", + "advancements.nether.netherite_armor.title": "Cover Me in Debris", + "advancements.nether.netherite_armor.description": "Get a full suit of Netherite armor", + "advancements.nether.use_lodestone.title": "Country Lode, Take Me Home", + "advancements.nether.use_lodestone.description": "Use a Compass on a Lodestone", + "advancements.nether.obtain_crying_obsidian.title": "Who is Cutting Onions?", + "advancements.nether.obtain_crying_obsidian.description": "Obtain Crying Obsidian", + "advancements.nether.charge_respawn_anchor.title": "Not Quite \"Nine\" Lives", + "advancements.nether.charge_respawn_anchor.description": "Charge a Respawn Anchor to the maximum", + "advancements.nether.ride_strider.title": "This Boat Has Legs", + "advancements.nether.ride_strider.description": "Ride a Strider with a Warped Fungus on a Stick", + "advancements.nether.ride_strider_in_overworld_lava.title": "Feels Like Home", + "advancements.nether.ride_strider_in_overworld_lava.description": "Take a Strider for a loooong ride on a lava lake in the Overworld", + "advancements.nether.explore_nether.title": "Hot Tourist Destinations", + "advancements.nether.explore_nether.description": "Explore all Nether biomes", + "advancements.nether.find_bastion.title": "Those Were the Days", + "advancements.nether.find_bastion.description": "Enter a Bastion Remnant", + "advancements.nether.loot_bastion.title": "War Pigs", + "advancements.nether.loot_bastion.description": "Loot a Chest in a Bastion Remnant", + "advancements.nether.distract_piglin.title": "Oh Shiny", + "advancements.nether.distract_piglin.description": "Distract Piglins with gold", + "advancements.story.cure_zombie_villager.title": "Zombie Doctor", + "advancements.story.cure_zombie_villager.description": "Weaken and then cure a Zombie Villager", + "advancements.story.deflect_arrow.title": "Not Today, Thank You", + "advancements.story.deflect_arrow.description": "Deflect a projectile with a Shield", + "advancements.story.enchant_item.title": "Enchanter", + "advancements.story.enchant_item.description": "Enchant an item at an Enchanting Table", + "advancements.story.enter_the_end.title": "The End?", + "advancements.story.enter_the_end.description": "Enter the End Portal", + "advancements.story.enter_the_nether.title": "We Need to Go Deeper", + "advancements.story.enter_the_nether.description": "Build, light and enter a Nether Portal", + "advancements.story.follow_ender_eye.title": "Eye Spy", + "advancements.story.follow_ender_eye.description": "Follow an Eye of Ender", + "advancements.story.form_obsidian.title": "Ice Bucket Challenge", + "advancements.story.form_obsidian.description": "Obtain a block of Obsidian", + "advancements.story.iron_tools.title": "Isn't It Iron Pick", + "advancements.story.iron_tools.description": "Upgrade your Pickaxe", + "advancements.story.lava_bucket.title": "Hot Stuff", + "advancements.story.lava_bucket.description": "Fill a Bucket with lava", + "advancements.story.mine_diamond.title": "Diamonds!", + "advancements.story.mine_diamond.description": "Acquire diamonds", + "advancements.story.mine_stone.title": "Stone Age", + "advancements.story.mine_stone.description": "Mine Stone with your new Pickaxe", + "advancements.story.obtain_armor.title": "Suit Up", + "advancements.story.obtain_armor.description": "Protect yourself with a piece of iron armor", + "advancements.story.root.title": "Minecraft", + "advancements.story.root.description": "The heart and story of the game", + "advancements.story.shiny_gear.title": "Cover Me with Diamonds", + "advancements.story.shiny_gear.description": "Diamond armor saves lives", + "advancements.story.smelt_iron.title": "Acquire Hardware", + "advancements.story.smelt_iron.description": "Smelt an Iron Ingot", + "advancements.story.upgrade_tools.title": "Getting an Upgrade", + "advancements.story.upgrade_tools.description": "Construct a better Pickaxe", + "team.visibility.always": "Always", + "team.visibility.never": "Never", + "team.visibility.hideForOtherTeams": "Hide for other teams", + "team.visibility.hideForOwnTeam": "Hide for own team", + "team.collision.always": "Always", + "team.collision.never": "Never", + "team.collision.pushOtherTeams": "Push other teams", + "team.collision.pushOwnTeam": "Push own team", + "argument.uuid.invalid": "Invalid UUID", + "argument.entity.selector.nearestPlayer": "Nearest player", + "argument.entity.selector.randomPlayer": "Random player", + "argument.entity.selector.allPlayers": "All players", + "argument.entity.selector.allEntities": "All entities", + "argument.entity.selector.self": "Current entity", + "argument.entity.options.name.description": "Entity name", + "argument.entity.options.distance.description": "Distance to entity", + "argument.entity.options.level.description": "Experience level", + "argument.entity.options.x.description": "x position", + "argument.entity.options.y.description": "y position", + "argument.entity.options.z.description": "z position", + "argument.entity.options.dx.description": "Entities between x and x + dx", + "argument.entity.options.dy.description": "Entities between y and y + dy", + "argument.entity.options.dz.description": "Entities between z and z + dz", + "argument.entity.options.x_rotation.description": "Entity's x rotation", + "argument.entity.options.y_rotation.description": "Entity's y rotation", + "argument.entity.options.limit.description": "Maximum number of entities to return", + "argument.entity.options.sort.description": "Sort the entities", + "argument.entity.options.gamemode.description": "Players with gamemode", + "argument.entity.options.team.description": "Entities on team", + "argument.entity.options.type.description": "Entities of type", + "argument.entity.options.tag.description": "Entities with tag", + "argument.entity.options.nbt.description": "Entities with NBT", + "argument.entity.options.scores.description": "Entities with scores", + "argument.entity.options.advancements.description": "Players with advancements", + "argument.entity.options.predicate.description": "Custom predicate", + "argument.resource.not_found": "Can't find element '%s' of type '%s'", + "argument.resource.invalid_type": "Element '%s' has wrong type '%s' (expected '%s')", + "argument.resource_tag.not_found": "Can't find tag '%s' of type '%s'", + "argument.resource_tag.invalid_type": "Tag '%s' has wrong type '%s' (expected '%s')", + "command.failed": "An unexpected error occurred trying to execute that command", + "command.context.here": "<--[HERE]", + "command.context.parse_error": "%s at position %s: %s", + "commands.publish.started": "Local game hosted on port %s", + "commands.publish.failed": "Unable to host local game", + "commands.advancement.advancementNotFound": "No advancement was found by the name '%1$s'", + "commands.advancement.criterionNotFound": "The advancement %1$s does not contain the criterion '%2$s'", + "commands.advancement.grant.one.to.one.success": "Granted the advancement %s to %s", + "commands.advancement.grant.one.to.one.failure": "Couldn't grant advancement %s to %s as they already have it", + "commands.advancement.grant.one.to.many.success": "Granted the advancement %s to %s players", + "commands.advancement.grant.one.to.many.failure": "Couldn't grant advancement %s to %s players as they already have it", + "commands.advancement.grant.many.to.one.success": "Granted %s advancements to %s", + "commands.advancement.grant.many.to.one.failure": "Couldn't grant %s advancements to %s as they already have them", + "commands.advancement.grant.many.to.many.success": "Granted %s advancements to %s players", + "commands.advancement.grant.many.to.many.failure": "Couldn't grant %s advancements to %s players as they already have them", + "commands.advancement.grant.criterion.to.one.success": "Granted criterion '%s' of advancement %s to %s", + "commands.advancement.grant.criterion.to.one.failure": "Couldn't grant criterion '%s' of advancement %s to %s as they already have it", + "commands.advancement.grant.criterion.to.many.success": "Granted criterion '%s' of advancement %s to %s players", + "commands.advancement.grant.criterion.to.many.failure": "Couldn't grant criterion '%s' of advancement %s to %s players as they already have it", + "commands.advancement.revoke.one.to.one.success": "Revoked the advancement %s from %s", + "commands.advancement.revoke.one.to.one.failure": "Couldn't revoke advancement %s from %s as they don't have it", + "commands.advancement.revoke.one.to.many.success": "Revoked the advancement %s from %s players", + "commands.advancement.revoke.one.to.many.failure": "Couldn't revoke advancement %s from %s players as they don't have it", + "commands.advancement.revoke.many.to.one.success": "Revoked %s advancements from %s", + "commands.advancement.revoke.many.to.one.failure": "Couldn't revoke %s advancements from %s as they don't have them", + "commands.advancement.revoke.many.to.many.success": "Revoked %s advancements from %s players", + "commands.advancement.revoke.many.to.many.failure": "Couldn't revoke %s advancements from %s players as they don't have them", + "commands.advancement.revoke.criterion.to.one.success": "Revoked criterion '%s' of advancement %s from %s", + "commands.advancement.revoke.criterion.to.one.failure": "Couldn't revoke criterion '%s' of advancement %s from %s as they don't have it", + "commands.advancement.revoke.criterion.to.many.success": "Revoked criterion '%s' of advancement %s from %s players", + "commands.advancement.revoke.criterion.to.many.failure": "Couldn't revoke criterion '%s' of advancement %s from %s players as they don't have it", + "commands.attribute.failed.entity": "%s is not a valid entity for this command", + "commands.attribute.failed.no_attribute": "Entity %s has no attribute %s", + "commands.attribute.failed.no_modifier": "Attribute %s for entity %s has no modifier %s", + "commands.attribute.failed.modifier_already_present": "Modifier %s is already present on attribute %s for entity %s", + "commands.attribute.value.get.success": "Value of attribute %s for entity %s is %s", + "commands.attribute.base_value.get.success": "Base value of attribute %s for entity %s is %s", + "commands.attribute.base_value.set.success": "Base value for attribute %s for entity %s set to %s", + "commands.attribute.modifier.add.success": "Added modifier %s to attribute %s for entity %s", + "commands.attribute.modifier.remove.success": "Removed modifier %s from attribute %s for entity %s", + "commands.attribute.modifier.value.get.success": "Value of modifier %s on attribute %s for entity %s is %s", + "commands.forceload.added.failure": "No chunks were marked for force loading", + "commands.forceload.added.single": "Marked chunk %s in %s to be force loaded", + "commands.forceload.added.multiple": "Marked %s chunks in %s from %s to %s to be force loaded", + "commands.forceload.query.success": "Chunk at %s in %s is marked for force loading", + "commands.forceload.query.failure": "Chunk at %s in %s is not marked for force loading", + "commands.forceload.list.single": "A force loaded chunk was found in %s at: %s", + "commands.forceload.list.multiple": "%s force loaded chunks were found in %s at: %s", + "commands.forceload.added.none": "No force loaded chunks were found in %s", + "commands.forceload.removed.all": "Unmarked all force loaded chunks in %s", + "commands.forceload.removed.failure": "No chunks were removed from force loading", + "commands.forceload.removed.single": "Unmarked chunk %s in %s for force loading", + "commands.forceload.removed.multiple": "Unmarked %s chunks in %s from %s to %s for force loading", + "commands.forceload.toobig": "Too many chunks in the specified area (maximum %s, specified %s)", + "commands.clear.success.single": "Removed %s items from player %s", + "commands.clear.success.multiple": "Removed %s items from %s players", + "commands.clear.test.single": "Found %s matching items on player %s", + "commands.clear.test.multiple": "Found %s matching items on %s players", + "commands.clone.success": "Successfully cloned %s blocks", + "commands.debug.started": "Started tick profiling", + "commands.debug.stopped": "Stopped tick profiling after %s seconds and %s ticks (%s ticks per second)", + "commands.debug.notRunning": "The tick profiler hasn't started", + "commands.debug.alreadyRunning": "The tick profiler is already started", + "commands.debug.function.success.single": "Traced %s commands from function '%s' to output file %s", + "commands.debug.function.success.multiple": "Traced %s commands from %s functions to output file %s", + "commands.debug.function.noRecursion": "Can't trace from inside of function", + "commands.debug.function.traceFailed": "Failed to trace function", + "commands.defaultgamemode.success": "The default game mode is now %s", + "commands.difficulty.success": "The difficulty has been set to %s", + "commands.difficulty.query": "The difficulty is %s", + "commands.drop.no_held_items": "Entity can't hold any items", + "commands.drop.no_loot_table": "Entity %s has no loot table", + "commands.drop.success.single": "Dropped %s %s", + "commands.drop.success.single_with_table": "Dropped %s %s from loot table %s", + "commands.drop.success.multiple": "Dropped %s items", + "commands.drop.success.multiple_with_table": "Dropped %s items from loot table %s", + "commands.effect.give.success.single": "Applied effect %s to %s", + "commands.effect.give.success.multiple": "Applied effect %s to %s targets", + "commands.effect.clear.everything.success.single": "Removed every effect from %s", + "commands.effect.clear.everything.success.multiple": "Removed every effect from %s targets", + "commands.effect.clear.specific.success.single": "Removed effect %s from %s", + "commands.effect.clear.specific.success.multiple": "Removed effect %s from %s targets", + "commands.enchant.success.single": "Applied enchantment %s to %s's item", + "commands.enchant.success.multiple": "Applied enchantment %s to %s entities", + "commands.experience.add.points.success.single": "Gave %s experience points to %s", + "commands.experience.add.points.success.multiple": "Gave %s experience points to %s players", + "commands.experience.add.levels.success.single": "Gave %s experience levels to %s", + "commands.experience.add.levels.success.multiple": "Gave %s experience levels to %s players", + "commands.experience.set.points.success.single": "Set %s experience points on %s", + "commands.experience.set.points.success.multiple": "Set %s experience points on %s players", + "commands.experience.set.levels.success.single": "Set %s experience levels on %s", + "commands.experience.set.levels.success.multiple": "Set %s experience levels on %s players", + "commands.experience.query.points": "%s has %s experience points", + "commands.experience.query.levels": "%s has %s experience levels", + "commands.fill.success": "Successfully filled %s blocks", + "commands.function.success.single": "Executed %s commands from function '%s'", + "commands.function.success.multiple": "Executed %s commands from %s functions", + "commands.give.failed.toomanyitems": "Can't give more than %s of %s", + "commands.give.success.single": "Gave %s %s to %s", + "commands.give.success.multiple": "Gave %s %s to %s players", + "commands.playsound.success.single": "Played sound %s to %s", + "commands.playsound.success.multiple": "Played sound %s to %s players", + "commands.publish.success": "Multiplayer game is now hosted on port %s", + "commands.list.players": "There are %s of a max of %s players online: %s", + "commands.list.nameAndId": "%s (%s)", + "commands.kill.success.single": "Killed %s", + "commands.kill.success.multiple": "Killed %s entities", + "commands.kick.success": "Kicked %s: %s", + "commands.message.display.outgoing": "You whisper to %s: %s", + "commands.message.display.incoming": "%s whispers to you: %s", + "commands.op.success": "Made %s a server operator", + "commands.deop.success": "Made %s no longer a server operator", + "commands.ban.success": "Banned %s: %s", + "commands.pardon.success": "Unbanned %s", + "commands.particle.success": "Displaying particle %s", + "commands.perf.started": "Started 10 second performance profiling run (use '/perf stop' to stop early)", + "commands.perf.stopped": "Stopped performance profiling after %s seconds and %s ticks (%s ticks per second)", + "commands.perf.reportSaved": "Created debug report in %s", + "commands.perf.reportFailed": "Failed to create debug report", + "commands.perf.notRunning": "The performance profiler hasn't started", + "commands.perf.alreadyRunning": "The performance profiler is already started", + "commands.jfr.started": "JFR profiling started", + "commands.jfr.start.failed": "Failed to start JFR profiling", + "commands.jfr.stopped": "JFR profiling stopped and dumped to %s", + "commands.jfr.dump.failed": "Failed to dump JFR recording: %s", + "commands.seed.success": "Seed: %s", + "commands.stop.stopping": "Stopping the server", + "commands.time.query": "The time is %s", + "commands.time.set": "Set the time to %s", + "commands.schedule.created.function": "Scheduled function '%s' in %s ticks at gametime %s", + "commands.schedule.created.tag": "Scheduled tag '%s' in %s ticks at gametime %s", + "commands.schedule.cleared.success": "Removed %s schedules with id %s", + "commands.schedule.cleared.failure": "No schedules with id %s", + "commands.schedule.same_tick": "Can't schedule for current tick", + "commands.gamemode.success.self": "Set own game mode to %s", + "commands.gamemode.success.other": "Set %s's game mode to %s", + "commands.gamerule.query": "Gamerule %s is currently set to: %s", + "commands.gamerule.set": "Gamerule %s is now set to: %s", + "commands.save.disabled": "Automatic saving is now disabled", + "commands.save.enabled": "Automatic saving is now enabled", + "commands.save.saving": "Saving the game (this may take a moment!)", + "commands.save.success": "Saved the game", + "commands.setidletimeout.success": "The player idle timeout is now %s minutes", + "commands.banlist.none": "There are no bans", + "commands.banlist.list": "There are %s bans:", + "commands.banlist.entry": "%s was banned by %s: %s", + "commands.bossbar.create.success": "Created custom bossbar %s", + "commands.bossbar.remove.success": "Removed custom bossbar %s", + "commands.bossbar.list.bars.none": "There are no custom bossbars active", + "commands.bossbar.list.bars.some": "There are %s custom bossbars active: %s", + "commands.bossbar.set.players.success.none": "Custom bossbar %s no longer has any players", + "commands.bossbar.set.players.success.some": "Custom bossbar %s now has %s players: %s", + "commands.bossbar.set.name.success": "Custom bossbar %s has been renamed", + "commands.bossbar.set.color.success": "Custom bossbar %s has changed color", + "commands.bossbar.set.style.success": "Custom bossbar %s has changed style", + "commands.bossbar.set.value.success": "Custom bossbar %s has changed value to %s", + "commands.bossbar.set.max.success": "Custom bossbar %s has changed maximum to %s", + "commands.bossbar.set.visible.success.visible": "Custom bossbar %s is now visible", + "commands.bossbar.set.visible.success.hidden": "Custom bossbar %s is now hidden", + "commands.bossbar.get.value": "Custom bossbar %s has a value of %s", + "commands.bossbar.get.max": "Custom bossbar %s has a maximum of %s", + "commands.bossbar.get.visible.visible": "Custom bossbar %s is currently shown", + "commands.bossbar.get.visible.hidden": "Custom bossbar %s is currently hidden", + "commands.bossbar.get.players.none": "Custom bossbar %s has no players currently online", + "commands.bossbar.get.players.some": "Custom bossbar %s has %s players currently online: %s", + "commands.recipe.give.success.single": "Unlocked %s recipes for %s", + "commands.recipe.give.success.multiple": "Unlocked %s recipes for %s players", + "commands.recipe.take.success.single": "Took %s recipes from %s", + "commands.recipe.take.success.multiple": "Took %s recipes from %s players", + "commands.summon.success": "Summoned new %s", + "commands.whitelist.enabled": "Whitelist is now turned on", + "commands.whitelist.disabled": "Whitelist is now turned off", + "commands.whitelist.none": "There are no whitelisted players", + "commands.whitelist.list": "There are %s whitelisted players: %s", + "commands.whitelist.add.success": "Added %s to the whitelist", + "commands.whitelist.remove.success": "Removed %s from the whitelist", + "commands.whitelist.reloaded": "Reloaded the whitelist", + "commands.weather.set.clear": "Set the weather to clear", + "commands.weather.set.rain": "Set the weather to rain", + "commands.weather.set.thunder": "Set the weather to rain & thunder", + "commands.spawnpoint.success.single": "Set spawn point to %s, %s, %s [%s] in %s for %s", + "commands.spawnpoint.success.multiple": "Set spawn point to %s, %s, %s [%s] in %s for %s players", + "commands.stopsound.success.source.sound": "Stopped sound '%s' on source '%s'", + "commands.stopsound.success.source.any": "Stopped all '%s' sounds", + "commands.stopsound.success.sourceless.sound": "Stopped sound '%s'", + "commands.stopsound.success.sourceless.any": "Stopped all sounds", + "commands.setworldspawn.success": "Set the world spawn point to %s, %s, %s [%s]", + "commands.spreadplayers.success.teams": "Spread %s teams around %s, %s with an average distance of %s blocks apart", + "commands.spreadplayers.success.entities": "Spread %s players around %s, %s with an average distance of %s blocks apart", + "commands.setblock.success": "Changed the block at %s, %s, %s", + "commands.banip.success": "Banned IP %s: %s", + "commands.banip.info": "This ban affects %s players: %s", + "commands.pardonip.success": "Unbanned IP %s", + "commands.teleport.success.entity.single": "Teleported %s to %s", + "commands.teleport.success.entity.multiple": "Teleported %s entities to %s", + "commands.teleport.success.location.single": "Teleported %s to %s, %s, %s", + "commands.teleport.success.location.multiple": "Teleported %s entities to %s, %s, %s", + "commands.teleport.invalidPosition": "Invalid position for teleport", + "commands.title.cleared.single": "Cleared titles for %s", + "commands.title.cleared.multiple": "Cleared titles for %s players", + "commands.title.reset.single": "Reset title options for %s", + "commands.title.reset.multiple": "Reset title options for %s players", + "commands.title.show.title.single": "Showing new title for %s", + "commands.title.show.title.multiple": "Showing new title for %s players", + "commands.title.show.subtitle.single": "Showing new subtitle for %s", + "commands.title.show.subtitle.multiple": "Showing new subtitle for %s players", + "commands.title.show.actionbar.single": "Showing new actionbar title for %s", + "commands.title.show.actionbar.multiple": "Showing new actionbar title for %s players", + "commands.title.times.single": "Changed title display times for %s", + "commands.title.times.multiple": "Changed title display times for %s players", + "commands.worldborder.set.grow": "Growing the world border to %s blocks wide over %s seconds", + "commands.worldborder.set.shrink": "Shrinking the world border to %s blocks wide over %s seconds", + "commands.worldborder.set.immediate": "Set the world border to %s blocks wide", + "commands.worldborder.center.success": "Set the center of the world border to %s, %s", + "commands.worldborder.get": "The world border is currently %s blocks wide", + "commands.worldborder.damage.buffer.success": "Set the world border damage buffer to %s blocks", + "commands.worldborder.damage.amount.success": "Set the world border damage to %s per block each second", + "commands.worldborder.warning.time.success": "Set the world border warning time to %s seconds", + "commands.worldborder.warning.distance.success": "Set the world border warning distance to %s blocks", + "commands.tag.add.success.single": "Added tag '%s' to %s", + "commands.tag.add.success.multiple": "Added tag '%s' to %s entities", + "commands.tag.remove.success.single": "Removed tag '%s' from %s", + "commands.tag.remove.success.multiple": "Removed tag '%s' from %s entities", + "commands.tag.list.single.empty": "%s has no tags", + "commands.tag.list.single.success": "%s has %s tags: %s", + "commands.tag.list.multiple.empty": "There are no tags on the %s entities", + "commands.tag.list.multiple.success": "The %s entities have %s total tags: %s", + "commands.team.list.members.empty": "There are no members on team %s", + "commands.team.list.members.success": "Team %s has %s members: %s", + "commands.team.list.teams.empty": "There are no teams", + "commands.team.list.teams.success": "There are %s teams: %s", + "commands.team.add.success": "Created team %s", + "commands.team.remove.success": "Removed team %s", + "commands.team.empty.success": "Removed %s members from team %s", + "commands.team.option.color.success": "Updated the color for team %s to %s", + "commands.team.option.name.success": "Updated the name of team %s", + "commands.team.option.friendlyfire.enabled": "Enabled friendly fire for team %s", + "commands.team.option.friendlyfire.disabled": "Disabled friendly fire for team %s", + "commands.team.option.seeFriendlyInvisibles.enabled": "Team %s can now see invisible teammates", + "commands.team.option.seeFriendlyInvisibles.disabled": "Team %s can no longer see invisible teammates", + "commands.team.option.nametagVisibility.success": "Nametag visibility for team %s is now \"%s\"", + "commands.team.option.deathMessageVisibility.success": "Death message visibility for team %s is now \"%s\"", + "commands.team.option.collisionRule.success": "Collision rule for team %s is now \"%s\"", + "commands.team.option.prefix.success": "Team prefix set to %s", + "commands.team.option.suffix.success": "Team suffix set to %s", + "commands.team.join.success.single": "Added %s to team %s", + "commands.team.join.success.multiple": "Added %s members to team %s", + "commands.team.leave.success.single": "Removed %s from any team", + "commands.team.leave.success.multiple": "Removed %s members from any team", + "commands.trigger.simple.success": "Triggered %s", + "commands.trigger.add.success": "Triggered %s (added %s to value)", + "commands.trigger.set.success": "Triggered %s (set value to %s)", + "commands.scoreboard.objectives.list.empty": "There are no objectives", + "commands.scoreboard.objectives.list.success": "There are %s objectives: %s", + "commands.scoreboard.objectives.add.success": "Created new objective %s", + "commands.scoreboard.objectives.remove.success": "Removed objective %s", + "commands.scoreboard.objectives.display.cleared": "Cleared any objectives in display slot %s", + "commands.scoreboard.objectives.display.set": "Set display slot %s to show objective %s", + "commands.scoreboard.objectives.modify.displayname": "Changed the display name of %s to %s", + "commands.scoreboard.objectives.modify.rendertype": "Changed the render type of objective %s", + "commands.scoreboard.players.list.empty": "There are no tracked entities", + "commands.scoreboard.players.list.success": "There are %s tracked entities: %s", + "commands.scoreboard.players.list.entity.empty": "%s has no scores to show", + "commands.scoreboard.players.list.entity.success": "%s has %s scores:", + "commands.scoreboard.players.list.entity.entry": "%s: %s", + "commands.scoreboard.players.set.success.single": "Set %s for %s to %s", + "commands.scoreboard.players.set.success.multiple": "Set %s for %s entities to %s", + "commands.scoreboard.players.add.success.single": "Added %s to %s for %s (now %s)", + "commands.scoreboard.players.add.success.multiple": "Added %s to %s for %s entities", + "commands.scoreboard.players.remove.success.single": "Removed %s from %s for %s (now %s)", + "commands.scoreboard.players.remove.success.multiple": "Removed %s from %s for %s entities", + "commands.scoreboard.players.reset.all.single": "Reset all scores for %s", + "commands.scoreboard.players.reset.all.multiple": "Reset all scores for %s entities", + "commands.scoreboard.players.reset.specific.single": "Reset %s for %s", + "commands.scoreboard.players.reset.specific.multiple": "Reset %s for %s entities", + "commands.scoreboard.players.enable.success.single": "Enabled trigger %s for %s", + "commands.scoreboard.players.enable.success.multiple": "Enabled trigger %s for %s entities", + "commands.scoreboard.players.operation.success.single": "Set %s for %s to %s", + "commands.scoreboard.players.operation.success.multiple": "Updated %s for %s entities", + "commands.scoreboard.players.get.success": "%s has %s %s", + "commands.reload.success": "Reloading!", + "commands.reload.failure": "Reload failed; keeping old data", + "commands.data.entity.modified": "Modified entity data of %s", + "commands.data.entity.query": "%s has the following entity data: %s", + "commands.data.entity.get": "%s on %s after scale factor of %s is %s", + "commands.data.block.modified": "Modified block data of %s, %s, %s", + "commands.data.block.query": "%s, %s, %s has the following block data: %s", + "commands.data.block.get": "%s on block %s, %s, %s after scale factor of %s is %s", + "commands.data.storage.modified": "Modified storage %s", + "commands.data.storage.query": "Storage %s has the following contents: %s", + "commands.data.storage.get": "%s in storage %s after scale factor of %s is %s", + "commands.datapack.list.enabled.success": "There are %s data packs enabled: %s", + "commands.datapack.list.enabled.none": "There are no data packs enabled", + "commands.datapack.list.available.success": "There are %s data packs available: %s", + "commands.datapack.list.available.none": "There are no more data packs available", + "commands.datapack.modify.enable": "Enabling data pack %s", + "commands.datapack.modify.disable": "Disabling data pack %s", + "commands.spectate.success.stopped": "No longer spectating an entity", + "commands.spectate.success.started": "Now spectating %s", + "commands.spectate.not_spectator": "%s is not in spectator mode", + "commands.spectate.self": "Cannot spectate yourself", + "commands.item.target.not_a_container": "Target position %s, %s, %s is not a container", + "commands.item.source.not_a_container": "Source position %s, %s, %s is not a container", + "commands.item.target.no_such_slot": "The target does not have slot %s", + "commands.item.source.no_such_slot": "The source does not have slot %s", + "commands.item.target.no_changes": "No targets accepted item into slot %s", + "commands.item.target.no_changed.known_item": "No targets accepted item %s into slot %s", + "commands.item.block.set.success": "Replaced a slot at %s, %s, %s with %s", + "commands.item.entity.set.success.single": "Replaced a slot on %s with %s", + "commands.item.entity.set.success.multiple": "Replaced a slot on %s entities with %s", + "argument.range.empty": "Expected value or range of values", + "argument.range.ints": "Only whole numbers allowed, not decimals", + "argument.range.swapped": "Min cannot be bigger than max", + "permissions.requires.player": "A player is required to run this command here", + "permissions.requires.entity": "An entity is required to run this command here", + "argument.angle.incomplete": "Incomplete (expected 1 angle)", + "argument.angle.invalid": "Invalid angle", + "argument.entity.toomany": "Only one entity is allowed, but the provided selector allows more than one", + "argument.player.toomany": "Only one player is allowed, but the provided selector allows more than one", + "argument.player.entities": "Only players may be affected by this command, but the provided selector includes entities", + "argument.entity.notfound.entity": "No entity was found", + "argument.entity.notfound.player": "No player was found", + "argument.player.unknown": "That player does not exist", + "arguments.nbtpath.node.invalid": "Invalid NBT path element", + "arguments.nbtpath.too_deep": "Resulting NBT too deeply nested", + "arguments.nbtpath.too_large": "Resulting NBT too large", + "arguments.nbtpath.nothing_found": "Found no elements matching %s", + "arguments.operation.invalid": "Invalid operation", + "arguments.operation.div0": "Cannot divide by zero", + "argument.scoreHolder.empty": "No relevant score holders could be found", + "argument.block.tag.disallowed": "Tags aren't allowed here, only actual blocks", + "argument.block.property.unclosed": "Expected closing ] for block state properties", + "argument.pos.unloaded": "That position is not loaded", + "argument.pos.outofworld": "That position is out of this world!", + "argument.pos.outofbounds": "That position is outside the allowed boundaries.", + "argument.rotation.incomplete": "Incomplete (expected 2 coordinates)", + "arguments.swizzle.invalid": "Invalid swizzle, expected combination of 'x', 'y' and 'z'", + "argument.pos2d.incomplete": "Incomplete (expected 2 coordinates)", + "argument.pos3d.incomplete": "Incomplete (expected 3 coordinates)", + "argument.pos.mixed": "Cannot mix world & local coordinates (everything must either use ^ or not)", + "argument.pos.missing.double": "Expected a coordinate", + "argument.pos.missing.int": "Expected a block position", + "argument.item.tag.disallowed": "Tags aren't allowed here, only actual items", + "argument.entity.invalid": "Invalid name or UUID", + "argument.entity.selector.missing": "Missing selector type", + "argument.entity.selector.not_allowed": "Selector not allowed", + "argument.entity.options.unterminated": "Expected end of options", + "argument.entity.options.distance.negative": "Distance cannot be negative", + "argument.entity.options.level.negative": "Level shouldn't be negative", + "argument.entity.options.limit.toosmall": "Limit must be at least 1", + "argument.nbt.trailing": "Unexpected trailing data", + "argument.nbt.expected.key": "Expected key", + "argument.nbt.expected.value": "Expected value", + "argument.id.invalid": "Invalid ID", + "argument.time.invalid_unit": "Invalid unit", + "argument.time.invalid_tick_count": "Tick count must be non-negative", + "argument.enum.invalid": "Invalid value \"%s\"", + "commands.banip.invalid": "Invalid IP address or unknown player", + "commands.banip.failed": "Nothing changed. That IP is already banned", + "commands.ban.failed": "Nothing changed. The player is already banned", + "commands.bossbar.set.players.unchanged": "Nothing changed. Those players are already on the bossbar with nobody to add or remove", + "commands.bossbar.set.name.unchanged": "Nothing changed. That's already the name of this bossbar", + "commands.bossbar.set.color.unchanged": "Nothing changed. That's already the color of this bossbar", + "commands.bossbar.set.style.unchanged": "Nothing changed. That's already the style of this bossbar", + "commands.bossbar.set.value.unchanged": "Nothing changed. That's already the value of this bossbar", + "commands.bossbar.set.max.unchanged": "Nothing changed. That's already the max of this bossbar", + "commands.bossbar.set.visibility.unchanged.hidden": "Nothing changed. The bossbar is already hidden", + "commands.bossbar.set.visibility.unchanged.visible": "Nothing changed. The bossbar is already visible", + "commands.clone.overlap": "The source and destination areas cannot overlap", + "commands.clone.failed": "No blocks were cloned", + "commands.deop.failed": "Nothing changed. The player is not an operator", + "commands.effect.give.failed": "Unable to apply this effect (target is either immune to effects, or has something stronger)", + "commands.effect.clear.everything.failed": "Target has no effects to remove", + "commands.effect.clear.specific.failed": "Target doesn't have the requested effect", + "commands.enchant.failed": "Nothing changed. Targets either have no item in their hands or the enchantment could not be applied", + "commands.experience.set.points.invalid": "Cannot set experience points above the maximum points for the player's current level", + "commands.fill.failed": "No blocks were filled", + "argument.gamemode.invalid": "Unknown gamemode: %s", + "commands.help.failed": "Unknown command or insufficient permissions", + "commands.locate.structure.success": "The nearest %s is at %s (%s blocks away)", + "commands.locate.structure.not_found": "Could not find a structure of type \"%s\" nearby", + "commands.locate.structure.invalid": "There is no structure with type \"%s\"", + "commands.locate.biome.success": "The nearest %s is at %s (%s blocks away)", + "commands.locate.biome.not_found": "Could not find a biome of type \"%s\" within reasonable distance", + "commands.locate.poi.success": "The nearest %s is at %s (%s blocks away)", + "commands.locate.poi.not_found": "Could not find a point of interest of type \"%s\" within reasonable distance", + "commands.op.failed": "Nothing changed. The player already is an operator", + "commands.pardon.failed": "Nothing changed. The player isn't banned", + "commands.pardonip.invalid": "Invalid IP address", + "commands.pardonip.failed": "Nothing changed. That IP isn't banned", + "commands.particle.failed": "The particle was not visible for anybody", + "commands.place.feature.failed": "Failed to place feature", + "commands.place.feature.invalid": "There is no feature with type \"%s\"", + "commands.place.feature.success": "Placed \"%s\" at %s, %s, %s", + "commands.place.jigsaw.failed": "Failed to generate jigsaw", + "commands.place.jigsaw.invalid": "There is no template pool with type \"%s\"", + "commands.place.jigsaw.success": "Generated jigsaw at %s, %s, %s", + "commands.place.structure.failed": "Failed to place structure", + "commands.place.structure.invalid": "There is no structure with type \"%s\"", + "commands.place.structure.success": "Generated structure \"%s\" at %s, %s, %s", + "commands.place.template.failed": "Failed to place template", + "commands.place.template.invalid": "There is no template with id \"%s\"", + "commands.place.template.success": "Loaded template \"%s\" at %s, %s, %s", + "commands.playsound.failed": "The sound is too far away to be heard", + "commands.recipe.give.failed": "No new recipes were learned", + "commands.recipe.take.failed": "No recipes could be forgotten", + "commands.save.failed": "Unable to save the game (is there enough disk space?)", + "commands.save.alreadyOff": "Saving is already turned off", + "commands.save.alreadyOn": "Saving is already turned on", + "commands.scoreboard.objectives.add.duplicate": "An objective already exists by that name", + "commands.scoreboard.objectives.display.alreadyEmpty": "Nothing changed. That display slot is already empty", + "commands.scoreboard.objectives.display.alreadySet": "Nothing changed. That display slot is already showing that objective", + "commands.scoreboard.players.enable.failed": "Nothing changed. That trigger is already enabled", + "commands.scoreboard.players.enable.invalid": "Enable only works on trigger-objectives", + "commands.setblock.failed": "Could not set the block", + "commands.summon.failed": "Unable to summon entity", + "commands.summon.failed.uuid": "Unable to summon entity due to duplicate UUIDs", + "commands.summon.invalidPosition": "Invalid position for summon", + "commands.tag.add.failed": "Target either already has the tag or has too many tags", + "commands.tag.remove.failed": "Target does not have this tag", + "commands.team.add.duplicate": "A team already exists by that name", + "commands.team.empty.unchanged": "Nothing changed. That team is already empty", + "commands.team.option.color.unchanged": "Nothing changed. That team already has that color", + "commands.team.option.name.unchanged": "Nothing changed. That team already has that name", + "commands.team.option.friendlyfire.alreadyEnabled": "Nothing changed. Friendly fire is already enabled for that team", + "commands.team.option.friendlyfire.alreadyDisabled": "Nothing changed. Friendly fire is already disabled for that team", + "commands.team.option.seeFriendlyInvisibles.alreadyEnabled": "Nothing changed. That team can already see invisible teammates", + "commands.team.option.seeFriendlyInvisibles.alreadyDisabled": "Nothing changed. That team already can't see invisible teammates", + "commands.team.option.nametagVisibility.unchanged": "Nothing changed. Nametag visibility is already that value", + "commands.team.option.deathMessageVisibility.unchanged": "Nothing changed. Death message visibility is already that value", + "commands.team.option.collisionRule.unchanged": "Nothing changed. Collision rule is already that value", + "commands.trigger.failed.unprimed": "You cannot trigger this objective yet", + "commands.trigger.failed.invalid": "You can only trigger objectives that are 'trigger' type", + "commands.whitelist.alreadyOn": "Whitelist is already turned on", + "commands.whitelist.alreadyOff": "Whitelist is already turned off", + "commands.whitelist.add.failed": "Player is already whitelisted", + "commands.whitelist.remove.failed": "Player is not whitelisted", + "commands.worldborder.center.failed": "Nothing changed. The world border is already centered there", + "commands.worldborder.set.failed.nochange": "Nothing changed. The world border is already that size", + "commands.worldborder.set.failed.small": "World border cannot be smaller than 1 block wide", + "commands.worldborder.set.failed.big": "World border cannot be bigger than %s blocks wide", + "commands.worldborder.set.failed.far": "World border cannot be further out than %s blocks", + "commands.worldborder.warning.time.failed": "Nothing changed. The world border warning is already that amount of time", + "commands.worldborder.warning.distance.failed": "Nothing changed. The world border warning is already that distance", + "commands.worldborder.damage.buffer.failed": "Nothing changed. The world border damage buffer is already that distance", + "commands.worldborder.damage.amount.failed": "Nothing changed. The world border damage is already that amount", + "commands.data.block.invalid": "The target block is not a block entity", + "commands.data.merge.failed": "Nothing changed. The specified properties already have these values", + "commands.data.modify.expected_list": "Expected list, got: %s", + "commands.data.modify.expected_object": "Expected object, got: %s", + "commands.data.modify.invalid_index": "Invalid list index: %s", + "commands.data.get.multiple": "This argument accepts a single NBT value", + "commands.data.entity.invalid": "Unable to modify player data", + "commands.teammsg.failed.noteam": "You must be on a team to message your team", + "argument.color.invalid": "Unknown color '%s'", + "argument.dimension.invalid": "Unknown dimension '%s'", + "argument.component.invalid": "Invalid chat component: %s", + "argument.anchor.invalid": "Invalid entity anchor position %s", + "lectern.take_book": "Take Book", + "arguments.objective.notFound": "Unknown scoreboard objective '%s'", + "arguments.objective.readonly": "Scoreboard objective '%s' is read-only", + "argument.criteria.invalid": "Unknown criterion '%s'", + "particle.notFound": "Unknown particle: %s", + "argument.id.unknown": "Unknown ID: %s", + "advancement.advancementNotFound": "Unknown advancement: %s", + "recipe.notFound": "Unknown recipe: %s", + "entity.not_summonable": "Can't summon entity of type %s", + "predicate.unknown": "Unknown predicate: %s", + "item_modifier.unknown": "Unknown item modifier: %s", + "argument.scoreboardDisplaySlot.invalid": "Unknown display slot '%s'", + "slot.unknown": "Unknown slot '%s'", + "team.notFound": "Unknown team '%s'", + "arguments.block.tag.unknown": "Unknown block tag '%s'", + "argument.block.id.invalid": "Unknown block type '%s'", + "argument.block.property.unknown": "Block %s does not have property '%s'", + "argument.block.property.duplicate": "Property '%s' can only be set once for block %s", + "argument.block.property.invalid": "Block %s does not accept '%s' for %s property", + "argument.block.property.novalue": "Expected value for property '%s' on block %s", + "arguments.function.tag.unknown": "Unknown function tag '%s'", + "arguments.function.unknown": "Unknown function %s", + "arguments.item.overstacked": "%s can only stack up to %s", + "argument.item.id.invalid": "Unknown item '%s'", + "arguments.item.tag.unknown": "Unknown item tag '%s'", + "argument.entity.selector.unknown": "Unknown selector type '%s'", + "argument.entity.options.valueless": "Expected value for option '%s'", + "argument.entity.options.unknown": "Unknown option '%s'", + "argument.entity.options.inapplicable": "Option '%s' isn't applicable here", + "argument.entity.options.sort.irreversible": "Invalid or unknown sort type '%s'", + "argument.entity.options.mode.invalid": "Invalid or unknown game mode '%s'", + "argument.entity.options.type.invalid": "Invalid or unknown entity type '%s'", + "argument.nbt.list.mixed": "Can't insert %s into list of %s", + "argument.nbt.array.mixed": "Can't insert %s into %s", + "argument.nbt.array.invalid": "Invalid array type '%s'", + "commands.bossbar.create.failed": "A bossbar already exists with the ID '%s'", + "commands.bossbar.unknown": "No bossbar exists with the ID '%s'", + "clear.failed.single": "No items were found on player %s", + "clear.failed.multiple": "No items were found on %s players", + "commands.clone.toobig": "Too many blocks in the specified area (maximum %s, specified %s)", + "commands.datapack.unknown": "Unknown data pack '%s'", + "commands.datapack.enable.failed": "Pack '%s' is already enabled!", + "commands.datapack.enable.failed.no_flags": "Pack '%s' cannot be enabled, since required flags are not enabled in this world: %s!", + "commands.datapack.disable.failed": "Pack '%s' is not enabled!", + "commands.difficulty.failure": "The difficulty did not change; it is already set to %s", + "commands.enchant.failed.entity": "%s is not a valid entity for this command", + "commands.enchant.failed.itemless": "%s is not holding any item", + "commands.enchant.failed.incompatible": "%s cannot support that enchantment", + "commands.enchant.failed.level": "%s is higher than the maximum level of %s supported by that enchantment", + "commands.execute.blocks.toobig": "Too many blocks in the specified area (maximum %s, specified %s)", + "commands.execute.conditional.pass": "Test passed", + "commands.execute.conditional.pass_count": "Test passed, count: %s", + "commands.execute.conditional.fail": "Test failed", + "commands.execute.conditional.fail_count": "Test failed, count: %s", + "commands.fill.toobig": "Too many blocks in the specified area (maximum %s, specified %s)", + "commands.fillbiome.toobig": "Too many blocks in the specified volume (maximum %s, specified %s)", + "commands.fillbiome.success": "Biomes set between %s, %s, %s and %s, %s, %s", + "commands.fillbiome.success.count": "%s biome entries set between %s, %s, %s and %s, %s, %s", + "commands.publish.alreadyPublished": "Multiplayer game is already hosted on port %s", + "commands.scoreboard.players.get.null": "Can't get value of %s for %s; none is set", + "commands.spreadplayers.failed.teams": "Could not spread %s teams around %s, %s (too many entities for space - try using spread of at most %s)", + "commands.spreadplayers.failed.entities": "Could not spread %s entities around %s, %s (too many entities for space - try using spread of at most %s)", + "commands.spreadplayers.failed.invalid.height": "Invalid maxHeight %s; expected higher than world minimum %s", + "commands.data.get.invalid": "Can't get %s; only numeric tags are allowed", + "commands.data.get.unknown": "Can't get %s; tag doesn't exist", + "argument.double.low": "Double must not be less than %s, found %s", + "argument.double.big": "Double must not be more than %s, found %s", + "argument.float.low": "Float must not be less than %s, found %s", + "argument.float.big": "Float must not be more than %s, found %s", + "argument.integer.low": "Integer must not be less than %s, found %s", + "argument.integer.big": "Integer must not be more than %s, found %s", + "argument.long.low": "Long must not be less than %s, found %s", + "argument.long.big": "Long must not be more than %s, found %s", + "argument.literal.incorrect": "Expected literal %s", + "parsing.quote.expected.start": "Expected quote to start a string", + "parsing.quote.expected.end": "Unclosed quoted string", + "parsing.quote.escape": "Invalid escape sequence '\\%s' in quoted string", + "parsing.bool.invalid": "Invalid boolean, expected 'true' or 'false' but found '%s'", + "parsing.int.invalid": "Invalid integer '%s'", + "parsing.int.expected": "Expected integer", + "parsing.long.invalid": "Invalid long '%s'", + "parsing.long.expected": "Expected long", + "command.exception": "Could not parse command: %s", + "parsing.double.invalid": "Invalid double '%s'", + "parsing.double.expected": "Expected double", + "parsing.float.invalid": "Invalid float '%s'", + "parsing.float.expected": "Expected float", + "parsing.bool.expected": "Expected boolean", + "parsing.expected": "Expected '%s'", + "command.unknown.command": "Unknown or incomplete command, see below for error", + "command.unknown.argument": "Incorrect argument for command", + "command.expected.separator": "Expected whitespace to end one argument, but found trailing data", + "biome.minecraft.badlands": "Badlands", + "biome.minecraft.bamboo_jungle": "Bamboo Jungle", + "biome.minecraft.basalt_deltas": "Basalt Deltas", + "biome.minecraft.beach": "Beach", + "biome.minecraft.birch_forest": "Birch Forest", + "biome.minecraft.cold_ocean": "Cold Ocean", + "biome.minecraft.crimson_forest": "Crimson Forest", + "biome.minecraft.dark_forest": "Dark Forest", + "biome.minecraft.deep_cold_ocean": "Deep Cold Ocean", + "biome.minecraft.deep_dark": "Deep Dark", + "biome.minecraft.deep_frozen_ocean": "Deep Frozen Ocean", + "biome.minecraft.deep_lukewarm_ocean": "Deep Lukewarm Ocean", + "biome.minecraft.deep_ocean": "Deep Ocean", + "biome.minecraft.desert": "Desert", + "biome.minecraft.dripstone_caves": "Dripstone Caves", + "biome.minecraft.old_growth_birch_forest": "Old Growth Birch Forest", + "biome.minecraft.old_growth_pine_taiga": "Old Growth Pine Taiga", + "biome.minecraft.old_growth_spruce_taiga": "Old Growth Spruce Taiga", + "biome.minecraft.end_barrens": "End Barrens", + "biome.minecraft.end_highlands": "End Highlands", + "biome.minecraft.end_midlands": "End Midlands", + "biome.minecraft.eroded_badlands": "Eroded Badlands", + "biome.minecraft.flower_forest": "Flower Forest", + "biome.minecraft.forest": "Forest", + "biome.minecraft.frozen_ocean": "Frozen Ocean", + "biome.minecraft.frozen_peaks": "Frozen Peaks", + "biome.minecraft.frozen_river": "Frozen River", + "biome.minecraft.grove": "Grove", + "biome.minecraft.ice_spikes": "Ice Spikes", + "biome.minecraft.jagged_peaks": "Jagged Peaks", + "biome.minecraft.jungle": "Jungle", + "biome.minecraft.lukewarm_ocean": "Lukewarm Ocean", + "biome.minecraft.lush_caves": "Lush Caves", + "biome.minecraft.mangrove_swamp": "Mangrove Swamp", + "biome.minecraft.meadow": "Meadow", + "biome.minecraft.mushroom_fields": "Mushroom Fields", + "biome.minecraft.nether_wastes": "Nether Wastes", + "biome.minecraft.ocean": "Ocean", + "biome.minecraft.plains": "Plains", + "biome.minecraft.river": "River", + "biome.minecraft.savanna_plateau": "Savanna Plateau", + "biome.minecraft.savanna": "Savanna", + "biome.minecraft.small_end_islands": "Small End Islands", + "biome.minecraft.snowy_beach": "Snowy Beach", + "biome.minecraft.snowy_plains": "Snowy Plains", + "biome.minecraft.snowy_slopes": "Snowy Slopes", + "biome.minecraft.snowy_taiga": "Snowy Taiga", + "biome.minecraft.soul_sand_valley": "Soul Sand Valley", + "biome.minecraft.sparse_jungle": "Sparse Jungle", + "biome.minecraft.stony_peaks": "Stony Peaks", + "biome.minecraft.stony_shore": "Stony Shore", + "biome.minecraft.sunflower_plains": "Sunflower Plains", + "biome.minecraft.swamp": "Swamp", + "biome.minecraft.taiga": "Taiga", + "biome.minecraft.the_end": "The End", + "biome.minecraft.the_void": "The Void", + "biome.minecraft.warm_ocean": "Warm Ocean", + "biome.minecraft.warped_forest": "Warped Forest", + "biome.minecraft.windswept_forest": "Windswept Forest", + "biome.minecraft.windswept_gravelly_hills": "Windswept Gravelly Hills", + "biome.minecraft.windswept_hills": "Windswept Hills", + "biome.minecraft.windswept_savanna": "Windswept Savanna", + "biome.minecraft.wooded_badlands": "Wooded Badlands", + "realms.missing.module.error.text": "Realms could not be opened right now, please try again later", + "realms.missing.snapshot.error.text": "Realms is currently not supported in snapshots", + "color.minecraft.white": "White", + "color.minecraft.orange": "Orange", + "color.minecraft.magenta": "Magenta", + "color.minecraft.light_blue": "Light Blue", + "color.minecraft.yellow": "Yellow", + "color.minecraft.lime": "Lime", + "color.minecraft.pink": "Pink", + "color.minecraft.gray": "Gray", + "color.minecraft.light_gray": "Light Gray", + "color.minecraft.cyan": "Cyan", + "color.minecraft.purple": "Purple", + "color.minecraft.blue": "Blue", + "color.minecraft.brown": "Brown", + "color.minecraft.green": "Green", + "color.minecraft.red": "Red", + "color.minecraft.black": "Black", + "title.singleplayer": "Singleplayer", + "title.multiplayer.realms": "Multiplayer (Realms)", + "title.multiplayer.lan": "Multiplayer (LAN)", + "title.multiplayer.other": "Multiplayer (3rd-party Server)", + "gamerule.announceAdvancements": "Announce advancements", + "gamerule.commandBlockOutput": "Broadcast command block output", + "gamerule.disableElytraMovementCheck": "Disable elytra movement check", + "gamerule.disableRaids": "Disable raids", + "gamerule.doDaylightCycle": "Advance time of day", + "gamerule.doEntityDrops": "Drop entity equipment", + "gamerule.doEntityDrops.description": "Controls drops from minecarts (including inventories), item frames, boats, etc.", + "gamerule.doFireTick": "Update fire", + "gamerule.doImmediateRespawn": "Respawn immediately", + "gamerule.doInsomnia": "Spawn phantoms", + "gamerule.doLimitedCrafting": "Require recipe for crafting", + "gamerule.doLimitedCrafting.description": "If enabled, players will be able to craft only unlocked recipes", + "gamerule.doMobLoot": "Drop mob loot", + "gamerule.doMobLoot.description": "Controls resource drops from mobs, including experience orbs", + "gamerule.doMobSpawning": "Spawn mobs", + "gamerule.doMobSpawning.description": "Some entities might have separate rules", + "gamerule.doPatrolSpawning": "Spawn pillager patrols", + "gamerule.doTileDrops": "Drop blocks", + "gamerule.doTileDrops.description": "Controls resource drops from blocks, including experience orbs", + "gamerule.doTraderSpawning": "Spawn Wandering Traders", + "gamerule.doWardenSpawning": "Spawn Wardens", + "gamerule.doWeatherCycle": "Update weather", + "gamerule.drowningDamage": "Deal drowning damage", + "gamerule.fallDamage": "Deal fall damage", + "gamerule.fireDamage": "Deal fire damage", + "gamerule.freezeDamage": "Deal freeze damage", + "gamerule.forgiveDeadPlayers": "Forgive dead players", + "gamerule.forgiveDeadPlayers.description": "Angered neutral mobs stop being angry when the targeted player dies nearby.", + "gamerule.keepInventory": "Keep inventory after death", + "gamerule.logAdminCommands": "Broadcast admin commands", + "gamerule.maxCommandChainLength": "Command chain size limit", + "gamerule.maxCommandChainLength.description": "Applies to command block chains and functions", + "gamerule.maxEntityCramming": "Entity cramming threshold", + "gamerule.mobGriefing": "Allow destructive mob actions", + "gamerule.naturalRegeneration": "Regenerate health", + "gamerule.randomTickSpeed": "Random tick speed rate", + "gamerule.reducedDebugInfo": "Reduce debug info", + "gamerule.reducedDebugInfo.description": "Limits contents of debug screen", + "gamerule.sendCommandFeedback": "Send command feedback", + "gamerule.showDeathMessages": "Show death messages", + "gamerule.playersSleepingPercentage": "Sleep percentage", + "gamerule.playersSleepingPercentage.description": "The percentage of players who must be sleeping to skip the night.", + "gamerule.spawnRadius": "Respawn location radius", + "gamerule.spectatorsGenerateChunks": "Allow spectators to generate terrain", + "gamerule.universalAnger": "Universal anger", + "gamerule.universalAnger.description": "Angered neutral mobs attack any nearby player, not just the player that angered them. Works best if forgiveDeadPlayers is disabled.", + "gamerule.blockExplosionDropDecay": "In block interaction explosions, some blocks won't drop their loot", + "gamerule.blockExplosionDropDecay.description": "Some of the drops from blocks destroyed by explosions caused by block interactions are lost in the explosion.", + "gamerule.mobExplosionDropDecay": "In mob explosions, some blocks won't drop their loot", + "gamerule.mobExplosionDropDecay.description": "Some of the drops from blocks destroyed by explosions caused by mobs are lost in the explosion.", + "gamerule.tntExplosionDropDecay": "In TNT explosions, some blocks won't drop their loot", + "gamerule.tntExplosionDropDecay.description": "Some of the drops from blocks destroyed by explosions caused by TNT are lost in the explosion.", + "gamerule.snowAccumulationHeight": "Snow accumulation height", + "gamerule.snowAccumulationHeight.description": "When it snows, layers of snow form on the ground up to at most this number of layers.", + "gamerule.waterSourceConversion": "Water converts to source", + "gamerule.waterSourceConversion.description": "When flowing water is surrounded on two sides by water sources it converts into a source.", + "gamerule.lavaSourceConversion": "Lava converts to source", + "gamerule.lavaSourceConversion.description": "When flowing lava is surrounded on two sides by lava sources it converts into a source.", + "gamerule.globalSoundEvents": "Global sound events", + "gamerule.globalSoundEvents.description": "When certain game events happen, like a boss spawning, the sound is heard everywhere.", + "gamerule.category.chat": "Chat", + "gamerule.category.spawning": "Spawning", + "gamerule.category.updates": "World Updates", + "gamerule.category.drops": "Drops", + "gamerule.category.mobs": "Mobs", + "gamerule.category.player": "Player", + "gamerule.category.misc": "Miscellaneous", + "pack.source.builtin": "built-in", + "pack.source.feature": "feature", + "pack.source.world": "world", + "pack.source.local": "local", + "pack.source.server": "server", + "mirror.none": "|", + "mirror.left_right": "\u2190 \u2192", + "mirror.front_back": "\u2191 \u2193", + "sleep.not_possible": "No amount of rest can pass this night", + "sleep.players_sleeping": "%s/%s players sleeping", + "sleep.skipping_night": "Sleeping through this night", + "compliance.playtime.greaterThan24Hours": "You've been playing for greater than 24 hours", + "compliance.playtime.message": "Excessive gaming may interfere with normal daily life", + "compliance.playtime.hours": "You've been playing for %s hour(s)", + "outOfMemory.title": "Out of memory!", + "outOfMemory.message": "Minecraft has run out of memory.\n\nThis could be caused by a bug in the game or by the Java Virtual Machine not being allocated enough memory.\n\nTo prevent level corruption, the current game has quit. We've tried to free up enough memory to let you go back to the main menu and back to playing, but this may not have worked.\n\nPlease restart the game if you see this message again.", + "mco.gui.ok": "Ok", + "mco.gui.button": "Button", + "mco.terms.buttons.agree": "Agree", + "mco.terms.buttons.disagree": "Don't agree", + "mco.terms.title": "Realms Terms of Service", + "mco.terms.sentence.1": "I agree to the Minecraft Realms", + "mco.terms.sentence.2": "Terms of Service", + "mco.selectServer.play": "Play", + "mco.selectServer.configure": "Configure", + "mco.selectServer.configureRealm": "Configure realm", + "mco.selectServer.leave": "Leave realm", + "mco.selectServer.create": "Create realm", + "mco.selectServer.purchase": "Add Realm", + "mco.selectServer.buy": "Buy a realm!", + "mco.selectServer.trial": "Get a trial!", + "mco.selectServer.close": "Close", + "mco.selectServer.expiredTrial": "Your trial has ended", + "mco.selectServer.expiredList": "Your subscription has expired", + "mco.selectServer.expiredSubscribe": "Subscribe", + "mco.selectServer.expiredRenew": "Renew", + "mco.selectServer.expired": "Expired realm", + "mco.selectServer.open": "Open realm", + "mco.selectServer.closed": "Closed realm", + "mco.selectServer.openserver": "Open realm", + "mco.selectServer.closeserver": "Close realm", + "mco.selectServer.minigame": "Minigame:", + "mco.selectServer.uninitialized": "Click to start your new realm!", + "mco.selectServer.expires.days": "Expires in %s days", + "mco.selectServer.expires.day": "Expires in a day", + "mco.selectServer.expires.soon": "Expires soon", + "mco.selectServer.note": "Note:", + "mco.selectServer.mapOnlySupportedForVersion": "This map is unsupported in %s", + "mco.selectServer.minigameNotSupportedInVersion": "Can't play this minigame in %s", + "mco.selectServer.popup": "Realms is a safe, simple way to enjoy an online Minecraft world with up to ten friends at a time. It supports loads of minigames and plenty of custom worlds! Only the owner of the realm needs to pay.", + "mco.configure.world.settings.title": "Settings", + "mco.configure.world.title": "Configure realm:", + "mco.configure.worlds.title": "Worlds", + "mco.configure.world.name": "Realm name", + "mco.configure.world.description": "Realm description", + "mco.configure.world.location": "Location", + "mco.configure.world.invited": "Invited", + "mco.configure.world.invite.narration": "You have %s new invites", + "mco.configure.world.buttons.edit": "Settings", + "mco.configure.world.buttons.done": "Done", + "mco.configure.world.buttons.delete": "Delete", + "mco.configure.world.buttons.open": "Open realm", + "mco.configure.world.buttons.close": "Close realm", + "mco.configure.world.buttons.invite": "Invite player", + "mco.configure.world.buttons.activity": "Player activity", + "mco.configure.world.activityfeed.disabled": "Player feed temporarily disabled", + "mco.configure.world.buttons.moreoptions": "More options", + "mco.configure.world.invite.profile.name": "Name", + "mco.configure.world.uninvite.question": "Are you sure that you want to uninvite", + "mco.configure.world.status": "Status", + "mco.configure.world.invites.ops.tooltip": "Operator", + "mco.configure.world.invites.normal.tooltip": "Normal user", + "mco.configure.world.invites.remove.tooltip": "Remove", + "mco.configure.world.closing": "Closing the realm...", + "mco.configure.world.opening": "Opening the realm...", + "mco.configure.world.buttons.players": "Players", + "mco.configure.world.buttons.settings": "Settings", + "mco.configure.world.buttons.subscription": "Subscription", + "mco.configure.world.buttons.options": "World options", + "mco.configure.world.backup": "World backups", + "mco.configure.world.buttons.resetworld": "Reset world", + "mco.configure.world.buttons.switchminigame": "Switch minigame", + "mco.configure.current.minigame": "Current", + "mco.configure.world.subscription.title": "Your subscription", + "mco.configure.world.subscription.timeleft": "Time left", + "mco.configure.world.subscription.unknown": "Unknown", + "mco.configure.world.subscription.recurring.daysleft": "Renewed automatically in", + "mco.configure.world.subscription.less_than_a_day": "Less than a day", + "mco.configure.world.subscription.expired": "Expired", + "mco.configure.world.subscription.start": "Start date", + "mco.configure.world.subscription.extend": "Extend subscription", + "mco.configure.world.subscription.day": "day", + "mco.configure.world.subscription.month": "month", + "mco.configure.world.subscription.days": "days", + "mco.configure.world.subscription.months": "months", + "mco.configure.world.pvp": "PVP", + "mco.configure.world.spawn_toggle.title": "Warning!", + "mco.configure.world.spawn_toggle.message": "Turning this option off will REMOVE ALL existing entities of that type", + "mco.configure.world.spawn_toggle.message.npc": "Turning this option off will REMOVE ALL existing entities of that type, like Villagers", + "mco.configure.world.spawnAnimals": "Spawn animals", + "mco.configure.world.spawnNPCs": "Spawn NPCs", + "mco.configure.world.spawnMonsters": "Spawn monsters", + "mco.configure.world.spawnProtection": "Spawn protection", + "mco.configure.world.commandBlocks": "Command blocks", + "mco.configure.world.forceGameMode": "Force game mode", + "mco.configure.world.slot": "World %s", + "mco.configure.world.slot.empty": "Empty", + "mco.create.world.wait": "Creating the realm...", + "mco.create.world.error": "You must enter a name!", + "mco.create.world.subtitle": "Optionally, select what world to put on your new realm", + "mco.create.world.skip": "Skip", + "mco.reset.world.title": "Reset world", + "mco.reset.world.warning": "This will replace the current world of your realm", + "mco.reset.world.seed": "Seed (Optional)", + "mco.reset.world.resetting.screen.title": "Resetting world...", + "mco.reset.world.generate": "New world", + "mco.reset.world.upload": "Upload world", + "mco.reset.world.adventure": "Adventures", + "mco.reset.world.template": "World templates", + "mco.reset.world.experience": "Experiences", + "mco.reset.world.inspiration": "Inspiration", + "mco.minigame.world.title": "Switch realm to minigame", + "mco.minigame.world.info.line1": "This will temporarily replace your world with a minigame!", + "mco.minigame.world.info.line2": "You can later return to your original world without losing anything.", + "mco.minigame.world.selected": "Selected minigame:", + "mco.minigame.world.noSelection": "Please make a selection", + "mco.minigame.world.startButton": "Switch", + "mco.minigame.world.starting.screen.title": "Starting minigame...", + "mco.minigame.world.changeButton": "Select another minigame", + "mco.minigame.world.stopButton": "End minigame", + "mco.minigame.world.switch.title": "Switch minigame", + "mco.minigame.world.switch.new": "Select another minigame?", + "mco.minigame.world.restore.question.line1": "The minigame will end and your realm will be restored.", + "mco.minigame.world.restore.question.line2": "Are you sure you want to continue?", + "mco.minigame.world.restore": "Ending minigame...", + "mco.configure.world.slot.tooltip": "Switch to world", + "mco.configure.world.slot.tooltip.minigame": "Switch to minigame", + "mco.configure.world.slot.tooltip.active": "Join", + "mco.configure.world.close.question.line1": "Your realm will become unavailable.", + "mco.configure.world.close.question.line2": "Are you sure you want to continue?", + "mco.configure.world.leave.question.line1": "If you leave this realm you won't see it unless you are invited again", + "mco.configure.world.leave.question.line2": "Are you sure you want to continue?", + "mco.configure.world.resourcepack.question.line1": "You need a custom resource pack to play on this realm", + "mco.configure.world.resourcepack.question.line2": "Do you want to download it and play?", + "mco.configure.world.reset.question.line1": "Your world will be regenerated and your current world will be lost", + "mco.configure.world.reset.question.line2": "Are you sure you want to continue?", + "mco.configure.world.restore.question.line1": "Your world will be restored to date '%s' (%s)", + "mco.configure.world.restore.question.line2": "Are you sure you want to continue?", + "mco.configure.world.restore.download.question.line1": "The world will be downloaded and added to your single player worlds.", + "mco.configure.world.restore.download.question.line2": "Do you want to continue?", + "mco.configure.world.slot.switch.question.line1": "Your realm will be switched to another world", + "mco.configure.world.slot.switch.question.line2": "Are you sure you want to continue?", + "mco.configure.world.switch.slot": "Create world", + "mco.configure.world.switch.slot.subtitle": "This world is empty, choose how to create your world", + "mco.minigame.world.slot.screen.title": "Switching world...", + "mco.configure.world.edit.subscreen.adventuremap": "Some settings are disabled since your current world is an adventure", + "mco.configure.world.edit.subscreen.experience": "Some settings are disabled since your current world is an experience", + "mco.configure.world.edit.subscreen.inspiration": "Some settings are disabled since your current world is an inspiration", + "mco.configure.world.edit.slot.name": "World name", + "mco.configure.world.players.title": "Players", + "mco.configure.world.players.error": "A player with the provided name does not exist", + "mco.configure.world.delete.button": "Delete realm", + "mco.configure.world.delete.question.line1": "Your realm will be permanently deleted", + "mco.configure.world.delete.question.line2": "Are you sure you want to continue?", + "mco.connect.connecting": "Connecting to the realm...", + "mco.connect.authorizing": "Logging in...", + "mco.connect.failed": "Failed to connect to the realm", + "mco.connect.success": "Done", + "mco.create.world": "Create", + "mco.create.world.reset.title": "Creating world...", + "mco.client.incompatible.title": "Client incompatible!", + "mco.client.incompatible.msg.line1": "Your client is not compatible with Realms.", + "mco.client.incompatible.msg.line2": "Please use the most recent version of Minecraft.", + "mco.client.incompatible.msg.line3": "Realms is not compatible with snapshot versions.", + "mco.backup.button.restore": "Restore", + "mco.backup.generate.world": "Generate world", + "mco.backup.restoring": "Restoring your realm", + "mco.backup.button.download": "Download latest", + "mco.backup.changes.tooltip": "Changes", + "mco.backup.nobackups": "This realm doesn't have any backups currently.", + "mco.backup.button.upload": "Upload world", + "mco.backup.button.reset": "Reset world", + "mco.download.title": "Downloading latest world", + "mco.download.cancelled": "Download cancelled", + "mco.download.failed": "Download failed", + "mco.download.done": "Download done", + "mco.download.downloading": "Downloading", + "mco.download.extracting": "Extracting", + "mco.download.preparing": "Preparing download", + "mco.download.confirmation.line1": "The world you are going to download is larger than %s", + "mco.download.confirmation.line2": "You won't be able to upload this world to your realm again", + "mco.template.title": "World templates", + "mco.template.title.minigame": "Minigames", + "mco.template.button.select": "Select", + "mco.template.button.trailer": "Trailer", + "mco.template.button.publisher": "Publisher", + "mco.template.default.name": "World template", + "mco.template.name": "Template", + "mco.template.info.tooltip": "Publisher website", + "mco.template.trailer.tooltip": "Map trailer", + "mco.template.select.none": "Oops, it looks like this content category is currently empty.\nPlease check back later for new content, or if you're a creator,\n%s.", + "mco.template.select.none.linkTitle": "consider submitting something yourself", + "mco.template.select.failure": "We couldn't retrieve the list of content for this category.\nPlease check your internet connection, or try again later.", + "mco.template.select.narrate.authors" : "Authors: %s", + "mco.template.select.narrate.version" : "version %s", + "mco.invites.button.accept": "Accept", + "mco.invites.button.reject": "Reject", + "mco.invites.title": "Pending Invites", + "mco.invites.pending": "New invites!", + "mco.invites.nopending": "No pending invites!", + "mco.upload.select.world.title": "Upload world", + "mco.upload.select.world.subtitle": "Please select a singleplayer world to upload", + "mco.upload.select.world.none": "No singleplayer worlds found!", + "mco.upload.button.name": "Upload", + "mco.upload.verifying": "Verifying your world", + "mco.upload.preparing": "Preparing your world", + "mco.upload.close.failure": "Could not close your realm, please try again later", + "mco.upload.size.failure.line1": "'%s' is too big!", + "mco.upload.size.failure.line2": "It is %s. The maximum allowed size is %s.", + "mco.upload.uploading": "Uploading '%s'", + "mco.upload.done": "Upload done", + "mco.upload.failed": "Upload failed! (%s)", + "mco.upload.cancelled": "Upload cancelled", + "mco.upload.hardcore": "Hardcore worlds can't be uploaded!", + "mco.activity.title": "Player activity", + "mco.activity.noactivity": "No activity for the past %s days", + "mco.errorMessage.6001": "Client outdated", + "mco.errorMessage.6002": "Terms of service not accepted", + "mco.errorMessage.6003": "Download limit reached", + "mco.errorMessage.6004": "Upload limit reached", + "mco.errorMessage.connectionFailure": "An error occurred, please try again later.", + "mco.errorMessage.serviceBusy": "Realms is busy at the moment.\nPlease try connecting to your Realm again in a couple of minutes.", + "mco.trial.message.line1": "Want to get your own realm?", + "mco.trial.message.line2": "Click here for more info!", + "mco.brokenworld.play": "Play", + "mco.brokenworld.download": "Download", + "mco.brokenworld.downloaded": "Downloaded", + "mco.brokenworld.reset": "Reset", + "mco.brokenworld.title": "Your current world is no longer supported", + "mco.brokenworld.message.line1": "Please reset or select another world.", + "mco.brokenworld.message.line2": "You can also choose to download the world to singleplayer.", + "mco.brokenworld.minigame.title": "This minigame is no longer supported", + "mco.brokenworld.nonowner.title": "World is out of date", + "mco.brokenworld.nonowner.error": "Please wait for the realm owner to reset the world", + "mco.error.invalid.session.title": "Invalid session", + "mco.error.invalid.session.message": "Please try restarting Minecraft", + "mco.news": "Realms news", + "mco.account.privacyinfo": "Mojang implements certain procedures to help protect children and their privacy including complying with the Children’s Online Privacy Protection Act (COPPA) and General Data Protection Regulation (GDPR).\n\nYou may need to obtain parental consent before accessing your Realms account.\n\nIf you have an older Minecraft account (you log in with your username), you need to migrate the account to a Mojang account in order to access Realms.", + "mco.account.update": "Update account", + "mco.account.privacy.info": "Read more about Mojang and privacy laws" +} diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 67c5cf72..39d00aa4 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -16,6 +16,7 @@ using Tomlet.Models; using static MinecraftClient.Settings.AppVarConfigHelper; using static MinecraftClient.Settings.ChatBotConfigHealper; using static MinecraftClient.Settings.ChatFormatConfigHelper; +using static MinecraftClient.Settings.ConsoleConfigHealper; using static MinecraftClient.Settings.HeadCommentHealper; using static MinecraftClient.Settings.LoggingConfigHealper; using static MinecraftClient.Settings.MainConfigHealper; @@ -31,10 +32,10 @@ namespace MinecraftClient 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\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"; + // Other Settings + public const string TranslationsFile_Version = "1.19.3"; + public const string TranslationsFile_Website_Index = "https://piston-meta.mojang.com/v1/packages/c492375ded5da34b646b8c5c0842a0028bc69cec/2.json"; + public const string TranslationsFile_Website_Download = "http://resources.download.minecraft.net"; public const string TranslationProjectUrl = "https://crwd.in/minecraft-console-client"; @@ -90,6 +91,12 @@ namespace MinecraftClient set { LoggingConfigHealper.Config = value; LoggingConfigHealper.Config.OnSettingUpdate(); } } + public ConsoleConfig Console + { + get { return ConsoleConfigHealper.Config; } + set { ConsoleConfigHealper.Config = value; ConsoleConfigHealper.Config.OnSettingUpdate(); } + } + public AppVarConfig AppVar { get { return AppVarConfigHelper.Config; } @@ -345,12 +352,12 @@ namespace MinecraftClient "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", "lol_us", "lt_lt", "lv_lv", "lzh", "mk_mk", - "mn_mn", "ms_my", "mt_mt", "nds_de", "nl_be", "nl_nl", "nn_no", "no_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" + "mn_mn", "ms_my", "mt_mt", "nah", "nds_de", "nl_be", "nl_nl", "nn_no", + "no_no", "oc_fr", "ovd", "pl_pl", "pt_br", "pt_pt", "qya_aa", "ro_ro", + "rpr", "ru_ru", "ry_ua", "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" }; /// @@ -421,7 +428,7 @@ namespace MinecraftClient if (!Advanced.LoadMccTranslation) { - CultureInfo culture = CultureInfo.CreateSpecificCulture("en_gb"); + CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US"); CultureInfo.DefaultThreadCurrentCulture = culture; CultureInfo.DefaultThreadCurrentUICulture = culture; Program.ActualCulture = culture; @@ -491,7 +498,7 @@ namespace MinecraftClient public class AdvancedConfig { [TomlInlineComment("$Main.Advanced.language$")] - public string Language = "en_gb"; + public string Language = "en_us"; [TomlInlineComment("$Main.Advanced.LoadMccTrans$")] public bool LoadMccTranslation = true; @@ -767,6 +774,139 @@ namespace MinecraftClient } } + public static class ConsoleConfigHealper + { + public static ConsoleConfig Config = new(); + + [TomlDoNotInlineObject] + public class ConsoleConfig + { + public MainConfig General = new(); + + [TomlPrecedingComment("$Console.CommandSuggestion$")] + public CommandSuggestionConfig CommandSuggestion = new(); + + public void OnSettingUpdate() + { + ConsoleInteractive.ConsoleWriter.EnableColor = General.Enable_Color; + + ConsoleInteractive.ConsoleReader.DisplayUesrInput = General.Display_Uesr_Input; + + ConsoleInteractive.ConsoleSuggestion.EnableColor = CommandSuggestion.Enable_Color; + + CommandSuggestion.Max_Suggestion_Width = + ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionLength(CommandSuggestion.Max_Suggestion_Width); + + CommandSuggestion.Max_Displayed_Suggestions = + ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionCount(CommandSuggestion.Max_Displayed_Suggestions); + + // CommandSuggestion color settings + { + if (!CheckColorCode(CommandSuggestion.Text_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.TextColor", CommandSuggestion.Text_Color)); + CommandSuggestion.Text_Color = "#f8fafc"; + } + if (!CheckColorCode(CommandSuggestion.Text_Background_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.TextBackgroundColor", CommandSuggestion.Text_Background_Color)); + CommandSuggestion.Text_Background_Color = "#64748b"; + } + if (!CheckColorCode(CommandSuggestion.Highlight_Text_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.HighlightTextColor", CommandSuggestion.Highlight_Text_Color)); + CommandSuggestion.Highlight_Text_Color = "#334155"; + } + if (!CheckColorCode(CommandSuggestion.Highlight_Text_Background_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.HighlightTextBackgroundColor", CommandSuggestion.Highlight_Text_Background_Color)); + CommandSuggestion.Highlight_Text_Background_Color = "#fde047"; + } + if (!CheckColorCode(CommandSuggestion.Tooltip_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.TooltipColor", CommandSuggestion.Tooltip_Color)); + CommandSuggestion.Tooltip_Color = "#7dd3fc"; + } + if (!CheckColorCode(CommandSuggestion.Highlight_Tooltip_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.HighlightTooltipColor", CommandSuggestion.Highlight_Tooltip_Color)); + CommandSuggestion.Highlight_Tooltip_Color = "#3b82f6"; + } + if (!CheckColorCode(CommandSuggestion.Arrow_Symbol_Color)) + { + ConsoleIO.WriteLine(string.Format(Translations.config_commandsuggestion_illegal_color, "CommandSuggestion.ArrowSymbolColor", CommandSuggestion.Arrow_Symbol_Color)); + CommandSuggestion.Arrow_Symbol_Color = "#d1d5db"; + } + + ConsoleInteractive.ConsoleSuggestion.SetColors( + CommandSuggestion.Text_Color, CommandSuggestion.Text_Background_Color, + CommandSuggestion.Highlight_Text_Color, CommandSuggestion.Highlight_Text_Background_Color, + CommandSuggestion.Tooltip_Color, CommandSuggestion.Highlight_Tooltip_Color, + CommandSuggestion.Arrow_Symbol_Color); + } + } + + private static bool CheckColorCode(string? input) + { + if (string.IsNullOrWhiteSpace(input)) + return false; + if (input.Length < 6 || input.Length > 7) + return false; + if (input.Length == 6 && input[0] == '#') + return false; + if (input.Length == 7 && input[0] != '#') + return false; + try + { + Convert.ToInt32(input.Length == 7 ? input[1..] : input, 16); + return true; + } + catch + { + return false; + } + } + + [TomlDoNotInlineObject] + public class MainConfig + { + [TomlInlineComment("$Console.Enable_Color$")] + public bool Enable_Color = true; + + [TomlInlineComment("$Console.General.Display_Uesr_Input$")] + public bool Display_Uesr_Input = true; + } + + [TomlDoNotInlineObject] + public class CommandSuggestionConfig + { + [TomlInlineComment("$Console.CommandSuggestion.Enable$")] + public bool Enable = true; + + [TomlInlineComment("$Console.Enable_Color$")] + public bool Enable_Color = true; + + public int Max_Suggestion_Width = 30; + + public int Max_Displayed_Suggestions = 6; + + public string Text_Color = "#f8fafc"; + + public string Text_Background_Color = "#64748b"; + + public string Highlight_Text_Color = "#334155"; + + public string Highlight_Text_Background_Color = "#fde047"; + + public string Tooltip_Color = "#7dd3fc"; + + public string Highlight_Tooltip_Color = "#3b82f6"; + + public string Arrow_Symbol_Color = "#d1d5db"; + } + } + } + public static class AppVarConfigHelper { public static AppVarConfig Config = new(); @@ -1238,7 +1378,7 @@ namespace MinecraftClient public static string GetDefaultGameLanguage() { - string gameLanguage = "en_gb"; + string gameLanguage = "en_us"; string systemLanguage = string.IsNullOrWhiteSpace(Program.ActualCulture.Name) ? Program.ActualCulture.Parent.Name : Program.ActualCulture.Name; @@ -1326,6 +1466,9 @@ namespace MinecraftClient case "en-TT": case "en-ZA": case "en-ZW": + case "en-US": + gameLanguage = "en_us"; + break; case "en-GB": gameLanguage = "en_gb"; break; @@ -1335,9 +1478,6 @@ namespace MinecraftClient case "en-CA": gameLanguage = "en_ca"; break; - case "en-US": - gameLanguage = "en_us"; - break; case "en-NZ": gameLanguage = "en_nz"; break; diff --git a/MinecraftClient/UpgradeHelper.cs b/MinecraftClient/UpgradeHelper.cs index 77c694d4..cca5b163 100644 --- a/MinecraftClient/UpgradeHelper.cs +++ b/MinecraftClient/UpgradeHelper.cs @@ -63,7 +63,7 @@ namespace MinecraftClient { string OSInfo = GetOSIdentifier(); if (Settings.Config.Logging.DebugMessages || string.IsNullOrEmpty(OSInfo)) - ConsoleIO.WriteLine(string.Format("OS: {0}, Arch: {1}, Framework: {2}", + ConsoleIO.WriteLine(string.Format("OS: {0}, Arch: {1}, Framework: {2}", RuntimeInformation.OSDescription, RuntimeInformation.ProcessArchitecture, RuntimeInformation.FrameworkDescription)); if (string.IsNullOrEmpty(OSInfo)) { @@ -308,7 +308,7 @@ namespace MinecraftClient { string proxyAddress; if (!string.IsNullOrWhiteSpace(Settings.Config.Proxy.Username) && !string.IsNullOrWhiteSpace(Settings.Config.Proxy.Password)) - proxyAddress = string.Format("{0}://{3}:{4}@{1}:{2}", + proxyAddress = string.Format("{0}://{3}:{4}@{1}:{2}", Settings.Config.Proxy.Proxy_Type.ToString().ToLower(), Settings.Config.Proxy.Server.Host, Settings.Config.Proxy.Server.Port, From 127978615c182206a2ac670d5a4dced98be1d93a Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 11 Dec 2022 14:54:11 +0800 Subject: [PATCH 04/44] Update tip message --- .gitignore | 1 + MinecraftClient/Resources/Translations/Translations.Designer.cs | 2 +- MinecraftClient/Resources/Translations/Translations.resx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 45f6cef2..22033cc8 100644 --- a/.gitignore +++ b/.gitignore @@ -409,6 +409,7 @@ FodyWeavers.xsd # translations /MinecraftClient/Resources/Translations/Translations.*.resx /MinecraftClient/Resources/AsciiArt/AsciiArt.*.resx +/MinecraftClient/Resources/ConfigComments/ConfigComments.*.resx /docs/.vuepress/translations/*.json !/docs/.vuepress/translations/en.json diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index 7af00b17..bc17ee17 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -2253,7 +2253,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Downloading '{0}.lang' from Mojang servers.... + /// Looks up a localized string similar to Downloading '{0}.json' from Mojang servers.... /// internal static string chat_download { get { diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index 1888597a..dd806246 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -864,7 +864,7 @@ Add the ID of this chat to "Authorized_Chat_Ids" field in the configuration file Done. File saved as '{0}' - Downloading '{0}.lang' from Mojang servers... + Downloading '{0}.json' from Mojang servers... Failed to download the file. From 94a3c92b36d97fddd3bedb380d07d6f4d12d111f Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 11 Dec 2022 16:30:45 +0800 Subject: [PATCH 05/44] Bug fix --- MinecraftClient/ChatBots/AutoCraft.cs | 12 +-- MinecraftClient/ChatBots/AutoDig.cs | 16 ++-- MinecraftClient/ChatBots/AutoDrop.cs | 12 +-- MinecraftClient/ChatBots/AutoFishing.cs | 12 +-- MinecraftClient/ChatBots/DiscordBridge.cs | 12 +-- MinecraftClient/ChatBots/Farmer.cs | 12 +-- MinecraftClient/ChatBots/FollowPlayer.cs | 12 +-- MinecraftClient/ChatBots/Mailer.cs | 12 +-- MinecraftClient/ChatBots/Map.cs | 12 +-- MinecraftClient/ChatBots/ReplayCapture.cs | 12 +-- MinecraftClient/ChatBots/TelegramBridge.cs | 12 +-- MinecraftClient/Command.cs | 4 +- .../ArgumentType/BotNameArgumentType.cs | 2 +- .../ArgumentType/HotbarSlotArgumentType.cs | 2 +- .../ArgumentType/InventoryIdArgumentType.cs | 2 +- .../ArgumentType/InventorySlotArgumentType.cs | 2 +- .../ArgumentType/LocationArgumentType.cs | 2 +- .../ArgumentType/MapBotMapIdArgumentType.cs | 2 +- .../ArgumentType/PlayerNameArgumentType.cs | 2 +- MinecraftClient/CommandHandler/CmdResult.cs | 2 +- MinecraftClient/Commands/Animation.cs | 11 +-- MinecraftClient/Commands/Bed.cs | 17 +++-- MinecraftClient/Commands/BlockInfo.cs | 34 +++++---- MinecraftClient/Commands/Bots.cs | 14 ++-- MinecraftClient/Commands/ChangeSlot.cs | 7 +- MinecraftClient/Commands/Chunk.cs | 32 ++++---- MinecraftClient/Commands/Connect.cs | 8 +- MinecraftClient/Commands/Debug.cs | 2 +- MinecraftClient/Commands/Dig.cs | 12 +-- MinecraftClient/Commands/DropItem.cs | 7 +- MinecraftClient/Commands/Enchant.cs | 11 +-- MinecraftClient/Commands/Entitycmd.cs | 50 +++++++------ MinecraftClient/Commands/ExecIf.cs | 7 +- MinecraftClient/Commands/ExecMulti.cs | 7 +- MinecraftClient/Commands/Exit.cs | 2 +- MinecraftClient/Commands/Health.cs | 7 +- MinecraftClient/Commands/Help.cs | 7 +- MinecraftClient/Commands/Inventory.cs | 75 +++++++++++-------- MinecraftClient/Commands/List.cs | 7 +- MinecraftClient/Commands/Log.cs | 2 +- MinecraftClient/Commands/Look.cs | 32 ++++---- MinecraftClient/Commands/Move.cs | 62 ++++++++------- MinecraftClient/Commands/Reco.cs | 4 +- MinecraftClient/Commands/Reload.cs | 7 +- MinecraftClient/Commands/Respawn.cs | 7 +- MinecraftClient/Commands/Script.cs | 7 +- MinecraftClient/Commands/Send.cs | 7 +- MinecraftClient/Commands/Set.cs | 2 +- MinecraftClient/Commands/SetRnd.cs | 2 +- MinecraftClient/Commands/Sneak.cs | 7 +- MinecraftClient/Commands/Tps.cs | 7 +- MinecraftClient/Commands/Upgrade.cs | 2 +- MinecraftClient/Commands/UseItem.cs | 7 +- MinecraftClient/Commands/Useblock.cs | 7 +- MinecraftClient/ConsoleIO.cs | 4 +- MinecraftClient/McClient.cs | 6 +- .../ConfigComments/ConfigComments.Designer.cs | 9 +++ .../ConfigComments/ConfigComments.resx | 3 + .../Translations/Translations.Designer.cs | 2 +- .../Resources/Translations/Translations.resx | 2 +- MinecraftClient/Scripting/ChatBot.cs | 2 +- MinecraftClient/Settings.cs | 5 ++ 62 files changed, 371 insertions(+), 297 deletions(-) diff --git a/MinecraftClient/ChatBots/AutoCraft.cs b/MinecraftClient/ChatBots/AutoCraft.cs index 890f235c..4e9b10a8 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -296,7 +296,7 @@ namespace MinecraftClient.ChatBots return; } - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Then(l => l.Literal("list") @@ -310,7 +310,7 @@ namespace MinecraftClient.ChatBots ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("list") .Executes(r => OnCommandList(r.Source))) .Then(l => l.Literal("start") @@ -319,14 +319,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -339,7 +339,7 @@ namespace MinecraftClient.ChatBots "stop" => Translations.bot_autoCraft_help_stop, "help" => Translations.bot_autoCraft_help_help, _ => string.Format(Translations.bot_autoCraft_available_cmd, "load, list, reload, resetcfg, start, stop, help") - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs index 49b9b44e..859ce35c 100644 --- a/MinecraftClient/ChatBots/AutoDig.cs +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -123,7 +123,7 @@ namespace MinecraftClient.ChatBots if (!inventoryEnabled && Config.Auto_Tool_Switch) LogToConsole(Translations.bot_autodig_no_inv_handle); - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Then(l => l.Literal("start") @@ -135,25 +135,25 @@ namespace MinecraftClient.ChatBots ) ); - var cmd = Handler.dispatcher.Register(l => l.Literal(CommandName) + var cmd = McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("start") .Executes(r => OnCommandStart(r.Source))) .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); - Handler.dispatcher.Register(l => l.Literal("digbot") + McClient.dispatcher.Register(l => l.Literal("digbot") .Redirect(cmd) ); } public override void OnUnload() { - Handler.dispatcher.Unregister("digbot"); - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister("digbot"); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -165,7 +165,7 @@ namespace MinecraftClient.ChatBots "stop" => Translations.bot_autodig_help_stop, "help" => Translations.bot_autodig_help_help, _ => string.Format(Translations.bot_autodig_available_cmd, "start, stop, help") - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index 7dea2c18..5a301830 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -54,7 +54,7 @@ namespace MinecraftClient.ChatBots return; } - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Then(l => l.Literal("add") @@ -66,7 +66,7 @@ namespace MinecraftClient.ChatBots ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("on") .Executes(r => OnCommandEnable(r.Source, true))) .Then(l => l.Literal("off") @@ -87,14 +87,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("everything") .Executes(r => OnCommandMode(r.Source, DropMode.everything)))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -106,7 +106,7 @@ namespace MinecraftClient.ChatBots "remove" => Translations.cmd_inventory_help_usage + ": remove ", "mode" => Translations.bot_autoDrop_unknown_mode, _ => string.Format(Translations.general_available_cmd, "on, off, add, remove, list, mode") - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index 28886bfd..ff028536 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -187,7 +187,7 @@ namespace MinecraftClient.ChatBots if (!inventoryEnabled) LogToConsole(Translations.bot_autoFish_no_inv_handle); - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Then(l => l.Literal("start") @@ -201,7 +201,7 @@ namespace MinecraftClient.ChatBots ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("start") .Executes(r => OnCommandStart(r.Source))) .Then(l => l.Literal("stop") @@ -211,14 +211,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("clear") .Executes(r => OnCommandStatusClear(r.Source)))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -231,7 +231,7 @@ namespace MinecraftClient.ChatBots "status" => Translations.bot_autoFish_help_status, "help" => Translations.bot_autoFish_help_help, _ => string.Format(Translations.bot_autoFish_available_cmd, "start, stop, status, help") - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index c9aa29a2..a3280bf4 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -76,13 +76,13 @@ namespace MinecraftClient.ChatBots public override void Initialize() { - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("direction") .Then(l => l.Literal("both") .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Both))) @@ -92,7 +92,7 @@ namespace MinecraftClient.ChatBots .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Discord))) ) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); Task.Run(async () => await MainAsync()); @@ -100,8 +100,8 @@ namespace MinecraftClient.ChatBots public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); Disconnect(); } @@ -111,7 +111,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => "dscbridge direction " - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index 64373ed8..2bb11817 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -90,13 +90,13 @@ namespace MinecraftClient.ChatBots return; } - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("start") @@ -105,14 +105,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Argument("OtherArgs", Arguments.GreedyString()) .Executes(r => OnCommandStart(r.Source, MccArguments.GetFarmerCropType(r, "CropType"), Arguments.GetString(r, "OtherArgs")))))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -121,7 +121,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => Translations.bot_farmer_desc + ": " + commandDescription - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index 4ff6695a..cf707699 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -62,13 +62,13 @@ namespace MinecraftClient.ChatBots return; } - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("start") .Then(l => l.Argument("PlayerName", MccArguments.PlayerName()) .Executes(r => OnCommandStart(r.Source, Arguments.GetString(r, "PlayerName"), takeRisk: false)) @@ -77,14 +77,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -93,7 +93,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => Translations.cmd_follow_desc + ": " + Translations.cmd_follow_usage - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index 8ef5c1d1..aba382d8 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -257,12 +257,12 @@ namespace MinecraftClient.ChatBots mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.DatabaseFile)!, Path.GetFileName(Config.DatabaseFile), FileMonitorCallback); ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.IgnoreListFile)!, Path.GetFileName(Config.IgnoreListFile), FileMonitorCallback); - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(string.Empty))) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("getmails") .Executes(r => OnCommandGetMails())) .Then(l => l.Literal("getignored") @@ -274,14 +274,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Argument("username", Arguments.String()) .Executes(r => OnCommandRemoveIgnored(Arguments.GetString(r, "username"))))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(string cmd) @@ -290,7 +290,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => Translations.bot_mailer_cmd_help + ": /mailer " - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); return 1; diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index 0839c2a8..4689c162 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -80,13 +80,13 @@ namespace MinecraftClient.ChatBots DeleteRenderedMaps(); - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Executes(r => OnCommandList(r.Source)) .Then(l => l.Literal("list") .Executes(r => OnCommandList(r.Source))) @@ -94,14 +94,14 @@ namespace MinecraftClient.ChatBots .Then(l => l.Argument("MapID", MccArguments.MapBotMapId()) .Executes(r => OnCommandRender(r.Source, Arguments.GetInteger(r, "MapID"))))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); DeleteRenderedMaps(); } @@ -111,7 +111,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => Translations.error_usage + ": /maps >" - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index 9db42ec2..f0a73a3a 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -46,26 +46,26 @@ namespace MinecraftClient.ChatBots replay.MetaData.serverName = GetServerHost() + GetServerPort(); backupCounter = Settings.DoubleToTick(Config.Backup_Interval); - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("save") .Executes(r => OnCommandSave(r.Source))) .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } private int OnCommandHelp(CmdResult r, string? cmd) @@ -74,7 +74,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => string.Format(Translations.general_available_cmd, "save, stop") - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index 392bc1cf..b92572db 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -76,13 +76,13 @@ namespace MinecraftClient.ChatBots public override void Initialize() { - Handler.dispatcher.Register(l => l.Literal("help") + McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) .Executes(r => OnCommandHelp(r.Source, string.Empty)) ) ); - Handler.dispatcher.Register(l => l.Literal(CommandName) + McClient.dispatcher.Register(l => l.Literal(CommandName) .Then(l => l.Literal("direction") .Then(l => l.Literal("both") .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Both))) @@ -91,7 +91,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("telegram") .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Telegram)))) .Then(l => l.Literal("_help") - .Redirect(Handler.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) + .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); Task.Run(async () => await MainAsync()); @@ -99,8 +99,8 @@ namespace MinecraftClient.ChatBots public override void OnUnload() { - Handler.dispatcher.Unregister(CommandName); - Handler.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); + McClient.dispatcher.Unregister(CommandName); + McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); Disconnect(); } @@ -110,7 +110,7 @@ namespace MinecraftClient.ChatBots { #pragma warning disable format // @formatter:off _ => Translations.error_usage + ": /tgbridge direction " - + '\n' + Handler.dispatcher.GetAllUsageString(CommandName, false), + + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); } diff --git a/MinecraftClient/Command.cs b/MinecraftClient/Command.cs index 796e56e0..e606f0dd 100644 --- a/MinecraftClient/Command.cs +++ b/MinecraftClient/Command.cs @@ -36,7 +36,7 @@ namespace MinecraftClient StringBuilder sb = new(); string s = (string.IsNullOrEmpty(CmdUsage) || string.IsNullOrEmpty(CmdDesc)) ? string.Empty : ": "; // If either one is empty, no colon : sb.Append("§e").Append(cmdChar).Append(CmdUsage).Append("§r").Append(s).AppendLine(CmdDesc); - sb.Append(CmdResult.client!.dispatcher.GetAllUsageString(CmdName, false)); + sb.Append(McClient.dispatcher.GetAllUsageString(CmdName, false)); return sb.ToString(); } @@ -48,7 +48,7 @@ namespace MinecraftClient /// /// Register the command. /// - public abstract void RegisterCommand(McClient handler, CommandDispatcher dispatcher); + public abstract void RegisterCommand(CommandDispatcher dispatcher); /// /// Check if at least one argument has been passed to the command diff --git a/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs index 0681ae0d..1f49da98 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/BotNameArgumentType.cs @@ -16,7 +16,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; if (client != null) { var botList = client.GetLoadedChatBots(); diff --git a/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs index 16d07611..ebf05813 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/HotbarSlotArgumentType.cs @@ -17,7 +17,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; if (client != null) { Inventory.Container? inventory = client.GetInventory(0); diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs index 6c7ae2af..15164956 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryIdArgumentType.cs @@ -17,7 +17,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; if (client != null) { var invList = client.GetInventories(); diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs index ecc796ca..2536aa1e 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/InventorySlotArgumentType.cs @@ -18,7 +18,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; if (client != null && context.Nodes.Count >= 2) { string invName = context.Nodes[1].Range.Get(builder.Input); diff --git a/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs index 79f51d62..f2ef9b0a 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/LocationArgumentType.cs @@ -47,7 +47,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; string[] args = builder.Remaining.Split(' ', StringSplitOptions.TrimEntries); if (args.Length == 0 || (args.Length == 1 && string.IsNullOrWhiteSpace(args[0]))) { diff --git a/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs index 55c05020..bd1ffee6 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/MapBotMapIdArgumentType.cs @@ -18,7 +18,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; if (client != null) { var bot = (Map?)client.GetLoadedChatBots().Find(bot => bot.GetType().Name == "Map"); diff --git a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs index d8b88093..b5092251 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs @@ -18,7 +18,7 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) { - McClient? client = CmdResult.client; + McClient? client = CmdResult.currentHandler; if (client != null) { var entityList = client.GetEntities().Values.ToList(); diff --git a/MinecraftClient/CommandHandler/CmdResult.cs b/MinecraftClient/CommandHandler/CmdResult.cs index b5477763..4be40a4a 100644 --- a/MinecraftClient/CommandHandler/CmdResult.cs +++ b/MinecraftClient/CommandHandler/CmdResult.cs @@ -6,7 +6,7 @@ namespace MinecraftClient.CommandHandler { public static readonly CmdResult Empty = new(); - internal static McClient? client; + internal static McClient? currentHandler; public enum Status { diff --git a/MinecraftClient/Commands/Animation.cs b/MinecraftClient/Commands/Animation.cs index 41f7affe..f5290e3e 100644 --- a/MinecraftClient/Commands/Animation.cs +++ b/MinecraftClient/Commands/Animation.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "animation "; } } public override string CmdDesc { get { return Translations.cmd_animation_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -23,11 +23,11 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoAnimation(r.Source, handler, mainhand: true)) + .Executes(r => DoAnimation(r.Source, mainhand: true)) .Then(l => l.Literal("mainhand") - .Executes(r => DoAnimation(r.Source, handler, mainhand: true))) + .Executes(r => DoAnimation(r.Source, mainhand: true))) .Then(l => l.Literal("offhand") - .Executes(r => DoAnimation(r.Source, handler, mainhand: false))) + .Executes(r => DoAnimation(r.Source, mainhand: false))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -45,8 +45,9 @@ namespace MinecraftClient.Commands }); } - private static int DoAnimation(CmdResult r, McClient handler, bool mainhand) + private static int DoAnimation(CmdResult r, bool mainhand) { + McClient handler = CmdResult.currentHandler!; return r.SetAndReturn(handler.DoAnimation(mainhand ? 1 : 0)); } } diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs index fa6e6958..fbeb15fb 100644 --- a/MinecraftClient/Commands/Bed.cs +++ b/MinecraftClient/Commands/Bed.cs @@ -16,7 +16,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bed leave|sleep |sleep "; } } public override string CmdDesc { get { return Translations.cmd_bed_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -30,12 +30,12 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Literal("leave") - .Executes(r => DoLeaveBed(r.Source, handler))) + .Executes(r => DoLeaveBed(r.Source))) .Then(l => l.Literal("sleep") .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => DoSleepBedWithLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Executes(r => DoSleepBedWithLocation(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Argument("Radius", Arguments.Double()) - .Executes(r => DoSleepBedWithRadius(r.Source, handler, Arguments.GetDouble(r, "Radius"))))) + .Executes(r => DoSleepBedWithRadius(r.Source, Arguments.GetDouble(r, "Radius"))))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -53,13 +53,15 @@ namespace MinecraftClient.Commands }); } - private static int DoLeaveBed(CmdResult r, McClient handler) + private static int DoLeaveBed(CmdResult r) { + McClient handler = CmdResult.currentHandler!; return r.SetAndReturn(Translations.cmd_bed_leaving, handler.SendEntityAction(Protocol.EntityActionType.LeaveBed)); } - private static int DoSleepBedWithRadius(CmdResult r, McClient handler, double radius) + private static int DoSleepBedWithRadius(CmdResult r, double radius) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -153,8 +155,9 @@ namespace MinecraftClient.Commands } } - private static int DoSleepBedWithLocation(CmdResult r, McClient handler, Location block) + private static int DoSleepBedWithLocation(CmdResult r, Location block) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/Commands/BlockInfo.cs b/MinecraftClient/Commands/BlockInfo.cs index dcc82d61..7ced900b 100644 --- a/MinecraftClient/Commands/BlockInfo.cs +++ b/MinecraftClient/Commands/BlockInfo.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "blockinfo [-s]"; } } public override string CmdDesc { get { return Translations.cmd_blockinfo_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -24,13 +24,13 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => LogBlockInfo(r.Source, handler, handler.GetCurrentLocation(), false)) + .Executes(r => LogBlockInfo(r.Source, null, false)) .Then(l => l.Literal("-s") - .Executes(r => LogBlockInfo(r.Source, handler, handler.GetCurrentLocation(), true))) + .Executes(r => LogBlockInfo(r.Source, null, true))) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => LogBlockInfo(r.Source, handler, MccArguments.GetLocation(r, "Location"), false)) + .Executes(r => LogBlockInfo(r.Source, MccArguments.GetLocation(r, "Location"), false)) .Then(l => l.Literal("-s") - .Executes(r => LogBlockInfo(r.Source, handler, MccArguments.GetLocation(r, "Location"), true)))) + .Executes(r => LogBlockInfo(r.Source, MccArguments.GetLocation(r, "Location"), true)))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -47,13 +47,18 @@ namespace MinecraftClient.Commands }); } - private static int LogBlockInfo(CmdResult r, McClient handler, Location targetBlock, bool reportSurrounding) + private static int LogBlockInfo(CmdResult r, Location? targetBlock, bool reportSurrounding) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); - targetBlock.ToAbsolute(handler.GetCurrentLocation()); - Block block = handler.GetWorld().GetBlock(targetBlock); + if (targetBlock.HasValue) + targetBlock.Value.ToAbsolute(handler.GetCurrentLocation()); + else + targetBlock = handler.GetCurrentLocation(); + + Block block = handler.GetWorld().GetBlock(targetBlock.Value); handler.Log.Info($"{Translations.cmd_blockinfo_BlockType}: {block.GetTypeString()}"); if (reportSurrounding) @@ -61,12 +66,13 @@ namespace MinecraftClient.Commands StringBuilder sb = new(); sb.AppendLine($"{Translations.cmd_blockinfo_BlocksAround}:"); - Block blockXPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X + 1, targetBlock.Y, targetBlock.Z)); - Block blockXNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X - 1, targetBlock.Y, targetBlock.Z)); - Block blockYPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y + 1, targetBlock.Z)); - Block blockYNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y - 1, targetBlock.Z)); - Block blockZPositive = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y, targetBlock.Z + 1)); - Block blockZNegative = handler.GetWorld().GetBlock(new Location(targetBlock.X, targetBlock.Y, targetBlock.Z - 1)); + double X = targetBlock.Value.X, Y = targetBlock.Value.Y, Z = targetBlock.Value.Z; + Block blockXPositive = handler.GetWorld().GetBlock(new Location(X + 1, Y, Z)); + Block blockXNegative = handler.GetWorld().GetBlock(new Location(X - 1, Y, Z)); + Block blockYPositive = handler.GetWorld().GetBlock(new Location(X, Y + 1, Z)); + Block blockYNegative = handler.GetWorld().GetBlock(new Location(X, Y - 1, Z)); + Block blockZPositive = handler.GetWorld().GetBlock(new Location(X, Y, Z + 1)); + Block blockZNegative = handler.GetWorld().GetBlock(new Location(X, Y, Z - 1)); sb.AppendLine($"[X {Translations.cmd_blockinfo_Positive}] {Translations.cmd_blockinfo_BlockType}: {blockXPositive.GetTypeString()}"); sb.AppendLine($"[X {Translations.cmd_blockinfo_Negative}] {Translations.cmd_blockinfo_BlockType}: {blockXNegative.GetTypeString()}"); diff --git a/MinecraftClient/Commands/Bots.cs b/MinecraftClient/Commands/Bots.cs index 60e0b41c..da9250e4 100644 --- a/MinecraftClient/Commands/Bots.cs +++ b/MinecraftClient/Commands/Bots.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "bots [list|unload ]"; } } public override string CmdDesc { get { return Translations.cmd_bots_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -26,12 +26,12 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoListBot(r.Source, handler)) + .Executes(r => DoListBot(r.Source)) .Then(l => l.Literal("list") - .Executes(r => DoListBot(r.Source, handler))) + .Executes(r => DoListBot(r.Source))) .Then(l => l.Literal("unload") .Then(l => l.Argument("BotName", MccArguments.BotName()) - .Executes(r => DoUnloadBot(r.Source, handler, Arguments.GetString(r, "BotName"))))) + .Executes(r => DoUnloadBot(r.Source, Arguments.GetString(r, "BotName"))))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -49,8 +49,9 @@ namespace MinecraftClient.Commands }); } - private int DoListBot(CmdResult r, McClient handler) + private int DoListBot(CmdResult r) { + McClient handler = CmdResult.currentHandler!; int length = handler.GetLoadedChatBots().Count; if (length == 0) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_bots_noloaded); @@ -66,8 +67,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_bots_list + ": " + sb.ToString()); } - private int DoUnloadBot(CmdResult r, McClient handler, string botName) + private int DoUnloadBot(CmdResult r, string botName) { + McClient handler = CmdResult.currentHandler!; if (botName.ToLower().Equals("all", StringComparison.OrdinalIgnoreCase)) { if (handler.GetLoadedChatBots().Count == 0) diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index 0353fe1e..d7631f65 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "changeslot <1-9>"; } } public override string CmdDesc { get { return Translations.cmd_changeSlot_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -21,7 +21,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("Slot", MccArguments.HotbarSlot()) - .Executes(r => DoChangeSlot(r.Source, handler, Arguments.GetInteger(r, "Slot")))) + .Executes(r => DoChangeSlot(r.Source, Arguments.GetInteger(r, "Slot")))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -37,8 +37,9 @@ namespace MinecraftClient.Commands }); } - private int DoChangeSlot(CmdResult r, McClient handler, int slot) + private int DoChangeSlot(CmdResult r, int slot) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(Status.FailNeedInventory); diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index 5d225db1..58380c9e 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -14,7 +14,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "chunk status [chunkX chunkZ|locationX locationY locationZ]"; } } public override string CmdDesc { get { return Translations.cmd_chunk_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -32,26 +32,26 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Literal("status") - .Executes(r => LogChunkStatus(r.Source, handler)) + .Executes(r => LogChunkStatus(r.Source)) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => LogChunkStatus(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Executes(r => LogChunkStatus(r.Source, pos: MccArguments.GetLocation(r, "Location")))) .Then(l => l.Argument("Chunk", MccArguments.Tuple()) - .Executes(r => LogChunkStatus(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Executes(r => LogChunkStatus(r.Source, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) .Then(l => l.Literal("_setloading") .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => DebugSetLoading(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Executes(r => DebugSetLoading(r.Source, pos: MccArguments.GetLocation(r, "Location")))) .Then(l => l.Argument("Chunk", MccArguments.Tuple()) - .Executes(r => DebugSetLoading(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Executes(r => DebugSetLoading(r.Source, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) .Then(l => l.Literal("_setloaded") .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => DebugSetLoaded(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Executes(r => DebugSetLoaded(r.Source, pos: MccArguments.GetLocation(r, "Location")))) .Then(l => l.Argument("Chunk", MccArguments.Tuple()) - .Executes(r => DebugSetLoaded(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Executes(r => DebugSetLoaded(r.Source, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) .Then(l => l.Literal("_delete") .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => DebugDelete(r.Source, handler, pos: MccArguments.GetLocation(r, "Location")))) + .Executes(r => DebugDelete(r.Source, pos: MccArguments.GetLocation(r, "Location")))) .Then(l => l.Argument("Chunk", MccArguments.Tuple()) - .Executes(r => DebugDelete(r.Source, handler, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) + .Executes(r => DebugDelete(r.Source, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -71,8 +71,9 @@ namespace MinecraftClient.Commands }); } - private static int LogChunkStatus(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + private static int LogChunkStatus(CmdResult r, Location? pos = null, Tuple? markedChunkPos = null) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -225,8 +226,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done); } - private static int DebugSetLoading(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + private static int DebugSetLoading(CmdResult r, Location? pos = null, Tuple? markedChunkPos = null) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -244,8 +246,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loading.", chunkX, chunkZ)); } - private static int DebugSetLoaded(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + private static int DebugSetLoaded(CmdResult r, Location? pos = null, Tuple? markedChunkPos = null) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -263,8 +266,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done, string.Format("Successfully marked chunk ({0}, {1}) as loaded.", chunkX, chunkZ)); } - private static int DebugDelete(CmdResult r, McClient handler, Location? pos = null, Tuple? markedChunkPos = null) + private static int DebugDelete(CmdResult r, Location? pos = null, Tuple? markedChunkPos = null) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs index 79892fc4..0224c528 100644 --- a/MinecraftClient/Commands/Connect.cs +++ b/MinecraftClient/Commands/Connect.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "connect [account]"; } } public override string CmdDesc { get { return Translations.cmd_connect_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -21,9 +21,9 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("ServerNick", MccArguments.ServerNick()) - .Executes(r => DoConnect(r.Source, handler, Arguments.GetString(r, "ServerNick"), string.Empty)) + .Executes(r => DoConnect(r.Source, Arguments.GetString(r, "ServerNick"), string.Empty)) .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) - .Executes(r => DoConnect(r.Source, handler, Arguments.GetString(r, "ServerNick"), Arguments.GetString(r, "AccountNick"))))) + .Executes(r => DoConnect(r.Source, Arguments.GetString(r, "ServerNick"), Arguments.GetString(r, "AccountNick"))))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -39,7 +39,7 @@ namespace MinecraftClient.Commands }); } - private int DoConnect(CmdResult r, McClient handler, string server, string account) + private int DoConnect(CmdResult r, string server, string account) { if (!string.IsNullOrWhiteSpace(account) && !Settings.Config.Main.Advanced.SetAccount(account)) return r.SetAndReturn(Status.Fail, string.Format(Translations.cmd_connect_unknown, account)); diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index acb6f7e9..3517505a 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "debug [on|off]"; } } public override string CmdDesc { get { return Translations.cmd_debug_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index 2903a817..80bf946f 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "dig "; } } public override string CmdDesc { get { return Translations.cmd_dig_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -22,9 +22,9 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DigLookAt(r.Source, handler)) + .Executes(r => DigLookAt(r.Source)) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => DigAt(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Executes(r => DigAt(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -40,8 +40,9 @@ namespace MinecraftClient.Commands }); } - private int DigAt(CmdResult r, McClient handler, Location blockToBreak) + private int DigAt(CmdResult r, Location blockToBreak) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -61,8 +62,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); } - private int DigLookAt(CmdResult r, McClient handler) + private int DigLookAt(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/Commands/DropItem.cs b/MinecraftClient/Commands/DropItem.cs index 01e9ecb1..2ee05f0c 100644 --- a/MinecraftClient/Commands/DropItem.cs +++ b/MinecraftClient/Commands/DropItem.cs @@ -14,7 +14,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "dropitem "; } } public override string CmdDesc { get { return Translations.cmd_dropItem_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -24,7 +24,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("ItemType", MccArguments.ItemType()) - .Executes(r => DoDropItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType")))) + .Executes(r => DoDropItem(r.Source, MccArguments.GetItemType(r, "ItemType")))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -40,8 +40,9 @@ namespace MinecraftClient.Commands }); } - private int DoDropItem(CmdResult r, McClient handler, ItemType itemType) + private int DoDropItem(CmdResult r, ItemType itemType) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/Commands/Enchant.cs b/MinecraftClient/Commands/Enchant.cs index bc7c7e10..74e5a5ab 100644 --- a/MinecraftClient/Commands/Enchant.cs +++ b/MinecraftClient/Commands/Enchant.cs @@ -12,7 +12,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "enchant "; } } public override string CmdDesc { get { return Translations.cmd_enchant_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -28,11 +28,11 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Literal("top") - .Executes(r => DoEnchant(r.Source, handler, slotId: 0))) + .Executes(r => DoEnchant(r.Source, slotId: 0))) .Then(l => l.Literal("middle") - .Executes(r => DoEnchant(r.Source, handler, slotId: 1))) + .Executes(r => DoEnchant(r.Source, slotId: 1))) .Then(l => l.Literal("bottom") - .Executes(r => DoEnchant(r.Source, handler, slotId: 2))) + .Executes(r => DoEnchant(r.Source, slotId: 2))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -51,8 +51,9 @@ namespace MinecraftClient.Commands }); } - private int DoEnchant(CmdResult r, McClient handler, int slotId) + private int DoEnchant(CmdResult r, int slotId) { + McClient handler = CmdResult.currentHandler!; Container? enchantingTable = null; foreach (var (id, container) in handler.GetInventories()) diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 0af7c1de..19f5fefe 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -19,7 +19,7 @@ namespace MinecraftClient.Commands private enum ActionType { Attack, Use, List }; - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -36,41 +36,41 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => GetFullEntityList(r.Source, handler)) + .Executes(r => GetFullEntityList(r.Source)) .Then(l => l.Literal("near") - .Executes(r => GetClosetEntityList(r.Source, handler)) + .Executes(r => GetClosetEntityList(r.Source)) .Then(l => l.Argument("EntityID", Arguments.Integer()) - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.List)) .Then(l => l.Literal("attack") - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) .Then(l => l.Literal("use") - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) .Then(l => l.Literal("list") - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) .Then(l => l.Argument("EntityType", MccArguments.EntityType()) - .Executes(r => OperateWithType(r.Source, handler, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) + .Executes(r => OperateWithType(r.Source, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) .Then(l => l.Literal("attack") - .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) + .Executes(r => OperateWithType(r.Source, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) .Then(l => l.Literal("use") - .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) + .Executes(r => OperateWithType(r.Source, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) .Then(l => l.Literal("list") - .Executes(r => OperateWithType(r.Source, handler, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List))))) + .Executes(r => OperateWithType(r.Source, near: true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List))))) .Then(l => l.Argument("EntityID", Arguments.Integer()) - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.List)) .Then(l => l.Literal("attack") - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.Attack))) .Then(l => l.Literal("use") - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.Use))) .Then(l => l.Literal("list") - .Executes(r => OperateWithId(r.Source, handler, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) + .Executes(r => OperateWithId(r.Source, Arguments.GetInteger(r, "EntityID"), ActionType.List)))) .Then(l => l.Argument("EntityType", MccArguments.EntityType()) - .Executes(r => OperateWithType(r.Source, handler, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) + .Executes(r => OperateWithType(r.Source, true, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)) .Then(l => l.Literal("attack") - .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) + .Executes(r => OperateWithType(r.Source, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Attack))) .Then(l => l.Literal("use") - .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) + .Executes(r => OperateWithType(r.Source, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.Use))) .Then(l => l.Literal("list") - .Executes(r => OperateWithType(r.Source, handler, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)))) + .Executes(r => OperateWithType(r.Source, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -90,8 +90,9 @@ namespace MinecraftClient.Commands }); } - private int GetFullEntityList(CmdResult r, McClient handler) + private int GetFullEntityList(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetEntityHandlingEnabled()) return r.SetAndReturn(Status.FailNeedEntity); @@ -106,8 +107,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done); } - private int GetClosetEntityList(CmdResult r, McClient handler) + private int GetClosetEntityList(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetEntityHandlingEnabled()) return r.SetAndReturn(Status.FailNeedEntity); @@ -120,8 +122,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); } - private int OperateWithId(CmdResult r, McClient handler, int entityID, ActionType action) + private int OperateWithId(CmdResult r, int entityID, ActionType action) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetEntityHandlingEnabled()) return r.SetAndReturn(Status.FailNeedEntity); @@ -134,8 +137,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Fail, Translations.cmd_entityCmd_not_found); } - private int OperateWithType(CmdResult r, McClient handler, bool near, EntityType entityType, ActionType action) + private int OperateWithType(CmdResult r, bool near, EntityType entityType, ActionType action) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetEntityHandlingEnabled()) return r.SetAndReturn(Status.FailNeedEntity); diff --git a/MinecraftClient/Commands/ExecIf.cs b/MinecraftClient/Commands/ExecIf.cs index 3557e200..1e441794 100644 --- a/MinecraftClient/Commands/ExecIf.cs +++ b/MinecraftClient/Commands/ExecIf.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "execif \"\" \"\""; } } public override string CmdDesc { get { return Translations.cmd_execif_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -24,7 +24,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("Condition", Arguments.String()) .Then(l => l.Argument("Command", Arguments.String()) - .Executes(r => HandleCommand(r.Source, handler, Arguments.GetString(r, "Condition"), Arguments.GetString(r, "Command"))))) + .Executes(r => HandleCommand(r.Source, Arguments.GetString(r, "Condition"), Arguments.GetString(r, "Command"))))) ); } @@ -38,8 +38,9 @@ namespace MinecraftClient.Commands }); } - private int HandleCommand(CmdResult r, McClient handler, string expressionText, string resultCommand) + private int HandleCommand(CmdResult r, string expressionText, string resultCommand) { + McClient handler = CmdResult.currentHandler!; try { var interpreter = new Interpreter(); diff --git a/MinecraftClient/Commands/ExecMulti.cs b/MinecraftClient/Commands/ExecMulti.cs index bebff1a1..98c2fe7c 100644 --- a/MinecraftClient/Commands/ExecMulti.cs +++ b/MinecraftClient/Commands/ExecMulti.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "execmulti -> -> -> ..."; } } public override string CmdDesc { get { return Translations.cmd_execmulti_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -23,7 +23,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("Commands", Arguments.GreedyString()) - .Executes(r => HandleCommand(r.Source, handler, Arguments.GetString(r, "Commands")))) + .Executes(r => HandleCommand(r.Source, Arguments.GetString(r, "Commands")))) ); } @@ -37,8 +37,9 @@ namespace MinecraftClient.Commands }); } - private int HandleCommand(CmdResult r, McClient handler, string commandsString) + private int HandleCommand(CmdResult r, string commandsString) { + McClient handler = CmdResult.currentHandler!; if (commandsString.Contains("execmulti", StringComparison.OrdinalIgnoreCase) || commandsString.Contains("execif", StringComparison.OrdinalIgnoreCase)) return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_execmulti_prevent); diff --git a/MinecraftClient/Commands/Exit.cs b/MinecraftClient/Commands/Exit.cs index 5618b248..281bd980 100644 --- a/MinecraftClient/Commands/Exit.cs +++ b/MinecraftClient/Commands/Exit.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "exit"; } } public override string CmdDesc { get { return Translations.cmd_exit_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) diff --git a/MinecraftClient/Commands/Health.cs b/MinecraftClient/Commands/Health.cs index 215b5d1a..308059b6 100644 --- a/MinecraftClient/Commands/Health.cs +++ b/MinecraftClient/Commands/Health.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "health"; } } public override string CmdDesc { get { return Translations.cmd_health_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -19,7 +19,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => LogHealth(r.Source, handler)) + .Executes(r => LogHealth(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -35,8 +35,9 @@ namespace MinecraftClient.Commands }); } - private int LogHealth(CmdResult r, McClient handler) + private int LogHealth(CmdResult r) { + McClient handler = CmdResult.currentHandler!; return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_health_response, handler.GetHealth(), handler.GetSaturation(), handler.GetLevel(), handler.GetTotalExperience())); } } diff --git a/MinecraftClient/Commands/Help.cs b/MinecraftClient/Commands/Help.cs index 4727f2bb..18dbaf3e 100644 --- a/MinecraftClient/Commands/Help.cs +++ b/MinecraftClient/Commands/Help.cs @@ -12,16 +12,17 @@ namespace MinecraftClient.Commands public override string CmdDesc => throw new NotImplementedException(); public override string CmdUsage => throw new NotImplementedException(); - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") - .Executes(r => LogHelp(r.Source, handler, dispatcher)) + .Executes(r => LogHelp(r.Source, dispatcher)) ); } - private int LogHelp(CmdResult r, McClient handler, CommandDispatcher dispatcher) + private int LogHelp(CmdResult r, CommandDispatcher dispatcher) { + McClient handler = CmdResult.currentHandler!; var usage = dispatcher.GetSmartUsage(dispatcher.GetRoot(), CmdResult.Empty); StringBuilder sb = new(); foreach (var item in usage) diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index cc674cc3..fd13a1c3 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -15,7 +15,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return GetBasicUsage(); } } public override string CmdDesc { get { return Translations.cmd_inventory_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -42,65 +42,65 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => ListAllInventories(r.Source, handler)) + .Executes(r => ListAllInventories(r.Source)) .Then(l => l.Literal("creativegive") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) .Then(l => l.Argument("ItemType", MccArguments.ItemType()) .Then(l => l.Argument("Count", Arguments.Integer(min: 1)) - .Executes(r => DoCreativeGive(r.Source, handler, Arguments.GetInteger(r, "Slot"), MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count"))))))) + .Executes(r => DoCreativeGive(r.Source, Arguments.GetInteger(r, "Slot"), MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count"))))))) .Then(l => l.Literal("creativedelete") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoCreativeDelete(r.Source, handler, Arguments.GetInteger(r, "Slot"))))) + .Executes(r => DoCreativeDelete(r.Source, Arguments.GetInteger(r, "Slot"))))) .Then(l => l.Literal("inventories") - .Executes(r => ListAvailableInventories(r.Source, handler))) + .Executes(r => ListAvailableInventories(r.Source))) .Then(l => l.Literal("search") .Then(l => l.Argument("ItemType", MccArguments.ItemType()) - .Executes(r => SearchItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType"), null)) + .Executes(r => SearchItem(r.Source, MccArguments.GetItemType(r, "ItemType"), null)) .Then(l => l.Argument("Count", Arguments.Integer(0, 64)) - .Executes(r => SearchItem(r.Source, handler, MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count")))))) + .Executes(r => SearchItem(r.Source, MccArguments.GetItemType(r, "ItemType"), Arguments.GetInteger(r, "Count")))))) .Then(l => l.Argument("InventoryId", MccArguments.InventoryId()) .Then(l => l.Literal("close") - .Executes(r => DoCloseAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) + .Executes(r => DoCloseAction(r.Source, Arguments.GetInteger(r, "InventoryId")))) .Then(l => l.Literal("list") - .Executes(r => DoListAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId")))) + .Executes(r => DoListAction(r.Source, Arguments.GetInteger(r, "InventoryId")))) .Then(l => l.Literal("click") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Executes(r => DoClickAction(r.Source, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) .Then(l => l.Argument("Action", MccArguments.InventoryAction()) - .Executes(r => DoClickAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Executes(r => DoClickAction(r.Source, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) .Then(l => l.Literal("drop") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Executes(r => DoDropAction(r.Source, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) .Then(l => l.Literal("all") - .Executes(r => DoDropAction(r.Source, handler, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Executes(r => DoDropAction(r.Source, Arguments.GetInteger(r, "InventoryId"), Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) .Then(l => l.Literal("player") .Then(l => l.Literal("list") - .Executes(r => DoListAction(r.Source, handler, inventoryId: 0))) + .Executes(r => DoListAction(r.Source, inventoryId: 0))) .Then(l => l.Literal("click") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Executes(r => DoClickAction(r.Source, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) .Then(l => l.Argument("Action", MccArguments.InventoryAction()) - .Executes(r => DoClickAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Executes(r => DoClickAction(r.Source, inventoryId: 0, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) .Then(l => l.Literal("drop") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Executes(r => DoDropAction(r.Source, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) .Then(l => l.Literal("all") - .Executes(r => DoDropAction(r.Source, handler, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Executes(r => DoDropAction(r.Source, inventoryId: 0, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) .Then(l => l.Literal("container") .Then(l => l.Literal("close") - .Executes(r => DoCloseAction(r.Source, handler, inventoryId: null))) + .Executes(r => DoCloseAction(r.Source, inventoryId: null))) .Then(l => l.Literal("list") - .Executes(r => DoListAction(r.Source, handler, inventoryId: null))) + .Executes(r => DoListAction(r.Source, inventoryId: null))) .Then(l => l.Literal("click") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) + .Executes(r => DoClickAction(r.Source, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.LeftClick)) .Then(l => l.Argument("Action", MccArguments.InventoryAction()) - .Executes(r => DoClickAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) + .Executes(r => DoClickAction(r.Source, inventoryId: null, Arguments.GetInteger(r, "Slot"), MccArguments.GetInventoryAction(r, "Action")))))) .Then(l => l.Literal("drop") .Then(l => l.Argument("Slot", MccArguments.InventorySlot()) - .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) + .Executes(r => DoDropAction(r.Source, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItem)) .Then(l => l.Literal("all") - .Executes(r => DoDropAction(r.Source, handler, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) + .Executes(r => DoDropAction(r.Source, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -133,8 +133,9 @@ namespace MinecraftClient.Commands return availableIds.Max(); // use foreground container } - private int ListAllInventories(CmdResult r, McClient handler) + private int ListAllInventories(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) { handler.Log.Info(Translations.extra_inventory_required); @@ -149,8 +150,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Done); } - private int DoCreativeGive(CmdResult r, McClient handler, int slot, ItemType itemType, int count) + private int DoCreativeGive(CmdResult r, int slot, ItemType itemType, int count) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -167,8 +169,9 @@ namespace MinecraftClient.Commands } } - private int DoCreativeDelete(CmdResult r, McClient handler, int slot) + private int DoCreativeDelete(CmdResult r, int slot) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -185,8 +188,9 @@ namespace MinecraftClient.Commands } } - private int ListAvailableInventories(CmdResult r, McClient handler) + private int ListAvailableInventories(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -202,8 +206,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Done); } - private int SearchItem(CmdResult r, McClient handler, ItemType itemType, int? itemCount) + private int SearchItem(CmdResult r, ItemType itemType, int? itemCount) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -256,8 +261,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Done); } - private int DoCloseAction(CmdResult r, McClient handler, int? inventoryId) + private int DoCloseAction(CmdResult r, int? inventoryId) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -278,8 +284,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_close_fail, inventoryId)); } - private int DoListAction(CmdResult r, McClient handler, int? inventoryId) + private int DoListAction(CmdResult r, int? inventoryId) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -320,8 +327,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Done); } - private int DoClickAction(CmdResult r, McClient handler, int? inventoryId, int slot, WindowActionType actionType) + private int DoClickAction(CmdResult r, int? inventoryId, int slot, WindowActionType actionType) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); @@ -349,8 +357,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(handler.DoWindowAction(inventoryId.Value, slot, actionType)); } - private int DoDropAction(CmdResult r, McClient handler, int? inventoryId, int slot, WindowActionType actionType) + private int DoDropAction(CmdResult r, int? inventoryId, int slot, WindowActionType actionType) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(CmdResult.Status.FailNeedInventory); diff --git a/MinecraftClient/Commands/List.cs b/MinecraftClient/Commands/List.cs index 82fd7f65..b06a4b9a 100644 --- a/MinecraftClient/Commands/List.cs +++ b/MinecraftClient/Commands/List.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "list"; } } public override string CmdDesc { get { return Translations.cmd_list_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoListPlayers(r.Source, handler)) + .Executes(r => DoListPlayers(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -36,8 +36,9 @@ namespace MinecraftClient.Commands }); } - private int DoListPlayers(CmdResult r, McClient handler) + private int DoListPlayers(CmdResult r) { + McClient handler = CmdResult.currentHandler!; return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_list_players, String.Join(", ", handler.GetOnlinePlayers()))); } } diff --git a/MinecraftClient/Commands/Log.cs b/MinecraftClient/Commands/Log.cs index e9181ed3..8cdf3ecf 100644 --- a/MinecraftClient/Commands/Log.cs +++ b/MinecraftClient/Commands/Log.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "log "; } } public override string CmdDesc { get { return Translations.cmd_log_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs index 2376bbc1..5c78f47e 100644 --- a/MinecraftClient/Commands/Look.cs +++ b/MinecraftClient/Commands/Look.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "look "; } } public override string CmdDesc { get { return Translations.cmd_look_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -28,24 +28,24 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => LogCurrentLooking(r.Source, handler)) + .Executes(r => LogCurrentLooking(r.Source)) .Then(l => l.Literal("up") - .Executes(r => LookAtDirection(r.Source, handler, Direction.Up))) + .Executes(r => LookAtDirection(r.Source, Direction.Up))) .Then(l => l.Literal("down") - .Executes(r => LookAtDirection(r.Source, handler, Direction.Down))) + .Executes(r => LookAtDirection(r.Source, Direction.Down))) .Then(l => l.Literal("east") - .Executes(r => LookAtDirection(r.Source, handler, Direction.East))) + .Executes(r => LookAtDirection(r.Source, Direction.East))) .Then(l => l.Literal("west") - .Executes(r => LookAtDirection(r.Source, handler, Direction.West))) + .Executes(r => LookAtDirection(r.Source, Direction.West))) .Then(l => l.Literal("north") - .Executes(r => LookAtDirection(r.Source, handler, Direction.North))) + .Executes(r => LookAtDirection(r.Source, Direction.North))) .Then(l => l.Literal("south") - .Executes(r => LookAtDirection(r.Source, handler, Direction.South))) + .Executes(r => LookAtDirection(r.Source, Direction.South))) .Then(l => l.Argument("Yaw", Arguments.Float()) .Then(l => l.Argument("Pitch", Arguments.Float()) - .Executes(r => LookAtAngle(r.Source, handler, Arguments.GetFloat(r, "Yaw"), Arguments.GetFloat(r, "Pitch"))))) + .Executes(r => LookAtAngle(r.Source, Arguments.GetFloat(r, "Yaw"), Arguments.GetFloat(r, "Pitch"))))) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => LookAtLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Executes(r => LookAtLocation(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -64,8 +64,9 @@ namespace MinecraftClient.Commands }); } - private int LogCurrentLooking(CmdResult r, McClient handler) + private int LogCurrentLooking(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -83,8 +84,9 @@ namespace MinecraftClient.Commands } } - private int LookAtDirection(CmdResult r, McClient handler, Direction direction) + private int LookAtDirection(CmdResult r, Direction direction) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -92,8 +94,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done, "Looking " + direction.ToString()); } - private int LookAtAngle(CmdResult r, McClient handler, float yaw, float pitch) + private int LookAtAngle(CmdResult r, float yaw, float pitch) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -101,8 +104,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_look_at, yaw.ToString("0.00"), pitch.ToString("0.00"))); } - private int LookAtLocation(CmdResult r, McClient handler, Location location) + private int LookAtLocation(CmdResult r, Location location) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index 734dc10d..89065765 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "move [-f]"; } } public override string CmdDesc { get { return Translations.cmd_move_desc + " \"-f\": " + Translations.cmd_move_desc_force; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -37,47 +37,47 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Literal("on") - .Executes(r => SetMovementEnable(r.Source, handler, enable: true))) + .Executes(r => SetMovementEnable(r.Source, enable: true))) .Then(l => l.Literal("off") - .Executes(r => SetMovementEnable(r.Source, handler, enable: false))) + .Executes(r => SetMovementEnable(r.Source, enable: false))) .Then(l => l.Literal("gravity") - .Executes(r => SetGravityEnable(r.Source, handler, enable: null)) + .Executes(r => SetGravityEnable(r.Source, enable: null)) .Then(l => l.Literal("on") - .Executes(r => SetGravityEnable(r.Source, handler, enable: true))) + .Executes(r => SetGravityEnable(r.Source, enable: true))) .Then(l => l.Literal("off") - .Executes(r => SetGravityEnable(r.Source, handler, enable: false)))) + .Executes(r => SetGravityEnable(r.Source, enable: false)))) .Then(l => l.Literal("up") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.Up, false)) + .Executes(r => MoveOnDirection(r.Source, Direction.Up, false)) .Then(l => l.Literal("-f") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.Up, true)))) + .Executes(r => MoveOnDirection(r.Source, Direction.Up, true)))) .Then(l => l.Literal("down") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.Down, false)) + .Executes(r => MoveOnDirection(r.Source, Direction.Down, false)) .Then(l => l.Literal("-f") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.Down, true)))) + .Executes(r => MoveOnDirection(r.Source, Direction.Down, true)))) .Then(l => l.Literal("east") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.East, false)) + .Executes(r => MoveOnDirection(r.Source, Direction.East, false)) .Then(l => l.Literal("-f") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.East, true)))) + .Executes(r => MoveOnDirection(r.Source, Direction.East, true)))) .Then(l => l.Literal("west") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.West, false)) + .Executes(r => MoveOnDirection(r.Source, Direction.West, false)) .Then(l => l.Literal("-f") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.West, true)))) + .Executes(r => MoveOnDirection(r.Source, Direction.West, true)))) .Then(l => l.Literal("north") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.North, false)) + .Executes(r => MoveOnDirection(r.Source, Direction.North, false)) .Then(l => l.Literal("-f") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.North, true)))) + .Executes(r => MoveOnDirection(r.Source, Direction.North, true)))) .Then(l => l.Literal("south") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.South, false)) + .Executes(r => MoveOnDirection(r.Source, Direction.South, false)) .Then(l => l.Literal("-f") - .Executes(r => MoveOnDirection(r.Source, handler, Direction.South, true)))) + .Executes(r => MoveOnDirection(r.Source, Direction.South, true)))) .Then(l => l.Literal("center") - .Executes(r => MoveToCenter(r.Source, handler))) + .Executes(r => MoveToCenter(r.Source))) .Then(l => l.Literal("get") - .Executes(r => GetCurrentLocation(r.Source, handler))) + .Executes(r => GetCurrentLocation(r.Source))) .Then(l => l.Argument("location", MccArguments.Location()) - .Executes(r => MoveToLocation(r.Source, handler, MccArguments.GetLocation(r, "location"), false)) + .Executes(r => MoveToLocation(r.Source, MccArguments.GetLocation(r, "location"), false)) .Then(l => l.Literal("-f") - .Executes(r => MoveToLocation(r.Source, handler, MccArguments.GetLocation(r, "location"), true)))) + .Executes(r => MoveToLocation(r.Source, MccArguments.GetLocation(r, "location"), true)))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -100,8 +100,9 @@ namespace MinecraftClient.Commands }); } - private int SetMovementEnable(CmdResult r, McClient handler, bool enable) + private int SetMovementEnable(CmdResult r, bool enable) { + McClient handler = CmdResult.currentHandler!; if (enable) { handler.SetTerrainEnabled(true); @@ -114,8 +115,9 @@ namespace MinecraftClient.Commands } } - private int SetGravityEnable(CmdResult r, McClient handler, bool? enable) + private int SetGravityEnable(CmdResult r, bool? enable) { + McClient handler = CmdResult.currentHandler!; if (enable.HasValue) Settings.InternalConfig.GravityEnabled = enable.Value; @@ -125,16 +127,18 @@ namespace MinecraftClient.Commands return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_move_gravity_disabled); } - private int GetCurrentLocation(CmdResult r, McClient handler) + private int GetCurrentLocation(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); return r.SetAndReturn(Status.Done, handler.GetCurrentLocation().ToString()); } - private int MoveToCenter(CmdResult r, McClient handler) + private int MoveToCenter(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -144,8 +148,9 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_walk, currentCenter, current)); } - private int MoveOnDirection(CmdResult r, McClient handler, Direction direction, bool takeRisk) + private int MoveOnDirection(CmdResult r, Direction direction, bool takeRisk) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); @@ -167,8 +172,9 @@ namespace MinecraftClient.Commands } } - private int MoveToLocation(CmdResult r, McClient handler, Location goal, bool takeRisk) + private int MoveToLocation(CmdResult r, Location goal, bool takeRisk) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs index c2ef3081..569ca465 100644 --- a/MinecraftClient/Commands/Reco.cs +++ b/MinecraftClient/Commands/Reco.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reco [account]"; } } public override string CmdDesc { get { return Translations.cmd_reco_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -40,7 +40,7 @@ namespace MinecraftClient.Commands private int DoReconnect(CmdResult r, string account) { - if (string.IsNullOrWhiteSpace(account)) + if (!string.IsNullOrWhiteSpace(account)) { account = account.Trim(); if (!Settings.Config.Main.Advanced.SetAccount(account)) diff --git a/MinecraftClient/Commands/Reload.cs b/MinecraftClient/Commands/Reload.cs index f004c821..be8ba98d 100644 --- a/MinecraftClient/Commands/Reload.cs +++ b/MinecraftClient/Commands/Reload.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "reload"; } } public override string CmdDesc { get { return Translations.cmd_reload_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -19,7 +19,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoReload(r.Source, handler)) + .Executes(r => DoReload(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -35,8 +35,9 @@ namespace MinecraftClient.Commands }); } - private int DoReload(CmdResult r, McClient handler) + private int DoReload(CmdResult r) { + McClient handler = CmdResult.currentHandler!; handler.Log.Info(Translations.cmd_reload_started); handler.ReloadSettings(); handler.Log.Warn(Translations.cmd_reload_warning1); diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs index d9bfbcbf..d494e0b7 100644 --- a/MinecraftClient/Commands/Respawn.cs +++ b/MinecraftClient/Commands/Respawn.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "respawn"; } } public override string CmdDesc { get { return Translations.cmd_respawn_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -19,7 +19,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoRespawn(r.Source, handler)) + .Executes(r => DoRespawn(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -35,8 +35,9 @@ namespace MinecraftClient.Commands }); } - private int DoRespawn(CmdResult r, McClient handler) + private int DoRespawn(CmdResult r) { + McClient handler = CmdResult.currentHandler!; handler.SendRespawnPacket(); return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_respawn_done); } diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index f7f850f2..3c8dd07c 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "script "; } } public override string CmdDesc { get { return Translations.cmd_script_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -21,7 +21,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("Script", Arguments.GreedyString()) - .Executes(r => DoExecuteScript(r.Source, handler, Arguments.GetString(r, "Script"), null))) + .Executes(r => DoExecuteScript(r.Source, Arguments.GetString(r, "Script"), null))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -37,8 +37,9 @@ namespace MinecraftClient.Commands }); } - private int DoExecuteScript(CmdResult r, McClient handler, string command, Dictionary? localVars) + private int DoExecuteScript(CmdResult r, string command, Dictionary? localVars) { + McClient handler = CmdResult.currentHandler!; handler.BotLoad(new ChatBots.Script(command.Trim(), null, localVars)); return r.SetAndReturn(CmdResult.Status.Done); } diff --git a/MinecraftClient/Commands/Send.cs b/MinecraftClient/Commands/Send.cs index d17bc3b5..cf6964ec 100644 --- a/MinecraftClient/Commands/Send.cs +++ b/MinecraftClient/Commands/Send.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "send "; } } public override string CmdDesc { get { return Translations.cmd_send_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("any", Arguments.GreedyString()) - .Executes(r => DoSendText(r.Source, handler, Arguments.GetString(r, "any")))) + .Executes(r => DoSendText(r.Source, Arguments.GetString(r, "any")))) ); } @@ -34,8 +34,9 @@ namespace MinecraftClient.Commands }); } - private int DoSendText(CmdResult r, McClient handler, string command) + private int DoSendText(CmdResult r, string command) { + McClient handler = CmdResult.currentHandler!; handler.SendText(command); return r.SetAndReturn(CmdResult.Status.Done); } diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs index 3bf6d86f..e46cfebb 100644 --- a/MinecraftClient/Commands/Set.cs +++ b/MinecraftClient/Commands/Set.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "set varname=value"; } } public override string CmdDesc { get { return Translations.cmd_set_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) diff --git a/MinecraftClient/Commands/SetRnd.cs b/MinecraftClient/Commands/SetRnd.cs index 1f93ca24..c792b5d7 100644 --- a/MinecraftClient/Commands/SetRnd.cs +++ b/MinecraftClient/Commands/SetRnd.cs @@ -13,7 +13,7 @@ namespace MinecraftClient.Commands public override string CmdDesc { get { return Translations.cmd_setrnd_desc; } } private static readonly Random rand = new(); - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index d21bea8a..f5401ffb 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "sneak"; } } public override string CmdDesc { get { return Translations.cmd_sneak_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoSneak(r.Source, handler)) + .Executes(r => DoSneak(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -36,8 +36,9 @@ namespace MinecraftClient.Commands }); } - private int DoSneak(CmdResult r, McClient handler) + private int DoSneak(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (sneaking) { var result = handler.SendEntityAction(Protocol.EntityActionType.StopSneaking); diff --git a/MinecraftClient/Commands/Tps.cs b/MinecraftClient/Commands/Tps.cs index 92fb0924..f96e9674 100644 --- a/MinecraftClient/Commands/Tps.cs +++ b/MinecraftClient/Commands/Tps.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "tps"; } } public override string CmdDesc { get { return Translations.cmd_tps_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoLogTps(r.Source, handler)) + .Executes(r => DoLogTps(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -36,8 +36,9 @@ namespace MinecraftClient.Commands }); } - private int DoLogTps(CmdResult r, McClient handler) + private int DoLogTps(CmdResult r) { + McClient handler = CmdResult.currentHandler!; var tps = Math.Round(handler.GetServerTPS(), 2); string color; if (tps < 10) diff --git a/MinecraftClient/Commands/Upgrade.cs b/MinecraftClient/Commands/Upgrade.cs index 625b5d5b..30757eaa 100644 --- a/MinecraftClient/Commands/Upgrade.cs +++ b/MinecraftClient/Commands/Upgrade.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "upgrade [-f|check|cancel|download]"; } } public override string CmdDesc { get { return string.Empty; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 41f056da..1768cf38 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -11,7 +11,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useitem"; } } public override string CmdDesc { get { return Translations.cmd_useitem_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Executes(r => DoUseItem(r.Source, handler)) + .Executes(r => DoUseItem(r.Source)) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -36,8 +36,9 @@ namespace MinecraftClient.Commands }); } - private int DoUseItem(CmdResult r, McClient handler) + private int DoUseItem(CmdResult r) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetInventoryEnabled()) return r.SetAndReturn(Status.FailNeedInventory); diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 4973d6a6..1169c994 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -12,7 +12,7 @@ namespace MinecraftClient.Commands public override string CmdUsage { get { return "useblock "; } } public override string CmdDesc { get { return Translations.cmd_useblock_desc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CmdName) @@ -22,7 +22,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => UseBlockAtLocation(r.Source, handler, MccArguments.GetLocation(r, "Location")))) + .Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Literal("_help") .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); @@ -38,8 +38,9 @@ namespace MinecraftClient.Commands }); } - private int UseBlockAtLocation(CmdResult r, McClient handler, Location block) + private int UseBlockAtLocation(CmdResult r, Location block) { + McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) return r.SetAndReturn(Status.FailNeedTerrain); diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs index db2f849b..f5262f73 100644 --- a/MinecraftClient/ConsoleIO.cs +++ b/MinecraftClient/ConsoleIO.cs @@ -224,7 +224,7 @@ namespace MinecraftClient sugList.Add(new("/")); - var childs = CmdResult.client?.dispatcher.GetRoot().Children; + var childs = McClient.dispatcher.GetRoot().Children; if (childs != null) foreach (var child in childs) sugList.Add(new(child.Name)); @@ -246,7 +246,7 @@ namespace MinecraftClient } else { - CommandDispatcher? dispatcher = CmdResult.client?.dispatcher; + CommandDispatcher? dispatcher = McClient.dispatcher; if (dispatcher == null) return; diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 2b38ab50..80defaf6 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -30,7 +30,7 @@ namespace MinecraftClient { public static int ReconnectionAttemptsLeft = 0; - public CommandDispatcher dispatcher = new(); + public static CommandDispatcher dispatcher = new(); private readonly Dictionary onlinePlayers = new(); private static bool commandsLoaded = false; @@ -150,7 +150,7 @@ namespace MinecraftClient /// ForgeInfo item stating that Forge is enabled public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo) { - CmdResult.client = this; + CmdResult.currentHandler = this; terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; entityHandlingEnabled = Config.Main.Advanced.EntityHandling; @@ -674,7 +674,7 @@ namespace MinecraftClient try { Command cmd = (Command)Activator.CreateInstance(type)!; - cmd.RegisterCommand(this, dispatcher); + cmd.RegisterCommand(dispatcher); } catch (Exception e) { diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index c27a20e8..bf0cbc7f 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1102,6 +1102,15 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to Enable this option if the arrows in the command suggestions are not displayed properly in your terminal.. + /// + internal static string Console_CommandSuggestion_Use_Basic_Arrow { + get { + return ResourceManager.GetString("Console.CommandSuggestion.Use_Basic_Arrow", resourceCulture); + } + } + /// /// Looks up a localized string similar to If a garbled code like "←[0m" appears on the terminal, you can turn off this.. /// diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index 3413fd87..1b71fcd9 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -544,6 +544,9 @@ When this happens, you'll need to configure chat format below, see https://mccte Whether to display command suggestions in the console. + + Enable this option if the arrows in the command suggestions are not displayed properly in your terminal. + If a garbled code like "←[0m" appears on the terminal, you can turn off this. diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index bc17ee17..9c1b8d84 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -5341,7 +5341,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Password(invisible): {0}. + /// Looks up a localized string similar to Password(invisible): . /// internal static string mcc_password_hidden { get { diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index dd806246..73e81493 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -1900,7 +1900,7 @@ Type '{0}quit' to leave the server. Please type the password for {0}. - Password(invisible): {0} + Password(invisible): You are dead. Type '{0}respawn' to respawn. diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 025bb850..d73f0884 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -1667,7 +1667,7 @@ namespace MinecraftClient.Scripting public override string CmdUsage { get { return _cmdUsage; } } public override string CmdDesc { get { return _cmdDesc; } } - public override void RegisterCommand(McClient handler, CommandDispatcher dispatcher) + public override void RegisterCommand(CommandDispatcher dispatcher) { } diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 39d00aa4..0a07bb40 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -794,6 +794,8 @@ namespace MinecraftClient ConsoleInteractive.ConsoleSuggestion.EnableColor = CommandSuggestion.Enable_Color; + ConsoleInteractive.ConsoleSuggestion.UseBasicArrow = CommandSuggestion.Use_Basic_Arrow; + CommandSuggestion.Max_Suggestion_Width = ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionLength(CommandSuggestion.Max_Suggestion_Width); @@ -886,6 +888,9 @@ namespace MinecraftClient [TomlInlineComment("$Console.Enable_Color$")] public bool Enable_Color = true; + [TomlInlineComment("$Console.CommandSuggestion.Use_Basic_Arrow$")] + public bool Use_Basic_Arrow = false; + public int Max_Suggestion_Width = 30; public int Max_Displayed_Suggestions = 6; From 7900108763d86a86188b0dc8940f2cf3b12f7776 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 11 Dec 2022 17:31:37 +0800 Subject: [PATCH 06/44] Bug fix --- MinecraftClient/ChatBots/AutoCraft.cs | 1 + MinecraftClient/ChatBots/AutoDig.cs | 1 + MinecraftClient/ChatBots/AutoDrop.cs | 1 + MinecraftClient/ChatBots/AutoFishing.cs | 1 + MinecraftClient/ChatBots/DiscordBridge.cs | 1 + MinecraftClient/ChatBots/Farmer.cs | 1 + MinecraftClient/ChatBots/FollowPlayer.cs | 1 + MinecraftClient/ChatBots/Mailer.cs | 9 +++++---- MinecraftClient/ChatBots/Map.cs | 1 + MinecraftClient/ChatBots/ReplayCapture.cs | 1 + MinecraftClient/ChatBots/TelegramBridge.cs | 1 + MinecraftClient/Commands/Animation.cs | 1 + MinecraftClient/Commands/Bed.cs | 1 + MinecraftClient/Commands/BlockInfo.cs | 1 + MinecraftClient/Commands/Bots.cs | 1 + MinecraftClient/Commands/ChangeSlot.cs | 1 + MinecraftClient/Commands/Chunk.cs | 1 + MinecraftClient/Commands/Connect.cs | 1 + MinecraftClient/Commands/Debug.cs | 1 + MinecraftClient/Commands/Dig.cs | 1 + MinecraftClient/Commands/DropItem.cs | 1 + MinecraftClient/Commands/Enchant.cs | 1 + MinecraftClient/Commands/Entitycmd.cs | 1 + MinecraftClient/Commands/Exit.cs | 1 + MinecraftClient/Commands/Health.cs | 1 + MinecraftClient/Commands/Inventory.cs | 1 + MinecraftClient/Commands/List.cs | 1 + MinecraftClient/Commands/Look.cs | 1 + MinecraftClient/Commands/Move.cs | 1 + MinecraftClient/Commands/Reco.cs | 1 + MinecraftClient/Commands/Reload.cs | 1 + MinecraftClient/Commands/Respawn.cs | 1 + MinecraftClient/Commands/Script.cs | 1 + MinecraftClient/Commands/Set.cs | 1 + MinecraftClient/Commands/Sneak.cs | 1 + MinecraftClient/Commands/Tps.cs | 1 + MinecraftClient/Commands/Upgrade.cs | 1 + MinecraftClient/Commands/UseItem.cs | 1 + MinecraftClient/Commands/Useblock.cs | 1 + 39 files changed, 43 insertions(+), 4 deletions(-) diff --git a/MinecraftClient/ChatBots/AutoCraft.cs b/MinecraftClient/ChatBots/AutoCraft.cs index 4e9b10a8..52d691ab 100644 --- a/MinecraftClient/ChatBots/AutoCraft.cs +++ b/MinecraftClient/ChatBots/AutoCraft.cs @@ -319,6 +319,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/AutoDig.cs b/MinecraftClient/ChatBots/AutoDig.cs index 859ce35c..cddb6ffd 100644 --- a/MinecraftClient/ChatBots/AutoDig.cs +++ b/MinecraftClient/ChatBots/AutoDig.cs @@ -141,6 +141,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); diff --git a/MinecraftClient/ChatBots/AutoDrop.cs b/MinecraftClient/ChatBots/AutoDrop.cs index 5a301830..36263a86 100644 --- a/MinecraftClient/ChatBots/AutoDrop.cs +++ b/MinecraftClient/ChatBots/AutoDrop.cs @@ -87,6 +87,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("everything") .Executes(r => OnCommandMode(r.Source, DropMode.everything)))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index ff028536..09ee140f 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -211,6 +211,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("clear") .Executes(r => OnCommandStatusClear(r.Source)))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index a3280bf4..b9d5a50e 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -92,6 +92,7 @@ namespace MinecraftClient.ChatBots .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Discord))) ) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index 2bb11817..072fe270 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -105,6 +105,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Argument("OtherArgs", Arguments.GreedyString()) .Executes(r => OnCommandStart(r.Source, MccArguments.GetFarmerCropType(r, "CropType"), Arguments.GetString(r, "OtherArgs")))))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index cf707699..0561bf0f 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -77,6 +77,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/Mailer.cs b/MinecraftClient/ChatBots/Mailer.cs index aba382d8..41132d71 100644 --- a/MinecraftClient/ChatBots/Mailer.cs +++ b/MinecraftClient/ChatBots/Mailer.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using Brigadier.NET; using Brigadier.NET.Builder; +using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Scripting; using Tomlet.Attributes; @@ -259,7 +260,7 @@ namespace MinecraftClient.ChatBots McClient.dispatcher.Register(l => l.Literal("help") .Then(l => l.Literal(CommandName) - .Executes(r => OnCommandHelp(string.Empty))) + .Executes(r => OnCommandHelp(r.Source, string.Empty))) ); McClient.dispatcher.Register(l => l.Literal(CommandName) @@ -274,6 +275,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Argument("username", Arguments.String()) .Executes(r => OnCommandRemoveIgnored(Arguments.GetString(r, "username"))))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } @@ -284,16 +286,15 @@ namespace MinecraftClient.ChatBots McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName); } - private int OnCommandHelp(string cmd) + private int OnCommandHelp(CmdResult r, string? cmd) { - LogToConsole(cmd switch + return r.SetAndReturn(cmd switch { #pragma warning disable format // @formatter:off _ => Translations.bot_mailer_cmd_help + ": /mailer " + '\n' + McClient.dispatcher.GetAllUsageString(CommandName, false), #pragma warning restore format // @formatter:on }); - return 1; } private int OnCommandGetMails() diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index 4689c162..fef48a90 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -94,6 +94,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Argument("MapID", MccArguments.MapBotMapId()) .Executes(r => OnCommandRender(r.Source, Arguments.GetInteger(r, "MapID"))))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/ReplayCapture.cs b/MinecraftClient/ChatBots/ReplayCapture.cs index f0a73a3a..eea74911 100644 --- a/MinecraftClient/ChatBots/ReplayCapture.cs +++ b/MinecraftClient/ChatBots/ReplayCapture.cs @@ -58,6 +58,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("stop") .Executes(r => OnCommandStop(r.Source))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); } diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index b92572db..6139a8d0 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -91,6 +91,7 @@ namespace MinecraftClient.ChatBots .Then(l => l.Literal("telegram") .Executes(r => OnCommandDirection(r.Source, BridgeDirection.Telegram)))) .Then(l => l.Literal("_help") + .Executes(r => OnCommandHelp(r.Source, string.Empty)) .Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName))) ); diff --git a/MinecraftClient/Commands/Animation.cs b/MinecraftClient/Commands/Animation.cs index f5290e3e..dab51b6f 100644 --- a/MinecraftClient/Commands/Animation.cs +++ b/MinecraftClient/Commands/Animation.cs @@ -29,6 +29,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("offhand") .Executes(r => DoAnimation(r.Source, mainhand: false))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs index fbeb15fb..f3d4b995 100644 --- a/MinecraftClient/Commands/Bed.cs +++ b/MinecraftClient/Commands/Bed.cs @@ -37,6 +37,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Radius", Arguments.Double()) .Executes(r => DoSleepBedWithRadius(r.Source, Arguments.GetDouble(r, "Radius"))))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/BlockInfo.cs b/MinecraftClient/Commands/BlockInfo.cs index 7ced900b..61e177d0 100644 --- a/MinecraftClient/Commands/BlockInfo.cs +++ b/MinecraftClient/Commands/BlockInfo.cs @@ -32,6 +32,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("-s") .Executes(r => LogBlockInfo(r.Source, MccArguments.GetLocation(r, "Location"), true)))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Bots.cs b/MinecraftClient/Commands/Bots.cs index da9250e4..73574a14 100644 --- a/MinecraftClient/Commands/Bots.cs +++ b/MinecraftClient/Commands/Bots.cs @@ -33,6 +33,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("BotName", MccArguments.BotName()) .Executes(r => DoUnloadBot(r.Source, Arguments.GetString(r, "BotName"))))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index d7631f65..acbe2e8d 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -23,6 +23,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Slot", MccArguments.HotbarSlot()) .Executes(r => DoChangeSlot(r.Source, Arguments.GetInteger(r, "Slot")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index 58380c9e..3452716b 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -53,6 +53,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Chunk", MccArguments.Tuple()) .Executes(r => DebugDelete(r.Source, markedChunkPos: MccArguments.GetTuple(r, "Chunk"))))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Connect.cs b/MinecraftClient/Commands/Connect.cs index 0224c528..c8691340 100644 --- a/MinecraftClient/Commands/Connect.cs +++ b/MinecraftClient/Commands/Connect.cs @@ -25,6 +25,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) .Executes(r => DoConnect(r.Source, Arguments.GetString(r, "ServerNick"), Arguments.GetString(r, "AccountNick"))))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Debug.cs b/MinecraftClient/Commands/Debug.cs index 3517505a..0302ea05 100644 --- a/MinecraftClient/Commands/Debug.cs +++ b/MinecraftClient/Commands/Debug.cs @@ -25,6 +25,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("off") .Executes(r => SetDebugMode(r.Source, false, false))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index 80bf946f..3a30619a 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -26,6 +26,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Location", MccArguments.Location()) .Executes(r => DigAt(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/DropItem.cs b/MinecraftClient/Commands/DropItem.cs index 2ee05f0c..38cfc4b6 100644 --- a/MinecraftClient/Commands/DropItem.cs +++ b/MinecraftClient/Commands/DropItem.cs @@ -26,6 +26,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("ItemType", MccArguments.ItemType()) .Executes(r => DoDropItem(r.Source, MccArguments.GetItemType(r, "ItemType")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Enchant.cs b/MinecraftClient/Commands/Enchant.cs index 74e5a5ab..813c734f 100644 --- a/MinecraftClient/Commands/Enchant.cs +++ b/MinecraftClient/Commands/Enchant.cs @@ -34,6 +34,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("bottom") .Executes(r => DoEnchant(r.Source, slotId: 2))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 19f5fefe..7cd4d8b4 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -72,6 +72,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("list") .Executes(r => OperateWithType(r.Source, near: false, MccArguments.GetEntityType(r, "EntityType"), ActionType.List)))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Exit.cs b/MinecraftClient/Commands/Exit.cs index 281bd980..d2ac389e 100644 --- a/MinecraftClient/Commands/Exit.cs +++ b/MinecraftClient/Commands/Exit.cs @@ -23,6 +23,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("ExitCode", Arguments.Integer()) .Executes(r => DoExit(r.Source, Arguments.GetInteger(r, "ExitCode")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); diff --git a/MinecraftClient/Commands/Health.cs b/MinecraftClient/Commands/Health.cs index 308059b6..21df0efd 100644 --- a/MinecraftClient/Commands/Health.cs +++ b/MinecraftClient/Commands/Health.cs @@ -21,6 +21,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => LogHealth(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index fd13a1c3..9b8325ea 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -102,6 +102,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("all") .Executes(r => DoDropAction(r.Source, inventoryId: null, Arguments.GetInteger(r, "Slot"), WindowActionType.DropItemStack)))))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/List.cs b/MinecraftClient/Commands/List.cs index b06a4b9a..7e991f55 100644 --- a/MinecraftClient/Commands/List.cs +++ b/MinecraftClient/Commands/List.cs @@ -22,6 +22,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoListPlayers(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Look.cs b/MinecraftClient/Commands/Look.cs index 5c78f47e..69f0dc2d 100644 --- a/MinecraftClient/Commands/Look.cs +++ b/MinecraftClient/Commands/Look.cs @@ -47,6 +47,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Location", MccArguments.Location()) .Executes(r => LookAtLocation(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index 89065765..fa94a06b 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -79,6 +79,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("-f") .Executes(r => MoveToLocation(r.Source, MccArguments.GetLocation(r, "location"), true)))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Reco.cs b/MinecraftClient/Commands/Reco.cs index 569ca465..482b4e0b 100644 --- a/MinecraftClient/Commands/Reco.cs +++ b/MinecraftClient/Commands/Reco.cs @@ -24,6 +24,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("AccountNick", MccArguments.AccountNick()) .Executes(r => DoReconnect(r.Source, Arguments.GetString(r, "AccountNick")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Reload.cs b/MinecraftClient/Commands/Reload.cs index be8ba98d..95359159 100644 --- a/MinecraftClient/Commands/Reload.cs +++ b/MinecraftClient/Commands/Reload.cs @@ -21,6 +21,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoReload(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs index d494e0b7..f8dd542a 100644 --- a/MinecraftClient/Commands/Respawn.cs +++ b/MinecraftClient/Commands/Respawn.cs @@ -21,6 +21,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoRespawn(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index 3c8dd07c..d2f310dd 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -23,6 +23,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Script", Arguments.GreedyString()) .Executes(r => DoExecuteScript(r.Source, Arguments.GetString(r, "Script"), null))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Set.cs b/MinecraftClient/Commands/Set.cs index e46cfebb..75d8bbd0 100644 --- a/MinecraftClient/Commands/Set.cs +++ b/MinecraftClient/Commands/Set.cs @@ -22,6 +22,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Expression", Arguments.GreedyString()) .Executes(r => DoSetVar(r.Source, Arguments.GetString(r, "Expression")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index f5401ffb..01d470fa 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -22,6 +22,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoSneak(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Tps.cs b/MinecraftClient/Commands/Tps.cs index f96e9674..a30749bb 100644 --- a/MinecraftClient/Commands/Tps.cs +++ b/MinecraftClient/Commands/Tps.cs @@ -22,6 +22,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoLogTps(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Upgrade.cs b/MinecraftClient/Commands/Upgrade.cs index 30757eaa..35ae3783 100644 --- a/MinecraftClient/Commands/Upgrade.cs +++ b/MinecraftClient/Commands/Upgrade.cs @@ -37,6 +37,7 @@ namespace MinecraftClient.Commands .Then(l => l.Literal("cancel") .Executes(r => CancelDownloadUpdate(r.Source))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 1768cf38..4a0fe1f6 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -22,6 +22,7 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DoUseItem(r.Source)) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 1169c994..994e34ae 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -24,6 +24,7 @@ namespace MinecraftClient.Commands .Then(l => l.Argument("Location", MccArguments.Location()) .Executes(r => UseBlockAtLocation(r.Source, MccArguments.GetLocation(r, "Location")))) .Then(l => l.Literal("_help") + .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) ); } From 7ee08092d4f4888fb4465723bdef3bc91cf69795 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Wed, 14 Dec 2022 14:45:51 +0800 Subject: [PATCH 07/44] legacy color support --- .github/workflows/build-and-release.yml | 2 +- MinecraftClient.sln | 4 +- MinecraftClient/ChatBots/DiscordBridge.cs | 14 +++---- MinecraftClient/ChatBots/Farmer.cs | 18 ++++----- MinecraftClient/ChatBots/TelegramBridge.cs | 16 ++++---- MinecraftClient/ColorHelper.cs | 35 ++++++++++++++--- MinecraftClient/Commands/Chunk.cs | 8 ++-- MinecraftClient/Crypto/AesCfb8Stream.cs | 20 ++++------ MinecraftClient/MinecraftClient.csproj | 2 +- MinecraftClient/Program.cs | 13 +++---- .../Protocol/Message/ChatParser.cs | 8 ++-- .../ConfigComments/ConfigComments.Designer.cs | 22 ++++------- .../ConfigComments/ConfigComments.resx | 12 +++--- MinecraftClient/Settings.cs | 39 ++++++++++++------- 14 files changed, 116 insertions(+), 97 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 0ab15085..5e74359a 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -8,7 +8,7 @@ on: env: PROJECT: "MinecraftClient" - target-version: "net6.0" + target-version: "net7.0" compile-flags: "--self-contained=true -c Release -p:UseAppHost=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None" jobs: diff --git a/MinecraftClient.sln b/MinecraftClient.sln index d769e6ae..8f0049d8 100644 --- a/MinecraftClient.sln +++ b/MinecraftClient.sln @@ -26,8 +26,8 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - RESX_SortFileContentOnSave = True - SolutionGuid = {6DED60F4-9CF4-4DB3-8966-582B2EBE8487} RESX_ShowErrorsInErrorList = False + SolutionGuid = {6DED60F4-9CF4-4DB3-8966-582B2EBE8487} + RESX_SortFileContentOnSave = False EndGlobalSection EndGlobal diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index b9d5a50e..b46ad770 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -163,7 +163,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_DiscordBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_DiscordBridge_canceled_sending); LogDebugToConsole(e); } @@ -233,7 +233,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_DiscordBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_DiscordBridge_canceled_sending); LogDebugToConsole(e); } } @@ -249,7 +249,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_DiscordBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_DiscordBridge_canceled_sending); LogDebugToConsole(e); } } @@ -265,7 +265,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_DiscordBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_DiscordBridge_canceled_sending); LogDebugToConsole(e); } } @@ -291,7 +291,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_DiscordBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_DiscordBridge_canceled_sending); LogDebugToConsole(e); } } @@ -420,12 +420,12 @@ namespace MinecraftClient.ChatBots }); IsConnected = true; - LogToConsole("§y§l§f" + Translations.bot_DiscordBridge_connected); + LogToConsole("§§2§l§f" + Translations.bot_DiscordBridge_connected); await Task.Delay(-1); } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_DiscordBridge_unknown_error); + LogToConsole("§§4§l§f" + Translations.bot_DiscordBridge_unknown_error); LogToConsole(e); return; } diff --git a/MinecraftClient/ChatBots/Farmer.cs b/MinecraftClient/ChatBots/Farmer.cs index 072fe270..b035e22c 100644 --- a/MinecraftClient/ChatBots/Farmer.cs +++ b/MinecraftClient/ChatBots/Farmer.cs @@ -160,7 +160,7 @@ namespace MinecraftClient.ChatBots { if (!currentArg.Contains(':')) { - LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); + LogToConsole("§§6§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); continue; } @@ -168,7 +168,7 @@ namespace MinecraftClient.ChatBots if (parts.Length != 2) { - LogToConsole("§x§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); + LogToConsole("§§6§1§0" + string.Format(Translations.bot_farmer_warining_invalid_parameter, currentArg)); continue; } @@ -177,11 +177,11 @@ namespace MinecraftClient.ChatBots case "r": case "radius": if (!int.TryParse(parts[1], NumberStyles.Any, CultureInfo.CurrentCulture, out radius)) - LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); + LogToConsole("§§6§1§0" + Translations.bot_farmer_invalid_radius); if (radius <= 0) { - LogToConsole("§x§1§0" + Translations.bot_farmer_invalid_radius); + LogToConsole("§§6§1§0" + Translations.bot_farmer_invalid_radius); radius = 30; } @@ -194,7 +194,7 @@ namespace MinecraftClient.ChatBots if (parts[1].Equals("true") || parts[1].Equals("1")) { - LogToConsole("§x§1§0" + Translations.bot_farmer_warining_force_unsafe); + LogToConsole("§§6§1§0" + Translations.bot_farmer_warining_force_unsafe); allowUnsafe = true; } else allowUnsafe = false; @@ -208,7 +208,7 @@ namespace MinecraftClient.ChatBots if (parts[1].Equals("true") || parts[1].Equals("1")) { - LogToConsole("§w§1§f" + Translations.bot_farmer_warining_allow_teleport); + LogToConsole("§§4§1§f" + Translations.bot_farmer_warining_allow_teleport); allowTeleport = true; } else allowTeleport = false; @@ -252,9 +252,9 @@ namespace MinecraftClient.ChatBots private void MainPorcess() { - LogToConsole("§y§1§f" + Translations.bot_farmer_started); - LogToConsole("§y§1§f " + Translations.bot_farmer_crop_type + ": " + cropType); - LogToConsole("§y§1§f " + Translations.bot_farmer_radius + ": " + farmingRadius); + LogToConsole("§§2§1§f" + Translations.bot_farmer_started); + LogToConsole("§§2§1§f " + Translations.bot_farmer_crop_type + ": " + cropType); + LogToConsole("§§2§1§f " + Translations.bot_farmer_radius + ": " + farmingRadius); while (running) { diff --git a/MinecraftClient/ChatBots/TelegramBridge.cs b/MinecraftClient/ChatBots/TelegramBridge.cs index 6139a8d0..eac48542 100644 --- a/MinecraftClient/ChatBots/TelegramBridge.cs +++ b/MinecraftClient/ChatBots/TelegramBridge.cs @@ -159,7 +159,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_TelegramBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_TelegramBridge_canceled_sending); LogDebugToConsole(e); } @@ -209,7 +209,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_TelegramBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_TelegramBridge_canceled_sending); LogDebugToConsole(e); } } @@ -232,7 +232,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole("§w§l§f" + Translations.bot_TelegramBridge_canceled_sending); + LogToConsole("§§4§l§f" + Translations.bot_TelegramBridge_canceled_sending); LogDebugToConsole(e); } } @@ -254,7 +254,7 @@ namespace MinecraftClient.ChatBots } if (string.IsNullOrEmpty(Config.ChannelId.Trim())) - LogToConsole("§w§l§f" + Translations.bot_TelegramBridge_missing_channel_id); + LogToConsole("§§4§l§f" + Translations.bot_TelegramBridge_missing_channel_id); botClient = new TelegramBotClient(Config.Token.Trim()); cancellationToken = new CancellationTokenSource(); @@ -273,12 +273,12 @@ namespace MinecraftClient.ChatBots IsConnected = true; SendMessage($"✅ {Translations.bot_TelegramBridge_connected}"); - LogToConsole($"§y§l§f{Translations.bot_TelegramBridge_connected}"); + LogToConsole($"§§2§l§f{Translations.bot_TelegramBridge_connected}"); if (Config.Authorized_Chat_Ids.Length == 0) { SendMessage($"⚠️ *{Translations.bot_TelegramBridge_missing_authorized_channels}* ⚠️"); - LogToConsole($"§w§l§f{Translations.bot_TelegramBridge_missing_authorized_channels}"); + LogToConsole($"§§4§l§f{Translations.bot_TelegramBridge_missing_authorized_channels}"); return; } @@ -286,7 +286,7 @@ namespace MinecraftClient.ChatBots } catch (Exception e) { - LogToConsole($"§w§l§f{Translations.bot_TelegramBridge_unknown_error}"); + LogToConsole($"§§4§l§f{Translations.bot_TelegramBridge_unknown_error}"); LogToConsole(e); return; } @@ -368,7 +368,7 @@ namespace MinecraftClient.ChatBots _ => exception.ToString() }; - LogToConsole("§w§l§f" + ErrorMessage); + LogToConsole("§§4§l§f" + ErrorMessage); return Task.CompletedTask; } } diff --git a/MinecraftClient/ColorHelper.cs b/MinecraftClient/ColorHelper.cs index 3f35b304..fe91a780 100644 --- a/MinecraftClient/ColorHelper.cs +++ b/MinecraftClient/ColorHelper.cs @@ -1,5 +1,5 @@ using System; -using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; +using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig; namespace MinecraftClient { @@ -75,14 +75,37 @@ namespace MinecraftClient public static string GetColorEscapeCode(byte R, byte G, byte B, bool foreground) { - return GetColorEscapeCode(R, G, B, foreground, Settings.Config.Main.Advanced.TerminalColorDepth); + return GetColorEscapeCode(R, G, B, foreground, Settings.Config.Console.General.ConsoleColorMode); } - public static string GetColorEscapeCode(byte R, byte G, byte B, bool foreground, TerminalColorDepthType colorDepth) + public static string GetColorEscapeCode(byte R, byte G, byte B, bool foreground, ConsoleColorModeType colorDepth) { switch (colorDepth) { - case TerminalColorDepthType.bit_4: + case ConsoleColorModeType.disable: + return string.Empty; + + case ConsoleColorModeType.legacy_4bit: + { + ColorRGBA color = new(R, G, B); + int best_idx = 0; + double min_distance = ColorMap4[0].Item1.Distance(color); + for (int i = 1; i < ColorMap4.Length; ++i) + { + double distance = ColorMap4[i].Item1.Distance(color); + if (distance < min_distance) + { + min_distance = distance; + best_idx = i; + } + } + if (foreground) + return $"§{best_idx:X}"; + else + return $"§§{best_idx:X}"; + } + + case ConsoleColorModeType.vt100_4bit: { ColorRGBA color = new(R, G, B); int best_idx = 0; @@ -99,7 +122,7 @@ namespace MinecraftClient return string.Format("\u001b[{0}m", ColorMap4[best_idx].Item2 - (foreground ? 10 : 0)); } - case TerminalColorDepthType.bit_8: + case ConsoleColorModeType.vt100_8bit: { ColorRGBA color = new(R, G, B); int R_idx = (int)(R <= 95 ? Math.Round(R / 95.0) : 1 + Math.Round((R - 95.0) / 40.0)); @@ -126,7 +149,7 @@ namespace MinecraftClient return string.Format("\u001B[{0};5;{1}m", (foreground ? 38 : 48), ColorMap8[best_idx].Item2); } - case TerminalColorDepthType.bit_24: + case ConsoleColorModeType.vt100_24bit: return string.Format("\u001B[{0};2;{1};{2};{3}m", (foreground ? 38 : 48), R, G, B); default: diff --git a/MinecraftClient/Commands/Chunk.cs b/MinecraftClient/Commands/Chunk.cs index 3452716b..cdc26e14 100644 --- a/MinecraftClient/Commands/Chunk.cs +++ b/MinecraftClient/Commands/Chunk.cs @@ -203,9 +203,9 @@ namespace MinecraftClient.Commands for (int x = leftMost; x <= rightMost; ++x) { if (z == current.ChunkZ && x == current.ChunkX) - sb.Append("§z"); // Player Location: background gray + sb.Append("§§7"); // Player Location: background gray else if (z == markChunkZ && x == markChunkX) - sb.Append("§w"); // Marked chunk: background red + sb.Append("§§4"); // Marked chunk: background red ChunkColumn? chunkColumn = world[x, z]; if (chunkColumn == null) @@ -216,12 +216,12 @@ namespace MinecraftClient.Commands sb.Append(chunkStatusStr[1]); if ((z == current.ChunkZ && x == current.ChunkX) || (z == markChunkZ && x == markChunkX)) - sb.Append("§r"); // Reset background color + sb.Append("§§r"); // Reset background color } sb.Append('\n'); } - sb.Append(string.Format(Translations.cmd_chunk_icon, "§z §r", "§w §r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); + sb.Append(string.Format(Translations.cmd_chunk_icon, "§§7 §§r", "§§4 §§r", chunkStatusStr[0], chunkStatusStr[1], chunkStatusStr[2])); handler.Log.Info(sb.ToString()); return r.SetAndReturn(Status.Done); diff --git a/MinecraftClient/Crypto/AesCfb8Stream.cs b/MinecraftClient/Crypto/AesCfb8Stream.cs index f653546c..d50aaa54 100644 --- a/MinecraftClient/Crypto/AesCfb8Stream.cs +++ b/MinecraftClient/Crypto/AesCfb8Stream.cs @@ -9,7 +9,7 @@ namespace MinecraftClient.Crypto { public class AesCfb8Stream : Stream { - public static readonly int blockSize = 16; + public const int blockSize = 16; private readonly Aes? Aes = null; private readonly FastAes? FastAes = null; @@ -109,7 +109,7 @@ namespace MinecraftClient.Crypto if (inStreamEnded) return 0; - Span blockOutput = FastAes != null ? stackalloc byte[blockSize] : null; + Span blockOutput = stackalloc byte[blockSize]; byte[] inputBuf = new byte[blockSize + required]; Array.Copy(ReadStreamIV, inputBuf, blockSize); @@ -135,18 +135,12 @@ namespace MinecraftClient.Crypto } else { - OrderablePartitioner> rangePartitioner = curRead <= 256 ? - Partitioner.Create(readed, processEnd, 32) : Partitioner.Create(readed, processEnd); - Parallel.ForEach(rangePartitioner, (range, loopState) => + for (int idx = readed; idx < processEnd; idx++) { - Span blockOutput = stackalloc byte[blockSize]; - for (int idx = range.Item1; idx < range.Item2; idx++) - { - ReadOnlySpan blockInput = new(inputBuf, idx, blockSize); - Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None); - buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]); - } - }); + ReadOnlySpan blockInput = new(inputBuf, idx, blockSize); + Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None); + buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]); + } } } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 95597b42..b0faada9 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -1,6 +1,6 @@  - net6.0 + net7.0 Exe publish\ false diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index cd525fb4..503f4f40 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -18,6 +18,7 @@ using MinecraftClient.Scripting; using MinecraftClient.WinAPI; using Tomlet; using static MinecraftClient.Settings; +using static MinecraftClient.Settings.ConsoleConfigHealper.ConsoleConfig; using static MinecraftClient.Settings.MainConfigHealper.MainConfig.AdvancedConfig; using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; @@ -98,9 +99,7 @@ namespace MinecraftClient //Build information to facilitate processing of bug reports if (BuildInfo != null) - { ConsoleIO.WriteLineFormatted("§8" + BuildInfo); - } //Debug input ? if (args.Length == 1 && args[0] == "--keyboard-debug") @@ -288,7 +287,7 @@ namespace MinecraftClient } } - if (Config.Main.Advanced.ConsoleTitle != "") + if (!string.IsNullOrWhiteSpace(Config.Main.Advanced.ConsoleTitle)) { InternalConfig.Username = "New Window"; Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); @@ -319,28 +318,28 @@ namespace MinecraftClient Random random = new(); { // Test 8 bit color StringBuilder sb = new(); - sb.Append("[0123456789]: (8bit)["); + sb.Append("[0123456789]: (vt100 8bit)["); for (int i = 0; i < 10; ++i) { sb.Append(ColorHelper.GetColorEscapeCode((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255), true, - TerminalColorDepthType.bit_8)).Append(i); + ConsoleColorModeType.vt100_8bit)).Append(i); } sb.Append(ColorHelper.GetResetEscapeCode()).Append(']'); ConsoleIO.WriteLine(string.Format(Translations.debug_color_test, sb.ToString())); } { // Test 24 bit color StringBuilder sb = new(); - sb.Append("[0123456789]: (24bit)["); + sb.Append("[0123456789]: (vt100 24bit)["); for (int i = 0; i < 10; ++i) { sb.Append(ColorHelper.GetColorEscapeCode((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255), true, - TerminalColorDepthType.bit_24)).Append(i); + ConsoleColorModeType.vt100_24bit)).Append(i); } sb.Append(ColorHelper.GetResetEscapeCode()).Append(']'); ConsoleIO.WriteLine(string.Format(Translations.debug_color_test, sb.ToString())); diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 3df06572..2aa87b00 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -119,7 +119,7 @@ namespace MinecraftClient.Protocol.Message if (message.isSystemChat) { if (Config.Signature.MarkSystemMessage) - color = "§z §r "; // Custom color code §z : Background Gray + color = "§§7 §§r "; // Background Gray } else { @@ -128,18 +128,18 @@ namespace MinecraftClient.Protocol.Message if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) { if (Config.Signature.MarkModifiedMsg) - color = "§x §r "; // Custom color code §x : Background Yellow + color = "§§6 §§r "; // Background Yellow } else { if (Config.Signature.MarkLegallySignedMsg) - color = "§y §r "; // Custom color code §y : Background Green + color = "§§2 §§r "; // Background Green } } else { if (Config.Signature.MarkIllegallySignedMsg) - color = "§w §r "; // Custom color code §w : Background Red + color = "§§4 §§r "; // Background Red } } return color + text; diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index bf0cbc7f..8f358c62 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1085,7 +1085,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The settings for command completion suggestions.. + /// Looks up a localized string similar to The settings for command completion suggestions. + ///Custom colors are only available when using "vt100_24bit" color mode.. /// internal static string Console_CommandSuggestion { get { @@ -1112,20 +1113,20 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to If a garbled code like "←[0m" appears on the terminal, you can turn off this.. + /// Looks up a localized string similar to Use "disable", "legacy_4bit", "vt100_4bit", "vt100_8bit" or "vt100_24bit". If a garbled code like "←[0m" appears on the terminal, you can try switching to "legacy_4bit" mode, or just disable it.. /// - internal static string Console_Enable_Color { + internal static string Console_General_ConsoleColorMode { get { - return ResourceManager.GetString("Console.Enable_Color", resourceCulture); + return ResourceManager.GetString("Console.General.ConsoleColorMode", resourceCulture); } } /// /// Looks up a localized string similar to You can use "Ctrl+P" to print out the current input and cursor position.. /// - internal static string Console_General_Display_Uesr_Input { + internal static string Console_General_Display_Input { get { - return ResourceManager.GetString("Console.General.Display_Uesr_Input", resourceCulture); + return ResourceManager.GetString("Console.General.Display_Input", resourceCulture); } } @@ -1559,15 +1560,6 @@ namespace MinecraftClient { } } - /// - /// Looks up a localized string similar to Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log.. - /// - internal static string Main_Advanced_TerminalColorDepth { - get { - return ResourceManager.GetString("Main.Advanced.TerminalColorDepth", resourceCulture); - } - } - /// /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. /// diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index 1b71fcd9..bd3a27de 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -539,7 +539,8 @@ When this happens, you'll need to configure chat format below, see https://mccte Console-related settings. - The settings for command completion suggestions. + The settings for command completion suggestions. +Custom colors are only available when using "vt100_24bit" color mode. Whether to display command suggestions in the console. @@ -547,10 +548,10 @@ When this happens, you'll need to configure chat format below, see https://mccte Enable this option if the arrows in the command suggestions are not displayed properly in your terminal. - - If a garbled code like "←[0m" appears on the terminal, you can turn off this. + + Use "disable", "legacy_4bit", "vt100_4bit", "vt100_8bit" or "vt100_24bit". If a garbled code like "←[0m" appears on the terminal, you can try switching to "legacy_4bit" mode, or just disable it. - + You can use "Ctrl+P" to print out the current input and cursor position. @@ -701,9 +702,6 @@ Usage examples: "/tell <mybot> connect Server1", "/connect Server2" Temporary fix for Badpacket issue on some servers. - - Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log. - Uses more ram, cpu, bandwidth but allows you to move around. diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 0a07bb40..8ddd556b 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -605,9 +605,6 @@ namespace MinecraftClient [TomlInlineComment("$Main.Advanced.enable_emoji$")] public bool EnableEmoji = true; - [TomlInlineComment("$Main.Advanced.TerminalColorDepth$")] - public TerminalColorDepthType TerminalColorDepth = TerminalColorDepthType.bit_24; - [TomlInlineComment("$Main.Advanced.MinTerminalWidth$")] public int MinTerminalWidth = 16; @@ -640,8 +637,6 @@ namespace MinecraftClient public enum ResolveSrvRecordType { no, fast, yes }; public enum ForgeConfigType { no, auto, force }; - - public enum TerminalColorDepthType { bit_4, bit_8, bit_24 }; } public struct AccountInfoConfig @@ -788,12 +783,26 @@ namespace MinecraftClient public void OnSettingUpdate() { - ConsoleInteractive.ConsoleWriter.EnableColor = General.Enable_Color; + // Reader + ConsoleInteractive.ConsoleReader.DisplayUesrInput = General.Display_Input; - ConsoleInteractive.ConsoleReader.DisplayUesrInput = General.Display_Uesr_Input; + // Writer + ConsoleInteractive.ConsoleWriter.EnableColor = General.ConsoleColorMode != ConsoleColorModeType.disable; + + ConsoleInteractive.ConsoleWriter.UseVT100ColorCode = General.ConsoleColorMode != ConsoleColorModeType.legacy_4bit; + + // Buffer + General.History_Input_Records = + ConsoleInteractive.ConsoleBuffer.SetBackreadBufferLimit(General.History_Input_Records); + + // Suggestion + if (General.ConsoleColorMode == ConsoleColorModeType.disable) + CommandSuggestion.Enable_Color = false; ConsoleInteractive.ConsoleSuggestion.EnableColor = CommandSuggestion.Enable_Color; + ConsoleInteractive.ConsoleSuggestion.Enable24bitColor = General.ConsoleColorMode == ConsoleColorModeType.vt100_24bit; + ConsoleInteractive.ConsoleSuggestion.UseBasicArrow = CommandSuggestion.Use_Basic_Arrow; CommandSuggestion.Max_Suggestion_Width = @@ -802,7 +811,7 @@ namespace MinecraftClient CommandSuggestion.Max_Displayed_Suggestions = ConsoleInteractive.ConsoleSuggestion.SetMaxSuggestionCount(CommandSuggestion.Max_Displayed_Suggestions); - // CommandSuggestion color settings + // Suggestion color settings { if (!CheckColorCode(CommandSuggestion.Text_Color)) { @@ -872,11 +881,14 @@ namespace MinecraftClient [TomlDoNotInlineObject] public class MainConfig { - [TomlInlineComment("$Console.Enable_Color$")] - public bool Enable_Color = true; + [TomlInlineComment("$Console.General.ConsoleColorMode$")] + public ConsoleColorModeType ConsoleColorMode = ConsoleColorModeType.vt100_24bit; - [TomlInlineComment("$Console.General.Display_Uesr_Input$")] - public bool Display_Uesr_Input = true; + [TomlInlineComment("$Console.General.Display_Input$")] + public bool Display_Input = true; + + [TomlInlineComment("$Console.General.History_Input_Records$")] + public int History_Input_Records = 32; } [TomlDoNotInlineObject] @@ -885,7 +897,6 @@ namespace MinecraftClient [TomlInlineComment("$Console.CommandSuggestion.Enable$")] public bool Enable = true; - [TomlInlineComment("$Console.Enable_Color$")] public bool Enable_Color = true; [TomlInlineComment("$Console.CommandSuggestion.Use_Basic_Arrow$")] @@ -909,6 +920,8 @@ namespace MinecraftClient public string Arrow_Symbol_Color = "#d1d5db"; } + + public enum ConsoleColorModeType { disable, legacy_4bit, vt100_4bit, vt100_8bit, vt100_24bit }; } } From 7a9bc7bd1dba851c7335709efd2835b77071d2cf Mon Sep 17 00:00:00 2001 From: Milutinke Date: Sat, 17 Dec 2022 18:25:21 +0100 Subject: [PATCH 08/44] Added 1.19.3 packet palette. TODO: Implement changes in the protocol handler. --- .../PacketPalettes/PacketPalette119.cs | 65 ++++--- .../PacketPalettes/PacketPalette1192.cs | 28 +-- .../PacketPalettes/PacketPalette1193.cs | 183 ++++++++++++++++++ .../Protocol/Handlers/PacketTypesIn.cs | 3 + .../Protocol/Handlers/PacketTypesOut.cs | 1 + .../Protocol/Handlers/Protocol18.cs | 1 + 6 files changed, 235 insertions(+), 46 deletions(-) create mode 100644 MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs index 6ec16097..6c16552b 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs @@ -6,12 +6,12 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { private readonly Dictionary typeIn = new() { - { 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) - DONE + { 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) { 0x01, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb) { 0x02, PacketTypesIn.SpawnPlayer }, // { 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound)) { 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics) - { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) - DONE + { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) { 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage) { 0x07, PacketTypesIn.BlockEntityData }, // { 0x08, PacketTypesIn.BlockAction }, // @@ -28,10 +28,10 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x13, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot) { 0x14, PacketTypesIn.SetCooldown }, // { 0x15, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) - { 0x16, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) - DONE (No need to be implemented) + { 0x16, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) (No need to be implemented) { 0x17, PacketTypesIn.Disconnect }, // { 0x18, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) - { 0x19, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) - DONE + { 0x19, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) { 0x1A, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) { 0x1B, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event) { 0x1C, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open) @@ -39,9 +39,9 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x1E, PacketTypesIn.KeepAlive }, // { 0x1F, PacketTypesIn.ChunkData }, // { 0x20, PacketTypesIn.Effect }, // (Wiki name: Level Event) - { 0x21, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) - DONE (No need to be implemented) + { 0x21, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) (No need to be implemented) { 0x22, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update) - { 0x23, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) - DONE + { 0x23, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) { 0x24, PacketTypesIn.MapData }, // (Wiki name: Map Item Data) { 0x25, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers) { 0x26, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position) @@ -58,14 +58,14 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x31, PacketTypesIn.EndCombatEvent }, // (Wiki name: Player Combat End) { 0x32, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Player Combat Enter) { 0x33, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Player Combat Kill) - { 0x34, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) - DONE + { 0x34, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) { 0x35, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At) { 0x36, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Player Position) { 0x37, PacketTypesIn.UnlockRecipes }, // (Wiki name: Recipe) { 0x38, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites) { 0x39, PacketTypesIn.RemoveEntityEffect }, // { 0x3A, PacketTypesIn.ResourcePackSend }, // (Wiki name: Resource Pack) - { 0x3B, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) - DONE + { 0x3B, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) { 0x3C, PacketTypesIn.EntityHeadLook }, // (Wiki name: Rotate Head) { 0x3D, PacketTypesIn.MultiBlockChange }, // (Wiki name: Sections Block Update) { 0x3E, PacketTypesIn.SelectAdvancementTab }, // @@ -99,7 +99,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x5A, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title) { 0x5B, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Titles Animation) { 0x5C, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity) - { 0x5D, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) - DONE (No need to be implemented) + { 0x5D, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) (No need to be implemented) { 0x5E, PacketTypesIn.StopSound }, // { 0x5F, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message) { 0x60, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Tab List) @@ -108,7 +108,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x63, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity) { 0x64, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements) { 0x65, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes) - { 0x66, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) - DONE + { 0x66, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) { 0x67, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes) { 0x68, PacketTypesIn.Tags }, // (Wiki name: Update Tags) }; @@ -118,9 +118,9 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly) - { 0x03, PacketTypesOut.ChatCommand }, // Added in 1.19 - { 0x04, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) - { 0x05, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound)) + { 0x03, PacketTypesOut.MessageAcknowledgment }, // TODO + { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 + { 0x05, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) { 0x06, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) { 0x07, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) { 0x08, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) @@ -143,28 +143,29 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x19, PacketTypesOut.PickItem }, // { 0x1A, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) { 0x1B, PacketTypesOut.PlayerAbilities }, // - { 0x1C, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) - DONE + { 0x1C, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) { 0x1D, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) { 0x1E, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) { 0x1F, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) - { 0x20, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) - { 0x21, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) - { 0x22, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) - { 0x23, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) - { 0x24, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) - { 0x25, PacketTypesOut.SelectTrade }, // - { 0x26, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - DONE - (No need to be implemented) - { 0x27, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) - { 0x28, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) - { 0x29, PacketTypesOut.UpdateCommandBlockMinecart }, // - { 0x2A, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) - { 0x2B, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) - { 0x2C, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) - { 0x2D, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) - { 0x2E, PacketTypesOut.Animation }, // (Wiki name: Swing) - { 0x2F, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) - { 0x30, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - DONE - { 0x31, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) - DONE + { 0x20, PacketTypesOut.PlayerSession }, // Added in 1.19.3 TODO + { 0x21, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) + { 0x22, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) + { 0x23, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) + { 0x24, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) + { 0x25, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) + { 0x26, PacketTypesOut.SelectTrade }, // + { 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) + { 0x28, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) + { 0x29, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) + { 0x2A, PacketTypesOut.UpdateCommandBlockMinecart }, // + { 0x2B, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) + { 0x2C, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) + { 0x2D, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) + { 0x2E, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) + { 0x2F, PacketTypesOut.Animation }, // (Wiki name: Swing) + { 0x30, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) + { 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) }; protected override Dictionary GetListIn() diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1192.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1192.cs index f497917e..1f743945 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1192.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1192.cs @@ -6,12 +6,12 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { private readonly Dictionary typeIn = new() { - { 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) - DONE + { 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) { 0x01, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb) { 0x02, PacketTypesIn.SpawnPlayer }, // { 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound)) { 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics) - { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) - DONE + { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) { 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage) { 0x07, PacketTypesIn.BlockEntityData }, // { 0x08, PacketTypesIn.BlockAction }, // @@ -29,11 +29,11 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x14, PacketTypesIn.SetCooldown }, // { 0x15, PacketTypesIn.ChatSuggestions }, // Added 1.19.1 { 0x16, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) - { 0x17, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) - DONE (No need to be implemented) + { 0x17, PacketTypesIn.NamedSoundEffect }, // Changed in 1.19 (Added "Speed" field) (Wiki name: Custom Sound Effect) (No need to be implemented) { 0x18, PacketTypesIn.HideMessage }, // Added 1.19.1 { 0x19, PacketTypesIn.Disconnect }, // { 0x1A, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) - { 0x1B, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) - DONE + { 0x1B, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) { 0x1C, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) { 0x1D, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event) { 0x1E, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open) @@ -41,9 +41,9 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x20, PacketTypesIn.KeepAlive }, // { 0x21, PacketTypesIn.ChunkData }, // { 0x22, PacketTypesIn.Effect }, // (Wiki name: Level Event) - { 0x23, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) - DONE (No need to be implemented) + { 0x23, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) (No need to be implemented) { 0x24, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update) - { 0x25, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) - DONE + { 0x25, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) { 0x26, PacketTypesIn.MapData }, // (Wiki name: Map Item Data) { 0x27, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers) { 0x28, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position) @@ -61,14 +61,14 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x34, PacketTypesIn.EndCombatEvent }, // (Wiki name: Player Combat End) { 0x35, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Player Combat Enter) { 0x36, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Player Combat Kill) - { 0x37, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) - DONE + { 0x37, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) { 0x38, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At) { 0x39, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Player Position) { 0x3A, PacketTypesIn.UnlockRecipes }, // (Wiki name: Recipe) { 0x3B, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites) { 0x3C, PacketTypesIn.RemoveEntityEffect }, // { 0x3D, PacketTypesIn.ResourcePackSend }, // (Wiki name: Resource Pack) - { 0x3E, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) - DONE + { 0x3E, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) { 0x3F, PacketTypesIn.EntityHeadLook }, // (Wiki name: Rotate Head) { 0x40, PacketTypesIn.MultiBlockChange }, // (Wiki name: Sections Block Update) { 0x41, PacketTypesIn.SelectAdvancementTab }, // @@ -102,7 +102,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x5D, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title) { 0x5E, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Titles Animation) { 0x5F, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity) - { 0x60, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) - DONE (No need to be implemented) + { 0x60, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) (No need to be implemented) { 0x61, PacketTypesIn.StopSound }, // { 0x62, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message) { 0x63, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Tab List) @@ -111,7 +111,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x66, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity) { 0x67, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements) { 0x68, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes) - { 0x69, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) - DONE + { 0x69, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) { 0x6A, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes) { 0x6B, PacketTypesIn.Tags }, // (Wiki name: Update Tags) }; @@ -147,7 +147,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x1A, PacketTypesOut.PickItem }, // { 0x1B, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) { 0x1C, PacketTypesOut.PlayerAbilities }, // - { 0x1D, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) - DONE + { 0x1D, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) { 0x1E, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) { 0x1F, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) { 0x20, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) @@ -157,7 +157,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x24, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) { 0x25, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) { 0x26, PacketTypesOut.SelectTrade }, // - { 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - DONE - (No need to be implemented) + { 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) { 0x28, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) { 0x29, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) { 0x2A, PacketTypesOut.UpdateCommandBlockMinecart }, // @@ -167,8 +167,8 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x2E, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) { 0x2F, PacketTypesOut.Animation }, // (Wiki name: Swing) { 0x30, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) - { 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - DONE - { 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) - DONE + { 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) }; protected override Dictionary GetListIn() diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs new file mode 100644 index 00000000..6b550948 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs @@ -0,0 +1,183 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes +{ + public class PacketPalette1193 : PacketTypePalette + { + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.SpawnEntity }, // Changed in 1.19 (Wiki name: Spawn Entity) + { 0x01, PacketTypesIn.SpawnExperienceOrb }, // (Wiki name: Spawn Exeprience Orb) + { 0x02, PacketTypesIn.SpawnPlayer }, // + { 0x03, PacketTypesIn.EntityAnimation }, // (Wiki name: Entity Animation (clientbound)) + { 0x04, PacketTypesIn.Statistics }, // (Wiki name: Award Statistics) + { 0x05, PacketTypesIn.BlockChangedAck }, // Added 1.19 (Wiki name: Acknowledge Block Change) + { 0x06, PacketTypesIn.BlockBreakAnimation }, // (Wiki name: Set Block Destroy Stage) + { 0x07, PacketTypesIn.BlockEntityData }, // + { 0x08, PacketTypesIn.BlockAction }, // + { 0x09, PacketTypesIn.BlockChange }, // (Wiki name: Block Update) + { 0x0A, PacketTypesIn.BossBar }, // + { 0x0B, PacketTypesIn.ServerDifficulty }, // (Wiki name: Change Difficulty) + { 0x0C, PacketTypesIn.ClearTiles }, // + { 0x0D, PacketTypesIn.TabComplete }, // (Wiki name: Command Suggestions Response) + { 0x0E, PacketTypesIn.DeclareCommands }, // (Wiki name: Commands) + { 0x0F, PacketTypesIn.CloseWindow }, // (Wiki name: Close Container (clientbound)) + { 0x10, PacketTypesIn.WindowItems }, // (Wiki name: Set Container Content) + { 0x11, PacketTypesIn.WindowProperty }, // (Wiki name: Set Container Property) + { 0x12, PacketTypesIn.SetSlot }, // (Wiki name: Set Container Slot) + { 0x13, PacketTypesIn.SetCooldown }, // + { 0x14, PacketTypesIn.ChatSuggestions }, // Added in 1.19.1 + { 0x15, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) + { 0x16, PacketTypesIn.HideMessage }, // Added in 1.19.1 + { 0x17, PacketTypesIn.Disconnect }, // + { 0x18, PacketTypesIn.ProfilelessChatMessage }, // Added in 1.19.3 - TODO + { 0x19, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) + { 0x1A, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) + { 0x1B, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) + { 0x1C, PacketTypesIn.ChangeGameState }, // (Wiki name: Game Event) + { 0x1D, PacketTypesIn.OpenHorseWindow }, // (Wiki name: Horse Screen Open) + { 0x1E, PacketTypesIn.InitializeWorldBorder }, // + { 0x1F, PacketTypesIn.KeepAlive }, // + { 0x20, PacketTypesIn.ChunkData }, // + { 0x21, PacketTypesIn.Effect }, // (Wiki name: World Event) + { 0x22, PacketTypesIn.Particle }, // Changed in 1.19 ("Particle Data" field is now "Max Speed", it's the same Float data type) (Wiki name: Level Particle) (No need to be implemented) + { 0x23, PacketTypesIn.UpdateLight }, // (Wiki name: Light Update) + { 0x24, PacketTypesIn.JoinGame }, // Changed in 1.19 (lot's of changes) (Wiki name: Login (play)) + { 0x25, PacketTypesIn.MapData }, // (Wiki name: Map Item Data) + { 0x26, PacketTypesIn.TradeList }, // (Wiki name: Merchant Offers) + { 0x27, PacketTypesIn.EntityPosition }, // (Wiki name: Move Entity Position) + { 0x28, PacketTypesIn.EntityPositionAndRotation }, // (Wiki name: Move Entity Position and Rotation) + { 0x29, PacketTypesIn.EntityRotation }, // (Wiki name: Move Entity Rotation) + { 0x2A, PacketTypesIn.VehicleMove }, // (Wiki name: Move Vehicle) + { 0x2B, PacketTypesIn.OpenBook }, // + { 0x2C, PacketTypesIn.OpenWindow }, // (Wiki name: Open Screen) + { 0x2D, PacketTypesIn.OpenSignEditor }, // + { 0x2E, PacketTypesIn.Ping }, // (Wiki name: Ping (play)) + { 0x2F, PacketTypesIn.CraftRecipeResponse }, // (Wiki name: Place Ghost Recipe) + { 0x30, PacketTypesIn.PlayerAbilities }, // + { 0x31, PacketTypesIn.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Player Chat Message) - TODO + { 0x32, PacketTypesIn.EndCombatEvent }, // (Wiki name: Player Combat End) + { 0x33, PacketTypesIn.EnterCombatEvent }, // (Wiki name: Player Combat Enter) + { 0x34, PacketTypesIn.DeathCombatEvent }, // (Wiki name: Player Combat Kill) + { 0x35, PacketTypesIn.PlayerRemove }, // Added in 1.19.3 (Not used) + { 0x36, PacketTypesIn.PlayerInfo }, // Changed in 1.19 (Heavy changes) + { 0x37, PacketTypesIn.FacePlayer }, // (Wiki name: Player Look At) + { 0x38, PacketTypesIn.PlayerPositionAndLook }, // (Wiki name: Player Position) + { 0x39, PacketTypesIn.UnlockRecipes }, // (Wiki name: Recipe) + { 0x3A, PacketTypesIn.DestroyEntities }, // (Wiki name: Remove Entites) + { 0x3B, PacketTypesIn.RemoveEntityEffect }, // + { 0x3C, PacketTypesIn.ResourcePackSend }, // (Wiki name: Resource Pack) + { 0x3D, PacketTypesIn.Respawn }, // Changed in 1.19 (Heavy changes) + { 0x3E, PacketTypesIn.EntityHeadLook }, // (Wiki name: Rotate Head) + { 0x3F, PacketTypesIn.MultiBlockChange }, // (Wiki name: Sections Block Update) + { 0x40, PacketTypesIn.SelectAdvancementTab }, // + { 0x41, PacketTypesIn.ServerData }, // Added in 1.19 + { 0x42, PacketTypesIn.ActionBar }, // (Wiki name: Set Action Bar Text) + { 0x43, PacketTypesIn.WorldBorderCenter }, // (Wiki name: Set Border Center) + { 0x44, PacketTypesIn.WorldBorderLerpSize }, // + { 0x45, PacketTypesIn.WorldBorderSize }, // (Wiki name: Set World Border Size) + { 0x46, PacketTypesIn.WorldBorderWarningDelay }, // (Wiki name: Set World Border Warning Delay) + { 0x47, PacketTypesIn.WorldBorderWarningReach }, // (Wiki name: Set Border Warning Distance) + { 0x48, PacketTypesIn.Camera }, // (Wiki name: Set Camera) + { 0x49, PacketTypesIn.HeldItemChange }, // (Wiki name: Set Carried Item (clientbound)) + { 0x4A, PacketTypesIn.UpdateViewPosition }, // (Wiki name: Set Chunk Cache Center) + { 0x4B, PacketTypesIn.UpdateViewDistance }, // (Wiki name: Set Chunk Cache Radius) + { 0x4C, PacketTypesIn.SpawnPosition }, // (Wiki name: Set Default Spawn Position) + { 0x4D, PacketTypesIn.DisplayScoreboard }, // (Wiki name: Set Display Objective) + { 0x4E, PacketTypesIn.EntityMetadata }, // (Wiki name: Set Entity Metadata) + { 0x4F, PacketTypesIn.AttachEntity }, // (Wiki name: Set Entity Link) + { 0x50, PacketTypesIn.EntityVelocity }, // (Wiki name: Set Entity Motion) + { 0x51, PacketTypesIn.EntityEquipment }, // (Wiki name: Set Equipment) + { 0x52, PacketTypesIn.SetExperience }, // + { 0x53, PacketTypesIn.UpdateHealth }, // (Wiki name: Set Health) + { 0x54, PacketTypesIn.ScoreboardObjective }, // (Wiki name: Set Objective) + { 0x55, PacketTypesIn.SetPassengers }, // + { 0x56, PacketTypesIn.Teams }, // (Wiki name: Set Player Team) + { 0x57, PacketTypesIn.UpdateScore }, // (Wiki name: Set Score) + { 0x58, PacketTypesIn.UpdateSimulationDistance }, // (Wiki name: Set Simulation Distance) + { 0x59, PacketTypesIn.SetTitleSubTitle }, // (Wiki name: Set Subtitle Test) + { 0x5A, PacketTypesIn.TimeUpdate }, // (Wiki name: Set Time) + { 0x5B, PacketTypesIn.SetTitleText }, // (Wiki name: Set Title) + { 0x5C, PacketTypesIn.SetTitleTime }, // (Wiki name: Set Titles Animation) + { 0x5D, PacketTypesIn.EntitySoundEffect }, // (Wiki name: Sound Entity) + { 0x5E, PacketTypesIn.SoundEffect }, // Changed in 1.19 (Added "Seed" field) (Wiki name: Sound Effect) (No need to be implemented) + { 0x5F, PacketTypesIn.StopSound }, // + { 0x60, PacketTypesIn.SystemChat }, // Added in 1.19 (Wiki name: System Chat Message) + { 0x61, PacketTypesIn.PlayerListHeaderAndFooter }, // (Wiki name: Tab List) + { 0x62, PacketTypesIn.NBTQueryResponse }, // (Wiki name: Tab Query) + { 0x63, PacketTypesIn.CollectItem }, // (Wiki name: Take Item Entity) + { 0x64, PacketTypesIn.EntityTeleport }, // (Wiki name: Teleport Entity) + { 0x65, PacketTypesIn.Advancements }, // (Wiki name: Update Advancements) + { 0x66, PacketTypesIn.EntityProperties }, // (Wiki name: Update Attributes) + { 0x67, PacketTypesIn.FeatureFlags }, // Added in 1.19.3 (Not yet clear what is the purpose of this packet) + { 0x68, PacketTypesIn.EntityEffect }, // Changed in 1.19 (Added "Has Factor Data" and "Factor Codec" fields) (Wiki name: Entity Effect) + { 0x69, PacketTypesIn.DeclareRecipes }, // (Wiki name: Update Recipes) + { 0x6A, PacketTypesIn.Tags }, // (Wiki name: Update Tags) + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) + { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) + { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly) + { 0x03, PacketTypesOut.MessageAcknowledgment }, // Added in 1.19.1 + { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 + { 0x05, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x06, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound)) + { 0x07, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) + { 0x08, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) + { 0x09, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) + { 0x0A, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button) + { 0x0B, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container) + { 0x0C, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound)) + { 0x0D, PacketTypesOut.PluginMessage }, // (Wiki name: Plugin Message (serverbound)) + { 0x0E, PacketTypesOut.EditBook }, // + { 0x0F, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag) + { 0x10, PacketTypesOut.InteractEntity }, // (Wiki name: Interact) + { 0x11, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate) + { 0x12, PacketTypesOut.KeepAlive }, // + { 0x13, PacketTypesOut.LockDifficulty }, // + { 0x14, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position) + { 0x15, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation) + { 0x16, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation) + { 0x17, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground) + { 0x18, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound)) + { 0x19, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat) + { 0x1A, PacketTypesOut.PickItem }, // + { 0x1B, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) + { 0x1C, PacketTypesOut.PlayerAbilities }, // + { 0x1D, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) + { 0x1E, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) + { 0x1F, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) + { 0x20, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) + { 0x21, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) + { 0x22, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) + { 0x23, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) + { 0x24, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) + { 0x25, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) + { 0x26, PacketTypesOut.SelectTrade }, // + { 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) + { 0x28, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) + { 0x29, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) + { 0x2A, PacketTypesOut.UpdateCommandBlockMinecart }, // + { 0x2B, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) + { 0x2C, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) + { 0x2D, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) + { 0x2E, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) + { 0x2F, PacketTypesOut.Animation }, // (Wiki name: Swing) + { 0x30, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) + { 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) + }; + + protected override Dictionary GetListIn() + { + return typeIn; + } + + protected override Dictionary GetListOut() + { + return typeOut; + } + } +} \ No newline at end of file diff --git a/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs b/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs index 4f7c5326..8529fd0a 100644 --- a/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs +++ b/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs @@ -51,6 +51,7 @@ EntityVelocity, // Explosion, // FacePlayer, // + FeatureFlags, // Added in 1.19.3 HeldItemChange, // HideMessage, // Added in 1.19.1 (1.19.2) InitializeWorldBorder, // @@ -71,8 +72,10 @@ PlayerAbilities, // PlayerInfo, // PlayerListHeaderAndFooter, // + PlayerRemove, // Added in 1.19.3 (Not used) PlayerPositionAndLook, // PluginMessage, // + ProfilelessChatMessage, // Added in 1.19.3 RemoveEntityEffect, // ResourcePackSend, // Respawn, // diff --git a/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs b/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs index 4c1d3346..fd3fe7c9 100644 --- a/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs +++ b/MinecraftClient/Protocol/Handlers/PacketTypesOut.cs @@ -36,6 +36,7 @@ PlayerPosition, // PlayerPositionAndRotation, // PlayerRotation, // + PlayerSession, // Added in 1.19.3 PluginMessage, // Pong, // PrepareCraftingGrid, // For 1.12 - 1.12.1 only diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 2d410e5e..022e7ad9 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -62,6 +62,7 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_18_2_Version = 758; internal const int MC_1_19_Version = 759; internal const int MC_1_19_2_Version = 760; + internal const int MC_1_19_3_Version = 761; private int compression_treshold = 0; private bool autocomplete_received = false; From d81a67762e1e8a730f87a9da6d3565e8fa5e04d4 Mon Sep 17 00:00:00 2001 From: Milutinke Date: Wed, 21 Dec 2022 12:03:39 +0100 Subject: [PATCH 09/44] Login Start, Encryption Reqest, Merchant Offers, Set Experience packets implemented. --- .../Protocol/Handlers/DataTypes.cs | 10 +++++-- .../Protocol/Handlers/Protocol18.cs | 26 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index ecc86bce..aac22859 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -728,11 +728,17 @@ namespace MinecraftClient.Protocol.Handlers { Item inputItem1 = ReadNextItemSlot(cache, itemPalette)!; Item outputItem = ReadNextItemSlot(cache, itemPalette)!; + Item? inputItem2 = null; - if (ReadNextBool(cache)) //check if villager has second item - { + + if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) inputItem2 = ReadNextItemSlot(cache, itemPalette); + else + { + if (ReadNextBool(cache)) //check if villager has second item + inputItem2 = ReadNextItemSlot(cache, itemPalette); } + bool tradeDisabled = ReadNextBool(cache); int numberOfTradeUses = ReadNextInt(cache); int maximumNumberOfTradeUses = ReadNextInt(cache); diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 022e7ad9..39760af8 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -1712,8 +1712,19 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.SetExperience: float experiencebar = dataTypes.ReadNextFloat(packetData); - int level = dataTypes.ReadNextVarInt(packetData); - int totalexperience = dataTypes.ReadNextVarInt(packetData); + int totalexperience, level; + + if (protocolVersion >= MC_1_19_3_Version) + { + totalexperience = dataTypes.ReadNextVarInt(packetData); + level = dataTypes.ReadNextVarInt(packetData); + } + else + { + level = dataTypes.ReadNextVarInt(packetData); + totalexperience = dataTypes.ReadNextVarInt(packetData); + } + handler.OnSetExperience(experiencebar, level, totalexperience); break; case PacketTypesIn.Explosion: @@ -1911,7 +1922,9 @@ namespace MinecraftClient.Protocol.Handlers List fullLoginPacket = new(); fullLoginPacket.AddRange(dataTypes.GetString(handler.GetUsername())); // Username - if (protocolVersion >= MC_1_19_Version) + + // 1.19 - 1.19.2 + if (protocolVersion >= MC_1_19_Version && protocolVersion < MC_1_19_3_Version) { if (playerKeyPair == null) fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has Sig Data @@ -1929,6 +1942,7 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_19_2_Version) { Guid uuid = handler.GetUserUuid(); + if (uuid == Guid.Empty) fullLoginPacket.AddRange(dataTypes.GetBool(false)); // Has UUID else @@ -1937,6 +1951,7 @@ namespace MinecraftClient.Protocol.Handlers fullLoginPacket.AddRange(dataTypes.GetUUID(uuid)); // UUID } } + SendPacket(0x00, fullLoginPacket); while (true) @@ -2018,7 +2033,9 @@ namespace MinecraftClient.Protocol.Handlers // Encryption Response packet List encryptionResponse = new(); encryptionResponse.AddRange(dataTypes.GetArray(RSAService.Encrypt(secretKey, false))); // Shared Secret - if (protocolVersion >= Protocol18Handler.MC_1_19_Version) + + // 1.19 - 1.19.2 + if (protocolVersion >= MC_1_19_Version && protocolVersion < MC_1_19_3_Version) { if (playerKeyPair == null) { @@ -2039,6 +2056,7 @@ namespace MinecraftClient.Protocol.Handlers { encryptionResponse.AddRange(dataTypes.GetArray(RSAService.Encrypt(token, false))); // Verify Token } + SendPacket(0x01, encryptionResponse); //Start client-side encryption From bb160d6d84eedb2bf69442d9f541089ac676b440 Mon Sep 17 00:00:00 2001 From: Milutinke Date: Wed, 21 Dec 2022 12:51:27 +0100 Subject: [PATCH 10/44] Server Data and Profileless Chat message packets implemented. ChatPreview packet removed. Message Header packet set only to 1.19.2. Chat Message packet partially implemented. --- MinecraftClient/Mapping/MessageFilterType.cs | 15 ++ .../Protocol/Handlers/Protocol18.cs | 208 +++++++++++------- 2 files changed, 143 insertions(+), 80 deletions(-) create mode 100644 MinecraftClient/Mapping/MessageFilterType.cs diff --git a/MinecraftClient/Mapping/MessageFilterType.cs b/MinecraftClient/Mapping/MessageFilterType.cs new file mode 100644 index 00000000..c89444c0 --- /dev/null +++ b/MinecraftClient/Mapping/MessageFilterType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinecraftClient.Mapping +{ + public enum MessageFilterType + { + PassThrough = 0, + FullyFiltered, + PartiallyFiltered + } +} diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 39760af8..5e835351 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -522,80 +522,126 @@ namespace MinecraftClient.Protocol.Handlers } else // 1.19.1 + { - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - - string signedChat = dataTypes.ReadNextString(packetData); - string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); - LastSeenMessageList.Entry[] lastSeenMessageList = new LastSeenMessageList.Entry[lastSeenMessageListLen]; - for (int i = 0; i < lastSeenMessageListLen; ++i) + if (protocolVersion >= MC_1_19_3_Version) { - Guid user = dataTypes.ReadNextUUID(packetData); - byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); - lastSeenMessageList[i] = new(user, lastSignature); - } - LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); + // Header section + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + int index = dataTypes.ReadNextVarInt(packetData); + byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + // Body + string message = dataTypes.ReadNextString(packetData); + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); - int filterEnum = dataTypes.ReadNextVarInt(packetData); - if (filterEnum == 2) // PARTIALLY_FILTERED - dataTypes.ReadNextULongArray(packetData); + // Previous Messages + int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); + List previousMessageSignatures = new(); - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - Dictionary chatInfo = Json.ParseJson(chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + for (int i = 0; i < totalPreviousMessages; i++) { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } + int messageId = dataTypes.ReadNextVarInt(packetData); - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; + if (messageId > 0) + previousMessageSignatures.Add(dataTypes.ReadNextByteArray(packetData)); + } + + // Other + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + + if (filterType == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + // Network Target + int chatType = dataTypes.ReadNextInt(packetData); + string networkName = dataTypes.ReadNextString(packetData); + bool networkTargetNamePresent = dataTypes.ReadNextBool(packetData); + string? networkTargetName = null; + + if (networkTargetNamePresent) + networkTargetName = dataTypes.ReadNextString(packetData); + + // TODO + } else { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) + byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); + + string signedChat = dataTypes.ReadNextString(packetData); + string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); + + int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); + LastSeenMessageList.Entry[] lastSeenMessageList = new LastSeenMessageList.Entry[lastSeenMessageListLen]; + for (int i = 0; i < lastSeenMessageListLen; ++i) + { + Guid user = dataTypes.ReadNextUUID(packetData); + byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); + lastSeenMessageList[i] = new(user, lastSignature); + } + LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); + + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + if (filterEnum == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + Dictionary chatInfo = Json.ParseJson(chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode) verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; else { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + bool lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); + } } - } - ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender()) - Acknowledge(chat); - handler.OnTextReceived(chat); + ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender()) + Acknowledge(chat); + handler.OnTextReceived(chat); + } } break; case PacketTypesIn.CombatEvent: @@ -625,8 +671,8 @@ namespace MinecraftClient.Protocol.Handlers ); break; - case PacketTypesIn.MessageHeader: - if (protocolVersion >= MC_1_19_2_Version) + case PacketTypesIn.MessageHeader: // 1.19.2 only + if (protocolVersion == MC_1_19_2_Version) { byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; Guid senderUUID = dataTypes.ReadNextUUID(packetData); @@ -654,6 +700,7 @@ namespace MinecraftClient.Protocol.Handlers } } } + break; case PacketTypesIn.Respawn: string? dimensionTypeNameRespawn = null; @@ -1104,7 +1151,11 @@ namespace MinecraftClient.Protocol.Handlers if (hasIcon) iconBase64 = dataTypes.ReadNextString(packetData); - bool previewsChat = dataTypes.ReadNextBool(packetData); + + bool previewsChat = false; + + if (protocolVersion < MC_1_19_3_Version) + dataTypes.ReadNextBool(packetData); handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); break; @@ -1134,25 +1185,22 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.SetDisplayChatPreview: bool previewsChatSetting = dataTypes.ReadNextBool(packetData); handler.OnChatPreviewSettingUpdate(previewsChatSetting); - break; - case PacketTypesIn.ChatPreview: - int queryID = dataTypes.ReadNextInt(packetData); - bool componentIsPresent = dataTypes.ReadNextBool(packetData); - - // Currently noy implemented - log.Debug("New chat preview: "); - log.Debug(">> Query ID: " + queryID); - log.Debug(">> Component is present: " + componentIsPresent); - if (componentIsPresent) - { - string message = dataTypes.ReadNextString(packetData); - log.Debug(">> Component: " + ChatParser.ParseText(message)); - //handler.OnTextReceived(message, true); - } - break; case PacketTypesIn.ChatSuggestions: break; + case PacketTypesIn.ProfilelessChatMessage: + string message_ = dataTypes.ReadNextString(packetData); + int messageType_ = dataTypes.ReadNextVarInt(packetData); + string messageName = dataTypes.ReadNextString(packetData); + bool hasTargetName = dataTypes.ReadNextBool(packetData); + + string? targetName_ = null; + + if (hasTargetName) + targetName_ = dataTypes.ReadNextString(packetData); + + // Not clear for what this is used as the time of writting + break; case PacketTypesIn.MapChunkBulk: if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) { From 20273af55497fcc93a4e42a908bad98d866d6611 Mon Sep 17 00:00:00 2001 From: Milutinke Date: Wed, 21 Dec 2022 13:04:41 +0100 Subject: [PATCH 11/44] Improvements to the code of the Chat Message packet. --- .../Protocol/Handlers/Protocol18.cs | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 5e835351..1ba4d59e 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -536,14 +536,13 @@ namespace MinecraftClient.Protocol.Handlers // Previous Messages int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); - List previousMessageSignatures = new(); + List> previousMessageSignatures = new(); for (int i = 0; i < totalPreviousMessages; i++) { int messageId = dataTypes.ReadNextVarInt(packetData); - if (messageId > 0) - previousMessageSignatures.Add(dataTypes.ReadNextByteArray(packetData)); + previousMessageSignatures.Add(new Tuple(messageId, dataTypes.ReadNextByteArray(packetData))); } // Other @@ -555,13 +554,30 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextULongArray(packetData); // Network Target - int chatType = dataTypes.ReadNextInt(packetData); - string networkName = dataTypes.ReadNextString(packetData); - bool networkTargetNamePresent = dataTypes.ReadNextBool(packetData); - string? networkTargetName = null; + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - if (networkTargetNamePresent) - networkTargetName = dataTypes.ReadNextString(packetData); + Dictionary chatInfo = Json.ParseJson(chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } // TODO } From f3b50738f1cda43f33581122a8cf2bb08676dee7 Mon Sep 17 00:00:00 2001 From: Milutinke Date: Tue, 27 Dec 2022 11:43:19 +0100 Subject: [PATCH 12/44] Cleaned some code a bit --- MinecraftClient/Protocol/Handlers/Protocol18.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 1ba4d59e..eecd7ca1 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -1208,14 +1208,9 @@ namespace MinecraftClient.Protocol.Handlers string message_ = dataTypes.ReadNextString(packetData); int messageType_ = dataTypes.ReadNextVarInt(packetData); string messageName = dataTypes.ReadNextString(packetData); - bool hasTargetName = dataTypes.ReadNextBool(packetData); + string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - string? targetName_ = null; - - if (hasTargetName) - targetName_ = dataTypes.ReadNextString(packetData); - - // Not clear for what this is used as the time of writting + // Not clear for what this is used for as the time of writing break; case PacketTypesIn.MapChunkBulk: if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) From 9c8afb7d3c20e98225d5c9583a865d92e244dc00 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Mon, 2 Jan 2023 18:52:49 +0800 Subject: [PATCH 13/44] Bug fix --- MinecraftClient/McClient.cs | 2 +- MinecraftClient/Program.cs | 6 +++--- MinecraftClient/Scripting/CSharpRunner.cs | 1 + MinecraftClient/Scripting/ChatBot.cs | 5 ----- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 80defaf6..003dcbc2 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -203,7 +203,7 @@ namespace MinecraftClient Log.Info(string.Format(Translations.mcc_joined, Config.Main.Advanced.InternalCmdChar.ToLogString())); cmdprompt = new CancellationTokenSource(); - ConsoleInteractive.ConsoleReader.BeginReadThread(cmdprompt); + ConsoleInteractive.ConsoleReader.BeginReadThread(); ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived; ConsoleInteractive.ConsoleReader.OnInputChange += ConsoleIO.AutocompleteHandler; } diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 503f4f40..7eaaafa8 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -287,7 +287,7 @@ namespace MinecraftClient } } - if (!string.IsNullOrWhiteSpace(Config.Main.Advanced.ConsoleTitle)) + if (OperatingSystem.IsWindows() && !string.IsNullOrWhiteSpace(Config.Main.Advanced.ConsoleTitle)) { InternalConfig.Username = "New Window"; Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); @@ -463,7 +463,7 @@ namespace MinecraftClient InternalConfig.Username = session.PlayerName; bool isRealms = false; - if (Config.Main.Advanced.ConsoleTitle != "") + if (OperatingSystem.IsWindows() && !string.IsNullOrWhiteSpace(Config.Main.Advanced.ConsoleTitle)) Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); if (Config.Main.Advanced.PlayerHeadAsIcon && OperatingSystem.IsWindows()) @@ -614,7 +614,7 @@ namespace MinecraftClient client = new McClient(session, playerKeyPair, InternalConfig.ServerIP, InternalConfig.ServerPort, protocolversion, forgeInfo); //Update console title - if (Config.Main.Advanced.ConsoleTitle != "") + if (OperatingSystem.IsWindows() && !string.IsNullOrWhiteSpace(Config.Main.Advanced.ConsoleTitle)) Console.Title = Config.AppVar.ExpandVars(Config.Main.Advanced.ConsoleTitle); } catch (NotSupportedException) diff --git a/MinecraftClient/Scripting/CSharpRunner.cs b/MinecraftClient/Scripting/CSharpRunner.cs index e6587440..d056a9a4 100644 --- a/MinecraftClient/Scripting/CSharpRunner.cs +++ b/MinecraftClient/Scripting/CSharpRunner.cs @@ -88,6 +88,7 @@ namespace MinecraftClient.Scripting "using System.Net;", "using System.Threading;", "using MinecraftClient;", + "using MinecraftClient.Scripting;", "using MinecraftClient.Mapping;", "using MinecraftClient.Inventory;", string.Join("\n", libs), diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index d73f0884..67bc3cb5 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -1672,11 +1672,6 @@ namespace MinecraftClient.Scripting } - public string Run(McClient handler, string command, Dictionary? localVars) - { - return Runner(command, GetArgs(command)); - } - /// /// ChatBotCommand Constructor /// From 8758e8c8c4d7e16ede36bc588db178dccfa7f01d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 13:06:03 +0000 Subject: [PATCH 14/44] Bump json5 from 2.2.1 to 2.2.3 in /docs Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/yarn.lock b/docs/yarn.lock index 0b93ab1d..8e72992e 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2803,9 +2803,9 @@ json-schema-traverse@^1.0.0: integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json5@^2.1.2, json5@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonc-parser@^3.0.0: version "3.2.0" From 640abd2d7839db3586aea6510429ac3ba75e8792 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Tue, 10 Jan 2023 16:52:18 +0800 Subject: [PATCH 15/44] AutoAttack: Add setting for attack range --- MinecraftClient/ChatBots/AutoAttack.cs | 6 + .../ConfigComments/ConfigComments.Designer.cs | 467 +++++++++--------- .../ConfigComments/ConfigComments.resx | 3 + 3 files changed, 247 insertions(+), 229 deletions(-) diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 6b577884..526ba052 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -32,6 +32,9 @@ namespace MinecraftClient.ChatBots [TomlInlineComment("$ChatBot.AutoAttack.Interaction$")] public InteractType Interaction = InteractType.Attack; + [TomlInlineComment("$ChatBot.AutoAttack.Attack_Range$")] + public int Attack_Range = 4; + [TomlInlineComment("$ChatBot.AutoAttack.Attack_Hostile$")] public bool Attack_Hostile = true; @@ -107,6 +110,9 @@ namespace MinecraftClient.ChatBots attackHostile = Config.Attack_Hostile; attackPassive = Config.Attack_Passive; + attackRange = Config.Attack_Range; + if (attackRange < 1) attackRange = 1; + if (attackRange > 4) attackRange = 4; } public override void Initialize() diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index 85527b2c..76798e5a 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// 這段程式碼是由工具產生的。 +// 執行階段版本:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// 對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼, +// 變更將會遺失。 // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace MinecraftClient { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// 用於查詢當地語系化字串等的強類型資源類別。 /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // 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. + // 這個類別是自動產生的,是利用 StronglyTypedResourceBuilder + // 類別透過 ResGen 或 Visual Studio 這類工具。 + // 若要加入或移除成員,請編輯您的 .ResX 檔,然後重新執行 ResGen + // (利用 /str 選項),或重建您的 VS 專案。 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace MinecraftClient { } /// - /// Returns the cached ResourceManager instance used by this class. + /// 傳回這個類別使用的快取的 ResourceManager 執行個體。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace MinecraftClient { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// 覆寫目前執行緒的 CurrentUICulture 屬性,對象是所有 + /// 使用這個強類型資源類別的資源查閱。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,8 +61,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to can be used in some other fields as %yourvar% - ///%username% and %serverip% are reserved variables.. + /// 查詢類似 can be used in some other fields as %yourvar% + ///%username% and %serverip% are reserved variables. 的當地語系化字串。 /// internal static string AppVars_Variables { get { @@ -71,9 +71,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to =============================== # + /// 查詢類似 =============================== # /// Minecraft Console Client Bots # - ///=============================== #. + ///=============================== # 的當地語系化字串。 /// internal static string ChatBot { get { @@ -82,8 +82,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Get alerted when specified words are detected in chat - ///Useful for moderating your server or detecting when someone is talking to you. + /// 查詢類似 Get alerted when specified words are detected in chat + ///Useful for moderating your server or detecting when someone is talking to you 的當地語系化字串。 /// internal static string ChatBot_Alerts { get { @@ -92,7 +92,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Play a beep sound when a word is detected in addition to highlighting.. + /// 查詢類似 Play a beep sound when a word is detected in addition to highlighting. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Beep_Enabled { get { @@ -101,7 +101,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to List of words/strings to NOT alert you on.. + /// 查詢類似 List of words/strings to NOT alert you on. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Excludes { get { @@ -110,7 +110,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The name of a file where alers logs will be written.. + /// 查詢類似 The name of a file where alers logs will be written. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Log_File { get { @@ -119,7 +119,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Log alerts info a file.. + /// 查詢類似 Log alerts info a file. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Log_To_File { get { @@ -128,7 +128,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to List of words/strings to alert you on.. + /// 查詢類似 List of words/strings to alert you on. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Matches { get { @@ -137,7 +137,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Trigger alerts when it rains and when it stops.. + /// 查詢類似 Trigger alerts when it rains and when it stops. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Trigger_By_Rain { get { @@ -146,7 +146,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Triggers alerts at the beginning and end of thunderstorms.. + /// 查詢類似 Triggers alerts at the beginning and end of thunderstorms. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Trigger_By_Thunderstorm { get { @@ -155,7 +155,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Triggers an alert after receiving a specified keyword.. + /// 查詢類似 Triggers an alert after receiving a specified keyword. 的當地語系化字串。 /// internal static string ChatBot_Alerts_Trigger_By_Words { get { @@ -164,9 +164,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection + /// 查詢類似 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). + ////!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5) 的當地語系化字串。 /// internal static string ChatBot_AntiAfk { get { @@ -175,7 +175,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Command to send to the server.. + /// 查詢類似 Command to send to the server. 的當地語系化字串。 /// internal static string ChatBot_AntiAfk_Command { get { @@ -184,7 +184,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The time interval for execution. (in seconds). + /// 查詢類似 The time interval for execution. (in seconds) 的當地語系化字串。 /// internal static string ChatBot_AntiAfk_Delay { get { @@ -193,7 +193,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to sneak when sending the command.. + /// 查詢類似 Whether to sneak when sending the command. 的當地語系化字串。 /// internal static string ChatBot_AntiAfk_Use_Sneak { get { @@ -202,7 +202,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use terrain handling to enable the bot to move around.. + /// 查詢類似 Use terrain handling to enable the bot to move around. 的當地語系化字串。 /// internal static string ChatBot_AntiAfk_Use_Terrain_Handling { get { @@ -211,7 +211,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be). + /// 查詢類似 The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be) 的當地語系化字串。 /// internal static string ChatBot_AntiAfk_Walk_Range { get { @@ -220,7 +220,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How many times can the bot fail trying to move before using the command method.. + /// 查詢類似 How many times can the bot fail trying to move before using the command method. 的當地語系化字串。 /// internal static string ChatBot_AntiAfk_Walk_Retries { get { @@ -229,10 +229,10 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically attack hostile mobs around you + /// 查詢類似 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!. + ////!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES! 的當地語系化字串。 /// internal static string ChatBot_AutoAttack { get { @@ -241,7 +241,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Allow attacking hostile mobs.. + /// 查詢類似 Allow attacking hostile mobs. 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Attack_Hostile { get { @@ -250,7 +250,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Allow attacking passive mobs.. + /// 查詢類似 Allow attacking passive mobs. 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Attack_Passive { get { @@ -259,7 +259,16 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How long to wait between each attack. Set "Custom = false" to let MCC calculate it.. + /// 查詢類似 Capped between 1 to 4 的當地語系化字串。 + /// + internal static string ChatBot_AutoAttack_Attack_Range { + get { + return ResourceManager.GetString("ChatBot.AutoAttack.Attack_Range", resourceCulture); + } + } + + /// + /// 查詢類似 How long to wait between each attack. Set "Custom = false" to let MCC calculate it. 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Cooldown_Time { get { @@ -268,7 +277,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to All entity types can be found here: https://mccteam.github.io/r/entity/#L15. + /// 查詢類似 All entity types can be found here: https://mccteam.github.io/r/entity/#L15 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Entites_List { get { @@ -277,7 +286,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack).. + /// 查詢類似 Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack). 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Interaction { get { @@ -286,7 +295,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Wether to treat the entities list as a "whitelist" or as a "blacklist".. + /// 查詢類似 Wether to treat the entities list as a "whitelist" or as a "blacklist". 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_List_Mode { get { @@ -295,7 +304,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to "single" or "multi". single target one mob per attack. multi target all mobs in range per attack. + /// 查詢類似 "single" or "multi". single target one mob per attack. multi target all mobs in range per attack 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Mode { get { @@ -304,7 +313,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to "health" or "distance". Only needed when using single mode. + /// 查詢類似 "health" or "distance". Only needed when using single mode 的當地語系化字串。 /// internal static string ChatBot_AutoAttack_Priority { get { @@ -313,10 +322,10 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically craft items in your inventory + /// 查詢類似 Automatically craft items in your inventory ///See https://mccteam.github.io/g/bots/#auto-craft 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. + ///You should also enable Terrain and Movements if you need to use a crafting table 的當地語系化字串。 /// internal static string ChatBot_AutoCraft { get { @@ -325,7 +334,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Location of the crafting table if you intended to use it. Terrain and movements must be enabled.. + /// 查詢類似 Location of the crafting table if you intended to use it. Terrain and movements must be enabled. 的當地語系化字串。 /// internal static string ChatBot_AutoCraft_CraftingTable { get { @@ -334,7 +343,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to What to do on crafting failure, "abort" or "wait".. + /// 查詢類似 What to do on crafting failure, "abort" or "wait". 的當地語系化字串。 /// internal static string ChatBot_AutoCraft_OnFailure { get { @@ -343,11 +352,11 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Recipes.Name: The name can be whatever you like and it is used to represent the recipe. + /// 查詢類似 Recipes.Name: The name can be whatever you like and it is used to represent the recipe. ///Recipes.Type: crafting table type: "player" or "table" ///Recipes.Result: the resulting item ///Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots. - ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12. + ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12 的當地語系化字串。 /// internal static string ChatBot_AutoCraft_Recipes { get { @@ -356,11 +365,11 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Auto-digging blocks. + /// 查詢類似 Auto-digging blocks. ///You need to enable Terrain Handling to use this bot ///You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig. ///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. - ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15. + ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15 的當地語系化字串。 /// internal static string ChatBot_AutoDig { get { @@ -369,7 +378,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start.. + /// 查詢類似 How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Auto_Start_Delay { get { @@ -378,7 +387,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically switch to the appropriate tool.. + /// 查詢類似 Automatically switch to the appropriate tool. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Auto_Tool_Switch { get { @@ -387,7 +396,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Mining a block for more than "Dig_Timeout" seconds will be considered a timeout.. + /// 查詢類似 Mining a block for more than "Dig_Timeout" seconds will be considered a timeout. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Dig_Timeout { get { @@ -396,7 +405,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to drop the current tool when its durability is too low.. + /// 查詢類似 Whether to drop the current tool when its durability is too low. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Drop_Low_Durability_Tools { get { @@ -405,7 +414,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Will not use tools with less durability than this. Set to zero to disable this feature.. + /// 查詢類似 Will not use tools with less durability than this. Set to zero to disable this feature. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Durability_Limit { get { @@ -414,7 +423,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Wether to treat the blocks list as a "whitelist" or as a "blacklist".. + /// 查詢類似 Wether to treat the blocks list as a "whitelist" or as a "blacklist". 的當地語系化字串。 /// internal static string ChatBot_AutoDig_List_Type { get { @@ -423,7 +432,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list.. + /// 查詢類似 "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Location_Order { get { @@ -432,7 +441,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The position of the blocks when using "fixedpos" or "both" mode.. + /// 查詢類似 The position of the blocks when using "fixedpos" or "both" mode. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Locations { get { @@ -441,7 +450,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to output logs when digging blocks.. + /// 查詢類似 Whether to output logs when digging blocks. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Log_Block_Dig { get { @@ -450,7 +459,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to "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.. + /// 查詢類似 "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. 的當地語系化字串。 /// internal static string ChatBot_AutoDig_Mode { get { @@ -459,9 +468,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically drop items in inventory + /// 查詢類似 Automatically drop items in inventory ///You need to enable Inventory Handling to use this bot - ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12. + ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12 的當地語系化字串。 /// internal static string ChatBot_AutoDrop { get { @@ -470,7 +479,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list. + /// 查詢類似 "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list 的當地語系化字串。 /// internal static string ChatBot_AutoDrop_Mode { get { @@ -479,8 +488,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically eat food when your Hunger value is low - ///You need to enable Inventory Handling to use this bot. + /// 查詢類似 Automatically eat food when your Hunger value is low + ///You need to enable Inventory Handling to use this bot 的當地語系化字串。 /// internal static string ChatBot_AutoEat { get { @@ -489,10 +498,10 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically catch fish using a fishing rod + /// 查詢類似 Automatically catch fish using a fishing rod ///Guide: https://mccteam.github.io/g/bots/#auto-fishing ///You can use "/fish" to control the bot manually. - ////!\ Make sure server rules allow automated farming before using this bot. + ////!\ Make sure server rules allow automated farming before using this bot 的當地語系化字串。 /// internal static string ChatBot_AutoFishing { get { @@ -501,7 +510,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Keep it as false if you have not changed it before.. + /// 查詢類似 Keep it as false if you have not changed it before. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Antidespawn { get { @@ -510,7 +519,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Switch to a new rod from inventory after the current rod is unavailable.. + /// 查詢類似 Switch to a new rod from inventory after the current rod is unavailable. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Auto_Rod_Switch { get { @@ -519,7 +528,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to start fishing automatically after entering a world.. + /// 查詢類似 Whether to start fishing automatically after entering a world. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Auto_Start { get { @@ -528,7 +537,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How soon to re-cast after successful fishing.. + /// 查詢類似 How soon to re-cast after successful fishing. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Cast_Delay { get { @@ -537,7 +546,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.. + /// 查詢類似 Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Durability_Limit { get { @@ -546,7 +555,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to This allows the player to change position/facing after each fish caught.. + /// 查詢類似 This allows the player to change position/facing after each fish caught. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Enable_Move { get { @@ -555,7 +564,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How long after entering the game to start fishing (seconds).. + /// 查詢類似 How long after entering the game to start fishing (seconds). 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Fishing_Delay { get { @@ -564,7 +573,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Fishing timeout (seconds). Timeout will trigger a re-cast.. + /// 查詢類似 Fishing timeout (seconds). Timeout will trigger a re-cast. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Fishing_Timeout { get { @@ -573,7 +582,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish.. + /// 查詢類似 A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Hook_Threshold { get { @@ -582,7 +591,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to 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.. + /// 查詢類似 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. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Log_Fish_Bobber { get { @@ -591,7 +600,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use the mainhand or the offhand to hold the rod.. + /// 查詢類似 Use the mainhand or the offhand to hold the rod. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Mainhand { get { @@ -600,7 +609,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to 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.. + /// 查詢類似 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. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Movements { get { @@ -609,7 +618,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Hook movement in the X and Z axis less than this value will be considered stationary.. + /// 查詢類似 Hook movement in the X and Z axis less than this value will be considered stationary. 的當地語系化字串。 /// internal static string ChatBot_AutoFishing_Stationary_Threshold { get { @@ -618,8 +627,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically relog when disconnected by server, for example because the server is restating - ////!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks. + /// 查詢類似 Automatically relog when disconnected by server, for example because the server is restating + ////!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks 的當地語系化字串。 /// internal static string ChatBot_AutoRelog { get { @@ -628,7 +637,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The delay time before joining the server. (in seconds). + /// 查詢類似 The delay time before joining the server. (in seconds) 的當地語系化字串。 /// internal static string ChatBot_AutoRelog_Delay { get { @@ -637,7 +646,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to When set to true, autorelog will reconnect regardless of kick messages.. + /// 查詢類似 When set to true, autorelog will reconnect regardless of kick messages. 的當地語系化字串。 /// internal static string ChatBot_AutoRelog_Ignore_Kick_Message { get { @@ -646,7 +655,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to If the kickout message matches any of the strings, then autorelog will be triggered.. + /// 查詢類似 If the kickout message matches any of the strings, then autorelog will be triggered. 的當地語系化字串。 /// internal static string ChatBot_AutoRelog_Kick_Messages { get { @@ -655,7 +664,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Retries when failing to relog to the server. use -1 for unlimited retries.. + /// 查詢類似 Retries when failing to relog to the server. use -1 for unlimited retries. 的當地語系化字串。 /// internal static string ChatBot_AutoRelog_Retries { get { @@ -664,9 +673,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Run commands or send messages automatically when a specified pattern is detected in chat + /// 查詢類似 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. + ////!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam 的當地語系化字串。 /// internal static string ChatBot_AutoRespond { get { @@ -675,7 +684,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work). + /// 查詢類似 Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work) 的當地語系化字串。 /// internal static string ChatBot_AutoRespond_Match_Colors { get { @@ -684,7 +693,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Logs chat messages in a file on disk.. + /// 查詢類似 Logs chat messages in a file on disk. 的當地語系化字串。 /// internal static string ChatBot_ChatLog { get { @@ -693,12 +702,12 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to This bot allows you to send and recieve messages and commands via a Discord channel. + /// 查詢類似 This bot allows you to send and recieve messages and commands via a Discord channel. ///For Setup you can either use the documentation or read here (Documentation has images). ///Documentation: https://mccteam.github.io/g/bots/#discord-bridge ///Setup: ///First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA . - ////!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [rest of string was truncated]";. + ////!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [字串的其餘部分已遭截斷]"; 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge { get { @@ -707,7 +716,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The ID of a channel where you want to interact with the MCC using the bot.. + /// 查詢類似 The ID of a channel where you want to interact with the MCC using the bot. 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge_ChannelId { get { @@ -716,10 +725,10 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Message formats + /// 查詢類似 Message formats ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html. + ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge_Formats { get { @@ -728,7 +737,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The ID of a server/guild where you have invited the bot to.. + /// 查詢類似 The ID of a server/guild where you have invited the bot to. 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge_GuildId { get { @@ -737,7 +746,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).. + /// 查詢類似 How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second). 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge_MessageSendTimeout { get { @@ -746,7 +755,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to A list of IDs of people you want to be able to interact with the MCC using the bot.. + /// 查詢類似 A list of IDs of people you want to be able to interact with the MCC using the bot. 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge_OwnersIds { get { @@ -755,7 +764,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Your Discord Bot token.. + /// 查詢類似 Your Discord Bot token. 的當地語系化字串。 /// internal static string ChatBot_DiscordBridge_Token { get { @@ -764,11 +773,11 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically farms crops for you (plants, breaks and bonemeals them). + /// 查詢類似 Automatically farms crops for you (plants, breaks and bonemeals them). ///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat. ///Usage: "/farmer start" command and "/farmer stop" command. ///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes. - ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [rest of string was truncated]";. + ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [字串的其餘部分已遭截斷]"; 的當地語系化字串。 /// internal static string ChatBot_Farmer { get { @@ -777,7 +786,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Delay between tasks in seconds (Minimum 1 second). + /// 查詢類似 Delay between tasks in seconds (Minimum 1 second) 的當地語系化字串。 /// internal static string ChatBot_Farmer_Delay_Between_Tasks { get { @@ -786,11 +795,11 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Enabled you to make the bot follow you + /// 查詢類似 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, /// [rest of string was truncated]";. + ///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, /// [字串的其餘部分已遭截斷]"; 的當地語系化字串。 /// internal static string ChatBot_FollowPlayer { get { @@ -799,7 +808,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to 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). + /// 查詢類似 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) 的當地語系化字串。 /// internal static string ChatBot_FollowPlayer_Stop_At_Distance { get { @@ -808,7 +817,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow). + /// 查詢類似 The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow) 的當地語系化字串。 /// internal static string ChatBot_FollowPlayer_Update_Limit { get { @@ -817,9 +826,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time. + /// 查詢類似 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 <bot username> start - ////!\ This bot may get a bit spammy if many players are interacting with it. + ////!\ This bot may get a bit spammy if many players are interacting with it 的當地語系化字串。 /// internal static string ChatBot_HangmanGame { get { @@ -828,9 +837,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Relay messages between players and servers, like a mail plugin + /// 查詢類似 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. + ////!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins 的當地語系化字串。 /// internal static string ChatBot_Mailer { get { @@ -839,12 +848,12 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) + /// 查詢類似 Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) ///This is useful for solving captchas which use maps ///The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled. ///NOTE: ///If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console. - ////!\ Make sure server rules allow bots to be used on the server, or you risk being punished.. + ////!\ Make sure server rules allow bots to be used on the server, or you risk being punished. 的當地語系化字串。 /// internal static string ChatBot_Map { get { @@ -853,7 +862,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Automatically render the map once it is received or updated from/by the server. + /// 查詢類似 Automatically render the map once it is received or updated from/by the server 的當地語系化字串。 /// internal static string ChatBot_Map_Auto_Render_On_Update { get { @@ -862,7 +871,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Delete all rendered maps on unload/reload or when you launch the MCC again.. + /// 查詢類似 Delete all rendered maps on unload/reload or when you launch the MCC again. 的當地語系化字串。 /// internal static string ChatBot_Map_Delete_All_On_Unload { get { @@ -871,7 +880,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Get a notification when you have gotten a map from the server for the first time. + /// 查詢類似 Get a notification when you have gotten a map from the server for the first time 的當地語系化字串。 /// internal static string ChatBot_Map_Notify_On_First_Update { get { @@ -880,7 +889,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord.. + /// 查詢類似 Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord. 的當地語系化字串。 /// internal static string ChatBot_Map_Rasize_Rendered_Image { get { @@ -889,7 +898,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to render the map in the console.. + /// 查詢類似 Whether to render the map in the console. 的當地語系化字串。 /// internal static string ChatBot_Map_Render_In_Console { get { @@ -898,7 +907,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The size that a rendered image should be resized to, in pixels (eg. 512).. + /// 查詢類似 The size that a rendered image should be resized to, in pixels (eg. 512). 的當地語系化字串。 /// internal static string ChatBot_Map_Resize_To { get { @@ -907,7 +916,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge).. + /// 查詢類似 Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge). 的當地語系化字串。 /// internal static string ChatBot_Map_Save_To_File { get { @@ -916,9 +925,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) + /// 查詢類似 Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) ///You need to enable Save_To_File in order for this to work. - ///We also recommend turning on resizing.. + ///We also recommend turning on resizing. 的當地語系化字串。 /// internal static string ChatBot_Map_Send_Rendered_To_Bridges { get { @@ -927,7 +936,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Log the list of players periodically into a textual file.. + /// 查詢類似 Log the list of players periodically into a textual file. 的當地語系化字串。 /// internal static string ChatBot_PlayerListLogger { get { @@ -936,7 +945,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to (In seconds). + /// 查詢類似 (In seconds) 的當地語系化字串。 /// internal static string ChatBot_PlayerListLogger_Delay { get { @@ -945,9 +954,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Send MCC console commands to your bot through server PMs (/tell) + /// 查詢類似 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. + ////!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins 的當地語系化字串。 /// internal static string ChatBot_RemoteControl { get { @@ -956,9 +965,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/) + /// 查詢類似 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!. + ////!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT! 的當地語系化字串。 /// internal static string ChatBot_ReplayCapture { get { @@ -967,7 +976,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How long should replay file be auto-saved, in seconds. Use -1 to disable.. + /// 查詢類似 How long should replay file be auto-saved, in seconds. Use -1 to disable. 的當地語系化字串。 /// internal static string ChatBot_ReplayCapture_Backup_Interval { get { @@ -976,8 +985,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Schedule commands and scripts to launch on various events such as server join, date/time or time interval - ///See https://mccteam.github.io/g/bots/#script-scheduler for more info. + /// 查詢類似 Schedule commands and scripts to launch on various events such as server join, date/time or time interval + ///See https://mccteam.github.io/g/bots/#script-scheduler for more info 的當地語系化字串。 /// internal static string ChatBot_ScriptScheduler { get { @@ -986,12 +995,12 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. + /// 查詢類似 This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. ////!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel. ///----------------------------------------------------------- ///Setup: ///First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather - ///Click on "Start" button and re [rest of string was truncated]";. + ///Click on "Start" button and re [字串的其餘部分已遭截斷]"; 的當地語系化字串。 /// internal static string ChatBot_TelegramBridge { get { @@ -1000,7 +1009,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.. + /// 查詢類似 A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram. 的當地語系化字串。 /// internal static string ChatBot_TelegramBridge_Authorized_Chat_Ids { get { @@ -1009,7 +1018,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to An ID of a channel where you want to interact with the MCC using the bot.. + /// 查詢類似 An ID of a channel where you want to interact with the MCC using the bot. 的當地語系化字串。 /// internal static string ChatBot_TelegramBridge_ChannelId { get { @@ -1018,10 +1027,10 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Message formats + /// 查詢類似 Message formats ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html. + ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html 的當地語系化字串。 /// internal static string ChatBot_TelegramBridge_Formats { get { @@ -1030,7 +1039,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).. + /// 查詢類似 How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second). 的當地語系化字串。 /// internal static string ChatBot_TelegramBridge_MessageSendTimeout { get { @@ -1039,7 +1048,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Your Telegram Bot token.. + /// 查詢類似 Your Telegram Bot token. 的當地語系化字串。 /// internal static string ChatBot_TelegramBridge_Token { get { @@ -1048,8 +1057,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to 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 https://mccteam.github.io/g/conf/#chat-format-section. + /// 查詢類似 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 https://mccteam.github.io/g/conf/#chat-format-section 的當地語系化字串。 /// internal static string ChatFormat { get { @@ -1058,7 +1067,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to MCC support for common message formats. Set "false" to avoid conflicts with custom formats.. + /// 查詢類似 MCC support for common message formats. Set "false" to avoid conflicts with custom formats. 的當地語系化字串。 /// internal static string ChatFormat_Builtins { get { @@ -1067,7 +1076,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to use the custom regular expressions below for detection.. + /// 查詢類似 Whether to use the custom regular expressions below for detection. 的當地語系化字串。 /// internal static string ChatFormat_UserDefined { get { @@ -1076,11 +1085,11 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Startup Config File + /// 查詢類似 Startup Config File ///Please do not record extraneous data in this file as it will be overwritten by MCC. /// ///New to Minecraft Console Client? Check out this document: https://mccteam.github.io/g/conf.html - ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download. + ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download 的當地語系化字串。 /// internal static string Head { get { @@ -1089,7 +1098,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to This setting affects only the messages in the console.. + /// 查詢類似 This setting affects only the messages in the console. 的當地語系化字串。 /// internal static string Logging { get { @@ -1098,7 +1107,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Regex for filtering chat message.. + /// 查詢類似 Regex for filtering chat message. 的當地語系化字串。 /// internal static string Logging_ChatFilter { get { @@ -1107,7 +1116,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Show server chat messages.. + /// 查詢類似 Show server chat messages. 的當地語系化字串。 /// internal static string Logging_ChatMessages { get { @@ -1116,7 +1125,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Regex for filtering debug message.. + /// 查詢類似 Regex for filtering debug message. 的當地語系化字串。 /// internal static string Logging_DebugFilter { get { @@ -1125,7 +1134,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Please enable this before submitting bug reports. Thanks!. + /// 查詢類似 Please enable this before submitting bug reports. Thanks! 的當地語系化字串。 /// internal static string Logging_DebugMessages { get { @@ -1134,7 +1143,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Show error messages.. + /// 查詢類似 Show error messages. 的當地語系化字串。 /// internal static string Logging_ErrorMessages { get { @@ -1143,7 +1152,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex.. + /// 查詢類似 "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex. 的當地語系化字串。 /// internal static string Logging_FilterMode { get { @@ -1152,7 +1161,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Informative messages. (i.e Most of the message from MCC). + /// 查詢類似 Informative messages. (i.e Most of the message from MCC) 的當地語系化字串。 /// internal static string Logging_InfoMessages { get { @@ -1161,7 +1170,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Log file name.. + /// 查詢類似 Log file name. 的當地語系化字串。 /// internal static string Logging_LogFile { get { @@ -1170,7 +1179,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Write log messages to file.. + /// 查詢類似 Write log messages to file. 的當地語系化字串。 /// internal static string Logging_LogToFile { get { @@ -1179,7 +1188,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Prepend timestamp to messages in log file.. + /// 查詢類似 Prepend timestamp to messages in log file. 的當地語系化字串。 /// internal static string Logging_PrependTimestamp { get { @@ -1188,7 +1197,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Keep color codes in the saved text.(look like "§b"). + /// 查詢類似 Keep color codes in the saved text.(look like "§b") 的當地語系化字串。 /// internal static string Logging_SaveColorCodes { get { @@ -1197,7 +1206,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Show warning messages.. + /// 查詢類似 Show warning messages. 的當地語系化字串。 /// internal static string Logging_WarningMessages { get { @@ -1206,7 +1215,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Make sure you understand what each setting does before changing anything!. + /// 查詢類似 Make sure you understand what each setting does before changing anything! 的當地語系化字串。 /// internal static string Main_Advanced { get { @@ -1215,8 +1224,8 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to AccountList: It allows a fast account switching without directly using the credentials - ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1". + /// 查詢類似 AccountList: It allows a fast account switching without directly using the credentials + ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1" 的當地語系化字串。 /// internal static string Main_Advanced_account_list { get { @@ -1225,7 +1234,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Toggle auto respawn if client player was dead (make sure your spawn point is safe).. + /// 查詢類似 Toggle auto respawn if client player was dead (make sure your spawn point is safe). 的當地語系化字串。 /// internal static string Main_Advanced_auto_respawn { get { @@ -1234,7 +1243,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Set the owner of the bot. /!\ Server admins can impersonate owners!. + /// 查詢類似 Set the owner of the bot. /!\ Server admins can impersonate owners! 的當地語系化字串。 /// internal static string Main_Advanced_bot_owners { get { @@ -1243,7 +1252,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server.. + /// 查詢類似 Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server. 的當地語系化字串。 /// internal static string Main_Advanced_brand_info { get { @@ -1252,7 +1261,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Leave empty for no logfile.. + /// 查詢類似 Leave empty for no logfile. 的當地語系化字串。 /// internal static string Main_Advanced_chatbot_log_file { get { @@ -1261,7 +1270,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to If turned off, the emoji will be replaced with a simpler character (for /chunk status).. + /// 查詢類似 If turned off, the emoji will be replaced with a simpler character (for /chunk status). 的當地語系化字串。 /// internal static string Main_Advanced_enable_emoji { get { @@ -1270,7 +1279,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Toggle entity handling.. + /// 查詢類似 Toggle entity handling. 的當地語系化字串。 /// internal static string Main_Advanced_entity_handling { get { @@ -1279,7 +1288,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to exit directly when an error occurs, for using MCC in non-interactive scripts.. + /// 查詢類似 Whether to exit directly when an error occurs, for using MCC in non-interactive scripts. 的當地語系化字串。 /// internal static string Main_Advanced_exit_on_failure { get { @@ -1288,7 +1297,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "none", "slash"(/) or "backslash"(\).. + /// 查詢類似 Use "none", "slash"(/) or "backslash"(\). 的當地語系化字串。 /// internal static string Main_Advanced_internal_cmd_char { get { @@ -1297,7 +1306,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Toggle inventory handling.. + /// 查詢類似 Toggle inventory handling. 的當地語系化字串。 /// internal static string Main_Advanced_inventory_handling { get { @@ -1306,7 +1315,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html. + /// 查詢類似 Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html 的當地語系化字串。 /// internal static string Main_Advanced_language { get { @@ -1315,7 +1324,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Load translations applied to MCC when available, turn it off to use English only.. + /// 查詢類似 Load translations applied to MCC when available, turn it off to use English only. 的當地語系化字串。 /// internal static string Main_Advanced_LoadMccTrans { get { @@ -1324,7 +1333,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "auto", "no" or "force". Force-enabling only works for MC 1.13+.. + /// 查詢類似 Use "auto", "no" or "force". Force-enabling only works for MC 1.13+. 的當地語系化字串。 /// internal static string Main_Advanced_mc_forge { get { @@ -1333,7 +1342,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "auto" or "1.X.X" values. Allows to skip server info retrieval.. + /// 查詢類似 Use "auto" or "1.X.X" values. Allows to skip server info retrieval. 的當地語系化字串。 /// internal static string Main_Advanced_mc_version { get { @@ -1342,7 +1351,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Controls the minimum interval (in seconds) between sending each message to the server.. + /// 查詢類似 Controls the minimum interval (in seconds) between sending each message to the server. 的當地語系化字串。 /// internal static string Main_Advanced_message_cooldown { get { @@ -1351,7 +1360,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Enable support for joining Minecraft Realms worlds.. + /// 查詢類似 Enable support for joining Minecraft Realms worlds. 的當地語系化字串。 /// internal static string Main_Advanced_minecraft_realms { get { @@ -1360,7 +1369,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The minimum height to use when calculating the image size from the height of the terminal.. + /// 查詢類似 The minimum height to use when calculating the image size from the height of the terminal. 的當地語系化字串。 /// internal static string Main_Advanced_MinTerminalHeight { get { @@ -1369,7 +1378,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to The minimum width used when calculating the image size from the width of the terminal.. + /// 查詢類似 The minimum width used when calculating the image size from the width of the terminal. 的當地語系化字串。 /// internal static string Main_Advanced_MinTerminalWidth { get { @@ -1378,7 +1387,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Enable head movement while walking to avoid anti-cheat triggers.. + /// 查詢類似 Enable head movement while walking to avoid anti-cheat triggers. 的當地語系化字串。 /// internal static string Main_Advanced_move_head_while_walking { get { @@ -1387,7 +1396,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to A movement speed higher than 2 may be considered cheating.. + /// 查詢類似 A movement speed higher than 2 may be considered cheating. 的當地語系化字串。 /// internal static string Main_Advanced_movement_speed { get { @@ -1396,7 +1405,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Only works on Windows XP-8 or Windows 10 with old console.. + /// 查詢類似 Only works on Windows XP-8 or Windows 10 with old console. 的當地語系化字串。 /// internal static string Main_Advanced_player_head_icon { get { @@ -1405,7 +1414,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to For remote control of the bot.. + /// 查詢類似 For remote control of the bot. 的當地語系化字串。 /// internal static string Main_Advanced_private_msgs_cmd_name { get { @@ -1414,7 +1423,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How to retain profile key. Use "none", "memory" or "disk".. + /// 查詢類似 How to retain profile key. Use "none", "memory" or "disk". 的當地語系化字串。 /// internal static string Main_Advanced_profilekey_cache { get { @@ -1423,7 +1432,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "no", "fast" (5s timeout), or "yes". Required for joining some servers.. + /// 查詢類似 Use "no", "fast" (5s timeout), or "yes". Required for joining some servers. 的當地語系化字串。 /// internal static string Main_Advanced_resolve_srv_records { get { @@ -1432,7 +1441,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Cache compiled scripts for faster load on low-end devices.. + /// 查詢類似 Cache compiled scripts for faster load on low-end devices. 的當地語系化字串。 /// internal static string Main_Advanced_script_cache { get { @@ -1441,9 +1450,9 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to ServerList: It allows an easier and faster server switching with short aliases instead of full server IP + /// 查詢類似 ServerList: It allows an easier and faster server switching with short aliases instead of full server IP ///Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. - ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2". + ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2" 的當地語系化字串。 /// internal static string Main_Advanced_server_list { get { @@ -1452,7 +1461,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to How to retain session tokens. Use "none", "memory" or "disk".. + /// 查詢類似 How to retain session tokens. Use "none", "memory" or "disk". 的當地語系化字串。 /// internal static string Main_Advanced_session_cache { get { @@ -1461,7 +1470,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Decode links embedded in chat messages and show them in console.. + /// 查詢類似 Decode links embedded in chat messages and show them in console. 的當地語系化字串。 /// internal static string Main_Advanced_show_chat_links { get { @@ -1470,7 +1479,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Show inventory layout as ASCII art in inventory command.. + /// 查詢類似 Show inventory layout as ASCII art in inventory command. 的當地語系化字串。 /// internal static string Main_Advanced_show_inventory_layout { get { @@ -1479,7 +1488,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to System messages for server ops.. + /// 查詢類似 System messages for server ops. 的當地語系化字串。 /// internal static string Main_Advanced_show_system_messages { get { @@ -1488,7 +1497,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Messages displayed above xp bar, set this to false in case of xp bar spam.. + /// 查詢類似 Messages displayed above xp bar, set this to false in case of xp bar spam. 的當地語系化字串。 /// internal static string Main_Advanced_show_xpbar_messages { get { @@ -1497,7 +1506,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers.. + /// 查詢類似 Temporary fix for Badpacket issue on some servers. 的當地語系化字串。 /// internal static string Main_Advanced_temporary_fix_badpacket { get { @@ -1506,7 +1515,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log.. + /// 查詢類似 Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log. 的當地語系化字串。 /// internal static string Main_Advanced_TerminalColorDepth { get { @@ -1515,7 +1524,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. + /// 查詢類似 Uses more ram, cpu, bandwidth but allows you to move around. 的當地語系化字串。 /// internal static string Main_Advanced_terrain_and_movements { get { @@ -1524,7 +1533,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Customize the TCP connection timeout with the server. (in seconds). + /// 查詢類似 Customize the TCP connection timeout with the server. (in seconds) 的當地語系化字串。 /// internal static string Main_Advanced_timeout { get { @@ -1533,7 +1542,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Prepend timestamps to chat messages.. + /// 查詢類似 Prepend timestamps to chat messages. 的當地語系化字串。 /// internal static string Main_Advanced_timestamps { get { @@ -1542,7 +1551,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.. + /// 查詢類似 Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup. 的當地語系化字串。 /// internal static string Main_General_account { get { @@ -1551,7 +1560,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to 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). + /// 查詢類似 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) 的當地語系化字串。 /// internal static string Main_General_login { get { @@ -1560,7 +1569,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once.. + /// 查詢類似 Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once. 的當地語系化字串。 /// internal static string Main_General_method { get { @@ -1569,7 +1578,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Account type: "mojang" OR "microsoft". Also affects interactive login in console.. + /// 查詢類似 Account type: "mojang" OR "microsoft". Also affects interactive login in console. 的當地語系化字串。 /// internal static string Main_General_server_info { get { @@ -1578,7 +1587,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Settings below are sent to the server and only affect server-side things like your skin.. + /// 查詢類似 Settings below are sent to the server and only affect server-side things like your skin. 的當地語系化字串。 /// internal static string MCSettings { get { @@ -1587,7 +1596,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Allows disabling chat colors server-side.. + /// 查詢類似 Allows disabling chat colors server-side. 的當地語系化字串。 /// internal static string MCSettings_ChatColors { get { @@ -1596,7 +1605,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use "enabled", "commands", or "disabled". Allows to mute yourself.... + /// 查詢類似 Use "enabled", "commands", or "disabled". Allows to mute yourself... 的當地語系化字串。 /// internal static string MCSettings_ChatMode { get { @@ -1605,7 +1614,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult".. + /// 查詢類似 MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult". 的當地語系化字串。 /// internal static string MCSettings_Difficulty { get { @@ -1614,7 +1623,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to If disabled, settings below are not sent to the server.. + /// 查詢類似 If disabled, settings below are not sent to the server. 的當地語系化字串。 /// internal static string MCSettings_Enabled { get { @@ -1623,7 +1632,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use any language implemented in Minecraft.. + /// 查詢類似 Use any language implemented in Minecraft. 的當地語系化字串。 /// internal static string MCSettings_Locale { get { @@ -1632,7 +1641,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to MC 1.9+ main hand. "left" or "right".. + /// 查詢類似 MC 1.9+ main hand. "left" or "right". 的當地語系化字串。 /// internal static string MCSettings_MainHand { get { @@ -1641,7 +1650,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Value range: [0 - 255].. + /// 查詢類似 Value range: [0 - 255]. 的當地語系化字串。 /// internal static string MCSettings_RenderDistance { get { @@ -1650,10 +1659,10 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Connect to a server via a proxy instead of connecting directly + /// 查詢類似 Connect to a server via a proxy instead of connecting directly ///If Mojang session services are blocked on your network, set Enabled_Login=true to login using proxy. ///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. - ////!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!. + ////!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences! 的當地語系化字串。 /// internal static string Proxy { get { @@ -1662,7 +1671,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to connect to the game server through a proxy.. + /// 查詢類似 Whether to connect to the game server through a proxy. 的當地語系化字串。 /// internal static string Proxy_Enabled_Ingame { get { @@ -1671,7 +1680,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to connect to the login server through a proxy.. + /// 查詢類似 Whether to connect to the login server through a proxy. 的當地語系化字串。 /// internal static string Proxy_Enabled_Login { get { @@ -1680,7 +1689,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to download MCC updates via proxy.. + /// 查詢類似 Whether to download MCC updates via proxy. 的當地語系化字串。 /// internal static string Proxy_Enabled_Update { get { @@ -1689,7 +1698,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Only required for password-protected proxies.. + /// 查詢類似 Only required for password-protected proxies. 的當地語系化字串。 /// internal static string Proxy_Password { get { @@ -1698,7 +1707,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5".. + /// 查詢類似 Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5". 的當地語系化字串。 /// internal static string Proxy_Proxy_Type { get { @@ -1707,7 +1716,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Proxy server must allow HTTPS for login, and non-443 ports for playing.. + /// 查詢類似 Proxy server must allow HTTPS for login, and non-443 ports for playing. 的當地語系化字串。 /// internal static string Proxy_Server { get { @@ -1716,7 +1725,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Only required for password-protected proxies.. + /// 查詢類似 Only required for password-protected proxies. 的當地語系化字串。 /// internal static string Proxy_Username { get { @@ -1725,7 +1734,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Chat signature related settings (affects minecraft 1.19+). + /// 查詢類似 Chat signature related settings (affects minecraft 1.19+) 的當地語系化字串。 /// internal static string Signature { get { @@ -1734,7 +1743,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true". + /// 查詢類似 Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true" 的當地語系化字串。 /// internal static string Signature_LoginWithSecureProfile { get { @@ -1743,7 +1752,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use red    color block to mark chat without legitimate signature. + /// 查詢類似 Use red    color block to mark chat without legitimate signature 的當地語系化字串。 /// internal static string Signature_MarkIllegallySignedMsg { get { @@ -1752,7 +1761,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use green  color block to mark chat with legitimate signatures. + /// 查詢類似 Use green  color block to mark chat with legitimate signatures 的當地語系化字串。 /// internal static string Signature_MarkLegallySignedMsg { get { @@ -1761,7 +1770,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use yellow color block to mark chat that have been modified by the server.. + /// 查詢類似 Use yellow color block to mark chat that have been modified by the server. 的當地語系化字串。 /// internal static string Signature_MarkModifiedMsg { get { @@ -1770,7 +1779,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Use gray   color block to mark system message (always without signature). + /// 查詢類似 Use gray   color block to mark system message (always without signature) 的當地語系化字串。 /// internal static string Signature_MarkSystemMessage { get { @@ -1779,7 +1788,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to display chat and messages in commands without legal signatures. + /// 查詢類似 Whether to display chat and messages in commands without legal signatures 的當地語系化字串。 /// internal static string Signature_ShowIllegalSignedChat { get { @@ -1788,7 +1797,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Set to true to display messages modified by the server, false to display the original signed messages. + /// 查詢類似 Set to true to display messages modified by the server, false to display the original signed messages 的當地語系化字串。 /// internal static string Signature_ShowModifiedChat { get { @@ -1797,7 +1806,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to sign the chat send from MCC. + /// 查詢類似 Whether to sign the chat send from MCC 的當地語系化字串。 /// internal static string Signature_SignChat { get { @@ -1806,7 +1815,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me". + /// 查詢類似 Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me" 的當地語系化字串。 /// internal static string Signature_SignMessageInCommand { get { diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index da56bedc..700f776a 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -189,6 +189,9 @@ You need to enable Entity Handling to use this bot Allow attacking passive mobs. + + Capped between 1 to 4 + How long to wait between each attack. Set "Custom = false" to let MCC calculate it. From 96245193ee91cad44e9244c6a093294271737290 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Tue, 10 Jan 2023 17:01:36 +0800 Subject: [PATCH 16/44] Revert language change in auto-gen file --- .../ConfigComments/ConfigComments.Designer.cs | 460 +++++++++--------- 1 file changed, 230 insertions(+), 230 deletions(-) diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index 76798e5a..f1675799 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// 這段程式碼是由工具產生的。 -// 執行階段版本:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼, -// 變更將會遺失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace MinecraftClient { /// - /// 用於查詢當地語系化字串等的強類型資源類別。 + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // 這個類別是自動產生的,是利用 StronglyTypedResourceBuilder - // 類別透過 ResGen 或 Visual Studio 這類工具。 - // 若要加入或移除成員,請編輯您的 .ResX 檔,然後重新執行 ResGen - // (利用 /str 選項),或重建您的 VS 專案。 + // This class was auto-generated by the StronglyTypedResourceBuilder + // 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", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace MinecraftClient { } /// - /// 傳回這個類別使用的快取的 ResourceManager 執行個體。 + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace MinecraftClient { } /// - /// 覆寫目前執行緒的 CurrentUICulture 屬性,對象是所有 - /// 使用這個強類型資源類別的資源查閱。 + /// 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)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,8 +61,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 can be used in some other fields as %yourvar% - ///%username% and %serverip% are reserved variables. 的當地語系化字串。 + /// Looks up a localized string similar to can be used in some other fields as %yourvar% + ///%username% and %serverip% are reserved variables.. /// internal static string AppVars_Variables { get { @@ -71,9 +71,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 =============================== # + /// Looks up a localized string similar to =============================== # /// Minecraft Console Client Bots # - ///=============================== # 的當地語系化字串。 + ///=============================== #. /// internal static string ChatBot { get { @@ -82,8 +82,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 Get alerted when specified words are detected in chat - ///Useful for moderating your server or detecting when someone is talking to you 的當地語系化字串。 + /// Looks up a localized string similar to Get alerted when specified words are detected in chat + ///Useful for moderating your server or detecting when someone is talking to you. /// internal static string ChatBot_Alerts { get { @@ -92,7 +92,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Play a beep sound when a word is detected in addition to highlighting. 的當地語系化字串。 + /// Looks up a localized string similar to Play a beep sound when a word is detected in addition to highlighting.. /// internal static string ChatBot_Alerts_Beep_Enabled { get { @@ -101,7 +101,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 List of words/strings to NOT alert you on. 的當地語系化字串。 + /// Looks up a localized string similar to List of words/strings to NOT alert you on.. /// internal static string ChatBot_Alerts_Excludes { get { @@ -110,7 +110,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The name of a file where alers logs will be written. 的當地語系化字串。 + /// Looks up a localized string similar to The name of a file where alers logs will be written.. /// internal static string ChatBot_Alerts_Log_File { get { @@ -119,7 +119,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Log alerts info a file. 的當地語系化字串。 + /// Looks up a localized string similar to Log alerts info a file.. /// internal static string ChatBot_Alerts_Log_To_File { get { @@ -128,7 +128,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 List of words/strings to alert you on. 的當地語系化字串。 + /// Looks up a localized string similar to List of words/strings to alert you on.. /// internal static string ChatBot_Alerts_Matches { get { @@ -137,7 +137,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Trigger alerts when it rains and when it stops. 的當地語系化字串。 + /// Looks up a localized string similar to Trigger alerts when it rains and when it stops.. /// internal static string ChatBot_Alerts_Trigger_By_Rain { get { @@ -146,7 +146,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Triggers alerts at the beginning and end of thunderstorms. 的當地語系化字串。 + /// Looks up a localized string similar to Triggers alerts at the beginning and end of thunderstorms.. /// internal static string ChatBot_Alerts_Trigger_By_Thunderstorm { get { @@ -155,7 +155,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Triggers an alert after receiving a specified keyword. 的當地語系化字串。 + /// Looks up a localized string similar to Triggers an alert after receiving a specified keyword.. /// internal static string ChatBot_Alerts_Trigger_By_Words { get { @@ -164,9 +164,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Send a command on a regular or random basis or make the bot walk around randomly to avoid automatic AFK disconnection + /// Looks up a localized string similar to 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) 的當地語系化字串。 + ////!\ Make sure you keep the bot in an enclosure to prevent it wandering off if you're using terrain handling! (Recommended size 5x5x5). /// internal static string ChatBot_AntiAfk { get { @@ -175,7 +175,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Command to send to the server. 的當地語系化字串。 + /// Looks up a localized string similar to Command to send to the server.. /// internal static string ChatBot_AntiAfk_Command { get { @@ -184,7 +184,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The time interval for execution. (in seconds) 的當地語系化字串。 + /// Looks up a localized string similar to The time interval for execution. (in seconds). /// internal static string ChatBot_AntiAfk_Delay { get { @@ -193,7 +193,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to sneak when sending the command. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to sneak when sending the command.. /// internal static string ChatBot_AntiAfk_Use_Sneak { get { @@ -202,7 +202,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use terrain handling to enable the bot to move around. 的當地語系化字串。 + /// Looks up a localized string similar to Use terrain handling to enable the bot to move around.. /// internal static string ChatBot_AntiAfk_Use_Terrain_Handling { get { @@ -211,7 +211,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be) 的當地語系化字串。 + /// Looks up a localized string similar to The range the bot can move around randomly (Note: the bigger the range, the slower the bot will be). /// internal static string ChatBot_AntiAfk_Walk_Range { get { @@ -220,7 +220,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How many times can the bot fail trying to move before using the command method. 的當地語系化字串。 + /// Looks up a localized string similar to How many times can the bot fail trying to move before using the command method.. /// internal static string ChatBot_AntiAfk_Walk_Retries { get { @@ -229,10 +229,10 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically attack hostile mobs around you + /// Looks up a localized string similar to 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! 的當地語系化字串。 + ////!\ SERVER PLUGINS may consider AutoAttack to be a CHEAT MOD and TAKE ACTION AGAINST YOUR ACCOUNT so DOUBLE CHECK WITH SERVER RULES!. /// internal static string ChatBot_AutoAttack { get { @@ -241,7 +241,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Allow attacking hostile mobs. 的當地語系化字串。 + /// Looks up a localized string similar to Allow attacking hostile mobs.. /// internal static string ChatBot_AutoAttack_Attack_Hostile { get { @@ -250,7 +250,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Allow attacking passive mobs. 的當地語系化字串。 + /// Looks up a localized string similar to Allow attacking passive mobs.. /// internal static string ChatBot_AutoAttack_Attack_Passive { get { @@ -259,7 +259,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Capped between 1 to 4 的當地語系化字串。 + /// Looks up a localized string similar to Capped between 1 to 4. /// internal static string ChatBot_AutoAttack_Attack_Range { get { @@ -268,7 +268,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How long to wait between each attack. Set "Custom = false" to let MCC calculate it. 的當地語系化字串。 + /// Looks up a localized string similar to How long to wait between each attack. Set "Custom = false" to let MCC calculate it.. /// internal static string ChatBot_AutoAttack_Cooldown_Time { get { @@ -277,7 +277,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 All entity types can be found here: https://mccteam.github.io/r/entity/#L15 的當地語系化字串。 + /// Looks up a localized string similar to All entity types can be found here: https://mccteam.github.io/r/entity/#L15. /// internal static string ChatBot_AutoAttack_Entites_List { get { @@ -286,7 +286,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack). 的當地語系化字串。 + /// Looks up a localized string similar to Possible values: "Interact", "Attack" (default), "InteractAt" (Interact and Attack).. /// internal static string ChatBot_AutoAttack_Interaction { get { @@ -295,7 +295,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Wether to treat the entities list as a "whitelist" or as a "blacklist". 的當地語系化字串。 + /// Looks up a localized string similar to Wether to treat the entities list as a "whitelist" or as a "blacklist".. /// internal static string ChatBot_AutoAttack_List_Mode { get { @@ -304,7 +304,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 "single" or "multi". single target one mob per attack. multi target all mobs in range per attack 的當地語系化字串。 + /// Looks up a localized string similar to "single" or "multi". single target one mob per attack. multi target all mobs in range per attack. /// internal static string ChatBot_AutoAttack_Mode { get { @@ -313,7 +313,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 "health" or "distance". Only needed when using single mode 的當地語系化字串。 + /// Looks up a localized string similar to "health" or "distance". Only needed when using single mode. /// internal static string ChatBot_AutoAttack_Priority { get { @@ -322,10 +322,10 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically craft items in your inventory + /// Looks up a localized string similar to Automatically craft items in your inventory ///See https://mccteam.github.io/g/bots/#auto-craft 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 的當地語系化字串。 + ///You should also enable Terrain and Movements if you need to use a crafting table. /// internal static string ChatBot_AutoCraft { get { @@ -334,7 +334,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Location of the crafting table if you intended to use it. Terrain and movements must be enabled. 的當地語系化字串。 + /// Looks up a localized string similar to Location of the crafting table if you intended to use it. Terrain and movements must be enabled.. /// internal static string ChatBot_AutoCraft_CraftingTable { get { @@ -343,7 +343,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 What to do on crafting failure, "abort" or "wait". 的當地語系化字串。 + /// Looks up a localized string similar to What to do on crafting failure, "abort" or "wait".. /// internal static string ChatBot_AutoCraft_OnFailure { get { @@ -352,11 +352,11 @@ namespace MinecraftClient { } /// - /// 查詢類似 Recipes.Name: The name can be whatever you like and it is used to represent the recipe. + /// Looks up a localized string similar to Recipes.Name: The name can be whatever you like and it is used to represent the recipe. ///Recipes.Type: crafting table type: "player" or "table" ///Recipes.Result: the resulting item ///Recipes.Slots: All slots, counting from left to right, top to bottom. Please fill in "Null" for empty slots. - ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12 的當地語系化字串。 + ///For the naming of the items, please see: https://mccteam.github.io/r/item/#L12. /// internal static string ChatBot_AutoCraft_Recipes { get { @@ -365,11 +365,11 @@ namespace MinecraftClient { } /// - /// 查詢類似 Auto-digging blocks. + /// Looks up a localized string similar to Auto-digging blocks. ///You need to enable Terrain Handling to use this bot ///You can use "/digbot start" and "/digbot stop" to control the start and stop of AutoDig. ///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. - ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15 的當地語系化字串。 + ///For the naming of the block, please see https://mccteam.github.io/r/block/#L15. /// internal static string ChatBot_AutoDig { get { @@ -378,7 +378,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start. 的當地語系化字串。 + /// Looks up a localized string similar to How many seconds to wait after entering the game to start digging automatically, set to -1 to disable automatic start.. /// internal static string ChatBot_AutoDig_Auto_Start_Delay { get { @@ -387,7 +387,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically switch to the appropriate tool. 的當地語系化字串。 + /// Looks up a localized string similar to Automatically switch to the appropriate tool.. /// internal static string ChatBot_AutoDig_Auto_Tool_Switch { get { @@ -396,7 +396,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Mining a block for more than "Dig_Timeout" seconds will be considered a timeout. 的當地語系化字串。 + /// Looks up a localized string similar to Mining a block for more than "Dig_Timeout" seconds will be considered a timeout.. /// internal static string ChatBot_AutoDig_Dig_Timeout { get { @@ -405,7 +405,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to drop the current tool when its durability is too low. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to drop the current tool when its durability is too low.. /// internal static string ChatBot_AutoDig_Drop_Low_Durability_Tools { get { @@ -414,7 +414,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Will not use tools with less durability than this. Set to zero to disable this feature. 的當地語系化字串。 + /// Looks up a localized string similar to Will not use tools with less durability than this. Set to zero to disable this feature.. /// internal static string ChatBot_AutoDig_Durability_Limit { get { @@ -423,7 +423,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Wether to treat the blocks list as a "whitelist" or as a "blacklist". 的當地語系化字串。 + /// Looks up a localized string similar to Wether to treat the blocks list as a "whitelist" or as a "blacklist".. /// internal static string ChatBot_AutoDig_List_Type { get { @@ -432,7 +432,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list. 的當地語系化字串。 + /// Looks up a localized string similar to "distance" or "index", When using the "fixedpos" mode, the blocks are determined by distance to the player, or by the order in the list.. /// internal static string ChatBot_AutoDig_Location_Order { get { @@ -441,7 +441,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The position of the blocks when using "fixedpos" or "both" mode. 的當地語系化字串。 + /// Looks up a localized string similar to The position of the blocks when using "fixedpos" or "both" mode.. /// internal static string ChatBot_AutoDig_Locations { get { @@ -450,7 +450,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to output logs when digging blocks. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to output logs when digging blocks.. /// internal static string ChatBot_AutoDig_Log_Block_Dig { get { @@ -459,7 +459,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 "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. 的當地語系化字串。 + /// Looks up a localized string similar to "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.. /// internal static string ChatBot_AutoDig_Mode { get { @@ -468,9 +468,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically drop items in inventory + /// Looks up a localized string similar to Automatically drop items in inventory ///You need to enable Inventory Handling to use this bot - ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12 的當地語系化字串。 + ///See this file for an up-to-date list of item types you can use with this bot: https://mccteam.github.io/r/item/#L12. /// internal static string ChatBot_AutoDrop { get { @@ -479,7 +479,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list 的當地語系化字串。 + /// Looks up a localized string similar to "include", "exclude" or "everything". Include: drop item IN the list. Exclude: drop item NOT IN the list. /// internal static string ChatBot_AutoDrop_Mode { get { @@ -488,8 +488,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically eat food when your Hunger value is low - ///You need to enable Inventory Handling to use this bot 的當地語系化字串。 + /// Looks up a localized string similar to Automatically eat food when your Hunger value is low + ///You need to enable Inventory Handling to use this bot. /// internal static string ChatBot_AutoEat { get { @@ -498,10 +498,10 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically catch fish using a fishing rod + /// Looks up a localized string similar to Automatically catch fish using a fishing rod ///Guide: https://mccteam.github.io/g/bots/#auto-fishing ///You can use "/fish" to control the bot manually. - ////!\ Make sure server rules allow automated farming before using this bot 的當地語系化字串。 + ////!\ Make sure server rules allow automated farming before using this bot. /// internal static string ChatBot_AutoFishing { get { @@ -510,7 +510,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Keep it as false if you have not changed it before. 的當地語系化字串。 + /// Looks up a localized string similar to Keep it as false if you have not changed it before.. /// internal static string ChatBot_AutoFishing_Antidespawn { get { @@ -519,7 +519,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Switch to a new rod from inventory after the current rod is unavailable. 的當地語系化字串。 + /// Looks up a localized string similar to Switch to a new rod from inventory after the current rod is unavailable.. /// internal static string ChatBot_AutoFishing_Auto_Rod_Switch { get { @@ -528,7 +528,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to start fishing automatically after entering a world. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to start fishing automatically after entering a world.. /// internal static string ChatBot_AutoFishing_Auto_Start { get { @@ -537,7 +537,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How soon to re-cast after successful fishing. 的當地語系化字串。 + /// Looks up a localized string similar to How soon to re-cast after successful fishing.. /// internal static string ChatBot_AutoFishing_Cast_Delay { get { @@ -546,7 +546,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature. 的當地語系化字串。 + /// Looks up a localized string similar to Will not use rods with less durability than this (full durability is 64). Set to zero to disable this feature.. /// internal static string ChatBot_AutoFishing_Durability_Limit { get { @@ -555,7 +555,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 This allows the player to change position/facing after each fish caught. 的當地語系化字串。 + /// Looks up a localized string similar to This allows the player to change position/facing after each fish caught.. /// internal static string ChatBot_AutoFishing_Enable_Move { get { @@ -564,7 +564,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How long after entering the game to start fishing (seconds). 的當地語系化字串。 + /// Looks up a localized string similar to How long after entering the game to start fishing (seconds).. /// internal static string ChatBot_AutoFishing_Fishing_Delay { get { @@ -573,7 +573,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Fishing timeout (seconds). Timeout will trigger a re-cast. 的當地語系化字串。 + /// Looks up a localized string similar to Fishing timeout (seconds). Timeout will trigger a re-cast.. /// internal static string ChatBot_AutoFishing_Fishing_Timeout { get { @@ -582,7 +582,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish. 的當地語系化字串。 + /// Looks up a localized string similar to A "stationary" hook that moves above this threshold in the Y-axis will be considered to have caught a fish.. /// internal static string ChatBot_AutoFishing_Hook_Threshold { get { @@ -591,7 +591,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 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. 的當地語系化字串。 + /// Looks up a localized string similar to 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.. /// internal static string ChatBot_AutoFishing_Log_Fish_Bobber { get { @@ -600,7 +600,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use the mainhand or the offhand to hold the rod. 的當地語系化字串。 + /// Looks up a localized string similar to Use the mainhand or the offhand to hold the rod.. /// internal static string ChatBot_AutoFishing_Mainhand { get { @@ -609,7 +609,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 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. 的當地語系化字串。 + /// Looks up a localized string similar to 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.. /// internal static string ChatBot_AutoFishing_Movements { get { @@ -618,7 +618,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Hook movement in the X and Z axis less than this value will be considered stationary. 的當地語系化字串。 + /// Looks up a localized string similar to Hook movement in the X and Z axis less than this value will be considered stationary.. /// internal static string ChatBot_AutoFishing_Stationary_Threshold { get { @@ -627,8 +627,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically relog when disconnected by server, for example because the server is restating - ////!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks 的當地語系化字串。 + /// Looks up a localized string similar to Automatically relog when disconnected by server, for example because the server is restating + ////!\ Use Ignore_Kick_Message=true at own risk! Server staff might not appreciate if you auto-relog on manual kicks. /// internal static string ChatBot_AutoRelog { get { @@ -637,7 +637,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The delay time before joining the server. (in seconds) 的當地語系化字串。 + /// Looks up a localized string similar to The delay time before joining the server. (in seconds). /// internal static string ChatBot_AutoRelog_Delay { get { @@ -646,7 +646,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 When set to true, autorelog will reconnect regardless of kick messages. 的當地語系化字串。 + /// Looks up a localized string similar to When set to true, autorelog will reconnect regardless of kick messages.. /// internal static string ChatBot_AutoRelog_Ignore_Kick_Message { get { @@ -655,7 +655,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 If the kickout message matches any of the strings, then autorelog will be triggered. 的當地語系化字串。 + /// Looks up a localized string similar to If the kickout message matches any of the strings, then autorelog will be triggered.. /// internal static string ChatBot_AutoRelog_Kick_Messages { get { @@ -664,7 +664,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Retries when failing to relog to the server. use -1 for unlimited retries. 的當地語系化字串。 + /// Looks up a localized string similar to Retries when failing to relog to the server. use -1 for unlimited retries.. /// internal static string ChatBot_AutoRelog_Retries { get { @@ -673,9 +673,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Run commands or send messages automatically when a specified pattern is detected in chat + /// Looks up a localized string similar to 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 的當地語系化字串。 + ////!\ This bot may get spammy depending on your rules, although the global messagecooldown setting can help you avoiding accidental spam. /// internal static string ChatBot_AutoRespond { get { @@ -684,7 +684,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work) 的當地語系化字串。 + /// Looks up a localized string similar to Do not remove colors from text (Note: Your matches will have to include color codes (ones using the § character) in order to work). /// internal static string ChatBot_AutoRespond_Match_Colors { get { @@ -693,7 +693,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Logs chat messages in a file on disk. 的當地語系化字串。 + /// Looks up a localized string similar to Logs chat messages in a file on disk.. /// internal static string ChatBot_ChatLog { get { @@ -702,12 +702,12 @@ namespace MinecraftClient { } /// - /// 查詢類似 This bot allows you to send and recieve messages and commands via a Discord channel. + /// Looks up a localized string similar to This bot allows you to send and recieve messages and commands via a Discord channel. ///For Setup you can either use the documentation or read here (Documentation has images). ///Documentation: https://mccteam.github.io/g/bots/#discord-bridge ///Setup: ///First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA . - ////!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [字串的其餘部分已遭截斷]"; 的當地語系化字串。 + ////!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent [rest of string was truncated]";. /// internal static string ChatBot_DiscordBridge { get { @@ -716,7 +716,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The ID of a channel where you want to interact with the MCC using the bot. 的當地語系化字串。 + /// Looks up a localized string similar to The ID of a channel where you want to interact with the MCC using the bot.. /// internal static string ChatBot_DiscordBridge_ChannelId { get { @@ -725,10 +725,10 @@ namespace MinecraftClient { } /// - /// 查詢類似 Message formats + /// Looks up a localized string similar to Message formats ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html 的當地語系化字串。 + ///For Discord message formatting, check the following: https://mccteam.github.io/r/dc-fmt.html. /// internal static string ChatBot_DiscordBridge_Formats { get { @@ -737,7 +737,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The ID of a server/guild where you have invited the bot to. 的當地語系化字串。 + /// Looks up a localized string similar to The ID of a server/guild where you have invited the bot to.. /// internal static string ChatBot_DiscordBridge_GuildId { get { @@ -746,7 +746,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second). 的當地語系化字串。 + /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).. /// internal static string ChatBot_DiscordBridge_MessageSendTimeout { get { @@ -755,7 +755,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 A list of IDs of people you want to be able to interact with the MCC using the bot. 的當地語系化字串。 + /// Looks up a localized string similar to A list of IDs of people you want to be able to interact with the MCC using the bot.. /// internal static string ChatBot_DiscordBridge_OwnersIds { get { @@ -764,7 +764,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Your Discord Bot token. 的當地語系化字串。 + /// Looks up a localized string similar to Your Discord Bot token.. /// internal static string ChatBot_DiscordBridge_Token { get { @@ -773,11 +773,11 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically farms crops for you (plants, breaks and bonemeals them). + /// Looks up a localized string similar to Automatically farms crops for you (plants, breaks and bonemeals them). ///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat. ///Usage: "/farmer start" command and "/farmer stop" command. ///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes. - ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [字串的其餘部分已遭截斷]"; 的當地語系化字串。 + ///or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this. [rest of string was truncated]";. /// internal static string ChatBot_Farmer { get { @@ -786,7 +786,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Delay between tasks in seconds (Minimum 1 second) 的當地語系化字串。 + /// Looks up a localized string similar to Delay between tasks in seconds (Minimum 1 second). /// internal static string ChatBot_Farmer_Delay_Between_Tasks { get { @@ -795,11 +795,11 @@ namespace MinecraftClient { } /// - /// 查詢類似 Enabled you to make the bot follow you + /// Looks up a localized string similar to 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, /// [字串的其餘部分已遭截斷]"; 的當地語系化字串。 + ///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, /// [rest of string was truncated]";. /// internal static string ChatBot_FollowPlayer { get { @@ -808,7 +808,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 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) 的當地語系化字串。 + /// Looks up a localized string similar to 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). /// internal static string ChatBot_FollowPlayer_Stop_At_Distance { get { @@ -817,7 +817,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow) 的當地語系化字串。 + /// Looks up a localized string similar to The rate at which the bot does calculations (in seconds) (You can tweak this if you feel the bot is too slow). /// internal static string ChatBot_FollowPlayer_Update_Limit { get { @@ -826,9 +826,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 A small game to demonstrate chat interactions. Players can guess mystery words one letter at a time. + /// Looks up a localized string similar to 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 <bot username> start - ////!\ This bot may get a bit spammy if many players are interacting with it 的當地語系化字串。 + ////!\ This bot may get a bit spammy if many players are interacting with it. /// internal static string ChatBot_HangmanGame { get { @@ -837,9 +837,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Relay messages between players and servers, like a mail plugin + /// Looks up a localized string similar to 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 的當地語系化字串。 + ////!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins. /// internal static string ChatBot_Mailer { get { @@ -848,12 +848,12 @@ namespace MinecraftClient { } /// - /// 查詢類似 Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) + /// Looks up a localized string similar to Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot) ///This is useful for solving captchas which use maps ///The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled. ///NOTE: ///If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console. - ////!\ Make sure server rules allow bots to be used on the server, or you risk being punished. 的當地語系化字串。 + ////!\ Make sure server rules allow bots to be used on the server, or you risk being punished.. /// internal static string ChatBot_Map { get { @@ -862,7 +862,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Automatically render the map once it is received or updated from/by the server 的當地語系化字串。 + /// Looks up a localized string similar to Automatically render the map once it is received or updated from/by the server. /// internal static string ChatBot_Map_Auto_Render_On_Update { get { @@ -871,7 +871,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Delete all rendered maps on unload/reload or when you launch the MCC again. 的當地語系化字串。 + /// Looks up a localized string similar to Delete all rendered maps on unload/reload or when you launch the MCC again.. /// internal static string ChatBot_Map_Delete_All_On_Unload { get { @@ -880,7 +880,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Get a notification when you have gotten a map from the server for the first time 的當地語系化字串。 + /// Looks up a localized string similar to Get a notification when you have gotten a map from the server for the first time. /// internal static string ChatBot_Map_Notify_On_First_Update { get { @@ -889,7 +889,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord. 的當地語系化字串。 + /// Looks up a localized string similar to Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord.. /// internal static string ChatBot_Map_Rasize_Rendered_Image { get { @@ -898,7 +898,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to render the map in the console. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to render the map in the console.. /// internal static string ChatBot_Map_Render_In_Console { get { @@ -907,7 +907,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The size that a rendered image should be resized to, in pixels (eg. 512). 的當地語系化字串。 + /// Looks up a localized string similar to The size that a rendered image should be resized to, in pixels (eg. 512).. /// internal static string ChatBot_Map_Resize_To { get { @@ -916,7 +916,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge). 的當地語系化字串。 + /// Looks up a localized string similar to Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge).. /// internal static string ChatBot_Map_Save_To_File { get { @@ -925,9 +925,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) + /// Looks up a localized string similar to Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram Bridge chat bot must be enabled and configured!) ///You need to enable Save_To_File in order for this to work. - ///We also recommend turning on resizing. 的當地語系化字串。 + ///We also recommend turning on resizing.. /// internal static string ChatBot_Map_Send_Rendered_To_Bridges { get { @@ -936,7 +936,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Log the list of players periodically into a textual file. 的當地語系化字串。 + /// Looks up a localized string similar to Log the list of players periodically into a textual file.. /// internal static string ChatBot_PlayerListLogger { get { @@ -945,7 +945,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 (In seconds) 的當地語系化字串。 + /// Looks up a localized string similar to (In seconds). /// internal static string ChatBot_PlayerListLogger_Delay { get { @@ -954,9 +954,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Send MCC console commands to your bot through server PMs (/tell) + /// Looks up a localized string similar to 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 的當地語系化字串。 + ////!\ Server admins can spoof PMs (/tellraw, /nick) so enable RemoteControl only if you trust server admins. /// internal static string ChatBot_RemoteControl { get { @@ -965,9 +965,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 Enable recording of the game (/replay start) and replay it later using the Replay Mod (https://www.replaymod.com/) + /// Looks up a localized string similar to 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! 的當地語系化字串。 + ////!\ You SHOULD use /replay stop or exit the program gracefully with /quit OR THE REPLAY FILE MAY GET CORRUPT!. /// internal static string ChatBot_ReplayCapture { get { @@ -976,7 +976,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How long should replay file be auto-saved, in seconds. Use -1 to disable. 的當地語系化字串。 + /// Looks up a localized string similar to How long should replay file be auto-saved, in seconds. Use -1 to disable.. /// internal static string ChatBot_ReplayCapture_Backup_Interval { get { @@ -985,8 +985,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 Schedule commands and scripts to launch on various events such as server join, date/time or time interval - ///See https://mccteam.github.io/g/bots/#script-scheduler for more info 的當地語系化字串。 + /// Looks up a localized string similar to Schedule commands and scripts to launch on various events such as server join, date/time or time interval + ///See https://mccteam.github.io/g/bots/#script-scheduler for more info. /// internal static string ChatBot_ScriptScheduler { get { @@ -995,12 +995,12 @@ namespace MinecraftClient { } /// - /// 查詢類似 This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. + /// Looks up a localized string similar to This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel. ////!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel. ///----------------------------------------------------------- ///Setup: ///First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather - ///Click on "Start" button and re [字串的其餘部分已遭截斷]"; 的當地語系化字串。 + ///Click on "Start" button and re [rest of string was truncated]";. /// internal static string ChatBot_TelegramBridge { get { @@ -1009,7 +1009,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram. 的當地語系化字串。 + /// Looks up a localized string similar to A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.. /// internal static string ChatBot_TelegramBridge_Authorized_Chat_Ids { get { @@ -1018,7 +1018,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 An ID of a channel where you want to interact with the MCC using the bot. 的當地語系化字串。 + /// Looks up a localized string similar to An ID of a channel where you want to interact with the MCC using the bot.. /// internal static string ChatBot_TelegramBridge_ChannelId { get { @@ -1027,10 +1027,10 @@ namespace MinecraftClient { } /// - /// 查詢類似 Message formats + /// Looks up a localized string similar to Message formats ///Words wrapped with { and } are going to be replaced during the code execution, do not change them! ///For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time. - ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html 的當地語系化字串。 + ///For Telegram message formatting, check the following: https://mccteam.github.io/r/tg-fmt.html. /// internal static string ChatBot_TelegramBridge_Formats { get { @@ -1039,7 +1039,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second). 的當地語系化字串。 + /// Looks up a localized string similar to How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).. /// internal static string ChatBot_TelegramBridge_MessageSendTimeout { get { @@ -1048,7 +1048,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Your Telegram Bot token. 的當地語系化字串。 + /// Looks up a localized string similar to Your Telegram Bot token.. /// internal static string ChatBot_TelegramBridge_Token { get { @@ -1057,8 +1057,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 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 https://mccteam.github.io/g/conf/#chat-format-section 的當地語系化字串。 + /// Looks up a localized string similar to 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 https://mccteam.github.io/g/conf/#chat-format-section. /// internal static string ChatFormat { get { @@ -1067,7 +1067,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 MCC support for common message formats. Set "false" to avoid conflicts with custom formats. 的當地語系化字串。 + /// Looks up a localized string similar to MCC support for common message formats. Set "false" to avoid conflicts with custom formats.. /// internal static string ChatFormat_Builtins { get { @@ -1076,7 +1076,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to use the custom regular expressions below for detection. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to use the custom regular expressions below for detection.. /// internal static string ChatFormat_UserDefined { get { @@ -1085,11 +1085,11 @@ namespace MinecraftClient { } /// - /// 查詢類似 Startup Config File + /// Looks up a localized string similar to Startup Config File ///Please do not record extraneous data in this file as it will be overwritten by MCC. /// ///New to Minecraft Console Client? Check out this document: https://mccteam.github.io/g/conf.html - ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download 的當地語系化字串。 + ///Want to upgrade to a newer version? See https://github.com/MCCTeam/Minecraft-Console-Client/#download. /// internal static string Head { get { @@ -1098,7 +1098,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 This setting affects only the messages in the console. 的當地語系化字串。 + /// Looks up a localized string similar to This setting affects only the messages in the console.. /// internal static string Logging { get { @@ -1107,7 +1107,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Regex for filtering chat message. 的當地語系化字串。 + /// Looks up a localized string similar to Regex for filtering chat message.. /// internal static string Logging_ChatFilter { get { @@ -1116,7 +1116,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Show server chat messages. 的當地語系化字串。 + /// Looks up a localized string similar to Show server chat messages.. /// internal static string Logging_ChatMessages { get { @@ -1125,7 +1125,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Regex for filtering debug message. 的當地語系化字串。 + /// Looks up a localized string similar to Regex for filtering debug message.. /// internal static string Logging_DebugFilter { get { @@ -1134,7 +1134,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Please enable this before submitting bug reports. Thanks! 的當地語系化字串。 + /// Looks up a localized string similar to Please enable this before submitting bug reports. Thanks!. /// internal static string Logging_DebugMessages { get { @@ -1143,7 +1143,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Show error messages. 的當地語系化字串。 + /// Looks up a localized string similar to Show error messages.. /// internal static string Logging_ErrorMessages { get { @@ -1152,7 +1152,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex. 的當地語系化字串。 + /// Looks up a localized string similar to "disable" or "blacklist" OR "whitelist". Blacklist hide message match regex. Whitelist show message match regex.. /// internal static string Logging_FilterMode { get { @@ -1161,7 +1161,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Informative messages. (i.e Most of the message from MCC) 的當地語系化字串。 + /// Looks up a localized string similar to Informative messages. (i.e Most of the message from MCC). /// internal static string Logging_InfoMessages { get { @@ -1170,7 +1170,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Log file name. 的當地語系化字串。 + /// Looks up a localized string similar to Log file name.. /// internal static string Logging_LogFile { get { @@ -1179,7 +1179,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Write log messages to file. 的當地語系化字串。 + /// Looks up a localized string similar to Write log messages to file.. /// internal static string Logging_LogToFile { get { @@ -1188,7 +1188,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Prepend timestamp to messages in log file. 的當地語系化字串。 + /// Looks up a localized string similar to Prepend timestamp to messages in log file.. /// internal static string Logging_PrependTimestamp { get { @@ -1197,7 +1197,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Keep color codes in the saved text.(look like "§b") 的當地語系化字串。 + /// Looks up a localized string similar to Keep color codes in the saved text.(look like "§b"). /// internal static string Logging_SaveColorCodes { get { @@ -1206,7 +1206,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Show warning messages. 的當地語系化字串。 + /// Looks up a localized string similar to Show warning messages.. /// internal static string Logging_WarningMessages { get { @@ -1215,7 +1215,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Make sure you understand what each setting does before changing anything! 的當地語系化字串。 + /// Looks up a localized string similar to Make sure you understand what each setting does before changing anything!. /// internal static string Main_Advanced { get { @@ -1224,8 +1224,8 @@ namespace MinecraftClient { } /// - /// 查詢類似 AccountList: It allows a fast account switching without directly using the credentials - ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1" 的當地語系化字串。 + /// Looks up a localized string similar to AccountList: It allows a fast account switching without directly using the credentials + ///Usage examples: "/tell <mybot> reco Player2", "/connect <serverip> Player1". /// internal static string Main_Advanced_account_list { get { @@ -1234,7 +1234,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Toggle auto respawn if client player was dead (make sure your spawn point is safe). 的當地語系化字串。 + /// Looks up a localized string similar to Toggle auto respawn if client player was dead (make sure your spawn point is safe).. /// internal static string Main_Advanced_auto_respawn { get { @@ -1243,7 +1243,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Set the owner of the bot. /!\ Server admins can impersonate owners! 的當地語系化字串。 + /// Looks up a localized string similar to Set the owner of the bot. /!\ Server admins can impersonate owners!. /// internal static string Main_Advanced_bot_owners { get { @@ -1252,7 +1252,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server. 的當地語系化字串。 + /// Looks up a localized string similar to Use "mcc", "vanilla" or "none". This is how MCC identifies itself to the server.. /// internal static string Main_Advanced_brand_info { get { @@ -1261,7 +1261,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Leave empty for no logfile. 的當地語系化字串。 + /// Looks up a localized string similar to Leave empty for no logfile.. /// internal static string Main_Advanced_chatbot_log_file { get { @@ -1270,7 +1270,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 If turned off, the emoji will be replaced with a simpler character (for /chunk status). 的當地語系化字串。 + /// Looks up a localized string similar to If turned off, the emoji will be replaced with a simpler character (for /chunk status).. /// internal static string Main_Advanced_enable_emoji { get { @@ -1279,7 +1279,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Toggle entity handling. 的當地語系化字串。 + /// Looks up a localized string similar to Toggle entity handling.. /// internal static string Main_Advanced_entity_handling { get { @@ -1288,7 +1288,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to exit directly when an error occurs, for using MCC in non-interactive scripts. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to exit directly when an error occurs, for using MCC in non-interactive scripts.. /// internal static string Main_Advanced_exit_on_failure { get { @@ -1297,7 +1297,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "none", "slash"(/) or "backslash"(\). 的當地語系化字串。 + /// Looks up a localized string similar to Use "none", "slash"(/) or "backslash"(\).. /// internal static string Main_Advanced_internal_cmd_char { get { @@ -1306,7 +1306,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Toggle inventory handling. 的當地語系化字串。 + /// Looks up a localized string similar to Toggle inventory handling.. /// internal static string Main_Advanced_inventory_handling { get { @@ -1315,7 +1315,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html 的當地語系化字串。 + /// Looks up a localized string similar to Fill in with in-game locale code, check https://mccteam.github.io/r/l-code.html. /// internal static string Main_Advanced_language { get { @@ -1324,7 +1324,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Load translations applied to MCC when available, turn it off to use English only. 的當地語系化字串。 + /// Looks up a localized string similar to Load translations applied to MCC when available, turn it off to use English only.. /// internal static string Main_Advanced_LoadMccTrans { get { @@ -1333,7 +1333,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "auto", "no" or "force". Force-enabling only works for MC 1.13+. 的當地語系化字串。 + /// Looks up a localized string similar to Use "auto", "no" or "force". Force-enabling only works for MC 1.13+.. /// internal static string Main_Advanced_mc_forge { get { @@ -1342,7 +1342,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "auto" or "1.X.X" values. Allows to skip server info retrieval. 的當地語系化字串。 + /// Looks up a localized string similar to Use "auto" or "1.X.X" values. Allows to skip server info retrieval.. /// internal static string Main_Advanced_mc_version { get { @@ -1351,7 +1351,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Controls the minimum interval (in seconds) between sending each message to the server. 的當地語系化字串。 + /// Looks up a localized string similar to Controls the minimum interval (in seconds) between sending each message to the server.. /// internal static string Main_Advanced_message_cooldown { get { @@ -1360,7 +1360,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Enable support for joining Minecraft Realms worlds. 的當地語系化字串。 + /// Looks up a localized string similar to Enable support for joining Minecraft Realms worlds.. /// internal static string Main_Advanced_minecraft_realms { get { @@ -1369,7 +1369,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The minimum height to use when calculating the image size from the height of the terminal. 的當地語系化字串。 + /// Looks up a localized string similar to The minimum height to use when calculating the image size from the height of the terminal.. /// internal static string Main_Advanced_MinTerminalHeight { get { @@ -1378,7 +1378,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 The minimum width used when calculating the image size from the width of the terminal. 的當地語系化字串。 + /// Looks up a localized string similar to The minimum width used when calculating the image size from the width of the terminal.. /// internal static string Main_Advanced_MinTerminalWidth { get { @@ -1387,7 +1387,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Enable head movement while walking to avoid anti-cheat triggers. 的當地語系化字串。 + /// Looks up a localized string similar to Enable head movement while walking to avoid anti-cheat triggers.. /// internal static string Main_Advanced_move_head_while_walking { get { @@ -1396,7 +1396,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 A movement speed higher than 2 may be considered cheating. 的當地語系化字串。 + /// Looks up a localized string similar to A movement speed higher than 2 may be considered cheating.. /// internal static string Main_Advanced_movement_speed { get { @@ -1405,7 +1405,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Only works on Windows XP-8 or Windows 10 with old console. 的當地語系化字串。 + /// Looks up a localized string similar to Only works on Windows XP-8 or Windows 10 with old console.. /// internal static string Main_Advanced_player_head_icon { get { @@ -1414,7 +1414,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 For remote control of the bot. 的當地語系化字串。 + /// Looks up a localized string similar to For remote control of the bot.. /// internal static string Main_Advanced_private_msgs_cmd_name { get { @@ -1423,7 +1423,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How to retain profile key. Use "none", "memory" or "disk". 的當地語系化字串。 + /// Looks up a localized string similar to How to retain profile key. Use "none", "memory" or "disk".. /// internal static string Main_Advanced_profilekey_cache { get { @@ -1432,7 +1432,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "no", "fast" (5s timeout), or "yes". Required for joining some servers. 的當地語系化字串。 + /// Looks up a localized string similar to Use "no", "fast" (5s timeout), or "yes". Required for joining some servers.. /// internal static string Main_Advanced_resolve_srv_records { get { @@ -1441,7 +1441,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Cache compiled scripts for faster load on low-end devices. 的當地語系化字串。 + /// Looks up a localized string similar to Cache compiled scripts for faster load on low-end devices.. /// internal static string Main_Advanced_script_cache { get { @@ -1450,9 +1450,9 @@ namespace MinecraftClient { } /// - /// 查詢類似 ServerList: It allows an easier and faster server switching with short aliases instead of full server IP + /// Looks up a localized string similar to ServerList: It allows an easier and faster server switching with short aliases instead of full server IP ///Aliases cannot contain dots or spaces, and the name "localhost" cannot be used as an alias. - ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2" 的當地語系化字串。 + ///Usage examples: "/tell <mybot> connect Server1", "/connect Server2". /// internal static string Main_Advanced_server_list { get { @@ -1461,7 +1461,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 How to retain session tokens. Use "none", "memory" or "disk". 的當地語系化字串。 + /// Looks up a localized string similar to How to retain session tokens. Use "none", "memory" or "disk".. /// internal static string Main_Advanced_session_cache { get { @@ -1470,7 +1470,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Decode links embedded in chat messages and show them in console. 的當地語系化字串。 + /// Looks up a localized string similar to Decode links embedded in chat messages and show them in console.. /// internal static string Main_Advanced_show_chat_links { get { @@ -1479,7 +1479,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Show inventory layout as ASCII art in inventory command. 的當地語系化字串。 + /// Looks up a localized string similar to Show inventory layout as ASCII art in inventory command.. /// internal static string Main_Advanced_show_inventory_layout { get { @@ -1488,7 +1488,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 System messages for server ops. 的當地語系化字串。 + /// Looks up a localized string similar to System messages for server ops.. /// internal static string Main_Advanced_show_system_messages { get { @@ -1497,7 +1497,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Messages displayed above xp bar, set this to false in case of xp bar spam. 的當地語系化字串。 + /// Looks up a localized string similar to Messages displayed above xp bar, set this to false in case of xp bar spam.. /// internal static string Main_Advanced_show_xpbar_messages { get { @@ -1506,7 +1506,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Temporary fix for Badpacket issue on some servers. 的當地語系化字串。 + /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers.. /// internal static string Main_Advanced_temporary_fix_badpacket { get { @@ -1515,7 +1515,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log. 的當地語系化字串。 + /// Looks up a localized string similar to Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log.. /// internal static string Main_Advanced_TerminalColorDepth { get { @@ -1524,7 +1524,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Uses more ram, cpu, bandwidth but allows you to move around. 的當地語系化字串。 + /// Looks up a localized string similar to Uses more ram, cpu, bandwidth but allows you to move around.. /// internal static string Main_Advanced_terrain_and_movements { get { @@ -1533,7 +1533,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Customize the TCP connection timeout with the server. (in seconds) 的當地語系化字串。 + /// Looks up a localized string similar to Customize the TCP connection timeout with the server. (in seconds). /// internal static string Main_Advanced_timeout { get { @@ -1542,7 +1542,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Prepend timestamps to chat messages. 的當地語系化字串。 + /// Looks up a localized string similar to Prepend timestamps to chat messages.. /// internal static string Main_Advanced_timestamps { get { @@ -1551,7 +1551,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup. 的當地語系化字串。 + /// Looks up a localized string similar to Login=Email or Name. Use "-" as password for offline mode. Leave blank to prompt user on startup.. /// internal static string Main_General_account { get { @@ -1560,7 +1560,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 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) 的當地語系化字串。 + /// Looks up a localized string similar to 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). /// internal static string Main_General_login { get { @@ -1569,7 +1569,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once. 的當地語系化字串。 + /// Looks up a localized string similar to Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once.. /// internal static string Main_General_method { get { @@ -1578,7 +1578,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Account type: "mojang" OR "microsoft". Also affects interactive login in console. 的當地語系化字串。 + /// Looks up a localized string similar to Account type: "mojang" OR "microsoft". Also affects interactive login in console.. /// internal static string Main_General_server_info { get { @@ -1587,7 +1587,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Settings below are sent to the server and only affect server-side things like your skin. 的當地語系化字串。 + /// Looks up a localized string similar to Settings below are sent to the server and only affect server-side things like your skin.. /// internal static string MCSettings { get { @@ -1596,7 +1596,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Allows disabling chat colors server-side. 的當地語系化字串。 + /// Looks up a localized string similar to Allows disabling chat colors server-side.. /// internal static string MCSettings_ChatColors { get { @@ -1605,7 +1605,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use "enabled", "commands", or "disabled". Allows to mute yourself... 的當地語系化字串。 + /// Looks up a localized string similar to Use "enabled", "commands", or "disabled". Allows to mute yourself.... /// internal static string MCSettings_ChatMode { get { @@ -1614,7 +1614,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult". 的當地語系化字串。 + /// Looks up a localized string similar to MC 1.7- difficulty. "peaceful", "easy", "normal", "difficult".. /// internal static string MCSettings_Difficulty { get { @@ -1623,7 +1623,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 If disabled, settings below are not sent to the server. 的當地語系化字串。 + /// Looks up a localized string similar to If disabled, settings below are not sent to the server.. /// internal static string MCSettings_Enabled { get { @@ -1632,7 +1632,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use any language implemented in Minecraft. 的當地語系化字串。 + /// Looks up a localized string similar to Use any language implemented in Minecraft.. /// internal static string MCSettings_Locale { get { @@ -1641,7 +1641,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 MC 1.9+ main hand. "left" or "right". 的當地語系化字串。 + /// Looks up a localized string similar to MC 1.9+ main hand. "left" or "right".. /// internal static string MCSettings_MainHand { get { @@ -1650,7 +1650,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Value range: [0 - 255]. 的當地語系化字串。 + /// Looks up a localized string similar to Value range: [0 - 255].. /// internal static string MCSettings_RenderDistance { get { @@ -1659,10 +1659,10 @@ namespace MinecraftClient { } /// - /// 查詢類似 Connect to a server via a proxy instead of connecting directly + /// Looks up a localized string similar to Connect to a server via a proxy instead of connecting directly ///If Mojang session services are blocked on your network, set Enabled_Login=true to login using proxy. ///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. - ////!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences! 的當地語系化字串。 + ////!\ Make sure your server rules allow Proxies or VPNs before setting enabled=true, or you may face consequences!. /// internal static string Proxy { get { @@ -1671,7 +1671,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to connect to the game server through a proxy. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to connect to the game server through a proxy.. /// internal static string Proxy_Enabled_Ingame { get { @@ -1680,7 +1680,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to connect to the login server through a proxy. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to connect to the login server through a proxy.. /// internal static string Proxy_Enabled_Login { get { @@ -1689,7 +1689,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to download MCC updates via proxy. 的當地語系化字串。 + /// Looks up a localized string similar to Whether to download MCC updates via proxy.. /// internal static string Proxy_Enabled_Update { get { @@ -1698,7 +1698,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Only required for password-protected proxies. 的當地語系化字串。 + /// Looks up a localized string similar to Only required for password-protected proxies.. /// internal static string Proxy_Password { get { @@ -1707,7 +1707,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5". 的當地語系化字串。 + /// Looks up a localized string similar to Supported types: "HTTP", "SOCKS4", "SOCKS4a", "SOCKS5".. /// internal static string Proxy_Proxy_Type { get { @@ -1716,7 +1716,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Proxy server must allow HTTPS for login, and non-443 ports for playing. 的當地語系化字串。 + /// Looks up a localized string similar to Proxy server must allow HTTPS for login, and non-443 ports for playing.. /// internal static string Proxy_Server { get { @@ -1725,7 +1725,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Only required for password-protected proxies. 的當地語系化字串。 + /// Looks up a localized string similar to Only required for password-protected proxies.. /// internal static string Proxy_Username { get { @@ -1734,7 +1734,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Chat signature related settings (affects minecraft 1.19+) 的當地語系化字串。 + /// Looks up a localized string similar to Chat signature related settings (affects minecraft 1.19+). /// internal static string Signature { get { @@ -1743,7 +1743,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true" 的當地語系化字串。 + /// Looks up a localized string similar to Microsoft accounts only. If disabled, will not be able to sign chat and join servers configured with "enforce-secure-profile=true". /// internal static string Signature_LoginWithSecureProfile { get { @@ -1752,7 +1752,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use red    color block to mark chat without legitimate signature 的當地語系化字串。 + /// Looks up a localized string similar to Use red    color block to mark chat without legitimate signature. /// internal static string Signature_MarkIllegallySignedMsg { get { @@ -1761,7 +1761,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use green  color block to mark chat with legitimate signatures 的當地語系化字串。 + /// Looks up a localized string similar to Use green  color block to mark chat with legitimate signatures. /// internal static string Signature_MarkLegallySignedMsg { get { @@ -1770,7 +1770,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use yellow color block to mark chat that have been modified by the server. 的當地語系化字串。 + /// Looks up a localized string similar to Use yellow color block to mark chat that have been modified by the server.. /// internal static string Signature_MarkModifiedMsg { get { @@ -1779,7 +1779,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Use gray   color block to mark system message (always without signature) 的當地語系化字串。 + /// Looks up a localized string similar to Use gray   color block to mark system message (always without signature). /// internal static string Signature_MarkSystemMessage { get { @@ -1788,7 +1788,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to display chat and messages in commands without legal signatures 的當地語系化字串。 + /// Looks up a localized string similar to Whether to display chat and messages in commands without legal signatures. /// internal static string Signature_ShowIllegalSignedChat { get { @@ -1797,7 +1797,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Set to true to display messages modified by the server, false to display the original signed messages 的當地語系化字串。 + /// Looks up a localized string similar to Set to true to display messages modified by the server, false to display the original signed messages. /// internal static string Signature_ShowModifiedChat { get { @@ -1806,7 +1806,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to sign the chat send from MCC 的當地語系化字串。 + /// Looks up a localized string similar to Whether to sign the chat send from MCC. /// internal static string Signature_SignChat { get { @@ -1815,7 +1815,7 @@ namespace MinecraftClient { } /// - /// 查詢類似 Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me" 的當地語系化字串。 + /// Looks up a localized string similar to Whether to sign the messages contained in the commands sent by MCC. For example, the message in "/msg" and "/me". /// internal static string Signature_SignMessageInCommand { get { From 657fc6117bccf7943878c6a88774bd2af345c60d Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Tue, 10 Jan 2023 18:27:48 +0800 Subject: [PATCH 17/44] Upgrade protocol to 1.19.3 --- MinecraftClient/Program.cs | 6 +- .../PacketPalettes/PacketPalette1193.cs | 56 +++++++++---------- .../Protocol/Handlers/PacketType18Handler.cs | 6 +- MinecraftClient/Protocol/ProtocolHandler.cs | 5 +- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 66ebde33..76f3e9b2 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -44,7 +44,7 @@ namespace MinecraftClient public const string Version = MCHighestVersion; public const string MCLowestVersion = "1.4.6"; - public const string MCHighestVersion = "1.19.2"; + public const string MCHighestVersion = "1.19.3"; public static readonly string? BuildInfo = null; private static Tuple? offlinePrompt = null; @@ -621,6 +621,10 @@ namespace MinecraftClient { HandleFailure(Translations.error_unsupported, true); } + catch (NotImplementedException) + { + throw; + } catch (Exception) { } } else HandleFailure(Translations.error_determine, true); diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs index 6b550948..e1cae885 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette1193.cs @@ -30,7 +30,7 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x15, PacketTypesIn.PluginMessage }, // (Wiki name: Plugin Message (clientbound)) { 0x16, PacketTypesIn.HideMessage }, // Added in 1.19.1 { 0x17, PacketTypesIn.Disconnect }, // - { 0x18, PacketTypesIn.ProfilelessChatMessage }, // Added in 1.19.3 - TODO + { 0x18, PacketTypesIn.ProfilelessChatMessage }, // Added in 1.19.3 (Wiki name: Disguised Chat Message) { 0x19, PacketTypesIn.EntityStatus }, // (Wiki name: Entity Event) { 0x1A, PacketTypesIn.Explosion }, // Changed in 1.19 (Location fields are now Double instead of Float) (Wiki name: Explosion) { 0x1B, PacketTypesIn.UnloadChunk }, // (Wiki name: Forget Chunk) @@ -123,33 +123,33 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x03, PacketTypesOut.MessageAcknowledgment }, // Added in 1.19.1 { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 { 0x05, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) - { 0x06, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound)) - { 0x07, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) - { 0x08, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) - { 0x09, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) - { 0x0A, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button) - { 0x0B, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container) - { 0x0C, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound)) - { 0x0D, PacketTypesOut.PluginMessage }, // (Wiki name: Plugin Message (serverbound)) - { 0x0E, PacketTypesOut.EditBook }, // - { 0x0F, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag) - { 0x10, PacketTypesOut.InteractEntity }, // (Wiki name: Interact) - { 0x11, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate) - { 0x12, PacketTypesOut.KeepAlive }, // - { 0x13, PacketTypesOut.LockDifficulty }, // - { 0x14, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position) - { 0x15, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation) - { 0x16, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation) - { 0x17, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground) - { 0x18, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound)) - { 0x19, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat) - { 0x1A, PacketTypesOut.PickItem }, // - { 0x1B, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) - { 0x1C, PacketTypesOut.PlayerAbilities }, // - { 0x1D, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) - { 0x1E, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) - { 0x1F, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) - { 0x20, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) + { 0x06, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) + { 0x07, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) + { 0x08, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) + { 0x09, PacketTypesOut.ClickWindowButton }, // (Wiki name: Click Container Button) + { 0x0A, PacketTypesOut.ClickWindow }, // (Wiki name: Click Container) + { 0x0B, PacketTypesOut.CloseWindow }, // (Wiki name: Close Container (serverbound)) + { 0x0C, PacketTypesOut.PluginMessage }, // (Wiki name: Plugin Message (serverbound)) + { 0x0D, PacketTypesOut.EditBook }, // + { 0x0E, PacketTypesOut.EntityNBTRequest }, // (Wiki name: Query Entity Tag) + { 0x0F, PacketTypesOut.InteractEntity }, // (Wiki name: Interact) + { 0x10, PacketTypesOut.GenerateStructure }, // (Wiki name: Jigsaw Generate) + { 0x11, PacketTypesOut.KeepAlive }, // + { 0x12, PacketTypesOut.LockDifficulty }, // + { 0x13, PacketTypesOut.PlayerPosition }, // (Wiki name: Move Player Position) + { 0x14, PacketTypesOut.PlayerPositionAndRotation }, // (Wiki name: Set Player Position and Rotation) + { 0x15, PacketTypesOut.PlayerRotation }, // (Wiki name: Set Player Rotation) + { 0x16, PacketTypesOut.PlayerMovement }, // (Wiki name: Set Player On Ground) + { 0x17, PacketTypesOut.VehicleMove }, // (Wiki name: Move Vehicle (serverbound)) + { 0x18, PacketTypesOut.SteerBoat }, // (Wiki name: Paddle Boat) + { 0x19, PacketTypesOut.PickItem }, // + { 0x1A, PacketTypesOut.CraftRecipeRequest }, // (Wiki name: Place recipe) + { 0x1B, PacketTypesOut.PlayerAbilities }, // + { 0x1C, PacketTypesOut.PlayerDigging }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Player Action) + { 0x1D, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) + { 0x1E, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) + { 0x1F, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) + { 0x20, PacketTypesOut.PlayerSession }, // Added in 1.19.3 { 0x21, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) { 0x22, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) { 0x23, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) diff --git a/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs b/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs index 20e4ce02..110a1efc 100644 --- a/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs +++ b/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs @@ -47,7 +47,7 @@ namespace MinecraftClient.Protocol.Handlers public PacketTypePalette GetTypeHandler(int protocol) { PacketTypePalette p; - if (protocol > Protocol18Handler.MC_1_19_2_Version) + if (protocol > Protocol18Handler.MC_1_19_3_Version) throw new NotImplementedException(Translations.exception_palette_packet); if (protocol <= Protocol18Handler.MC_1_8_Version) @@ -74,8 +74,10 @@ namespace MinecraftClient.Protocol.Handlers p = new PacketPalette118(); else if (protocol <= Protocol18Handler.MC_1_19_Version) p = new PacketPalette119(); - else + else if (protocol <= Protocol18Handler.MC_1_19_2_Version) p = new PacketPalette1192(); + else + p = new PacketPalette1193(); p.SetForgeEnabled(forgeEnabled); return p; diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index 3328faf0..345fe2a8 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -134,7 +134,7 @@ namespace MinecraftClient.Protocol if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1) return new Protocol16Handler(Client, ProtocolVersion, Handler); - int[] supportedVersions_Protocol18 = { 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760 }; + int[] supportedVersions_Protocol18 = { 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760, 761 }; if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1) return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo); @@ -313,6 +313,8 @@ namespace MinecraftClient.Protocol case "1.19.1": case "1.19.2": return 760; + case "1.19.3": + return 761; default: return 0; } @@ -389,6 +391,7 @@ namespace MinecraftClient.Protocol case 758: return "1.18.2"; case 759: return "1.19"; case 760: return "1.19.2"; + case 761: return "1.19.3"; default: return "0.0"; } } From 08da49756f069f6346f9d6f82e7142bfdfa29e05 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Tue, 10 Jan 2023 20:43:54 +0800 Subject: [PATCH 18/44] Done receive message --- .../Protocol/Handlers/DataTypes.cs | 12 ++++++ .../Protocol/Handlers/Protocol18.cs | 38 ++++++++++++++----- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index aac22859..dc439018 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -240,6 +240,18 @@ namespace MinecraftClient.Protocol.Handlers return ReadData(len, cache); } + /// + /// Read a byte array with given length from a cache of bytes and remove it from the cache + /// + /// Cache of bytes to read from + /// Length of the bytes array + /// The byte array + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public byte[] ReadNextByteArray(Queue cache, int length) + { + return ReadData(length, cache); + } + /// /// Reads a length-prefixed array of unsigned long integers and removes it from the cache /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index eecd7ca1..dffb9bbc 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -522,12 +522,14 @@ namespace MinecraftClient.Protocol.Handlers } else // 1.19.1 + { + // 1.19.3+ if (protocolVersion >= MC_1_19_3_Version) { // Header section Guid senderUUID = dataTypes.ReadNextUUID(packetData); int index = dataTypes.ReadNextVarInt(packetData); - byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + // Signature is fixed size of 256 bytes + byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; // Body string message = dataTypes.ReadNextString(packetData); @@ -541,8 +543,8 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < totalPreviousMessages; i++) { int messageId = dataTypes.ReadNextVarInt(packetData); - if (messageId > 0) - previousMessageSignatures.Add(new Tuple(messageId, dataTypes.ReadNextByteArray(packetData))); + if (messageId == 0) // from botcraft implementation. Only read if id is 0. Byte array is fixed size of 256 bytes + previousMessageSignatures.Add(new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256))); } // Other @@ -579,10 +581,13 @@ namespace MinecraftClient.Protocol.Handlers } } - // TODO + // TODO: Verify the message + ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, false); + handler.OnTextReceived(chat); } else { + // 1.19.1 - 1.19.2 byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; Guid senderUUID = dataTypes.ReadNextUUID(packetData); byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); @@ -2483,7 +2488,8 @@ namespace MinecraftClient.Protocol.Handlers if (!isOnlineMode || playerKeyPair == null || !Config.Signature.LoginWithSecureProfile || !Config.Signature.SignChat) { fields.AddRange(dataTypes.GetLong(0)); // Salt: Long - fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt + if (protocolVersion < MC_1_19_3_Version) + fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) } else { @@ -2493,21 +2499,33 @@ namespace MinecraftClient.Protocol.Handlers // Signature Length & Signature: (VarInt) and Byte Array Guid uuid = handler.GetUserUuid(); - byte[] sign = (protocolVersion >= MC_1_19_2_Version) ? - playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen) : - playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - fields.AddRange(dataTypes.GetVarInt(sign.Length)); + byte[] sign; + if (protocolVersion < MC_1_19_2_Version) // 1.19.1 or lower + sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); + else if (protocolVersion < MC_1_19_3_Version) // 1.19.2 + sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen); + else // 1.19.3 + sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); // TODO? + + if (protocolVersion < MC_1_19_3_Version) + fields.AddRange(dataTypes.GetVarInt(sign.Length)); + else + fields.AddRange(dataTypes.GetBool(true)); fields.AddRange(sign); } // Signed Preview: Boolean - fields.AddRange(dataTypes.GetBool(false)); + if (protocolVersion < MC_1_19_3_Version) + fields.AddRange(dataTypes.GetBool(false)); + else + fields.AddRange(dataTypes.GetVarInt(1)); // message count if (protocolVersion >= MC_1_19_2_Version) { // Message Acknowledgment fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); } + } SendPacket(PacketTypesOut.ChatMessage, fields); return true; From 6f87710232cd71bcaafd37a4e33823924254a599 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Wed, 11 Jan 2023 13:08:37 +0800 Subject: [PATCH 19/44] AutoAttack: Range check on setting change --- .gitignore | 1 + MinecraftClient/ChatBots/AutoAttack.cs | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 45f6cef2..22033cc8 100644 --- a/.gitignore +++ b/.gitignore @@ -409,6 +409,7 @@ FodyWeavers.xsd # translations /MinecraftClient/Resources/Translations/Translations.*.resx /MinecraftClient/Resources/AsciiArt/AsciiArt.*.resx +/MinecraftClient/Resources/ConfigComments/ConfigComments.*.resx /docs/.vuepress/translations/*.json !/docs/.vuepress/translations/en.json diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 526ba052..0211c0ea 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -33,7 +33,7 @@ namespace MinecraftClient.ChatBots public InteractType Interaction = InteractType.Attack; [TomlInlineComment("$ChatBot.AutoAttack.Attack_Range$")] - public int Attack_Range = 4; + public double Attack_Range = 4.0; [TomlInlineComment("$ChatBot.AutoAttack.Attack_Hostile$")] public bool Attack_Hostile = true; @@ -54,6 +54,12 @@ namespace MinecraftClient.ChatBots LogToConsole(BotName, Translations.bot_autoAttack_invalidcooldown); Cooldown_Time.value = 1.0; } + + if (Attack_Range < 1.0) + Attack_Range = 1.0; + + if (Attack_Range > 4.0) + Attack_Range = 4.0; } public enum AttackMode { single, multi }; @@ -111,8 +117,6 @@ namespace MinecraftClient.ChatBots attackHostile = Config.Attack_Hostile; attackPassive = Config.Attack_Passive; attackRange = Config.Attack_Range; - if (attackRange < 1) attackRange = 1; - if (attackRange > 4) attackRange = 4; } public override void Initialize() From 4be7a05006cfafbe9449468a204cd923d8242670 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Wed, 11 Jan 2023 17:25:25 +0800 Subject: [PATCH 20/44] Player session --- MinecraftClient/McClient.cs | 10 +++ .../Protocol/Handlers/Protocol16.cs | 5 ++ .../Protocol/Handlers/Protocol18.cs | 63 ++++++++++++++++--- MinecraftClient/Protocol/IMinecraftCom.cs | 6 ++ .../Protocol/IMinecraftComHandler.cs | 2 + .../Protocol/ProfileKey/KeyUtils.cs | 9 +++ 6 files changed, 87 insertions(+), 8 deletions(-) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index de92a500..9d49ae59 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -9,6 +9,7 @@ using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; using MinecraftClient.Protocol; +using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; @@ -1127,6 +1128,11 @@ namespace MinecraftClient } } + public PlayerKeyPair? GetPlayerKeyPair() + { + return playerKeyPair; + } + #endregion #region Action methods: Perform an action on the Server @@ -2402,6 +2408,10 @@ namespace MinecraftClient Config.MCSettings.Skin.GetByte(), (byte)Config.MCSettings.MainHand); + if (protocolversion >= Protocol18Handler.MC_1_19_3_Version + && playerKeyPair != null) + handler.SendPlayerSession(playerKeyPair); + if (inventoryHandlingRequested) { diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 6ca5a609..aba5c555 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -909,5 +909,10 @@ namespace MinecraftClient.Protocol.Handlers { return false; //Currently not implemented } + + public bool SendPlayerSession(PlayerKeyPair? playerKeyPair) + { + return false; + } } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index dffb9bbc..937e6c22 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -2490,6 +2490,8 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(dataTypes.GetLong(0)); // Salt: Long if (protocolVersion < MC_1_19_3_Version) fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) + else + fields.AddRange(dataTypes.GetBool(false)); // Has signature: bool (1.19.3) } else { @@ -2507,23 +2509,34 @@ namespace MinecraftClient.Protocol.Handlers else // 1.19.3 sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); // TODO? - if (protocolVersion < MC_1_19_3_Version) - fields.AddRange(dataTypes.GetVarInt(sign.Length)); - else + if (protocolVersion >= MC_1_19_3_Version) fields.AddRange(dataTypes.GetBool(true)); + else + fields.AddRange(dataTypes.GetVarInt(sign.Length)); fields.AddRange(sign); } // Signed Preview: Boolean - if (protocolVersion < MC_1_19_3_Version) - fields.AddRange(dataTypes.GetBool(false)); - else + if (protocolVersion >= MC_1_19_3_Version) fields.AddRange(dataTypes.GetVarInt(1)); // message count + else + fields.AddRange(dataTypes.GetBool(false)); if (protocolVersion >= MC_1_19_2_Version) { - // Message Acknowledgment - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); + if (protocolVersion >= MC_1_19_3_Version) + { + // 1.19.3 + // Acknowledged: BitSet (no idea what is this) + //fields.AddRange(dataTypes.GetVarInt(0)); + fields.AddRange(new byte[3] {0,0,0 }); + } + else + { + // 1.19.2 + // Message Acknowledgment + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); + } } } @@ -3175,6 +3188,40 @@ namespace MinecraftClient.Protocol.Handlers else { return false; } } + public bool SendPlayerSession(PlayerKeyPair? playerKeyPair) + { + if (protocolVersion >= MC_1_19_3_Version) + { + try + { + List packet = new(); + + var uuid = Guid.NewGuid(); + + byte[] timestampByte = BitConverter.GetBytes(playerKeyPair.GetExpirationMilliseconds()); + Array.Reverse(timestampByte); + var signature = KeyUtils.ComputeHash(timestampByte.Concat(playerKeyPair.PublicKey.Key).ToArray()); + + packet.AddRange(dataTypes.GetUUID(uuid)); + packet.AddRange(dataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); + packet.AddRange(dataTypes.GetVarInt(playerKeyPair.PublicKey.Key.Length)); + packet.AddRange(playerKeyPair.PublicKey.Key); + //packet.AddRange(dataTypes.GetVarInt(signature.Length)); + //packet.AddRange(signature); + packet.AddRange(dataTypes.GetVarInt(playerKeyPair.PublicKey.SignatureV2.Length)); + packet.AddRange(playerKeyPair.PublicKey.SignatureV2); + + SendPacket(PacketTypesOut.PlayerSession, packet); + + return true; + } + catch (SocketException) { return false; } + catch (System.IO.IOException) { return false; } + catch (ObjectDisposedException) { return false; } + } + else { return false; } + } + private byte[] GenerateSalt() { byte[] salt = new byte[8]; diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index babd004e..44468e9b 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -255,6 +255,12 @@ namespace MinecraftClient.Protocol /// The uuid of the player/entity to spectate/teleport to. bool SendSpectate(Guid uuid); + /// + /// Send player session + /// + /// + bool SendPlayerSession(PlayerKeyPair? playerKeyPair); + /// /// Get net read thread (main thread) ID /// diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index eab385c3..59823062 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; +using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; namespace MinecraftClient.Protocol @@ -27,6 +28,7 @@ namespace MinecraftClient.Protocol string[] GetOnlinePlayers(); Dictionary GetOnlinePlayersWithUUID(); PlayerInfo? GetPlayerInfo(Guid uuid); + PlayerKeyPair? GetPlayerKeyPair(); Location GetCurrentLocation(); World GetWorld(); bool GetIsSupportPreviewsChat(); diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 94cce827..12889fac 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -124,6 +124,15 @@ namespace MinecraftClient.Protocol.Keys return data.ToArray(); } + public static byte[] GetSignatureData(string message, DateTimeOffset timestamp, ref byte[] salt, Guid sender, Guid sessionUuid) + { + List data = new(); + + // TODO! + + return data.ToArray(); + } + // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs public static string EscapeString(string src) { From e447fea16be440a851c3cf6926680bab4704eacc Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Fri, 13 Jan 2023 15:53:33 +0800 Subject: [PATCH 21/44] Send and receive message without message signing --- .../Protocol/Handlers/Protocol18.cs | 76 +++++++++++++++---- .../Protocol/ProfileKey/KeyUtils.cs | 28 ++++++- .../Protocol/ProfileKey/PrivateKey.cs | 5 ++ 3 files changed, 93 insertions(+), 16 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 937e6c22..6030e81e 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -79,6 +79,8 @@ namespace MinecraftClient.Protocol.Handlers private int pendingAcknowledgments = 0; private readonly LastSeenMessagesCollector lastSeenMessagesCollector = new(5); private LastSeenMessageList.Entry? lastReceivedMessage = null; + private Guid playerSessionUuid = Guid.NewGuid(); + private int messageCount = 0; readonly Protocol18Forge pForge; readonly Protocol18Terrain pTerrain; readonly IMinecraftComHandler handler; @@ -2437,14 +2439,27 @@ namespace MinecraftClient.Protocol.Handlers } } - // Signed Preview: Boolean - fields.AddRange(dataTypes.GetBool(false)); - - if (protocolVersion >= MC_1_19_2_Version) + if (protocolVersion < MC_1_19_3_Version) { - // Message Acknowledgment + // Signed Preview: Boolean (1.19.2) + fields.AddRange(dataTypes.GetBool(false)); + } + else + { + fields.AddRange(dataTypes.GetVarInt(messageCount)); // Message count (1.19.3) + } + + if (protocolVersion == MC_1_19_2_Version) + { + // Message Acknowledgment (1.19.2) fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); } + else if (protocolVersion >= MC_1_19_3_Version) + { + // 1.19.3 + // Acknowledged: BitSet (no idea what is this) + fields.AddRange(new byte[3] { 0, 0, 0 }); + } SendPacket(PacketTypesOut.ChatCommand, fields); return true; @@ -2502,12 +2517,21 @@ namespace MinecraftClient.Protocol.Handlers // Signature Length & Signature: (VarInt) and Byte Array Guid uuid = handler.GetUserUuid(); byte[] sign; - if (protocolVersion < MC_1_19_2_Version) // 1.19.1 or lower + if (protocolVersion < MC_1_19_2_Version) + { + // 1.19.1 or lower sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - else if (protocolVersion < MC_1_19_3_Version) // 1.19.2 + } + else if (protocolVersion < MC_1_19_3_Version) + { + // 1.19.2 sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen); - else // 1.19.3 - sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); // TODO? + } + else + { + // 1.19.3 + sign = playerKeyPair.PrivateKey.SignMessage(message, timeNow, ref salt, messageCount, uuid, playerSessionUuid); + } if (protocolVersion >= MC_1_19_3_Version) fields.AddRange(dataTypes.GetBool(true)); @@ -2516,11 +2540,14 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(sign); } - // Signed Preview: Boolean + if (protocolVersion >= MC_1_19_3_Version) - fields.AddRange(dataTypes.GetVarInt(1)); // message count + fields.AddRange(dataTypes.GetVarInt(messageCount)); // Message count (1.19.3) else - fields.AddRange(dataTypes.GetBool(false)); + fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean (1.19.2) + + //if (protocolVersion >= MC_1_19_3_Version) + // messageCount++; if (protocolVersion >= MC_1_19_2_Version) { @@ -3196,13 +3223,11 @@ namespace MinecraftClient.Protocol.Handlers { List packet = new(); - var uuid = Guid.NewGuid(); - byte[] timestampByte = BitConverter.GetBytes(playerKeyPair.GetExpirationMilliseconds()); Array.Reverse(timestampByte); var signature = KeyUtils.ComputeHash(timestampByte.Concat(playerKeyPair.PublicKey.Key).ToArray()); - packet.AddRange(dataTypes.GetUUID(uuid)); + packet.AddRange(dataTypes.GetUUID(playerSessionUuid)); packet.AddRange(dataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); packet.AddRange(dataTypes.GetVarInt(playerKeyPair.PublicKey.Key.Length)); packet.AddRange(playerKeyPair.PublicKey.Key); @@ -3222,6 +3247,27 @@ namespace MinecraftClient.Protocol.Handlers else { return false; } } + public bool SendMessageAcknowledgment(int messageCount) + { + if (protocolVersion >= MC_1_19_3_Version) + { + try + { + List packet = new(); + + packet.AddRange(dataTypes.GetVarInt(messageCount)); + + SendPacket(PacketTypesOut.MessageAcknowledgment, packet); + + return true; + } + catch (SocketException) { return false; } + catch (System.IO.IOException) { return false; } + catch (ObjectDisposedException) { return false; } + } + else { return false; } + } + private byte[] GenerateSalt() { byte[] salt = new byte[8]; diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 12889fac..b7f31c77 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Security.Cryptography; using System.Text; +using ImageMagick; using MinecraftClient.Protocol.Message; namespace MinecraftClient.Protocol.Keys @@ -124,11 +125,36 @@ namespace MinecraftClient.Protocol.Keys return data.ToArray(); } - public static byte[] GetSignatureData(string message, DateTimeOffset timestamp, ref byte[] salt, Guid sender, Guid sessionUuid) + public static byte[] GetSignatureData(string message, DateTimeOffset timestamp, ref byte[] salt, int messageCount, Guid sender, Guid sessionUuid) { List data = new(); // TODO! + byte[] unknownInt1 = BitConverter.GetBytes(1); + Array.Reverse(unknownInt1); + data.AddRange(unknownInt1); + + data.AddRange(sender.ToBigEndianBytes()); + data.AddRange(sessionUuid.ToBigEndianBytes()); + + byte[] msgCountByte = BitConverter.GetBytes(messageCount); + Array.Reverse(msgCountByte); + data.AddRange(msgCountByte); + data.AddRange(salt); + + byte[] timestampByte = BitConverter.GetBytes(timestamp.ToUnixTimeSeconds()); + Array.Reverse(timestampByte); + data.AddRange(timestampByte); + + byte[] msgByte = Encoding.UTF8.GetBytes(message); + byte[] msgLengthByte = BitConverter.GetBytes(msgByte.Length); + Array.Reverse(msgLengthByte); + data.AddRange(msgLengthByte); + data.AddRange(msgByte); + + byte[] unknownInt2 = BitConverter.GetBytes(0); + Array.Reverse(unknownInt2); + data.AddRange(unknownInt2); return data.ToArray(); } diff --git a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs index b3dd4093..65c569c6 100644 --- a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs @@ -64,5 +64,10 @@ namespace MinecraftClient.Protocol.Keys return msgSign; } + public byte[] SignMessage(string message, DateTimeOffset timestamp, ref byte[] salt, int messageCount, Guid sender, Guid sessionUuid) + { + byte[] data = KeyUtils.GetSignatureData(message, timestamp, ref salt, messageCount, sender, sessionUuid); + return SignData(data); + } } } From fe0b268878ca06ca50a54f684f97acbda1166f9a Mon Sep 17 00:00:00 2001 From: BruceChen Date: Fri, 13 Jan 2023 16:12:10 +0800 Subject: [PATCH 22/44] 1.19.3 Message signing support. --- .gitignore | 1 + .../Protocol/Handlers/DataTypes.cs | 30 +- .../Protocol/Handlers/Protocol18.cs | 590 ++++++++++-------- .../Protocol/Handlers/Protocol18Forge.cs | 16 +- .../Protocol/Message/ChatMessage.cs | 4 +- .../Protocol/Message/LastSeenMessageList.cs | 102 ++- MinecraftClient/Protocol/PlayerInfo.cs | 30 +- .../Protocol/ProfileKey/KeyUtils.cs | 30 +- .../Protocol/ProfileKey/PrivateKey.cs | 20 +- MinecraftClient/Protocol/ReplayHandler.cs | 4 +- .../ConfigComments/ConfigComments.Designer.cs | 2 +- .../ConfigComments/ConfigComments.resx | 2 +- MinecraftClient/Settings.cs | 14 +- 13 files changed, 532 insertions(+), 313 deletions(-) diff --git a/.gitignore b/.gitignore index 45f6cef2..22033cc8 100644 --- a/.gitignore +++ b/.gitignore @@ -409,6 +409,7 @@ FodyWeavers.xsd # translations /MinecraftClient/Resources/Translations/Translations.*.resx /MinecraftClient/Resources/AsciiArt/AsciiArt.*.resx +/MinecraftClient/Resources/ConfigComments/ConfigComments.*.resx /docs/.vuepress/translations/*.json !/docs/.vuepress/translations/en.json diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index dc439018..71671c7c 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -935,7 +935,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// Integer to encode /// Byte array for this integer - public byte[] GetVarInt(int paramInt) + public static byte[] GetVarInt(int paramInt) { List bytes = new(); while ((paramInt & -128) != 0) @@ -966,7 +966,19 @@ namespace MinecraftClient.Protocol.Handlers /// /// Long to process /// Array ready to send - public byte[] GetLong(long number) + public static byte[] GetLong(long number) + { + byte[] theLong = BitConverter.GetBytes(number); + Array.Reverse(theLong); + return theLong; + } + + /// + /// Get byte array representing a long integer + /// + /// Long to process + /// Array ready to send + public static byte[] GetULong(ulong number) { byte[] theLong = BitConverter.GetBytes(number); Array.Reverse(theLong); @@ -978,7 +990,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// Integer to process /// Array ready to send - public byte[] GetInt(int number) + public static byte[] GetInt(int number) { byte[] theInt = BitConverter.GetBytes(number); Array.Reverse(theInt); @@ -1161,7 +1173,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// UUID of Player/Entity /// UUID representation - public byte[] GetUUID(Guid UUID) + public static byte[] GetUUID(Guid UUID) { return UUID.ToBigEndianBytes(); } @@ -1206,11 +1218,11 @@ namespace MinecraftClient.Protocol.Handlers { List fields = new(); fields.AddRange(GetVarInt(msgList.entries.Length)); // Message list size - foreach (Message.LastSeenMessageList.Entry entry in msgList.entries) + foreach (Message.LastSeenMessageList.AcknowledgedMessage entry in msgList.entries) { fields.AddRange(entry.profileId.ToBigEndianBytes()); // UUID - fields.AddRange(GetVarInt(entry.lastSignature.Length)); // Signature length - fields.AddRange(entry.lastSignature); // Signature data + fields.AddRange(GetVarInt(entry.signature.Length)); // Signature length + fields.AddRange(entry.signature); // Signature data } return fields.ToArray(); } @@ -1232,8 +1244,8 @@ namespace MinecraftClient.Protocol.Handlers { fields.AddRange(GetBool(true)); fields.AddRange(ack.lastReceived.profileId.ToBigEndianBytes()); // Has last received message - fields.AddRange(GetVarInt(ack.lastReceived.lastSignature.Length)); // Last received message signature length - fields.AddRange(ack.lastReceived.lastSignature); // Last received message signature data + fields.AddRange(GetVarInt(ack.lastReceived.signature.Length)); // Last received message signature length + fields.AddRange(ack.lastReceived.signature); // Last received message signature data } return fields.ToArray(); } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 6030e81e..5a6f0f5f 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -76,11 +76,10 @@ namespace MinecraftClient.Protocol.Handlers private readonly BlockingCollection>> packetQueue = new(); private float LastYaw, LastPitch; - private int pendingAcknowledgments = 0; - private readonly LastSeenMessagesCollector lastSeenMessagesCollector = new(5); - private LastSeenMessageList.Entry? lastReceivedMessage = null; - private Guid playerSessionUuid = Guid.NewGuid(); - private int messageCount = 0; + private Guid chatUuid = Guid.Empty; + private int pendingAcknowledgments = 0, messageIndex = 0; + private LastSeenMessagesCollector lastSeenMessagesCollector; + private LastSeenMessageList.AcknowledgedMessage? lastReceivedMessage = null; readonly Protocol18Forge pForge; readonly Protocol18Terrain pTerrain; readonly IMinecraftComHandler handler; @@ -107,6 +106,7 @@ namespace MinecraftClient.Protocol.Handlers packetPalette = new PacketTypeHandler(protocolVersion, forgeInfo != null).GetTypeHandler(); log = handler.GetLogger(); randomGen = RandomNumberGenerator.Create(); + lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5); if (handler.GetTerrainEnabled() && protocolVersion > MC_1_19_2_Version) { @@ -454,6 +454,10 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextLocation(packetData); // Death location } } + + if (protocolVersion >= MC_1_19_3_Version) + SendPlayerSession(handler.GetPlayerKeyPair()); + break; case PacketTypesIn.DeclareCommands: if (protocolVersion >= MC_1_19_Version) @@ -522,149 +526,148 @@ namespace MinecraftClient.Protocol.Handlers ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); handler.OnTextReceived(chat); } - else // 1.19.1 + + else if (protocolVersion == MC_1_19_2_Version) { - // 1.19.3+ - if (protocolVersion >= MC_1_19_3_Version) + // 1.19.1 - 1.19.2 + byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); + + string signedChat = dataTypes.ReadNextString(packetData); + string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); + + int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); + LastSeenMessageList.AcknowledgedMessage[] lastSeenMessageList = new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; + for (int i = 0; i < lastSeenMessageListLen; ++i) { - // Header section - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - int index = dataTypes.ReadNextVarInt(packetData); - // Signature is fixed size of 256 bytes - byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; - - // Body - string message = dataTypes.ReadNextString(packetData); - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - // Previous Messages - int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); - List> previousMessageSignatures = new(); - - for (int i = 0; i < totalPreviousMessages; i++) - { - int messageId = dataTypes.ReadNextVarInt(packetData); - if (messageId == 0) // from botcraft implementation. Only read if id is 0. Byte array is fixed size of 256 bytes - previousMessageSignatures.Add(new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256))); - } - - // Other - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - - if (filterType == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - // Network Target - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - Dictionary chatInfo = Json.ParseJson(chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - // TODO: Verify the message - ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, false); - handler.OnTextReceived(chat); + Guid user = dataTypes.ReadNextUUID(packetData); + byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); + lastSeenMessageList[i] = new(user, lastSignature, true); } + LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); + + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + if (filterEnum == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + Dictionary chatInfo = Json.ParseJson(chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; else { - // 1.19.1 - 1.19.2 - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - - string signedChat = dataTypes.ReadNextString(packetData); - string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); - LastSeenMessageList.Entry[] lastSeenMessageList = new LastSeenMessageList.Entry[lastSeenMessageListLen]; - for (int i = 0; i < lastSeenMessageListLen; ++i) - { - Guid user = dataTypes.ReadNextUUID(packetData); - byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); - lastSeenMessageList[i] = new(user, lastSignature); - } - LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); - - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - if (filterEnum == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - Dictionary chatInfo = Json.ParseJson(chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - bool verifyResult; - if (!isOnlineMode) + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; else { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); - } + bool lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); } - - ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender()) - Acknowledge(chat); - handler.OnTextReceived(chat); } + + ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender()) + Acknowledge(chat); + handler.OnTextReceived(chat); + } + else if (protocolVersion >= MC_1_19_3_Version) + { + // 1.19.3+ + // Header section + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + int index = dataTypes.ReadNextVarInt(packetData); + // Signature is fixed size of 256 bytes + byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; + + // Body + string message = dataTypes.ReadNextString(packetData); + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); + + // Previous Messages + int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); + List> previousMessageSignatures = new(); + + for (int i = 0; i < totalPreviousMessages; i++) + { + int messageId = dataTypes.ReadNextVarInt(packetData); + if (messageId == 0) // from botcraft implementation. Only read if id is 0. Byte array is fixed size of 256 bytes + previousMessageSignatures.Add(new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256))); + } + + // Other + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + + if (filterType == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + // Network Target + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + Dictionary chatInfo = Json.ParseJson(chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + // TODO: Verify the message + ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, false); + if (isOnlineMode && !chat.LacksSender() && messageSignature != null) + Acknowledge(chat); + handler.OnTextReceived(chat); } break; case PacketTypesIn.CombatEvent: @@ -819,7 +822,7 @@ namespace MinecraftClient.Protocol.Handlers handler.UpdateLocation(location, yaw, pitch); // Teleport confirm packet - SendPacket(PacketTypesOut.TeleportConfirm, dataTypes.GetVarInt(teleportID)); + SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportID)); if (Config.Main.Advanced.TemporaryFixBadpacket) { SendLocationUpdate(location, true, yaw, pitch, true); @@ -1291,7 +1294,69 @@ namespace MinecraftClient.Protocol.Handlers } break; case PacketTypesIn.PlayerInfo: - if (protocolVersion >= MC_1_8_Version) + if (protocolVersion >= MC_1_19_3_Version) + { + // int actions = dataTypes.ReadNextVarInt(packetData); + ulong actionBitset = dataTypes.ReadNextByte(packetData); + int numberOfActions = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < numberOfActions; i++) + { + Guid playerUuid = dataTypes.ReadNextUUID(packetData); + + if ((actionBitset & (1ul << 0)) > 0) // Actions bit 0: add player + { + string name = dataTypes.ReadNextString(packetData); + int numberOfProperties = dataTypes.ReadNextVarInt(packetData); + for (int j = 0; j < numberOfProperties; ++j) + { + dataTypes.SkipNextString(packetData); + dataTypes.SkipNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + handler.OnPlayerJoin(new(name, playerUuid)); + } + + PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; + if ((actionBitset & (1ul << 1)) > 0) // Actions bit 1: initialize chat + { + bool hasSignatureData = dataTypes.ReadNextBool(packetData); + if (hasSignatureData) + { + Guid chatUuid = dataTypes.ReadNextUUID(packetData); + long publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); + byte[] encodedPublicKey = dataTypes.ReadNextByteArray(packetData); + byte[] publicKeySignature = dataTypes.ReadNextByteArray(packetData); + player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, publicKeySignature); + } + else + { + player.ClearPublicKey(); + } + } + if ((actionBitset & (1ul << 2)) > 0) // Actions bit 2: update gamemode + { + handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); + } + if ((actionBitset & (1ul << 3)) > 0) // Actions bit 3: update listed + { + player.Listed = dataTypes.ReadNextBool(packetData); + } + if ((actionBitset & (1ul << 4)) > 0) // Actions bit 4: update latency + { + int latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(playerUuid, latency); //Update latency; + } + if ((actionBitset & (1ul << 5)) > 0) // Actions bit 5: update display name + { + if (dataTypes.ReadNextBool(packetData)) + player.DisplayName = dataTypes.ReadNextString(packetData); + else + player.DisplayName = null; + } + } + } + else if (protocolVersion >= MC_1_8_Version) { int action = dataTypes.ReadNextVarInt(packetData); // Action Name int numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players @@ -1541,9 +1606,9 @@ namespace MinecraftClient.Protocol.Handlers //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory byte[] responseHeader = Array.Empty(); if (protocolVersion < MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses - responseHeader = dataTypes.ConcatBytes(dataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); - SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, dataTypes.GetVarInt(3))); //Accepted pack - SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, dataTypes.GetVarInt(0))); //Successfully loaded + responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); + SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); //Accepted pack + SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); //Successfully loaded break; case PacketTypesIn.SpawnEntity: if (handler.GetEntityHandlingEnabled()) @@ -1953,24 +2018,24 @@ namespace MinecraftClient.Protocol.Handlers // log.Info("[C -> S] Sending packet " + packetID + " > " + dataTypes.ByteArrayToString(packetData.ToArray())); //The inner packet - byte[] the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(packetID), packetData.ToArray()); + byte[] the_packet = dataTypes.ConcatBytes(DataTypes.GetVarInt(packetID), packetData.ToArray()); if (compression_treshold > 0) //Compression enabled? { if (the_packet.Length >= compression_treshold) //Packet long enough for compressing? { byte[] compressed_packet = ZlibUtils.Compress(the_packet); - the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), compressed_packet); + the_packet = dataTypes.ConcatBytes(DataTypes.GetVarInt(the_packet.Length), compressed_packet); } else { - byte[] uncompressed_length = dataTypes.GetVarInt(0); //Not compressed (short packet) + byte[] uncompressed_length = DataTypes.GetVarInt(0); //Not compressed (short packet) the_packet = dataTypes.ConcatBytes(uncompressed_length, the_packet); } } //log.Debug("[C -> S] Sending packet " + packetID + " > " + dataTypes.ByteArrayToString(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet))); - socketWrapper.SendDataRAW(dataTypes.ConcatBytes(dataTypes.GetVarInt(the_packet.Length), the_packet)); + socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(the_packet.Length), the_packet)); } /// @@ -1979,10 +2044,10 @@ namespace MinecraftClient.Protocol.Handlers /// True if login successful public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) { - byte[] protocol_version = dataTypes.GetVarInt(protocolVersion); + byte[] protocol_version = DataTypes.GetVarInt(protocolVersion); string server_address = pForge.GetServerAddress(handler.GetServerHost()); byte[] server_port = dataTypes.GetUShort((ushort)handler.GetServerPort()); - byte[] next_state = dataTypes.GetVarInt(2); + byte[] next_state = DataTypes.GetVarInt(2); byte[] handshake_packet = dataTypes.ConcatBytes(protocol_version, dataTypes.GetString(server_address), server_port, next_state); SendPacket(0x00, handshake_packet); @@ -1997,7 +2062,7 @@ namespace MinecraftClient.Protocol.Handlers else { fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has Sig Data - fullLoginPacket.AddRange(dataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); // Expiration time + fullLoginPacket.AddRange(DataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); // Expiration time fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.Key)); // Public key received from Microsoft API if (protocolVersion >= MC_1_19_2_Version) fullLoginPacket.AddRange(dataTypes.GetArray(playerKeyPair.PublicKey.SignatureV2!)); // Public key signature received from Microsoft API @@ -2014,7 +2079,7 @@ namespace MinecraftClient.Protocol.Handlers else { fullLoginPacket.AddRange(dataTypes.GetBool(true)); // Has UUID - fullLoginPacket.AddRange(dataTypes.GetUUID(uuid)); // UUID + fullLoginPacket.AddRange(DataTypes.GetUUID(uuid)); // UUID } } @@ -2201,7 +2266,7 @@ namespace MinecraftClient.Protocol.Handlers if (String.IsNullOrEmpty(BehindCursor)) return Array.Empty(); - byte[] transaction_id = dataTypes.GetVarInt(autocomplete_transaction_id); + byte[] transaction_id = DataTypes.GetVarInt(autocomplete_transaction_id); byte[] assume_command = new byte[] { 0x00 }; byte[] has_position = new byte[] { 0x00 }; @@ -2261,17 +2326,17 @@ namespace MinecraftClient.Protocol.Handlers SocketWrapper socketWrapper = new(tcp); DataTypes dataTypes = new(MC_1_8_Version); - byte[] packet_id = dataTypes.GetVarInt(0); - byte[] protocol_version = dataTypes.GetVarInt(-1); + byte[] packet_id = DataTypes.GetVarInt(0); + byte[] protocol_version = DataTypes.GetVarInt(-1); byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port); - byte[] next_state = dataTypes.GetVarInt(1); + byte[] next_state = DataTypes.GetVarInt(1); byte[] packet = dataTypes.ConcatBytes(packet_id, protocol_version, dataTypes.GetString(host), server_port, next_state); - byte[] tosend = dataTypes.ConcatBytes(dataTypes.GetVarInt(packet.Length), packet); + byte[] tosend = dataTypes.ConcatBytes(DataTypes.GetVarInt(packet.Length), packet); socketWrapper.SendDataRAW(tosend); - byte[] status_request = dataTypes.GetVarInt(0); - byte[] request_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(status_request.Length), status_request); + byte[] status_request = DataTypes.GetVarInt(0); + byte[] request_packet = dataTypes.ConcatBytes(DataTypes.GetVarInt(status_request.Length), status_request); socketWrapper.SendDataRAW(request_packet); @@ -2362,6 +2427,26 @@ namespace MinecraftClient.Protocol.Handlers catch (ObjectDisposedException) { return false; } } + /// + /// Send MessageAcknowledgment packet + /// + /// Message acknowledgment + /// True if properly sent + public bool SendMessageAcknowledgment(int messageCount) + { + try + { + byte[] fields = DataTypes.GetVarInt(messageCount); + + SendPacket(PacketTypesOut.MessageAcknowledgment, fields); + + return true; + } + catch (SocketException) { return false; } + catch (System.IO.IOException) { return false; } + catch (ObjectDisposedException) { return false; } + } + public LastSeenMessageList.Acknowledgment ConsumeAcknowledgment() { pendingAcknowledgments = 0; @@ -2370,15 +2455,28 @@ namespace MinecraftClient.Protocol.Handlers public void Acknowledge(ChatMessage message) { - LastSeenMessageList.Entry? entry = message.ToLastSeenMessageEntry(); + LastSeenMessageList.AcknowledgedMessage? entry = message.ToLastSeenMessageEntry(); if (entry != null) { - lastSeenMessagesCollector.Add(entry); - lastReceivedMessage = null; - - if (pendingAcknowledgments++ > 64) - SendMessageAcknowledgment(ConsumeAcknowledgment()); + if (protocolVersion >= MC_1_19_3_Version) + { + lastSeenMessagesCollector.Add_1_19_3(entry, true); + lastReceivedMessage = null; + if (lastSeenMessagesCollector.messageCount > 64) + { + int messageCount = lastSeenMessagesCollector.ResetMessageCount(); + if (messageCount > 0) + SendMessageAcknowledgment(messageCount); + } + } + else + { + lastSeenMessagesCollector.Add_1_19_2(entry); + lastReceivedMessage = null; + if (pendingAcknowledgments++ > 64) + SendMessageAcknowledgment(ConsumeAcknowledgment()); + } } } @@ -2410,7 +2508,7 @@ namespace MinecraftClient.Protocol.Handlers // Timestamp: Instant(Long) DateTimeOffset timeNow = DateTimeOffset.UtcNow; - fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); + fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); List>? needSigned = null; // List< Argument Name, Argument Value > if (playerKeyPair != null && isOnlineMode && protocolVersion >= MC_1_19_Version @@ -2419,22 +2517,22 @@ namespace MinecraftClient.Protocol.Handlers if (needSigned == null || needSigned!.Count == 0) { - fields.AddRange(dataTypes.GetLong(0)); // Salt: Long - fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt + fields.AddRange(DataTypes.GetLong(0)); // Salt: Long + fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt } else { Guid uuid = handler.GetUserUuid(); byte[] salt = GenerateSalt(); fields.AddRange(salt); // Salt: Long - fields.AddRange(dataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt + fields.AddRange(DataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt foreach ((string argName, string message) in needSigned) { fields.AddRange(dataTypes.GetString(argName)); // Argument name: String byte[] sign = (protocolVersion >= MC_1_19_2_Version) ? playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen) : playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - fields.AddRange(dataTypes.GetVarInt(sign.Length)); // Signature length: VarInt + fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt fields.AddRange(sign); // Signature: Byte Array } } @@ -2493,18 +2591,21 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_19_Version) { - LastSeenMessageList.Acknowledgment? acknowledgment = - (protocolVersion >= MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = + (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + + (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = + (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); // Timestamp: Instant(Long) DateTimeOffset timeNow = DateTimeOffset.UtcNow; - fields.AddRange(dataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); + fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); if (!isOnlineMode || playerKeyPair == null || !Config.Signature.LoginWithSecureProfile || !Config.Signature.SignChat) { - fields.AddRange(dataTypes.GetLong(0)); // Salt: Long + fields.AddRange(DataTypes.GetLong(0)); // Salt: Long if (protocolVersion < MC_1_19_3_Version) - fields.AddRange(dataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) + fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) else fields.AddRange(dataTypes.GetBool(false)); // Has signature: bool (1.19.3) } @@ -2515,57 +2616,38 @@ namespace MinecraftClient.Protocol.Handlers fields.AddRange(salt); // Signature Length & Signature: (VarInt) and Byte Array - Guid uuid = handler.GetUserUuid(); + Guid playerUuid = handler.GetUserUuid(); byte[] sign; - if (protocolVersion < MC_1_19_2_Version) - { - // 1.19.1 or lower - sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - } - else if (protocolVersion < MC_1_19_3_Version) - { - // 1.19.2 - sign = playerKeyPair.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen); - } - else - { - // 1.19.3 - sign = playerKeyPair.PrivateKey.SignMessage(message, timeNow, ref salt, messageCount, uuid, playerSessionUuid); - } + if (protocolVersion == MC_1_19_Version) // 1.19.1 or lower + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt); + else if (protocolVersion == MC_1_19_2_Version) // 1.19.2 + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); + else // 1.19.3+ + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, handler.GetPlayerInfo(playerUuid)!.ChatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); if (protocolVersion >= MC_1_19_3_Version) fields.AddRange(dataTypes.GetBool(true)); else - fields.AddRange(dataTypes.GetVarInt(sign.Length)); + fields.AddRange(DataTypes.GetVarInt(sign.Length)); fields.AddRange(sign); } - + if (protocolVersion <= MC_1_19_2_Version) + fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean + if (protocolVersion >= MC_1_19_3_Version) - fields.AddRange(dataTypes.GetVarInt(messageCount)); // Message count (1.19.3) - else - fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean (1.19.2) - - //if (protocolVersion >= MC_1_19_3_Version) - // messageCount++; - - if (protocolVersion >= MC_1_19_2_Version) { - if (protocolVersion >= MC_1_19_3_Version) - { - // 1.19.3 - // Acknowledged: BitSet (no idea what is this) - //fields.AddRange(dataTypes.GetVarInt(0)); - fields.AddRange(new byte[3] {0,0,0 }); - } - else - { - // 1.19.2 - // Message Acknowledgment - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); - } + // message count + fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); + + // Acknowledged: BitSet + fields.AddRange(bitset_1_19_3); + } + else if (protocolVersion == MC_1_19_2_Version) + { + // Message Acknowledgment + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); } - } SendPacket(PacketTypesOut.ChatMessage, fields); return true; @@ -2580,9 +2662,9 @@ namespace MinecraftClient.Protocol.Handlers try { List fields = new(); - fields.AddRange(dataTypes.GetVarInt(PlayerEntityID)); - fields.AddRange(dataTypes.GetVarInt(ActionID)); - fields.AddRange(dataTypes.GetVarInt(0)); + fields.AddRange(DataTypes.GetVarInt(PlayerEntityID)); + fields.AddRange(DataTypes.GetVarInt(ActionID)); + fields.AddRange(DataTypes.GetVarInt(0)); SendPacket(PacketTypesOut.EntityAction, fields); return true; } @@ -2648,7 +2730,7 @@ namespace MinecraftClient.Protocol.Handlers fields.Add(viewDistance); if (protocolVersion >= MC_1_9_Version) - fields.AddRange(dataTypes.GetVarInt(chatMode)); + fields.AddRange(DataTypes.GetVarInt(chatMode)); else fields.AddRange(new byte[] { chatMode }); @@ -2660,7 +2742,7 @@ namespace MinecraftClient.Protocol.Handlers } else fields.Add(skinParts); if (protocolVersion >= MC_1_9_Version) - fields.AddRange(dataTypes.GetVarInt(mainHand)); + fields.AddRange(DataTypes.GetVarInt(mainHand)); if (protocolVersion >= MC_1_17_Version) { if (protocolVersion >= MC_1_18_1_Version) @@ -2781,7 +2863,7 @@ namespace MinecraftClient.Protocol.Handlers { try { - SendPacket(0x02, dataTypes.ConcatBytes(dataTypes.GetVarInt(messageId), dataTypes.GetBool(understood), data)); + SendPacket(0x02, dataTypes.ConcatBytes(DataTypes.GetVarInt(messageId), dataTypes.GetBool(understood), data)); return true; } catch (SocketException) { return false; } @@ -2800,8 +2882,8 @@ namespace MinecraftClient.Protocol.Handlers try { List fields = new(); - fields.AddRange(dataTypes.GetVarInt(EntityID)); - fields.AddRange(dataTypes.GetVarInt(type)); + fields.AddRange(DataTypes.GetVarInt(EntityID)); + fields.AddRange(DataTypes.GetVarInt(type)); // Is player Sneaking (Only 1.16 and above) // Currently hardcoded to false @@ -2823,12 +2905,12 @@ namespace MinecraftClient.Protocol.Handlers try { List fields = new(); - fields.AddRange(dataTypes.GetVarInt(EntityID)); - fields.AddRange(dataTypes.GetVarInt(type)); + fields.AddRange(DataTypes.GetVarInt(EntityID)); + fields.AddRange(DataTypes.GetVarInt(type)); fields.AddRange(dataTypes.GetFloat(X)); fields.AddRange(dataTypes.GetFloat(Y)); fields.AddRange(dataTypes.GetFloat(Z)); - fields.AddRange(dataTypes.GetVarInt(hand)); + fields.AddRange(DataTypes.GetVarInt(hand)); // Is player Sneaking (Only 1.16 and above) // Currently hardcoded to false // TODO: Update to reflect the real player state @@ -2846,9 +2928,9 @@ namespace MinecraftClient.Protocol.Handlers try { List fields = new(); - fields.AddRange(dataTypes.GetVarInt(EntityID)); - fields.AddRange(dataTypes.GetVarInt(type)); - fields.AddRange(dataTypes.GetVarInt(hand)); + fields.AddRange(DataTypes.GetVarInt(EntityID)); + fields.AddRange(DataTypes.GetVarInt(type)); + fields.AddRange(DataTypes.GetVarInt(hand)); // Is player Sneaking (Only 1.16 and above) // Currently hardcoded to false // TODO: Update to reflect the real player state @@ -2876,9 +2958,9 @@ namespace MinecraftClient.Protocol.Handlers try { List packet = new(); - packet.AddRange(dataTypes.GetVarInt(hand)); + packet.AddRange(DataTypes.GetVarInt(hand)); if (protocolVersion >= MC_1_19_Version) - packet.AddRange(dataTypes.GetVarInt(sequenceId)); + packet.AddRange(DataTypes.GetVarInt(sequenceId)); SendPacket(PacketTypesOut.UseItem, packet); return true; } @@ -2892,11 +2974,11 @@ namespace MinecraftClient.Protocol.Handlers try { List packet = new(); - packet.AddRange(dataTypes.GetVarInt(status)); + packet.AddRange(DataTypes.GetVarInt(status)); packet.AddRange(dataTypes.GetLocation(location)); - packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face))); + packet.AddRange(DataTypes.GetVarInt(dataTypes.GetBlockFace(face))); if (protocolVersion >= MC_1_19_Version) - packet.AddRange(dataTypes.GetVarInt(sequenceId)); + packet.AddRange(DataTypes.GetVarInt(sequenceId)); SendPacket(PacketTypesOut.PlayerDigging, packet); return true; } @@ -2912,15 +2994,15 @@ namespace MinecraftClient.Protocol.Handlers try { List packet = new(); - packet.AddRange(dataTypes.GetVarInt(hand)); + packet.AddRange(DataTypes.GetVarInt(hand)); packet.AddRange(dataTypes.GetLocation(location)); - packet.AddRange(dataTypes.GetVarInt(dataTypes.GetBlockFace(face))); + packet.AddRange(DataTypes.GetVarInt(dataTypes.GetBlockFace(face))); packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorX packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorY packet.AddRange(dataTypes.GetFloat(0.5f)); // cursorZ packet.Add(0); // insideBlock = false; if (protocolVersion >= MC_1_19_Version) - packet.AddRange(dataTypes.GetVarInt(sequenceId)); + packet.AddRange(DataTypes.GetVarInt(sequenceId)); SendPacket(PacketTypesOut.PlayerBlockPlacement, packet); return true; } @@ -2986,14 +3068,14 @@ namespace MinecraftClient.Protocol.Handlers // 1.18+ if (protocolVersion >= MC_1_18_1_Version) { - packet.AddRange(dataTypes.GetVarInt(stateId)); // State ID + packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID } // 1.17.1 else if (protocolVersion == MC_1_17_1_Version) { packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID - packet.AddRange(dataTypes.GetVarInt(stateId)); // State ID + packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID } // Older else @@ -3007,13 +3089,13 @@ namespace MinecraftClient.Protocol.Handlers packet.AddRange(dataTypes.GetShort(actionNumber)); if (protocolVersion >= MC_1_9_Version) - packet.AddRange(dataTypes.GetVarInt(mode)); // Mode + packet.AddRange(DataTypes.GetVarInt(mode)); // Mode else packet.Add(mode); // 1.17+ Array of changed slots if (protocolVersion >= MC_1_17_Version) { - packet.AddRange(dataTypes.GetVarInt(changedSlots.Count)); // Length of the array + packet.AddRange(DataTypes.GetVarInt(changedSlots.Count)); // Length of the array foreach (var slot in changedSlots) { packet.AddRange(dataTypes.GetShort(slot.Item1)); // slot ID @@ -3071,7 +3153,7 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion < MC_1_8_Version) { - packet.AddRange(dataTypes.GetInt(playerid)); + packet.AddRange(DataTypes.GetInt(playerid)); packet.Add((byte)1); // Swing arm } else if (protocolVersion < MC_1_9_Version) @@ -3080,7 +3162,7 @@ namespace MinecraftClient.Protocol.Handlers } else // MC 1.9+ { - packet.AddRange(dataTypes.GetVarInt(animation)); + packet.AddRange(DataTypes.GetVarInt(animation)); } SendPacket(PacketTypesOut.Animation, packet); @@ -3149,7 +3231,7 @@ namespace MinecraftClient.Protocol.Handlers List packet = new(); packet.AddRange(dataTypes.GetLocation(location)); packet.AddRange(dataTypes.GetString(command)); - packet.AddRange(dataTypes.GetVarInt((int)mode)); + packet.AddRange(DataTypes.GetVarInt((int)mode)); packet.Add((byte)flags); SendPacket(PacketTypesOut.UpdateSign, packet); return true; @@ -3185,7 +3267,7 @@ namespace MinecraftClient.Protocol.Handlers try { List packet = new(); - packet.AddRange(dataTypes.GetVarInt(selectedSlot)); + packet.AddRange(DataTypes.GetVarInt(selectedSlot)); SendPacket(PacketTypesOut.SelectTrade, packet); return true; } @@ -3204,7 +3286,7 @@ namespace MinecraftClient.Protocol.Handlers try { List packet = new(); - packet.AddRange(dataTypes.GetUUID(UUID)); + packet.AddRange(DataTypes.GetUUID(UUID)); SendPacket(PacketTypesOut.Spectate, packet); return true; } @@ -3217,23 +3299,21 @@ namespace MinecraftClient.Protocol.Handlers public bool SendPlayerSession(PlayerKeyPair? playerKeyPair) { + if (playerKeyPair == null) + return false; + if (protocolVersion >= MC_1_19_3_Version) { try { List packet = new(); - byte[] timestampByte = BitConverter.GetBytes(playerKeyPair.GetExpirationMilliseconds()); - Array.Reverse(timestampByte); - var signature = KeyUtils.ComputeHash(timestampByte.Concat(playerKeyPair.PublicKey.Key).ToArray()); - - packet.AddRange(dataTypes.GetUUID(playerSessionUuid)); - packet.AddRange(dataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); - packet.AddRange(dataTypes.GetVarInt(playerKeyPair.PublicKey.Key.Length)); + chatUuid = Guid.NewGuid(); + packet.AddRange(DataTypes.GetUUID(chatUuid)); + packet.AddRange(DataTypes.GetLong(playerKeyPair.GetExpirationMilliseconds())); + packet.AddRange(DataTypes.GetVarInt(playerKeyPair.PublicKey.Key.Length)); packet.AddRange(playerKeyPair.PublicKey.Key); - //packet.AddRange(dataTypes.GetVarInt(signature.Length)); - //packet.AddRange(signature); - packet.AddRange(dataTypes.GetVarInt(playerKeyPair.PublicKey.SignatureV2.Length)); + packet.AddRange(DataTypes.GetVarInt(playerKeyPair.PublicKey.SignatureV2!.Length)); packet.AddRange(playerKeyPair.PublicKey.SignatureV2); SendPacket(PacketTypesOut.PlayerSession, packet); diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index 05a30927..09f57f56 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -149,7 +149,7 @@ namespace MinecraftClient.Protocol.Handlers mods[i] = dataTypes.ConcatBytes(dataTypes.GetString(mod.ModID), dataTypes.GetString(mod.Version)); } SendForgeHandshakePacket(FMLHandshakeDiscriminator.ModList, - dataTypes.ConcatBytes(dataTypes.GetVarInt(forgeInfo.Mods.Count), dataTypes.ConcatBytes(mods))); + dataTypes.ConcatBytes(DataTypes.GetVarInt(forgeInfo.Mods.Count), dataTypes.ConcatBytes(mods))); fmlHandshakeState = FMLHandshakeClientState.WAITINGSERVERDATA; @@ -336,19 +336,19 @@ namespace MinecraftClient.Protocol.Handlers ConsoleIO.WriteLineFormatted("§8" + Translations.forge_fml2_mod_send, acceptnewlines: true); // Packet ID 2: Client to Server Mod List - fmlResponsePacket.AddRange(dataTypes.GetVarInt(2)); - fmlResponsePacket.AddRange(dataTypes.GetVarInt(mods.Count)); + fmlResponsePacket.AddRange(DataTypes.GetVarInt(2)); + fmlResponsePacket.AddRange(DataTypes.GetVarInt(mods.Count)); foreach (string mod in mods) fmlResponsePacket.AddRange(dataTypes.GetString(mod)); - fmlResponsePacket.AddRange(dataTypes.GetVarInt(channels.Count)); + fmlResponsePacket.AddRange(DataTypes.GetVarInt(channels.Count)); foreach (KeyValuePair item in channels) { fmlResponsePacket.AddRange(dataTypes.GetString(item.Key)); fmlResponsePacket.AddRange(dataTypes.GetString(item.Value)); } - fmlResponsePacket.AddRange(dataTypes.GetVarInt(registries.Count)); + fmlResponsePacket.AddRange(DataTypes.GetVarInt(registries.Count)); foreach (string registry in registries) { fmlResponsePacket.AddRange(dataTypes.GetString(registry)); @@ -374,7 +374,7 @@ namespace MinecraftClient.Protocol.Handlers ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_registry, registryName)); } - fmlResponsePacket.AddRange(dataTypes.GetVarInt(99)); + fmlResponsePacket.AddRange(DataTypes.GetVarInt(99)); fmlResponseReady = true; break; @@ -393,7 +393,7 @@ namespace MinecraftClient.Protocol.Handlers ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_config, configName)); } - fmlResponsePacket.AddRange(dataTypes.GetVarInt(99)); + fmlResponsePacket.AddRange(DataTypes.GetVarInt(99)); fmlResponseReady = true; break; @@ -408,7 +408,7 @@ namespace MinecraftClient.Protocol.Handlers // Wrap our FML packet into a LoginPluginResponse payload responseData.Clear(); responseData.AddRange(dataTypes.GetString(fmlChannel)); - responseData.AddRange(dataTypes.GetVarInt(fmlResponsePacket.Count)); + responseData.AddRange(DataTypes.GetVarInt(fmlResponsePacket.Count)); responseData.AddRange(fmlResponsePacket); return true; } diff --git a/MinecraftClient/Protocol/Message/ChatMessage.cs b/MinecraftClient/Protocol/Message/ChatMessage.cs index 790c70af..23a34be4 100644 --- a/MinecraftClient/Protocol/Message/ChatMessage.cs +++ b/MinecraftClient/Protocol/Message/ChatMessage.cs @@ -57,9 +57,9 @@ namespace MinecraftClient.Protocol.Message this.senderUUID = senderUUID; } - public LastSeenMessageList.Entry? ToLastSeenMessageEntry() + public LastSeenMessageList.AcknowledgedMessage? ToLastSeenMessageEntry() { - return signature != null ? new LastSeenMessageList.Entry(senderUUID, signature) : null; + return signature != null ? new LastSeenMessageList.AcknowledgedMessage(senderUUID, signature, true) : null; } public bool LacksSender() diff --git a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs index c3de5178..528e3cee 100644 --- a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs +++ b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Security.Permissions; +using static MinecraftClient.Protocol.Message.LastSeenMessageList; namespace MinecraftClient.Protocol.Message { @@ -8,38 +10,45 @@ namespace MinecraftClient.Protocol.Message /// public class LastSeenMessageList { - public static readonly LastSeenMessageList EMPTY = new(Array.Empty()); + public static readonly LastSeenMessageList EMPTY = new(Array.Empty()); public static readonly int MAX_ENTRIES = 5; - public Entry[] entries; + public AcknowledgedMessage[] entries; - public LastSeenMessageList(Entry[] list) + public LastSeenMessageList(AcknowledgedMessage[] list) { entries = list; } public void WriteForSign(List data) { - foreach (Entry entry in entries) + foreach (AcknowledgedMessage entry in entries) { data.Add(70); data.AddRange(entry.profileId.ToBigEndianBytes()); - data.AddRange(entry.lastSignature); + data.AddRange(entry.signature); } } /// /// A pair of a player's UUID and the signature of the last message they saw, used as an entry of LastSeenMessageList. /// - public class Entry + public record AcknowledgedMessage { + public bool pending; public Guid profileId; - public byte[] lastSignature; + public byte[] signature; - public Entry(Guid profileId, byte[] lastSignature) + public AcknowledgedMessage(Guid profileId, byte[] lastSignature, bool pending) { this.profileId = profileId; - this.lastSignature = lastSignature; + this.signature = lastSignature; + this.pending = pending; + } + + public AcknowledgedMessage UnmarkAsPending() + { + return this.pending ? new AcknowledgedMessage(profileId, signature, false) : this; } } @@ -50,9 +59,9 @@ namespace MinecraftClient.Protocol.Message public class Acknowledgment { public LastSeenMessageList lastSeen; - public Entry? lastReceived; + public AcknowledgedMessage? lastReceived; - public Acknowledgment(LastSeenMessageList lastSeenMessageList, Entry? lastReceivedMessage) + public Acknowledgment(LastSeenMessageList lastSeenMessageList, AcknowledgedMessage? lastReceivedMessage) { lastSeen = lastSeenMessageList; lastReceived = lastReceivedMessage; @@ -70,24 +79,26 @@ namespace MinecraftClient.Protocol.Message /// public class LastSeenMessagesCollector { - private readonly LastSeenMessageList.Entry[] entries; - private int size = 0; + private readonly LastSeenMessageList.AcknowledgedMessage?[] acknowledgedMessages; + private int nextIndex = 0; + internal int messageCount { private set; get; } = 0; + private LastSeenMessageList.AcknowledgedMessage? lastEntry = null; private LastSeenMessageList lastSeenMessages; public LastSeenMessagesCollector(int size) { lastSeenMessages = LastSeenMessageList.EMPTY; - entries = new LastSeenMessageList.Entry[size]; + acknowledgedMessages = new LastSeenMessageList.AcknowledgedMessage[size]; } - public void Add(LastSeenMessageList.Entry entry) + public void Add_1_19_2(LastSeenMessageList.AcknowledgedMessage entry) { - LastSeenMessageList.Entry? lastEntry = entry; + LastSeenMessageList.AcknowledgedMessage? lastEntry = entry; - for (int i = 0; i < size; ++i) + for (int i = 0; i < messageCount; ++i) { - LastSeenMessageList.Entry curEntry = entries[i]; - entries[i] = lastEntry; + LastSeenMessageList.AcknowledgedMessage curEntry = acknowledgedMessages[i]!; + acknowledgedMessages[i] = lastEntry; lastEntry = curEntry; if (curEntry.profileId == entry.profileId) { @@ -96,19 +107,62 @@ namespace MinecraftClient.Protocol.Message } } - if (lastEntry != null && size < entries.Length) - entries[size++] = lastEntry; + if (lastEntry != null && messageCount < acknowledgedMessages.Length) + acknowledgedMessages[messageCount++] = lastEntry; - LastSeenMessageList.Entry[] msgList = new LastSeenMessageList.Entry[size]; - for (int i = 0; i < size; ++i) - msgList[i] = entries[i]; + LastSeenMessageList.AcknowledgedMessage[] msgList = new LastSeenMessageList.AcknowledgedMessage[messageCount]; + for (int i = 0; i < messageCount; ++i) + msgList[i] = acknowledgedMessages[i]!; lastSeenMessages = new LastSeenMessageList(msgList); } + public bool Add_1_19_3(LastSeenMessageList.AcknowledgedMessage entry, bool displayed) + { + // net.minecraft.network.message.LastSeenMessagesCollector#add(net.minecraft.network.message.MessageSignatureData, boolean) + // net.minecraft.network.message.LastSeenMessagesCollector#add(net.minecraft.network.message.AcknowledgedMessage) + if (entry == lastEntry) + return false; + lastEntry = entry; + + int index = nextIndex; + nextIndex = (index + 1) % acknowledgedMessages.Length; + + ++messageCount; + acknowledgedMessages[index] = displayed ? entry : null; + + return true; + } + + public Tuple Collect_1_19_3() + { + // net.minecraft.network.message.LastSeenMessagesCollector#collect + int count = ResetMessageCount(); + byte[] bitset = new byte[3]; // new Bitset(20); Todo: Use a complete bitset implementation. + List objectList = new(acknowledgedMessages.Length); + for (int j = 0; j < acknowledgedMessages.Length; ++j) + { + int k = (nextIndex + j) % acknowledgedMessages.Length; + AcknowledgedMessage? acknowledgedMessage = acknowledgedMessages[k]; + if (acknowledgedMessage == null) + continue; + bitset[j / 8] |= (byte)(1 << (j % 8)); // bitSet.set(j, true); + objectList.Add(acknowledgedMessage); + acknowledgedMessages[k] = acknowledgedMessage.UnmarkAsPending(); + } + return new(objectList.ToArray(), bitset, count); + } + public LastSeenMessageList GetLastSeenMessages() { return lastSeenMessages; } + public int ResetMessageCount() + { + // net.minecraft.network.message.LastSeenMessagesCollector#resetMessageCount + int cnt = messageCount; + messageCount = 0; + return cnt; + } } } diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index b77aea58..94899dc7 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -20,15 +20,19 @@ namespace MinecraftClient.Protocol public string? DisplayName; + public bool Listed = true; + // Entity info public Mapping.Entity? entity; // For message signature - private readonly PublicKey? PublicKey; + public Guid ChatUuid = Guid.Empty; - private readonly DateTime? KeyExpiresAt; + private PublicKey? PublicKey; + + private DateTime? KeyExpiresAt; private bool lastMessageVerified; @@ -71,6 +75,28 @@ namespace MinecraftClient.Protocol precedingSignature = null; } + public void ClearPublicKey() + { + ChatUuid = Guid.Empty; + PublicKey = null; + KeyExpiresAt = null; + } + + public void SetPublicKey(Guid chatUuid, long publicKeyExpiryTime, byte[] encodedPublicKey, byte[] publicKeySignature) + { + ChatUuid = chatUuid; + KeyExpiresAt = DateTimeOffset.FromUnixTimeMilliseconds(publicKeyExpiryTime).UtcDateTime; + try + { + PublicKey = new PublicKey(encodedPublicKey, publicKeySignature); + lastMessageVerified = true; + } + catch (System.Security.Cryptography.CryptographicException) + { + PublicKey = null; + } + } + public bool IsMessageChainLegal() { return lastMessageVerified; diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index b7f31c77..1fb91721 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Security.Cryptography; using System.Text; -using ImageMagick; +using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Message; +using static MinecraftClient.Protocol.Message.LastSeenMessageList; namespace MinecraftClient.Protocol.Keys { @@ -111,6 +112,33 @@ namespace MinecraftClient.Protocol.Keys return data.ToArray(); } + public static byte[] GetSignatureData_1_19_3(string message, Guid playerUuid, Guid chatUuid, int messageIndex, DateTimeOffset timestamp, ref byte[] salt, AcknowledgedMessage[] lastSeenMessages) + { + List data = new(); + + // net.minecraft.network.message.SignedMessage#update + data.AddRange(DataTypes.GetInt(1)); + + // message link + // net.minecraft.network.message.MessageLink#update + data.AddRange(DataTypes.GetUUID(playerUuid)); + data.AddRange(DataTypes.GetUUID(chatUuid)); + data.AddRange(DataTypes.GetInt(messageIndex)); + + // message body + // net.minecraft.network.message.MessageBody#update + data.AddRange(salt); + data.AddRange(DataTypes.GetLong(timestamp.ToUnixTimeSeconds())); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + data.AddRange(DataTypes.GetInt(messageBytes.Length)); + data.AddRange(messageBytes); + data.AddRange(DataTypes.GetInt(lastSeenMessages.Length)); + foreach (AcknowledgedMessage ack in lastSeenMessages) + data.AddRange(ack.signature); + + return data.ToArray(); + } + public static byte[] GetSignatureData(byte[]? precedingSignature, Guid sender, byte[] bodySign) { List data = new(); diff --git a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs index 65c569c6..3fecbe91 100644 --- a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs @@ -1,6 +1,7 @@ using System; using System.Security.Cryptography; using MinecraftClient.Protocol.Message; +using static MinecraftClient.Protocol.Message.LastSeenMessageList; namespace MinecraftClient.Protocol.Keys { @@ -43,7 +44,7 @@ namespace MinecraftClient.Protocol.Keys } /// - /// Sign message - 1.19.1 and above + /// Sign message - 1.19.1 and 1.19.2 /// /// Message content /// Sender uuid @@ -64,10 +65,21 @@ namespace MinecraftClient.Protocol.Keys return msgSign; } - public byte[] SignMessage(string message, DateTimeOffset timestamp, ref byte[] salt, int messageCount, Guid sender, Guid sessionUuid) + /// + /// Sign message - 1.19.3 and above + /// + /// Message content + /// Sender uuid + /// Timestamp + /// Salt + /// LastSeenMessageList + /// Signature data + public byte[] SignMessage(string message, Guid playerUuid, Guid chatUuid, int messageIndex, DateTimeOffset timestamp, ref byte[] salt, AcknowledgedMessage[] lastSeenMessages) { - byte[] data = KeyUtils.GetSignatureData(message, timestamp, ref salt, messageCount, sender, sessionUuid); - return SignData(data); + byte[] bodySignData = KeyUtils.GetSignatureData_1_19_3(message, playerUuid, chatUuid, messageIndex, timestamp, ref salt, lastSeenMessages); + + return SignData(bodySignData); } + } } diff --git a/MinecraftClient/Protocol/ReplayHandler.cs b/MinecraftClient/Protocol/ReplayHandler.cs index abaeecfc..1c88eb6a 100644 --- a/MinecraftClient/Protocol/ReplayHandler.cs +++ b/MinecraftClient/Protocol/ReplayHandler.cs @@ -233,7 +233,7 @@ namespace MinecraftClient.Protocol // build raw packet // format: packetID + packetData List rawPacket = new(); - rawPacket.AddRange(dataTypes.GetVarInt(packetID).ToArray()); + rawPacket.AddRange(DataTypes.GetVarInt(packetID).ToArray()); rawPacket.AddRange(packetData.ToArray()); // build format // format: timestamp + packetLength + RawPacket @@ -376,7 +376,7 @@ namespace MinecraftClient.Protocol private byte[] GetSpawnPlayerPacket(int entityID, Guid playerUUID, Location location, double pitch, double yaw) { List packet = new(); - packet.AddRange(dataTypes.GetVarInt(entityID)); + packet.AddRange(DataTypes.GetVarInt(entityID)); packet.AddRange(playerUUID.ToBigEndianBytes()); packet.AddRange(dataTypes.GetDouble(location.X)); packet.AddRange(dataTypes.GetDouble(location.Y)); diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index 85527b2c..1ae446e4 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1497,7 +1497,7 @@ namespace MinecraftClient { } /// - /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers.. + /// Looks up a localized string similar to Temporary fix for Badpacket issue on some servers. Need to enable "TerrainAndMovements" first.. /// internal static string Main_Advanced_temporary_fix_badpacket { get { diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index da56bedc..9d864598 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -681,7 +681,7 @@ Usage examples: "/tell <mybot> connect Server1", "/connect Server2"Messages displayed above xp bar, set this to false in case of xp bar spam. - Temporary fix for Badpacket issue on some servers. + Temporary fix for Badpacket issue on some servers. Need to enable "TerrainAndMovements" first. Use "none", "bit_4", "bit_8" or "bit_24". This can be checked by opening the debug log. diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 67c5cf72..c1647c77 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -465,6 +465,12 @@ namespace MinecraftClient Advanced.MinTerminalWidth = 1; if (Advanced.MinTerminalHeight < 1) Advanced.MinTerminalHeight = 1; + + if (Advanced.TemporaryFixBadpacket && !Advanced.TerrainAndMovements) + { + Advanced.TerrainAndMovements = true; + ConsoleIO.WriteLineFormatted("§c[Settings]You need to enable TerrainAndMovements before enabling TemporaryFixBadpacket."); + } } [TomlDoNotInlineObject] @@ -1046,27 +1052,27 @@ namespace MinecraftClient catch (ArgumentException) { checkResult = false; - ConsoleIO.WriteLineFormatted("§cIllegal regular expression: ChatFormat.Public = " + Public); + ConsoleIO.WriteLineFormatted("§c[Settings]Illegal regular expression: ChatFormat.Public = " + Public); } try { _ = new Regex(Private); } catch (ArgumentException) { checkResult = false; - ConsoleIO.WriteLineFormatted("§cIllegal regular expression: ChatFormat.Private = " + Private); + ConsoleIO.WriteLineFormatted("§c[Settings]Illegal regular expression: ChatFormat.Private = " + Private); } try { _ = new Regex(TeleportRequest); } catch (ArgumentException) { checkResult = false; - ConsoleIO.WriteLineFormatted("§cIllegal regular expression: ChatFormat.TeleportRequest = " + TeleportRequest); + ConsoleIO.WriteLineFormatted("§c[Settings]Illegal regular expression: ChatFormat.TeleportRequest = " + TeleportRequest); } if (!checkResult) { UserDefined = false; - ConsoleIO.WriteLineFormatted("§cChatFormat: User-defined regular expressions are disabled."); + ConsoleIO.WriteLineFormatted("§c[Settings]ChatFormat: User-defined regular expressions are disabled."); } } } From 0ce969077841f3b7a89df98755576372f8fe5c2e Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 00:53:36 +0800 Subject: [PATCH 23/44] 1.19.3 Chat command signing support & Update chat paser --- MinecraftClient/McClient.cs | 6 +- .../Protocol/Handlers/Protocol16.cs | 2 +- .../Protocol/Handlers/Protocol18.cs | 192 ++++++++++-------- .../Protocol/Message/ChatMessage.cs | 27 +-- .../Protocol/Message/ChatParser.cs | 37 +++- .../Protocol/Message/LastSeenMessageList.cs | 3 +- 6 files changed, 164 insertions(+), 103 deletions(-) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 9d49ae59..d001f872 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -64,6 +64,7 @@ namespace MinecraftClient private double motionY; public enum MovementType { Sneak, Walk, Sprint } private int sequenceId; // User for player block synchronization (Aka. digging, placing blocks, etc..) + private bool CanSendMessage = false; private readonly string host; private readonly int port; @@ -286,6 +287,9 @@ namespace MinecraftClient /// private void TrySendMessageToServer() { + if (!CanSendMessage) + return; + while (chatQueue.Count > 0 && nextMessageSendTime < DateTime.Now) { string text = chatQueue.Dequeue(); @@ -2411,7 +2415,7 @@ namespace MinecraftClient if (protocolversion >= Protocol18Handler.MC_1_19_3_Version && playerKeyPair != null) handler.SendPlayerSession(playerKeyPair); - + CanSendMessage = true; if (inventoryHandlingRequested) { diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index aba5c555..6ac8d7be 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -119,7 +119,7 @@ namespace MinecraftClient.Protocol.Handlers case 0x02: ReadData(1); ReadNextString(); ReadNextString(); ReadData(4); break; case 0x03: string message = ReadNextString(); - handler.OnTextReceived(new ChatMessage(message, protocolversion >= 72, 0, Guid.Empty)); break; + handler.OnTextReceived(new ChatMessage(message, null, protocolversion >= 72, -1, Guid.Empty)); break; case 0x04: ReadData(16); break; case 0x05: ReadData(6); ReadNextItemSlot(); break; case 0x06: ReadData(12); break; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 5a6f0f5f..c47b2d27 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -377,6 +377,8 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < worldCount; i++) dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above var registryCodec = dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above + if (protocolVersion >= MC_1_19_Version) + ChatParser.ReadChatType(registryCodec); if (handler.GetTerrainEnabled()) World.StoreDimensionList(registryCodec); } @@ -455,9 +457,6 @@ namespace MinecraftClient.Protocol.Handlers } } - if (protocolVersion >= MC_1_19_3_Version) - SendPlayerSession(handler.GetPlayerKeyPair()); - break; case PacketTypesIn.DeclareCommands: if (protocolVersion >= MC_1_19_Version) @@ -486,7 +485,7 @@ namespace MinecraftClient.Protocol.Handlers else senderUUID = Guid.Empty; - handler.OnTextReceived(new(message, true, messageType, senderUUID)); + handler.OnTextReceived(new(message, null, true, messageType, senderUUID)); } else if (protocolVersion == MC_1_19_Version) // 1.19 { @@ -608,25 +607,31 @@ namespace MinecraftClient.Protocol.Handlers { // 1.19.3+ // Header section + // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write Guid senderUUID = dataTypes.ReadNextUUID(packetData); int index = dataTypes.ReadNextVarInt(packetData); // Signature is fixed size of 256 bytes byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; // Body + // net.minecraft.network.message.MessageBody.Serialized#write string message = dataTypes.ReadNextString(packetData); long timestamp = dataTypes.ReadNextLong(packetData); long salt = dataTypes.ReadNextLong(packetData); // Previous Messages + // net.minecraft.network.message.LastSeenMessageList.Indexed#write + // net.minecraft.network.message.MessageSignatureData.Indexed#write int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); - List> previousMessageSignatures = new(); - + Tuple[] previousMessageSignatures = new Tuple[totalPreviousMessages]; for (int i = 0; i < totalPreviousMessages; i++) { - int messageId = dataTypes.ReadNextVarInt(packetData); - if (messageId == 0) // from botcraft implementation. Only read if id is 0. Byte array is fixed size of 256 bytes - previousMessageSignatures.Add(new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256))); + // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf + int messageId = dataTypes.ReadNextVarInt(packetData) - 1; + if (messageId == -1) + previousMessageSignatures[i] = new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256)); + else + previousMessageSignatures[i] = new Tuple(messageId, null); } // Other @@ -638,14 +643,16 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextULongArray(packetData); // Network Target + // net.minecraft.network.message.MessageType.Serialized#write int chatTypeId = dataTypes.ReadNextVarInt(packetData); string chatName = dataTypes.ReadNextString(packetData); string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - Dictionary chatInfo = Json.ParseJson(chatName).Properties; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + + Dictionary chatInfo = Json.ParseJson(targetName ?? chatName).Properties; string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); if (targetName != null && (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; @@ -663,13 +670,68 @@ namespace MinecraftClient.Protocol.Handlers } } - // TODO: Verify the message - ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, false); + // Todo: verify message + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + verifyResult = false; + //bool lastVerifyResult = player.IsMessageChainLegal(); + //verifyResult = player.VerifyMessage(message, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); + //if (lastVerifyResult && !verifyResult) + // log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); + } + } + + ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); if (isOnlineMode && !chat.LacksSender() && messageSignature != null) Acknowledge(chat); handler.OnTextReceived(chat); } break; + case PacketTypesIn.SystemChat: + string systemMessage = dataTypes.ReadNextString(packetData); + if (protocolVersion >= MC_1_19_3_Version) + { + bool isOverlay = dataTypes.ReadNextBool(packetData); + if (isOverlay) + { + if (!Config.Main.Advanced.ShowXPBarMessages) + break; + } + else + { + if (!Config.Main.Advanced.ShowSystemMessages) + break; + } + handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); + } + else + { + int msgType = dataTypes.ReadNextVarInt(packetData); + if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) + break; + handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); + } + break; + case PacketTypesIn.ProfilelessChatMessage: + string message_ = dataTypes.ReadNextString(packetData); + int messageType_ = dataTypes.ReadNextVarInt(packetData); + string messageName = dataTypes.ReadNextString(packetData); + string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + ChatMessage profilelessChat = new(message_, messageName, true, messageType_, Guid.Empty, true); + profilelessChat.isSenderJson = true; + profilelessChat.teamName = targetName_; + handler.OnTextReceived(profilelessChat); + break; case PacketTypesIn.CombatEvent: // 1.8 - 1.16.5 if (protocolVersion >= MC_1_8_Version && protocolVersion <= MC_1_16_5_Version) @@ -1214,14 +1276,6 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.ChatSuggestions: break; - case PacketTypesIn.ProfilelessChatMessage: - string message_ = dataTypes.ReadNextString(packetData); - int messageType_ = dataTypes.ReadNextVarInt(packetData); - string messageName = dataTypes.ReadNextString(packetData); - string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - // Not clear for what this is used for as the time of writing - break; case PacketTypesIn.MapChunkBulk: if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) { @@ -1296,14 +1350,13 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.PlayerInfo: if (protocolVersion >= MC_1_19_3_Version) { - // int actions = dataTypes.ReadNextVarInt(packetData); - ulong actionBitset = dataTypes.ReadNextByte(packetData); + byte actionBitset = dataTypes.ReadNextByte(packetData); int numberOfActions = dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < numberOfActions; i++) { Guid playerUuid = dataTypes.ReadNextUUID(packetData); - if ((actionBitset & (1ul << 0)) > 0) // Actions bit 0: add player + if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player { string name = dataTypes.ReadNextString(packetData); int numberOfProperties = dataTypes.ReadNextVarInt(packetData); @@ -1318,7 +1371,7 @@ namespace MinecraftClient.Protocol.Handlers } PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; - if ((actionBitset & (1ul << 1)) > 0) // Actions bit 1: initialize chat + if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat { bool hasSignatureData = dataTypes.ReadNextBool(packetData); if (hasSignatureData) @@ -1334,20 +1387,20 @@ namespace MinecraftClient.Protocol.Handlers player.ClearPublicKey(); } } - if ((actionBitset & (1ul << 2)) > 0) // Actions bit 2: update gamemode + if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode { handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); } - if ((actionBitset & (1ul << 3)) > 0) // Actions bit 3: update listed + if ((actionBitset & (1 << 3)) > 0) // Actions bit 3: update listed { player.Listed = dataTypes.ReadNextBool(packetData); } - if ((actionBitset & (1ul << 4)) > 0) // Actions bit 4: update latency + if ((actionBitset & (1 << 4)) > 0) // Actions bit 4: update latency { int latency = dataTypes.ReadNextVarInt(packetData); handler.OnLatencyUpdate(playerUuid, latency); //Update latency; } - if ((actionBitset & (1ul << 5)) > 0) // Actions bit 5: update display name + if ((actionBitset & (1 << 5)) > 0) // Actions bit 5: update display name { if (dataTypes.ReadNextBool(packetData)) player.DisplayName = dataTypes.ReadNextString(packetData); @@ -1811,13 +1864,6 @@ namespace MinecraftClient.Protocol.Handlers long TimeOfday = dataTypes.ReadNextLong(packetData); handler.OnTimeUpdate(WorldAge, TimeOfday); break; - case PacketTypesIn.SystemChat: - string systemMessage = dataTypes.ReadNextString(packetData); - int msgType = dataTypes.ReadNextVarInt(packetData); - if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) - break; - handler.OnTextReceived(new(systemMessage, true, msgType, Guid.Empty, true)); - break; case PacketTypesIn.EntityTeleport: if (handler.GetEntityHandlingEnabled()) { @@ -2498,9 +2544,12 @@ namespace MinecraftClient.Protocol.Handlers try { - LastSeenMessageList.Acknowledgment? acknowledgment = - (protocolVersion >= MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = + (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = + (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); + List fields = new(); // Command: String @@ -2529,34 +2578,37 @@ namespace MinecraftClient.Protocol.Handlers foreach ((string argName, string message) in needSigned) { fields.AddRange(dataTypes.GetString(argName)); // Argument name: String - byte[] sign = (protocolVersion >= MC_1_19_2_Version) ? - playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment!.lastSeen) : - playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt - fields.AddRange(sign); // Signature: Byte Array + + byte[] sign; + if (protocolVersion == MC_1_19_Version) + sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); + else if (protocolVersion == MC_1_19_2_Version) + sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); + else // protocolVersion >= MC_1_19_3_Version + sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); + + if (protocolVersion <= MC_1_19_2_Version) + fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt + + fields.AddRange(sign); // Signature: Byte Array } } - if (protocolVersion < MC_1_19_3_Version) - { - // Signed Preview: Boolean (1.19.2) - fields.AddRange(dataTypes.GetBool(false)); - } - else - { - fields.AddRange(dataTypes.GetVarInt(messageCount)); // Message count (1.19.3) - } + if (protocolVersion <= MC_1_19_2_Version) + fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean if (protocolVersion == MC_1_19_2_Version) { // Message Acknowledgment (1.19.2) - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); } else if (protocolVersion >= MC_1_19_3_Version) { - // 1.19.3 - // Acknowledged: BitSet (no idea what is this) - fields.AddRange(new byte[3] { 0, 0, 0 }); + // message count + fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); + + // Acknowledged: BitSet + fields.AddRange(bitset_1_19_3); } SendPacket(PacketTypesOut.ChatCommand, fields); @@ -2575,7 +2627,7 @@ namespace MinecraftClient.Protocol.Handlers /// True if properly sent public bool SendChatMessage(string message, PlayerKeyPair? playerKeyPair) { - if (String.IsNullOrEmpty(message)) + if (string.IsNullOrEmpty(message)) return true; // Process Chat Command - 1.19 and above @@ -2622,8 +2674,8 @@ namespace MinecraftClient.Protocol.Handlers sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt); else if (protocolVersion == MC_1_19_2_Version) // 1.19.2 sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); - else // 1.19.3+ - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, handler.GetPlayerInfo(playerUuid)!.ChatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); + else // protocolVersion >= MC_1_19_3_Version + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); if (protocolVersion >= MC_1_19_3_Version) fields.AddRange(dataTypes.GetBool(true)); @@ -3317,28 +3369,6 @@ namespace MinecraftClient.Protocol.Handlers packet.AddRange(playerKeyPair.PublicKey.SignatureV2); SendPacket(PacketTypesOut.PlayerSession, packet); - - return true; - } - catch (SocketException) { return false; } - catch (System.IO.IOException) { return false; } - catch (ObjectDisposedException) { return false; } - } - else { return false; } - } - - public bool SendMessageAcknowledgment(int messageCount) - { - if (protocolVersion >= MC_1_19_3_Version) - { - try - { - List packet = new(); - - packet.AddRange(dataTypes.GetVarInt(messageCount)); - - SendPacket(PacketTypesOut.MessageAcknowledgment, packet); - return true; } catch (SocketException) { return false; } diff --git a/MinecraftClient/Protocol/Message/ChatMessage.cs b/MinecraftClient/Protocol/Message/ChatMessage.cs index 23a34be4..c2b74fc6 100644 --- a/MinecraftClient/Protocol/Message/ChatMessage.cs +++ b/MinecraftClient/Protocol/Message/ChatMessage.cs @@ -5,31 +5,31 @@ namespace MinecraftClient.Protocol.Message public class ChatMessage { // in 1.19 and above, isSignedChat = true - public readonly bool isSignedChat; + public bool isSignedChat; - public readonly string content; + public string content; - public readonly bool isJson; + public bool isJson, isSenderJson; // 0: chat (chat box), 1: system message (chat box), 2: game info (above hotbar), 3: say command, // 4: msg command, 5: team msg command, 6: emote command, 7: tellraw command - public readonly int chatTypeId; + public int chatTypeId; - public readonly Guid senderUUID; + public Guid senderUUID; - public readonly bool isSystemChat; + public bool isSystemChat; - public readonly string? unsignedContent; + public string? unsignedContent; - public readonly string? displayName; + public string? displayName; - public readonly string? teamName; + public string? teamName; - public readonly DateTime? timestamp; + public DateTime? timestamp; - public readonly byte[]? signature; + public byte[]? signature; - public readonly bool? isSignatureLegal; + public bool? isSignatureLegal; public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, string? unsignedContent, string displayName, string? teamName, long timestamp, byte[] signature, bool isSignatureLegal) { @@ -47,11 +47,12 @@ namespace MinecraftClient.Protocol.Message this.isSignatureLegal = isSignatureLegal; } - public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, bool isSystemChat = false) + public ChatMessage(string content, string? displayName, bool isJson, int chatType, Guid senderUUID, bool isSystemChat = false) { isSignedChat = isSystemChat; this.isSystemChat = isSystemChat; this.content = content; + this.displayName = displayName; this.isJson = isJson; chatTypeId = chatType; this.senderUUID = senderUUID; diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index c0eef1bc..06c316d1 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -28,6 +29,30 @@ namespace MinecraftClient.Protocol public static Dictionary? ChatId2Type; + public static void ReadChatType(Dictionary registryCodec) + { + Dictionary chatTypeDictionary = ChatId2Type ?? new(); + var chatTypeListNbt = (object[])(((Dictionary)registryCodec["minecraft:chat_type"])["value"]); + foreach (var (chatName, chatId) in from Dictionary chatTypeNbt in chatTypeListNbt + let chatName = (string)chatTypeNbt["name"] + let chatId = (int)chatTypeNbt["id"] + select (chatName, chatId)) + { + chatTypeDictionary[chatId] = chatName switch + { + "minecraft:chat" => MessageType.CHAT, + "minecraft:emote_command" => MessageType.EMOTE_COMMAND, + "minecraft:msg_command_incoming" => MessageType.MSG_COMMAND_INCOMING, + "minecraft:msg_command_outgoing" => MessageType.MSG_COMMAND_OUTGOING, + "minecraft:say_command" => MessageType.SAY_COMMAND, + "minecraft:team_msg_command_incoming" => MessageType.TEAM_MSG_COMMAND_INCOMING, + "minecraft:team_msg_command_outgoing" => MessageType.TEAM_MSG_COMMAND_OUTGOING, + _ => MessageType.CHAT, + }; + } + ChatId2Type = chatTypeDictionary; + } + /// /// The main function to convert text from MC 1.6+ JSON to MC 1.5.2 formatted text /// @@ -47,7 +72,7 @@ namespace MinecraftClient.Protocol /// Returns the translated text public static string ParseSignedChat(ChatMessage message, List? links = null) { - string sender = message.displayName!; + string sender = message.isSenderJson ? ParseText(message.displayName!) : message.displayName!; string content; if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) { @@ -66,7 +91,7 @@ namespace MinecraftClient.Protocol List usingData = new(); MessageType chatType; - if (message.isSystemChat) + if (message.chatTypeId == -1) chatType = MessageType.RAW_MSG; else if (!ChatId2Type!.TryGetValue(message.chatTypeId, out chatType)) chatType = MessageType.CHAT; @@ -119,7 +144,7 @@ namespace MinecraftClient.Protocol if (message.isSystemChat) { if (Config.Signature.MarkSystemMessage) - color = "§z §r "; // Custom color code §z : Background Gray + color = "§z▍§r"; // Custom color code §z : Background Gray } else { @@ -128,18 +153,18 @@ namespace MinecraftClient.Protocol if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) { if (Config.Signature.MarkModifiedMsg) - color = "§x §r "; // Custom color code §x : Background Yellow + color = "§x▍§r"; // Custom color code §x : Background Yellow } else { if (Config.Signature.MarkLegallySignedMsg) - color = "§y §r "; // Custom color code §y : Background Green + color = "§y▍§r"; // Custom color code §y : Background Green } } else { if (Config.Signature.MarkIllegallySignedMsg) - color = "§w §r "; // Custom color code §w : Background Red + color = "§w▍§r"; // Custom color code §w : Background Red } } return color + text; diff --git a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs index 528e3cee..a068c6af 100644 --- a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs +++ b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Security.Permissions; using static MinecraftClient.Protocol.Message.LastSeenMessageList; @@ -120,7 +121,7 @@ namespace MinecraftClient.Protocol.Message { // net.minecraft.network.message.LastSeenMessagesCollector#add(net.minecraft.network.message.MessageSignatureData, boolean) // net.minecraft.network.message.LastSeenMessagesCollector#add(net.minecraft.network.message.AcknowledgedMessage) - if (entry == lastEntry) + if (lastEntry != null && entry.signature.SequenceEqual(lastEntry.signature)) return false; lastEntry = entry; From 052060b23c1b33a470f698cd0959cbddafb31a21 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 02:41:03 +0800 Subject: [PATCH 24/44] Bug fix --- .../Protocol/Handlers/Protocol18.cs | 30 +++++++++---------- .../Protocol/Message/ChatMessage.cs | 2 +- MinecraftClient/Protocol/PlayerInfo.cs | 29 ++++++++++++++++-- .../Protocol/ProfileKey/PublicKey.cs | 4 +-- 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index c47b2d27..9ad6a9e2 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -670,29 +670,28 @@ namespace MinecraftClient.Protocol.Handlers } } - // Todo: verify message bool verifyResult; - if (!isOnlineMode) + if (!isOnlineMode || messageSignature == null) verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; else { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; + if (senderUUID == handler.GetUserUuid()) + verifyResult = true; else { - verifyResult = false; - //bool lastVerifyResult = player.IsMessageChainLegal(); - //verifyResult = player.VerifyMessage(message, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); - //if (lastVerifyResult && !verifyResult) - // log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + verifyResult = false; + verifyResult = player.VerifyMessage(message, senderUUID, player.ChatUuid, index, timestamp, salt, ref messageSignature, previousMessageSignatures); + } } } ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender() && messageSignature != null) + if (isOnlineMode && !chat.LacksSender() && verifyResult) Acknowledge(chat); handler.OnTextReceived(chat); } @@ -727,9 +726,8 @@ namespace MinecraftClient.Protocol.Handlers int messageType_ = dataTypes.ReadNextVarInt(packetData); string messageName = dataTypes.ReadNextString(packetData); string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - ChatMessage profilelessChat = new(message_, messageName, true, messageType_, Guid.Empty, true); + ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, Guid.Empty, true); profilelessChat.isSenderJson = true; - profilelessChat.teamName = targetName_; handler.OnTextReceived(profilelessChat); break; case PacketTypesIn.CombatEvent: @@ -2549,7 +2547,7 @@ namespace MinecraftClient.Protocol.Handlers (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); - + List fields = new(); // Command: String diff --git a/MinecraftClient/Protocol/Message/ChatMessage.cs b/MinecraftClient/Protocol/Message/ChatMessage.cs index c2b74fc6..09048f3f 100644 --- a/MinecraftClient/Protocol/Message/ChatMessage.cs +++ b/MinecraftClient/Protocol/Message/ChatMessage.cs @@ -31,7 +31,7 @@ namespace MinecraftClient.Protocol.Message public bool? isSignatureLegal; - public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, string? unsignedContent, string displayName, string? teamName, long timestamp, byte[] signature, bool isSignatureLegal) + public ChatMessage(string content, bool isJson, int chatType, Guid senderUUID, string? unsignedContent, string displayName, string? teamName, long timestamp, byte[]? signature, bool isSignatureLegal) { isSignedChat = true; isSystemChat = false; diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index 94899dc7..c1a7f24d 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -1,5 +1,9 @@ using System; +using System.Collections.Generic; +using System.Data.SqlTypes; using System.Linq; +using System.Text; +using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; @@ -28,6 +32,8 @@ namespace MinecraftClient.Protocol // For message signature + public int MessageIndex = -1; + public Guid ChatUuid = Guid.Empty; private PublicKey? PublicKey; @@ -131,7 +137,7 @@ namespace MinecraftClient.Protocol } /// - /// Verify message - 1.19.1 and above + /// Verify message - 1.19.1 and 1.19.2 /// /// Message content /// Timestamp @@ -169,7 +175,7 @@ namespace MinecraftClient.Protocol } /// - /// Verify message head - 1.19.1 and above + /// Verify message head - 1.19.1 and 1.19.2 /// /// Preceding message signature /// Message signature @@ -197,5 +203,24 @@ namespace MinecraftClient.Protocol return res; } + + /// + /// Verify message - 1.19.3 and above + /// + /// Message content + /// Timestamp + /// Salt + /// Message signature + /// Preceding message signature + /// LastSeenMessages + /// Is this message chain vaild + public bool VerifyMessage(string message, Guid playerUuid, Guid chatUuid, int messageIndex, long timestamp, long salt, ref byte[] signature, Tuple[] previousMessageSignatures) + { + if (PublicKey == null || IsKeyExpired()) + return false; + + // net.minecraft.server.network.ServerPlayNetworkHandler#validateMessage + return true; + } } } diff --git a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs index 429c3962..e8f57c79 100644 --- a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs @@ -58,7 +58,7 @@ namespace MinecraftClient.Protocol.Keys } /// - /// Verify message - 1.19.1 and above + /// Verify message - 1.19.1 and 1.19.2 /// /// Message content /// Sender uuid @@ -79,7 +79,7 @@ namespace MinecraftClient.Protocol.Keys } /// - /// Verify message head - 1.19.1 and above + /// Verify message head - 1.19.1 and 1.19.2 /// /// Message body hash /// Message signature From e0294f1bebcc018431a0b663618d74360414b420 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 15:55:40 +0800 Subject: [PATCH 25/44] 1.19.3 PlayerRemove & Explosion packet --- .../Protocol/Handlers/Protocol18.cs | 3160 +++++++++-------- .../Protocol/Handlers/Protocol18Terrain.cs | 2 - 2 files changed, 1587 insertions(+), 1575 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 9ad6a9e2..c71936b3 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -329,1649 +329,1662 @@ namespace MinecraftClient.Protocol.Handlers /// TRUE if the packet was processed, FALSE if ignored or unknown internal bool HandlePacket(int packetID, Queue packetData) { +#if Release try { - if (login_phase) +#endif + if (login_phase) + { + switch (packetID) //Packet IDs are different while logging in { - switch (packetID) //Packet IDs are different while logging in - { - case 0x03: - if (protocolVersion >= MC_1_8_Version) - compression_treshold = dataTypes.ReadNextVarInt(packetData); - break; - case 0x04: - int messageId = dataTypes.ReadNextVarInt(packetData); - string channel = dataTypes.ReadNextString(packetData); - List responseData = new(); - bool understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); - SendLoginPluginResponse(messageId, understood, responseData.ToArray()); - return understood; - default: - return false; //Ignored packet - } + case 0x03: + if (protocolVersion >= MC_1_8_Version) + compression_treshold = dataTypes.ReadNextVarInt(packetData); + break; + case 0x04: + int messageId = dataTypes.ReadNextVarInt(packetData); + string channel = dataTypes.ReadNextString(packetData); + List responseData = new(); + bool understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); + SendLoginPluginResponse(messageId, understood, responseData.ToArray()); + return understood; + default: + return false; //Ignored packet } - // Regular in-game packets - else switch (packetPalette.GetIncommingTypeById(packetID)) - { - case PacketTypesIn.KeepAlive: - SendPacket(PacketTypesOut.KeepAlive, packetData); - handler.OnServerKeepAlive(); - break; - case PacketTypesIn.Ping: - SendPacket(PacketTypesOut.Pong, packetData); - break; - case PacketTypesIn.JoinGame: - handler.OnGameJoined(); - int playerEntityID = dataTypes.ReadNextInt(packetData); - handler.OnReceivePlayerEntityID(playerEntityID); + } + // Regular in-game packets + else switch (packetPalette.GetIncommingTypeById(packetID)) + { + case PacketTypesIn.KeepAlive: + SendPacket(PacketTypesOut.KeepAlive, packetData); + handler.OnServerKeepAlive(); + break; + case PacketTypesIn.Ping: + SendPacket(PacketTypesOut.Pong, packetData); + break; + case PacketTypesIn.JoinGame: + handler.OnGameJoined(); + int playerEntityID = dataTypes.ReadNextInt(packetData); + handler.OnReceivePlayerEntityID(playerEntityID); - if (protocolVersion >= MC_1_16_2_Version) - dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above + if (protocolVersion >= MC_1_16_2_Version) + dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above - handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); + handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 and above - int worldCount = dataTypes.ReadNextVarInt(packetData); // Dimension Count (World Count) - 1.16 and above - for (int i = 0; i < worldCount; i++) - dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above - var registryCodec = dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above - if (protocolVersion >= MC_1_19_Version) - ChatParser.ReadChatType(registryCodec); - if (handler.GetTerrainEnabled()) - World.StoreDimensionList(registryCodec); - } - - // Current dimension - // String: 1.19 and above - // NBT Tag Compound: [1.16.2 to 1.18.2] - // String identifier: 1.16 and 1.16.1 - // varInt: [1.9.1 to 1.15.2] - // byte: below 1.9.1 - string? dimensionTypeName = null; - Dictionary? dimensionType = null; - if (protocolVersion >= MC_1_16_Version) - { - if (protocolVersion >= MC_1_19_Version) - dimensionTypeName = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - else if (protocolVersion >= MC_1_16_2_Version) - dimensionType = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound - else - dataTypes.ReadNextString(packetData); - currentDimension = 0; - } - else if (protocolVersion >= MC_1_9_1_Version) - currentDimension = dataTypes.ReadNextInt(packetData); - else - currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); - - if (protocolVersion < MC_1_14_Version) - dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - - if (protocolVersion >= MC_1_16_Version) - { - string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) - { - World.StoreOneDimension(dimensionName, dimensionType!); - World.SetDimension(dimensionName); - } - else if (protocolVersion >= MC_1_19_Version) - { - World.SetDimension(dimensionTypeName!); - } - } - } - - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above - if (protocolVersion >= MC_1_16_2_Version) - dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above - else - dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below - if (protocolVersion < MC_1_16_Version) - dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below - if (protocolVersion >= MC_1_14_Version) - dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above - if (protocolVersion >= MC_1_18_1_Version) - dataTypes.ReadNextVarInt(packetData); // Simulation Distance - 1.18 and above - if (protocolVersion >= MC_1_8_Version) - dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above - dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above - } + if (protocolVersion >= MC_1_16_Version) + { + dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 and above + int worldCount = dataTypes.ReadNextVarInt(packetData); // Dimension Count (World Count) - 1.16 and above + for (int i = 0; i < worldCount; i++) + dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above + var registryCodec = dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above if (protocolVersion >= MC_1_19_Version) - { - bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location - if (hasDeathLocation) - { - dataTypes.SkipNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - } + ChatParser.ReadChatType(registryCodec); + if (handler.GetTerrainEnabled()) + World.StoreDimensionList(registryCodec); + } - break; - case PacketTypesIn.DeclareCommands: + // Current dimension + // String: 1.19 and above + // NBT Tag Compound: [1.16.2 to 1.18.2] + // String identifier: 1.16 and 1.16.1 + // varInt: [1.9.1 to 1.15.2] + // byte: below 1.9.1 + string? dimensionTypeName = null; + Dictionary? dimensionType = null; + if (protocolVersion >= MC_1_16_Version) + { if (protocolVersion >= MC_1_19_Version) - DeclareCommands.Read(dataTypes, packetData); - break; - case PacketTypesIn.ChatMessage: - int messageType = 0; - - if (protocolVersion <= MC_1_18_2_Version) // 1.18 and bellow - { - string message = dataTypes.ReadNextString(packetData); - - Guid senderUUID; - if (protocolVersion >= MC_1_8_Version) - { - //Hide system messages or xp bar messages? - messageType = dataTypes.ReadNextByte(packetData); - if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) - || (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages)) - break; - - if (protocolVersion >= MC_1_16_5_Version) - senderUUID = dataTypes.ReadNextUUID(packetData); - else senderUUID = Guid.Empty; - } - else - senderUUID = Guid.Empty; - - handler.OnTextReceived(new(message, null, true, messageType, senderUUID)); - } - else if (protocolVersion == MC_1_19_Version) // 1.19 - { - string signedChat = dataTypes.ReadNextString(packetData); - - bool hasUnsignedChatContent = dataTypes.ReadNextBool(packetData); - string? unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; - - messageType = dataTypes.ReadNextVarInt(packetData); - if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) - || (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages)) - break; - - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - string senderDisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - - bool hasSenderTeamName = dataTypes.ReadNextBool(packetData); - string? senderTeamName = hasSenderTeamName ? ChatParser.ParseText(dataTypes.ReadNextString(packetData)) : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - - long salt = dataTypes.ReadNextLong(packetData); - - byte[] messageSignature = dataTypes.ReadNextByteArray(packetData); - - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, ref messageSignature); - } - - ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - handler.OnTextReceived(chat); - } - else if (protocolVersion == MC_1_19_2_Version) - { - // 1.19.1 - 1.19.2 - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - - string signedChat = dataTypes.ReadNextString(packetData); - string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); - LastSeenMessageList.AcknowledgedMessage[] lastSeenMessageList = new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; - for (int i = 0; i < lastSeenMessageListLen; ++i) - { - Guid user = dataTypes.ReadNextUUID(packetData); - byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); - lastSeenMessageList[i] = new(user, lastSignature, true); - } - LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); - - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - if (filterEnum == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - Dictionary chatInfo = Json.ParseJson(chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); - } - } - - ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender()) - Acknowledge(chat); - handler.OnTextReceived(chat); - } - else if (protocolVersion >= MC_1_19_3_Version) - { - // 1.19.3+ - // Header section - // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - int index = dataTypes.ReadNextVarInt(packetData); - // Signature is fixed size of 256 bytes - byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; - - // Body - // net.minecraft.network.message.MessageBody.Serialized#write - string message = dataTypes.ReadNextString(packetData); - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - // Previous Messages - // net.minecraft.network.message.LastSeenMessageList.Indexed#write - // net.minecraft.network.message.MessageSignatureData.Indexed#write - int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); - Tuple[] previousMessageSignatures = new Tuple[totalPreviousMessages]; - for (int i = 0; i < totalPreviousMessages; i++) - { - // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf - int messageId = dataTypes.ReadNextVarInt(packetData) - 1; - if (messageId == -1) - previousMessageSignatures[i] = new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256)); - else - previousMessageSignatures[i] = new Tuple(messageId, null); - } - - // Other - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - - if (filterType == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - // Network Target - // net.minecraft.network.message.MessageType.Serialized#write - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - - Dictionary chatInfo = Json.ParseJson(targetName ?? chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - bool verifyResult; - if (!isOnlineMode || messageSignature == null) - verifyResult = false; - else - { - if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - verifyResult = false; - verifyResult = player.VerifyMessage(message, senderUUID, player.ChatUuid, index, timestamp, salt, ref messageSignature, previousMessageSignatures); - } - } - } - - ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender() && verifyResult) - Acknowledge(chat); - handler.OnTextReceived(chat); - } - break; - case PacketTypesIn.SystemChat: - string systemMessage = dataTypes.ReadNextString(packetData); - if (protocolVersion >= MC_1_19_3_Version) - { - bool isOverlay = dataTypes.ReadNextBool(packetData); - if (isOverlay) - { - if (!Config.Main.Advanced.ShowXPBarMessages) - break; - } - else - { - if (!Config.Main.Advanced.ShowSystemMessages) - break; - } - handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); - } + dimensionTypeName = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + else if (protocolVersion >= MC_1_16_2_Version) + dimensionType = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound else - { - int msgType = dataTypes.ReadNextVarInt(packetData); - if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) - break; - handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); - } - break; - case PacketTypesIn.ProfilelessChatMessage: - string message_ = dataTypes.ReadNextString(packetData); - int messageType_ = dataTypes.ReadNextVarInt(packetData); - string messageName = dataTypes.ReadNextString(packetData); - string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, Guid.Empty, true); - profilelessChat.isSenderJson = true; - handler.OnTextReceived(profilelessChat); - break; - case PacketTypesIn.CombatEvent: - // 1.8 - 1.16.5 - if (protocolVersion >= MC_1_8_Version && protocolVersion <= MC_1_16_5_Version) - { - CombatEventType eventType = (CombatEventType)dataTypes.ReadNextVarInt(packetData); + dataTypes.ReadNextString(packetData); + currentDimension = 0; + } + else if (protocolVersion >= MC_1_9_1_Version) + currentDimension = dataTypes.ReadNextInt(packetData); + else + currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); - if (eventType == CombatEventType.EntityDead) - { - dataTypes.SkipNextVarInt(packetData); + if (protocolVersion < MC_1_14_Version) + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - handler.OnPlayerKilled( - dataTypes.ReadNextInt(packetData), - ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - ); - } - } - - break; - case PacketTypesIn.DeathCombatEvent: - dataTypes.SkipNextVarInt(packetData); - - handler.OnPlayerKilled( - dataTypes.ReadNextInt(packetData), - ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - ); - - break; - case PacketTypesIn.MessageHeader: // 1.19.2 only - if (protocolVersion == MC_1_19_2_Version) - { - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - byte[] bodyDigest = dataTypes.ReadNextByteArray(packetData); - - bool verifyResult; - - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - - if (player == null || !player.IsMessageChainLegal()) - verifyResult = false; - else - { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessageHead(ref precedingSignature, ref headerSignature, ref bodyDigest); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, player.Name)); - } - } - } - - break; - case PacketTypesIn.Respawn: - string? dimensionTypeNameRespawn = null; - Dictionary? dimensionTypeRespawn = null; - if (protocolVersion >= MC_1_16_Version) - { - if (protocolVersion >= MC_1_19_Version) - dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - else if (protocolVersion >= MC_1_16_2_Version) - dimensionTypeRespawn = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound - else - dataTypes.ReadNextString(packetData); - currentDimension = 0; - } - else - { // 1.15 and below - currentDimension = dataTypes.ReadNextInt(packetData); - } - - if (protocolVersion >= MC_1_16_Version) - { - string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) - { - World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); - World.SetDimension(dimensionName); - } - else if (protocolVersion >= MC_1_19_Version) - { - World.SetDimension(dimensionTypeNameRespawn!); - } - } - } - - if (protocolVersion < MC_1_14_Version) - dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above - dataTypes.ReadNextByte(packetData); // Gamemode - if (protocolVersion >= MC_1_16_Version) - dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above - if (protocolVersion < MC_1_16_Version) - dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above - dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above - dataTypes.ReadNextBool(packetData); // Copy metadata - 1.16 and above - } - if (protocolVersion >= MC_1_19_Version) - { - bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location - if (hasDeathLocation) - { - dataTypes.ReadNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - } - handler.OnRespawn(); - break; - case PacketTypesIn.PlayerPositionAndLook: - { - // These always need to be read, since we need the field after them for teleport confirm - double x = dataTypes.ReadNextDouble(packetData); - double y = dataTypes.ReadNextDouble(packetData); - double z = dataTypes.ReadNextDouble(packetData); - Location location = new(x, y, z); - float yaw = dataTypes.ReadNextFloat(packetData); - float pitch = dataTypes.ReadNextFloat(packetData); - byte locMask = dataTypes.ReadNextByte(packetData); - - // entity handling require player pos for distance calculating - if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) - { - if (protocolVersion >= MC_1_8_Version) - { - Location current = handler.GetCurrentLocation(); - location.X = (locMask & 1 << 0) != 0 ? current.X + x : x; - location.Y = (locMask & 1 << 1) != 0 ? current.Y + y : y; - location.Z = (locMask & 1 << 2) != 0 ? current.Z + z : z; - } - } - - if (protocolVersion >= MC_1_9_Version) - { - int teleportID = dataTypes.ReadNextVarInt(packetData); - - if (teleportID < 0) { yaw = LastYaw; pitch = LastPitch; } - else { LastYaw = yaw; LastPitch = pitch; } - - handler.UpdateLocation(location, yaw, pitch); - - // Teleport confirm packet - SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportID)); - if (Config.Main.Advanced.TemporaryFixBadpacket) - { - SendLocationUpdate(location, true, yaw, pitch, true); - if (teleportID == 1) - SendLocationUpdate(location, true, yaw, pitch, true); - } - } - else - { - handler.UpdateLocation(location, yaw, pitch); - LastYaw = yaw; LastPitch = pitch; - } - - if (protocolVersion >= MC_1_17_Version) - dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 and above - } - break; - case PacketTypesIn.ChunkData: + if (protocolVersion >= MC_1_16_Version) + { + string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above if (handler.GetTerrainEnabled()) { - Interlocked.Increment(ref handler.GetWorld().chunkCnt); - Interlocked.Increment(ref handler.GetWorld().chunkLoadNotCompleted); - - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - if (protocolVersion >= MC_1_17_Version) + if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) { - ulong[]? verticalStripBitmask = null; - - if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) - verticalStripBitmask = dataTypes.ReadNextULongArray(packetData); // Bit Mask Length and Primary Bit Mask - - dataTypes.ReadNextNbt(packetData); // Heightmaps - - if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) - { - int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - for (int i = 0; i < biomesLength; i++) - dataTypes.SkipNextVarInt(packetData); // Biomes - } - - int dataSize = dataTypes.ReadNextVarInt(packetData); // Size - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - - // Block Entity data: ignored - // Light data: ignored + World.StoreOneDimension(dimensionName, dimensionType!); + World.SetDimension(dimensionName); } + else if (protocolVersion >= MC_1_19_Version) + { + World.SetDimension(dimensionTypeName!); + } + } + } + + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above + if (protocolVersion >= MC_1_16_2_Version) + dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above + else + dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below + if (protocolVersion < MC_1_16_Version) + dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below + if (protocolVersion >= MC_1_14_Version) + dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above + if (protocolVersion >= MC_1_18_1_Version) + dataTypes.ReadNextVarInt(packetData); // Simulation Distance - 1.18 and above + if (protocolVersion >= MC_1_8_Version) + dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above + if (protocolVersion >= MC_1_16_Version) + { + dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above + dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above + } + if (protocolVersion >= MC_1_19_Version) + { + bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location + if (hasDeathLocation) + { + dataTypes.SkipNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location + } + } + + break; + case PacketTypesIn.DeclareCommands: + if (protocolVersion >= MC_1_19_Version) + DeclareCommands.Read(dataTypes, packetData); + break; + case PacketTypesIn.ChatMessage: + int messageType = 0; + + if (protocolVersion <= MC_1_18_2_Version) // 1.18 and bellow + { + string message = dataTypes.ReadNextString(packetData); + + Guid senderUUID; + if (protocolVersion >= MC_1_8_Version) + { + //Hide system messages or xp bar messages? + messageType = dataTypes.ReadNextByte(packetData); + if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) + || (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages)) + break; + + if (protocolVersion >= MC_1_16_5_Version) + senderUUID = dataTypes.ReadNextUUID(packetData); + else senderUUID = Guid.Empty; + } + else + senderUUID = Guid.Empty; + + handler.OnTextReceived(new(message, null, true, messageType, senderUUID)); + } + else if (protocolVersion == MC_1_19_Version) // 1.19 + { + string signedChat = dataTypes.ReadNextString(packetData); + + bool hasUnsignedChatContent = dataTypes.ReadNextBool(packetData); + string? unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; + + messageType = dataTypes.ReadNextVarInt(packetData); + if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) + || (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages)) + break; + + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + string senderDisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + + bool hasSenderTeamName = dataTypes.ReadNextBool(packetData); + string? senderTeamName = hasSenderTeamName ? ChatParser.ParseText(dataTypes.ReadNextString(packetData)) : null; + + long timestamp = dataTypes.ReadNextLong(packetData); + + long salt = dataTypes.ReadNextLong(packetData); + + byte[] messageSignature = dataTypes.ReadNextByteArray(packetData); + + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, ref messageSignature); + } + + ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); + handler.OnTextReceived(chat); + } + else if (protocolVersion == MC_1_19_2_Version) + { + // 1.19.1 - 1.19.2 + byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); + + string signedChat = dataTypes.ReadNextString(packetData); + string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); + + int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); + LastSeenMessageList.AcknowledgedMessage[] lastSeenMessageList = new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; + for (int i = 0; i < lastSeenMessageListLen; ++i) + { + Guid user = dataTypes.ReadNextUUID(packetData); + byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); + lastSeenMessageList[i] = new(user, lastSignature, true); + } + LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); + + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + if (filterEnum == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + Dictionary chatInfo = Json.ParseJson(chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; else { - bool chunksContinuous = dataTypes.ReadNextBool(packetData); - if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) - dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only - ushort chunkMask = protocolVersion >= MC_1_9_Version - ? (ushort)dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextUShort(packetData); - if (protocolVersion < MC_1_8_Version) - { - ushort addBitmap = dataTypes.ReadNextUShort(packetData); - int compressedDataSize = dataTypes.ReadNextInt(packetData); - byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); - byte[] decompressed = ZlibUtils.Decompress(compressed); + bool lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); + } + } - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed)); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - } + ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender()) + Acknowledge(chat); + handler.OnTextReceived(chat); + } + else if (protocolVersion >= MC_1_19_3_Version) + { + // 1.19.3+ + // Header section + // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + int index = dataTypes.ReadNextVarInt(packetData); + // Signature is fixed size of 256 bytes + byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; + + // Body + // net.minecraft.network.message.MessageBody.Serialized#write + string message = dataTypes.ReadNextString(packetData); + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); + + // Previous Messages + // net.minecraft.network.message.LastSeenMessageList.Indexed#write + // net.minecraft.network.message.MessageSignatureData.Indexed#write + int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); + Tuple[] previousMessageSignatures = new Tuple[totalPreviousMessages]; + for (int i = 0; i < totalPreviousMessages; i++) + { + // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf + int messageId = dataTypes.ReadNextVarInt(packetData) - 1; + if (messageId == -1) + previousMessageSignatures[i] = new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256)); + else + previousMessageSignatures[i] = new Tuple(messageId, null); + } + + // Other + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + + if (filterType == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + // Network Target + // net.minecraft.network.message.MessageType.Serialized#write + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + + Dictionary chatInfo = Json.ParseJson(targetName ?? chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode || messageSignature == null) + verifyResult = false; + else + { + if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; else { - if (protocolVersion >= MC_1_14_Version) - dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above - int biomesLength = 0; - if (protocolVersion >= MC_1_16_2_Version) - if (chunksContinuous) - biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - 1.16.2 and above - if (protocolVersion >= MC_1_15_Version && chunksContinuous) - { - if (protocolVersion >= MC_1_16_2_Version) - { - for (int i = 0; i < biomesLength; i++) - { - // Biomes - 1.16.2 and above - // Don't use ReadNextVarInt because it cost too much time - dataTypes.SkipNextVarInt(packetData); - } - } - else dataTypes.DropData(1024 * 4, packetData); // Biomes - 1.15 and above - } - int dataSize = dataTypes.ReadNextVarInt(packetData); - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + verifyResult = false; + verifyResult = player.VerifyMessage(message, senderUUID, player.ChatUuid, index, timestamp, salt, ref messageSignature, previousMessageSignatures); } } } - break; - case PacketTypesIn.MapData: - if (protocolVersion < MC_1_8_Version) - break; - int mapid = dataTypes.ReadNextVarInt(packetData); - byte scale = dataTypes.ReadNextByte(packetData); - - - // 1.9 + - bool trackingPosition = true; - - // 1.14+ - bool locked = false; - - // 1.17+ (locked and trackingPosition switched places) - if (protocolVersion >= MC_1_17_Version) + ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender() && verifyResult) + Acknowledge(chat); + handler.OnTextReceived(chat); + } + break; + case PacketTypesIn.SystemChat: + string systemMessage = dataTypes.ReadNextString(packetData); + if (protocolVersion >= MC_1_19_3_Version) + { + bool isOverlay = dataTypes.ReadNextBool(packetData); + if (isOverlay) { - if (protocolVersion >= MC_1_14_Version) - locked = dataTypes.ReadNextBool(packetData); - - if (protocolVersion >= MC_1_9_Version) - trackingPosition = dataTypes.ReadNextBool(packetData); + if (!Config.Main.Advanced.ShowXPBarMessages) + break; } else { - if (protocolVersion >= MC_1_9_Version) - trackingPosition = dataTypes.ReadNextBool(packetData); - - if (protocolVersion >= MC_1_14_Version) - locked = dataTypes.ReadNextBool(packetData); + if (!Config.Main.Advanced.ShowSystemMessages) + break; } + handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); + } + else + { + int msgType = dataTypes.ReadNextVarInt(packetData); + if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) + break; + handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); + } + break; + case PacketTypesIn.ProfilelessChatMessage: + string message_ = dataTypes.ReadNextString(packetData); + int messageType_ = dataTypes.ReadNextVarInt(packetData); + string messageName = dataTypes.ReadNextString(packetData); + string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, Guid.Empty, true); + profilelessChat.isSenderJson = true; + handler.OnTextReceived(profilelessChat); + break; + case PacketTypesIn.CombatEvent: + // 1.8 - 1.16.5 + if (protocolVersion >= MC_1_8_Version && protocolVersion <= MC_1_16_5_Version) + { + CombatEventType eventType = (CombatEventType)dataTypes.ReadNextVarInt(packetData); - int iconcount = 0; - List icons = new(); - - // 1,9 + = needs tracking position to be true to get the icons - if (protocolVersion <= MC_1_16_5_Version || trackingPosition) + if (eventType == CombatEventType.EntityDead) { - iconcount = dataTypes.ReadNextVarInt(packetData); + dataTypes.SkipNextVarInt(packetData); - for (int i = 0; i < iconcount; i++) + handler.OnPlayerKilled( + dataTypes.ReadNextInt(packetData), + ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + ); + } + } + + break; + case PacketTypesIn.DeathCombatEvent: + dataTypes.SkipNextVarInt(packetData); + + handler.OnPlayerKilled( + dataTypes.ReadNextInt(packetData), + ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + ); + + break; + case PacketTypesIn.MessageHeader: // 1.19.2 only + if (protocolVersion == MC_1_19_2_Version) + { + byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); + byte[] bodyDigest = dataTypes.ReadNextByteArray(packetData); + + bool verifyResult; + + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else { - MapIcon mapIcon = new(); + bool lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessageHead(ref precedingSignature, ref headerSignature, ref bodyDigest); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, player.Name)); + } + } + } - // 1.8 - 1.13 - if (protocolVersion < MC_1_13_2_Version) - { - byte directionAndtype = dataTypes.ReadNextByte(packetData); - byte direction, type; + break; + case PacketTypesIn.Respawn: + string? dimensionTypeNameRespawn = null; + Dictionary? dimensionTypeRespawn = null; + if (protocolVersion >= MC_1_16_Version) + { + if (protocolVersion >= MC_1_19_Version) + dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + else if (protocolVersion >= MC_1_16_2_Version) + dimensionTypeRespawn = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound + else + dataTypes.ReadNextString(packetData); + currentDimension = 0; + } + else + { // 1.15 and below + currentDimension = dataTypes.ReadNextInt(packetData); + } - // 1.12.2+ - if (protocolVersion >= MC_1_12_2_Version) - { - direction = (byte)(directionAndtype & 0xF); - type = (byte)((directionAndtype >> 4) & 0xF); - } - else // 1.8 - 1.12 - { - direction = (byte)((directionAndtype >> 4) & 0xF); - type = (byte)(directionAndtype & 0xF); - } + if (protocolVersion >= MC_1_16_Version) + { + string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above + if (handler.GetTerrainEnabled()) + { + if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) + { + World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); + World.SetDimension(dimensionName); + } + else if (protocolVersion >= MC_1_19_Version) + { + World.SetDimension(dimensionTypeNameRespawn!); + } + } + } - mapIcon.Type = (MapIconType)type; - mapIcon.Direction = direction; - } + if (protocolVersion < MC_1_14_Version) + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above + dataTypes.ReadNextByte(packetData); // Gamemode + if (protocolVersion >= MC_1_16_Version) + dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above + if (protocolVersion < MC_1_16_Version) + dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below + if (protocolVersion >= MC_1_16_Version) + { + dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above + dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above + dataTypes.ReadNextBool(packetData); // Copy metadata - 1.16 and above + } + if (protocolVersion >= MC_1_19_Version) + { + bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location + if (hasDeathLocation) + { + dataTypes.ReadNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location + } + } + handler.OnRespawn(); + break; + case PacketTypesIn.PlayerPositionAndLook: + { + // These always need to be read, since we need the field after them for teleport confirm + double x = dataTypes.ReadNextDouble(packetData); + double y = dataTypes.ReadNextDouble(packetData); + double z = dataTypes.ReadNextDouble(packetData); + Location location = new(x, y, z); + float yaw = dataTypes.ReadNextFloat(packetData); + float pitch = dataTypes.ReadNextFloat(packetData); + byte locMask = dataTypes.ReadNextByte(packetData); - // 1.13.2+ - if (protocolVersion >= MC_1_13_2_Version) - mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); - - mapIcon.X = dataTypes.ReadNextByte(packetData); - mapIcon.Z = dataTypes.ReadNextByte(packetData); - - // 1.13.2+ - if (protocolVersion >= MC_1_13_2_Version) - { - mapIcon.Direction = dataTypes.ReadNextByte(packetData); - - if (dataTypes.ReadNextBool(packetData)) // Has Display Name? - mapIcon.DisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - - icons.Add(mapIcon); + // entity handling require player pos for distance calculating + if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) + { + if (protocolVersion >= MC_1_8_Version) + { + Location current = handler.GetCurrentLocation(); + location.X = (locMask & 1 << 0) != 0 ? current.X + x : x; + location.Y = (locMask & 1 << 1) != 0 ? current.Y + y : y; + location.Z = (locMask & 1 << 2) != 0 ? current.Z + z : z; } } - byte columnsUpdated = dataTypes.ReadNextByte(packetData); // width - byte rowsUpdated = 0; // height - byte mapCoulmnX = 0; - byte mapRowZ = 0; - byte[]? colors = null; - - if (columnsUpdated > 0) + if (protocolVersion >= MC_1_9_Version) { - rowsUpdated = dataTypes.ReadNextByte(packetData); // height - mapCoulmnX = dataTypes.ReadNextByte(packetData); - mapRowZ = dataTypes.ReadNextByte(packetData); - colors = dataTypes.ReadNextByteArray(packetData); + int teleportID = dataTypes.ReadNextVarInt(packetData); + + if (teleportID < 0) { yaw = LastYaw; pitch = LastPitch; } + else { LastYaw = yaw; LastPitch = pitch; } + + handler.UpdateLocation(location, yaw, pitch); + + // Teleport confirm packet + SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportID)); + if (Config.Main.Advanced.TemporaryFixBadpacket) + { + SendLocationUpdate(location, true, yaw, pitch, true); + if (teleportID == 1) + SendLocationUpdate(location, true, yaw, pitch, true); + } + } + else + { + handler.UpdateLocation(location, yaw, pitch); + LastYaw = yaw; LastPitch = pitch; } - handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapRowZ, colors); + if (protocolVersion >= MC_1_17_Version) + dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 and above + } + break; + case PacketTypesIn.ChunkData: + if (handler.GetTerrainEnabled()) + { + Interlocked.Increment(ref handler.GetWorld().chunkCnt); + Interlocked.Increment(ref handler.GetWorld().chunkLoadNotCompleted); + + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); + if (protocolVersion >= MC_1_17_Version) + { + ulong[]? verticalStripBitmask = null; + + if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) + verticalStripBitmask = dataTypes.ReadNextULongArray(packetData); // Bit Mask Length and Primary Bit Mask + + dataTypes.ReadNextNbt(packetData); // Heightmaps + + if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) + { + int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length + for (int i = 0; i < biomesLength; i++) + dataTypes.SkipNextVarInt(packetData); // Biomes + } + + int dataSize = dataTypes.ReadNextVarInt(packetData); // Size + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + + // Block Entity data: ignored + // Light data: ignored + } + else + { + bool chunksContinuous = dataTypes.ReadNextBool(packetData); + if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) + dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only + ushort chunkMask = protocolVersion >= MC_1_9_Version + ? (ushort)dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextUShort(packetData); + if (protocolVersion < MC_1_8_Version) + { + ushort addBitmap = dataTypes.ReadNextUShort(packetData); + int compressedDataSize = dataTypes.ReadNextInt(packetData); + byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); + byte[] decompressed = ZlibUtils.Decompress(compressed); + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed)); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + else + { + if (protocolVersion >= MC_1_14_Version) + dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above + int biomesLength = 0; + if (protocolVersion >= MC_1_16_2_Version) + if (chunksContinuous) + biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - 1.16.2 and above + if (protocolVersion >= MC_1_15_Version && chunksContinuous) + { + if (protocolVersion >= MC_1_16_2_Version) + { + for (int i = 0; i < biomesLength; i++) + { + // Biomes - 1.16.2 and above + // Don't use ReadNextVarInt because it cost too much time + dataTypes.SkipNextVarInt(packetData); + } + } + else dataTypes.DropData(1024 * 4, packetData); // Biomes - 1.15 and above + } + int dataSize = dataTypes.ReadNextVarInt(packetData); + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + } + } + break; + case PacketTypesIn.MapData: + if (protocolVersion < MC_1_8_Version) break; - case PacketTypesIn.TradeList: - if ((protocolVersion >= MC_1_14_Version) && (handler.GetInventoryEnabled())) + + int mapid = dataTypes.ReadNextVarInt(packetData); + byte scale = dataTypes.ReadNextByte(packetData); + + + // 1.9 + + bool trackingPosition = true; + + // 1.14+ + bool locked = false; + + // 1.17+ (locked and trackingPosition switched places) + if (protocolVersion >= MC_1_17_Version) + { + if (protocolVersion >= MC_1_14_Version) + locked = dataTypes.ReadNextBool(packetData); + + if (protocolVersion >= MC_1_9_Version) + trackingPosition = dataTypes.ReadNextBool(packetData); + } + else + { + if (protocolVersion >= MC_1_9_Version) + trackingPosition = dataTypes.ReadNextBool(packetData); + + if (protocolVersion >= MC_1_14_Version) + locked = dataTypes.ReadNextBool(packetData); + } + + int iconcount = 0; + List icons = new(); + + // 1,9 + = needs tracking position to be true to get the icons + if (protocolVersion <= MC_1_16_5_Version || trackingPosition) + { + iconcount = dataTypes.ReadNextVarInt(packetData); + + for (int i = 0; i < iconcount; i++) + { + MapIcon mapIcon = new(); + + // 1.8 - 1.13 + if (protocolVersion < MC_1_13_2_Version) + { + byte directionAndtype = dataTypes.ReadNextByte(packetData); + byte direction, type; + + // 1.12.2+ + if (protocolVersion >= MC_1_12_2_Version) + { + direction = (byte)(directionAndtype & 0xF); + type = (byte)((directionAndtype >> 4) & 0xF); + } + else // 1.8 - 1.12 + { + direction = (byte)((directionAndtype >> 4) & 0xF); + type = (byte)(directionAndtype & 0xF); + } + + mapIcon.Type = (MapIconType)type; + mapIcon.Direction = direction; + } + + // 1.13.2+ + if (protocolVersion >= MC_1_13_2_Version) + mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); + + mapIcon.X = dataTypes.ReadNextByte(packetData); + mapIcon.Z = dataTypes.ReadNextByte(packetData); + + // 1.13.2+ + if (protocolVersion >= MC_1_13_2_Version) + { + mapIcon.Direction = dataTypes.ReadNextByte(packetData); + + if (dataTypes.ReadNextBool(packetData)) // Has Display Name? + mapIcon.DisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + + icons.Add(mapIcon); + } + } + + byte columnsUpdated = dataTypes.ReadNextByte(packetData); // width + byte rowsUpdated = 0; // height + byte mapCoulmnX = 0; + byte mapRowZ = 0; + byte[]? colors = null; + + if (columnsUpdated > 0) + { + rowsUpdated = dataTypes.ReadNextByte(packetData); // height + mapCoulmnX = dataTypes.ReadNextByte(packetData); + mapRowZ = dataTypes.ReadNextByte(packetData); + colors = dataTypes.ReadNextByteArray(packetData); + } + + handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapRowZ, colors); + break; + case PacketTypesIn.TradeList: + if ((protocolVersion >= MC_1_14_Version) && (handler.GetInventoryEnabled())) + { + // MC 1.14 or greater + int windowID = dataTypes.ReadNextVarInt(packetData); + int size = dataTypes.ReadNextByte(packetData); + List trades = new(); + for (int tradeId = 0; tradeId < size; tradeId++) + { + VillagerTrade trade = dataTypes.ReadNextTrade(packetData, itemPalette); + trades.Add(trade); + } + VillagerInfo villagerInfo = new() + { + Level = dataTypes.ReadNextVarInt(packetData), + Experience = dataTypes.ReadNextVarInt(packetData), + IsRegularVillager = dataTypes.ReadNextBool(packetData), + CanRestock = dataTypes.ReadNextBool(packetData) + }; + handler.OnTradeList(windowID, trades, villagerInfo); + } + break; + case PacketTypesIn.Title: + if (protocolVersion >= MC_1_8_Version) + { + int action2 = dataTypes.ReadNextVarInt(packetData); + string titletext = String.Empty; + string subtitletext = String.Empty; + string actionbartext = String.Empty; + string json = String.Empty; + int fadein = -1; + int stay = -1; + int fadeout = -1; + if (protocolVersion >= MC_1_10_Version) + { + if (action2 == 0) + { + json = titletext; + titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 1) + { + json = subtitletext; + subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 2) + { + json = actionbartext; + actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 3) + { + fadein = dataTypes.ReadNextInt(packetData); + stay = dataTypes.ReadNextInt(packetData); + fadeout = dataTypes.ReadNextInt(packetData); + } + } + else + { + if (action2 == 0) + { + json = titletext; + titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 1) + { + json = subtitletext; + subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 2) + { + fadein = dataTypes.ReadNextInt(packetData); + stay = dataTypes.ReadNextInt(packetData); + fadeout = dataTypes.ReadNextInt(packetData); + } + } + handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json); + } + break; + case PacketTypesIn.MultiBlockChange: + if (handler.GetTerrainEnabled()) + { + if (protocolVersion >= MC_1_16_2_Version) + { + long chunkSection = dataTypes.ReadNextLong(packetData); + int sectionX = (int)(chunkSection >> 42); + int sectionY = (int)((chunkSection << 44) >> 44); + int sectionZ = (int)((chunkSection << 22) >> 42); + dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update) + int blocksSize = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < blocksSize; i++) + { + 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 block = new((ushort)blockId); + int blockX = (sectionX * 16) + localX; + int blockY = (sectionY * 16) + localY; + int blockZ = (sectionZ * 16) + localZ; + + Location location = new(blockX, blockY, blockZ); + + handler.OnBlockChange(location, block); + } + } + else + { + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); + int recordCount = protocolVersion < MC_1_8_Version + ? (int)dataTypes.ReadNextShort(packetData) + : dataTypes.ReadNextVarInt(packetData); + + for (int i = 0; i < recordCount; i++) + { + byte locationXZ; + ushort blockIdMeta; + int blockY; + + if (protocolVersion < MC_1_8_Version) + { + blockIdMeta = dataTypes.ReadNextUShort(packetData); + blockY = (ushort)dataTypes.ReadNextByte(packetData); + locationXZ = dataTypes.ReadNextByte(packetData); + } + else + { + locationXZ = dataTypes.ReadNextByte(packetData); + blockY = (ushort)dataTypes.ReadNextByte(packetData); + blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData); + } + + int blockX = locationXZ >> 4; + int blockZ = locationXZ & 0x0F; + + Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); + Block block = new(blockIdMeta); + handler.OnBlockChange(location, block); + } + } + } + break; + case PacketTypesIn.ServerData: + string motd = "-"; + bool hasMotd = dataTypes.ReadNextBool(packetData); + if (hasMotd) + motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + + string iconBase64 = "-"; + bool hasIcon = dataTypes.ReadNextBool(packetData); + if (hasIcon) + iconBase64 = dataTypes.ReadNextString(packetData); + + bool previewsChat = false; + if (protocolVersion < MC_1_19_3_Version) + previewsChat = dataTypes.ReadNextBool(packetData); + + handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); + break; + case PacketTypesIn.BlockChange: + if (handler.GetTerrainEnabled()) + { + if (protocolVersion < MC_1_8_Version) + { + int blockX = dataTypes.ReadNextInt(packetData); + int blockY = dataTypes.ReadNextByte(packetData); + int blockZ = dataTypes.ReadNextInt(packetData); + short blockId = (short)dataTypes.ReadNextVarInt(packetData); + byte blockMeta = dataTypes.ReadNextByte(packetData); + + Location location = new(blockX, blockY, blockZ); + Block block = new(blockId, blockMeta); + handler.OnBlockChange(location, block); + } + else + { + Location location = dataTypes.ReadNextLocation(packetData); + Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); + handler.OnBlockChange(location, block); + } + } + break; + case PacketTypesIn.SetDisplayChatPreview: + bool previewsChatSetting = dataTypes.ReadNextBool(packetData); + handler.OnChatPreviewSettingUpdate(previewsChatSetting); + break; + case PacketTypesIn.ChatSuggestions: + break; + case PacketTypesIn.MapChunkBulk: + if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) + { + int chunkCount; + bool hasSkyLight; + Queue chunkData = packetData; + + //Read global fields + if (protocolVersion < MC_1_8_Version) + { + chunkCount = dataTypes.ReadNextShort(packetData); + int compressedDataSize = dataTypes.ReadNextInt(packetData); + hasSkyLight = dataTypes.ReadNextBool(packetData); + byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); + byte[] decompressed = ZlibUtils.Decompress(compressed); + chunkData = new Queue(decompressed); + } + else + { + hasSkyLight = dataTypes.ReadNextBool(packetData); + chunkCount = dataTypes.ReadNextVarInt(packetData); + } + + //Read chunk records + int[] chunkXs = new int[chunkCount]; + int[] chunkZs = new int[chunkCount]; + ushort[] chunkMasks = new ushort[chunkCount]; + ushort[] addBitmaps = new ushort[chunkCount]; + for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + { + chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData); + addBitmaps[chunkColumnNo] = protocolVersion < MC_1_8_Version + ? dataTypes.ReadNextUShort(packetData) + : (ushort)0; + } + + //Process chunk records + for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + { + pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + + } + break; + case PacketTypesIn.UnloadChunk: + if (protocolVersion >= MC_1_9_Version && handler.GetTerrainEnabled()) + { + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); + + // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. + // Since chunks that have not been loaded are not recorded, this may result + // in loading chunks that should be unloaded and inaccurate statistics. + if (handler.GetWorld()[chunkX, chunkZ] != null) + Interlocked.Decrement(ref handler.GetWorld().chunkCnt); + + handler.GetWorld()[chunkX, chunkZ] = null; + } + break; + case PacketTypesIn.ChangeGameState: + if (protocolVersion >= MC_1_15_2_Version) + { + byte reason = dataTypes.ReadNextByte(packetData); + float state = dataTypes.ReadNextFloat(packetData); + + handler.OnGameEvent(reason, state); + } + break; + case PacketTypesIn.PlayerInfo: + if (protocolVersion >= MC_1_19_3_Version) + { + byte actionBitset = dataTypes.ReadNextByte(packetData); + int numberOfActions = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < numberOfActions; i++) + { + Guid playerUuid = dataTypes.ReadNextUUID(packetData); + + if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player + { + string name = dataTypes.ReadNextString(packetData); + int numberOfProperties = dataTypes.ReadNextVarInt(packetData); + for (int j = 0; j < numberOfProperties; ++j) + { + dataTypes.SkipNextString(packetData); + dataTypes.SkipNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + handler.OnPlayerJoin(new(name, playerUuid)); + } + + PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; + if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat + { + bool hasSignatureData = dataTypes.ReadNextBool(packetData); + if (hasSignatureData) + { + Guid chatUuid = dataTypes.ReadNextUUID(packetData); + long publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); + byte[] encodedPublicKey = dataTypes.ReadNextByteArray(packetData); + byte[] publicKeySignature = dataTypes.ReadNextByteArray(packetData); + player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, publicKeySignature); + } + else + { + player.ClearPublicKey(); + } + } + if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode + { + handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); + } + if ((actionBitset & (1 << 3)) > 0) // Actions bit 3: update listed + { + player.Listed = dataTypes.ReadNextBool(packetData); + } + if ((actionBitset & (1 << 4)) > 0) // Actions bit 4: update latency + { + int latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(playerUuid, latency); //Update latency; + } + if ((actionBitset & (1 << 5)) > 0) // Actions bit 5: update display name + { + if (dataTypes.ReadNextBool(packetData)) + player.DisplayName = dataTypes.ReadNextString(packetData); + else + player.DisplayName = null; + } + } + } + else if (protocolVersion >= MC_1_8_Version) + { + int action = dataTypes.ReadNextVarInt(packetData); // Action Name + int numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players + + for (int i = 0; i < numberOfPlayers; i++) + { + Guid uuid = dataTypes.ReadNextUUID(packetData); // Player UUID + + switch (action) + { + case 0x00: //Player Join (Add player since 1.19) + string name = dataTypes.ReadNextString(packetData); // Player name + int propNum = dataTypes.ReadNextVarInt(packetData); // Number of properties in the following array + + // Property: Tuple[]? properties = useProperty ? + new Tuple[propNum] : null; + for (int p = 0; p < propNum; p++) + { + string propertyName = dataTypes.ReadNextString(packetData); // Name: String (32767) + string val = dataTypes.ReadNextString(packetData); // Value: String (32767) + string? propertySignature = null; + if (dataTypes.ReadNextBool(packetData)) // Is Signed + propertySignature = dataTypes.ReadNextString(packetData); // Signature: String (32767) + if (useProperty) + properties![p] = new(propertyName, val, propertySignature); + } +#pragma warning restore CS0162 // Unreachable code detected + + int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode + handler.OnGamemodeUpdate(uuid, gameMode); + + int ping = dataTypes.ReadNextVarInt(packetData); // Ping + + string? displayName = null; + if (dataTypes.ReadNextBool(packetData)) // Has display name + displayName = dataTypes.ReadNextString(packetData); // Display name + + // 1.19 Additions + long? keyExpiration = null; + byte[]? publicKey = null, signature = null; + if (protocolVersion >= MC_1_19_Version) + { + if (dataTypes.ReadNextBool(packetData)) // Has Sig Data (if true, red the following fields) + { + keyExpiration = dataTypes.ReadNextLong(packetData); // Timestamp + + int publicKeyLength = dataTypes.ReadNextVarInt(packetData); // Public Key Length + if (publicKeyLength > 0) + publicKey = dataTypes.ReadData(publicKeyLength, packetData); // Public key + + int signatureLength = dataTypes.ReadNextVarInt(packetData); // Signature Length + if (signatureLength > 0) + signature = dataTypes.ReadData(signatureLength, packetData); // Public key + } + } + + handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature)); + break; + case 0x01: //Update gamemode + handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData)); + break; + case 0x02: //Update latency + int latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(uuid, latency); //Update latency; + break; + case 0x03: //Update display name + if (dataTypes.ReadNextBool(packetData)) + { + PlayerInfo? player = handler.GetPlayerInfo(uuid); + if (player != null) + player.DisplayName = dataTypes.ReadNextString(packetData); + else + dataTypes.SkipNextString(packetData); + } + break; + case 0x04: //Player Leave + handler.OnPlayerLeave(uuid); + break; + default: + //Unknown player list item type + break; + } + } + } + else //MC 1.7.X does not provide UUID in tab-list updates + { + string name = dataTypes.ReadNextString(packetData); + bool online = dataTypes.ReadNextBool(packetData); + short ping = dataTypes.ReadNextShort(packetData); + Guid FakeUUID = new(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); + if (online) + handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); + else handler.OnPlayerLeave(FakeUUID); + } + break; + case PacketTypesIn.PlayerRemove: + int numberOfLeavePlayers = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < numberOfLeavePlayers; ++i) + { + Guid playerUuid = dataTypes.ReadNextUUID(packetData); + handler.OnPlayerLeave(playerUuid); + } + break; + case PacketTypesIn.TabComplete: + if (protocolVersion >= MC_1_13_Version) + { + autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); + dataTypes.ReadNextVarInt(packetData); // Start of text to replace + dataTypes.ReadNextVarInt(packetData); // Length of text to replace + } + + int autocomplete_count = dataTypes.ReadNextVarInt(packetData); + autocomplete_result.Clear(); + + for (int i = 0; i < autocomplete_count; i++) + { + autocomplete_result.Add(dataTypes.ReadNextString(packetData)); + if (protocolVersion >= MC_1_13_Version) + { + // Skip optional tooltip for each tab-complete result + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + } + + autocomplete_received = true; + break; + case PacketTypesIn.PluginMessage: + String channel = dataTypes.ReadNextString(packetData); + // Length is unneeded as the whole remaining packetData is the entire payload of the packet. + if (protocolVersion < MC_1_8_Version) + pForge.ReadNextVarShort(packetData); + handler.OnPluginChannelMessage(channel, packetData.ToArray()); + return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); + case PacketTypesIn.Disconnect: + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); + return false; + case PacketTypesIn.SetCompression: + if (protocolVersion >= MC_1_8_Version && protocolVersion < MC_1_9_Version) + compression_treshold = dataTypes.ReadNextVarInt(packetData); + break; + case PacketTypesIn.OpenWindow: + if (handler.GetInventoryEnabled()) + { + if (protocolVersion < MC_1_14_Version) + { + // MC 1.13 or lower + byte windowID = dataTypes.ReadNextByte(packetData); + string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper(); + ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type); + string title = dataTypes.ReadNextString(packetData); + byte slots = dataTypes.ReadNextByte(packetData); + Container inventory = new(windowID, inventoryType, ChatParser.ParseText(title)); + handler.OnInventoryOpen(windowID, inventory); + } + else { // MC 1.14 or greater int windowID = dataTypes.ReadNextVarInt(packetData); - int size = dataTypes.ReadNextByte(packetData); - List trades = new(); - for (int tradeId = 0; tradeId < size; tradeId++) - { - VillagerTrade trade = dataTypes.ReadNextTrade(packetData, itemPalette); - trades.Add(trade); - } - VillagerInfo villagerInfo = new() - { - Level = dataTypes.ReadNextVarInt(packetData), - Experience = dataTypes.ReadNextVarInt(packetData), - IsRegularVillager = dataTypes.ReadNextBool(packetData), - CanRestock = dataTypes.ReadNextBool(packetData) - }; - handler.OnTradeList(windowID, trades, villagerInfo); + int windowType = dataTypes.ReadNextVarInt(packetData); + string title = dataTypes.ReadNextString(packetData); + Container inventory = new(windowID, windowType, ChatParser.ParseText(title)); + handler.OnInventoryOpen(windowID, inventory); } - break; - case PacketTypesIn.Title: - if (protocolVersion >= MC_1_8_Version) + } + break; + case PacketTypesIn.CloseWindow: + if (handler.GetInventoryEnabled()) + { + byte windowID = dataTypes.ReadNextByte(packetData); + lock (window_actions) { window_actions[windowID] = 0; } + handler.OnInventoryClose(windowID); + } + break; + case PacketTypesIn.WindowItems: + if (handler.GetInventoryEnabled()) + { + byte windowId = dataTypes.ReadNextByte(packetData); + int stateId = -1; + int elements = 0; + + if (protocolVersion >= MC_1_17_1_Version) { - int action2 = dataTypes.ReadNextVarInt(packetData); - string titletext = String.Empty; - string subtitletext = String.Empty; - string actionbartext = String.Empty; - string json = String.Empty; - int fadein = -1; - int stay = -1; - int fadeout = -1; - if (protocolVersion >= MC_1_10_Version) - { - if (action2 == 0) - { - json = titletext; - titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 1) - { - json = subtitletext; - subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 2) - { - json = actionbartext; - actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 3) - { - fadein = dataTypes.ReadNextInt(packetData); - stay = dataTypes.ReadNextInt(packetData); - fadeout = dataTypes.ReadNextInt(packetData); - } - } - else - { - if (action2 == 0) - { - json = titletext; - titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 1) - { - json = subtitletext; - subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 2) - { - fadein = dataTypes.ReadNextInt(packetData); - stay = dataTypes.ReadNextInt(packetData); - fadeout = dataTypes.ReadNextInt(packetData); - } - } - handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json); + // State ID and Elements as VarInt - 1.17.1 and above + stateId = dataTypes.ReadNextVarInt(packetData); + elements = dataTypes.ReadNextVarInt(packetData); } - break; - case PacketTypesIn.MultiBlockChange: - if (handler.GetTerrainEnabled()) + else { - if (protocolVersion >= MC_1_16_2_Version) - { - long chunkSection = dataTypes.ReadNextLong(packetData); - int sectionX = (int)(chunkSection >> 42); - int sectionY = (int)((chunkSection << 44) >> 44); - int sectionZ = (int)((chunkSection << 22) >> 42); - dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update) - int blocksSize = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < blocksSize; i++) - { - 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 block = new((ushort)blockId); - int blockX = (sectionX * 16) + localX; - int blockY = (sectionY * 16) + localY; - int blockZ = (sectionZ * 16) + localZ; - - Location location = new(blockX, blockY, blockZ); - - handler.OnBlockChange(location, block); - } - } - else - { - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - int recordCount = protocolVersion < MC_1_8_Version - ? (int)dataTypes.ReadNextShort(packetData) - : dataTypes.ReadNextVarInt(packetData); - - for (int i = 0; i < recordCount; i++) - { - byte locationXZ; - ushort blockIdMeta; - int blockY; - - if (protocolVersion < MC_1_8_Version) - { - blockIdMeta = dataTypes.ReadNextUShort(packetData); - blockY = (ushort)dataTypes.ReadNextByte(packetData); - locationXZ = dataTypes.ReadNextByte(packetData); - } - else - { - locationXZ = dataTypes.ReadNextByte(packetData); - blockY = (ushort)dataTypes.ReadNextByte(packetData); - blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData); - } - - int blockX = locationXZ >> 4; - int blockZ = locationXZ & 0x0F; - - Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); - Block block = new(blockIdMeta); - handler.OnBlockChange(location, block); - } - } - } - break; - case PacketTypesIn.ServerData: - string motd = "-"; - bool hasMotd = dataTypes.ReadNextBool(packetData); - if (hasMotd) - motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - - string iconBase64 = "-"; - bool hasIcon = dataTypes.ReadNextBool(packetData); - if (hasIcon) - iconBase64 = dataTypes.ReadNextString(packetData); - - - bool previewsChat = false; - - if (protocolVersion < MC_1_19_3_Version) - dataTypes.ReadNextBool(packetData); - - handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); - break; - case PacketTypesIn.BlockChange: - if (handler.GetTerrainEnabled()) - { - if (protocolVersion < MC_1_8_Version) - { - int blockX = dataTypes.ReadNextInt(packetData); - int blockY = dataTypes.ReadNextByte(packetData); - int blockZ = dataTypes.ReadNextInt(packetData); - short blockId = (short)dataTypes.ReadNextVarInt(packetData); - byte blockMeta = dataTypes.ReadNextByte(packetData); - - Location location = new(blockX, blockY, blockZ); - Block block = new(blockId, blockMeta); - handler.OnBlockChange(location, block); - } - else - { - Location location = dataTypes.ReadNextLocation(packetData); - Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); - handler.OnBlockChange(location, block); - } - } - break; - case PacketTypesIn.SetDisplayChatPreview: - bool previewsChatSetting = dataTypes.ReadNextBool(packetData); - handler.OnChatPreviewSettingUpdate(previewsChatSetting); - break; - case PacketTypesIn.ChatSuggestions: - break; - case PacketTypesIn.MapChunkBulk: - if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) - { - int chunkCount; - bool hasSkyLight; - Queue chunkData = packetData; - - //Read global fields - if (protocolVersion < MC_1_8_Version) - { - chunkCount = dataTypes.ReadNextShort(packetData); - int compressedDataSize = dataTypes.ReadNextInt(packetData); - hasSkyLight = dataTypes.ReadNextBool(packetData); - byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); - byte[] decompressed = ZlibUtils.Decompress(compressed); - chunkData = new Queue(decompressed); - } - else - { - hasSkyLight = dataTypes.ReadNextBool(packetData); - chunkCount = dataTypes.ReadNextVarInt(packetData); - } - - //Read chunk records - int[] chunkXs = new int[chunkCount]; - int[] chunkZs = new int[chunkCount]; - ushort[] chunkMasks = new ushort[chunkCount]; - ushort[] addBitmaps = new ushort[chunkCount]; - for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) - { - chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); - chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); - chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData); - addBitmaps[chunkColumnNo] = protocolVersion < MC_1_8_Version - ? dataTypes.ReadNextUShort(packetData) - : (ushort)0; - } - - //Process chunk records - for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) - { - pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - } - - } - break; - case PacketTypesIn.UnloadChunk: - if (protocolVersion >= MC_1_9_Version && handler.GetTerrainEnabled()) - { - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - - // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. - // Since chunks that have not been loaded are not recorded, this may result - // in loading chunks that should be unloaded and inaccurate statistics. - if (handler.GetWorld()[chunkX, chunkZ] != null) - Interlocked.Decrement(ref handler.GetWorld().chunkCnt); - - handler.GetWorld()[chunkX, chunkZ] = null; - } - break; - case PacketTypesIn.ChangeGameState: - if (protocolVersion >= MC_1_15_2_Version) - { - byte reason = dataTypes.ReadNextByte(packetData); - float state = dataTypes.ReadNextFloat(packetData); - - handler.OnGameEvent(reason, state); - } - break; - case PacketTypesIn.PlayerInfo: - if (protocolVersion >= MC_1_19_3_Version) - { - byte actionBitset = dataTypes.ReadNextByte(packetData); - int numberOfActions = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < numberOfActions; i++) - { - Guid playerUuid = dataTypes.ReadNextUUID(packetData); - - if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player - { - string name = dataTypes.ReadNextString(packetData); - int numberOfProperties = dataTypes.ReadNextVarInt(packetData); - for (int j = 0; j < numberOfProperties; ++j) - { - dataTypes.SkipNextString(packetData); - dataTypes.SkipNextString(packetData); - if (dataTypes.ReadNextBool(packetData)) - dataTypes.SkipNextString(packetData); - } - handler.OnPlayerJoin(new(name, playerUuid)); - } - - PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; - if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat - { - bool hasSignatureData = dataTypes.ReadNextBool(packetData); - if (hasSignatureData) - { - Guid chatUuid = dataTypes.ReadNextUUID(packetData); - long publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); - byte[] encodedPublicKey = dataTypes.ReadNextByteArray(packetData); - byte[] publicKeySignature = dataTypes.ReadNextByteArray(packetData); - player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, publicKeySignature); - } - else - { - player.ClearPublicKey(); - } - } - if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode - { - handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); - } - if ((actionBitset & (1 << 3)) > 0) // Actions bit 3: update listed - { - player.Listed = dataTypes.ReadNextBool(packetData); - } - if ((actionBitset & (1 << 4)) > 0) // Actions bit 4: update latency - { - int latency = dataTypes.ReadNextVarInt(packetData); - handler.OnLatencyUpdate(playerUuid, latency); //Update latency; - } - if ((actionBitset & (1 << 5)) > 0) // Actions bit 5: update display name - { - if (dataTypes.ReadNextBool(packetData)) - player.DisplayName = dataTypes.ReadNextString(packetData); - else - player.DisplayName = null; - } - } - } - else if (protocolVersion >= MC_1_8_Version) - { - int action = dataTypes.ReadNextVarInt(packetData); // Action Name - int numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players - - for (int i = 0; i < numberOfPlayers; i++) - { - Guid uuid = dataTypes.ReadNextUUID(packetData); // Player UUID - - switch (action) - { - case 0x00: //Player Join (Add player since 1.19) - string name = dataTypes.ReadNextString(packetData); // Player name - int propNum = dataTypes.ReadNextVarInt(packetData); // Number of properties in the following array - - // Property: Tuple[]? properties = useProperty ? - new Tuple[propNum] : null; - for (int p = 0; p < propNum; p++) - { - string propertyName = dataTypes.ReadNextString(packetData); // Name: String (32767) - string val = dataTypes.ReadNextString(packetData); // Value: String (32767) - string? propertySignature = null; - if (dataTypes.ReadNextBool(packetData)) // Is Signed - propertySignature = dataTypes.ReadNextString(packetData); // Signature: String (32767) - if (useProperty) - properties![p] = new(propertyName, val, propertySignature); - } -#pragma warning restore CS0162 // Unreachable code detected - - int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode - handler.OnGamemodeUpdate(uuid, gameMode); - - int ping = dataTypes.ReadNextVarInt(packetData); // Ping - - string? displayName = null; - if (dataTypes.ReadNextBool(packetData)) // Has display name - displayName = dataTypes.ReadNextString(packetData); // Display name - - // 1.19 Additions - long? keyExpiration = null; - byte[]? publicKey = null, signature = null; - if (protocolVersion >= MC_1_19_Version) - { - if (dataTypes.ReadNextBool(packetData)) // Has Sig Data (if true, red the following fields) - { - keyExpiration = dataTypes.ReadNextLong(packetData); // Timestamp - - int publicKeyLength = dataTypes.ReadNextVarInt(packetData); // Public Key Length - if (publicKeyLength > 0) - publicKey = dataTypes.ReadData(publicKeyLength, packetData); // Public key - - int signatureLength = dataTypes.ReadNextVarInt(packetData); // Signature Length - if (signatureLength > 0) - signature = dataTypes.ReadData(signatureLength, packetData); // Public key - } - } - - handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature)); - break; - case 0x01: //Update gamemode - handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData)); - break; - case 0x02: //Update latency - int latency = dataTypes.ReadNextVarInt(packetData); - handler.OnLatencyUpdate(uuid, latency); //Update latency; - break; - case 0x03: //Update display name - if (dataTypes.ReadNextBool(packetData)) - { - PlayerInfo? player = handler.GetPlayerInfo(uuid); - if (player != null) - player.DisplayName = dataTypes.ReadNextString(packetData); - else - dataTypes.SkipNextString(packetData); - } - break; - case 0x04: //Player Leave - handler.OnPlayerLeave(uuid); - break; - default: - //Unknown player list item type - break; - } - } - } - else //MC 1.7.X does not provide UUID in tab-list updates - { - string name = dataTypes.ReadNextString(packetData); - bool online = dataTypes.ReadNextBool(packetData); - short ping = dataTypes.ReadNextShort(packetData); - Guid FakeUUID = new(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) - handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); - else handler.OnPlayerLeave(FakeUUID); - } - break; - case PacketTypesIn.TabComplete: - if (protocolVersion >= MC_1_13_Version) - { - autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); - dataTypes.ReadNextVarInt(packetData); // Start of text to replace - dataTypes.ReadNextVarInt(packetData); // Length of text to replace + // Elements as Short - 1.17.0 and below + dataTypes.ReadNextShort(packetData); } - int autocomplete_count = dataTypes.ReadNextVarInt(packetData); - autocomplete_result.Clear(); - - for (int i = 0; i < autocomplete_count; i++) + Dictionary inventorySlots = new(); + for (int slotId = 0; slotId < elements; slotId++) { - autocomplete_result.Add(dataTypes.ReadNextString(packetData)); - if (protocolVersion >= MC_1_13_Version) - { - // Skip optional tooltip for each tab-complete result - if (dataTypes.ReadNextBool(packetData)) - dataTypes.SkipNextString(packetData); - } - } - - autocomplete_received = true; - break; - case PacketTypesIn.PluginMessage: - String channel = dataTypes.ReadNextString(packetData); - // Length is unneeded as the whole remaining packetData is the entire payload of the packet. - if (protocolVersion < MC_1_8_Version) - pForge.ReadNextVarShort(packetData); - handler.OnPluginChannelMessage(channel, packetData.ToArray()); - return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); - case PacketTypesIn.Disconnect: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); - return false; - case PacketTypesIn.SetCompression: - if (protocolVersion >= MC_1_8_Version && protocolVersion < MC_1_9_Version) - compression_treshold = dataTypes.ReadNextVarInt(packetData); - break; - case PacketTypesIn.OpenWindow: - if (handler.GetInventoryEnabled()) - { - if (protocolVersion < MC_1_14_Version) - { - // MC 1.13 or lower - byte windowID = dataTypes.ReadNextByte(packetData); - string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper(); - ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type); - string title = dataTypes.ReadNextString(packetData); - byte slots = dataTypes.ReadNextByte(packetData); - Container inventory = new(windowID, inventoryType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); - } - else - { - // MC 1.14 or greater - int windowID = dataTypes.ReadNextVarInt(packetData); - int windowType = dataTypes.ReadNextVarInt(packetData); - string title = dataTypes.ReadNextString(packetData); - Container inventory = new(windowID, windowType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); - } - } - break; - case PacketTypesIn.CloseWindow: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - lock (window_actions) { window_actions[windowID] = 0; } - handler.OnInventoryClose(windowID); - } - break; - case PacketTypesIn.WindowItems: - if (handler.GetInventoryEnabled()) - { - byte windowId = dataTypes.ReadNextByte(packetData); - int stateId = -1; - int elements = 0; - - if (protocolVersion >= MC_1_17_1_Version) - { - // State ID and Elements as VarInt - 1.17.1 and above - stateId = dataTypes.ReadNextVarInt(packetData); - elements = dataTypes.ReadNextVarInt(packetData); - } - else - { - // Elements as Short - 1.17.0 and below - dataTypes.ReadNextShort(packetData); - } - - Dictionary inventorySlots = new(); - for (int slotId = 0; slotId < elements; slotId++) - { - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - if (item != null) - inventorySlots[slotId] = item; - } - - if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above - dataTypes.ReadNextItemSlot(packetData, itemPalette); - - handler.OnWindowItems(windowId, inventorySlots, stateId); - } - break; - case PacketTypesIn.WindowProperty: - byte containerId = dataTypes.ReadNextByte(packetData); - short propertyId = dataTypes.ReadNextShort(packetData); - short propertyValue = dataTypes.ReadNextShort(packetData); - - handler.OnWindowProperties(containerId, propertyId, propertyValue); - break; - case PacketTypesIn.SetSlot: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - int stateId = -1; - if (protocolVersion >= MC_1_17_1_Version) - stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above - short slotID = dataTypes.ReadNextShort(packetData); Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnSetSlot(windowID, slotID, item, stateId); + if (item != null) + inventorySlots[slotId] = item; } + + if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above + dataTypes.ReadNextItemSlot(packetData, itemPalette); + + handler.OnWindowItems(windowId, inventorySlots, stateId); + } + break; + case PacketTypesIn.WindowProperty: + byte containerId = dataTypes.ReadNextByte(packetData); + short propertyId = dataTypes.ReadNextShort(packetData); + short propertyValue = dataTypes.ReadNextShort(packetData); + + handler.OnWindowProperties(containerId, propertyId, propertyValue); + break; + case PacketTypesIn.SetSlot: + if (handler.GetInventoryEnabled()) + { + byte windowID = dataTypes.ReadNextByte(packetData); + int stateId = -1; + if (protocolVersion >= MC_1_17_1_Version) + stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above + short slotID = dataTypes.ReadNextShort(packetData); + Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnSetSlot(windowID, slotID, item, stateId); + } + break; + case PacketTypesIn.WindowConfirmation: + if (handler.GetInventoryEnabled()) + { + byte windowID = dataTypes.ReadNextByte(packetData); + short actionID = dataTypes.ReadNextShort(packetData); + bool accepted = dataTypes.ReadNextBool(packetData); + if (!accepted) + SendWindowConfirmation(windowID, actionID, true); + } + break; + case PacketTypesIn.ResourcePackSend: + string url = dataTypes.ReadNextString(packetData); + string hash = dataTypes.ReadNextString(packetData); + bool forced = true; // Assume forced for MC 1.16 and below + if (protocolVersion >= MC_1_17_Version) + { + forced = dataTypes.ReadNextBool(packetData); + bool hasPromptMessage = dataTypes.ReadNextBool(packetData); // Has Prompt Message (Boolean) - 1.17 and above + if (hasPromptMessage) + dataTypes.SkipNextString(packetData); // Prompt Message (Optional Chat) - 1.17 and above + } + // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) + if (!url.StartsWith("http") && hash.Length != 40) // Some server may have null hash value break; - case PacketTypesIn.WindowConfirmation: - if (handler.GetInventoryEnabled()) + //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory + byte[] responseHeader = Array.Empty(); + if (protocolVersion < MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses + responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); + SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); //Accepted pack + SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); //Successfully loaded + break; + case PacketTypesIn.SpawnEntity: + if (handler.GetEntityHandlingEnabled()) + { + Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); + handler.OnSpawnEntity(entity); + } + break; + case PacketTypesIn.EntityEquipment: + if (handler.GetEntityHandlingEnabled()) + { + int entityid = dataTypes.ReadNextVarInt(packetData); + if (protocolVersion >= MC_1_16_Version) { - byte windowID = dataTypes.ReadNextByte(packetData); - short actionID = dataTypes.ReadNextShort(packetData); - bool accepted = dataTypes.ReadNextBool(packetData); - if (!accepted) - SendWindowConfirmation(windowID, actionID, true); - } - break; - case PacketTypesIn.ResourcePackSend: - string url = dataTypes.ReadNextString(packetData); - string hash = dataTypes.ReadNextString(packetData); - bool forced = true; // Assume forced for MC 1.16 and below - if (protocolVersion >= MC_1_17_Version) - { - forced = dataTypes.ReadNextBool(packetData); - bool hasPromptMessage = dataTypes.ReadNextBool(packetData); // Has Prompt Message (Boolean) - 1.17 and above - if (hasPromptMessage) - dataTypes.SkipNextString(packetData); // Prompt Message (Optional Chat) - 1.17 and above - } - // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) - if (!url.StartsWith("http") && hash.Length != 40) // Some server may have null hash value - break; - //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory - byte[] responseHeader = Array.Empty(); - if (protocolVersion < MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses - responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); - SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); //Accepted pack - SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); //Successfully loaded - break; - case PacketTypesIn.SpawnEntity: - if (handler.GetEntityHandlingEnabled()) - { - Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); - handler.OnSpawnEntity(entity); - } - break; - case PacketTypesIn.EntityEquipment: - if (handler.GetEntityHandlingEnabled()) - { - int entityid = dataTypes.ReadNextVarInt(packetData); - if (protocolVersion >= MC_1_16_Version) + bool hasNext; + do { - bool hasNext; - do - { - byte bitsData = dataTypes.ReadNextByte(packetData); - // Top bit set if another entry follows, and otherwise unset if this is the last item in the array - hasNext = (bitsData >> 7) == 1; - int slot2 = bitsData >> 1; - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); - } while (hasNext); - } - else - { - int slot2 = dataTypes.ReadNextVarInt(packetData); + byte bitsData = dataTypes.ReadNextByte(packetData); + // Top bit set if another entry follows, and otherwise unset if this is the last item in the array + hasNext = (bitsData >> 7) == 1; + int slot2 = bitsData >> 1; Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); handler.OnEntityEquipment(entityid, slot2, item); - } - } - break; - case PacketTypesIn.SpawnLivingEntity: - if (handler.GetEntityHandlingEnabled()) - { - Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true); - // packet before 1.15 has metadata at the end - // this is not handled in dataTypes.ReadNextEntity() - // we are simply ignoring leftover data in packet - handler.OnSpawnEntity(entity); - } - break; - case PacketTypesIn.SpawnPlayer: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Guid UUID = dataTypes.ReadNextUUID(packetData); - double X = dataTypes.ReadNextDouble(packetData); - double Y = dataTypes.ReadNextDouble(packetData); - double Z = dataTypes.ReadNextDouble(packetData); - byte Yaw = dataTypes.ReadNextByte(packetData); - byte Pitch = dataTypes.ReadNextByte(packetData); - - Location EntityLocation = new(X, Y, Z); - - handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); - } - break; - case PacketTypesIn.EntityEffect: - if (handler.GetEntityHandlingEnabled()) - { - int entityid = dataTypes.ReadNextVarInt(packetData); - Inventory.Effects effect = Effects.Speed; - int effectId = protocolVersion >= MC_1_18_2_Version ? - dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextByte(packetData); - if (Enum.TryParse(effectId.ToString(), out effect)) - { - int amplifier = dataTypes.ReadNextByte(packetData); - int duration = dataTypes.ReadNextVarInt(packetData); - byte flags = dataTypes.ReadNextByte(packetData); - - bool hasFactorData = false; - Dictionary? factorCodec = null; - - if (protocolVersion >= MC_1_19_Version) - { - hasFactorData = dataTypes.ReadNextBool(packetData); - if (hasFactorData) - factorCodec = dataTypes.ReadNextNbt(packetData); - } - - handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, factorCodec); - } - } - break; - case PacketTypesIn.DestroyEntities: - if (handler.GetEntityHandlingEnabled()) - { - int entityCount = 1; // 1.17.0 has only one entity per packet - if (protocolVersion != MC_1_17_Version) - entityCount = dataTypes.ReadNextVarInt(packetData); // All other versions have a "count" field - int[] entityList = new int[entityCount]; - for (int i = 0; i < entityCount; i++) - { - entityList[i] = dataTypes.ReadNextVarInt(packetData); - } - handler.OnDestroyEntities(entityList); - } - break; - case PacketTypesIn.EntityPosition: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - bool OnGround = dataTypes.ReadNextBool(packetData); - DeltaX /= (128 * 32); - DeltaY /= (128 * 32); - DeltaZ /= (128 * 32); - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); - } - break; - case PacketTypesIn.EntityPositionAndRotation: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - byte _yaw = dataTypes.ReadNextByte(packetData); - byte _pitch = dataTypes.ReadNextByte(packetData); - bool OnGround = dataTypes.ReadNextBool(packetData); - DeltaX /= (128 * 32); - DeltaY /= (128 * 32); - DeltaZ /= (128 * 32); - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); - } - break; - case PacketTypesIn.EntityProperties: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - int NumberOfProperties = protocolVersion >= MC_1_17_Version ? dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextInt(packetData); - Dictionary keys = new(); - for (int i = 0; i < NumberOfProperties; i++) - { - string _key = dataTypes.ReadNextString(packetData); - Double _value = dataTypes.ReadNextDouble(packetData); - - List op0 = new(); - List op1 = new(); - List op2 = new(); - int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData); - for (int j = 0; j < NumberOfModifiers; j++) - { - dataTypes.ReadNextUUID(packetData); - Double amount = dataTypes.ReadNextDouble(packetData); - byte operation = dataTypes.ReadNextByte(packetData); - switch (operation) - { - case 0: op0.Add(amount); break; - case 1: op1.Add(amount); break; - case 2: op2.Add(amount + 1); break; - } - } - if (op0.Count > 0) _value += op0.Sum(); - if (op1.Count > 0) _value *= 1 + op1.Sum(); - if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x); - keys.Add(_key, _value); - } - handler.OnEntityProperties(EntityID, keys); - } - break; - case PacketTypesIn.EntityMetadata: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); - - int healthField; // See https://wiki.vg/Entity_metadata#Living_Entity - if (protocolVersion > MC_1_19_2_Version) - throw new NotImplementedException(Translations.exception_palette_healthfield); - else if (protocolVersion >= MC_1_17_Version) // 1.17 and above - healthField = 9; - else if (protocolVersion >= MC_1_14_Version) // 1.14 and above - healthField = 8; - else if (protocolVersion >= MC_1_10_Version) // 1.10 and above - healthField = 7; - else - throw new NotImplementedException(Translations.exception_palette_healthfield); - - if (metadata.TryGetValue(healthField, out object? healthObj) && healthObj != null && healthObj.GetType() == typeof(float)) - handler.OnEntityHealth(EntityID, (float)healthObj); - - handler.OnEntityMetadata(EntityID, metadata); - } - break; - case PacketTypesIn.EntityStatus: - if (handler.GetEntityHandlingEnabled()) - { - int entityId = dataTypes.ReadNextInt(packetData); - byte status = dataTypes.ReadNextByte(packetData); - handler.OnEntityStatus(entityId, status); - } - break; - case PacketTypesIn.TimeUpdate: - long WorldAge = dataTypes.ReadNextLong(packetData); - long TimeOfday = dataTypes.ReadNextLong(packetData); - handler.OnTimeUpdate(WorldAge, TimeOfday); - break; - case PacketTypesIn.EntityTeleport: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Double X = dataTypes.ReadNextDouble(packetData); - Double Y = dataTypes.ReadNextDouble(packetData); - Double Z = dataTypes.ReadNextDouble(packetData); - byte EntityYaw = dataTypes.ReadNextByte(packetData); - byte EntityPitch = dataTypes.ReadNextByte(packetData); - bool OnGround = dataTypes.ReadNextBool(packetData); - handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround); - } - break; - case PacketTypesIn.UpdateHealth: - float health = dataTypes.ReadNextFloat(packetData); - int food; - if (protocolVersion >= MC_1_8_Version) - food = dataTypes.ReadNextVarInt(packetData); - else - food = dataTypes.ReadNextShort(packetData); - dataTypes.ReadNextFloat(packetData); // Food Saturation - handler.OnUpdateHealth(health, food); - break; - case PacketTypesIn.SetExperience: - float experiencebar = dataTypes.ReadNextFloat(packetData); - int totalexperience, level; - - if (protocolVersion >= MC_1_19_3_Version) - { - totalexperience = dataTypes.ReadNextVarInt(packetData); - level = dataTypes.ReadNextVarInt(packetData); + } while (hasNext); } else { - level = dataTypes.ReadNextVarInt(packetData); - totalexperience = dataTypes.ReadNextVarInt(packetData); + int slot2 = dataTypes.ReadNextVarInt(packetData); + Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnEntityEquipment(entityid, slot2, item); } + } + break; + case PacketTypesIn.SpawnLivingEntity: + if (handler.GetEntityHandlingEnabled()) + { + Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true); + // packet before 1.15 has metadata at the end + // this is not handled in dataTypes.ReadNextEntity() + // we are simply ignoring leftover data in packet + handler.OnSpawnEntity(entity); + } + break; + case PacketTypesIn.SpawnPlayer: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Guid UUID = dataTypes.ReadNextUUID(packetData); + double X = dataTypes.ReadNextDouble(packetData); + double Y = dataTypes.ReadNextDouble(packetData); + double Z = dataTypes.ReadNextDouble(packetData); + byte Yaw = dataTypes.ReadNextByte(packetData); + byte Pitch = dataTypes.ReadNextByte(packetData); - handler.OnSetExperience(experiencebar, level, totalexperience); - break; - case PacketTypesIn.Explosion: - Location explosionLocation = new(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); + Location EntityLocation = new(X, Y, Z); - float explosionStrength = dataTypes.ReadNextFloat(packetData); - int explosionBlockCount = protocolVersion >= MC_1_17_Version + handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); + } + break; + case PacketTypesIn.EntityEffect: + if (handler.GetEntityHandlingEnabled()) + { + int entityid = dataTypes.ReadNextVarInt(packetData); + Inventory.Effects effect = Effects.Speed; + int effectId = protocolVersion >= MC_1_18_2_Version ? + dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextByte(packetData); + if (Enum.TryParse(effectId.ToString(), out effect)) + { + int amplifier = dataTypes.ReadNextByte(packetData); + int duration = dataTypes.ReadNextVarInt(packetData); + byte flags = dataTypes.ReadNextByte(packetData); + + bool hasFactorData = false; + Dictionary? factorCodec = null; + + if (protocolVersion >= MC_1_19_Version) + { + hasFactorData = dataTypes.ReadNextBool(packetData); + if (hasFactorData) + factorCodec = dataTypes.ReadNextNbt(packetData); + } + + handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, factorCodec); + } + } + break; + case PacketTypesIn.DestroyEntities: + if (handler.GetEntityHandlingEnabled()) + { + int entityCount = 1; // 1.17.0 has only one entity per packet + if (protocolVersion != MC_1_17_Version) + entityCount = dataTypes.ReadNextVarInt(packetData); // All other versions have a "count" field + int[] entityList = new int[entityCount]; + for (int i = 0; i < entityCount; i++) + { + entityList[i] = dataTypes.ReadNextVarInt(packetData); + } + handler.OnDestroyEntities(entityList); + } + break; + case PacketTypesIn.EntityPosition: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + bool OnGround = dataTypes.ReadNextBool(packetData); + DeltaX /= (128 * 32); + DeltaY /= (128 * 32); + DeltaZ /= (128 * 32); + handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); + } + break; + case PacketTypesIn.EntityPositionAndRotation: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + byte _yaw = dataTypes.ReadNextByte(packetData); + byte _pitch = dataTypes.ReadNextByte(packetData); + bool OnGround = dataTypes.ReadNextBool(packetData); + DeltaX /= (128 * 32); + DeltaY /= (128 * 32); + DeltaZ /= (128 * 32); + handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); + } + break; + case PacketTypesIn.EntityProperties: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + int NumberOfProperties = protocolVersion >= MC_1_17_Version ? dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextInt(packetData); + Dictionary keys = new(); + for (int i = 0; i < NumberOfProperties; i++) + { + string _key = dataTypes.ReadNextString(packetData); + Double _value = dataTypes.ReadNextDouble(packetData); + + List op0 = new(); + List op1 = new(); + List op2 = new(); + int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData); + for (int j = 0; j < NumberOfModifiers; j++) + { + dataTypes.ReadNextUUID(packetData); + Double amount = dataTypes.ReadNextDouble(packetData); + byte operation = dataTypes.ReadNextByte(packetData); + switch (operation) + { + case 0: op0.Add(amount); break; + case 1: op1.Add(amount); break; + case 2: op2.Add(amount + 1); break; + } + } + if (op0.Count > 0) _value += op0.Sum(); + if (op1.Count > 0) _value *= 1 + op1.Sum(); + if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x); + keys.Add(_key, _value); + } + handler.OnEntityProperties(EntityID, keys); + } + break; + case PacketTypesIn.EntityMetadata: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); + + int healthField; // See https://wiki.vg/Entity_metadata#Living_Entity + if (protocolVersion > MC_1_19_2_Version) + throw new NotImplementedException(Translations.exception_palette_healthfield); + else if (protocolVersion >= MC_1_17_Version) // 1.17 and above + healthField = 9; + else if (protocolVersion >= MC_1_14_Version) // 1.14 and above + healthField = 8; + else if (protocolVersion >= MC_1_10_Version) // 1.10 and above + healthField = 7; + else + throw new NotImplementedException(Translations.exception_palette_healthfield); + + if (metadata.TryGetValue(healthField, out object? healthObj) && healthObj != null && healthObj.GetType() == typeof(float)) + handler.OnEntityHealth(EntityID, (float)healthObj); + + handler.OnEntityMetadata(EntityID, metadata); + } + break; + case PacketTypesIn.EntityStatus: + if (handler.GetEntityHandlingEnabled()) + { + int entityId = dataTypes.ReadNextInt(packetData); + byte status = dataTypes.ReadNextByte(packetData); + handler.OnEntityStatus(entityId, status); + } + break; + case PacketTypesIn.TimeUpdate: + long WorldAge = dataTypes.ReadNextLong(packetData); + long TimeOfday = dataTypes.ReadNextLong(packetData); + handler.OnTimeUpdate(WorldAge, TimeOfday); + break; + case PacketTypesIn.EntityTeleport: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Double X = dataTypes.ReadNextDouble(packetData); + Double Y = dataTypes.ReadNextDouble(packetData); + Double Z = dataTypes.ReadNextDouble(packetData); + byte EntityYaw = dataTypes.ReadNextByte(packetData); + byte EntityPitch = dataTypes.ReadNextByte(packetData); + bool OnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround); + } + break; + case PacketTypesIn.UpdateHealth: + float health = dataTypes.ReadNextFloat(packetData); + int food; + if (protocolVersion >= MC_1_8_Version) + food = dataTypes.ReadNextVarInt(packetData); + else + food = dataTypes.ReadNextShort(packetData); + dataTypes.ReadNextFloat(packetData); // Food Saturation + handler.OnUpdateHealth(health, food); + break; + case PacketTypesIn.SetExperience: + float experiencebar = dataTypes.ReadNextFloat(packetData); + int totalexperience, level; + + if (protocolVersion >= MC_1_19_3_Version) + { + totalexperience = dataTypes.ReadNextVarInt(packetData); + level = dataTypes.ReadNextVarInt(packetData); + } + else + { + level = dataTypes.ReadNextVarInt(packetData); + totalexperience = dataTypes.ReadNextVarInt(packetData); + } + + handler.OnSetExperience(experiencebar, level, totalexperience); + break; + case PacketTypesIn.Explosion: + Location explosionLocation; + if (protocolVersion >= MC_1_19_3_Version) + explosionLocation = new(dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); + else + explosionLocation = new(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); + + float explosionStrength = dataTypes.ReadNextFloat(packetData); + int explosionBlockCount = protocolVersion >= MC_1_17_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextInt(packetData); + + for (int i = 0; i < explosionBlockCount; i++) + dataTypes.ReadData(3, packetData); + + float playerVelocityX = dataTypes.ReadNextFloat(packetData); + float playerVelocityY = dataTypes.ReadNextFloat(packetData); + float playerVelocityZ = dataTypes.ReadNextFloat(packetData); + + handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); + break; + case PacketTypesIn.HeldItemChange: + byte slot = dataTypes.ReadNextByte(packetData); + handler.OnHeldItemChange(slot); + break; + case PacketTypesIn.ScoreboardObjective: + string objectivename = dataTypes.ReadNextString(packetData); + byte mode = dataTypes.ReadNextByte(packetData); + string objectivevalue = String.Empty; + int type2 = -1; + if (mode == 0 || mode == 2) + { + objectivevalue = dataTypes.ReadNextString(packetData); + type2 = dataTypes.ReadNextVarInt(packetData); + } + handler.OnScoreboardObjective(objectivename, mode, objectivevalue, type2); + break; + case PacketTypesIn.UpdateScore: + string entityname = dataTypes.ReadNextString(packetData); + int action3 = protocolVersion >= MC_1_18_2_Version ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextInt(packetData); - - for (int i = 0; i < explosionBlockCount; i++) - dataTypes.ReadData(3, packetData); - - float playerVelocityX = dataTypes.ReadNextFloat(packetData); - float playerVelocityY = dataTypes.ReadNextFloat(packetData); - float playerVelocityZ = dataTypes.ReadNextFloat(packetData); - - handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); - break; - case PacketTypesIn.HeldItemChange: - byte slot = dataTypes.ReadNextByte(packetData); - handler.OnHeldItemChange(slot); - break; - case PacketTypesIn.ScoreboardObjective: - string objectivename = dataTypes.ReadNextString(packetData); - byte mode = dataTypes.ReadNextByte(packetData); - string objectivevalue = String.Empty; - int type2 = -1; - if (mode == 0 || mode == 2) - { - objectivevalue = dataTypes.ReadNextString(packetData); - type2 = dataTypes.ReadNextVarInt(packetData); - } - handler.OnScoreboardObjective(objectivename, mode, objectivevalue, type2); - break; - case PacketTypesIn.UpdateScore: - string entityname = dataTypes.ReadNextString(packetData); - int action3 = protocolVersion >= MC_1_18_2_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextByte(packetData); - string objectivename2 = string.Empty; - int value = -1; - if (action3 != 1 || protocolVersion >= MC_1_8_Version) - objectivename2 = dataTypes.ReadNextString(packetData); - if (action3 != 1) - value = dataTypes.ReadNextVarInt(packetData); - handler.OnUpdateScore(entityname, action3, objectivename2, value); - break; - case PacketTypesIn.BlockChangedAck: - handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); - break; - case PacketTypesIn.BlockBreakAnimation: - if (handler.GetEntityHandlingEnabled() && handler.GetTerrainEnabled()) - { - int playerId = dataTypes.ReadNextVarInt(packetData); - Location blockLocation = dataTypes.ReadNextLocation(packetData); - byte stage = dataTypes.ReadNextByte(packetData); - handler.OnBlockBreakAnimation(playerId, blockLocation, stage); - } - break; - case PacketTypesIn.EntityAnimation: - if (handler.GetEntityHandlingEnabled()) - { - int playerId2 = dataTypes.ReadNextVarInt(packetData); - byte animation = dataTypes.ReadNextByte(packetData); - handler.OnEntityAnimation(playerId2, animation); - } - break; - default: - return false; //Ignored packet - } - return true; //Packet processed + : dataTypes.ReadNextByte(packetData); + string objectivename2 = string.Empty; + int value = -1; + if (action3 != 1 || protocolVersion >= MC_1_8_Version) + objectivename2 = dataTypes.ReadNextString(packetData); + if (action3 != 1) + value = dataTypes.ReadNextVarInt(packetData); + handler.OnUpdateScore(entityname, action3, objectivename2, value); + break; + case PacketTypesIn.BlockChangedAck: + handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); + break; + case PacketTypesIn.BlockBreakAnimation: + if (handler.GetEntityHandlingEnabled() && handler.GetTerrainEnabled()) + { + int playerId = dataTypes.ReadNextVarInt(packetData); + Location blockLocation = dataTypes.ReadNextLocation(packetData); + byte stage = dataTypes.ReadNextByte(packetData); + handler.OnBlockBreakAnimation(playerId, blockLocation, stage); + } + break; + case PacketTypesIn.EntityAnimation: + if (handler.GetEntityHandlingEnabled()) + { + int playerId2 = dataTypes.ReadNextVarInt(packetData); + byte animation = dataTypes.ReadNextByte(packetData); + handler.OnEntityAnimation(playerId2, animation); + } + break; + default: + return false; //Ignored packet + } + return true; //Packet processed +#if Release } catch (Exception innerException) { @@ -1986,6 +1999,7 @@ namespace MinecraftClient.Protocol.Handlers innerException.GetType()), innerException); } +#endif } /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs index d144f37b..fc66edd0 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs @@ -264,9 +264,7 @@ namespace MinecraftClient.Protocol.Handlers int[] palette = new int[paletteLength]; for (int i = 0; i < paletteLength; i++) - { palette[i] = dataTypes.ReadNextVarInt(cache); - } // Bit mask covering bitsPerBlock bits // EG, if bitsPerBlock = 5, valueMask = 00011111 in binary From de9b47b2d9ecd14971c370f0063295a774eaab8d Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 15:58:36 +0800 Subject: [PATCH 26/44] Reduce conflict --- .../Protocol/Handlers/Protocol18.cs | 3074 +++++++++-------- 1 file changed, 1538 insertions(+), 1536 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index c71936b3..b04a0400 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -329,355 +329,260 @@ namespace MinecraftClient.Protocol.Handlers /// TRUE if the packet was processed, FALSE if ignored or unknown internal bool HandlePacket(int packetID, Queue packetData) { -#if Release try { -#endif - if (login_phase) - { - switch (packetID) //Packet IDs are different while logging in + if (login_phase) { - case 0x03: - if (protocolVersion >= MC_1_8_Version) - compression_treshold = dataTypes.ReadNextVarInt(packetData); - break; - case 0x04: - int messageId = dataTypes.ReadNextVarInt(packetData); - string channel = dataTypes.ReadNextString(packetData); - List responseData = new(); - bool understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); - SendLoginPluginResponse(messageId, understood, responseData.ToArray()); - return understood; - default: - return false; //Ignored packet - } - } - // Regular in-game packets - else switch (packetPalette.GetIncommingTypeById(packetID)) - { - case PacketTypesIn.KeepAlive: - SendPacket(PacketTypesOut.KeepAlive, packetData); - handler.OnServerKeepAlive(); - break; - case PacketTypesIn.Ping: - SendPacket(PacketTypesOut.Pong, packetData); - break; - case PacketTypesIn.JoinGame: - handler.OnGameJoined(); - int playerEntityID = dataTypes.ReadNextInt(packetData); - handler.OnReceivePlayerEntityID(playerEntityID); - - if (protocolVersion >= MC_1_16_2_Version) - dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above - - handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); - - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 and above - int worldCount = dataTypes.ReadNextVarInt(packetData); // Dimension Count (World Count) - 1.16 and above - for (int i = 0; i < worldCount; i++) - dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above - var registryCodec = dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above - if (protocolVersion >= MC_1_19_Version) - ChatParser.ReadChatType(registryCodec); - if (handler.GetTerrainEnabled()) - World.StoreDimensionList(registryCodec); - } - - // Current dimension - // String: 1.19 and above - // NBT Tag Compound: [1.16.2 to 1.18.2] - // String identifier: 1.16 and 1.16.1 - // varInt: [1.9.1 to 1.15.2] - // byte: below 1.9.1 - string? dimensionTypeName = null; - Dictionary? dimensionType = null; - if (protocolVersion >= MC_1_16_Version) - { - if (protocolVersion >= MC_1_19_Version) - dimensionTypeName = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - else if (protocolVersion >= MC_1_16_2_Version) - dimensionType = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound - else - dataTypes.ReadNextString(packetData); - currentDimension = 0; - } - else if (protocolVersion >= MC_1_9_1_Version) - currentDimension = dataTypes.ReadNextInt(packetData); - else - currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); - - if (protocolVersion < MC_1_14_Version) - dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - - if (protocolVersion >= MC_1_16_Version) - { - string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) - { - World.StoreOneDimension(dimensionName, dimensionType!); - World.SetDimension(dimensionName); - } - else if (protocolVersion >= MC_1_19_Version) - { - World.SetDimension(dimensionTypeName!); - } - } - } - - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above - if (protocolVersion >= MC_1_16_2_Version) - dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above - else - dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below - if (protocolVersion < MC_1_16_Version) - dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below - if (protocolVersion >= MC_1_14_Version) - dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above - if (protocolVersion >= MC_1_18_1_Version) - dataTypes.ReadNextVarInt(packetData); // Simulation Distance - 1.18 and above - if (protocolVersion >= MC_1_8_Version) - dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above - dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above - } - if (protocolVersion >= MC_1_19_Version) - { - bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location - if (hasDeathLocation) - { - dataTypes.SkipNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - } - - break; - case PacketTypesIn.DeclareCommands: - if (protocolVersion >= MC_1_19_Version) - DeclareCommands.Read(dataTypes, packetData); - break; - case PacketTypesIn.ChatMessage: - int messageType = 0; - - if (protocolVersion <= MC_1_18_2_Version) // 1.18 and bellow - { - string message = dataTypes.ReadNextString(packetData); - - Guid senderUUID; + switch (packetID) //Packet IDs are different while logging in + { + case 0x03: if (protocolVersion >= MC_1_8_Version) + compression_treshold = dataTypes.ReadNextVarInt(packetData); + break; + case 0x04: + int messageId = dataTypes.ReadNextVarInt(packetData); + string channel = dataTypes.ReadNextString(packetData); + List responseData = new(); + bool understood = pForge.HandleLoginPluginRequest(channel, packetData, ref responseData); + SendLoginPluginResponse(messageId, understood, responseData.ToArray()); + return understood; + default: + return false; //Ignored packet + } + } + // Regular in-game packets + else switch (packetPalette.GetIncommingTypeById(packetID)) + { + case PacketTypesIn.KeepAlive: + SendPacket(PacketTypesOut.KeepAlive, packetData); + handler.OnServerKeepAlive(); + break; + case PacketTypesIn.Ping: + SendPacket(PacketTypesOut.Pong, packetData); + break; + case PacketTypesIn.JoinGame: + handler.OnGameJoined(); + int playerEntityID = dataTypes.ReadNextInt(packetData); + handler.OnReceivePlayerEntityID(playerEntityID); + + if (protocolVersion >= MC_1_16_2_Version) + dataTypes.ReadNextBool(packetData); // Is hardcore - 1.16.2 and above + + handler.OnGamemodeUpdate(Guid.Empty, dataTypes.ReadNextByte(packetData)); + + if (protocolVersion >= MC_1_16_Version) { - //Hide system messages or xp bar messages? - messageType = dataTypes.ReadNextByte(packetData); + dataTypes.ReadNextByte(packetData); // Previous Gamemode - 1.16 and above + int worldCount = dataTypes.ReadNextVarInt(packetData); // Dimension Count (World Count) - 1.16 and above + for (int i = 0; i < worldCount; i++) + dataTypes.ReadNextString(packetData); // Dimension Names (World Names) - 1.16 and above + var registryCodec = dataTypes.ReadNextNbt(packetData); // Registry Codec (Dimension Codec) - 1.16 and above + if (protocolVersion >= MC_1_19_Version) + ChatParser.ReadChatType(registryCodec); + if (handler.GetTerrainEnabled()) + World.StoreDimensionList(registryCodec); + } + + // Current dimension + // String: 1.19 and above + // NBT Tag Compound: [1.16.2 to 1.18.2] + // String identifier: 1.16 and 1.16.1 + // varInt: [1.9.1 to 1.15.2] + // byte: below 1.9.1 + string? dimensionTypeName = null; + Dictionary? dimensionType = null; + if (protocolVersion >= MC_1_16_Version) + { + if (protocolVersion >= MC_1_19_Version) + dimensionTypeName = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + else if (protocolVersion >= MC_1_16_2_Version) + dimensionType = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound + else + dataTypes.ReadNextString(packetData); + currentDimension = 0; + } + else if (protocolVersion >= MC_1_9_1_Version) + currentDimension = dataTypes.ReadNextInt(packetData); + else + currentDimension = (sbyte)dataTypes.ReadNextByte(packetData); + + if (protocolVersion < MC_1_14_Version) + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + + if (protocolVersion >= MC_1_16_Version) + { + string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above + if (handler.GetTerrainEnabled()) + { + if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) + { + World.StoreOneDimension(dimensionName, dimensionType!); + World.SetDimension(dimensionName); + } + else if (protocolVersion >= MC_1_19_Version) + { + World.SetDimension(dimensionTypeName!); + } + } + } + + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above + if (protocolVersion >= MC_1_16_2_Version) + dataTypes.ReadNextVarInt(packetData); // Max Players - 1.16.2 and above + else + dataTypes.ReadNextByte(packetData); // Max Players - 1.16.1 and below + if (protocolVersion < MC_1_16_Version) + dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below + if (protocolVersion >= MC_1_14_Version) + dataTypes.ReadNextVarInt(packetData); // View distance - 1.14 and above + if (protocolVersion >= MC_1_18_1_Version) + dataTypes.ReadNextVarInt(packetData); // Simulation Distance - 1.18 and above + if (protocolVersion >= MC_1_8_Version) + dataTypes.ReadNextBool(packetData); // Reduced debug info - 1.8 and above + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextBool(packetData); // Enable respawn screen - 1.15 and above + if (protocolVersion >= MC_1_16_Version) + { + dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above + dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above + } + if (protocolVersion >= MC_1_19_Version) + { + bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location + if (hasDeathLocation) + { + dataTypes.SkipNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location + } + } + + break; + case PacketTypesIn.DeclareCommands: + if (protocolVersion >= MC_1_19_Version) + DeclareCommands.Read(dataTypes, packetData); + break; + case PacketTypesIn.ChatMessage: + int messageType = 0; + + if (protocolVersion <= MC_1_18_2_Version) // 1.18 and bellow + { + string message = dataTypes.ReadNextString(packetData); + + Guid senderUUID; + if (protocolVersion >= MC_1_8_Version) + { + //Hide system messages or xp bar messages? + messageType = dataTypes.ReadNextByte(packetData); + if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) + || (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages)) + break; + + if (protocolVersion >= MC_1_16_5_Version) + senderUUID = dataTypes.ReadNextUUID(packetData); + else senderUUID = Guid.Empty; + } + else + senderUUID = Guid.Empty; + + handler.OnTextReceived(new(message, null, true, messageType, senderUUID)); + } + else if (protocolVersion == MC_1_19_Version) // 1.19 + { + string signedChat = dataTypes.ReadNextString(packetData); + + bool hasUnsignedChatContent = dataTypes.ReadNextBool(packetData); + string? unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; + + messageType = dataTypes.ReadNextVarInt(packetData); if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) - || (messageType == 2 && !Config.Main.Advanced.ShowSystemMessages)) + || (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages)) break; - if (protocolVersion >= MC_1_16_5_Version) - senderUUID = dataTypes.ReadNextUUID(packetData); - else senderUUID = Guid.Empty; - } - else - senderUUID = Guid.Empty; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + string senderDisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - handler.OnTextReceived(new(message, null, true, messageType, senderUUID)); - } - else if (protocolVersion == MC_1_19_Version) // 1.19 - { - string signedChat = dataTypes.ReadNextString(packetData); + bool hasSenderTeamName = dataTypes.ReadNextBool(packetData); + string? senderTeamName = hasSenderTeamName ? ChatParser.ParseText(dataTypes.ReadNextString(packetData)) : null; - bool hasUnsignedChatContent = dataTypes.ReadNextBool(packetData); - string? unsignedChatContent = hasUnsignedChatContent ? dataTypes.ReadNextString(packetData) : null; + long timestamp = dataTypes.ReadNextLong(packetData); - messageType = dataTypes.ReadNextVarInt(packetData); - if ((messageType == 1 && !Config.Main.Advanced.ShowSystemMessages) - || (messageType == 2 && !Config.Main.Advanced.ShowXPBarMessages)) - break; + long salt = dataTypes.ReadNextLong(packetData); - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - string senderDisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + byte[] messageSignature = dataTypes.ReadNextByteArray(packetData); - bool hasSenderTeamName = dataTypes.ReadNextBool(packetData); - string? senderTeamName = hasSenderTeamName ? ChatParser.ParseText(dataTypes.ReadNextString(packetData)) : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - - long salt = dataTypes.ReadNextLong(packetData); - - byte[] messageSignature = dataTypes.ReadNextByteArray(packetData); - - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, ref messageSignature); - } - - ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - handler.OnTextReceived(chat); - } - else if (protocolVersion == MC_1_19_2_Version) - { - // 1.19.1 - 1.19.2 - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - - string signedChat = dataTypes.ReadNextString(packetData); - string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); - LastSeenMessageList.AcknowledgedMessage[] lastSeenMessageList = new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; - for (int i = 0; i < lastSeenMessageListLen; ++i) - { - Guid user = dataTypes.ReadNextUUID(packetData); - byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); - lastSeenMessageList[i] = new(user, lastSignature, true); - } - LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); - - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - if (filterEnum == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - Dictionary chatInfo = Json.ParseJson(chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) - { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; - } - } - - bool verifyResult; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player == null || !player.IsMessageChainLegal()) + bool verifyResult; + if (!isOnlineMode) verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; else { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + verifyResult = player != null && player.VerifyMessage(signedChat, timestamp, salt, ref messageSignature); } + + ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); + handler.OnTextReceived(chat); } - - ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender()) - Acknowledge(chat); - handler.OnTextReceived(chat); - } - else if (protocolVersion >= MC_1_19_3_Version) - { - // 1.19.3+ - // Header section - // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - int index = dataTypes.ReadNextVarInt(packetData); - // Signature is fixed size of 256 bytes - byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; - - // Body - // net.minecraft.network.message.MessageBody.Serialized#write - string message = dataTypes.ReadNextString(packetData); - long timestamp = dataTypes.ReadNextLong(packetData); - long salt = dataTypes.ReadNextLong(packetData); - - // Previous Messages - // net.minecraft.network.message.LastSeenMessageList.Indexed#write - // net.minecraft.network.message.MessageSignatureData.Indexed#write - int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); - Tuple[] previousMessageSignatures = new Tuple[totalPreviousMessages]; - for (int i = 0; i < totalPreviousMessages; i++) + else if (protocolVersion == MC_1_19_2_Version) { - // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf - int messageId = dataTypes.ReadNextVarInt(packetData) - 1; - if (messageId == -1) - previousMessageSignatures[i] = new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256)); - else - previousMessageSignatures[i] = new Tuple(messageId, null); - } + // 1.19.1 - 1.19.2 + byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - // Other - string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + string signedChat = dataTypes.ReadNextString(packetData); + string? decorated = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); - if (filterType == MessageFilterType.PartiallyFiltered) - dataTypes.ReadNextULongArray(packetData); - - // Network Target - // net.minecraft.network.message.MessageType.Serialized#write - int chatTypeId = dataTypes.ReadNextVarInt(packetData); - string chatName = dataTypes.ReadNextString(packetData); - string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - - ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - - Dictionary chatInfo = Json.ParseJson(targetName ?? chatName).Properties; - string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; - string? senderTeamName = null; - if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) - senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; - - if (string.IsNullOrWhiteSpace(senderDisplayName)) - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); - if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + int lastSeenMessageListLen = dataTypes.ReadNextVarInt(packetData); + LastSeenMessageList.AcknowledgedMessage[] lastSeenMessageList = new LastSeenMessageList.AcknowledgedMessage[lastSeenMessageListLen]; + for (int i = 0; i < lastSeenMessageListLen; ++i) { - senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); - if (string.IsNullOrWhiteSpace(senderDisplayName)) - senderDisplayName = player.DisplayName ?? player.Name; - else - senderDisplayName += "§r"; + Guid user = dataTypes.ReadNextUUID(packetData); + byte[] lastSignature = dataTypes.ReadNextByteArray(packetData); + lastSeenMessageList[i] = new(user, lastSignature, true); } - } + LastSeenMessageList lastSeenMessages = new(lastSeenMessageList); - bool verifyResult; - if (!isOnlineMode || messageSignature == null) - verifyResult = false; - else - { - if (senderUUID == handler.GetUserUuid()) + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + MessageFilterType filterEnum = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); + if (filterEnum == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); + + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + + Dictionary chatInfo = Json.ParseJson(chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) verifyResult = true; else { @@ -686,1306 +591,1399 @@ namespace MinecraftClient.Protocol.Handlers verifyResult = false; else { - verifyResult = false; - verifyResult = player.VerifyMessage(message, senderUUID, player.ChatUuid, index, timestamp, salt, ref messageSignature, previousMessageSignatures); + bool lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessage(signedChat, timestamp, salt, ref headerSignature, ref precedingSignature, lastSeenMessages); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, senderDisplayName)); } } + + ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender()) + Acknowledge(chat); + handler.OnTextReceived(chat); } - - ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender() && verifyResult) - Acknowledge(chat); - handler.OnTextReceived(chat); - } - break; - case PacketTypesIn.SystemChat: - string systemMessage = dataTypes.ReadNextString(packetData); - if (protocolVersion >= MC_1_19_3_Version) - { - bool isOverlay = dataTypes.ReadNextBool(packetData); - if (isOverlay) + else if (protocolVersion >= MC_1_19_3_Version) { - if (!Config.Main.Advanced.ShowXPBarMessages) - break; - } - else - { - if (!Config.Main.Advanced.ShowSystemMessages) - break; - } - handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); - } - else - { - int msgType = dataTypes.ReadNextVarInt(packetData); - if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) - break; - handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); - } - break; - case PacketTypesIn.ProfilelessChatMessage: - string message_ = dataTypes.ReadNextString(packetData); - int messageType_ = dataTypes.ReadNextVarInt(packetData); - string messageName = dataTypes.ReadNextString(packetData); - string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, Guid.Empty, true); - profilelessChat.isSenderJson = true; - handler.OnTextReceived(profilelessChat); - break; - case PacketTypesIn.CombatEvent: - // 1.8 - 1.16.5 - if (protocolVersion >= MC_1_8_Version && protocolVersion <= MC_1_16_5_Version) - { - CombatEventType eventType = (CombatEventType)dataTypes.ReadNextVarInt(packetData); + // 1.19.3+ + // Header section + // net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket#write + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + int index = dataTypes.ReadNextVarInt(packetData); + // Signature is fixed size of 256 bytes + byte[]? messageSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData, 256) : null; - if (eventType == CombatEventType.EntityDead) - { - dataTypes.SkipNextVarInt(packetData); + // Body + // net.minecraft.network.message.MessageBody.Serialized#write + string message = dataTypes.ReadNextString(packetData); + long timestamp = dataTypes.ReadNextLong(packetData); + long salt = dataTypes.ReadNextLong(packetData); - handler.OnPlayerKilled( - dataTypes.ReadNextInt(packetData), - ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - ); - } - } + // Previous Messages + // net.minecraft.network.message.LastSeenMessageList.Indexed#write + // net.minecraft.network.message.MessageSignatureData.Indexed#write + int totalPreviousMessages = dataTypes.ReadNextVarInt(packetData); + Tuple[] previousMessageSignatures = new Tuple[totalPreviousMessages]; + for (int i = 0; i < totalPreviousMessages; i++) + { + // net.minecraft.network.message.MessageSignatureData.Indexed#fromBuf + int messageId = dataTypes.ReadNextVarInt(packetData) - 1; + if (messageId == -1) + previousMessageSignatures[i] = new Tuple(messageId, dataTypes.ReadNextByteArray(packetData, 256)); + else + previousMessageSignatures[i] = new Tuple(messageId, null); + } - break; - case PacketTypesIn.DeathCombatEvent: - dataTypes.SkipNextVarInt(packetData); + // Other + string? unsignedChatContent = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - handler.OnPlayerKilled( - dataTypes.ReadNextInt(packetData), - ChatParser.ParseText(dataTypes.ReadNextString(packetData)) - ); + MessageFilterType filterType = (MessageFilterType)dataTypes.ReadNextVarInt(packetData); - break; - case PacketTypesIn.MessageHeader: // 1.19.2 only - if (protocolVersion == MC_1_19_2_Version) - { - byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; - Guid senderUUID = dataTypes.ReadNextUUID(packetData); - byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); - byte[] bodyDigest = dataTypes.ReadNextByteArray(packetData); + if (filterType == MessageFilterType.PartiallyFiltered) + dataTypes.ReadNextULongArray(packetData); - bool verifyResult; + // Network Target + // net.minecraft.network.message.MessageType.Serialized#write + int chatTypeId = dataTypes.ReadNextVarInt(packetData); + string chatName = dataTypes.ReadNextString(packetData); + string? targetName = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; - if (!isOnlineMode) - verifyResult = false; - else if (senderUUID == handler.GetUserUuid()) - verifyResult = true; - else - { - PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + ChatParser.MessageType messageTypeEnum = ChatParser.ChatId2Type!.GetValueOrDefault(chatTypeId, ChatParser.MessageType.CHAT); - if (player == null || !player.IsMessageChainLegal()) + Dictionary chatInfo = Json.ParseJson(targetName ?? chatName).Properties; + string senderDisplayName = (chatInfo.ContainsKey("insertion") ? chatInfo["insertion"] : chatInfo["text"]).StringValue; + string? senderTeamName = null; + if (targetName != null && + (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0].Properties["text"].StringValue; + + if (string.IsNullOrWhiteSpace(senderDisplayName)) + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player != null && (player.DisplayName != null || player.Name != null) && string.IsNullOrWhiteSpace(senderDisplayName)) + { + senderDisplayName = ChatParser.ParseText(player.DisplayName ?? player.Name); + if (string.IsNullOrWhiteSpace(senderDisplayName)) + senderDisplayName = player.DisplayName ?? player.Name; + else + senderDisplayName += "§r"; + } + } + + bool verifyResult; + if (!isOnlineMode || messageSignature == null) verifyResult = false; else { - bool lastVerifyResult = player.IsMessageChainLegal(); - verifyResult = player.VerifyMessageHead(ref precedingSignature, ref headerSignature, ref bodyDigest); - if (lastVerifyResult && !verifyResult) - log.Warn(string.Format(Translations.chat_message_chain_broken, player.Name)); - } - } - } - - break; - case PacketTypesIn.Respawn: - string? dimensionTypeNameRespawn = null; - Dictionary? dimensionTypeRespawn = null; - if (protocolVersion >= MC_1_16_Version) - { - if (protocolVersion >= MC_1_19_Version) - dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier - else if (protocolVersion >= MC_1_16_2_Version) - dimensionTypeRespawn = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound - else - dataTypes.ReadNextString(packetData); - currentDimension = 0; - } - else - { // 1.15 and below - currentDimension = dataTypes.ReadNextInt(packetData); - } - - if (protocolVersion >= MC_1_16_Version) - { - string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) - { - World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); - World.SetDimension(dimensionName); - } - else if (protocolVersion >= MC_1_19_Version) - { - World.SetDimension(dimensionTypeNameRespawn!); - } - } - } - - if (protocolVersion < MC_1_14_Version) - dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below - if (protocolVersion >= MC_1_15_Version) - dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above - dataTypes.ReadNextByte(packetData); // Gamemode - if (protocolVersion >= MC_1_16_Version) - dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above - if (protocolVersion < MC_1_16_Version) - dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below - if (protocolVersion >= MC_1_16_Version) - { - dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above - dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above - dataTypes.ReadNextBool(packetData); // Copy metadata - 1.16 and above - } - if (protocolVersion >= MC_1_19_Version) - { - bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location - if (hasDeathLocation) - { - dataTypes.ReadNextString(packetData); // Death dimension name: Identifier - dataTypes.ReadNextLocation(packetData); // Death location - } - } - handler.OnRespawn(); - break; - case PacketTypesIn.PlayerPositionAndLook: - { - // These always need to be read, since we need the field after them for teleport confirm - double x = dataTypes.ReadNextDouble(packetData); - double y = dataTypes.ReadNextDouble(packetData); - double z = dataTypes.ReadNextDouble(packetData); - Location location = new(x, y, z); - float yaw = dataTypes.ReadNextFloat(packetData); - float pitch = dataTypes.ReadNextFloat(packetData); - byte locMask = dataTypes.ReadNextByte(packetData); - - // entity handling require player pos for distance calculating - if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) - { - if (protocolVersion >= MC_1_8_Version) - { - Location current = handler.GetCurrentLocation(); - location.X = (locMask & 1 << 0) != 0 ? current.X + x : x; - location.Y = (locMask & 1 << 1) != 0 ? current.Y + y : y; - location.Z = (locMask & 1 << 2) != 0 ? current.Z + z : z; - } - } - - if (protocolVersion >= MC_1_9_Version) - { - int teleportID = dataTypes.ReadNextVarInt(packetData); - - if (teleportID < 0) { yaw = LastYaw; pitch = LastPitch; } - else { LastYaw = yaw; LastPitch = pitch; } - - handler.UpdateLocation(location, yaw, pitch); - - // Teleport confirm packet - SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportID)); - if (Config.Main.Advanced.TemporaryFixBadpacket) - { - SendLocationUpdate(location, true, yaw, pitch, true); - if (teleportID == 1) - SendLocationUpdate(location, true, yaw, pitch, true); - } - } - else - { - handler.UpdateLocation(location, yaw, pitch); - LastYaw = yaw; LastPitch = pitch; - } - - if (protocolVersion >= MC_1_17_Version) - dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 and above - } - break; - case PacketTypesIn.ChunkData: - if (handler.GetTerrainEnabled()) - { - Interlocked.Increment(ref handler.GetWorld().chunkCnt); - Interlocked.Increment(ref handler.GetWorld().chunkLoadNotCompleted); - - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - if (protocolVersion >= MC_1_17_Version) - { - ulong[]? verticalStripBitmask = null; - - if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) - verticalStripBitmask = dataTypes.ReadNextULongArray(packetData); // Bit Mask Length and Primary Bit Mask - - dataTypes.ReadNextNbt(packetData); // Heightmaps - - if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) - { - int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - for (int i = 0; i < biomesLength; i++) - dataTypes.SkipNextVarInt(packetData); // Biomes + if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + verifyResult = false; + verifyResult = player.VerifyMessage(message, senderUUID, player.ChatUuid, index, timestamp, salt, ref messageSignature, previousMessageSignatures); + } + } } - int dataSize = dataTypes.ReadNextVarInt(packetData); // Size - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - - // Block Entity data: ignored - // Light data: ignored + ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); + if (isOnlineMode && !chat.LacksSender() && verifyResult) + Acknowledge(chat); + handler.OnTextReceived(chat); } - else + break; + case PacketTypesIn.SystemChat: + string systemMessage = dataTypes.ReadNextString(packetData); + if (protocolVersion >= MC_1_19_3_Version) { - bool chunksContinuous = dataTypes.ReadNextBool(packetData); - if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) - dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only - ushort chunkMask = protocolVersion >= MC_1_9_Version - ? (ushort)dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextUShort(packetData); - if (protocolVersion < MC_1_8_Version) + bool isOverlay = dataTypes.ReadNextBool(packetData); + if (isOverlay) { - ushort addBitmap = dataTypes.ReadNextUShort(packetData); - int compressedDataSize = dataTypes.ReadNextInt(packetData); - byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); - byte[] decompressed = ZlibUtils.Decompress(compressed); - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed)); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + if (!Config.Main.Advanced.ShowXPBarMessages) + break; } else { - if (protocolVersion >= MC_1_14_Version) - dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above - int biomesLength = 0; - if (protocolVersion >= MC_1_16_2_Version) - if (chunksContinuous) - biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - 1.16.2 and above - if (protocolVersion >= MC_1_15_Version && chunksContinuous) - { - if (protocolVersion >= MC_1_16_2_Version) - { - for (int i = 0; i < biomesLength; i++) - { - // Biomes - 1.16.2 and above - // Don't use ReadNextVarInt because it cost too much time - dataTypes.SkipNextVarInt(packetData); - } - } - else dataTypes.DropData(1024 * 4, packetData); // Biomes - 1.15 and above - } - int dataSize = dataTypes.ReadNextVarInt(packetData); - - pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + if (!Config.Main.Advanced.ShowSystemMessages) + break; } + handler.OnTextReceived(new(systemMessage, null, true, -1, Guid.Empty, true)); + } + else + { + int msgType = dataTypes.ReadNextVarInt(packetData); + if ((msgType == 1 && !Config.Main.Advanced.ShowSystemMessages)) + break; + handler.OnTextReceived(new(systemMessage, null, true, msgType, Guid.Empty, true)); } - } - break; - case PacketTypesIn.MapData: - if (protocolVersion < MC_1_8_Version) break; - - int mapid = dataTypes.ReadNextVarInt(packetData); - byte scale = dataTypes.ReadNextByte(packetData); - - - // 1.9 + - bool trackingPosition = true; - - // 1.14+ - bool locked = false; - - // 1.17+ (locked and trackingPosition switched places) - if (protocolVersion >= MC_1_17_Version) - { - if (protocolVersion >= MC_1_14_Version) - locked = dataTypes.ReadNextBool(packetData); - - if (protocolVersion >= MC_1_9_Version) - trackingPosition = dataTypes.ReadNextBool(packetData); - } - else - { - if (protocolVersion >= MC_1_9_Version) - trackingPosition = dataTypes.ReadNextBool(packetData); - - if (protocolVersion >= MC_1_14_Version) - locked = dataTypes.ReadNextBool(packetData); - } - - int iconcount = 0; - List icons = new(); - - // 1,9 + = needs tracking position to be true to get the icons - if (protocolVersion <= MC_1_16_5_Version || trackingPosition) - { - iconcount = dataTypes.ReadNextVarInt(packetData); - - for (int i = 0; i < iconcount; i++) + case PacketTypesIn.ProfilelessChatMessage: + string message_ = dataTypes.ReadNextString(packetData); + int messageType_ = dataTypes.ReadNextVarInt(packetData); + string messageName = dataTypes.ReadNextString(packetData); + string? targetName_ = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextString(packetData) : null; + ChatMessage profilelessChat = new(message_, targetName_ ?? messageName, true, messageType_, Guid.Empty, true); + profilelessChat.isSenderJson = true; + handler.OnTextReceived(profilelessChat); + break; + case PacketTypesIn.CombatEvent: + // 1.8 - 1.16.5 + if (protocolVersion >= MC_1_8_Version && protocolVersion <= MC_1_16_5_Version) { - MapIcon mapIcon = new(); + CombatEventType eventType = (CombatEventType)dataTypes.ReadNextVarInt(packetData); - // 1.8 - 1.13 - if (protocolVersion < MC_1_13_2_Version) + if (eventType == CombatEventType.EntityDead) { - byte directionAndtype = dataTypes.ReadNextByte(packetData); - byte direction, type; + dataTypes.SkipNextVarInt(packetData); - // 1.12.2+ - if (protocolVersion >= MC_1_12_2_Version) - { - direction = (byte)(directionAndtype & 0xF); - type = (byte)((directionAndtype >> 4) & 0xF); - } - else // 1.8 - 1.12 - { - direction = (byte)((directionAndtype >> 4) & 0xF); - type = (byte)(directionAndtype & 0xF); - } - - mapIcon.Type = (MapIconType)type; - mapIcon.Direction = direction; + handler.OnPlayerKilled( + dataTypes.ReadNextInt(packetData), + ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + ); } - - // 1.13.2+ - if (protocolVersion >= MC_1_13_2_Version) - mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); - - mapIcon.X = dataTypes.ReadNextByte(packetData); - mapIcon.Z = dataTypes.ReadNextByte(packetData); - - // 1.13.2+ - if (protocolVersion >= MC_1_13_2_Version) - { - mapIcon.Direction = dataTypes.ReadNextByte(packetData); - - if (dataTypes.ReadNextBool(packetData)) // Has Display Name? - mapIcon.DisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - - icons.Add(mapIcon); } - } - byte columnsUpdated = dataTypes.ReadNextByte(packetData); // width - byte rowsUpdated = 0; // height - byte mapCoulmnX = 0; - byte mapRowZ = 0; - byte[]? colors = null; + break; + case PacketTypesIn.DeathCombatEvent: + dataTypes.SkipNextVarInt(packetData); - if (columnsUpdated > 0) - { - rowsUpdated = dataTypes.ReadNextByte(packetData); // height - mapCoulmnX = dataTypes.ReadNextByte(packetData); - mapRowZ = dataTypes.ReadNextByte(packetData); - colors = dataTypes.ReadNextByteArray(packetData); - } + handler.OnPlayerKilled( + dataTypes.ReadNextInt(packetData), + ChatParser.ParseText(dataTypes.ReadNextString(packetData)) + ); - handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapRowZ, colors); - break; - case PacketTypesIn.TradeList: - if ((protocolVersion >= MC_1_14_Version) && (handler.GetInventoryEnabled())) - { - // MC 1.14 or greater - int windowID = dataTypes.ReadNextVarInt(packetData); - int size = dataTypes.ReadNextByte(packetData); - List trades = new(); - for (int tradeId = 0; tradeId < size; tradeId++) + break; + case PacketTypesIn.MessageHeader: // 1.19.2 only + if (protocolVersion == MC_1_19_2_Version) { - VillagerTrade trade = dataTypes.ReadNextTrade(packetData, itemPalette); - trades.Add(trade); + byte[]? precedingSignature = dataTypes.ReadNextBool(packetData) ? dataTypes.ReadNextByteArray(packetData) : null; + Guid senderUUID = dataTypes.ReadNextUUID(packetData); + byte[] headerSignature = dataTypes.ReadNextByteArray(packetData); + byte[] bodyDigest = dataTypes.ReadNextByteArray(packetData); + + bool verifyResult; + + if (!isOnlineMode) + verifyResult = false; + else if (senderUUID == handler.GetUserUuid()) + verifyResult = true; + else + { + PlayerInfo? player = handler.GetPlayerInfo(senderUUID); + + if (player == null || !player.IsMessageChainLegal()) + verifyResult = false; + else + { + bool lastVerifyResult = player.IsMessageChainLegal(); + verifyResult = player.VerifyMessageHead(ref precedingSignature, ref headerSignature, ref bodyDigest); + if (lastVerifyResult && !verifyResult) + log.Warn(string.Format(Translations.chat_message_chain_broken, player.Name)); + } + } } - VillagerInfo villagerInfo = new() + + break; + case PacketTypesIn.Respawn: + string? dimensionTypeNameRespawn = null; + Dictionary? dimensionTypeRespawn = null; + if (protocolVersion >= MC_1_16_Version) { - Level = dataTypes.ReadNextVarInt(packetData), - Experience = dataTypes.ReadNextVarInt(packetData), - IsRegularVillager = dataTypes.ReadNextBool(packetData), - CanRestock = dataTypes.ReadNextBool(packetData) - }; - handler.OnTradeList(windowID, trades, villagerInfo); - } - break; - case PacketTypesIn.Title: - if (protocolVersion >= MC_1_8_Version) - { - int action2 = dataTypes.ReadNextVarInt(packetData); - string titletext = String.Empty; - string subtitletext = String.Empty; - string actionbartext = String.Empty; - string json = String.Empty; - int fadein = -1; - int stay = -1; - int fadeout = -1; - if (protocolVersion >= MC_1_10_Version) - { - if (action2 == 0) - { - json = titletext; - titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 1) - { - json = subtitletext; - subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 2) - { - json = actionbartext; - actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 3) - { - fadein = dataTypes.ReadNextInt(packetData); - stay = dataTypes.ReadNextInt(packetData); - fadeout = dataTypes.ReadNextInt(packetData); - } + if (protocolVersion >= MC_1_19_Version) + dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier + else if (protocolVersion >= MC_1_16_2_Version) + dimensionTypeRespawn = dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound + else + dataTypes.ReadNextString(packetData); + currentDimension = 0; } else + { // 1.15 and below + currentDimension = dataTypes.ReadNextInt(packetData); + } + + if (protocolVersion >= MC_1_16_Version) { - if (action2 == 0) + string dimensionName = dataTypes.ReadNextString(packetData); // Dimension Name (World Name) - 1.16 and above + if (handler.GetTerrainEnabled()) { - json = titletext; - titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 1) - { - json = subtitletext; - subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - } - else if (action2 == 2) - { - fadein = dataTypes.ReadNextInt(packetData); - stay = dataTypes.ReadNextInt(packetData); - fadeout = dataTypes.ReadNextInt(packetData); + if (protocolVersion >= MC_1_16_2_Version && protocolVersion <= MC_1_18_2_Version) + { + World.StoreOneDimension(dimensionName, dimensionTypeRespawn!); + World.SetDimension(dimensionName); + } + else if (protocolVersion >= MC_1_19_Version) + { + World.SetDimension(dimensionTypeNameRespawn!); + } } } - handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json); - } - break; - case PacketTypesIn.MultiBlockChange: - if (handler.GetTerrainEnabled()) - { - if (protocolVersion >= MC_1_16_2_Version) + + if (protocolVersion < MC_1_14_Version) + dataTypes.ReadNextByte(packetData); // Difficulty - 1.13 and below + if (protocolVersion >= MC_1_15_Version) + dataTypes.ReadNextLong(packetData); // Hashed world seed - 1.15 and above + dataTypes.ReadNextByte(packetData); // Gamemode + if (protocolVersion >= MC_1_16_Version) + dataTypes.ReadNextByte(packetData); // Previous Game mode - 1.16 and above + if (protocolVersion < MC_1_16_Version) + dataTypes.SkipNextString(packetData); // Level Type - 1.15 and below + if (protocolVersion >= MC_1_16_Version) { - long chunkSection = dataTypes.ReadNextLong(packetData); - int sectionX = (int)(chunkSection >> 42); - int sectionY = (int)((chunkSection << 44) >> 44); - int sectionZ = (int)((chunkSection << 22) >> 42); - dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update) - int blocksSize = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < blocksSize; i++) + dataTypes.ReadNextBool(packetData); // Is Debug - 1.16 and above + dataTypes.ReadNextBool(packetData); // Is Flat - 1.16 and above + dataTypes.ReadNextBool(packetData); // Copy metadata - 1.16 and above + } + if (protocolVersion >= MC_1_19_Version) + { + bool hasDeathLocation = dataTypes.ReadNextBool(packetData); // Has death location + if (hasDeathLocation) { - 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 block = new((ushort)blockId); - int blockX = (sectionX * 16) + localX; - int blockY = (sectionY * 16) + localY; - int blockZ = (sectionZ * 16) + localZ; - - Location location = new(blockX, blockY, blockZ); - - handler.OnBlockChange(location, block); + dataTypes.ReadNextString(packetData); // Death dimension name: Identifier + dataTypes.ReadNextLocation(packetData); // Death location } } - else + handler.OnRespawn(); + break; + case PacketTypesIn.PlayerPositionAndLook: { + // These always need to be read, since we need the field after them for teleport confirm + double x = dataTypes.ReadNextDouble(packetData); + double y = dataTypes.ReadNextDouble(packetData); + double z = dataTypes.ReadNextDouble(packetData); + Location location = new(x, y, z); + float yaw = dataTypes.ReadNextFloat(packetData); + float pitch = dataTypes.ReadNextFloat(packetData); + byte locMask = dataTypes.ReadNextByte(packetData); + + // entity handling require player pos for distance calculating + if (handler.GetTerrainEnabled() || handler.GetEntityHandlingEnabled()) + { + if (protocolVersion >= MC_1_8_Version) + { + Location current = handler.GetCurrentLocation(); + location.X = (locMask & 1 << 0) != 0 ? current.X + x : x; + location.Y = (locMask & 1 << 1) != 0 ? current.Y + y : y; + location.Z = (locMask & 1 << 2) != 0 ? current.Z + z : z; + } + } + + if (protocolVersion >= MC_1_9_Version) + { + int teleportID = dataTypes.ReadNextVarInt(packetData); + + if (teleportID < 0) { yaw = LastYaw; pitch = LastPitch; } + else { LastYaw = yaw; LastPitch = pitch; } + + handler.UpdateLocation(location, yaw, pitch); + + // Teleport confirm packet + SendPacket(PacketTypesOut.TeleportConfirm, DataTypes.GetVarInt(teleportID)); + if (Config.Main.Advanced.TemporaryFixBadpacket) + { + SendLocationUpdate(location, true, yaw, pitch, true); + if (teleportID == 1) + SendLocationUpdate(location, true, yaw, pitch, true); + } + } + else + { + handler.UpdateLocation(location, yaw, pitch); + LastYaw = yaw; LastPitch = pitch; + } + + if (protocolVersion >= MC_1_17_Version) + dataTypes.ReadNextBool(packetData); // Dismount Vehicle - 1.17 and above + } + break; + case PacketTypesIn.ChunkData: + if (handler.GetTerrainEnabled()) + { + Interlocked.Increment(ref handler.GetWorld().chunkCnt); + Interlocked.Increment(ref handler.GetWorld().chunkLoadNotCompleted); + int chunkX = dataTypes.ReadNextInt(packetData); int chunkZ = dataTypes.ReadNextInt(packetData); - int recordCount = protocolVersion < MC_1_8_Version - ? (int)dataTypes.ReadNextShort(packetData) - : dataTypes.ReadNextVarInt(packetData); - - for (int i = 0; i < recordCount; i++) + if (protocolVersion >= MC_1_17_Version) { - byte locationXZ; - ushort blockIdMeta; - int blockY; + ulong[]? verticalStripBitmask = null; + if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) + verticalStripBitmask = dataTypes.ReadNextULongArray(packetData); // Bit Mask Length and Primary Bit Mask + + dataTypes.ReadNextNbt(packetData); // Heightmaps + + if (protocolVersion == MC_1_17_Version || protocolVersion == MC_1_17_1_Version) + { + int biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length + for (int i = 0; i < biomesLength; i++) + dataTypes.SkipNextVarInt(packetData); // Biomes + } + + int dataSize = dataTypes.ReadNextVarInt(packetData); // Size + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, verticalStripBitmask, packetData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + + // Block Entity data: ignored + // Light data: ignored + } + else + { + bool chunksContinuous = dataTypes.ReadNextBool(packetData); + if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) + dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only + ushort chunkMask = protocolVersion >= MC_1_9_Version + ? (ushort)dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextUShort(packetData); if (protocolVersion < MC_1_8_Version) { - blockIdMeta = dataTypes.ReadNextUShort(packetData); - blockY = (ushort)dataTypes.ReadNextByte(packetData); - locationXZ = dataTypes.ReadNextByte(packetData); + ushort addBitmap = dataTypes.ReadNextUShort(packetData); + int compressedDataSize = dataTypes.ReadNextInt(packetData); + byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); + byte[] decompressed = ZlibUtils.Decompress(compressed); + + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, addBitmap, currentDimension == 0, chunksContinuous, currentDimension, new Queue(decompressed)); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); } else { - locationXZ = dataTypes.ReadNextByte(packetData); - blockY = (ushort)dataTypes.ReadNextByte(packetData); - blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData); - } - - int blockX = locationXZ >> 4; - int blockZ = locationXZ & 0x0F; - - Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); - Block block = new(blockIdMeta); - handler.OnBlockChange(location, block); - } - } - } - break; - case PacketTypesIn.ServerData: - string motd = "-"; - bool hasMotd = dataTypes.ReadNextBool(packetData); - if (hasMotd) - motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); - - string iconBase64 = "-"; - bool hasIcon = dataTypes.ReadNextBool(packetData); - if (hasIcon) - iconBase64 = dataTypes.ReadNextString(packetData); - - bool previewsChat = false; - if (protocolVersion < MC_1_19_3_Version) - previewsChat = dataTypes.ReadNextBool(packetData); - - handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); - break; - case PacketTypesIn.BlockChange: - if (handler.GetTerrainEnabled()) - { - if (protocolVersion < MC_1_8_Version) - { - int blockX = dataTypes.ReadNextInt(packetData); - int blockY = dataTypes.ReadNextByte(packetData); - int blockZ = dataTypes.ReadNextInt(packetData); - short blockId = (short)dataTypes.ReadNextVarInt(packetData); - byte blockMeta = dataTypes.ReadNextByte(packetData); - - Location location = new(blockX, blockY, blockZ); - Block block = new(blockId, blockMeta); - handler.OnBlockChange(location, block); - } - else - { - Location location = dataTypes.ReadNextLocation(packetData); - Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); - handler.OnBlockChange(location, block); - } - } - break; - case PacketTypesIn.SetDisplayChatPreview: - bool previewsChatSetting = dataTypes.ReadNextBool(packetData); - handler.OnChatPreviewSettingUpdate(previewsChatSetting); - break; - case PacketTypesIn.ChatSuggestions: - break; - case PacketTypesIn.MapChunkBulk: - if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) - { - int chunkCount; - bool hasSkyLight; - Queue chunkData = packetData; - - //Read global fields - if (protocolVersion < MC_1_8_Version) - { - chunkCount = dataTypes.ReadNextShort(packetData); - int compressedDataSize = dataTypes.ReadNextInt(packetData); - hasSkyLight = dataTypes.ReadNextBool(packetData); - byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); - byte[] decompressed = ZlibUtils.Decompress(compressed); - chunkData = new Queue(decompressed); - } - else - { - hasSkyLight = dataTypes.ReadNextBool(packetData); - chunkCount = dataTypes.ReadNextVarInt(packetData); - } - - //Read chunk records - int[] chunkXs = new int[chunkCount]; - int[] chunkZs = new int[chunkCount]; - ushort[] chunkMasks = new ushort[chunkCount]; - ushort[] addBitmaps = new ushort[chunkCount]; - for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) - { - chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); - chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); - chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData); - addBitmaps[chunkColumnNo] = protocolVersion < MC_1_8_Version - ? dataTypes.ReadNextUShort(packetData) - : (ushort)0; - } - - //Process chunk records - for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) - { - pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData); - Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); - } - - } - break; - case PacketTypesIn.UnloadChunk: - if (protocolVersion >= MC_1_9_Version && handler.GetTerrainEnabled()) - { - int chunkX = dataTypes.ReadNextInt(packetData); - int chunkZ = dataTypes.ReadNextInt(packetData); - - // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. - // Since chunks that have not been loaded are not recorded, this may result - // in loading chunks that should be unloaded and inaccurate statistics. - if (handler.GetWorld()[chunkX, chunkZ] != null) - Interlocked.Decrement(ref handler.GetWorld().chunkCnt); - - handler.GetWorld()[chunkX, chunkZ] = null; - } - break; - case PacketTypesIn.ChangeGameState: - if (protocolVersion >= MC_1_15_2_Version) - { - byte reason = dataTypes.ReadNextByte(packetData); - float state = dataTypes.ReadNextFloat(packetData); - - handler.OnGameEvent(reason, state); - } - break; - case PacketTypesIn.PlayerInfo: - if (protocolVersion >= MC_1_19_3_Version) - { - byte actionBitset = dataTypes.ReadNextByte(packetData); - int numberOfActions = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < numberOfActions; i++) - { - Guid playerUuid = dataTypes.ReadNextUUID(packetData); - - if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player - { - string name = dataTypes.ReadNextString(packetData); - int numberOfProperties = dataTypes.ReadNextVarInt(packetData); - for (int j = 0; j < numberOfProperties; ++j) - { - dataTypes.SkipNextString(packetData); - dataTypes.SkipNextString(packetData); - if (dataTypes.ReadNextBool(packetData)) - dataTypes.SkipNextString(packetData); - } - handler.OnPlayerJoin(new(name, playerUuid)); - } - - PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; - if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat - { - bool hasSignatureData = dataTypes.ReadNextBool(packetData); - if (hasSignatureData) - { - Guid chatUuid = dataTypes.ReadNextUUID(packetData); - long publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); - byte[] encodedPublicKey = dataTypes.ReadNextByteArray(packetData); - byte[] publicKeySignature = dataTypes.ReadNextByteArray(packetData); - player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, publicKeySignature); - } - else - { - player.ClearPublicKey(); - } - } - if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode - { - handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); - } - if ((actionBitset & (1 << 3)) > 0) // Actions bit 3: update listed - { - player.Listed = dataTypes.ReadNextBool(packetData); - } - if ((actionBitset & (1 << 4)) > 0) // Actions bit 4: update latency - { - int latency = dataTypes.ReadNextVarInt(packetData); - handler.OnLatencyUpdate(playerUuid, latency); //Update latency; - } - if ((actionBitset & (1 << 5)) > 0) // Actions bit 5: update display name - { - if (dataTypes.ReadNextBool(packetData)) - player.DisplayName = dataTypes.ReadNextString(packetData); - else - player.DisplayName = null; - } - } - } - else if (protocolVersion >= MC_1_8_Version) - { - int action = dataTypes.ReadNextVarInt(packetData); // Action Name - int numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players - - for (int i = 0; i < numberOfPlayers; i++) - { - Guid uuid = dataTypes.ReadNextUUID(packetData); // Player UUID - - switch (action) - { - case 0x00: //Player Join (Add player since 1.19) - string name = dataTypes.ReadNextString(packetData); // Player name - int propNum = dataTypes.ReadNextVarInt(packetData); // Number of properties in the following array - - // Property: Tuple[]? properties = useProperty ? - new Tuple[propNum] : null; - for (int p = 0; p < propNum; p++) + if (protocolVersion >= MC_1_14_Version) + dataTypes.ReadNextNbt(packetData); // Heightmaps - 1.14 and above + int biomesLength = 0; + if (protocolVersion >= MC_1_16_2_Version) + if (chunksContinuous) + biomesLength = dataTypes.ReadNextVarInt(packetData); // Biomes length - 1.16.2 and above + if (protocolVersion >= MC_1_15_Version && chunksContinuous) { - string propertyName = dataTypes.ReadNextString(packetData); // Name: String (32767) - string val = dataTypes.ReadNextString(packetData); // Value: String (32767) - string? propertySignature = null; - if (dataTypes.ReadNextBool(packetData)) // Is Signed - propertySignature = dataTypes.ReadNextString(packetData); // Signature: String (32767) - if (useProperty) - properties![p] = new(propertyName, val, propertySignature); - } -#pragma warning restore CS0162 // Unreachable code detected - - int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode - handler.OnGamemodeUpdate(uuid, gameMode); - - int ping = dataTypes.ReadNextVarInt(packetData); // Ping - - string? displayName = null; - if (dataTypes.ReadNextBool(packetData)) // Has display name - displayName = dataTypes.ReadNextString(packetData); // Display name - - // 1.19 Additions - long? keyExpiration = null; - byte[]? publicKey = null, signature = null; - if (protocolVersion >= MC_1_19_Version) - { - if (dataTypes.ReadNextBool(packetData)) // Has Sig Data (if true, red the following fields) + if (protocolVersion >= MC_1_16_2_Version) { - keyExpiration = dataTypes.ReadNextLong(packetData); // Timestamp - - int publicKeyLength = dataTypes.ReadNextVarInt(packetData); // Public Key Length - if (publicKeyLength > 0) - publicKey = dataTypes.ReadData(publicKeyLength, packetData); // Public key - - int signatureLength = dataTypes.ReadNextVarInt(packetData); // Signature Length - if (signatureLength > 0) - signature = dataTypes.ReadData(signatureLength, packetData); // Public key + for (int i = 0; i < biomesLength; i++) + { + // Biomes - 1.16.2 and above + // Don't use ReadNextVarInt because it cost too much time + dataTypes.SkipNextVarInt(packetData); + } } + else dataTypes.DropData(1024 * 4, packetData); // Biomes - 1.15 and above } + int dataSize = dataTypes.ReadNextVarInt(packetData); - handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature)); - break; - case 0x01: //Update gamemode - handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData)); - break; - case 0x02: //Update latency - int latency = dataTypes.ReadNextVarInt(packetData); - handler.OnLatencyUpdate(uuid, latency); //Update latency; - break; - case 0x03: //Update display name - if (dataTypes.ReadNextBool(packetData)) - { - PlayerInfo? player = handler.GetPlayerInfo(uuid); - if (player != null) - player.DisplayName = dataTypes.ReadNextString(packetData); - else - dataTypes.SkipNextString(packetData); - } - break; - case 0x04: //Player Leave - handler.OnPlayerLeave(uuid); - break; - default: - //Unknown player list item type - break; + pTerrain.ProcessChunkColumnData(chunkX, chunkZ, chunkMask, 0, false, chunksContinuous, currentDimension, packetData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } } } - } - else //MC 1.7.X does not provide UUID in tab-list updates - { - string name = dataTypes.ReadNextString(packetData); - bool online = dataTypes.ReadNextBool(packetData); - short ping = dataTypes.ReadNextShort(packetData); - Guid FakeUUID = new(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) - handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); - else handler.OnPlayerLeave(FakeUUID); - } - break; - case PacketTypesIn.PlayerRemove: - int numberOfLeavePlayers = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < numberOfLeavePlayers; ++i) - { - Guid playerUuid = dataTypes.ReadNextUUID(packetData); - handler.OnPlayerLeave(playerUuid); - } - break; - case PacketTypesIn.TabComplete: - if (protocolVersion >= MC_1_13_Version) - { - autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); - dataTypes.ReadNextVarInt(packetData); // Start of text to replace - dataTypes.ReadNextVarInt(packetData); // Length of text to replace - } + break; + case PacketTypesIn.MapData: + if (protocolVersion < MC_1_8_Version) + break; - int autocomplete_count = dataTypes.ReadNextVarInt(packetData); - autocomplete_result.Clear(); + int mapid = dataTypes.ReadNextVarInt(packetData); + byte scale = dataTypes.ReadNextByte(packetData); - for (int i = 0; i < autocomplete_count; i++) - { - autocomplete_result.Add(dataTypes.ReadNextString(packetData)); - if (protocolVersion >= MC_1_13_Version) + + // 1.9 + + bool trackingPosition = true; + + // 1.14+ + bool locked = false; + + // 1.17+ (locked and trackingPosition switched places) + if (protocolVersion >= MC_1_17_Version) { - // Skip optional tooltip for each tab-complete result - if (dataTypes.ReadNextBool(packetData)) - dataTypes.SkipNextString(packetData); - } - } + if (protocolVersion >= MC_1_14_Version) + locked = dataTypes.ReadNextBool(packetData); - autocomplete_received = true; - break; - case PacketTypesIn.PluginMessage: - String channel = dataTypes.ReadNextString(packetData); - // Length is unneeded as the whole remaining packetData is the entire payload of the packet. - if (protocolVersion < MC_1_8_Version) - pForge.ReadNextVarShort(packetData); - handler.OnPluginChannelMessage(channel, packetData.ToArray()); - return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); - case PacketTypesIn.Disconnect: - handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); - return false; - case PacketTypesIn.SetCompression: - if (protocolVersion >= MC_1_8_Version && protocolVersion < MC_1_9_Version) - compression_treshold = dataTypes.ReadNextVarInt(packetData); - break; - case PacketTypesIn.OpenWindow: - if (handler.GetInventoryEnabled()) - { - if (protocolVersion < MC_1_14_Version) - { - // MC 1.13 or lower - byte windowID = dataTypes.ReadNextByte(packetData); - string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper(); - ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type); - string title = dataTypes.ReadNextString(packetData); - byte slots = dataTypes.ReadNextByte(packetData); - Container inventory = new(windowID, inventoryType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); + if (protocolVersion >= MC_1_9_Version) + trackingPosition = dataTypes.ReadNextBool(packetData); } else + { + if (protocolVersion >= MC_1_9_Version) + trackingPosition = dataTypes.ReadNextBool(packetData); + + if (protocolVersion >= MC_1_14_Version) + locked = dataTypes.ReadNextBool(packetData); + } + + int iconcount = 0; + List icons = new(); + + // 1,9 + = needs tracking position to be true to get the icons + if (protocolVersion <= MC_1_16_5_Version || trackingPosition) + { + iconcount = dataTypes.ReadNextVarInt(packetData); + + for (int i = 0; i < iconcount; i++) + { + MapIcon mapIcon = new(); + + // 1.8 - 1.13 + if (protocolVersion < MC_1_13_2_Version) + { + byte directionAndtype = dataTypes.ReadNextByte(packetData); + byte direction, type; + + // 1.12.2+ + if (protocolVersion >= MC_1_12_2_Version) + { + direction = (byte)(directionAndtype & 0xF); + type = (byte)((directionAndtype >> 4) & 0xF); + } + else // 1.8 - 1.12 + { + direction = (byte)((directionAndtype >> 4) & 0xF); + type = (byte)(directionAndtype & 0xF); + } + + mapIcon.Type = (MapIconType)type; + mapIcon.Direction = direction; + } + + // 1.13.2+ + if (protocolVersion >= MC_1_13_2_Version) + mapIcon.Type = (MapIconType)dataTypes.ReadNextVarInt(packetData); + + mapIcon.X = dataTypes.ReadNextByte(packetData); + mapIcon.Z = dataTypes.ReadNextByte(packetData); + + // 1.13.2+ + if (protocolVersion >= MC_1_13_2_Version) + { + mapIcon.Direction = dataTypes.ReadNextByte(packetData); + + if (dataTypes.ReadNextBool(packetData)) // Has Display Name? + mapIcon.DisplayName = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + + icons.Add(mapIcon); + } + } + + byte columnsUpdated = dataTypes.ReadNextByte(packetData); // width + byte rowsUpdated = 0; // height + byte mapCoulmnX = 0; + byte mapRowZ = 0; + byte[]? colors = null; + + if (columnsUpdated > 0) + { + rowsUpdated = dataTypes.ReadNextByte(packetData); // height + mapCoulmnX = dataTypes.ReadNextByte(packetData); + mapRowZ = dataTypes.ReadNextByte(packetData); + colors = dataTypes.ReadNextByteArray(packetData); + } + + handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapRowZ, colors); + break; + case PacketTypesIn.TradeList: + if ((protocolVersion >= MC_1_14_Version) && (handler.GetInventoryEnabled())) { // MC 1.14 or greater int windowID = dataTypes.ReadNextVarInt(packetData); - int windowType = dataTypes.ReadNextVarInt(packetData); - string title = dataTypes.ReadNextString(packetData); - Container inventory = new(windowID, windowType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); - } - } - break; - case PacketTypesIn.CloseWindow: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - lock (window_actions) { window_actions[windowID] = 0; } - handler.OnInventoryClose(windowID); - } - break; - case PacketTypesIn.WindowItems: - if (handler.GetInventoryEnabled()) - { - byte windowId = dataTypes.ReadNextByte(packetData); - int stateId = -1; - int elements = 0; - - if (protocolVersion >= MC_1_17_1_Version) - { - // State ID and Elements as VarInt - 1.17.1 and above - stateId = dataTypes.ReadNextVarInt(packetData); - elements = dataTypes.ReadNextVarInt(packetData); - } - else - { - // Elements as Short - 1.17.0 and below - dataTypes.ReadNextShort(packetData); - } - - Dictionary inventorySlots = new(); - for (int slotId = 0; slotId < elements; slotId++) - { - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - if (item != null) - inventorySlots[slotId] = item; - } - - if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above - dataTypes.ReadNextItemSlot(packetData, itemPalette); - - handler.OnWindowItems(windowId, inventorySlots, stateId); - } - break; - case PacketTypesIn.WindowProperty: - byte containerId = dataTypes.ReadNextByte(packetData); - short propertyId = dataTypes.ReadNextShort(packetData); - short propertyValue = dataTypes.ReadNextShort(packetData); - - handler.OnWindowProperties(containerId, propertyId, propertyValue); - break; - case PacketTypesIn.SetSlot: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - int stateId = -1; - if (protocolVersion >= MC_1_17_1_Version) - stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above - short slotID = dataTypes.ReadNextShort(packetData); - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnSetSlot(windowID, slotID, item, stateId); - } - break; - case PacketTypesIn.WindowConfirmation: - if (handler.GetInventoryEnabled()) - { - byte windowID = dataTypes.ReadNextByte(packetData); - short actionID = dataTypes.ReadNextShort(packetData); - bool accepted = dataTypes.ReadNextBool(packetData); - if (!accepted) - SendWindowConfirmation(windowID, actionID, true); - } - break; - case PacketTypesIn.ResourcePackSend: - string url = dataTypes.ReadNextString(packetData); - string hash = dataTypes.ReadNextString(packetData); - bool forced = true; // Assume forced for MC 1.16 and below - if (protocolVersion >= MC_1_17_Version) - { - forced = dataTypes.ReadNextBool(packetData); - bool hasPromptMessage = dataTypes.ReadNextBool(packetData); // Has Prompt Message (Boolean) - 1.17 and above - if (hasPromptMessage) - dataTypes.SkipNextString(packetData); // Prompt Message (Optional Chat) - 1.17 and above - } - // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) - if (!url.StartsWith("http") && hash.Length != 40) // Some server may have null hash value - break; - //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory - byte[] responseHeader = Array.Empty(); - if (protocolVersion < MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses - responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); - SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); //Accepted pack - SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); //Successfully loaded - break; - case PacketTypesIn.SpawnEntity: - if (handler.GetEntityHandlingEnabled()) - { - Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); - handler.OnSpawnEntity(entity); - } - break; - case PacketTypesIn.EntityEquipment: - if (handler.GetEntityHandlingEnabled()) - { - int entityid = dataTypes.ReadNextVarInt(packetData); - if (protocolVersion >= MC_1_16_Version) - { - bool hasNext; - do + int size = dataTypes.ReadNextByte(packetData); + List trades = new(); + for (int tradeId = 0; tradeId < size; tradeId++) { - byte bitsData = dataTypes.ReadNextByte(packetData); - // Top bit set if another entry follows, and otherwise unset if this is the last item in the array - hasNext = (bitsData >> 7) == 1; - int slot2 = bitsData >> 1; - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); - } while (hasNext); - } - else - { - int slot2 = dataTypes.ReadNextVarInt(packetData); - Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); - } - } - break; - case PacketTypesIn.SpawnLivingEntity: - if (handler.GetEntityHandlingEnabled()) - { - Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true); - // packet before 1.15 has metadata at the end - // this is not handled in dataTypes.ReadNextEntity() - // we are simply ignoring leftover data in packet - handler.OnSpawnEntity(entity); - } - break; - case PacketTypesIn.SpawnPlayer: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Guid UUID = dataTypes.ReadNextUUID(packetData); - double X = dataTypes.ReadNextDouble(packetData); - double Y = dataTypes.ReadNextDouble(packetData); - double Z = dataTypes.ReadNextDouble(packetData); - byte Yaw = dataTypes.ReadNextByte(packetData); - byte Pitch = dataTypes.ReadNextByte(packetData); - - Location EntityLocation = new(X, Y, Z); - - handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); - } - break; - case PacketTypesIn.EntityEffect: - if (handler.GetEntityHandlingEnabled()) - { - int entityid = dataTypes.ReadNextVarInt(packetData); - Inventory.Effects effect = Effects.Speed; - int effectId = protocolVersion >= MC_1_18_2_Version ? - dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextByte(packetData); - if (Enum.TryParse(effectId.ToString(), out effect)) - { - int amplifier = dataTypes.ReadNextByte(packetData); - int duration = dataTypes.ReadNextVarInt(packetData); - byte flags = dataTypes.ReadNextByte(packetData); - - bool hasFactorData = false; - Dictionary? factorCodec = null; - - if (protocolVersion >= MC_1_19_Version) - { - hasFactorData = dataTypes.ReadNextBool(packetData); - if (hasFactorData) - factorCodec = dataTypes.ReadNextNbt(packetData); + VillagerTrade trade = dataTypes.ReadNextTrade(packetData, itemPalette); + trades.Add(trade); } - - handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, factorCodec); - } - } - break; - case PacketTypesIn.DestroyEntities: - if (handler.GetEntityHandlingEnabled()) - { - int entityCount = 1; // 1.17.0 has only one entity per packet - if (protocolVersion != MC_1_17_Version) - entityCount = dataTypes.ReadNextVarInt(packetData); // All other versions have a "count" field - int[] entityList = new int[entityCount]; - for (int i = 0; i < entityCount; i++) - { - entityList[i] = dataTypes.ReadNextVarInt(packetData); - } - handler.OnDestroyEntities(entityList); - } - break; - case PacketTypesIn.EntityPosition: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - bool OnGround = dataTypes.ReadNextBool(packetData); - DeltaX /= (128 * 32); - DeltaY /= (128 * 32); - DeltaZ /= (128 * 32); - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); - } - break; - case PacketTypesIn.EntityPositionAndRotation: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); - byte _yaw = dataTypes.ReadNextByte(packetData); - byte _pitch = dataTypes.ReadNextByte(packetData); - bool OnGround = dataTypes.ReadNextBool(packetData); - DeltaX /= (128 * 32); - DeltaY /= (128 * 32); - DeltaZ /= (128 * 32); - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); - } - break; - case PacketTypesIn.EntityProperties: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - int NumberOfProperties = protocolVersion >= MC_1_17_Version ? dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextInt(packetData); - Dictionary keys = new(); - for (int i = 0; i < NumberOfProperties; i++) - { - string _key = dataTypes.ReadNextString(packetData); - Double _value = dataTypes.ReadNextDouble(packetData); - - List op0 = new(); - List op1 = new(); - List op2 = new(); - int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData); - for (int j = 0; j < NumberOfModifiers; j++) + VillagerInfo villagerInfo = new() { - dataTypes.ReadNextUUID(packetData); - Double amount = dataTypes.ReadNextDouble(packetData); - byte operation = dataTypes.ReadNextByte(packetData); - switch (operation) + Level = dataTypes.ReadNextVarInt(packetData), + Experience = dataTypes.ReadNextVarInt(packetData), + IsRegularVillager = dataTypes.ReadNextBool(packetData), + CanRestock = dataTypes.ReadNextBool(packetData) + }; + handler.OnTradeList(windowID, trades, villagerInfo); + } + break; + case PacketTypesIn.Title: + if (protocolVersion >= MC_1_8_Version) + { + int action2 = dataTypes.ReadNextVarInt(packetData); + string titletext = String.Empty; + string subtitletext = String.Empty; + string actionbartext = String.Empty; + string json = String.Empty; + int fadein = -1; + int stay = -1; + int fadeout = -1; + if (protocolVersion >= MC_1_10_Version) + { + if (action2 == 0) { - case 0: op0.Add(amount); break; - case 1: op1.Add(amount); break; - case 2: op2.Add(amount + 1); break; + json = titletext; + titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 1) + { + json = subtitletext; + subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 2) + { + json = actionbartext; + actionbartext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 3) + { + fadein = dataTypes.ReadNextInt(packetData); + stay = dataTypes.ReadNextInt(packetData); + fadeout = dataTypes.ReadNextInt(packetData); } } - if (op0.Count > 0) _value += op0.Sum(); - if (op1.Count > 0) _value *= 1 + op1.Sum(); - if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x); - keys.Add(_key, _value); + else + { + if (action2 == 0) + { + json = titletext; + titletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 1) + { + json = subtitletext; + subtitletext = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + } + else if (action2 == 2) + { + fadein = dataTypes.ReadNextInt(packetData); + stay = dataTypes.ReadNextInt(packetData); + fadeout = dataTypes.ReadNextInt(packetData); + } + } + handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json); } - handler.OnEntityProperties(EntityID, keys); - } - break; - case PacketTypesIn.EntityMetadata: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); + break; + case PacketTypesIn.MultiBlockChange: + if (handler.GetTerrainEnabled()) + { + if (protocolVersion >= MC_1_16_2_Version) + { + long chunkSection = dataTypes.ReadNextLong(packetData); + int sectionX = (int)(chunkSection >> 42); + int sectionY = (int)((chunkSection << 44) >> 44); + int sectionZ = (int)((chunkSection << 22) >> 42); + dataTypes.ReadNextBool(packetData); // Useless boolean (Related to light update) + int blocksSize = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < blocksSize; i++) + { + 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); - int healthField; // See https://wiki.vg/Entity_metadata#Living_Entity - if (protocolVersion > MC_1_19_2_Version) - throw new NotImplementedException(Translations.exception_palette_healthfield); - else if (protocolVersion >= MC_1_17_Version) // 1.17 and above - healthField = 9; - else if (protocolVersion >= MC_1_14_Version) // 1.14 and above - healthField = 8; - else if (protocolVersion >= MC_1_10_Version) // 1.10 and above - healthField = 7; + Block block = new((ushort)blockId); + int blockX = (sectionX * 16) + localX; + int blockY = (sectionY * 16) + localY; + int blockZ = (sectionZ * 16) + localZ; + + Location location = new(blockX, blockY, blockZ); + + handler.OnBlockChange(location, block); + } + } + else + { + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); + int recordCount = protocolVersion < MC_1_8_Version + ? (int)dataTypes.ReadNextShort(packetData) + : dataTypes.ReadNextVarInt(packetData); + + for (int i = 0; i < recordCount; i++) + { + byte locationXZ; + ushort blockIdMeta; + int blockY; + + if (protocolVersion < MC_1_8_Version) + { + blockIdMeta = dataTypes.ReadNextUShort(packetData); + blockY = (ushort)dataTypes.ReadNextByte(packetData); + locationXZ = dataTypes.ReadNextByte(packetData); + } + else + { + locationXZ = dataTypes.ReadNextByte(packetData); + blockY = (ushort)dataTypes.ReadNextByte(packetData); + blockIdMeta = (ushort)dataTypes.ReadNextVarInt(packetData); + } + + int blockX = locationXZ >> 4; + int blockZ = locationXZ & 0x0F; + + Location location = new(chunkX, chunkZ, blockX, blockY, blockZ); + Block block = new(blockIdMeta); + handler.OnBlockChange(location, block); + } + } + } + break; + case PacketTypesIn.ServerData: + string motd = "-"; + bool hasMotd = dataTypes.ReadNextBool(packetData); + if (hasMotd) + motd = ChatParser.ParseText(dataTypes.ReadNextString(packetData)); + + string iconBase64 = "-"; + bool hasIcon = dataTypes.ReadNextBool(packetData); + if (hasIcon) + iconBase64 = dataTypes.ReadNextString(packetData); + + bool previewsChat = false; + if (protocolVersion < MC_1_19_3_Version) + previewsChat = dataTypes.ReadNextBool(packetData); + + handler.OnServerDataRecived(hasMotd, motd, hasIcon, iconBase64, previewsChat); + break; + case PacketTypesIn.BlockChange: + if (handler.GetTerrainEnabled()) + { + if (protocolVersion < MC_1_8_Version) + { + int blockX = dataTypes.ReadNextInt(packetData); + int blockY = dataTypes.ReadNextByte(packetData); + int blockZ = dataTypes.ReadNextInt(packetData); + short blockId = (short)dataTypes.ReadNextVarInt(packetData); + byte blockMeta = dataTypes.ReadNextByte(packetData); + + Location location = new(blockX, blockY, blockZ); + Block block = new(blockId, blockMeta); + handler.OnBlockChange(location, block); + } + else + { + Location location = dataTypes.ReadNextLocation(packetData); + Block block = new((ushort)dataTypes.ReadNextVarInt(packetData)); + handler.OnBlockChange(location, block); + } + } + break; + case PacketTypesIn.SetDisplayChatPreview: + bool previewsChatSetting = dataTypes.ReadNextBool(packetData); + handler.OnChatPreviewSettingUpdate(previewsChatSetting); + break; + case PacketTypesIn.ChatSuggestions: + break; + case PacketTypesIn.MapChunkBulk: + if (protocolVersion < MC_1_9_Version && handler.GetTerrainEnabled()) + { + int chunkCount; + bool hasSkyLight; + Queue chunkData = packetData; + + //Read global fields + if (protocolVersion < MC_1_8_Version) + { + chunkCount = dataTypes.ReadNextShort(packetData); + int compressedDataSize = dataTypes.ReadNextInt(packetData); + hasSkyLight = dataTypes.ReadNextBool(packetData); + byte[] compressed = dataTypes.ReadData(compressedDataSize, packetData); + byte[] decompressed = ZlibUtils.Decompress(compressed); + chunkData = new Queue(decompressed); + } + else + { + hasSkyLight = dataTypes.ReadNextBool(packetData); + chunkCount = dataTypes.ReadNextVarInt(packetData); + } + + //Read chunk records + int[] chunkXs = new int[chunkCount]; + int[] chunkZs = new int[chunkCount]; + ushort[] chunkMasks = new ushort[chunkCount]; + ushort[] addBitmaps = new ushort[chunkCount]; + for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + { + chunkXs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkZs[chunkColumnNo] = dataTypes.ReadNextInt(packetData); + chunkMasks[chunkColumnNo] = dataTypes.ReadNextUShort(packetData); + addBitmaps[chunkColumnNo] = protocolVersion < MC_1_8_Version + ? dataTypes.ReadNextUShort(packetData) + : (ushort)0; + } + + //Process chunk records + for (int chunkColumnNo = 0; chunkColumnNo < chunkCount; chunkColumnNo++) + { + pTerrain.ProcessChunkColumnData(chunkXs[chunkColumnNo], chunkZs[chunkColumnNo], chunkMasks[chunkColumnNo], addBitmaps[chunkColumnNo], hasSkyLight, true, currentDimension, chunkData); + Interlocked.Decrement(ref handler.GetWorld().chunkLoadNotCompleted); + } + + } + break; + case PacketTypesIn.UnloadChunk: + if (protocolVersion >= MC_1_9_Version && handler.GetTerrainEnabled()) + { + int chunkX = dataTypes.ReadNextInt(packetData); + int chunkZ = dataTypes.ReadNextInt(packetData); + + // Warning: It is legal to include unloaded chunks in the UnloadChunk packet. + // Since chunks that have not been loaded are not recorded, this may result + // in loading chunks that should be unloaded and inaccurate statistics. + if (handler.GetWorld()[chunkX, chunkZ] != null) + Interlocked.Decrement(ref handler.GetWorld().chunkCnt); + + handler.GetWorld()[chunkX, chunkZ] = null; + } + break; + case PacketTypesIn.ChangeGameState: + if (protocolVersion >= MC_1_15_2_Version) + { + byte reason = dataTypes.ReadNextByte(packetData); + float state = dataTypes.ReadNextFloat(packetData); + + handler.OnGameEvent(reason, state); + } + break; + case PacketTypesIn.PlayerInfo: + if (protocolVersion >= MC_1_19_3_Version) + { + byte actionBitset = dataTypes.ReadNextByte(packetData); + int numberOfActions = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < numberOfActions; i++) + { + Guid playerUuid = dataTypes.ReadNextUUID(packetData); + + if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player + { + string name = dataTypes.ReadNextString(packetData); + int numberOfProperties = dataTypes.ReadNextVarInt(packetData); + for (int j = 0; j < numberOfProperties; ++j) + { + dataTypes.SkipNextString(packetData); + dataTypes.SkipNextString(packetData); + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + handler.OnPlayerJoin(new(name, playerUuid)); + } + + PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; + if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat + { + bool hasSignatureData = dataTypes.ReadNextBool(packetData); + if (hasSignatureData) + { + Guid chatUuid = dataTypes.ReadNextUUID(packetData); + long publicKeyExpiryTime = dataTypes.ReadNextLong(packetData); + byte[] encodedPublicKey = dataTypes.ReadNextByteArray(packetData); + byte[] publicKeySignature = dataTypes.ReadNextByteArray(packetData); + player.SetPublicKey(chatUuid, publicKeyExpiryTime, encodedPublicKey, publicKeySignature); + } + else + { + player.ClearPublicKey(); + } + } + if ((actionBitset & 1 << 2) > 0) // Actions bit 2: update gamemode + { + handler.OnGamemodeUpdate(playerUuid, dataTypes.ReadNextVarInt(packetData)); + } + if ((actionBitset & (1 << 3)) > 0) // Actions bit 3: update listed + { + player.Listed = dataTypes.ReadNextBool(packetData); + } + if ((actionBitset & (1 << 4)) > 0) // Actions bit 4: update latency + { + int latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(playerUuid, latency); //Update latency; + } + if ((actionBitset & (1 << 5)) > 0) // Actions bit 5: update display name + { + if (dataTypes.ReadNextBool(packetData)) + player.DisplayName = dataTypes.ReadNextString(packetData); + else + player.DisplayName = null; + } + } + } + else if (protocolVersion >= MC_1_8_Version) + { + int action = dataTypes.ReadNextVarInt(packetData); // Action Name + int numberOfPlayers = dataTypes.ReadNextVarInt(packetData); // Number Of Players + + for (int i = 0; i < numberOfPlayers; i++) + { + Guid uuid = dataTypes.ReadNextUUID(packetData); // Player UUID + + switch (action) + { + case 0x00: //Player Join (Add player since 1.19) + string name = dataTypes.ReadNextString(packetData); // Player name + int propNum = dataTypes.ReadNextVarInt(packetData); // Number of properties in the following array + + // Property: Tuple[]? properties = useProperty ? + new Tuple[propNum] : null; + for (int p = 0; p < propNum; p++) + { + string propertyName = dataTypes.ReadNextString(packetData); // Name: String (32767) + string val = dataTypes.ReadNextString(packetData); // Value: String (32767) + string? propertySignature = null; + if (dataTypes.ReadNextBool(packetData)) // Is Signed + propertySignature = dataTypes.ReadNextString(packetData); // Signature: String (32767) + if (useProperty) + properties![p] = new(propertyName, val, propertySignature); + } +#pragma warning restore CS0162 // Unreachable code detected + + int gameMode = dataTypes.ReadNextVarInt(packetData); // Gamemode + handler.OnGamemodeUpdate(uuid, gameMode); + + int ping = dataTypes.ReadNextVarInt(packetData); // Ping + + string? displayName = null; + if (dataTypes.ReadNextBool(packetData)) // Has display name + displayName = dataTypes.ReadNextString(packetData); // Display name + + // 1.19 Additions + long? keyExpiration = null; + byte[]? publicKey = null, signature = null; + if (protocolVersion >= MC_1_19_Version) + { + if (dataTypes.ReadNextBool(packetData)) // Has Sig Data (if true, red the following fields) + { + keyExpiration = dataTypes.ReadNextLong(packetData); // Timestamp + + int publicKeyLength = dataTypes.ReadNextVarInt(packetData); // Public Key Length + if (publicKeyLength > 0) + publicKey = dataTypes.ReadData(publicKeyLength, packetData); // Public key + + int signatureLength = dataTypes.ReadNextVarInt(packetData); // Signature Length + if (signatureLength > 0) + signature = dataTypes.ReadData(signatureLength, packetData); // Public key + } + } + + handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature)); + break; + case 0x01: //Update gamemode + handler.OnGamemodeUpdate(uuid, dataTypes.ReadNextVarInt(packetData)); + break; + case 0x02: //Update latency + int latency = dataTypes.ReadNextVarInt(packetData); + handler.OnLatencyUpdate(uuid, latency); //Update latency; + break; + case 0x03: //Update display name + if (dataTypes.ReadNextBool(packetData)) + { + PlayerInfo? player = handler.GetPlayerInfo(uuid); + if (player != null) + player.DisplayName = dataTypes.ReadNextString(packetData); + else + dataTypes.SkipNextString(packetData); + } + break; + case 0x04: //Player Leave + handler.OnPlayerLeave(uuid); + break; + default: + //Unknown player list item type + break; + } + } + } + else //MC 1.7.X does not provide UUID in tab-list updates + { + string name = dataTypes.ReadNextString(packetData); + bool online = dataTypes.ReadNextBool(packetData); + short ping = dataTypes.ReadNextShort(packetData); + Guid FakeUUID = new(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); + if (online) + handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); + else handler.OnPlayerLeave(FakeUUID); + } + break; + case PacketTypesIn.PlayerRemove: + int numberOfLeavePlayers = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < numberOfLeavePlayers; ++i) + { + Guid playerUuid = dataTypes.ReadNextUUID(packetData); + handler.OnPlayerLeave(playerUuid); + } + break; + case PacketTypesIn.TabComplete: + if (protocolVersion >= MC_1_13_Version) + { + autocomplete_transaction_id = dataTypes.ReadNextVarInt(packetData); + dataTypes.ReadNextVarInt(packetData); // Start of text to replace + dataTypes.ReadNextVarInt(packetData); // Length of text to replace + } + + int autocomplete_count = dataTypes.ReadNextVarInt(packetData); + autocomplete_result.Clear(); + + for (int i = 0; i < autocomplete_count; i++) + { + autocomplete_result.Add(dataTypes.ReadNextString(packetData)); + if (protocolVersion >= MC_1_13_Version) + { + // Skip optional tooltip for each tab-complete result + if (dataTypes.ReadNextBool(packetData)) + dataTypes.SkipNextString(packetData); + } + } + + autocomplete_received = true; + break; + case PacketTypesIn.PluginMessage: + String channel = dataTypes.ReadNextString(packetData); + // Length is unneeded as the whole remaining packetData is the entire payload of the packet. + if (protocolVersion < MC_1_8_Version) + pForge.ReadNextVarShort(packetData); + handler.OnPluginChannelMessage(channel, packetData.ToArray()); + return pForge.HandlePluginMessage(channel, packetData, ref currentDimension); + case PacketTypesIn.Disconnect: + handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(dataTypes.ReadNextString(packetData))); + return false; + case PacketTypesIn.SetCompression: + if (protocolVersion >= MC_1_8_Version && protocolVersion < MC_1_9_Version) + compression_treshold = dataTypes.ReadNextVarInt(packetData); + break; + case PacketTypesIn.OpenWindow: + if (handler.GetInventoryEnabled()) + { + if (protocolVersion < MC_1_14_Version) + { + // MC 1.13 or lower + byte windowID = dataTypes.ReadNextByte(packetData); + string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper(); + ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type); + string title = dataTypes.ReadNextString(packetData); + byte slots = dataTypes.ReadNextByte(packetData); + Container inventory = new(windowID, inventoryType, ChatParser.ParseText(title)); + handler.OnInventoryOpen(windowID, inventory); + } + else + { + // MC 1.14 or greater + int windowID = dataTypes.ReadNextVarInt(packetData); + int windowType = dataTypes.ReadNextVarInt(packetData); + string title = dataTypes.ReadNextString(packetData); + Container inventory = new(windowID, windowType, ChatParser.ParseText(title)); + handler.OnInventoryOpen(windowID, inventory); + } + } + break; + case PacketTypesIn.CloseWindow: + if (handler.GetInventoryEnabled()) + { + byte windowID = dataTypes.ReadNextByte(packetData); + lock (window_actions) { window_actions[windowID] = 0; } + handler.OnInventoryClose(windowID); + } + break; + case PacketTypesIn.WindowItems: + if (handler.GetInventoryEnabled()) + { + byte windowId = dataTypes.ReadNextByte(packetData); + int stateId = -1; + int elements = 0; + + if (protocolVersion >= MC_1_17_1_Version) + { + // State ID and Elements as VarInt - 1.17.1 and above + stateId = dataTypes.ReadNextVarInt(packetData); + elements = dataTypes.ReadNextVarInt(packetData); + } + else + { + // Elements as Short - 1.17.0 and below + dataTypes.ReadNextShort(packetData); + } + + Dictionary inventorySlots = new(); + for (int slotId = 0; slotId < elements; slotId++) + { + Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + if (item != null) + inventorySlots[slotId] = item; + } + + if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above + dataTypes.ReadNextItemSlot(packetData, itemPalette); + + handler.OnWindowItems(windowId, inventorySlots, stateId); + } + break; + case PacketTypesIn.WindowProperty: + byte containerId = dataTypes.ReadNextByte(packetData); + short propertyId = dataTypes.ReadNextShort(packetData); + short propertyValue = dataTypes.ReadNextShort(packetData); + + handler.OnWindowProperties(containerId, propertyId, propertyValue); + break; + case PacketTypesIn.SetSlot: + if (handler.GetInventoryEnabled()) + { + byte windowID = dataTypes.ReadNextByte(packetData); + int stateId = -1; + if (protocolVersion >= MC_1_17_1_Version) + stateId = dataTypes.ReadNextVarInt(packetData); // State ID - 1.17.1 and above + short slotID = dataTypes.ReadNextShort(packetData); + Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnSetSlot(windowID, slotID, item, stateId); + } + break; + case PacketTypesIn.WindowConfirmation: + if (handler.GetInventoryEnabled()) + { + byte windowID = dataTypes.ReadNextByte(packetData); + short actionID = dataTypes.ReadNextShort(packetData); + bool accepted = dataTypes.ReadNextBool(packetData); + if (!accepted) + SendWindowConfirmation(windowID, actionID, true); + } + break; + case PacketTypesIn.ResourcePackSend: + string url = dataTypes.ReadNextString(packetData); + string hash = dataTypes.ReadNextString(packetData); + bool forced = true; // Assume forced for MC 1.16 and below + if (protocolVersion >= MC_1_17_Version) + { + forced = dataTypes.ReadNextBool(packetData); + bool hasPromptMessage = dataTypes.ReadNextBool(packetData); // Has Prompt Message (Boolean) - 1.17 and above + if (hasPromptMessage) + dataTypes.SkipNextString(packetData); // Prompt Message (Optional Chat) - 1.17 and above + } + // Some server plugins may send invalid resource packs to probe the client and we need to ignore them (issue #1056) + if (!url.StartsWith("http") && hash.Length != 40) // Some server may have null hash value + break; + //Send back "accepted" and "successfully loaded" responses for plugins or server config making use of resource pack mandatory + byte[] responseHeader = Array.Empty(); + if (protocolVersion < MC_1_10_Version) //MC 1.10 does not include resource pack hash in responses + responseHeader = dataTypes.ConcatBytes(DataTypes.GetVarInt(hash.Length), Encoding.UTF8.GetBytes(hash)); + SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(3))); //Accepted pack + SendPacket(PacketTypesOut.ResourcePackStatus, dataTypes.ConcatBytes(responseHeader, DataTypes.GetVarInt(0))); //Successfully loaded + break; + case PacketTypesIn.SpawnEntity: + if (handler.GetEntityHandlingEnabled()) + { + Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, false); + handler.OnSpawnEntity(entity); + } + break; + case PacketTypesIn.EntityEquipment: + if (handler.GetEntityHandlingEnabled()) + { + int entityid = dataTypes.ReadNextVarInt(packetData); + if (protocolVersion >= MC_1_16_Version) + { + bool hasNext; + do + { + byte bitsData = dataTypes.ReadNextByte(packetData); + // Top bit set if another entry follows, and otherwise unset if this is the last item in the array + hasNext = (bitsData >> 7) == 1; + int slot2 = bitsData >> 1; + Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnEntityEquipment(entityid, slot2, item); + } while (hasNext); + } + else + { + int slot2 = dataTypes.ReadNextVarInt(packetData); + Item? item = dataTypes.ReadNextItemSlot(packetData, itemPalette); + handler.OnEntityEquipment(entityid, slot2, item); + } + } + break; + case PacketTypesIn.SpawnLivingEntity: + if (handler.GetEntityHandlingEnabled()) + { + Entity entity = dataTypes.ReadNextEntity(packetData, entityPalette, true); + // packet before 1.15 has metadata at the end + // this is not handled in dataTypes.ReadNextEntity() + // we are simply ignoring leftover data in packet + handler.OnSpawnEntity(entity); + } + break; + case PacketTypesIn.SpawnPlayer: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Guid UUID = dataTypes.ReadNextUUID(packetData); + double X = dataTypes.ReadNextDouble(packetData); + double Y = dataTypes.ReadNextDouble(packetData); + double Z = dataTypes.ReadNextDouble(packetData); + byte Yaw = dataTypes.ReadNextByte(packetData); + byte Pitch = dataTypes.ReadNextByte(packetData); + + Location EntityLocation = new(X, Y, Z); + + handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); + } + break; + case PacketTypesIn.EntityEffect: + if (handler.GetEntityHandlingEnabled()) + { + int entityid = dataTypes.ReadNextVarInt(packetData); + Inventory.Effects effect = Effects.Speed; + int effectId = protocolVersion >= MC_1_18_2_Version ? + dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextByte(packetData); + if (Enum.TryParse(effectId.ToString(), out effect)) + { + int amplifier = dataTypes.ReadNextByte(packetData); + int duration = dataTypes.ReadNextVarInt(packetData); + byte flags = dataTypes.ReadNextByte(packetData); + + bool hasFactorData = false; + Dictionary? factorCodec = null; + + if (protocolVersion >= MC_1_19_Version) + { + hasFactorData = dataTypes.ReadNextBool(packetData); + if (hasFactorData) + factorCodec = dataTypes.ReadNextNbt(packetData); + } + + handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, factorCodec); + } + } + break; + case PacketTypesIn.DestroyEntities: + if (handler.GetEntityHandlingEnabled()) + { + int entityCount = 1; // 1.17.0 has only one entity per packet + if (protocolVersion != MC_1_17_Version) + entityCount = dataTypes.ReadNextVarInt(packetData); // All other versions have a "count" field + int[] entityList = new int[entityCount]; + for (int i = 0; i < entityCount; i++) + { + entityList[i] = dataTypes.ReadNextVarInt(packetData); + } + handler.OnDestroyEntities(entityList); + } + break; + case PacketTypesIn.EntityPosition: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + bool OnGround = dataTypes.ReadNextBool(packetData); + DeltaX /= (128 * 32); + DeltaY /= (128 * 32); + DeltaZ /= (128 * 32); + handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); + } + break; + case PacketTypesIn.EntityPositionAndRotation: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Double DeltaX = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaY = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + Double DeltaZ = Convert.ToDouble(dataTypes.ReadNextShort(packetData)); + byte _yaw = dataTypes.ReadNextByte(packetData); + byte _pitch = dataTypes.ReadNextByte(packetData); + bool OnGround = dataTypes.ReadNextBool(packetData); + DeltaX /= (128 * 32); + DeltaY /= (128 * 32); + DeltaZ /= (128 * 32); + handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); + } + break; + case PacketTypesIn.EntityProperties: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + int NumberOfProperties = protocolVersion >= MC_1_17_Version ? dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextInt(packetData); + Dictionary keys = new(); + for (int i = 0; i < NumberOfProperties; i++) + { + string _key = dataTypes.ReadNextString(packetData); + Double _value = dataTypes.ReadNextDouble(packetData); + + List op0 = new(); + List op1 = new(); + List op2 = new(); + int NumberOfModifiers = dataTypes.ReadNextVarInt(packetData); + for (int j = 0; j < NumberOfModifiers; j++) + { + dataTypes.ReadNextUUID(packetData); + Double amount = dataTypes.ReadNextDouble(packetData); + byte operation = dataTypes.ReadNextByte(packetData); + switch (operation) + { + case 0: op0.Add(amount); break; + case 1: op1.Add(amount); break; + case 2: op2.Add(amount + 1); break; + } + } + if (op0.Count > 0) _value += op0.Sum(); + if (op1.Count > 0) _value *= 1 + op1.Sum(); + if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x); + keys.Add(_key, _value); + } + handler.OnEntityProperties(EntityID, keys); + } + break; + case PacketTypesIn.EntityMetadata: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); + + int healthField; // See https://wiki.vg/Entity_metadata#Living_Entity + if (protocolVersion > MC_1_19_2_Version) + throw new NotImplementedException(Translations.exception_palette_healthfield); + else if (protocolVersion >= MC_1_17_Version) // 1.17 and above + healthField = 9; + else if (protocolVersion >= MC_1_14_Version) // 1.14 and above + healthField = 8; + else if (protocolVersion >= MC_1_10_Version) // 1.10 and above + healthField = 7; + else + throw new NotImplementedException(Translations.exception_palette_healthfield); + + if (metadata.TryGetValue(healthField, out object? healthObj) && healthObj != null && healthObj.GetType() == typeof(float)) + handler.OnEntityHealth(EntityID, (float)healthObj); + + handler.OnEntityMetadata(EntityID, metadata); + } + break; + case PacketTypesIn.EntityStatus: + if (handler.GetEntityHandlingEnabled()) + { + int entityId = dataTypes.ReadNextInt(packetData); + byte status = dataTypes.ReadNextByte(packetData); + handler.OnEntityStatus(entityId, status); + } + break; + case PacketTypesIn.TimeUpdate: + long WorldAge = dataTypes.ReadNextLong(packetData); + long TimeOfday = dataTypes.ReadNextLong(packetData); + handler.OnTimeUpdate(WorldAge, TimeOfday); + break; + case PacketTypesIn.EntityTeleport: + if (handler.GetEntityHandlingEnabled()) + { + int EntityID = dataTypes.ReadNextVarInt(packetData); + Double X = dataTypes.ReadNextDouble(packetData); + Double Y = dataTypes.ReadNextDouble(packetData); + Double Z = dataTypes.ReadNextDouble(packetData); + byte EntityYaw = dataTypes.ReadNextByte(packetData); + byte EntityPitch = dataTypes.ReadNextByte(packetData); + bool OnGround = dataTypes.ReadNextBool(packetData); + handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround); + } + break; + case PacketTypesIn.UpdateHealth: + float health = dataTypes.ReadNextFloat(packetData); + int food; + if (protocolVersion >= MC_1_8_Version) + food = dataTypes.ReadNextVarInt(packetData); else - throw new NotImplementedException(Translations.exception_palette_healthfield); + food = dataTypes.ReadNextShort(packetData); + dataTypes.ReadNextFloat(packetData); // Food Saturation + handler.OnUpdateHealth(health, food); + break; + case PacketTypesIn.SetExperience: + float experiencebar = dataTypes.ReadNextFloat(packetData); + int totalexperience, level; - if (metadata.TryGetValue(healthField, out object? healthObj) && healthObj != null && healthObj.GetType() == typeof(float)) - handler.OnEntityHealth(EntityID, (float)healthObj); + if (protocolVersion >= MC_1_19_3_Version) + { + totalexperience = dataTypes.ReadNextVarInt(packetData); + level = dataTypes.ReadNextVarInt(packetData); + } + else + { + level = dataTypes.ReadNextVarInt(packetData); + totalexperience = dataTypes.ReadNextVarInt(packetData); + } - handler.OnEntityMetadata(EntityID, metadata); - } - break; - case PacketTypesIn.EntityStatus: - if (handler.GetEntityHandlingEnabled()) - { - int entityId = dataTypes.ReadNextInt(packetData); - byte status = dataTypes.ReadNextByte(packetData); - handler.OnEntityStatus(entityId, status); - } - break; - case PacketTypesIn.TimeUpdate: - long WorldAge = dataTypes.ReadNextLong(packetData); - long TimeOfday = dataTypes.ReadNextLong(packetData); - handler.OnTimeUpdate(WorldAge, TimeOfday); - break; - case PacketTypesIn.EntityTeleport: - if (handler.GetEntityHandlingEnabled()) - { - int EntityID = dataTypes.ReadNextVarInt(packetData); - Double X = dataTypes.ReadNextDouble(packetData); - Double Y = dataTypes.ReadNextDouble(packetData); - Double Z = dataTypes.ReadNextDouble(packetData); - byte EntityYaw = dataTypes.ReadNextByte(packetData); - byte EntityPitch = dataTypes.ReadNextByte(packetData); - bool OnGround = dataTypes.ReadNextBool(packetData); - handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround); - } - break; - case PacketTypesIn.UpdateHealth: - float health = dataTypes.ReadNextFloat(packetData); - int food; - if (protocolVersion >= MC_1_8_Version) - food = dataTypes.ReadNextVarInt(packetData); - else - food = dataTypes.ReadNextShort(packetData); - dataTypes.ReadNextFloat(packetData); // Food Saturation - handler.OnUpdateHealth(health, food); - break; - case PacketTypesIn.SetExperience: - float experiencebar = dataTypes.ReadNextFloat(packetData); - int totalexperience, level; + handler.OnSetExperience(experiencebar, level, totalexperience); + break; + case PacketTypesIn.Explosion: + Location explosionLocation; + if (protocolVersion >= MC_1_19_3_Version) + explosionLocation = new(dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); + else + explosionLocation = new(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); - if (protocolVersion >= MC_1_19_3_Version) - { - totalexperience = dataTypes.ReadNextVarInt(packetData); - level = dataTypes.ReadNextVarInt(packetData); - } - else - { - level = dataTypes.ReadNextVarInt(packetData); - totalexperience = dataTypes.ReadNextVarInt(packetData); - } - - handler.OnSetExperience(experiencebar, level, totalexperience); - break; - case PacketTypesIn.Explosion: - Location explosionLocation; - if (protocolVersion >= MC_1_19_3_Version) - explosionLocation = new(dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData), dataTypes.ReadNextDouble(packetData)); - else - explosionLocation = new(dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData), dataTypes.ReadNextFloat(packetData)); - - float explosionStrength = dataTypes.ReadNextFloat(packetData); - int explosionBlockCount = protocolVersion >= MC_1_17_Version - ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextInt(packetData); - - for (int i = 0; i < explosionBlockCount; i++) - dataTypes.ReadData(3, packetData); - - float playerVelocityX = dataTypes.ReadNextFloat(packetData); - float playerVelocityY = dataTypes.ReadNextFloat(packetData); - float playerVelocityZ = dataTypes.ReadNextFloat(packetData); - - handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); - break; - case PacketTypesIn.HeldItemChange: - byte slot = dataTypes.ReadNextByte(packetData); - handler.OnHeldItemChange(slot); - break; - case PacketTypesIn.ScoreboardObjective: - string objectivename = dataTypes.ReadNextString(packetData); - byte mode = dataTypes.ReadNextByte(packetData); - string objectivevalue = String.Empty; - int type2 = -1; - if (mode == 0 || mode == 2) - { - objectivevalue = dataTypes.ReadNextString(packetData); - type2 = dataTypes.ReadNextVarInt(packetData); - } - handler.OnScoreboardObjective(objectivename, mode, objectivevalue, type2); - break; - case PacketTypesIn.UpdateScore: - string entityname = dataTypes.ReadNextString(packetData); - int action3 = protocolVersion >= MC_1_18_2_Version + float explosionStrength = dataTypes.ReadNextFloat(packetData); + int explosionBlockCount = protocolVersion >= MC_1_17_Version ? dataTypes.ReadNextVarInt(packetData) - : dataTypes.ReadNextByte(packetData); - string objectivename2 = string.Empty; - int value = -1; - if (action3 != 1 || protocolVersion >= MC_1_8_Version) - objectivename2 = dataTypes.ReadNextString(packetData); - if (action3 != 1) - value = dataTypes.ReadNextVarInt(packetData); - handler.OnUpdateScore(entityname, action3, objectivename2, value); - break; - case PacketTypesIn.BlockChangedAck: - handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); - break; - case PacketTypesIn.BlockBreakAnimation: - if (handler.GetEntityHandlingEnabled() && handler.GetTerrainEnabled()) - { - int playerId = dataTypes.ReadNextVarInt(packetData); - Location blockLocation = dataTypes.ReadNextLocation(packetData); - byte stage = dataTypes.ReadNextByte(packetData); - handler.OnBlockBreakAnimation(playerId, blockLocation, stage); - } - break; - case PacketTypesIn.EntityAnimation: - if (handler.GetEntityHandlingEnabled()) - { - int playerId2 = dataTypes.ReadNextVarInt(packetData); - byte animation = dataTypes.ReadNextByte(packetData); - handler.OnEntityAnimation(playerId2, animation); - } - break; - default: - return false; //Ignored packet - } - return true; //Packet processed -#if Release + : dataTypes.ReadNextInt(packetData); + + for (int i = 0; i < explosionBlockCount; i++) + dataTypes.ReadData(3, packetData); + + float playerVelocityX = dataTypes.ReadNextFloat(packetData); + float playerVelocityY = dataTypes.ReadNextFloat(packetData); + float playerVelocityZ = dataTypes.ReadNextFloat(packetData); + + handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); + break; + case PacketTypesIn.HeldItemChange: + byte slot = dataTypes.ReadNextByte(packetData); + handler.OnHeldItemChange(slot); + break; + case PacketTypesIn.ScoreboardObjective: + string objectivename = dataTypes.ReadNextString(packetData); + byte mode = dataTypes.ReadNextByte(packetData); + string objectivevalue = String.Empty; + int type2 = -1; + if (mode == 0 || mode == 2) + { + objectivevalue = dataTypes.ReadNextString(packetData); + type2 = dataTypes.ReadNextVarInt(packetData); + } + handler.OnScoreboardObjective(objectivename, mode, objectivevalue, type2); + break; + case PacketTypesIn.UpdateScore: + string entityname = dataTypes.ReadNextString(packetData); + int action3 = protocolVersion >= MC_1_18_2_Version + ? dataTypes.ReadNextVarInt(packetData) + : dataTypes.ReadNextByte(packetData); + string objectivename2 = string.Empty; + int value = -1; + if (action3 != 1 || protocolVersion >= MC_1_8_Version) + objectivename2 = dataTypes.ReadNextString(packetData); + if (action3 != 1) + value = dataTypes.ReadNextVarInt(packetData); + handler.OnUpdateScore(entityname, action3, objectivename2, value); + break; + case PacketTypesIn.BlockChangedAck: + handler.OnBlockChangeAck(dataTypes.ReadNextVarInt(packetData)); + break; + case PacketTypesIn.BlockBreakAnimation: + if (handler.GetEntityHandlingEnabled() && handler.GetTerrainEnabled()) + { + int playerId = dataTypes.ReadNextVarInt(packetData); + Location blockLocation = dataTypes.ReadNextLocation(packetData); + byte stage = dataTypes.ReadNextByte(packetData); + handler.OnBlockBreakAnimation(playerId, blockLocation, stage); + } + break; + case PacketTypesIn.EntityAnimation: + if (handler.GetEntityHandlingEnabled()) + { + int playerId2 = dataTypes.ReadNextVarInt(packetData); + byte animation = dataTypes.ReadNextByte(packetData); + handler.OnEntityAnimation(playerId2, animation); + } + break; + default: + return false; //Ignored packet + } + return true; //Packet processed } +#if Release catch (Exception innerException) { if (innerException is ThreadAbortException || innerException is SocketException || innerException.InnerException is SocketException) @@ -1999,6 +1997,10 @@ namespace MinecraftClient.Protocol.Handlers innerException.GetType()), innerException); } +#else + catch (SocketException) { } + catch (ThreadAbortException) { } + catch (ObjectDisposedException) { } #endif } From d4b3c42d8c21679689d68e909a96abef73a8f41f Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 20:43:32 +0800 Subject: [PATCH 27/44] Update DeclareCommands for 1.19.3 --- .../Handlers/Packet/s2c/DeclareCommands.cs | 67 +++++++++++++------ .../Protocol/Handlers/Protocol18.cs | 8 +-- .../Protocol/IMinecraftComHandler.cs | 2 +- MinecraftClient/Protocol/PlayerInfo.cs | 1 - 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs index 22835b61..b2a9600c 100644 --- a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs +++ b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs @@ -8,7 +8,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c private static int RootIdx; private static CommandNode[] Nodes = Array.Empty(); - public static void Read(DataTypes dataTypes, Queue packetData) + public static void Read(DataTypes dataTypes, Queue packetData, int protocolVersion) { int count = dataTypes.ReadNextVarInt(packetData); Nodes = new CommandNode[count]; @@ -21,7 +21,7 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c for (int j = 0; j < childCount; ++j) childs[j] = dataTypes.ReadNextVarInt(packetData); - int redirectNode = ((flags & 0x08) > 0) ? dataTypes.ReadNextVarInt(packetData) : -1; + int redirectNode = ((flags & 0x08) == 0x08) ? dataTypes.ReadNextVarInt(packetData) : -1; string? name = ((flags & 0x03) == 1 || (flags & 0x03) == 2) ? dataTypes.ReadNextString(packetData) : null; @@ -29,28 +29,51 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c Paser? paser = null; if ((flags & 0x03) == 2) { - paser = paserId switch - { - 1 => new PaserFloat(dataTypes, packetData), - 2 => new PaserDouble(dataTypes, packetData), - 3 => new PaserInteger(dataTypes, packetData), - 4 => new PaserLong(dataTypes, packetData), - 5 => new PaserString(dataTypes, packetData), - 6 => new PaserEntity(dataTypes, packetData), - 8 => new PaserBlockPos(dataTypes, packetData), - 9 => new PaserColumnPos(dataTypes, packetData), - 10 => new PaserVec3(dataTypes, packetData), - 11 => new PaserVec2(dataTypes, packetData), - 18 => new PaserMessage(dataTypes, packetData), - 27 => new PaserRotation(dataTypes, packetData), - 29 => new PaserScoreHolder(dataTypes, packetData), - 43 => new PaserResourceOrTag(dataTypes, packetData), - 44 => new PaserResource(dataTypes, packetData), - _ => new PaserEmpty(dataTypes, packetData), - }; + if (protocolVersion <= Protocol18Handler.MC_1_19_2_Version) + paser = paserId switch + { + 1 => new PaserFloat(dataTypes, packetData), + 2 => new PaserDouble(dataTypes, packetData), + 3 => new PaserInteger(dataTypes, packetData), + 4 => new PaserLong(dataTypes, packetData), + 5 => new PaserString(dataTypes, packetData), + 6 => new PaserEntity(dataTypes, packetData), + 8 => new PaserBlockPos(dataTypes, packetData), + 9 => new PaserColumnPos(dataTypes, packetData), + 10 => new PaserVec3(dataTypes, packetData), + 11 => new PaserVec2(dataTypes, packetData), + 18 => new PaserMessage(dataTypes, packetData), + 27 => new PaserRotation(dataTypes, packetData), + 29 => new PaserScoreHolder(dataTypes, packetData), + 43 => new PaserResourceOrTag(dataTypes, packetData), + 44 => new PaserResource(dataTypes, packetData), + _ => new PaserEmpty(dataTypes, packetData), + }; + else // protocolVersion >= MC_1_19_3_Version + paser = paserId switch + { + 1 => new PaserFloat(dataTypes, packetData), + 2 => new PaserDouble(dataTypes, packetData), + 3 => new PaserInteger(dataTypes, packetData), + 4 => new PaserLong(dataTypes, packetData), + 5 => new PaserString(dataTypes, packetData), + 6 => new PaserEntity(dataTypes, packetData), + 8 => new PaserBlockPos(dataTypes, packetData), + 9 => new PaserColumnPos(dataTypes, packetData), + 10 => new PaserVec3(dataTypes, packetData), + 11 => new PaserVec2(dataTypes, packetData), + 18 => new PaserMessage(dataTypes, packetData), + 27 => new PaserRotation(dataTypes, packetData), + 29 => new PaserScoreHolder(dataTypes, packetData), + 41 => new PaserResourceOrTag(dataTypes, packetData), + 42 => new PaserResourceOrTag(dataTypes, packetData), + 43 => new PaserResource(dataTypes, packetData), + 44 => new PaserResource(dataTypes, packetData), + _ => new PaserEmpty(dataTypes, packetData), + }; } - string? suggestionsType = ((flags & 0x10) > 0) ? dataTypes.ReadNextString(packetData) : null; + string? suggestionsType = ((flags & 0x10) == 0x10) ? dataTypes.ReadNextString(packetData) : null; Nodes[i] = new(flags, childs, redirectNode, name, paser, suggestionsType); } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index d9e8cb1a..1d71f012 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -459,7 +459,7 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.DeclareCommands: if (protocolVersion >= MC_1_19_Version) - DeclareCommands.Read(dataTypes, packetData); + DeclareCommands.Read(dataTypes, packetData, protocolVersion); break; case PacketTypesIn.ChatMessage: int messageType = 0; @@ -1997,9 +1997,9 @@ namespace MinecraftClient.Protocol.Handlers innerException); } #else - catch (SocketException) { } - catch (ThreadAbortException) { } - catch (ObjectDisposedException) { } + catch (SocketException) { throw; } + catch (ThreadAbortException) { throw; } + catch (ObjectDisposedException) { throw; } #endif } diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 532a3f88..e128507d 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; +using MinecraftClient.Protocol.ProfileKey; using MinecraftClient.Scripting; namespace MinecraftClient.Protocol diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index ea89db1e..bb1a05df 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -4,7 +4,6 @@ using System.Data.SqlTypes; using System.Linq; using System.Text; using MinecraftClient.Protocol.Handlers; -using MinecraftClient.Protocol.Keys; using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.ProfileKey; From ced65122f18b552d2900834ab710ab5b7f1fadde Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 21:07:09 +0800 Subject: [PATCH 28/44] Trim --- MinecraftClient/Crypto/AesCfb8Stream.cs | 2 -- MinecraftClient/Mapping/MessageFilterType.cs | 8 +------- MinecraftClient/Protocol/Message/ChatParser.cs | 8 ++++---- MinecraftClient/Protocol/Message/LastSeenMessageList.cs | 1 - MinecraftClient/Protocol/PlayerInfo.cs | 4 ---- MinecraftClient/Protocol/ProfileKey/PrivateKey.cs | 2 +- MinecraftClient/Settings.cs | 2 +- 7 files changed, 7 insertions(+), 20 deletions(-) diff --git a/MinecraftClient/Crypto/AesCfb8Stream.cs b/MinecraftClient/Crypto/AesCfb8Stream.cs index d50aaa54..dfa2dd78 100644 --- a/MinecraftClient/Crypto/AesCfb8Stream.cs +++ b/MinecraftClient/Crypto/AesCfb8Stream.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Concurrent; using System.IO; using System.Runtime.CompilerServices; using System.Security.Cryptography; -using System.Threading.Tasks; namespace MinecraftClient.Crypto { diff --git a/MinecraftClient/Mapping/MessageFilterType.cs b/MinecraftClient/Mapping/MessageFilterType.cs index c89444c0..9c3cfe1d 100644 --- a/MinecraftClient/Mapping/MessageFilterType.cs +++ b/MinecraftClient/Mapping/MessageFilterType.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MinecraftClient.Mapping +namespace MinecraftClient.Mapping { public enum MessageFilterType { diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 4a98e76b..54df413b 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -144,7 +144,7 @@ namespace MinecraftClient.Protocol.Message if (message.isSystemChat) { if (Config.Signature.MarkSystemMessage) - color = "§§7▍§§r"; // Background Gray + color = "§7▌§r"; // Background Gray } else { @@ -153,18 +153,18 @@ namespace MinecraftClient.Protocol.Message if (Config.Signature.ShowModifiedChat && message.unsignedContent != null) { if (Config.Signature.MarkModifiedMsg) - color = "§§6▍§§r"; // Background Yellow + color = "§6▌§r"; // Background Yellow } else { if (Config.Signature.MarkLegallySignedMsg) - color = "§§2▍§§r"; // Background Green + color = "§2▌§r"; // Background Green } } else { if (Config.Signature.MarkIllegallySignedMsg) - color = "§§4▍§§r"; // Background Red + color = "§4▌§r"; // Background Red } } return color + text; diff --git a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs index a068c6af..62b1227e 100644 --- a/MinecraftClient/Protocol/Message/LastSeenMessageList.cs +++ b/MinecraftClient/Protocol/Message/LastSeenMessageList.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security.Permissions; using static MinecraftClient.Protocol.Message.LastSeenMessageList; namespace MinecraftClient.Protocol.Message diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index bb1a05df..74134068 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Data.SqlTypes; using System.Linq; -using System.Text; -using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.ProfileKey; diff --git a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs index dac1f520..c471a103 100644 --- a/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PrivateKey.cs @@ -77,7 +77,7 @@ namespace MinecraftClient.Protocol.ProfileKey public byte[] SignMessage(string message, Guid playerUuid, Guid chatUuid, int messageIndex, DateTimeOffset timestamp, ref byte[] salt, AcknowledgedMessage[] lastSeenMessages) { byte[] bodySignData = KeyUtils.GetSignatureData_1_19_3(message, playerUuid, chatUuid, messageIndex, timestamp, ref salt, lastSeenMessages); - + return SignData(bodySignData); } diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 4f0f045c..4407f937 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -798,7 +798,7 @@ namespace MinecraftClient ConsoleInteractive.ConsoleWriter.UseVT100ColorCode = General.ConsoleColorMode != ConsoleColorModeType.legacy_4bit; // Buffer - General.History_Input_Records = + General.History_Input_Records = ConsoleInteractive.ConsoleBuffer.SetBackreadBufferLimit(General.History_Input_Records); // Suggestion From 1298654693126785a450e3c228ff5ddbb48cf9ac Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 21:20:22 +0800 Subject: [PATCH 29/44] Bug fix --- MinecraftClient/McClient.cs | 11 ++++------- .../Protocol/Handlers/Protocol18.cs | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 3bd59aa7..56eb4811 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1107,13 +1107,10 @@ namespace MinecraftClient /// Player info public PlayerInfo? GetPlayerInfo(Guid uuid) { - lock (onlinePlayers) - { - if (onlinePlayers.ContainsKey(uuid)) - return onlinePlayers[uuid]; - else - return null; - } + if (onlinePlayers.TryGetValue(uuid, out PlayerInfo? player)) + return player; + else + return null; } public PlayerKeyPair? GetPlayerKeyPair() diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 1d71f012..1fed292e 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -1351,6 +1351,7 @@ namespace MinecraftClient.Protocol.Handlers { Guid playerUuid = dataTypes.ReadNextUUID(packetData); + PlayerInfo player; if ((actionBitset & (1 << 0)) > 0) // Actions bit 0: add player { string name = dataTypes.ReadNextString(packetData); @@ -1362,10 +1363,23 @@ namespace MinecraftClient.Protocol.Handlers if (dataTypes.ReadNextBool(packetData)) dataTypes.SkipNextString(packetData); } - handler.OnPlayerJoin(new(name, playerUuid)); + player = new(name, playerUuid); + handler.OnPlayerJoin(player); + } + else + { + PlayerInfo? playerGet = handler.GetPlayerInfo(playerUuid); + if (playerGet == null) + { + player = new(string.Empty, playerUuid); + handler.OnPlayerJoin(player); + } + else + { + player = playerGet; + } } - PlayerInfo player = handler.GetPlayerInfo(playerUuid)!; if ((actionBitset & (1 << 1)) > 0) // Actions bit 1: initialize chat { bool hasSignatureData = dataTypes.ReadNextBool(packetData); From b4d7d64cdd1a9a9d03db34ba4a1a9ba323c8fb1a Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 21:32:19 +0800 Subject: [PATCH 30/44] Merge some upgrades from AsyncMCC branch --- MinecraftClient/ChatBots/Map.cs | 76 +++++++++++-------- .../InventoryActionArgumentType.cs | 20 +++-- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index fef48a90..25e75a74 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -344,10 +344,8 @@ namespace MinecraftClient.ChatBots private static void RenderInConsole(McMap map) { StringBuilder sb = new(); - int consoleWidth = Math.Max(Console.BufferWidth, Settings.Config.Main.Advanced.MinTerminalWidth) / 2; int consoleHeight = Math.Max(Console.BufferHeight, Settings.Config.Main.Advanced.MinTerminalHeight) - 1; - int scaleX = (map.Width + consoleWidth - 1) / consoleWidth; int scaleY = (map.Height + consoleHeight - 1) / consoleHeight; int scale = Math.Max(scaleX, scaleY); @@ -356,11 +354,12 @@ namespace MinecraftClient.ChatBots for (int base_y = 0; base_y < map.Height; base_y += scale) { - int last_R = -1, last_G = -1, last_B = -1; + string lastFg = string.Empty, lagtBg = string.Empty; for (int base_x = 0; base_x < map.Width; base_x += scale) { - int RL = 0, GL = 0, BL = 0, RR = 0, GR = 0, BR = 0; - double mid_dx = (double)(scale - 1) / 2; + int RUL = 0, GUL = 0, BUL = 0, RUR = 0, GUR = 0, BUR = 0; + int RDL = 0, GDL = 0, BDL = 0, RDR = 0, GDR = 0, BDR = 0; + double mid = (double)(scale - 1) / 2; for (int dy = 0; dy < scale; ++dy) { for (int dx = 0; dx < scale; ++dx) @@ -368,40 +367,57 @@ namespace MinecraftClient.ChatBots int x = Math.Min(base_x + dx, map.Width - 1); int y = Math.Min(base_y + dy, map.Height - 1); ColorRGBA color = MapColors.ColorByteToRGBA(map.Colors![x + y * map.Width]); - if (dx <= mid_dx) + if (dx <= mid) { - RL += color.R; GL += color.G; BL += color.B; + if (dy <= mid) + { + RUL += color.R; GUL += color.G; BUL += color.B; + } + if (dy >= mid) + { + RDL += color.R; GDL += color.G; BDL += color.B; + } } - if (dx >= mid_dx) + if (dx >= mid) { - RR += color.R; GR += color.G; BR += color.B; + if (dy <= mid) + { + RUR += color.R; GUR += color.G; BUR += color.B; + } + if (dy >= mid) + { + RDR += color.R; GDR += color.G; BDR += color.B; + } } } } - int pixel_cnt = ((scale + 1) / 2) * scale; - RL = (int)Math.Round((double)RL / pixel_cnt); - GL = (int)Math.Round((double)GL / pixel_cnt); - BL = (int)Math.Round((double)BL / pixel_cnt); - RR = (int)Math.Round((double)RR / pixel_cnt); - GR = (int)Math.Round((double)GR / pixel_cnt); - BR = (int)Math.Round((double)BR / pixel_cnt); + int pixel_cnt = ((scale + 1) / 2) * ((scale + 1) / 2); + RDL = (int)Math.Round((double)RDL / pixel_cnt); + GDL = (int)Math.Round((double)GDL / pixel_cnt); + BDL = (int)Math.Round((double)BDL / pixel_cnt); + RDR = (int)Math.Round((double)RDR / pixel_cnt); + GDR = (int)Math.Round((double)GDR / pixel_cnt); + BDR = (int)Math.Round((double)BDR / pixel_cnt); - if (RL == last_R && GL == last_G && BL == last_B) - sb.Append(' '); - else - { - sb.Append(ColorHelper.GetColorEscapeCode((byte)RL, (byte)GL, (byte)BL, false)).Append(' '); - last_R = RL; last_G = GL; last_B = BL; - } + RUL = (int)Math.Round((double)RUL / pixel_cnt); + GUL = (int)Math.Round((double)GUL / pixel_cnt); + BUL = (int)Math.Round((double)BUL / pixel_cnt); + RUR = (int)Math.Round((double)RUR / pixel_cnt); + GUR = (int)Math.Round((double)GUR / pixel_cnt); + BUR = (int)Math.Round((double)BUR / pixel_cnt); - if (RR == last_R && GR == last_G && BR == last_B) - sb.Append(' '); - else - { - sb.Append(ColorHelper.GetColorEscapeCode((byte)RR, (byte)GR, (byte)BR, false)).Append(' '); - last_R = RR; last_G = GR; last_B = BR; - } + string colorCode = ColorHelper.GetColorEscapeCode((byte)RUL, (byte)GUL, (byte)BUL, true); + if (lastFg != colorCode) { sb.Append(colorCode); lastFg = colorCode; } + colorCode = ColorHelper.GetColorEscapeCode((byte)RDL, (byte)GDL, (byte)BDL, false); + if (lagtBg != colorCode) { sb.Append(colorCode); lagtBg = colorCode; } + sb.Append('▀'); + + colorCode = ColorHelper.GetColorEscapeCode((byte)RUR, (byte)GUR, (byte)BUR, true); + if (lastFg != colorCode) { sb.Append(colorCode); lastFg = colorCode; } + colorCode = ColorHelper.GetColorEscapeCode((byte)RDR, (byte)GDR, (byte)BDR, false); + if (lagtBg != colorCode) { sb.Append(colorCode); lagtBg = colorCode; } + sb.Append('▀'); } if (base_y >= map.Height - scale) sb.Append(ColorHelper.GetResetEscapeCode()); diff --git a/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs index 89616af6..5448218b 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/InventoryActionArgumentType.cs @@ -21,14 +21,20 @@ namespace MinecraftClient.CommandHandler.ArgumentType public override WindowActionType Parse(IStringReader reader) { reader.SkipWhitespace(); - string inputStr = reader.ReadString(); - foreach (var action in SupportActions) + string inputStr = reader.ReadString().ToLower(); + return inputStr switch { - string actionStr = action.ToString(); - if (string.Compare(inputStr, actionStr, true) == 0) - return action; - } - throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, inputStr); + "left" => WindowActionType.LeftClick, + "leftclick" => WindowActionType.LeftClick, + "right" => WindowActionType.RightClick, + "rightclick" => WindowActionType.RightClick, + "mid" => WindowActionType.MiddleClick, + "middle" => WindowActionType.MiddleClick, + "middleclick" => WindowActionType.MiddleClick, + "shift" => WindowActionType.ShiftClick, + "shiftclick" => WindowActionType.ShiftClick, + _ => throw CommandSyntaxException.BuiltInExceptions.LiteralIncorrect().CreateWithContext(reader, inputStr) + }; } public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) From 957054eb128ecae700440c9b3db434ae15551df7 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 14 Jan 2023 22:18:05 +0800 Subject: [PATCH 31/44] 1.19.3 entity & inventory & terrain support --- .../Inventory/ItemPalettes/ItemPalette1193.cs | 1204 +++++++++++++ MinecraftClient/Inventory/ItemType.cs | 34 + .../Mapping/BlockPalettes/Palette1193.cs | 1605 +++++++++++++++++ .../EntityPalettes/EntityPalette1193.cs | 137 ++ MinecraftClient/Mapping/EntityType.cs | 1 + MinecraftClient/Mapping/Material.cs | 39 + .../Protocol/Handlers/DataTypes.cs | 20 +- .../Protocol/Handlers/Protocol18.cs | 26 +- 8 files changed, 3052 insertions(+), 14 deletions(-) create mode 100644 MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs create mode 100644 MinecraftClient/Mapping/BlockPalettes/Palette1193.cs create mode 100644 MinecraftClient/Mapping/EntityPalettes/EntityPalette1193.cs diff --git a/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs new file mode 100644 index 00000000..563b16a5 --- /dev/null +++ b/MinecraftClient/Inventory/ItemPalettes/ItemPalette1193.cs @@ -0,0 +1,1204 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Inventory.ItemPalettes +{ + public class ItemPalette1193 : ItemPalette + { + private static readonly Dictionary mappings = new(); + + static ItemPalette1193() + { + mappings[721] = ItemType.AcaciaBoat; + mappings[648] = ItemType.AcaciaButton; + mappings[722] = ItemType.AcaciaChestBoat; + mappings[673] = ItemType.AcaciaDoor; + mappings[279] = ItemType.AcaciaFence; + mappings[694] = ItemType.AcaciaFenceGate; + mappings[835] = ItemType.AcaciaHangingSign; + mappings[150] = ItemType.AcaciaLeaves; + mappings[110] = ItemType.AcaciaLog; + mappings[27] = ItemType.AcaciaPlanks; + mappings[662] = ItemType.AcaciaPressurePlate; + mappings[38] = ItemType.AcaciaSapling; + mappings[825] = ItemType.AcaciaSign; + mappings[222] = ItemType.AcaciaSlab; + mappings[350] = ItemType.AcaciaStairs; + mappings[684] = ItemType.AcaciaTrapdoor; + mappings[141] = ItemType.AcaciaWood; + mappings[703] = ItemType.ActivatorRail; + mappings[0] = ItemType.Air; + mappings[940] = ItemType.AllaySpawnEgg; + mappings[190] = ItemType.Allium; + mappings[68] = ItemType.AmethystBlock; + mappings[1179] = ItemType.AmethystCluster; + mappings[743] = ItemType.AmethystShard; + mappings[63] = ItemType.AncientDebris; + mappings[6] = ItemType.Andesite; + mappings[609] = ItemType.AndesiteSlab; + mappings[592] = ItemType.AndesiteStairs; + mappings[369] = ItemType.AndesiteWall; + mappings[381] = ItemType.Anvil; + mappings[734] = ItemType.Apple; + mappings[1049] = ItemType.ArmorStand; + mappings[736] = ItemType.Arrow; + mappings[852] = ItemType.AxolotlBucket; + mappings[941] = ItemType.AxolotlSpawnEgg; + mappings[166] = ItemType.Azalea; + mappings[153] = ItemType.AzaleaLeaves; + mappings[191] = ItemType.AzureBluet; + mappings[1025] = ItemType.BakedPotato; + mappings[217] = ItemType.Bamboo; + mappings[117] = ItemType.BambooBlock; + mappings[651] = ItemType.BambooButton; + mappings[728] = ItemType.BambooChestRaft; + mappings[676] = ItemType.BambooDoor; + mappings[282] = ItemType.BambooFence; + mappings[697] = ItemType.BambooFenceGate; + mappings[838] = ItemType.BambooHangingSign; + mappings[33] = ItemType.BambooMosaic; + mappings[226] = ItemType.BambooMosaicSlab; + mappings[354] = ItemType.BambooMosaicStairs; + mappings[30] = ItemType.BambooPlanks; + mappings[665] = ItemType.BambooPressurePlate; + mappings[727] = ItemType.BambooRaft; + mappings[828] = ItemType.BambooSign; + mappings[225] = ItemType.BambooSlab; + mappings[353] = ItemType.BambooStairs; + mappings[687] = ItemType.BambooTrapdoor; + mappings[1123] = ItemType.Barrel; + mappings[405] = ItemType.Barrier; + mappings[291] = ItemType.Basalt; + mappings[942] = ItemType.BatSpawnEgg; + mappings[358] = ItemType.Beacon; + mappings[41] = ItemType.Bedrock; + mappings[1140] = ItemType.BeeNest; + mappings[943] = ItemType.BeeSpawnEgg; + mappings[920] = ItemType.Beef; + mappings[1141] = ItemType.Beehive; + mappings[1078] = ItemType.Beetroot; + mappings[1079] = ItemType.BeetrootSeeds; + mappings[1080] = ItemType.BeetrootSoup; + mappings[1131] = ItemType.Bell; + mappings[215] = ItemType.BigDripleaf; + mappings[717] = ItemType.BirchBoat; + mappings[646] = ItemType.BirchButton; + mappings[718] = ItemType.BirchChestBoat; + mappings[671] = ItemType.BirchDoor; + mappings[277] = ItemType.BirchFence; + mappings[692] = ItemType.BirchFenceGate; + mappings[833] = ItemType.BirchHangingSign; + mappings[148] = ItemType.BirchLeaves; + mappings[108] = ItemType.BirchLog; + mappings[25] = ItemType.BirchPlanks; + mappings[660] = ItemType.BirchPressurePlate; + mappings[36] = ItemType.BirchSapling; + mappings[823] = ItemType.BirchSign; + mappings[220] = ItemType.BirchSlab; + mappings[348] = ItemType.BirchStairs; + mappings[682] = ItemType.BirchTrapdoor; + mappings[139] = ItemType.BirchWood; + mappings[1074] = ItemType.BlackBanner; + mappings[912] = ItemType.BlackBed; + mappings[1175] = ItemType.BlackCandle; + mappings[423] = ItemType.BlackCarpet; + mappings[532] = ItemType.BlackConcrete; + mappings[548] = ItemType.BlackConcretePowder; + mappings[892] = ItemType.BlackDye; + mappings[516] = ItemType.BlackGlazedTerracotta; + mappings[500] = ItemType.BlackShulkerBox; + mappings[448] = ItemType.BlackStainedGlass; + mappings[464] = ItemType.BlackStainedGlassPane; + mappings[404] = ItemType.BlackTerracotta; + mappings[186] = ItemType.BlackWool; + mappings[1146] = ItemType.Blackstone; + mappings[1147] = ItemType.BlackstoneSlab; + mappings[1148] = ItemType.BlackstoneStairs; + mappings[374] = ItemType.BlackstoneWall; + mappings[1125] = ItemType.BlastFurnace; + mappings[934] = ItemType.BlazePowder; + mappings[926] = ItemType.BlazeRod; + mappings[944] = ItemType.BlazeSpawnEgg; + mappings[1070] = ItemType.BlueBanner; + mappings[908] = ItemType.BlueBed; + mappings[1171] = ItemType.BlueCandle; + mappings[419] = ItemType.BlueCarpet; + mappings[528] = ItemType.BlueConcrete; + mappings[544] = ItemType.BlueConcretePowder; + mappings[888] = ItemType.BlueDye; + mappings[512] = ItemType.BlueGlazedTerracotta; + mappings[580] = ItemType.BlueIce; + mappings[189] = ItemType.BlueOrchid; + mappings[496] = ItemType.BlueShulkerBox; + mappings[444] = ItemType.BlueStainedGlass; + mappings[460] = ItemType.BlueStainedGlassPane; + mappings[400] = ItemType.BlueTerracotta; + mappings[182] = ItemType.BlueWool; + mappings[894] = ItemType.Bone; + mappings[482] = ItemType.BoneBlock; + mappings[893] = ItemType.BoneMeal; + mappings[858] = ItemType.Book; + mappings[251] = ItemType.Bookshelf; + mappings[735] = ItemType.Bow; + mappings[783] = ItemType.Bowl; + mappings[561] = ItemType.BrainCoral; + mappings[556] = ItemType.BrainCoralBlock; + mappings[571] = ItemType.BrainCoralFan; + mappings[790] = ItemType.Bread; + mappings[936] = ItemType.BrewingStand; + mappings[854] = ItemType.Brick; + mappings[235] = ItemType.BrickSlab; + mappings[324] = ItemType.BrickStairs; + mappings[361] = ItemType.BrickWall; + mappings[250] = ItemType.Bricks; + mappings[1071] = ItemType.BrownBanner; + mappings[909] = ItemType.BrownBed; + mappings[1172] = ItemType.BrownCandle; + mappings[420] = ItemType.BrownCarpet; + mappings[529] = ItemType.BrownConcrete; + mappings[545] = ItemType.BrownConcretePowder; + mappings[889] = ItemType.BrownDye; + mappings[513] = ItemType.BrownGlazedTerracotta; + mappings[201] = ItemType.BrownMushroom; + mappings[315] = ItemType.BrownMushroomBlock; + mappings[497] = ItemType.BrownShulkerBox; + mappings[445] = ItemType.BrownStainedGlass; + mappings[461] = ItemType.BrownStainedGlassPane; + mappings[401] = ItemType.BrownTerracotta; + mappings[183] = ItemType.BrownWool; + mappings[562] = ItemType.BubbleCoral; + mappings[557] = ItemType.BubbleCoralBlock; + mappings[572] = ItemType.BubbleCoralFan; + mappings[841] = ItemType.Bucket; + mappings[69] = ItemType.BuddingAmethyst; + mappings[863] = ItemType.Bundle; + mappings[272] = ItemType.Cactus; + mappings[896] = ItemType.Cake; + mappings[11] = ItemType.Calcite; + mappings[946] = ItemType.CamelSpawnEgg; + mappings[1136] = ItemType.Campfire; + mappings[1159] = ItemType.Candle; + mappings[1023] = ItemType.Carrot; + mappings[710] = ItemType.CarrotOnAStick; + mappings[1126] = ItemType.CartographyTable; + mappings[286] = ItemType.CarvedPumpkin; + mappings[945] = ItemType.CatSpawnEgg; + mappings[937] = ItemType.Cauldron; + mappings[947] = ItemType.CaveSpiderSpawnEgg; + mappings[319] = ItemType.Chain; + mappings[477] = ItemType.ChainCommandBlock; + mappings[798] = ItemType.ChainmailBoots; + mappings[796] = ItemType.ChainmailChestplate; + mappings[795] = ItemType.ChainmailHelmet; + mappings[797] = ItemType.ChainmailLeggings; + mappings[738] = ItemType.Charcoal; + mappings[263] = ItemType.Chest; + mappings[706] = ItemType.ChestMinecart; + mappings[922] = ItemType.Chicken; + mappings[948] = ItemType.ChickenSpawnEgg; + mappings[382] = ItemType.ChippedAnvil; + mappings[252] = ItemType.ChiseledBookshelf; + mappings[313] = ItemType.ChiseledDeepslate; + mappings[331] = ItemType.ChiseledNetherBricks; + mappings[1153] = ItemType.ChiseledPolishedBlackstone; + mappings[384] = ItemType.ChiseledQuartzBlock; + mappings[473] = ItemType.ChiseledRedSandstone; + mappings[161] = ItemType.ChiseledSandstone; + mappings[306] = ItemType.ChiseledStoneBricks; + mappings[258] = ItemType.ChorusFlower; + mappings[1076] = ItemType.ChorusFruit; + mappings[257] = ItemType.ChorusPlant; + mappings[273] = ItemType.Clay; + mappings[855] = ItemType.ClayBall; + mappings[865] = ItemType.Clock; + mappings[737] = ItemType.Coal; + mappings[64] = ItemType.CoalBlock; + mappings[45] = ItemType.CoalOre; + mappings[16] = ItemType.CoarseDirt; + mappings[9] = ItemType.CobbledDeepslate; + mappings[613] = ItemType.CobbledDeepslateSlab; + mappings[596] = ItemType.CobbledDeepslateStairs; + mappings[377] = ItemType.CobbledDeepslateWall; + mappings[22] = ItemType.Cobblestone; + mappings[234] = ItemType.CobblestoneSlab; + mappings[268] = ItemType.CobblestoneStairs; + mappings[359] = ItemType.CobblestoneWall; + mappings[163] = ItemType.Cobweb; + mappings[876] = ItemType.CocoaBeans; + mappings[868] = ItemType.Cod; + mappings[850] = ItemType.CodBucket; + mappings[949] = ItemType.CodSpawnEgg; + mappings[357] = ItemType.CommandBlock; + mappings[1056] = ItemType.CommandBlockMinecart; + mappings[622] = ItemType.Comparator; + mappings[861] = ItemType.Compass; + mappings[1122] = ItemType.Composter; + mappings[581] = ItemType.Conduit; + mappings[921] = ItemType.CookedBeef; + mappings[923] = ItemType.CookedChicken; + mappings[872] = ItemType.CookedCod; + mappings[1058] = ItemType.CookedMutton; + mappings[817] = ItemType.CookedPorkchop; + mappings[1045] = ItemType.CookedRabbit; + mappings[873] = ItemType.CookedSalmon; + mappings[913] = ItemType.Cookie; + mappings[71] = ItemType.CopperBlock; + mappings[747] = ItemType.CopperIngot; + mappings[49] = ItemType.CopperOre; + mappings[197] = ItemType.Cornflower; + mappings[950] = ItemType.CowSpawnEgg; + mappings[310] = ItemType.CrackedDeepslateBricks; + mappings[312] = ItemType.CrackedDeepslateTiles; + mappings[330] = ItemType.CrackedNetherBricks; + mappings[1157] = ItemType.CrackedPolishedBlackstoneBricks; + mappings[305] = ItemType.CrackedStoneBricks; + mappings[264] = ItemType.CraftingTable; + mappings[1116] = ItemType.CreeperBannerPattern; + mappings[1033] = ItemType.CreeperHead; + mappings[951] = ItemType.CreeperSpawnEgg; + mappings[652] = ItemType.CrimsonButton; + mappings[677] = ItemType.CrimsonDoor; + mappings[283] = ItemType.CrimsonFence; + mappings[698] = ItemType.CrimsonFenceGate; + mappings[203] = ItemType.CrimsonFungus; + mappings[839] = ItemType.CrimsonHangingSign; + mappings[144] = ItemType.CrimsonHyphae; + mappings[20] = ItemType.CrimsonNylium; + mappings[31] = ItemType.CrimsonPlanks; + mappings[666] = ItemType.CrimsonPressurePlate; + mappings[205] = ItemType.CrimsonRoots; + mappings[829] = ItemType.CrimsonSign; + mappings[227] = ItemType.CrimsonSlab; + mappings[355] = ItemType.CrimsonStairs; + mappings[115] = ItemType.CrimsonStem; + mappings[688] = ItemType.CrimsonTrapdoor; + mappings[1112] = ItemType.Crossbow; + mappings[1145] = ItemType.CryingObsidian; + mappings[78] = ItemType.CutCopper; + mappings[86] = ItemType.CutCopperSlab; + mappings[82] = ItemType.CutCopperStairs; + mappings[474] = ItemType.CutRedSandstone; + mappings[241] = ItemType.CutRedSandstoneSlab; + mappings[162] = ItemType.CutSandstone; + mappings[232] = ItemType.CutSandstoneSlab; + mappings[1068] = ItemType.CyanBanner; + mappings[906] = ItemType.CyanBed; + mappings[1169] = ItemType.CyanCandle; + mappings[417] = ItemType.CyanCarpet; + mappings[526] = ItemType.CyanConcrete; + mappings[542] = ItemType.CyanConcretePowder; + mappings[886] = ItemType.CyanDye; + mappings[510] = ItemType.CyanGlazedTerracotta; + mappings[494] = ItemType.CyanShulkerBox; + mappings[442] = ItemType.CyanStainedGlass; + mappings[458] = ItemType.CyanStainedGlassPane; + mappings[398] = ItemType.CyanTerracotta; + mappings[180] = ItemType.CyanWool; + mappings[383] = ItemType.DamagedAnvil; + mappings[187] = ItemType.Dandelion; + mappings[723] = ItemType.DarkOakBoat; + mappings[649] = ItemType.DarkOakButton; + mappings[724] = ItemType.DarkOakChestBoat; + mappings[674] = ItemType.DarkOakDoor; + mappings[280] = ItemType.DarkOakFence; + mappings[695] = ItemType.DarkOakFenceGate; + mappings[836] = ItemType.DarkOakHangingSign; + mappings[151] = ItemType.DarkOakLeaves; + mappings[111] = ItemType.DarkOakLog; + mappings[28] = ItemType.DarkOakPlanks; + mappings[663] = ItemType.DarkOakPressurePlate; + mappings[39] = ItemType.DarkOakSapling; + mappings[826] = ItemType.DarkOakSign; + mappings[223] = ItemType.DarkOakSlab; + mappings[351] = ItemType.DarkOakStairs; + mappings[685] = ItemType.DarkOakTrapdoor; + mappings[142] = ItemType.DarkOakWood; + mappings[467] = ItemType.DarkPrismarine; + mappings[245] = ItemType.DarkPrismarineSlab; + mappings[470] = ItemType.DarkPrismarineStairs; + mappings[635] = ItemType.DaylightDetector; + mappings[565] = ItemType.DeadBrainCoral; + mappings[551] = ItemType.DeadBrainCoralBlock; + mappings[576] = ItemType.DeadBrainCoralFan; + mappings[566] = ItemType.DeadBubbleCoral; + mappings[552] = ItemType.DeadBubbleCoralBlock; + mappings[577] = ItemType.DeadBubbleCoralFan; + mappings[168] = ItemType.DeadBush; + mappings[567] = ItemType.DeadFireCoral; + mappings[553] = ItemType.DeadFireCoralBlock; + mappings[578] = ItemType.DeadFireCoralFan; + mappings[568] = ItemType.DeadHornCoral; + mappings[554] = ItemType.DeadHornCoralBlock; + mappings[579] = ItemType.DeadHornCoralFan; + mappings[569] = ItemType.DeadTubeCoral; + mappings[550] = ItemType.DeadTubeCoralBlock; + mappings[575] = ItemType.DeadTubeCoralFan; + mappings[1091] = ItemType.DebugStick; + mappings[8] = ItemType.Deepslate; + mappings[615] = ItemType.DeepslateBrickSlab; + mappings[598] = ItemType.DeepslateBrickStairs; + mappings[379] = ItemType.DeepslateBrickWall; + mappings[309] = ItemType.DeepslateBricks; + mappings[46] = ItemType.DeepslateCoalOre; + mappings[50] = ItemType.DeepslateCopperOre; + mappings[60] = ItemType.DeepslateDiamondOre; + mappings[56] = ItemType.DeepslateEmeraldOre; + mappings[52] = ItemType.DeepslateGoldOre; + mappings[48] = ItemType.DeepslateIronOre; + mappings[58] = ItemType.DeepslateLapisOre; + mappings[54] = ItemType.DeepslateRedstoneOre; + mappings[616] = ItemType.DeepslateTileSlab; + mappings[599] = ItemType.DeepslateTileStairs; + mappings[380] = ItemType.DeepslateTileWall; + mappings[311] = ItemType.DeepslateTiles; + mappings[701] = ItemType.DetectorRail; + mappings[739] = ItemType.Diamond; + mappings[775] = ItemType.DiamondAxe; + mappings[73] = ItemType.DiamondBlock; + mappings[806] = ItemType.DiamondBoots; + mappings[804] = ItemType.DiamondChestplate; + mappings[803] = ItemType.DiamondHelmet; + mappings[776] = ItemType.DiamondHoe; + mappings[1052] = ItemType.DiamondHorseArmor; + mappings[805] = ItemType.DiamondLeggings; + mappings[59] = ItemType.DiamondOre; + mappings[774] = ItemType.DiamondPickaxe; + mappings[773] = ItemType.DiamondShovel; + mappings[772] = ItemType.DiamondSword; + mappings[4] = ItemType.Diorite; + mappings[612] = ItemType.DioriteSlab; + mappings[595] = ItemType.DioriteStairs; + mappings[373] = ItemType.DioriteWall; + mappings[15] = ItemType.Dirt; + mappings[426] = ItemType.DirtPath; + mappings[1107] = ItemType.DiscFragment5; + mappings[629] = ItemType.Dispenser; + mappings[952] = ItemType.DolphinSpawnEgg; + mappings[953] = ItemType.DonkeySpawnEgg; + mappings[1081] = ItemType.DragonBreath; + mappings[342] = ItemType.DragonEgg; + mappings[1034] = ItemType.DragonHead; + mappings[917] = ItemType.DriedKelp; + mappings[856] = ItemType.DriedKelpBlock; + mappings[13] = ItemType.DripstoneBlock; + mappings[630] = ItemType.Dropper; + mappings[954] = ItemType.DrownedSpawnEgg; + mappings[1185] = ItemType.EchoShard; + mappings[860] = ItemType.Egg; + mappings[955] = ItemType.ElderGuardianSpawnEgg; + mappings[712] = ItemType.Elytra; + mappings[740] = ItemType.Emerald; + mappings[345] = ItemType.EmeraldBlock; + mappings[55] = ItemType.EmeraldOre; + mappings[1040] = ItemType.EnchantedBook; + mappings[820] = ItemType.EnchantedGoldenApple; + mappings[338] = ItemType.EnchantingTable; + mappings[1075] = ItemType.EndCrystal; + mappings[339] = ItemType.EndPortalFrame; + mappings[256] = ItemType.EndRod; + mappings[340] = ItemType.EndStone; + mappings[605] = ItemType.EndStoneBrickSlab; + mappings[587] = ItemType.EndStoneBrickStairs; + mappings[372] = ItemType.EndStoneBrickWall; + mappings[341] = ItemType.EndStoneBricks; + mappings[344] = ItemType.EnderChest; + mappings[956] = ItemType.EnderDragonSpawnEgg; + mappings[938] = ItemType.EnderEye; + mappings[925] = ItemType.EnderPearl; + mappings[957] = ItemType.EndermanSpawnEgg; + mappings[958] = ItemType.EndermiteSpawnEgg; + mappings[959] = ItemType.EvokerSpawnEgg; + mappings[1016] = ItemType.ExperienceBottle; + mappings[75] = ItemType.ExposedCopper; + mappings[79] = ItemType.ExposedCutCopper; + mappings[87] = ItemType.ExposedCutCopperSlab; + mappings[83] = ItemType.ExposedCutCopperStairs; + mappings[265] = ItemType.Farmland; + mappings[786] = ItemType.Feather; + mappings[933] = ItemType.FermentedSpiderEye; + mappings[165] = ItemType.Fern; + mappings[914] = ItemType.FilledMap; + mappings[1017] = ItemType.FireCharge; + mappings[563] = ItemType.FireCoral; + mappings[558] = ItemType.FireCoralBlock; + mappings[573] = ItemType.FireCoralFan; + mappings[1038] = ItemType.FireworkRocket; + mappings[1039] = ItemType.FireworkStar; + mappings[864] = ItemType.FishingRod; + mappings[1127] = ItemType.FletchingTable; + mappings[815] = ItemType.Flint; + mappings[733] = ItemType.FlintAndSteel; + mappings[1115] = ItemType.FlowerBannerPattern; + mappings[1022] = ItemType.FlowerPot; + mappings[167] = ItemType.FloweringAzalea; + mappings[154] = ItemType.FloweringAzaleaLeaves; + mappings[960] = ItemType.FoxSpawnEgg; + mappings[961] = ItemType.FrogSpawnEgg; + mappings[1184] = ItemType.Frogspawn; + mappings[266] = ItemType.Furnace; + mappings[707] = ItemType.FurnaceMinecart; + mappings[962] = ItemType.GhastSpawnEgg; + mappings[927] = ItemType.GhastTear; + mappings[1149] = ItemType.GildedBlackstone; + mappings[157] = ItemType.Glass; + mappings[931] = ItemType.GlassBottle; + mappings[320] = ItemType.GlassPane; + mappings[939] = ItemType.GlisteringMelonSlice; + mappings[1119] = ItemType.GlobeBannerPattern; + mappings[1135] = ItemType.GlowBerries; + mappings[875] = ItemType.GlowInkSac; + mappings[1021] = ItemType.GlowItemFrame; + mappings[323] = ItemType.GlowLichen; + mappings[963] = ItemType.GlowSquidSpawnEgg; + mappings[295] = ItemType.Glowstone; + mappings[867] = ItemType.GlowstoneDust; + mappings[1121] = ItemType.GoatHorn; + mappings[964] = ItemType.GoatSpawnEgg; + mappings[72] = ItemType.GoldBlock; + mappings[749] = ItemType.GoldIngot; + mappings[928] = ItemType.GoldNugget; + mappings[51] = ItemType.GoldOre; + mappings[819] = ItemType.GoldenApple; + mappings[765] = ItemType.GoldenAxe; + mappings[810] = ItemType.GoldenBoots; + mappings[1028] = ItemType.GoldenCarrot; + mappings[808] = ItemType.GoldenChestplate; + mappings[807] = ItemType.GoldenHelmet; + mappings[766] = ItemType.GoldenHoe; + mappings[1051] = ItemType.GoldenHorseArmor; + mappings[809] = ItemType.GoldenLeggings; + mappings[764] = ItemType.GoldenPickaxe; + mappings[763] = ItemType.GoldenShovel; + mappings[762] = ItemType.GoldenSword; + mappings[2] = ItemType.Granite; + mappings[608] = ItemType.GraniteSlab; + mappings[591] = ItemType.GraniteStairs; + mappings[365] = ItemType.GraniteWall; + mappings[164] = ItemType.Grass; + mappings[14] = ItemType.GrassBlock; + mappings[44] = ItemType.Gravel; + mappings[1066] = ItemType.GrayBanner; + mappings[904] = ItemType.GrayBed; + mappings[1167] = ItemType.GrayCandle; + mappings[415] = ItemType.GrayCarpet; + mappings[524] = ItemType.GrayConcrete; + mappings[540] = ItemType.GrayConcretePowder; + mappings[884] = ItemType.GrayDye; + mappings[508] = ItemType.GrayGlazedTerracotta; + mappings[492] = ItemType.GrayShulkerBox; + mappings[440] = ItemType.GrayStainedGlass; + mappings[456] = ItemType.GrayStainedGlassPane; + mappings[396] = ItemType.GrayTerracotta; + mappings[178] = ItemType.GrayWool; + mappings[1072] = ItemType.GreenBanner; + mappings[910] = ItemType.GreenBed; + mappings[1173] = ItemType.GreenCandle; + mappings[421] = ItemType.GreenCarpet; + mappings[530] = ItemType.GreenConcrete; + mappings[546] = ItemType.GreenConcretePowder; + mappings[890] = ItemType.GreenDye; + mappings[514] = ItemType.GreenGlazedTerracotta; + mappings[498] = ItemType.GreenShulkerBox; + mappings[446] = ItemType.GreenStainedGlass; + mappings[462] = ItemType.GreenStainedGlassPane; + mappings[402] = ItemType.GreenTerracotta; + mappings[184] = ItemType.GreenWool; + mappings[1128] = ItemType.Grindstone; + mappings[965] = ItemType.GuardianSpawnEgg; + mappings[787] = ItemType.Gunpowder; + mappings[214] = ItemType.HangingRoots; + mappings[407] = ItemType.HayBlock; + mappings[1111] = ItemType.HeartOfTheSea; + mappings[657] = ItemType.HeavyWeightedPressurePlate; + mappings[966] = ItemType.HoglinSpawnEgg; + mappings[626] = ItemType.HoneyBlock; + mappings[1142] = ItemType.HoneyBottle; + mappings[1139] = ItemType.Honeycomb; + mappings[1143] = ItemType.HoneycombBlock; + mappings[628] = ItemType.Hopper; + mappings[709] = ItemType.HopperMinecart; + mappings[564] = ItemType.HornCoral; + mappings[559] = ItemType.HornCoralBlock; + mappings[574] = ItemType.HornCoralFan; + mappings[967] = ItemType.HorseSpawnEgg; + mappings[968] = ItemType.HuskSpawnEgg; + mappings[270] = ItemType.Ice; + mappings[301] = ItemType.InfestedChiseledStoneBricks; + mappings[297] = ItemType.InfestedCobblestone; + mappings[300] = ItemType.InfestedCrackedStoneBricks; + mappings[302] = ItemType.InfestedDeepslate; + mappings[299] = ItemType.InfestedMossyStoneBricks; + mappings[296] = ItemType.InfestedStone; + mappings[298] = ItemType.InfestedStoneBricks; + mappings[874] = ItemType.InkSac; + mappings[770] = ItemType.IronAxe; + mappings[318] = ItemType.IronBars; + mappings[70] = ItemType.IronBlock; + mappings[802] = ItemType.IronBoots; + mappings[800] = ItemType.IronChestplate; + mappings[668] = ItemType.IronDoor; + mappings[969] = ItemType.IronGolemSpawnEgg; + mappings[799] = ItemType.IronHelmet; + mappings[771] = ItemType.IronHoe; + mappings[1050] = ItemType.IronHorseArmor; + mappings[745] = ItemType.IronIngot; + mappings[801] = ItemType.IronLeggings; + mappings[1089] = ItemType.IronNugget; + mappings[47] = ItemType.IronOre; + mappings[769] = ItemType.IronPickaxe; + mappings[768] = ItemType.IronShovel; + mappings[767] = ItemType.IronSword; + mappings[679] = ItemType.IronTrapdoor; + mappings[1020] = ItemType.ItemFrame; + mappings[287] = ItemType.JackOLantern; + mappings[730] = ItemType.Jigsaw; + mappings[274] = ItemType.Jukebox; + mappings[719] = ItemType.JungleBoat; + mappings[647] = ItemType.JungleButton; + mappings[720] = ItemType.JungleChestBoat; + mappings[672] = ItemType.JungleDoor; + mappings[278] = ItemType.JungleFence; + mappings[693] = ItemType.JungleFenceGate; + mappings[834] = ItemType.JungleHangingSign; + mappings[149] = ItemType.JungleLeaves; + mappings[109] = ItemType.JungleLog; + mappings[26] = ItemType.JunglePlanks; + mappings[661] = ItemType.JunglePressurePlate; + mappings[37] = ItemType.JungleSapling; + mappings[824] = ItemType.JungleSign; + mappings[221] = ItemType.JungleSlab; + mappings[349] = ItemType.JungleStairs; + mappings[683] = ItemType.JungleTrapdoor; + mappings[140] = ItemType.JungleWood; + mappings[211] = ItemType.Kelp; + mappings[1090] = ItemType.KnowledgeBook; + mappings[267] = ItemType.Ladder; + mappings[1132] = ItemType.Lantern; + mappings[159] = ItemType.LapisBlock; + mappings[741] = ItemType.LapisLazuli; + mappings[57] = ItemType.LapisOre; + mappings[1178] = ItemType.LargeAmethystBud; + mappings[432] = ItemType.LargeFern; + mappings[843] = ItemType.LavaBucket; + mappings[1054] = ItemType.Lead; + mappings[846] = ItemType.Leather; + mappings[794] = ItemType.LeatherBoots; + mappings[792] = ItemType.LeatherChestplate; + mappings[791] = ItemType.LeatherHelmet; + mappings[1053] = ItemType.LeatherHorseArmor; + mappings[793] = ItemType.LeatherLeggings; + mappings[631] = ItemType.Lectern; + mappings[633] = ItemType.Lever; + mappings[406] = ItemType.Light; + mappings[1062] = ItemType.LightBlueBanner; + mappings[900] = ItemType.LightBlueBed; + mappings[1163] = ItemType.LightBlueCandle; + mappings[411] = ItemType.LightBlueCarpet; + mappings[520] = ItemType.LightBlueConcrete; + mappings[536] = ItemType.LightBlueConcretePowder; + mappings[880] = ItemType.LightBlueDye; + mappings[504] = ItemType.LightBlueGlazedTerracotta; + mappings[488] = ItemType.LightBlueShulkerBox; + mappings[436] = ItemType.LightBlueStainedGlass; + mappings[452] = ItemType.LightBlueStainedGlassPane; + mappings[392] = ItemType.LightBlueTerracotta; + mappings[174] = ItemType.LightBlueWool; + mappings[1067] = ItemType.LightGrayBanner; + mappings[905] = ItemType.LightGrayBed; + mappings[1168] = ItemType.LightGrayCandle; + mappings[416] = ItemType.LightGrayCarpet; + mappings[525] = ItemType.LightGrayConcrete; + mappings[541] = ItemType.LightGrayConcretePowder; + mappings[885] = ItemType.LightGrayDye; + mappings[509] = ItemType.LightGrayGlazedTerracotta; + mappings[493] = ItemType.LightGrayShulkerBox; + mappings[441] = ItemType.LightGrayStainedGlass; + mappings[457] = ItemType.LightGrayStainedGlassPane; + mappings[397] = ItemType.LightGrayTerracotta; + mappings[179] = ItemType.LightGrayWool; + mappings[656] = ItemType.LightWeightedPressurePlate; + mappings[634] = ItemType.LightningRod; + mappings[428] = ItemType.Lilac; + mappings[198] = ItemType.LilyOfTheValley; + mappings[328] = ItemType.LilyPad; + mappings[1064] = ItemType.LimeBanner; + mappings[902] = ItemType.LimeBed; + mappings[1165] = ItemType.LimeCandle; + mappings[413] = ItemType.LimeCarpet; + mappings[522] = ItemType.LimeConcrete; + mappings[538] = ItemType.LimeConcretePowder; + mappings[882] = ItemType.LimeDye; + mappings[506] = ItemType.LimeGlazedTerracotta; + mappings[490] = ItemType.LimeShulkerBox; + mappings[438] = ItemType.LimeStainedGlass; + mappings[454] = ItemType.LimeStainedGlassPane; + mappings[394] = ItemType.LimeTerracotta; + mappings[176] = ItemType.LimeWool; + mappings[1085] = ItemType.LingeringPotion; + mappings[970] = ItemType.LlamaSpawnEgg; + mappings[1144] = ItemType.Lodestone; + mappings[1114] = ItemType.Loom; + mappings[1061] = ItemType.MagentaBanner; + mappings[899] = ItemType.MagentaBed; + mappings[1162] = ItemType.MagentaCandle; + mappings[410] = ItemType.MagentaCarpet; + mappings[519] = ItemType.MagentaConcrete; + mappings[535] = ItemType.MagentaConcretePowder; + mappings[879] = ItemType.MagentaDye; + mappings[503] = ItemType.MagentaGlazedTerracotta; + mappings[487] = ItemType.MagentaShulkerBox; + mappings[435] = ItemType.MagentaStainedGlass; + mappings[451] = ItemType.MagentaStainedGlassPane; + mappings[391] = ItemType.MagentaTerracotta; + mappings[173] = ItemType.MagentaWool; + mappings[478] = ItemType.MagmaBlock; + mappings[935] = ItemType.MagmaCream; + mappings[971] = ItemType.MagmaCubeSpawnEgg; + mappings[725] = ItemType.MangroveBoat; + mappings[650] = ItemType.MangroveButton; + mappings[726] = ItemType.MangroveChestBoat; + mappings[675] = ItemType.MangroveDoor; + mappings[281] = ItemType.MangroveFence; + mappings[696] = ItemType.MangroveFenceGate; + mappings[837] = ItemType.MangroveHangingSign; + mappings[152] = ItemType.MangroveLeaves; + mappings[112] = ItemType.MangroveLog; + mappings[29] = ItemType.MangrovePlanks; + mappings[664] = ItemType.MangrovePressurePlate; + mappings[40] = ItemType.MangrovePropagule; + mappings[113] = ItemType.MangroveRoots; + mappings[827] = ItemType.MangroveSign; + mappings[224] = ItemType.MangroveSlab; + mappings[352] = ItemType.MangroveStairs; + mappings[686] = ItemType.MangroveTrapdoor; + mappings[143] = ItemType.MangroveWood; + mappings[1027] = ItemType.Map; + mappings[1177] = ItemType.MediumAmethystBud; + mappings[321] = ItemType.Melon; + mappings[919] = ItemType.MelonSeeds; + mappings[916] = ItemType.MelonSlice; + mappings[847] = ItemType.MilkBucket; + mappings[705] = ItemType.Minecart; + mappings[1118] = ItemType.MojangBannerPattern; + mappings[972] = ItemType.MooshroomSpawnEgg; + mappings[213] = ItemType.MossBlock; + mappings[212] = ItemType.MossCarpet; + mappings[253] = ItemType.MossyCobblestone; + mappings[604] = ItemType.MossyCobblestoneSlab; + mappings[586] = ItemType.MossyCobblestoneStairs; + mappings[360] = ItemType.MossyCobblestoneWall; + mappings[602] = ItemType.MossyStoneBrickSlab; + mappings[584] = ItemType.MossyStoneBrickStairs; + mappings[364] = ItemType.MossyStoneBrickWall; + mappings[304] = ItemType.MossyStoneBricks; + mappings[19] = ItemType.Mud; + mappings[237] = ItemType.MudBrickSlab; + mappings[326] = ItemType.MudBrickStairs; + mappings[367] = ItemType.MudBrickWall; + mappings[308] = ItemType.MudBricks; + mappings[114] = ItemType.MuddyMangroveRoots; + mappings[973] = ItemType.MuleSpawnEgg; + mappings[317] = ItemType.MushroomStem; + mappings[784] = ItemType.MushroomStew; + mappings[1102] = ItemType.MusicDisc11; + mappings[1092] = ItemType.MusicDisc13; + mappings[1105] = ItemType.MusicDisc5; + mappings[1094] = ItemType.MusicDiscBlocks; + mappings[1093] = ItemType.MusicDiscCat; + mappings[1095] = ItemType.MusicDiscChirp; + mappings[1096] = ItemType.MusicDiscFar; + mappings[1097] = ItemType.MusicDiscMall; + mappings[1098] = ItemType.MusicDiscMellohi; + mappings[1104] = ItemType.MusicDiscOtherside; + mappings[1106] = ItemType.MusicDiscPigstep; + mappings[1099] = ItemType.MusicDiscStal; + mappings[1100] = ItemType.MusicDiscStrad; + mappings[1103] = ItemType.MusicDiscWait; + mappings[1101] = ItemType.MusicDiscWard; + mappings[1057] = ItemType.Mutton; + mappings[327] = ItemType.Mycelium; + mappings[1055] = ItemType.NameTag; + mappings[1110] = ItemType.NautilusShell; + mappings[1041] = ItemType.NetherBrick; + mappings[332] = ItemType.NetherBrickFence; + mappings[238] = ItemType.NetherBrickSlab; + mappings[333] = ItemType.NetherBrickStairs; + mappings[368] = ItemType.NetherBrickWall; + mappings[329] = ItemType.NetherBricks; + mappings[61] = ItemType.NetherGoldOre; + mappings[62] = ItemType.NetherQuartzOre; + mappings[207] = ItemType.NetherSprouts; + mappings[1036] = ItemType.NetherStar; + mappings[929] = ItemType.NetherWart; + mappings[479] = ItemType.NetherWartBlock; + mappings[780] = ItemType.NetheriteAxe; + mappings[74] = ItemType.NetheriteBlock; + mappings[814] = ItemType.NetheriteBoots; + mappings[812] = ItemType.NetheriteChestplate; + mappings[811] = ItemType.NetheriteHelmet; + mappings[781] = ItemType.NetheriteHoe; + mappings[750] = ItemType.NetheriteIngot; + mappings[813] = ItemType.NetheriteLeggings; + mappings[779] = ItemType.NetheritePickaxe; + mappings[751] = ItemType.NetheriteScrap; + mappings[778] = ItemType.NetheriteShovel; + mappings[777] = ItemType.NetheriteSword; + mappings[288] = ItemType.Netherrack; + mappings[641] = ItemType.NoteBlock; + mappings[713] = ItemType.OakBoat; + mappings[644] = ItemType.OakButton; + mappings[714] = ItemType.OakChestBoat; + mappings[669] = ItemType.OakDoor; + mappings[275] = ItemType.OakFence; + mappings[690] = ItemType.OakFenceGate; + mappings[831] = ItemType.OakHangingSign; + mappings[146] = ItemType.OakLeaves; + mappings[106] = ItemType.OakLog; + mappings[23] = ItemType.OakPlanks; + mappings[658] = ItemType.OakPressurePlate; + mappings[34] = ItemType.OakSapling; + mappings[821] = ItemType.OakSign; + mappings[218] = ItemType.OakSlab; + mappings[346] = ItemType.OakStairs; + mappings[680] = ItemType.OakTrapdoor; + mappings[137] = ItemType.OakWood; + mappings[627] = ItemType.Observer; + mappings[254] = ItemType.Obsidian; + mappings[974] = ItemType.OcelotSpawnEgg; + mappings[1181] = ItemType.OchreFroglight; + mappings[1060] = ItemType.OrangeBanner; + mappings[898] = ItemType.OrangeBed; + mappings[1161] = ItemType.OrangeCandle; + mappings[409] = ItemType.OrangeCarpet; + mappings[518] = ItemType.OrangeConcrete; + mappings[534] = ItemType.OrangeConcretePowder; + mappings[878] = ItemType.OrangeDye; + mappings[502] = ItemType.OrangeGlazedTerracotta; + mappings[486] = ItemType.OrangeShulkerBox; + mappings[434] = ItemType.OrangeStainedGlass; + mappings[450] = ItemType.OrangeStainedGlassPane; + mappings[390] = ItemType.OrangeTerracotta; + mappings[193] = ItemType.OrangeTulip; + mappings[172] = ItemType.OrangeWool; + mappings[196] = ItemType.OxeyeDaisy; + mappings[77] = ItemType.OxidizedCopper; + mappings[81] = ItemType.OxidizedCutCopper; + mappings[89] = ItemType.OxidizedCutCopperSlab; + mappings[85] = ItemType.OxidizedCutCopperStairs; + mappings[425] = ItemType.PackedIce; + mappings[307] = ItemType.PackedMud; + mappings[818] = ItemType.Painting; + mappings[975] = ItemType.PandaSpawnEgg; + mappings[857] = ItemType.Paper; + mappings[976] = ItemType.ParrotSpawnEgg; + mappings[1183] = ItemType.PearlescentFroglight; + mappings[430] = ItemType.Peony; + mappings[233] = ItemType.PetrifiedOakSlab; + mappings[1109] = ItemType.PhantomMembrane; + mappings[977] = ItemType.PhantomSpawnEgg; + mappings[978] = ItemType.PigSpawnEgg; + mappings[1120] = ItemType.PiglinBannerPattern; + mappings[980] = ItemType.PiglinBruteSpawnEgg; + mappings[1035] = ItemType.PiglinHead; + mappings[979] = ItemType.PiglinSpawnEgg; + mappings[981] = ItemType.PillagerSpawnEgg; + mappings[1065] = ItemType.PinkBanner; + mappings[903] = ItemType.PinkBed; + mappings[1166] = ItemType.PinkCandle; + mappings[414] = ItemType.PinkCarpet; + mappings[523] = ItemType.PinkConcrete; + mappings[539] = ItemType.PinkConcretePowder; + mappings[883] = ItemType.PinkDye; + mappings[507] = ItemType.PinkGlazedTerracotta; + mappings[491] = ItemType.PinkShulkerBox; + mappings[439] = ItemType.PinkStainedGlass; + mappings[455] = ItemType.PinkStainedGlassPane; + mappings[395] = ItemType.PinkTerracotta; + mappings[195] = ItemType.PinkTulip; + mappings[177] = ItemType.PinkWool; + mappings[623] = ItemType.Piston; + mappings[1031] = ItemType.PlayerHead; + mappings[17] = ItemType.Podzol; + mappings[1180] = ItemType.PointedDripstone; + mappings[1026] = ItemType.PoisonousPotato; + mappings[982] = ItemType.PolarBearSpawnEgg; + mappings[7] = ItemType.PolishedAndesite; + mappings[611] = ItemType.PolishedAndesiteSlab; + mappings[594] = ItemType.PolishedAndesiteStairs; + mappings[292] = ItemType.PolishedBasalt; + mappings[1150] = ItemType.PolishedBlackstone; + mappings[1155] = ItemType.PolishedBlackstoneBrickSlab; + mappings[1156] = ItemType.PolishedBlackstoneBrickStairs; + mappings[376] = ItemType.PolishedBlackstoneBrickWall; + mappings[1154] = ItemType.PolishedBlackstoneBricks; + mappings[643] = ItemType.PolishedBlackstoneButton; + mappings[655] = ItemType.PolishedBlackstonePressurePlate; + mappings[1151] = ItemType.PolishedBlackstoneSlab; + mappings[1152] = ItemType.PolishedBlackstoneStairs; + mappings[375] = ItemType.PolishedBlackstoneWall; + mappings[10] = ItemType.PolishedDeepslate; + mappings[614] = ItemType.PolishedDeepslateSlab; + mappings[597] = ItemType.PolishedDeepslateStairs; + mappings[378] = ItemType.PolishedDeepslateWall; + mappings[5] = ItemType.PolishedDiorite; + mappings[603] = ItemType.PolishedDioriteSlab; + mappings[585] = ItemType.PolishedDioriteStairs; + mappings[3] = ItemType.PolishedGranite; + mappings[600] = ItemType.PolishedGraniteSlab; + mappings[582] = ItemType.PolishedGraniteStairs; + mappings[1077] = ItemType.PoppedChorusFruit; + mappings[188] = ItemType.Poppy; + mappings[816] = ItemType.Porkchop; + mappings[1024] = ItemType.Potato; + mappings[930] = ItemType.Potion; + mappings[844] = ItemType.PowderSnowBucket; + mappings[700] = ItemType.PoweredRail; + mappings[465] = ItemType.Prismarine; + mappings[244] = ItemType.PrismarineBrickSlab; + mappings[469] = ItemType.PrismarineBrickStairs; + mappings[466] = ItemType.PrismarineBricks; + mappings[1043] = ItemType.PrismarineCrystals; + mappings[1042] = ItemType.PrismarineShard; + mappings[243] = ItemType.PrismarineSlab; + mappings[468] = ItemType.PrismarineStairs; + mappings[362] = ItemType.PrismarineWall; + mappings[871] = ItemType.Pufferfish; + mappings[848] = ItemType.PufferfishBucket; + mappings[983] = ItemType.PufferfishSpawnEgg; + mappings[285] = ItemType.Pumpkin; + mappings[1037] = ItemType.PumpkinPie; + mappings[918] = ItemType.PumpkinSeeds; + mappings[1069] = ItemType.PurpleBanner; + mappings[907] = ItemType.PurpleBed; + mappings[1170] = ItemType.PurpleCandle; + mappings[418] = ItemType.PurpleCarpet; + mappings[527] = ItemType.PurpleConcrete; + mappings[543] = ItemType.PurpleConcretePowder; + mappings[887] = ItemType.PurpleDye; + mappings[511] = ItemType.PurpleGlazedTerracotta; + mappings[495] = ItemType.PurpleShulkerBox; + mappings[443] = ItemType.PurpleStainedGlass; + mappings[459] = ItemType.PurpleStainedGlassPane; + mappings[399] = ItemType.PurpleTerracotta; + mappings[181] = ItemType.PurpleWool; + mappings[259] = ItemType.PurpurBlock; + mappings[260] = ItemType.PurpurPillar; + mappings[242] = ItemType.PurpurSlab; + mappings[261] = ItemType.PurpurStairs; + mappings[742] = ItemType.Quartz; + mappings[385] = ItemType.QuartzBlock; + mappings[386] = ItemType.QuartzBricks; + mappings[387] = ItemType.QuartzPillar; + mappings[239] = ItemType.QuartzSlab; + mappings[388] = ItemType.QuartzStairs; + mappings[1044] = ItemType.Rabbit; + mappings[1047] = ItemType.RabbitFoot; + mappings[1048] = ItemType.RabbitHide; + mappings[984] = ItemType.RabbitSpawnEgg; + mappings[1046] = ItemType.RabbitStew; + mappings[702] = ItemType.Rail; + mappings[985] = ItemType.RavagerSpawnEgg; + mappings[746] = ItemType.RawCopper; + mappings[66] = ItemType.RawCopperBlock; + mappings[748] = ItemType.RawGold; + mappings[67] = ItemType.RawGoldBlock; + mappings[744] = ItemType.RawIron; + mappings[65] = ItemType.RawIronBlock; + mappings[862] = ItemType.RecoveryCompass; + mappings[1073] = ItemType.RedBanner; + mappings[911] = ItemType.RedBed; + mappings[1174] = ItemType.RedCandle; + mappings[422] = ItemType.RedCarpet; + mappings[531] = ItemType.RedConcrete; + mappings[547] = ItemType.RedConcretePowder; + mappings[891] = ItemType.RedDye; + mappings[515] = ItemType.RedGlazedTerracotta; + mappings[202] = ItemType.RedMushroom; + mappings[316] = ItemType.RedMushroomBlock; + mappings[610] = ItemType.RedNetherBrickSlab; + mappings[593] = ItemType.RedNetherBrickStairs; + mappings[370] = ItemType.RedNetherBrickWall; + mappings[481] = ItemType.RedNetherBricks; + mappings[43] = ItemType.RedSand; + mappings[472] = ItemType.RedSandstone; + mappings[240] = ItemType.RedSandstoneSlab; + mappings[475] = ItemType.RedSandstoneStairs; + mappings[363] = ItemType.RedSandstoneWall; + mappings[499] = ItemType.RedShulkerBox; + mappings[447] = ItemType.RedStainedGlass; + mappings[463] = ItemType.RedStainedGlassPane; + mappings[403] = ItemType.RedTerracotta; + mappings[192] = ItemType.RedTulip; + mappings[185] = ItemType.RedWool; + mappings[618] = ItemType.Redstone; + mappings[620] = ItemType.RedstoneBlock; + mappings[640] = ItemType.RedstoneLamp; + mappings[53] = ItemType.RedstoneOre; + mappings[619] = ItemType.RedstoneTorch; + mappings[314] = ItemType.ReinforcedDeepslate; + mappings[621] = ItemType.Repeater; + mappings[476] = ItemType.RepeatingCommandBlock; + mappings[1158] = ItemType.RespawnAnchor; + mappings[18] = ItemType.RootedDirt; + mappings[429] = ItemType.RoseBush; + mappings[924] = ItemType.RottenFlesh; + mappings[704] = ItemType.Saddle; + mappings[869] = ItemType.Salmon; + mappings[849] = ItemType.SalmonBucket; + mappings[986] = ItemType.SalmonSpawnEgg; + mappings[42] = ItemType.Sand; + mappings[160] = ItemType.Sandstone; + mappings[231] = ItemType.SandstoneSlab; + mappings[343] = ItemType.SandstoneStairs; + mappings[371] = ItemType.SandstoneWall; + mappings[617] = ItemType.Scaffolding; + mappings[334] = ItemType.Sculk; + mappings[336] = ItemType.SculkCatalyst; + mappings[636] = ItemType.SculkSensor; + mappings[337] = ItemType.SculkShrieker; + mappings[335] = ItemType.SculkVein; + mappings[732] = ItemType.Scute; + mappings[471] = ItemType.SeaLantern; + mappings[170] = ItemType.SeaPickle; + mappings[169] = ItemType.Seagrass; + mappings[915] = ItemType.Shears; + mappings[987] = ItemType.SheepSpawnEgg; + mappings[1086] = ItemType.Shield; + mappings[1138] = ItemType.Shroomlight; + mappings[484] = ItemType.ShulkerBox; + mappings[1088] = ItemType.ShulkerShell; + mappings[988] = ItemType.ShulkerSpawnEgg; + mappings[989] = ItemType.SilverfishSpawnEgg; + mappings[991] = ItemType.SkeletonHorseSpawnEgg; + mappings[1029] = ItemType.SkeletonSkull; + mappings[990] = ItemType.SkeletonSpawnEgg; + mappings[1117] = ItemType.SkullBannerPattern; + mappings[859] = ItemType.SlimeBall; + mappings[625] = ItemType.SlimeBlock; + mappings[992] = ItemType.SlimeSpawnEgg; + mappings[1176] = ItemType.SmallAmethystBud; + mappings[216] = ItemType.SmallDripleaf; + mappings[1129] = ItemType.SmithingTable; + mappings[1124] = ItemType.Smoker; + mappings[293] = ItemType.SmoothBasalt; + mappings[246] = ItemType.SmoothQuartz; + mappings[607] = ItemType.SmoothQuartzSlab; + mappings[590] = ItemType.SmoothQuartzStairs; + mappings[247] = ItemType.SmoothRedSandstone; + mappings[601] = ItemType.SmoothRedSandstoneSlab; + mappings[583] = ItemType.SmoothRedSandstoneStairs; + mappings[248] = ItemType.SmoothSandstone; + mappings[606] = ItemType.SmoothSandstoneSlab; + mappings[589] = ItemType.SmoothSandstoneStairs; + mappings[249] = ItemType.SmoothStone; + mappings[230] = ItemType.SmoothStoneSlab; + mappings[269] = ItemType.Snow; + mappings[271] = ItemType.SnowBlock; + mappings[993] = ItemType.SnowGolemSpawnEgg; + mappings[845] = ItemType.Snowball; + mappings[1137] = ItemType.SoulCampfire; + mappings[1133] = ItemType.SoulLantern; + mappings[289] = ItemType.SoulSand; + mappings[290] = ItemType.SoulSoil; + mappings[294] = ItemType.SoulTorch; + mappings[262] = ItemType.Spawner; + mappings[1083] = ItemType.SpectralArrow; + mappings[932] = ItemType.SpiderEye; + mappings[994] = ItemType.SpiderSpawnEgg; + mappings[1082] = ItemType.SplashPotion; + mappings[155] = ItemType.Sponge; + mappings[200] = ItemType.SporeBlossom; + mappings[715] = ItemType.SpruceBoat; + mappings[645] = ItemType.SpruceButton; + mappings[716] = ItemType.SpruceChestBoat; + mappings[670] = ItemType.SpruceDoor; + mappings[276] = ItemType.SpruceFence; + mappings[691] = ItemType.SpruceFenceGate; + mappings[832] = ItemType.SpruceHangingSign; + mappings[147] = ItemType.SpruceLeaves; + mappings[107] = ItemType.SpruceLog; + mappings[24] = ItemType.SprucePlanks; + mappings[659] = ItemType.SprucePressurePlate; + mappings[35] = ItemType.SpruceSapling; + mappings[822] = ItemType.SpruceSign; + mappings[219] = ItemType.SpruceSlab; + mappings[347] = ItemType.SpruceStairs; + mappings[681] = ItemType.SpruceTrapdoor; + mappings[138] = ItemType.SpruceWood; + mappings[866] = ItemType.Spyglass; + mappings[995] = ItemType.SquidSpawnEgg; + mappings[782] = ItemType.Stick; + mappings[624] = ItemType.StickyPiston; + mappings[1] = ItemType.Stone; + mappings[760] = ItemType.StoneAxe; + mappings[236] = ItemType.StoneBrickSlab; + mappings[325] = ItemType.StoneBrickStairs; + mappings[366] = ItemType.StoneBrickWall; + mappings[303] = ItemType.StoneBricks; + mappings[642] = ItemType.StoneButton; + mappings[761] = ItemType.StoneHoe; + mappings[759] = ItemType.StonePickaxe; + mappings[654] = ItemType.StonePressurePlate; + mappings[758] = ItemType.StoneShovel; + mappings[229] = ItemType.StoneSlab; + mappings[588] = ItemType.StoneStairs; + mappings[757] = ItemType.StoneSword; + mappings[1130] = ItemType.Stonecutter; + mappings[996] = ItemType.StraySpawnEgg; + mappings[997] = ItemType.StriderSpawnEgg; + mappings[785] = ItemType.String; + mappings[122] = ItemType.StrippedAcaciaLog; + mappings[131] = ItemType.StrippedAcaciaWood; + mappings[136] = ItemType.StrippedBambooBlock; + mappings[120] = ItemType.StrippedBirchLog; + mappings[129] = ItemType.StrippedBirchWood; + mappings[134] = ItemType.StrippedCrimsonHyphae; + mappings[125] = ItemType.StrippedCrimsonStem; + mappings[123] = ItemType.StrippedDarkOakLog; + mappings[132] = ItemType.StrippedDarkOakWood; + mappings[121] = ItemType.StrippedJungleLog; + mappings[130] = ItemType.StrippedJungleWood; + mappings[124] = ItemType.StrippedMangroveLog; + mappings[133] = ItemType.StrippedMangroveWood; + mappings[118] = ItemType.StrippedOakLog; + mappings[127] = ItemType.StrippedOakWood; + mappings[119] = ItemType.StrippedSpruceLog; + mappings[128] = ItemType.StrippedSpruceWood; + mappings[135] = ItemType.StrippedWarpedHyphae; + mappings[126] = ItemType.StrippedWarpedStem; + mappings[729] = ItemType.StructureBlock; + mappings[483] = ItemType.StructureVoid; + mappings[895] = ItemType.Sugar; + mappings[210] = ItemType.SugarCane; + mappings[427] = ItemType.Sunflower; + mappings[1113] = ItemType.SuspiciousStew; + mappings[1134] = ItemType.SweetBerries; + mappings[853] = ItemType.TadpoleBucket; + mappings[998] = ItemType.TadpoleSpawnEgg; + mappings[431] = ItemType.TallGrass; + mappings[632] = ItemType.Target; + mappings[424] = ItemType.Terracotta; + mappings[158] = ItemType.TintedGlass; + mappings[1084] = ItemType.TippedArrow; + mappings[639] = ItemType.Tnt; + mappings[708] = ItemType.TntMinecart; + mappings[255] = ItemType.Torch; + mappings[1087] = ItemType.TotemOfUndying; + mappings[999] = ItemType.TraderLlamaSpawnEgg; + mappings[638] = ItemType.TrappedChest; + mappings[1108] = ItemType.Trident; + mappings[637] = ItemType.TripwireHook; + mappings[870] = ItemType.TropicalFish; + mappings[851] = ItemType.TropicalFishBucket; + mappings[1000] = ItemType.TropicalFishSpawnEgg; + mappings[560] = ItemType.TubeCoral; + mappings[555] = ItemType.TubeCoralBlock; + mappings[570] = ItemType.TubeCoralFan; + mappings[12] = ItemType.Tuff; + mappings[549] = ItemType.TurtleEgg; + mappings[731] = ItemType.TurtleHelmet; + mappings[1001] = ItemType.TurtleSpawnEgg; + mappings[209] = ItemType.TwistingVines; + mappings[1182] = ItemType.VerdantFroglight; + mappings[1002] = ItemType.VexSpawnEgg; + mappings[1003] = ItemType.VillagerSpawnEgg; + mappings[1004] = ItemType.VindicatorSpawnEgg; + mappings[322] = ItemType.Vine; + mappings[1005] = ItemType.WanderingTraderSpawnEgg; + mappings[1006] = ItemType.WardenSpawnEgg; + mappings[653] = ItemType.WarpedButton; + mappings[678] = ItemType.WarpedDoor; + mappings[284] = ItemType.WarpedFence; + mappings[699] = ItemType.WarpedFenceGate; + mappings[204] = ItemType.WarpedFungus; + mappings[711] = ItemType.WarpedFungusOnAStick; + mappings[840] = ItemType.WarpedHangingSign; + mappings[145] = ItemType.WarpedHyphae; + mappings[21] = ItemType.WarpedNylium; + mappings[32] = ItemType.WarpedPlanks; + mappings[667] = ItemType.WarpedPressurePlate; + mappings[206] = ItemType.WarpedRoots; + mappings[830] = ItemType.WarpedSign; + mappings[228] = ItemType.WarpedSlab; + mappings[356] = ItemType.WarpedStairs; + mappings[116] = ItemType.WarpedStem; + mappings[689] = ItemType.WarpedTrapdoor; + mappings[480] = ItemType.WarpedWartBlock; + mappings[842] = ItemType.WaterBucket; + mappings[90] = ItemType.WaxedCopperBlock; + mappings[94] = ItemType.WaxedCutCopper; + mappings[102] = ItemType.WaxedCutCopperSlab; + mappings[98] = ItemType.WaxedCutCopperStairs; + mappings[91] = ItemType.WaxedExposedCopper; + mappings[95] = ItemType.WaxedExposedCutCopper; + mappings[103] = ItemType.WaxedExposedCutCopperSlab; + mappings[99] = ItemType.WaxedExposedCutCopperStairs; + mappings[93] = ItemType.WaxedOxidizedCopper; + mappings[97] = ItemType.WaxedOxidizedCutCopper; + mappings[105] = ItemType.WaxedOxidizedCutCopperSlab; + mappings[101] = ItemType.WaxedOxidizedCutCopperStairs; + mappings[92] = ItemType.WaxedWeatheredCopper; + mappings[96] = ItemType.WaxedWeatheredCutCopper; + mappings[104] = ItemType.WaxedWeatheredCutCopperSlab; + mappings[100] = ItemType.WaxedWeatheredCutCopperStairs; + mappings[76] = ItemType.WeatheredCopper; + mappings[80] = ItemType.WeatheredCutCopper; + mappings[88] = ItemType.WeatheredCutCopperSlab; + mappings[84] = ItemType.WeatheredCutCopperStairs; + mappings[208] = ItemType.WeepingVines; + mappings[156] = ItemType.WetSponge; + mappings[789] = ItemType.Wheat; + mappings[788] = ItemType.WheatSeeds; + mappings[1059] = ItemType.WhiteBanner; + mappings[897] = ItemType.WhiteBed; + mappings[1160] = ItemType.WhiteCandle; + mappings[408] = ItemType.WhiteCarpet; + mappings[517] = ItemType.WhiteConcrete; + mappings[533] = ItemType.WhiteConcretePowder; + mappings[877] = ItemType.WhiteDye; + mappings[501] = ItemType.WhiteGlazedTerracotta; + mappings[485] = ItemType.WhiteShulkerBox; + mappings[433] = ItemType.WhiteStainedGlass; + mappings[449] = ItemType.WhiteStainedGlassPane; + mappings[389] = ItemType.WhiteTerracotta; + mappings[194] = ItemType.WhiteTulip; + mappings[171] = ItemType.WhiteWool; + mappings[1007] = ItemType.WitchSpawnEgg; + mappings[199] = ItemType.WitherRose; + mappings[1030] = ItemType.WitherSkeletonSkull; + mappings[1009] = ItemType.WitherSkeletonSpawnEgg; + mappings[1008] = ItemType.WitherSpawnEgg; + mappings[1010] = ItemType.WolfSpawnEgg; + mappings[755] = ItemType.WoodenAxe; + mappings[756] = ItemType.WoodenHoe; + mappings[754] = ItemType.WoodenPickaxe; + mappings[753] = ItemType.WoodenShovel; + mappings[752] = ItemType.WoodenSword; + mappings[1018] = ItemType.WritableBook; + mappings[1019] = ItemType.WrittenBook; + mappings[1063] = ItemType.YellowBanner; + mappings[901] = ItemType.YellowBed; + mappings[1164] = ItemType.YellowCandle; + mappings[412] = ItemType.YellowCarpet; + mappings[521] = ItemType.YellowConcrete; + mappings[537] = ItemType.YellowConcretePowder; + mappings[881] = ItemType.YellowDye; + mappings[505] = ItemType.YellowGlazedTerracotta; + mappings[489] = ItemType.YellowShulkerBox; + mappings[437] = ItemType.YellowStainedGlass; + mappings[453] = ItemType.YellowStainedGlassPane; + mappings[393] = ItemType.YellowTerracotta; + mappings[175] = ItemType.YellowWool; + mappings[1011] = ItemType.ZoglinSpawnEgg; + mappings[1032] = ItemType.ZombieHead; + mappings[1013] = ItemType.ZombieHorseSpawnEgg; + mappings[1012] = ItemType.ZombieSpawnEgg; + mappings[1014] = ItemType.ZombieVillagerSpawnEgg; + mappings[1015] = ItemType.ZombifiedPiglinSpawnEgg; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Inventory/ItemType.cs b/MinecraftClient/Inventory/ItemType.cs index 0e487d5b..11807ab7 100644 --- a/MinecraftClient/Inventory/ItemType.cs +++ b/MinecraftClient/Inventory/ItemType.cs @@ -20,6 +20,7 @@ AcaciaDoor, AcaciaFence, AcaciaFenceGate, + AcaciaHangingSign, AcaciaLeaves, AcaciaLog, AcaciaPlanks, @@ -53,6 +54,23 @@ AzureBluet, BakedPotato, Bamboo, + BambooBlock, + BambooButton, + BambooChestRaft, + BambooDoor, + BambooFence, + BambooFenceGate, + BambooHangingSign, + BambooMosaic, + BambooMosaicSlab, + BambooMosaicStairs, + BambooPlanks, + BambooPressurePlate, + BambooRaft, + BambooSign, + BambooSlab, + BambooStairs, + BambooTrapdoor, Barrel, Barrier, Basalt, @@ -74,6 +92,7 @@ BirchDoor, BirchFence, BirchFenceGate, + BirchHangingSign, BirchLeaves, BirchLog, BirchPlanks, @@ -161,6 +180,7 @@ Cactus, Cake, Calcite, + CamelSpawnEgg, Campfire, Candle, Carrot, @@ -182,6 +202,7 @@ Chicken, ChickenSpawnEgg, ChippedAnvil, + ChiseledBookshelf, ChiseledDeepslate, ChiseledNetherBricks, ChiseledPolishedBlackstone, @@ -245,6 +266,7 @@ CrimsonFence, CrimsonFenceGate, CrimsonFungus, + CrimsonHangingSign, CrimsonHyphae, CrimsonNylium, CrimsonPlanks, @@ -285,6 +307,7 @@ DarkOakDoor, DarkOakFence, DarkOakFenceGate, + DarkOakHangingSign, DarkOakLeaves, DarkOakLog, DarkOakPlanks, @@ -384,6 +407,7 @@ EndStoneBrickWall, EndStoneBricks, EnderChest, + EnderDragonSpawnEgg, EnderEye, EnderPearl, EndermanSpawnEgg, @@ -518,6 +542,7 @@ IronBoots, IronChestplate, IronDoor, + IronGolemSpawnEgg, IronHelmet, IronHoe, IronHorseArmor, @@ -539,6 +564,7 @@ JungleDoor, JungleFence, JungleFenceGate, + JungleHangingSign, JungleLeaves, JungleLog, JunglePlanks, @@ -639,6 +665,7 @@ MangroveDoor, MangroveFence, MangroveFenceGate, + MangroveHangingSign, MangroveLeaves, MangroveLog, MangrovePlanks, @@ -729,6 +756,7 @@ OakDoor, OakFence, OakFenceGate, + OakHangingSign, OakLeaves, OakLog, OakPlanks, @@ -776,6 +804,7 @@ PigSpawnEgg, PiglinBannerPattern, PiglinBruteSpawnEgg, + PiglinHead, PiglinSpawnEgg, PillagerSpawnEgg, PinkBanner, @@ -970,6 +999,7 @@ SmoothStoneSlab, Snow, SnowBlock, + SnowGolemSpawnEgg, Snowball, SoulCampfire, SoulLantern, @@ -989,6 +1019,7 @@ SpruceDoor, SpruceFence, SpruceFenceGate, + SpruceHangingSign, SpruceLeaves, SpruceLog, SprucePlanks, @@ -1023,6 +1054,7 @@ String, StrippedAcaciaLog, StrippedAcaciaWood, + StrippedBambooBlock, StrippedBirchLog, StrippedBirchWood, StrippedCrimsonHyphae, @@ -1085,6 +1117,7 @@ WarpedFenceGate, WarpedFungus, WarpedFungusOnAStick, + WarpedHangingSign, WarpedHyphae, WarpedNylium, WarpedPlanks, @@ -1139,6 +1172,7 @@ WitherRose, WitherSkeletonSkull, WitherSkeletonSpawnEgg, + WitherSpawnEgg, WolfSpawnEgg, WoodenAxe, WoodenHoe, diff --git a/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs b/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs new file mode 100644 index 00000000..eb1e61fb --- /dev/null +++ b/MinecraftClient/Mapping/BlockPalettes/Palette1193.cs @@ -0,0 +1,1605 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.BlockPalettes +{ + public class Palette1193 : BlockPalette + { + private static readonly Dictionary materials = new(); + + static Palette1193() + { + for (int i = 8475; i <= 8498; i++) + materials[i] = Material.AcaciaButton; + for (int i = 11467; i <= 11530; i++) + materials[i] = Material.AcaciaDoor; + for (int i = 11147; i <= 11178; i++) + materials[i] = Material.AcaciaFence; + for (int i = 10923; i <= 10954; i++) + materials[i] = Material.AcaciaFenceGate; + for (int i = 4934; i <= 4997; i++) + materials[i] = Material.AcaciaHangingSign; + for (int i = 326; i <= 353; i++) + materials[i] = Material.AcaciaLeaves; + for (int i = 131; i <= 133; i++) + materials[i] = Material.AcaciaLog; + materials[19] = Material.AcaciaPlanks; + for (int i = 5560; i <= 5561; i++) + materials[i] = Material.AcaciaPressurePlate; + for (int i = 32; i <= 33; i++) + materials[i] = Material.AcaciaSapling; + for (int i = 4346; i <= 4377; i++) + materials[i] = Material.AcaciaSign; + for (int i = 10709; i <= 10714; i++) + materials[i] = Material.AcaciaSlab; + for (int i = 9488; i <= 9567; i++) + materials[i] = Material.AcaciaStairs; + for (int i = 6052; i <= 6115; i++) + materials[i] = Material.AcaciaTrapdoor; + for (int i = 5406; i <= 5413; i++) + materials[i] = Material.AcaciaWallHangingSign; + for (int i = 4702; i <= 4709; i++) + materials[i] = Material.AcaciaWallSign; + for (int i = 184; i <= 186; i++) + materials[i] = Material.AcaciaWood; + for (int i = 8924; i <= 8947; i++) + materials[i] = Material.ActivatorRail; + materials[0] = Material.Air; + materials[2027] = Material.Allium; + materials[20403] = Material.AmethystBlock; + for (int i = 20405; i <= 20416; i++) + materials[i] = Material.AmethystCluster; + materials[18820] = Material.AncientDebris; + materials[6] = Material.Andesite; + for (int i = 13508; i <= 13513; i++) + materials[i] = Material.AndesiteSlab; + for (int i = 13134; i <= 13213; i++) + materials[i] = Material.AndesiteStairs; + for (int i = 16124; i <= 16447; i++) + materials[i] = Material.AndesiteWall; + for (int i = 8711; i <= 8714; i++) + materials[i] = Material.Anvil; + for (int i = 6587; i <= 6590; i++) + materials[i] = Material.AttachedMelonStem; + for (int i = 6583; i <= 6586; i++) + materials[i] = Material.AttachedPumpkinStem; + materials[21498] = Material.Azalea; + for (int i = 410; i <= 437; i++) + materials[i] = Material.AzaleaLeaves; + materials[2028] = Material.AzureBluet; + for (int i = 12317; i <= 12328; i++) + materials[i] = Material.Bamboo; + for (int i = 145; i <= 147; i++) + materials[i] = Material.BambooBlock; + for (int i = 8547; i <= 8570; i++) + materials[i] = Material.BambooButton; + for (int i = 11659; i <= 11722; i++) + materials[i] = Material.BambooDoor; + for (int i = 11243; i <= 11274; i++) + materials[i] = Material.BambooFence; + for (int i = 11019; i <= 11050; i++) + materials[i] = Material.BambooFenceGate; + for (int i = 5318; i <= 5381; i++) + materials[i] = Material.BambooHangingSign; + materials[23] = Material.BambooMosaic; + for (int i = 10733; i <= 10738; i++) + materials[i] = Material.BambooMosaicSlab; + for (int i = 9808; i <= 9887; i++) + materials[i] = Material.BambooMosaicStairs; + materials[22] = Material.BambooPlanks; + for (int i = 5566; i <= 5567; i++) + materials[i] = Material.BambooPressurePlate; + materials[12316] = Material.BambooSapling; + for (int i = 4474; i <= 4505; i++) + materials[i] = Material.BambooSign; + for (int i = 10727; i <= 10732; i++) + materials[i] = Material.BambooSlab; + for (int i = 9728; i <= 9807; i++) + materials[i] = Material.BambooStairs; + for (int i = 6244; i <= 6307; i++) + materials[i] = Material.BambooTrapdoor; + for (int i = 5454; i <= 5461; i++) + materials[i] = Material.BambooWallHangingSign; + for (int i = 4734; i <= 4741; i++) + materials[i] = Material.BambooWallSign; + for (int i = 17780; i <= 17791; i++) + materials[i] = Material.Barrel; + materials[9889] = Material.Barrier; + for (int i = 5687; i <= 5689; i++) + materials[i] = Material.Basalt; + materials[7688] = Material.Beacon; + materials[76] = Material.Bedrock; + for (int i = 18769; i <= 18792; i++) + materials[i] = Material.BeeNest; + for (int i = 18793; i <= 18816; i++) + materials[i] = Material.Beehive; + for (int i = 11884; i <= 11887; i++) + materials[i] = Material.Beetroots; + for (int i = 17843; i <= 17874; i++) + materials[i] = Material.Bell; + for (int i = 21502; i <= 21533; i++) + materials[i] = Material.BigDripleaf; + for (int i = 21534; i <= 21541; i++) + materials[i] = Material.BigDripleafStem; + for (int i = 8427; i <= 8450; i++) + materials[i] = Material.BirchButton; + for (int i = 11339; i <= 11402; i++) + materials[i] = Material.BirchDoor; + for (int i = 11083; i <= 11114; i++) + materials[i] = Material.BirchFence; + for (int i = 10859; i <= 10890; i++) + materials[i] = Material.BirchFenceGate; + for (int i = 4870; i <= 4933; i++) + materials[i] = Material.BirchHangingSign; + for (int i = 270; i <= 297; i++) + materials[i] = Material.BirchLeaves; + for (int i = 125; i <= 127; i++) + materials[i] = Material.BirchLog; + materials[17] = Material.BirchPlanks; + for (int i = 5556; i <= 5557; i++) + materials[i] = Material.BirchPressurePlate; + for (int i = 28; i <= 29; i++) + materials[i] = Material.BirchSapling; + for (int i = 4314; i <= 4345; i++) + materials[i] = Material.BirchSign; + for (int i = 10697; i <= 10702; i++) + materials[i] = Material.BirchSlab; + for (int i = 7516; i <= 7595; i++) + materials[i] = Material.BirchStairs; + for (int i = 5924; i <= 5987; i++) + materials[i] = Material.BirchTrapdoor; + for (int i = 5398; i <= 5405; i++) + materials[i] = Material.BirchWallHangingSign; + for (int i = 4694; i <= 4701; i++) + materials[i] = Material.BirchWallSign; + for (int i = 178; i <= 180; i++) + materials[i] = Material.BirchWood; + for (int i = 10522; i <= 10537; i++) + materials[i] = Material.BlackBanner; + for (int i = 1877; i <= 1892; i++) + materials[i] = Material.BlackBed; + for (int i = 20353; i <= 20368; i++) + materials[i] = Material.BlackCandle; + for (int i = 20401; i <= 20402; i++) + materials[i] = Material.BlackCandleCake; + materials[10266] = Material.BlackCarpet; + materials[12118] = Material.BlackConcrete; + materials[12134] = Material.BlackConcretePowder; + for (int i = 12099; i <= 12102; i++) + materials[i] = Material.BlackGlazedTerracotta; + for (int i = 12033; i <= 12038; i++) + materials[i] = Material.BlackShulkerBox; + materials[5795] = Material.BlackStainedGlass; + for (int i = 9456; i <= 9487; i++) + materials[i] = Material.BlackStainedGlassPane; + materials[8975] = Material.BlackTerracotta; + for (int i = 10598; i <= 10601; i++) + materials[i] = Material.BlackWallBanner; + materials[2011] = Material.BlackWool; + materials[18832] = Material.Blackstone; + for (int i = 19237; i <= 19242; i++) + materials[i] = Material.BlackstoneSlab; + for (int i = 18833; i <= 18912; i++) + materials[i] = Material.BlackstoneStairs; + for (int i = 18913; i <= 19236; i++) + materials[i] = Material.BlackstoneWall; + for (int i = 17800; i <= 17807; i++) + materials[i] = Material.BlastFurnace; + for (int i = 10458; i <= 10473; i++) + materials[i] = Material.BlueBanner; + for (int i = 1813; i <= 1828; i++) + materials[i] = Material.BlueBed; + for (int i = 20289; i <= 20304; i++) + materials[i] = Material.BlueCandle; + for (int i = 20393; i <= 20394; i++) + materials[i] = Material.BlueCandleCake; + materials[10262] = Material.BlueCarpet; + materials[12114] = Material.BlueConcrete; + materials[12130] = Material.BlueConcretePowder; + for (int i = 12083; i <= 12086; i++) + materials[i] = Material.BlueGlazedTerracotta; + materials[12313] = Material.BlueIce; + materials[2026] = Material.BlueOrchid; + for (int i = 12009; i <= 12014; i++) + materials[i] = Material.BlueShulkerBox; + materials[5791] = Material.BlueStainedGlass; + for (int i = 9328; i <= 9359; i++) + materials[i] = Material.BlueStainedGlassPane; + materials[8971] = Material.BlueTerracotta; + for (int i = 10582; i <= 10585; i++) + materials[i] = Material.BlueWallBanner; + materials[2007] = Material.BlueWool; + for (int i = 11921; i <= 11923; i++) + materials[i] = Material.BoneBlock; + materials[2044] = Material.Bookshelf; + for (int i = 12197; i <= 12198; i++) + materials[i] = Material.BrainCoral; + materials[12181] = Material.BrainCoralBlock; + for (int i = 12217; i <= 12218; i++) + materials[i] = Material.BrainCoralFan; + for (int i = 12273; i <= 12280; i++) + materials[i] = Material.BrainCoralWallFan; + for (int i = 7160; i <= 7167; i++) + materials[i] = Material.BrewingStand; + for (int i = 10775; i <= 10780; i++) + materials[i] = Material.BrickSlab; + for (int i = 6799; i <= 6878; i++) + materials[i] = Material.BrickStairs; + for (int i = 13532; i <= 13855; i++) + materials[i] = Material.BrickWall; + materials[2041] = Material.Bricks; + for (int i = 10474; i <= 10489; i++) + materials[i] = Material.BrownBanner; + for (int i = 1829; i <= 1844; i++) + materials[i] = Material.BrownBed; + for (int i = 20305; i <= 20320; i++) + materials[i] = Material.BrownCandle; + for (int i = 20395; i <= 20396; i++) + materials[i] = Material.BrownCandleCake; + materials[10263] = Material.BrownCarpet; + materials[12115] = Material.BrownConcrete; + materials[12131] = Material.BrownConcretePowder; + for (int i = 12087; i <= 12090; i++) + materials[i] = Material.BrownGlazedTerracotta; + materials[2037] = Material.BrownMushroom; + for (int i = 6320; i <= 6383; i++) + materials[i] = Material.BrownMushroomBlock; + for (int i = 12015; i <= 12020; i++) + materials[i] = Material.BrownShulkerBox; + materials[5792] = Material.BrownStainedGlass; + for (int i = 9360; i <= 9391; i++) + materials[i] = Material.BrownStainedGlassPane; + materials[8972] = Material.BrownTerracotta; + for (int i = 10586; i <= 10589; i++) + materials[i] = Material.BrownWallBanner; + materials[2008] = Material.BrownWool; + for (int i = 12332; i <= 12333; i++) + materials[i] = Material.BubbleColumn; + for (int i = 12199; i <= 12200; i++) + materials[i] = Material.BubbleCoral; + materials[12182] = Material.BubbleCoralBlock; + for (int i = 12219; i <= 12220; i++) + materials[i] = Material.BubbleCoralFan; + for (int i = 12281; i <= 12288; i++) + materials[i] = Material.BubbleCoralWallFan; + materials[20404] = Material.BuddingAmethyst; + for (int i = 5616; i <= 5631; i++) + materials[i] = Material.Cactus; + for (int i = 5709; i <= 5715; i++) + materials[i] = Material.Cake; + materials[20454] = Material.Calcite; + for (int i = 17883; i <= 17914; i++) + materials[i] = Material.Campfire; + for (int i = 20097; i <= 20112; i++) + materials[i] = Material.Candle; + for (int i = 20369; i <= 20370; i++) + materials[i] = Material.CandleCake; + for (int i = 8363; i <= 8370; i++) + materials[i] = Material.Carrots; + materials[17808] = Material.CartographyTable; + for (int i = 5701; i <= 5704; i++) + materials[i] = Material.CarvedPumpkin; + materials[7168] = Material.Cauldron; + materials[12331] = Material.CaveAir; + for (int i = 21443; i <= 21494; i++) + materials[i] = Material.CaveVines; + for (int i = 21495; i <= 21496; i++) + materials[i] = Material.CaveVinesPlant; + for (int i = 6544; i <= 6549; i++) + materials[i] = Material.Chain; + for (int i = 11902; i <= 11913; i++) + materials[i] = Material.ChainCommandBlock; + for (int i = 2902; i <= 2925; i++) + materials[i] = Material.Chest; + for (int i = 8715; i <= 8718; i++) + materials[i] = Material.ChippedAnvil; + for (int i = 2045; i <= 2300; i++) + materials[i] = Material.ChiseledBookshelf; + materials[23209] = Material.ChiseledDeepslate; + materials[20094] = Material.ChiseledNetherBricks; + materials[19246] = Material.ChiseledPolishedBlackstone; + materials[8840] = Material.ChiseledQuartzBlock; + materials[10603] = Material.ChiseledRedSandstone; + materials[485] = Material.ChiseledSandstone; + materials[6311] = Material.ChiseledStoneBricks; + for (int i = 11793; i <= 11798; i++) + materials[i] = Material.ChorusFlower; + for (int i = 11729; i <= 11792; i++) + materials[i] = Material.ChorusPlant; + materials[5632] = Material.Clay; + materials[10268] = Material.CoalBlock; + materials[116] = Material.CoalOre; + materials[11] = Material.CoarseDirt; + materials[21565] = Material.CobbledDeepslate; + for (int i = 21646; i <= 21651; i++) + materials[i] = Material.CobbledDeepslateSlab; + for (int i = 21566; i <= 21645; i++) + materials[i] = Material.CobbledDeepslateStairs; + for (int i = 21652; i <= 21975; i++) + materials[i] = Material.CobbledDeepslateWall; + materials[14] = Material.Cobblestone; + for (int i = 10769; i <= 10774; i++) + materials[i] = Material.CobblestoneSlab; + for (int i = 4598; i <= 4677; i++) + materials[i] = Material.CobblestoneStairs; + for (int i = 7689; i <= 8012; i++) + materials[i] = Material.CobblestoneWall; + materials[1953] = Material.Cobweb; + for (int i = 7189; i <= 7200; i++) + materials[i] = Material.Cocoa; + for (int i = 7676; i <= 7687; i++) + materials[i] = Material.CommandBlock; + for (int i = 8779; i <= 8794; i++) + materials[i] = Material.Comparator; + for (int i = 18744; i <= 18752; i++) + materials[i] = Material.Composter; + for (int i = 12314; i <= 12315; i++) + materials[i] = Material.Conduit; + materials[20695] = Material.CopperBlock; + materials[20696] = Material.CopperOre; + materials[2034] = Material.Cornflower; + materials[23210] = Material.CrackedDeepslateBricks; + materials[23211] = Material.CrackedDeepslateTiles; + materials[20095] = Material.CrackedNetherBricks; + materials[19245] = Material.CrackedPolishedBlackstoneBricks; + materials[6310] = Material.CrackedStoneBricks; + materials[4225] = Material.CraftingTable; + for (int i = 8651; i <= 8666; i++) + materials[i] = Material.CreeperHead; + for (int i = 8667; i <= 8670; i++) + materials[i] = Material.CreeperWallHead; + for (int i = 18472; i <= 18495; i++) + materials[i] = Material.CrimsonButton; + for (int i = 18520; i <= 18583; i++) + materials[i] = Material.CrimsonDoor; + for (int i = 18056; i <= 18087; i++) + materials[i] = Material.CrimsonFence; + for (int i = 18248; i <= 18279; i++) + materials[i] = Material.CrimsonFenceGate; + materials[17981] = Material.CrimsonFungus; + for (int i = 5126; i <= 5189; i++) + materials[i] = Material.CrimsonHangingSign; + for (int i = 17974; i <= 17976; i++) + materials[i] = Material.CrimsonHyphae; + materials[17980] = Material.CrimsonNylium; + materials[18038] = Material.CrimsonPlanks; + for (int i = 18052; i <= 18053; i++) + materials[i] = Material.CrimsonPressurePlate; + materials[18037] = Material.CrimsonRoots; + for (int i = 18648; i <= 18679; i++) + materials[i] = Material.CrimsonSign; + for (int i = 18040; i <= 18045; i++) + materials[i] = Material.CrimsonSlab; + for (int i = 18312; i <= 18391; i++) + materials[i] = Material.CrimsonStairs; + for (int i = 17968; i <= 17970; i++) + materials[i] = Material.CrimsonStem; + for (int i = 18120; i <= 18183; i++) + materials[i] = Material.CrimsonTrapdoor; + for (int i = 5438; i <= 5445; i++) + materials[i] = Material.CrimsonWallHangingSign; + for (int i = 18712; i <= 18719; i++) + materials[i] = Material.CrimsonWallSign; + materials[18821] = Material.CryingObsidian; + materials[20701] = Material.CutCopper; + for (int i = 21040; i <= 21045; i++) + materials[i] = Material.CutCopperSlab; + for (int i = 20942; i <= 21021; i++) + materials[i] = Material.CutCopperStairs; + materials[10604] = Material.CutRedSandstone; + for (int i = 10811; i <= 10816; i++) + materials[i] = Material.CutRedSandstoneSlab; + materials[486] = Material.CutSandstone; + for (int i = 10757; i <= 10762; i++) + materials[i] = Material.CutSandstoneSlab; + for (int i = 10426; i <= 10441; i++) + materials[i] = Material.CyanBanner; + for (int i = 1781; i <= 1796; i++) + materials[i] = Material.CyanBed; + for (int i = 20257; i <= 20272; i++) + materials[i] = Material.CyanCandle; + for (int i = 20389; i <= 20390; i++) + materials[i] = Material.CyanCandleCake; + materials[10260] = Material.CyanCarpet; + materials[12112] = Material.CyanConcrete; + materials[12128] = Material.CyanConcretePowder; + for (int i = 12075; i <= 12078; i++) + materials[i] = Material.CyanGlazedTerracotta; + for (int i = 11997; i <= 12002; i++) + materials[i] = Material.CyanShulkerBox; + materials[5789] = Material.CyanStainedGlass; + for (int i = 9264; i <= 9295; i++) + materials[i] = Material.CyanStainedGlassPane; + materials[8969] = Material.CyanTerracotta; + for (int i = 10574; i <= 10577; i++) + materials[i] = Material.CyanWallBanner; + materials[2005] = Material.CyanWool; + for (int i = 8719; i <= 8722; i++) + materials[i] = Material.DamagedAnvil; + materials[2024] = Material.Dandelion; + for (int i = 8499; i <= 8522; i++) + materials[i] = Material.DarkOakButton; + for (int i = 11531; i <= 11594; i++) + materials[i] = Material.DarkOakDoor; + for (int i = 11179; i <= 11210; i++) + materials[i] = Material.DarkOakFence; + for (int i = 10955; i <= 10986; i++) + materials[i] = Material.DarkOakFenceGate; + for (int i = 5062; i <= 5125; i++) + materials[i] = Material.DarkOakHangingSign; + for (int i = 354; i <= 381; i++) + materials[i] = Material.DarkOakLeaves; + for (int i = 134; i <= 136; i++) + materials[i] = Material.DarkOakLog; + materials[20] = Material.DarkOakPlanks; + for (int i = 5562; i <= 5563; i++) + materials[i] = Material.DarkOakPressurePlate; + for (int i = 34; i <= 35; i++) + materials[i] = Material.DarkOakSapling; + for (int i = 4410; i <= 4441; i++) + materials[i] = Material.DarkOakSign; + for (int i = 10715; i <= 10720; i++) + materials[i] = Material.DarkOakSlab; + for (int i = 9568; i <= 9647; i++) + materials[i] = Material.DarkOakStairs; + for (int i = 6116; i <= 6179; i++) + materials[i] = Material.DarkOakTrapdoor; + for (int i = 5422; i <= 5429; i++) + materials[i] = Material.DarkOakWallHangingSign; + for (int i = 4718; i <= 4725; i++) + materials[i] = Material.DarkOakWallSign; + for (int i = 187; i <= 189; i++) + materials[i] = Material.DarkOakWood; + materials[9988] = Material.DarkPrismarine; + for (int i = 10241; i <= 10246; i++) + materials[i] = Material.DarkPrismarineSlab; + for (int i = 10149; i <= 10228; i++) + materials[i] = Material.DarkPrismarineStairs; + for (int i = 8795; i <= 8826; i++) + materials[i] = Material.DaylightDetector; + for (int i = 12187; i <= 12188; i++) + materials[i] = Material.DeadBrainCoral; + materials[12176] = Material.DeadBrainCoralBlock; + for (int i = 12207; i <= 12208; i++) + materials[i] = Material.DeadBrainCoralFan; + for (int i = 12233; i <= 12240; i++) + materials[i] = Material.DeadBrainCoralWallFan; + for (int i = 12189; i <= 12190; i++) + materials[i] = Material.DeadBubbleCoral; + materials[12177] = Material.DeadBubbleCoralBlock; + for (int i = 12209; i <= 12210; i++) + materials[i] = Material.DeadBubbleCoralFan; + for (int i = 12241; i <= 12248; i++) + materials[i] = Material.DeadBubbleCoralWallFan; + materials[1956] = Material.DeadBush; + for (int i = 12191; i <= 12192; i++) + materials[i] = Material.DeadFireCoral; + materials[12178] = Material.DeadFireCoralBlock; + for (int i = 12211; i <= 12212; i++) + materials[i] = Material.DeadFireCoralFan; + for (int i = 12249; i <= 12256; i++) + materials[i] = Material.DeadFireCoralWallFan; + for (int i = 12193; i <= 12194; i++) + materials[i] = Material.DeadHornCoral; + materials[12179] = Material.DeadHornCoralBlock; + for (int i = 12213; i <= 12214; i++) + materials[i] = Material.DeadHornCoralFan; + for (int i = 12257; i <= 12264; i++) + materials[i] = Material.DeadHornCoralWallFan; + for (int i = 12185; i <= 12186; i++) + materials[i] = Material.DeadTubeCoral; + materials[12175] = Material.DeadTubeCoralBlock; + for (int i = 12205; i <= 12206; i++) + materials[i] = Material.DeadTubeCoralFan; + for (int i = 12225; i <= 12232; i++) + materials[i] = Material.DeadTubeCoralWallFan; + for (int i = 21562; i <= 21564; i++) + materials[i] = Material.Deepslate; + for (int i = 22879; i <= 22884; i++) + materials[i] = Material.DeepslateBrickSlab; + for (int i = 22799; i <= 22878; i++) + materials[i] = Material.DeepslateBrickStairs; + for (int i = 22885; i <= 23208; i++) + materials[i] = Material.DeepslateBrickWall; + materials[22798] = Material.DeepslateBricks; + materials[117] = Material.DeepslateCoalOre; + materials[20697] = Material.DeepslateCopperOre; + materials[4223] = Material.DeepslateDiamondOre; + materials[7282] = Material.DeepslateEmeraldOre; + materials[113] = Material.DeepslateGoldOre; + materials[115] = Material.DeepslateIronOre; + materials[470] = Material.DeepslateLapisOre; + for (int i = 5570; i <= 5571; i++) + materials[i] = Material.DeepslateRedstoneOre; + for (int i = 22468; i <= 22473; i++) + materials[i] = Material.DeepslateTileSlab; + for (int i = 22388; i <= 22467; i++) + materials[i] = Material.DeepslateTileStairs; + for (int i = 22474; i <= 22797; i++) + materials[i] = Material.DeepslateTileWall; + materials[22387] = Material.DeepslateTiles; + for (int i = 1917; i <= 1940; i++) + materials[i] = Material.DetectorRail; + materials[4224] = Material.DiamondBlock; + materials[4222] = Material.DiamondOre; + materials[4] = Material.Diorite; + for (int i = 13526; i <= 13531; i++) + materials[i] = Material.DioriteSlab; + for (int i = 13374; i <= 13453; i++) + materials[i] = Material.DioriteStairs; + for (int i = 17420; i <= 17743; i++) + materials[i] = Material.DioriteWall; + materials[10] = Material.Dirt; + materials[11888] = Material.DirtPath; + for (int i = 472; i <= 483; i++) + materials[i] = Material.Dispenser; + materials[7186] = Material.DragonEgg; + for (int i = 8671; i <= 8686; i++) + materials[i] = Material.DragonHead; + for (int i = 8687; i <= 8690; i++) + materials[i] = Material.DragonWallHead; + materials[12162] = Material.DriedKelpBlock; + materials[21442] = Material.DripstoneBlock; + for (int i = 8948; i <= 8959; i++) + materials[i] = Material.Dropper; + materials[7435] = Material.EmeraldBlock; + materials[7281] = Material.EmeraldOre; + materials[7159] = Material.EnchantingTable; + materials[11889] = Material.EndGateway; + materials[7176] = Material.EndPortal; + for (int i = 7177; i <= 7184; i++) + materials[i] = Material.EndPortalFrame; + for (int i = 11723; i <= 11728; i++) + materials[i] = Material.EndRod; + materials[7185] = Material.EndStone; + for (int i = 13484; i <= 13489; i++) + materials[i] = Material.EndStoneBrickSlab; + for (int i = 12734; i <= 12813; i++) + materials[i] = Material.EndStoneBrickStairs; + for (int i = 17096; i <= 17419; i++) + materials[i] = Material.EndStoneBrickWall; + materials[11883] = Material.EndStoneBricks; + for (int i = 7283; i <= 7290; i++) + materials[i] = Material.EnderChest; + materials[20694] = Material.ExposedCopper; + materials[20700] = Material.ExposedCutCopper; + for (int i = 21034; i <= 21039; i++) + materials[i] = Material.ExposedCutCopperSlab; + for (int i = 20862; i <= 20941; i++) + materials[i] = Material.ExposedCutCopperStairs; + for (int i = 4234; i <= 4241; i++) + materials[i] = Material.Farmland; + materials[1955] = Material.Fern; + for (int i = 2308; i <= 2819; i++) + materials[i] = Material.Fire; + for (int i = 12201; i <= 12202; i++) + materials[i] = Material.FireCoral; + materials[12183] = Material.FireCoralBlock; + for (int i = 12221; i <= 12222; i++) + materials[i] = Material.FireCoralFan; + for (int i = 12289; i <= 12296; i++) + materials[i] = Material.FireCoralWallFan; + materials[17809] = Material.FletchingTable; + materials[8337] = Material.FlowerPot; + materials[21499] = Material.FloweringAzalea; + for (int i = 438; i <= 465; i++) + materials[i] = Material.FloweringAzaleaLeaves; + materials[23230] = Material.Frogspawn; + for (int i = 11914; i <= 11917; i++) + materials[i] = Material.FrostedIce; + for (int i = 4242; i <= 4249; i++) + materials[i] = Material.Furnace; + materials[19657] = Material.GildedBlackstone; + materials[468] = Material.Glass; + for (int i = 6550; i <= 6581; i++) + materials[i] = Material.GlassPane; + for (int i = 6639; i <= 6766; i++) + materials[i] = Material.GlowLichen; + materials[5698] = Material.Glowstone; + materials[2039] = Material.GoldBlock; + materials[112] = Material.GoldOre; + materials[2] = Material.Granite; + for (int i = 13502; i <= 13507; i++) + materials[i] = Material.GraniteSlab; + for (int i = 13054; i <= 13133; i++) + materials[i] = Material.GraniteStairs; + for (int i = 14828; i <= 15151; i++) + materials[i] = Material.GraniteWall; + materials[1954] = Material.Grass; + for (int i = 8; i <= 9; i++) + materials[i] = Material.GrassBlock; + materials[111] = Material.Gravel; + for (int i = 10394; i <= 10409; i++) + materials[i] = Material.GrayBanner; + for (int i = 1749; i <= 1764; i++) + materials[i] = Material.GrayBed; + for (int i = 20225; i <= 20240; i++) + materials[i] = Material.GrayCandle; + for (int i = 20385; i <= 20386; i++) + materials[i] = Material.GrayCandleCake; + materials[10258] = Material.GrayCarpet; + materials[12110] = Material.GrayConcrete; + materials[12126] = Material.GrayConcretePowder; + for (int i = 12067; i <= 12070; i++) + materials[i] = Material.GrayGlazedTerracotta; + for (int i = 11985; i <= 11990; i++) + materials[i] = Material.GrayShulkerBox; + materials[5787] = Material.GrayStainedGlass; + for (int i = 9200; i <= 9231; i++) + materials[i] = Material.GrayStainedGlassPane; + materials[8967] = Material.GrayTerracotta; + for (int i = 10566; i <= 10569; i++) + materials[i] = Material.GrayWallBanner; + materials[2003] = Material.GrayWool; + for (int i = 10490; i <= 10505; i++) + materials[i] = Material.GreenBanner; + for (int i = 1845; i <= 1860; i++) + materials[i] = Material.GreenBed; + for (int i = 20321; i <= 20336; i++) + materials[i] = Material.GreenCandle; + for (int i = 20397; i <= 20398; i++) + materials[i] = Material.GreenCandleCake; + materials[10264] = Material.GreenCarpet; + materials[12116] = Material.GreenConcrete; + materials[12132] = Material.GreenConcretePowder; + for (int i = 12091; i <= 12094; i++) + materials[i] = Material.GreenGlazedTerracotta; + for (int i = 12021; i <= 12026; i++) + materials[i] = Material.GreenShulkerBox; + materials[5793] = Material.GreenStainedGlass; + for (int i = 9392; i <= 9423; i++) + materials[i] = Material.GreenStainedGlassPane; + materials[8973] = Material.GreenTerracotta; + for (int i = 10590; i <= 10593; i++) + materials[i] = Material.GreenWallBanner; + materials[2009] = Material.GreenWool; + for (int i = 17810; i <= 17821; i++) + materials[i] = Material.Grindstone; + for (int i = 21558; i <= 21559; i++) + materials[i] = Material.HangingRoots; + for (int i = 10248; i <= 10250; i++) + materials[i] = Material.HayBlock; + for (int i = 8763; i <= 8778; i++) + materials[i] = Material.HeavyWeightedPressurePlate; + materials[18817] = Material.HoneyBlock; + materials[18818] = Material.HoneycombBlock; + for (int i = 8829; i <= 8838; i++) + materials[i] = Material.Hopper; + for (int i = 12203; i <= 12204; i++) + materials[i] = Material.HornCoral; + materials[12184] = Material.HornCoralBlock; + for (int i = 12223; i <= 12224; i++) + materials[i] = Material.HornCoralFan; + for (int i = 12297; i <= 12304; i++) + materials[i] = Material.HornCoralWallFan; + materials[5614] = Material.Ice; + materials[6319] = Material.InfestedChiseledStoneBricks; + materials[6315] = Material.InfestedCobblestone; + materials[6318] = Material.InfestedCrackedStoneBricks; + for (int i = 23212; i <= 23214; i++) + materials[i] = Material.InfestedDeepslate; + materials[6317] = Material.InfestedMossyStoneBricks; + materials[6314] = Material.InfestedStone; + materials[6316] = Material.InfestedStoneBricks; + for (int i = 6512; i <= 6543; i++) + materials[i] = Material.IronBars; + materials[2040] = Material.IronBlock; + for (int i = 5488; i <= 5551; i++) + materials[i] = Material.IronDoor; + materials[114] = Material.IronOre; + for (int i = 9922; i <= 9985; i++) + materials[i] = Material.IronTrapdoor; + for (int i = 5705; i <= 5708; i++) + materials[i] = Material.JackOLantern; + for (int i = 18732; i <= 18743; i++) + materials[i] = Material.Jigsaw; + for (int i = 5649; i <= 5650; i++) + materials[i] = Material.Jukebox; + for (int i = 8451; i <= 8474; i++) + materials[i] = Material.JungleButton; + for (int i = 11403; i <= 11466; i++) + materials[i] = Material.JungleDoor; + for (int i = 11115; i <= 11146; i++) + materials[i] = Material.JungleFence; + for (int i = 10891; i <= 10922; i++) + materials[i] = Material.JungleFenceGate; + for (int i = 4998; i <= 5061; i++) + materials[i] = Material.JungleHangingSign; + for (int i = 298; i <= 325; i++) + materials[i] = Material.JungleLeaves; + for (int i = 128; i <= 130; i++) + materials[i] = Material.JungleLog; + materials[18] = Material.JunglePlanks; + for (int i = 5558; i <= 5559; i++) + materials[i] = Material.JunglePressurePlate; + for (int i = 30; i <= 31; i++) + materials[i] = Material.JungleSapling; + for (int i = 4378; i <= 4409; i++) + materials[i] = Material.JungleSign; + for (int i = 10703; i <= 10708; i++) + materials[i] = Material.JungleSlab; + for (int i = 7596; i <= 7675; i++) + materials[i] = Material.JungleStairs; + for (int i = 5988; i <= 6051; i++) + materials[i] = Material.JungleTrapdoor; + for (int i = 5414; i <= 5421; i++) + materials[i] = Material.JungleWallHangingSign; + for (int i = 4710; i <= 4717; i++) + materials[i] = Material.JungleWallSign; + for (int i = 181; i <= 183; i++) + materials[i] = Material.JungleWood; + for (int i = 12135; i <= 12160; i++) + materials[i] = Material.Kelp; + materials[12161] = Material.KelpPlant; + for (int i = 4570; i <= 4577; i++) + materials[i] = Material.Ladder; + for (int i = 17875; i <= 17878; i++) + materials[i] = Material.Lantern; + materials[471] = Material.LapisBlock; + materials[469] = Material.LapisOre; + for (int i = 20417; i <= 20428; i++) + materials[i] = Material.LargeAmethystBud; + for (int i = 10280; i <= 10281; i++) + materials[i] = Material.LargeFern; + for (int i = 93; i <= 108; i++) + materials[i] = Material.Lava; + materials[7172] = Material.LavaCauldron; + for (int i = 17822; i <= 17837; i++) + materials[i] = Material.Lectern; + for (int i = 5462; i <= 5485; i++) + materials[i] = Material.Lever; + for (int i = 9890; i <= 9921; i++) + materials[i] = Material.Light; + for (int i = 10330; i <= 10345; i++) + materials[i] = Material.LightBlueBanner; + for (int i = 1685; i <= 1700; i++) + materials[i] = Material.LightBlueBed; + for (int i = 20161; i <= 20176; i++) + materials[i] = Material.LightBlueCandle; + for (int i = 20377; i <= 20378; i++) + materials[i] = Material.LightBlueCandleCake; + materials[10254] = Material.LightBlueCarpet; + materials[12106] = Material.LightBlueConcrete; + materials[12122] = Material.LightBlueConcretePowder; + for (int i = 12051; i <= 12054; i++) + materials[i] = Material.LightBlueGlazedTerracotta; + for (int i = 11961; i <= 11966; i++) + materials[i] = Material.LightBlueShulkerBox; + materials[5783] = Material.LightBlueStainedGlass; + for (int i = 9072; i <= 9103; i++) + materials[i] = Material.LightBlueStainedGlassPane; + materials[8963] = Material.LightBlueTerracotta; + for (int i = 10550; i <= 10553; i++) + materials[i] = Material.LightBlueWallBanner; + materials[1999] = Material.LightBlueWool; + for (int i = 10410; i <= 10425; i++) + materials[i] = Material.LightGrayBanner; + for (int i = 1765; i <= 1780; i++) + materials[i] = Material.LightGrayBed; + for (int i = 20241; i <= 20256; i++) + materials[i] = Material.LightGrayCandle; + for (int i = 20387; i <= 20388; i++) + materials[i] = Material.LightGrayCandleCake; + materials[10259] = Material.LightGrayCarpet; + materials[12111] = Material.LightGrayConcrete; + materials[12127] = Material.LightGrayConcretePowder; + for (int i = 12071; i <= 12074; i++) + materials[i] = Material.LightGrayGlazedTerracotta; + for (int i = 11991; i <= 11996; i++) + materials[i] = Material.LightGrayShulkerBox; + materials[5788] = Material.LightGrayStainedGlass; + for (int i = 9232; i <= 9263; i++) + materials[i] = Material.LightGrayStainedGlassPane; + materials[8968] = Material.LightGrayTerracotta; + for (int i = 10570; i <= 10573; i++) + materials[i] = Material.LightGrayWallBanner; + materials[2004] = Material.LightGrayWool; + for (int i = 8747; i <= 8762; i++) + materials[i] = Material.LightWeightedPressurePlate; + for (int i = 21398; i <= 21421; i++) + materials[i] = Material.LightningRod; + for (int i = 10272; i <= 10273; i++) + materials[i] = Material.Lilac; + materials[2036] = Material.LilyOfTheValley; + materials[7041] = Material.LilyPad; + for (int i = 10362; i <= 10377; i++) + materials[i] = Material.LimeBanner; + for (int i = 1717; i <= 1732; i++) + materials[i] = Material.LimeBed; + for (int i = 20193; i <= 20208; i++) + materials[i] = Material.LimeCandle; + for (int i = 20381; i <= 20382; i++) + materials[i] = Material.LimeCandleCake; + materials[10256] = Material.LimeCarpet; + materials[12108] = Material.LimeConcrete; + materials[12124] = Material.LimeConcretePowder; + for (int i = 12059; i <= 12062; i++) + materials[i] = Material.LimeGlazedTerracotta; + for (int i = 11973; i <= 11978; i++) + materials[i] = Material.LimeShulkerBox; + materials[5785] = Material.LimeStainedGlass; + for (int i = 9136; i <= 9167; i++) + materials[i] = Material.LimeStainedGlassPane; + materials[8965] = Material.LimeTerracotta; + for (int i = 10558; i <= 10561; i++) + materials[i] = Material.LimeWallBanner; + materials[2001] = Material.LimeWool; + materials[18831] = Material.Lodestone; + for (int i = 17776; i <= 17779; i++) + materials[i] = Material.Loom; + for (int i = 10314; i <= 10329; i++) + materials[i] = Material.MagentaBanner; + for (int i = 1669; i <= 1684; i++) + materials[i] = Material.MagentaBed; + for (int i = 20145; i <= 20160; i++) + materials[i] = Material.MagentaCandle; + for (int i = 20375; i <= 20376; i++) + materials[i] = Material.MagentaCandleCake; + materials[10253] = Material.MagentaCarpet; + materials[12105] = Material.MagentaConcrete; + materials[12121] = Material.MagentaConcretePowder; + for (int i = 12047; i <= 12050; i++) + materials[i] = Material.MagentaGlazedTerracotta; + for (int i = 11955; i <= 11960; i++) + materials[i] = Material.MagentaShulkerBox; + materials[5782] = Material.MagentaStainedGlass; + for (int i = 9040; i <= 9071; i++) + materials[i] = Material.MagentaStainedGlassPane; + materials[8962] = Material.MagentaTerracotta; + for (int i = 10546; i <= 10549; i++) + materials[i] = Material.MagentaWallBanner; + materials[1998] = Material.MagentaWool; + materials[11918] = Material.MagmaBlock; + for (int i = 8523; i <= 8546; i++) + materials[i] = Material.MangroveButton; + for (int i = 11595; i <= 11658; i++) + materials[i] = Material.MangroveDoor; + for (int i = 11211; i <= 11242; i++) + materials[i] = Material.MangroveFence; + for (int i = 10987; i <= 11018; i++) + materials[i] = Material.MangroveFenceGate; + for (int i = 5254; i <= 5317; i++) + materials[i] = Material.MangroveHangingSign; + for (int i = 382; i <= 409; i++) + materials[i] = Material.MangroveLeaves; + for (int i = 137; i <= 139; i++) + materials[i] = Material.MangroveLog; + materials[21] = Material.MangrovePlanks; + for (int i = 5564; i <= 5565; i++) + materials[i] = Material.MangrovePressurePlate; + for (int i = 36; i <= 75; i++) + materials[i] = Material.MangrovePropagule; + for (int i = 140; i <= 141; i++) + materials[i] = Material.MangroveRoots; + for (int i = 4442; i <= 4473; i++) + materials[i] = Material.MangroveSign; + for (int i = 10721; i <= 10726; i++) + materials[i] = Material.MangroveSlab; + for (int i = 9648; i <= 9727; i++) + materials[i] = Material.MangroveStairs; + for (int i = 6180; i <= 6243; i++) + materials[i] = Material.MangroveTrapdoor; + for (int i = 5430; i <= 5437; i++) + materials[i] = Material.MangroveWallHangingSign; + for (int i = 4726; i <= 4733; i++) + materials[i] = Material.MangroveWallSign; + for (int i = 190; i <= 192; i++) + materials[i] = Material.MangroveWood; + for (int i = 20429; i <= 20440; i++) + materials[i] = Material.MediumAmethystBud; + materials[6582] = Material.Melon; + for (int i = 6599; i <= 6606; i++) + materials[i] = Material.MelonStem; + materials[21501] = Material.MossBlock; + materials[21500] = Material.MossCarpet; + materials[2301] = Material.MossyCobblestone; + for (int i = 13478; i <= 13483; i++) + materials[i] = Material.MossyCobblestoneSlab; + for (int i = 12654; i <= 12733; i++) + materials[i] = Material.MossyCobblestoneStairs; + for (int i = 8013; i <= 8336; i++) + materials[i] = Material.MossyCobblestoneWall; + for (int i = 13466; i <= 13471; i++) + materials[i] = Material.MossyStoneBrickSlab; + for (int i = 12494; i <= 12573; i++) + materials[i] = Material.MossyStoneBrickStairs; + for (int i = 14504; i <= 14827; i++) + materials[i] = Material.MossyStoneBrickWall; + materials[6309] = Material.MossyStoneBricks; + for (int i = 2012; i <= 2023; i++) + materials[i] = Material.MovingPiston; + materials[21561] = Material.Mud; + for (int i = 10787; i <= 10792; i++) + materials[i] = Material.MudBrickSlab; + for (int i = 6959; i <= 7038; i++) + materials[i] = Material.MudBrickStairs; + for (int i = 15476; i <= 15799; i++) + materials[i] = Material.MudBrickWall; + materials[6313] = Material.MudBricks; + for (int i = 142; i <= 144; i++) + materials[i] = Material.MuddyMangroveRoots; + for (int i = 6448; i <= 6511; i++) + materials[i] = Material.MushroomStem; + for (int i = 7039; i <= 7040; i++) + materials[i] = Material.Mycelium; + for (int i = 7043; i <= 7074; i++) + materials[i] = Material.NetherBrickFence; + for (int i = 10793; i <= 10798; i++) + materials[i] = Material.NetherBrickSlab; + for (int i = 7075; i <= 7154; i++) + materials[i] = Material.NetherBrickStairs; + for (int i = 15800; i <= 16123; i++) + materials[i] = Material.NetherBrickWall; + materials[7042] = Material.NetherBricks; + materials[118] = Material.NetherGoldOre; + for (int i = 5699; i <= 5700; i++) + materials[i] = Material.NetherPortal; + materials[8828] = Material.NetherQuartzOre; + materials[17967] = Material.NetherSprouts; + for (int i = 7155; i <= 7158; i++) + materials[i] = Material.NetherWart; + materials[11919] = Material.NetherWartBlock; + materials[18819] = Material.NetheriteBlock; + materials[5684] = Material.Netherrack; + for (int i = 487; i <= 1636; i++) + materials[i] = Material.NoteBlock; + for (int i = 8379; i <= 8402; i++) + materials[i] = Material.OakButton; + for (int i = 4506; i <= 4569; i++) + materials[i] = Material.OakDoor; + for (int i = 5651; i <= 5682; i++) + materials[i] = Material.OakFence; + for (int i = 6767; i <= 6798; i++) + materials[i] = Material.OakFenceGate; + for (int i = 4742; i <= 4805; i++) + materials[i] = Material.OakHangingSign; + for (int i = 214; i <= 241; i++) + materials[i] = Material.OakLeaves; + for (int i = 119; i <= 121; i++) + materials[i] = Material.OakLog; + materials[15] = Material.OakPlanks; + for (int i = 5552; i <= 5553; i++) + materials[i] = Material.OakPressurePlate; + for (int i = 24; i <= 25; i++) + materials[i] = Material.OakSapling; + for (int i = 4250; i <= 4281; i++) + materials[i] = Material.OakSign; + for (int i = 10685; i <= 10690; i++) + materials[i] = Material.OakSlab; + for (int i = 2822; i <= 2901; i++) + materials[i] = Material.OakStairs; + for (int i = 5796; i <= 5859; i++) + materials[i] = Material.OakTrapdoor; + for (int i = 5382; i <= 5389; i++) + materials[i] = Material.OakWallHangingSign; + for (int i = 4678; i <= 4685; i++) + materials[i] = Material.OakWallSign; + for (int i = 172; i <= 174; i++) + materials[i] = Material.OakWood; + for (int i = 11925; i <= 11936; i++) + materials[i] = Material.Observer; + materials[2302] = Material.Obsidian; + for (int i = 23221; i <= 23223; i++) + materials[i] = Material.OchreFroglight; + for (int i = 10298; i <= 10313; i++) + materials[i] = Material.OrangeBanner; + for (int i = 1653; i <= 1668; i++) + materials[i] = Material.OrangeBed; + for (int i = 20129; i <= 20144; i++) + materials[i] = Material.OrangeCandle; + for (int i = 20373; i <= 20374; i++) + materials[i] = Material.OrangeCandleCake; + materials[10252] = Material.OrangeCarpet; + materials[12104] = Material.OrangeConcrete; + materials[12120] = Material.OrangeConcretePowder; + for (int i = 12043; i <= 12046; i++) + materials[i] = Material.OrangeGlazedTerracotta; + for (int i = 11949; i <= 11954; i++) + materials[i] = Material.OrangeShulkerBox; + materials[5781] = Material.OrangeStainedGlass; + for (int i = 9008; i <= 9039; i++) + materials[i] = Material.OrangeStainedGlassPane; + materials[8961] = Material.OrangeTerracotta; + materials[2030] = Material.OrangeTulip; + for (int i = 10542; i <= 10545; i++) + materials[i] = Material.OrangeWallBanner; + materials[1997] = Material.OrangeWool; + materials[2033] = Material.OxeyeDaisy; + materials[20692] = Material.OxidizedCopper; + materials[20698] = Material.OxidizedCutCopper; + for (int i = 21022; i <= 21027; i++) + materials[i] = Material.OxidizedCutCopperSlab; + for (int i = 20702; i <= 20781; i++) + materials[i] = Material.OxidizedCutCopperStairs; + materials[10269] = Material.PackedIce; + materials[6312] = Material.PackedMud; + for (int i = 23227; i <= 23229; i++) + materials[i] = Material.PearlescentFroglight; + for (int i = 10276; i <= 10277; i++) + materials[i] = Material.Peony; + for (int i = 10763; i <= 10768; i++) + materials[i] = Material.PetrifiedOakSlab; + for (int i = 8691; i <= 8706; i++) + materials[i] = Material.PiglinHead; + for (int i = 8707; i <= 8710; i++) + materials[i] = Material.PiglinWallHead; + for (int i = 10378; i <= 10393; i++) + materials[i] = Material.PinkBanner; + for (int i = 1733; i <= 1748; i++) + materials[i] = Material.PinkBed; + for (int i = 20209; i <= 20224; i++) + materials[i] = Material.PinkCandle; + for (int i = 20383; i <= 20384; i++) + materials[i] = Material.PinkCandleCake; + materials[10257] = Material.PinkCarpet; + materials[12109] = Material.PinkConcrete; + materials[12125] = Material.PinkConcretePowder; + for (int i = 12063; i <= 12066; i++) + materials[i] = Material.PinkGlazedTerracotta; + for (int i = 11979; i <= 11984; i++) + materials[i] = Material.PinkShulkerBox; + materials[5786] = Material.PinkStainedGlass; + for (int i = 9168; i <= 9199; i++) + materials[i] = Material.PinkStainedGlassPane; + materials[8966] = Material.PinkTerracotta; + materials[2032] = Material.PinkTulip; + for (int i = 10562; i <= 10565; i++) + materials[i] = Material.PinkWallBanner; + materials[2002] = Material.PinkWool; + for (int i = 1960; i <= 1971; i++) + materials[i] = Material.Piston; + for (int i = 1972; i <= 1995; i++) + materials[i] = Material.PistonHead; + for (int i = 8631; i <= 8646; i++) + materials[i] = Material.PlayerHead; + for (int i = 8647; i <= 8650; i++) + materials[i] = Material.PlayerWallHead; + for (int i = 12; i <= 13; i++) + materials[i] = Material.Podzol; + for (int i = 21422; i <= 21441; i++) + materials[i] = Material.PointedDripstone; + materials[7] = Material.PolishedAndesite; + for (int i = 13520; i <= 13525; i++) + materials[i] = Material.PolishedAndesiteSlab; + for (int i = 13294; i <= 13373; i++) + materials[i] = Material.PolishedAndesiteStairs; + for (int i = 5690; i <= 5692; i++) + materials[i] = Material.PolishedBasalt; + materials[19243] = Material.PolishedBlackstone; + for (int i = 19247; i <= 19252; i++) + materials[i] = Material.PolishedBlackstoneBrickSlab; + for (int i = 19253; i <= 19332; i++) + materials[i] = Material.PolishedBlackstoneBrickStairs; + for (int i = 19333; i <= 19656; i++) + materials[i] = Material.PolishedBlackstoneBrickWall; + materials[19244] = Material.PolishedBlackstoneBricks; + for (int i = 19746; i <= 19769; i++) + materials[i] = Material.PolishedBlackstoneButton; + for (int i = 19744; i <= 19745; i++) + materials[i] = Material.PolishedBlackstonePressurePlate; + for (int i = 19738; i <= 19743; i++) + materials[i] = Material.PolishedBlackstoneSlab; + for (int i = 19658; i <= 19737; i++) + materials[i] = Material.PolishedBlackstoneStairs; + for (int i = 19770; i <= 20093; i++) + materials[i] = Material.PolishedBlackstoneWall; + materials[21976] = Material.PolishedDeepslate; + for (int i = 22057; i <= 22062; i++) + materials[i] = Material.PolishedDeepslateSlab; + for (int i = 21977; i <= 22056; i++) + materials[i] = Material.PolishedDeepslateStairs; + for (int i = 22063; i <= 22386; i++) + materials[i] = Material.PolishedDeepslateWall; + materials[5] = Material.PolishedDiorite; + for (int i = 13472; i <= 13477; i++) + materials[i] = Material.PolishedDioriteSlab; + for (int i = 12574; i <= 12653; i++) + materials[i] = Material.PolishedDioriteStairs; + materials[3] = Material.PolishedGranite; + for (int i = 13454; i <= 13459; i++) + materials[i] = Material.PolishedGraniteSlab; + for (int i = 12334; i <= 12413; i++) + materials[i] = Material.PolishedGraniteStairs; + materials[2025] = Material.Poppy; + for (int i = 8371; i <= 8378; i++) + materials[i] = Material.Potatoes; + materials[8342] = Material.PottedAcaciaSapling; + materials[8349] = Material.PottedAllium; + materials[23219] = Material.PottedAzaleaBush; + materials[8350] = Material.PottedAzureBluet; + materials[12329] = Material.PottedBamboo; + materials[8340] = Material.PottedBirchSapling; + materials[8348] = Material.PottedBlueOrchid; + materials[8360] = Material.PottedBrownMushroom; + materials[8362] = Material.PottedCactus; + materials[8356] = Material.PottedCornflower; + materials[18827] = Material.PottedCrimsonFungus; + materials[18829] = Material.PottedCrimsonRoots; + materials[8346] = Material.PottedDandelion; + materials[8343] = Material.PottedDarkOakSapling; + materials[8361] = Material.PottedDeadBush; + materials[8345] = Material.PottedFern; + materials[23220] = Material.PottedFloweringAzaleaBush; + materials[8341] = Material.PottedJungleSapling; + materials[8357] = Material.PottedLilyOfTheValley; + materials[8344] = Material.PottedMangrovePropagule; + materials[8338] = Material.PottedOakSapling; + materials[8352] = Material.PottedOrangeTulip; + materials[8355] = Material.PottedOxeyeDaisy; + materials[8354] = Material.PottedPinkTulip; + materials[8347] = Material.PottedPoppy; + materials[8359] = Material.PottedRedMushroom; + materials[8351] = Material.PottedRedTulip; + materials[8339] = Material.PottedSpruceSapling; + materials[18828] = Material.PottedWarpedFungus; + materials[18830] = Material.PottedWarpedRoots; + materials[8353] = Material.PottedWhiteTulip; + materials[8358] = Material.PottedWitherRose; + materials[20456] = Material.PowderSnow; + for (int i = 7173; i <= 7175; i++) + materials[i] = Material.PowderSnowCauldron; + for (int i = 1893; i <= 1916; i++) + materials[i] = Material.PoweredRail; + materials[9986] = Material.Prismarine; + for (int i = 10235; i <= 10240; i++) + materials[i] = Material.PrismarineBrickSlab; + for (int i = 10069; i <= 10148; i++) + materials[i] = Material.PrismarineBrickStairs; + materials[9987] = Material.PrismarineBricks; + for (int i = 10229; i <= 10234; i++) + materials[i] = Material.PrismarineSlab; + for (int i = 9989; i <= 10068; i++) + materials[i] = Material.PrismarineStairs; + for (int i = 13856; i <= 14179; i++) + materials[i] = Material.PrismarineWall; + materials[5683] = Material.Pumpkin; + for (int i = 6591; i <= 6598; i++) + materials[i] = Material.PumpkinStem; + for (int i = 10442; i <= 10457; i++) + materials[i] = Material.PurpleBanner; + for (int i = 1797; i <= 1812; i++) + materials[i] = Material.PurpleBed; + for (int i = 20273; i <= 20288; i++) + materials[i] = Material.PurpleCandle; + for (int i = 20391; i <= 20392; i++) + materials[i] = Material.PurpleCandleCake; + materials[10261] = Material.PurpleCarpet; + materials[12113] = Material.PurpleConcrete; + materials[12129] = Material.PurpleConcretePowder; + for (int i = 12079; i <= 12082; i++) + materials[i] = Material.PurpleGlazedTerracotta; + for (int i = 12003; i <= 12008; i++) + materials[i] = Material.PurpleShulkerBox; + materials[5790] = Material.PurpleStainedGlass; + for (int i = 9296; i <= 9327; i++) + materials[i] = Material.PurpleStainedGlassPane; + materials[8970] = Material.PurpleTerracotta; + for (int i = 10578; i <= 10581; i++) + materials[i] = Material.PurpleWallBanner; + materials[2006] = Material.PurpleWool; + materials[11799] = Material.PurpurBlock; + for (int i = 11800; i <= 11802; i++) + materials[i] = Material.PurpurPillar; + for (int i = 10817; i <= 10822; i++) + materials[i] = Material.PurpurSlab; + for (int i = 11803; i <= 11882; i++) + materials[i] = Material.PurpurStairs; + materials[8839] = Material.QuartzBlock; + materials[20096] = Material.QuartzBricks; + for (int i = 8841; i <= 8843; i++) + materials[i] = Material.QuartzPillar; + for (int i = 10799; i <= 10804; i++) + materials[i] = Material.QuartzSlab; + for (int i = 8844; i <= 8923; i++) + materials[i] = Material.QuartzStairs; + for (int i = 4578; i <= 4597; i++) + materials[i] = Material.Rail; + materials[23217] = Material.RawCopperBlock; + materials[23218] = Material.RawGoldBlock; + materials[23216] = Material.RawIronBlock; + for (int i = 10506; i <= 10521; i++) + materials[i] = Material.RedBanner; + for (int i = 1861; i <= 1876; i++) + materials[i] = Material.RedBed; + for (int i = 20337; i <= 20352; i++) + materials[i] = Material.RedCandle; + for (int i = 20399; i <= 20400; i++) + materials[i] = Material.RedCandleCake; + materials[10265] = Material.RedCarpet; + materials[12117] = Material.RedConcrete; + materials[12133] = Material.RedConcretePowder; + for (int i = 12095; i <= 12098; i++) + materials[i] = Material.RedGlazedTerracotta; + materials[2038] = Material.RedMushroom; + for (int i = 6384; i <= 6447; i++) + materials[i] = Material.RedMushroomBlock; + for (int i = 13514; i <= 13519; i++) + materials[i] = Material.RedNetherBrickSlab; + for (int i = 13214; i <= 13293; i++) + materials[i] = Material.RedNetherBrickStairs; + for (int i = 16448; i <= 16771; i++) + materials[i] = Material.RedNetherBrickWall; + materials[11920] = Material.RedNetherBricks; + materials[110] = Material.RedSand; + materials[10602] = Material.RedSandstone; + for (int i = 10805; i <= 10810; i++) + materials[i] = Material.RedSandstoneSlab; + for (int i = 10605; i <= 10684; i++) + materials[i] = Material.RedSandstoneStairs; + for (int i = 14180; i <= 14503; i++) + materials[i] = Material.RedSandstoneWall; + for (int i = 12027; i <= 12032; i++) + materials[i] = Material.RedShulkerBox; + materials[5794] = Material.RedStainedGlass; + for (int i = 9424; i <= 9455; i++) + materials[i] = Material.RedStainedGlassPane; + materials[8974] = Material.RedTerracotta; + materials[2029] = Material.RedTulip; + for (int i = 10594; i <= 10597; i++) + materials[i] = Material.RedWallBanner; + materials[2010] = Material.RedWool; + materials[8827] = Material.RedstoneBlock; + for (int i = 7187; i <= 7188; i++) + materials[i] = Material.RedstoneLamp; + for (int i = 5568; i <= 5569; i++) + materials[i] = Material.RedstoneOre; + for (int i = 5572; i <= 5573; i++) + materials[i] = Material.RedstoneTorch; + for (int i = 5574; i <= 5581; i++) + materials[i] = Material.RedstoneWallTorch; + for (int i = 2926; i <= 4221; i++) + materials[i] = Material.RedstoneWire; + materials[23231] = Material.ReinforcedDeepslate; + for (int i = 5716; i <= 5779; i++) + materials[i] = Material.Repeater; + for (int i = 11890; i <= 11901; i++) + materials[i] = Material.RepeatingCommandBlock; + for (int i = 18822; i <= 18826; i++) + materials[i] = Material.RespawnAnchor; + materials[21560] = Material.RootedDirt; + for (int i = 10274; i <= 10275; i++) + materials[i] = Material.RoseBush; + materials[109] = Material.Sand; + materials[484] = Material.Sandstone; + for (int i = 10751; i <= 10756; i++) + materials[i] = Material.SandstoneSlab; + for (int i = 7201; i <= 7280; i++) + materials[i] = Material.SandstoneStairs; + for (int i = 16772; i <= 17095; i++) + materials[i] = Material.SandstoneWall; + for (int i = 17744; i <= 17775; i++) + materials[i] = Material.Scaffolding; + materials[20553] = Material.Sculk; + for (int i = 20682; i <= 20683; i++) + materials[i] = Material.SculkCatalyst; + for (int i = 20457; i <= 20552; i++) + materials[i] = Material.SculkSensor; + for (int i = 20684; i <= 20691; i++) + materials[i] = Material.SculkShrieker; + for (int i = 20554; i <= 20681; i++) + materials[i] = Material.SculkVein; + materials[10247] = Material.SeaLantern; + for (int i = 12305; i <= 12312; i++) + materials[i] = Material.SeaPickle; + materials[1957] = Material.Seagrass; + materials[17982] = Material.Shroomlight; + for (int i = 11937; i <= 11942; i++) + materials[i] = Material.ShulkerBox; + for (int i = 8571; i <= 8586; i++) + materials[i] = Material.SkeletonSkull; + for (int i = 8587; i <= 8590; i++) + materials[i] = Material.SkeletonWallSkull; + materials[9888] = Material.SlimeBlock; + for (int i = 20441; i <= 20452; i++) + materials[i] = Material.SmallAmethystBud; + for (int i = 21542; i <= 21557; i++) + materials[i] = Material.SmallDripleaf; + materials[17838] = Material.SmithingTable; + for (int i = 17792; i <= 17799; i++) + materials[i] = Material.Smoker; + materials[23215] = Material.SmoothBasalt; + materials[10825] = Material.SmoothQuartz; + for (int i = 13496; i <= 13501; i++) + materials[i] = Material.SmoothQuartzSlab; + for (int i = 12974; i <= 13053; i++) + materials[i] = Material.SmoothQuartzStairs; + materials[10826] = Material.SmoothRedSandstone; + for (int i = 13460; i <= 13465; i++) + materials[i] = Material.SmoothRedSandstoneSlab; + for (int i = 12414; i <= 12493; i++) + materials[i] = Material.SmoothRedSandstoneStairs; + materials[10824] = Material.SmoothSandstone; + for (int i = 13490; i <= 13495; i++) + materials[i] = Material.SmoothSandstoneSlab; + for (int i = 12894; i <= 12973; i++) + materials[i] = Material.SmoothSandstoneStairs; + materials[10823] = Material.SmoothStone; + for (int i = 10745; i <= 10750; i++) + materials[i] = Material.SmoothStoneSlab; + for (int i = 5606; i <= 5613; i++) + materials[i] = Material.Snow; + materials[5615] = Material.SnowBlock; + for (int i = 17915; i <= 17946; i++) + materials[i] = Material.SoulCampfire; + materials[2820] = Material.SoulFire; + for (int i = 17879; i <= 17882; i++) + materials[i] = Material.SoulLantern; + materials[5685] = Material.SoulSand; + materials[5686] = Material.SoulSoil; + materials[5693] = Material.SoulTorch; + for (int i = 5694; i <= 5697; i++) + materials[i] = Material.SoulWallTorch; + materials[2821] = Material.Spawner; + materials[466] = Material.Sponge; + materials[21497] = Material.SporeBlossom; + for (int i = 8403; i <= 8426; i++) + materials[i] = Material.SpruceButton; + for (int i = 11275; i <= 11338; i++) + materials[i] = Material.SpruceDoor; + for (int i = 11051; i <= 11082; i++) + materials[i] = Material.SpruceFence; + for (int i = 10827; i <= 10858; i++) + materials[i] = Material.SpruceFenceGate; + for (int i = 4806; i <= 4869; i++) + materials[i] = Material.SpruceHangingSign; + for (int i = 242; i <= 269; i++) + materials[i] = Material.SpruceLeaves; + for (int i = 122; i <= 124; i++) + materials[i] = Material.SpruceLog; + materials[16] = Material.SprucePlanks; + for (int i = 5554; i <= 5555; i++) + materials[i] = Material.SprucePressurePlate; + for (int i = 26; i <= 27; i++) + materials[i] = Material.SpruceSapling; + for (int i = 4282; i <= 4313; i++) + materials[i] = Material.SpruceSign; + for (int i = 10691; i <= 10696; i++) + materials[i] = Material.SpruceSlab; + for (int i = 7436; i <= 7515; i++) + materials[i] = Material.SpruceStairs; + for (int i = 5860; i <= 5923; i++) + materials[i] = Material.SpruceTrapdoor; + for (int i = 5390; i <= 5397; i++) + materials[i] = Material.SpruceWallHangingSign; + for (int i = 4686; i <= 4693; i++) + materials[i] = Material.SpruceWallSign; + for (int i = 175; i <= 177; i++) + materials[i] = Material.SpruceWood; + for (int i = 1941; i <= 1952; i++) + materials[i] = Material.StickyPiston; + materials[1] = Material.Stone; + for (int i = 10781; i <= 10786; i++) + materials[i] = Material.StoneBrickSlab; + for (int i = 6879; i <= 6958; i++) + materials[i] = Material.StoneBrickStairs; + for (int i = 15152; i <= 15475; i++) + materials[i] = Material.StoneBrickWall; + materials[6308] = Material.StoneBricks; + for (int i = 5582; i <= 5605; i++) + materials[i] = Material.StoneButton; + for (int i = 5486; i <= 5487; i++) + materials[i] = Material.StonePressurePlate; + for (int i = 10739; i <= 10744; i++) + materials[i] = Material.StoneSlab; + for (int i = 12814; i <= 12893; i++) + materials[i] = Material.StoneStairs; + for (int i = 17839; i <= 17842; i++) + materials[i] = Material.Stonecutter; + for (int i = 157; i <= 159; i++) + materials[i] = Material.StrippedAcaciaLog; + for (int i = 205; i <= 207; i++) + materials[i] = Material.StrippedAcaciaWood; + for (int i = 169; i <= 171; i++) + materials[i] = Material.StrippedBambooBlock; + for (int i = 151; i <= 153; i++) + materials[i] = Material.StrippedBirchLog; + for (int i = 199; i <= 201; i++) + materials[i] = Material.StrippedBirchWood; + for (int i = 17977; i <= 17979; i++) + materials[i] = Material.StrippedCrimsonHyphae; + for (int i = 17971; i <= 17973; i++) + materials[i] = Material.StrippedCrimsonStem; + for (int i = 160; i <= 162; i++) + materials[i] = Material.StrippedDarkOakLog; + for (int i = 208; i <= 210; i++) + materials[i] = Material.StrippedDarkOakWood; + for (int i = 154; i <= 156; i++) + materials[i] = Material.StrippedJungleLog; + for (int i = 202; i <= 204; i++) + materials[i] = Material.StrippedJungleWood; + for (int i = 166; i <= 168; i++) + materials[i] = Material.StrippedMangroveLog; + for (int i = 211; i <= 213; i++) + materials[i] = Material.StrippedMangroveWood; + for (int i = 163; i <= 165; i++) + materials[i] = Material.StrippedOakLog; + for (int i = 193; i <= 195; i++) + materials[i] = Material.StrippedOakWood; + for (int i = 148; i <= 150; i++) + materials[i] = Material.StrippedSpruceLog; + for (int i = 196; i <= 198; i++) + materials[i] = Material.StrippedSpruceWood; + for (int i = 17960; i <= 17962; i++) + materials[i] = Material.StrippedWarpedHyphae; + for (int i = 17954; i <= 17956; i++) + materials[i] = Material.StrippedWarpedStem; + for (int i = 18728; i <= 18731; i++) + materials[i] = Material.StructureBlock; + materials[11924] = Material.StructureVoid; + for (int i = 5633; i <= 5648; i++) + materials[i] = Material.SugarCane; + for (int i = 10270; i <= 10271; i++) + materials[i] = Material.Sunflower; + for (int i = 17947; i <= 17950; i++) + materials[i] = Material.SweetBerryBush; + for (int i = 10278; i <= 10279; i++) + materials[i] = Material.TallGrass; + for (int i = 1958; i <= 1959; i++) + materials[i] = Material.TallSeagrass; + for (int i = 18753; i <= 18768; i++) + materials[i] = Material.Target; + materials[10267] = Material.Terracotta; + materials[20455] = Material.TintedGlass; + for (int i = 2042; i <= 2043; i++) + materials[i] = Material.Tnt; + materials[2303] = Material.Torch; + for (int i = 8723; i <= 8746; i++) + materials[i] = Material.TrappedChest; + for (int i = 7307; i <= 7434; i++) + materials[i] = Material.Tripwire; + for (int i = 7291; i <= 7306; i++) + materials[i] = Material.TripwireHook; + for (int i = 12195; i <= 12196; i++) + materials[i] = Material.TubeCoral; + materials[12180] = Material.TubeCoralBlock; + for (int i = 12215; i <= 12216; i++) + materials[i] = Material.TubeCoralFan; + for (int i = 12265; i <= 12272; i++) + materials[i] = Material.TubeCoralWallFan; + materials[20453] = Material.Tuff; + for (int i = 12163; i <= 12174; i++) + materials[i] = Material.TurtleEgg; + for (int i = 18010; i <= 18035; i++) + materials[i] = Material.TwistingVines; + materials[18036] = Material.TwistingVinesPlant; + for (int i = 23224; i <= 23226; i++) + materials[i] = Material.VerdantFroglight; + for (int i = 6607; i <= 6638; i++) + materials[i] = Material.Vine; + materials[12330] = Material.VoidAir; + for (int i = 2304; i <= 2307; i++) + materials[i] = Material.WallTorch; + for (int i = 18496; i <= 18519; i++) + materials[i] = Material.WarpedButton; + for (int i = 18584; i <= 18647; i++) + materials[i] = Material.WarpedDoor; + for (int i = 18088; i <= 18119; i++) + materials[i] = Material.WarpedFence; + for (int i = 18280; i <= 18311; i++) + materials[i] = Material.WarpedFenceGate; + materials[17964] = Material.WarpedFungus; + for (int i = 5190; i <= 5253; i++) + materials[i] = Material.WarpedHangingSign; + for (int i = 17957; i <= 17959; i++) + materials[i] = Material.WarpedHyphae; + materials[17963] = Material.WarpedNylium; + materials[18039] = Material.WarpedPlanks; + for (int i = 18054; i <= 18055; i++) + materials[i] = Material.WarpedPressurePlate; + materials[17966] = Material.WarpedRoots; + for (int i = 18680; i <= 18711; i++) + materials[i] = Material.WarpedSign; + for (int i = 18046; i <= 18051; i++) + materials[i] = Material.WarpedSlab; + for (int i = 18392; i <= 18471; i++) + materials[i] = Material.WarpedStairs; + for (int i = 17951; i <= 17953; i++) + materials[i] = Material.WarpedStem; + for (int i = 18184; i <= 18247; i++) + materials[i] = Material.WarpedTrapdoor; + for (int i = 5446; i <= 5453; i++) + materials[i] = Material.WarpedWallHangingSign; + for (int i = 18720; i <= 18727; i++) + materials[i] = Material.WarpedWallSign; + materials[17965] = Material.WarpedWartBlock; + for (int i = 77; i <= 92; i++) + materials[i] = Material.Water; + for (int i = 7169; i <= 7171; i++) + materials[i] = Material.WaterCauldron; + materials[21046] = Material.WaxedCopperBlock; + materials[21053] = Material.WaxedCutCopper; + for (int i = 21392; i <= 21397; i++) + materials[i] = Material.WaxedCutCopperSlab; + for (int i = 21294; i <= 21373; i++) + materials[i] = Material.WaxedCutCopperStairs; + materials[21048] = Material.WaxedExposedCopper; + materials[21052] = Material.WaxedExposedCutCopper; + for (int i = 21386; i <= 21391; i++) + materials[i] = Material.WaxedExposedCutCopperSlab; + for (int i = 21214; i <= 21293; i++) + materials[i] = Material.WaxedExposedCutCopperStairs; + materials[21049] = Material.WaxedOxidizedCopper; + materials[21050] = Material.WaxedOxidizedCutCopper; + for (int i = 21374; i <= 21379; i++) + materials[i] = Material.WaxedOxidizedCutCopperSlab; + for (int i = 21054; i <= 21133; i++) + materials[i] = Material.WaxedOxidizedCutCopperStairs; + materials[21047] = Material.WaxedWeatheredCopper; + materials[21051] = Material.WaxedWeatheredCutCopper; + for (int i = 21380; i <= 21385; i++) + materials[i] = Material.WaxedWeatheredCutCopperSlab; + for (int i = 21134; i <= 21213; i++) + materials[i] = Material.WaxedWeatheredCutCopperStairs; + materials[20693] = Material.WeatheredCopper; + materials[20699] = Material.WeatheredCutCopper; + for (int i = 21028; i <= 21033; i++) + materials[i] = Material.WeatheredCutCopperSlab; + for (int i = 20782; i <= 20861; i++) + materials[i] = Material.WeatheredCutCopperStairs; + for (int i = 17983; i <= 18008; i++) + materials[i] = Material.WeepingVines; + materials[18009] = Material.WeepingVinesPlant; + materials[467] = Material.WetSponge; + for (int i = 4226; i <= 4233; i++) + materials[i] = Material.Wheat; + for (int i = 10282; i <= 10297; i++) + materials[i] = Material.WhiteBanner; + for (int i = 1637; i <= 1652; i++) + materials[i] = Material.WhiteBed; + for (int i = 20113; i <= 20128; i++) + materials[i] = Material.WhiteCandle; + for (int i = 20371; i <= 20372; i++) + materials[i] = Material.WhiteCandleCake; + materials[10251] = Material.WhiteCarpet; + materials[12103] = Material.WhiteConcrete; + materials[12119] = Material.WhiteConcretePowder; + for (int i = 12039; i <= 12042; i++) + materials[i] = Material.WhiteGlazedTerracotta; + for (int i = 11943; i <= 11948; i++) + materials[i] = Material.WhiteShulkerBox; + materials[5780] = Material.WhiteStainedGlass; + for (int i = 8976; i <= 9007; i++) + materials[i] = Material.WhiteStainedGlassPane; + materials[8960] = Material.WhiteTerracotta; + materials[2031] = Material.WhiteTulip; + for (int i = 10538; i <= 10541; i++) + materials[i] = Material.WhiteWallBanner; + materials[1996] = Material.WhiteWool; + materials[2035] = Material.WitherRose; + for (int i = 8591; i <= 8606; i++) + materials[i] = Material.WitherSkeletonSkull; + for (int i = 8607; i <= 8610; i++) + materials[i] = Material.WitherSkeletonWallSkull; + for (int i = 10346; i <= 10361; i++) + materials[i] = Material.YellowBanner; + for (int i = 1701; i <= 1716; i++) + materials[i] = Material.YellowBed; + for (int i = 20177; i <= 20192; i++) + materials[i] = Material.YellowCandle; + for (int i = 20379; i <= 20380; i++) + materials[i] = Material.YellowCandleCake; + materials[10255] = Material.YellowCarpet; + materials[12107] = Material.YellowConcrete; + materials[12123] = Material.YellowConcretePowder; + for (int i = 12055; i <= 12058; i++) + materials[i] = Material.YellowGlazedTerracotta; + for (int i = 11967; i <= 11972; i++) + materials[i] = Material.YellowShulkerBox; + materials[5784] = Material.YellowStainedGlass; + for (int i = 9104; i <= 9135; i++) + materials[i] = Material.YellowStainedGlassPane; + materials[8964] = Material.YellowTerracotta; + for (int i = 10554; i <= 10557; i++) + materials[i] = Material.YellowWallBanner; + materials[2000] = Material.YellowWool; + for (int i = 8611; i <= 8626; i++) + materials[i] = Material.ZombieHead; + for (int i = 8627; i <= 8630; i++) + materials[i] = Material.ZombieWallHead; + } + + protected override Dictionary GetDict() + { + return materials; + } + } +} diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1193.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1193.cs new file mode 100644 index 00000000..d02e09a7 --- /dev/null +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1193.cs @@ -0,0 +1,137 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Mapping.EntityPalettes +{ + public class EntityPalette1193 : EntityPalette + { + private static readonly Dictionary mappings = new(); + + static EntityPalette1193() + { + mappings[0] = EntityType.Allay; + mappings[1] = EntityType.AreaEffectCloud; + mappings[2] = EntityType.ArmorStand; + mappings[3] = EntityType.Arrow; + mappings[4] = EntityType.Axolotl; + mappings[5] = EntityType.Bat; + mappings[6] = EntityType.Bee; + mappings[7] = EntityType.Blaze; + mappings[8] = EntityType.Boat; + mappings[11] = EntityType.Camel; + mappings[10] = EntityType.Cat; + mappings[12] = EntityType.CaveSpider; + mappings[9] = EntityType.ChestBoat; + mappings[55] = EntityType.ChestMinecart; + mappings[13] = EntityType.Chicken; + mappings[14] = EntityType.Cod; + mappings[56] = EntityType.CommandBlockMinecart; + mappings[15] = EntityType.Cow; + mappings[16] = EntityType.Creeper; + mappings[17] = EntityType.Dolphin; + mappings[18] = EntityType.Donkey; + mappings[19] = EntityType.DragonFireball; + mappings[20] = EntityType.Drowned; + mappings[94] = EntityType.Egg; + mappings[21] = EntityType.ElderGuardian; + mappings[22] = EntityType.EndCrystal; + mappings[23] = EntityType.EnderDragon; + mappings[95] = EntityType.EnderPearl; + mappings[24] = EntityType.Enderman; + mappings[25] = EntityType.Endermite; + mappings[26] = EntityType.Evoker; + mappings[27] = EntityType.EvokerFangs; + mappings[96] = EntityType.ExperienceBottle; + mappings[28] = EntityType.ExperienceOrb; + mappings[29] = EntityType.EyeOfEnder; + mappings[30] = EntityType.FallingBlock; + mappings[47] = EntityType.Fireball; + mappings[31] = EntityType.FireworkRocket; + mappings[118] = EntityType.FishingBobber; + mappings[32] = EntityType.Fox; + mappings[33] = EntityType.Frog; + mappings[57] = EntityType.FurnaceMinecart; + mappings[34] = EntityType.Ghast; + mappings[35] = EntityType.Giant; + mappings[36] = EntityType.GlowItemFrame; + mappings[37] = EntityType.GlowSquid; + mappings[38] = EntityType.Goat; + mappings[39] = EntityType.Guardian; + mappings[40] = EntityType.Hoglin; + mappings[58] = EntityType.HopperMinecart; + mappings[41] = EntityType.Horse; + mappings[42] = EntityType.Husk; + mappings[43] = EntityType.Illusioner; + mappings[44] = EntityType.IronGolem; + mappings[45] = EntityType.Item; + mappings[46] = EntityType.ItemFrame; + mappings[48] = EntityType.LeashKnot; + mappings[49] = EntityType.LightningBolt; + mappings[50] = EntityType.Llama; + mappings[51] = EntityType.LlamaSpit; + mappings[52] = EntityType.MagmaCube; + mappings[53] = EntityType.Marker; + mappings[54] = EntityType.Minecart; + mappings[62] = EntityType.Mooshroom; + mappings[61] = EntityType.Mule; + mappings[63] = EntityType.Ocelot; + mappings[64] = EntityType.Painting; + mappings[65] = EntityType.Panda; + mappings[66] = EntityType.Parrot; + mappings[67] = EntityType.Phantom; + mappings[68] = EntityType.Pig; + mappings[69] = EntityType.Piglin; + mappings[70] = EntityType.PiglinBrute; + mappings[71] = EntityType.Pillager; + mappings[117] = EntityType.Player; + mappings[72] = EntityType.PolarBear; + mappings[97] = EntityType.Potion; + mappings[74] = EntityType.Pufferfish; + mappings[75] = EntityType.Rabbit; + mappings[76] = EntityType.Ravager; + mappings[77] = EntityType.Salmon; + mappings[78] = EntityType.Sheep; + mappings[79] = EntityType.Shulker; + mappings[80] = EntityType.ShulkerBullet; + mappings[81] = EntityType.Silverfish; + mappings[82] = EntityType.Skeleton; + mappings[83] = EntityType.SkeletonHorse; + mappings[84] = EntityType.Slime; + mappings[85] = EntityType.SmallFireball; + mappings[86] = EntityType.SnowGolem; + mappings[87] = EntityType.Snowball; + mappings[59] = EntityType.SpawnerMinecart; + mappings[88] = EntityType.SpectralArrow; + mappings[89] = EntityType.Spider; + mappings[90] = EntityType.Squid; + mappings[91] = EntityType.Stray; + mappings[92] = EntityType.Strider; + mappings[93] = EntityType.Tadpole; + mappings[73] = EntityType.Tnt; + mappings[60] = EntityType.TntMinecart; + mappings[99] = EntityType.TraderLlama; + mappings[98] = EntityType.Trident; + mappings[100] = EntityType.TropicalFish; + mappings[101] = EntityType.Turtle; + mappings[102] = EntityType.Vex; + mappings[103] = EntityType.Villager; + mappings[104] = EntityType.Vindicator; + mappings[105] = EntityType.WanderingTrader; + mappings[106] = EntityType.Warden; + mappings[107] = EntityType.Witch; + mappings[108] = EntityType.Wither; + mappings[109] = EntityType.WitherSkeleton; + mappings[110] = EntityType.WitherSkull; + mappings[111] = EntityType.Wolf; + mappings[112] = EntityType.Zoglin; + mappings[113] = EntityType.Zombie; + mappings[114] = EntityType.ZombieHorse; + mappings[115] = EntityType.ZombieVillager; + mappings[116] = EntityType.ZombifiedPiglin; + } + + protected override Dictionary GetDict() + { + return mappings; + } + } +} diff --git a/MinecraftClient/Mapping/EntityType.cs b/MinecraftClient/Mapping/EntityType.cs index ddb8a6b2..4351de3c 100644 --- a/MinecraftClient/Mapping/EntityType.cs +++ b/MinecraftClient/Mapping/EntityType.cs @@ -23,6 +23,7 @@ Bee, Blaze, Boat, + Camel, Cat, CaveSpider, ChestBoat, diff --git a/MinecraftClient/Mapping/Material.cs b/MinecraftClient/Mapping/Material.cs index 507dcc56..f0023009 100644 --- a/MinecraftClient/Mapping/Material.cs +++ b/MinecraftClient/Mapping/Material.cs @@ -18,6 +18,7 @@ AcaciaDoor, AcaciaFence, AcaciaFenceGate, + AcaciaHangingSign, AcaciaLeaves, AcaciaLog, AcaciaPlanks, @@ -27,6 +28,7 @@ AcaciaSlab, AcaciaStairs, AcaciaTrapdoor, + AcaciaWallHangingSign, AcaciaWallSign, AcaciaWood, ActivatorRail, @@ -46,7 +48,24 @@ AzaleaLeaves, AzureBluet, Bamboo, + BambooBlock, + BambooButton, + BambooDoor, + BambooFence, + BambooFenceGate, + BambooHangingSign, + BambooMosaic, + BambooMosaicSlab, + BambooMosaicStairs, + BambooPlanks, + BambooPressurePlate, BambooSapling, + BambooSign, + BambooSlab, + BambooStairs, + BambooTrapdoor, + BambooWallHangingSign, + BambooWallSign, Barrel, Barrier, Basalt, @@ -62,6 +81,7 @@ BirchDoor, BirchFence, BirchFenceGate, + BirchHangingSign, BirchLeaves, BirchLog, BirchPlanks, @@ -71,6 +91,7 @@ BirchSlab, BirchStairs, BirchTrapdoor, + BirchWallHangingSign, BirchWallSign, BirchWood, BlackBanner, @@ -158,6 +179,7 @@ ChainCommandBlock, Chest, ChippedAnvil, + ChiseledBookshelf, ChiseledDeepslate, ChiseledNetherBricks, ChiseledPolishedBlackstone, @@ -201,6 +223,7 @@ CrimsonFence, CrimsonFenceGate, CrimsonFungus, + CrimsonHangingSign, CrimsonHyphae, CrimsonNylium, CrimsonPlanks, @@ -211,6 +234,7 @@ CrimsonStairs, CrimsonStem, CrimsonTrapdoor, + CrimsonWallHangingSign, CrimsonWallSign, CryingObsidian, CutCopper, @@ -240,6 +264,7 @@ DarkOakDoor, DarkOakFence, DarkOakFenceGate, + DarkOakHangingSign, DarkOakLeaves, DarkOakLog, DarkOakPlanks, @@ -249,6 +274,7 @@ DarkOakSlab, DarkOakStairs, DarkOakTrapdoor, + DarkOakWallHangingSign, DarkOakWallSign, DarkOakWood, DarkPrismarine, @@ -413,6 +439,7 @@ JungleDoor, JungleFence, JungleFenceGate, + JungleHangingSign, JungleLeaves, JungleLog, JunglePlanks, @@ -422,6 +449,7 @@ JungleSlab, JungleStairs, JungleTrapdoor, + JungleWallHangingSign, JungleWallSign, JungleWood, Kelp, @@ -505,6 +533,7 @@ MangroveDoor, MangroveFence, MangroveFenceGate, + MangroveHangingSign, MangroveLeaves, MangroveLog, MangrovePlanks, @@ -515,6 +544,7 @@ MangroveSlab, MangroveStairs, MangroveTrapdoor, + MangroveWallHangingSign, MangroveWallSign, MangroveWood, MediumAmethystBud, @@ -557,6 +587,7 @@ OakDoor, OakFence, OakFenceGate, + OakHangingSign, OakLeaves, OakLog, OakPlanks, @@ -566,6 +597,7 @@ OakSlab, OakStairs, OakTrapdoor, + OakWallHangingSign, OakWallSign, OakWood, Observer, @@ -596,6 +628,8 @@ PearlescentFroglight, Peony, PetrifiedOakSlab, + PiglinHead, + PiglinWallHead, PinkBanner, PinkBed, PinkCandle, @@ -803,6 +837,7 @@ SpruceDoor, SpruceFence, SpruceFenceGate, + SpruceHangingSign, SpruceLeaves, SpruceLog, SprucePlanks, @@ -812,6 +847,7 @@ SpruceSlab, SpruceStairs, SpruceTrapdoor, + SpruceWallHangingSign, SpruceWallSign, SpruceWood, StickyPiston, @@ -827,6 +863,7 @@ Stonecutter, StrippedAcaciaLog, StrippedAcaciaWood, + StrippedBambooBlock, StrippedBirchLog, StrippedBirchWood, StrippedCrimsonHyphae, @@ -875,6 +912,7 @@ WarpedFence, WarpedFenceGate, WarpedFungus, + WarpedHangingSign, WarpedHyphae, WarpedNylium, WarpedPlanks, @@ -885,6 +923,7 @@ WarpedStairs, WarpedStem, WarpedTrapdoor, + WarpedWallHangingSign, WarpedWallSign, WarpedWartBlock, Water, diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 71671c7c..e804a7d0 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -591,6 +591,9 @@ namespace MinecraftClient.Protocol.Handlers { int type = ReadNextVarInt(cache); + // Value's data type is depended on Type + object? value = null; + // starting from 1.13, Optional Chat is inserted as number 5 in 1.13 and IDs after 5 got shifted. // Increase type ID by 1 if // - below 1.13 @@ -598,20 +601,29 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion < Protocol18Handler.MC_1_13_Version) { if (type > 4) + ++type; + } + else if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) + { + if (type == 2) { - type += 1; + value = ReadNextVarLong(cache); + type = -1; + } + else if (type >= 3) + { + --type; } } - // Value's data type is depended on Type - object? value = null; - // This is backward compatible since new type is appended to the end // Version upgrade note // - Check type ID got shifted or not // - Add new type if any switch (type) { + case -1: // already readed + break; case 0: // byte value = ReadNextByte(cache); break; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 1fed292e..dfde4386 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -107,28 +107,30 @@ namespace MinecraftClient.Protocol.Handlers randomGen = RandomNumberGenerator.Create(); lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5); - if (handler.GetTerrainEnabled() && protocolVersion > MC_1_19_2_Version) + if (handler.GetTerrainEnabled() && protocolVersion > MC_1_19_3_Version) { log.Error("§c" + Translations.extra_terrainandmovement_disabled); handler.SetTerrainEnabled(false); } - if (handler.GetInventoryEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_2_Version)) + if (handler.GetInventoryEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_3_Version)) { log.Error("§c" + Translations.extra_inventory_disabled); handler.SetInventoryEnabled(false); } - if (handler.GetEntityHandlingEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_2_Version)) + if (handler.GetEntityHandlingEnabled() && (protocolVersion < MC_1_10_Version || protocolVersion > MC_1_19_3_Version)) { log.Error("§c" + Translations.extra_entity_disabled); handler.SetEntityHandlingEnabled(false); } // Block palette - if (protocolVersion > MC_1_19_2_Version && handler.GetTerrainEnabled()) + if (protocolVersion > MC_1_19_3_Version && handler.GetTerrainEnabled()) throw new NotImplementedException(Translations.exception_palette_block); - if (protocolVersion >= MC_1_19_Version) + if (protocolVersion >= MC_1_19_3_Version) + Block.Palette = new Palette1193(); + else if (protocolVersion >= MC_1_19_Version) Block.Palette = new Palette119(); else if (protocolVersion >= MC_1_17_Version) Block.Palette = new Palette117(); @@ -144,10 +146,12 @@ namespace MinecraftClient.Protocol.Handlers Block.Palette = new Palette112(); // Entity palette - if (protocolVersion > MC_1_19_2_Version && handler.GetEntityHandlingEnabled()) + if (protocolVersion > MC_1_19_3_Version && handler.GetEntityHandlingEnabled()) throw new NotImplementedException(Translations.exception_palette_entity); - if (protocolVersion >= MC_1_19_Version) + if (protocolVersion >= MC_1_19_3_Version) + entityPalette = new EntityPalette1193(); + else if (protocolVersion >= MC_1_19_Version) entityPalette = new EntityPalette119(); else if (protocolVersion >= MC_1_17_Version) entityPalette = new EntityPalette117(); @@ -165,10 +169,12 @@ namespace MinecraftClient.Protocol.Handlers entityPalette = new EntityPalette112(); // Item palette - if (protocolVersion > MC_1_19_2_Version && handler.GetInventoryEnabled()) + if (protocolVersion > MC_1_19_3_Version && handler.GetInventoryEnabled()) throw new NotImplementedException(Translations.exception_palette_item); - if (protocolVersion >= MC_1_19_Version) + if (protocolVersion >= MC_1_19_3_Version) + itemPalette = new ItemPalette1193(); + else if (protocolVersion >= MC_1_19_Version) itemPalette = new ItemPalette119(); else if (protocolVersion >= MC_1_18_1_Version) itemPalette = new ItemPalette118(); @@ -1851,7 +1857,7 @@ namespace MinecraftClient.Protocol.Handlers Dictionary metadata = dataTypes.ReadNextMetadata(packetData, itemPalette); int healthField; // See https://wiki.vg/Entity_metadata#Living_Entity - if (protocolVersion > MC_1_19_2_Version) + if (protocolVersion > MC_1_19_3_Version) throw new NotImplementedException(Translations.exception_palette_healthfield); else if (protocolVersion >= MC_1_17_Version) // 1.17 and above healthField = 9; From 8db0467f696c84b797706955efbc96e876682781 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 15 Jan 2023 17:18:42 +0800 Subject: [PATCH 32/44] Bug fix --- MinecraftClient/Protocol/Handlers/Protocol18.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index dfde4386..d5f07f97 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -696,7 +696,7 @@ namespace MinecraftClient.Protocol.Handlers } ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender() && verifyResult) + if (isOnlineMode && !chat.LacksSender() && messageSignature != null) Acknowledge(chat); handler.OnTextReceived(chat); } From 338f534239a147cca50a5ac5fff4cb747c80e190 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 15 Jan 2023 19:59:57 +0800 Subject: [PATCH 33/44] Fix script not showing execution results. --- MinecraftClient/ChatBots/Script.cs | 18 +++++-- MinecraftClient/ChatBots/ScriptScheduler.cs | 16 ++++-- .../ArgumentType/ScriptNameArgumentType.cs | 54 +++++++++++++++++++ .../CommandHandler/MccArguments.cs | 5 ++ MinecraftClient/Commands/Script.cs | 2 +- 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 MinecraftClient/CommandHandler/ArgumentType/ScriptNameArgumentType.cs diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 8ed7cb42..9e139db9 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reflection; using System.Text; using System.Threading; +using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; namespace MinecraftClient.ChatBots @@ -85,7 +86,7 @@ namespace MinecraftClient.ChatBots public static bool LookForScript(ref string filename) { //Automatically look in subfolders and try to add ".txt" file extension - char dir_slash = Path.DirectorySeparatorChar; + char dir_slash = Path.DirectorySeparatorChar; string[] files = new string[] { filename, @@ -210,11 +211,22 @@ namespace MinecraftClient.ChatBots sleepticks = ticks; break; default: - if (!PerformInternalCommand(instruction_line)) + CmdResult response = new(); + if (PerformInternalCommand(instruction_line, ref response)) + { + if (instruction_name.ToLower() != "log") + { + LogToConsole(instruction_line); + } + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) + { + LogToConsole(response); + } + } + else { Update(); //Unknown command : process next line immediately } - else if (instruction_name.ToLower() != "log") { LogToConsole(instruction_line); } break; } } diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index bf8680a4..0e15949d 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -1,4 +1,5 @@ using System; +using MinecraftClient.CommandHandler; using MinecraftClient.Scripting; using Tomlet.Attributes; using static MinecraftClient.ChatBots.ScriptScheduler.Configs; @@ -191,7 +192,10 @@ namespace MinecraftClient.ChatBots { task.Trigger_On_Time_Already_Triggered = true; LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_time, task.Action)); - PerformInternalCommand(task.Action); + CmdResult response = new(); + PerformInternalCommand(task.Action, ref response); + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) + LogToConsole(response); } } } @@ -209,7 +213,10 @@ namespace MinecraftClient.ChatBots if (task.Trigger_On_Login || (firstlogin_done == false && task.Trigger_On_First_Login)) { LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_login, task.Action)); - PerformInternalCommand(task.Action); + CmdResult response = new(); + PerformInternalCommand(task.Action, ref response); + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) + LogToConsole(response); } } @@ -229,7 +236,10 @@ namespace MinecraftClient.ChatBots Settings.DoubleToTick(task.Trigger_On_Interval.MinTime), Settings.DoubleToTick(task.Trigger_On_Interval.MaxTime) ); LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action)); - PerformInternalCommand(task.Action); + CmdResult response = new(); + PerformInternalCommand(task.Action, ref response); + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) + LogToConsole(response); } else task.Trigger_On_Interval_Countdown--; } diff --git a/MinecraftClient/CommandHandler/ArgumentType/ScriptNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/ScriptNameArgumentType.cs new file mode 100644 index 00000000..fe77a04c --- /dev/null +++ b/MinecraftClient/CommandHandler/ArgumentType/ScriptNameArgumentType.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Brigadier.NET; +using Brigadier.NET.ArgumentTypes; +using Brigadier.NET.Context; +using Brigadier.NET.Suggestion; + +namespace MinecraftClient.CommandHandler.ArgumentType +{ + public class ScriptNameArgumentType : ArgumentType + { + public override string Parse(IStringReader reader) + { + string remaining = reader.Remaining; + reader.Cursor += reader.RemainingLength; + return remaining; + } + + public override Task ListSuggestions(CommandContext context, SuggestionsBuilder builder) + { + try + { + string? dir = Path.GetDirectoryName(builder.Remaining); + if (!string.IsNullOrEmpty(dir) && Path.Exists(dir)) + { + foreach (string fileName in Directory.GetFiles(dir, "*.cs")) + builder.Suggest(fileName); + foreach (string fileName in Directory.GetFiles(dir, "*.txt")) + builder.Suggest(fileName); + } + } + catch (IOException) { } + catch (ArgumentException) { } + catch (UnauthorizedAccessException) { } + + try + { + foreach (string fileName in Directory.GetFiles("." + Path.DirectorySeparatorChar, "*.cs")) + builder.Suggest(fileName); + foreach (string fileName in Directory.GetFiles("." + Path.DirectorySeparatorChar, "*.txt")) + builder.Suggest(fileName); + } + catch (IOException) { } + catch (ArgumentException) { } + catch (UnauthorizedAccessException) { } + + return builder.BuildFuture(); + } + } +} diff --git a/MinecraftClient/CommandHandler/MccArguments.cs b/MinecraftClient/CommandHandler/MccArguments.cs index c659ee3e..78416bc1 100644 --- a/MinecraftClient/CommandHandler/MccArguments.cs +++ b/MinecraftClient/CommandHandler/MccArguments.cs @@ -110,5 +110,10 @@ namespace MinecraftClient.CommandHandler { return new HotbarSlotArgumentType(); } + + public static ScriptNameArgumentType ScriptName() + { + return new ScriptNameArgumentType(); + } } } diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index d2f310dd..37a8da80 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -20,7 +20,7 @@ namespace MinecraftClient.Commands ); dispatcher.Register(l => l.Literal(CmdName) - .Then(l => l.Argument("Script", Arguments.GreedyString()) + .Then(l => l.Argument("Script", MccArguments.ScriptName()) .Executes(r => DoExecuteScript(r.Source, Arguments.GetString(r, "Script"), null))) .Then(l => l.Literal("_help") .Executes(r => GetUsage(r.Source, string.Empty)) From 50dd5a3ba3602ec0f44d0a6725b33682c4d0c246 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 15 Jan 2023 20:56:10 +0800 Subject: [PATCH 34/44] Support specifying the digging duration --- MinecraftClient/Commands/Dig.cs | 14 ++-- MinecraftClient/McClient.cs | 64 +++++++++++++++---- .../Translations/Translations.Designer.cs | 18 ++++++ .../Resources/Translations/Translations.resx | 6 ++ 4 files changed, 84 insertions(+), 18 deletions(-) diff --git a/MinecraftClient/Commands/Dig.cs b/MinecraftClient/Commands/Dig.cs index 3a30619a..3a00c44b 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -23,8 +23,12 @@ namespace MinecraftClient.Commands dispatcher.Register(l => l.Literal(CmdName) .Executes(r => DigLookAt(r.Source)) + .Then(l => l.Argument("Duration", Arguments.Double()) + .Executes(r => DigLookAt(r.Source, Arguments.GetDouble(r, "Duration")))) .Then(l => l.Argument("Location", MccArguments.Location()) - .Executes(r => DigAt(r.Source, MccArguments.GetLocation(r, "Location")))) + .Executes(r => DigAt(r.Source, MccArguments.GetLocation(r, "Location"))) + .Then(l => l.Argument("Duration", Arguments.Double()) + .Executes(r => DigAt(r.Source, MccArguments.GetLocation(r, "Location"), Arguments.GetDouble(r, "Duration"))))) .Then(l => l.Literal("_help") .Executes(r => GetUsage(r.Source, string.Empty)) .Redirect(dispatcher.GetRoot().GetChild("help").GetChild(CmdName))) @@ -41,7 +45,7 @@ namespace MinecraftClient.Commands }); } - private int DigAt(CmdResult r, Location blockToBreak) + private int DigAt(CmdResult r, Location blockToBreak, double duration = 0) { McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) @@ -54,7 +58,7 @@ namespace MinecraftClient.Commands Block block = handler.GetWorld().GetBlock(blockToBreak); if (block.Type == Material.Air) return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); - else if (handler.DigBlock(blockToBreak)) + else if (handler.DigBlock(blockToBreak, duration: duration)) { blockToBreak = blockToBreak.ToCenter(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString())); @@ -63,7 +67,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); } - private int DigLookAt(CmdResult r) + private int DigLookAt(CmdResult r, double duration = 0) { McClient handler = CmdResult.currentHandler!; if (!handler.GetTerrainEnabled()) @@ -74,7 +78,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.Fail, Translations.cmd_dig_too_far); else if (block.Type == Material.Air) return r.SetAndReturn(Status.Fail, Translations.cmd_dig_no_block); - else if (handler.DigBlock(blockLoc, lookAtBlock: false)) + else if (handler.DigBlock(blockLoc, lookAtBlock: false, duration: duration)) return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockLoc.X, blockLoc.Y, blockLoc.Z, block.GetTypeString())); else return r.SetAndReturn(Status.Fail, Translations.cmd_dig_fail); diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 56eb4811..e61d2350 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -87,6 +87,10 @@ namespace MinecraftClient private int playerEntityID; + private object DigLock = new(); + private Tuple? LastDigPosition; + private int RemainingDiggingTime = 0; + // player health and hunger private float playerHealth; private int playerFoodSaturation; @@ -380,6 +384,22 @@ namespace MinecraftClient taskToRun(); } } + + lock (DigLock) + { + if (RemainingDiggingTime > 0) + { + if (--RemainingDiggingTime == 0 && LastDigPosition != null) + { + handler.SendPlayerDigging(2, LastDigPosition.Item1, LastDigPosition.Item2, sequenceId++); + Log.Info(string.Format(Translations.cmd_dig_end, LastDigPosition.Item1)); + } + else + { + DoAnimation((int)Hand.MainHand); + } + } + } } #region Connection Lost and Disconnect from Server @@ -1299,7 +1319,7 @@ namespace MinecraftClient /// TRUE if the item was successfully used public bool UseItemOnHand() { - return InvokeOnMainThread(() => handler.SendUseItem(0, sequenceId)); + return InvokeOnMainThread(() => handler.SendUseItem(0, sequenceId++)); } /// @@ -1308,7 +1328,7 @@ namespace MinecraftClient /// TRUE if the item was successfully used public bool UseItemOnLeftHand() { - return InvokeOnMainThread(() => handler.SendUseItem(1, sequenceId)); + return InvokeOnMainThread(() => handler.SendUseItem(1, sequenceId++)); } /// @@ -2193,7 +2213,7 @@ namespace MinecraftClient /// TRUE if successfully placed public bool PlaceBlock(Location location, Direction blockFace, Hand hand = Hand.MainHand) { - return InvokeOnMainThread(() => handler.SendPlayerBlockPlacement((int)hand, location, blockFace, sequenceId)); + return InvokeOnMainThread(() => handler.SendPlayerBlockPlacement((int)hand, location, blockFace, sequenceId++)); } /// @@ -2202,26 +2222,44 @@ namespace MinecraftClient /// Location of block to dig /// Also perform the "arm swing" animation /// Also look at the block before digging - public bool DigBlock(Location location, bool swingArms = true, bool lookAtBlock = true) + public bool DigBlock(Location location, bool swingArms = true, bool lookAtBlock = true, double duration = 0) { if (!GetTerrainEnabled()) return false; if (InvokeRequired) - return InvokeOnMainThread(() => DigBlock(location, swingArms, lookAtBlock)); + return InvokeOnMainThread(() => DigBlock(location, swingArms, lookAtBlock, duration)); // TODO select best face from current player location Direction blockFace = Direction.Down; - // Look at block before attempting to break it - if (lookAtBlock) - UpdateLocation(GetCurrentLocation(), location); + lock (DigLock) + { + if (RemainingDiggingTime > 0 && LastDigPosition != null) + { + handler.SendPlayerDigging(1, LastDigPosition.Item1, LastDigPosition.Item2, sequenceId++); + Log.Info(string.Format(Translations.cmd_dig_cancel, LastDigPosition.Item1)); + } - // Send dig start and dig end, will need to wait for server response to know dig result - // See https://wiki.vg/How_to_Write_a_Client#Digging for more details - return handler.SendPlayerDigging(0, location, blockFace, sequenceId) - && (!swingArms || DoAnimation((int)Hand.MainHand)) - && handler.SendPlayerDigging(2, location, blockFace, sequenceId); + // Look at block before attempting to break it + if (lookAtBlock) + UpdateLocation(GetCurrentLocation(), location); + + // Send dig start and dig end, will need to wait for server response to know dig result + // See https://wiki.vg/How_to_Write_a_Client#Digging for more details + bool result = handler.SendPlayerDigging(0, location, blockFace, sequenceId++) + && (!swingArms || DoAnimation((int)Hand.MainHand)); + + if (duration <= 0) + result &= handler.SendPlayerDigging(2, location, blockFace, sequenceId++); + else + { + LastDigPosition = new(location, blockFace); + RemainingDiggingTime = Settings.DoubleToTick(duration); + } + + return result; + } } /// diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index 9c1b8d84..a0ca3082 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -2704,6 +2704,15 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to Cancel mining the block located at {0}.. + /// + internal static string cmd_dig_cancel { + get { + return ResourceManager.GetString("cmd.dig.cancel", resourceCulture); + } + } + /// /// Looks up a localized string similar to Attempt to break a block. /// @@ -2722,6 +2731,15 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to Mining of the block located at {0} ends.. + /// + internal static string cmd_dig_end { + get { + return ResourceManager.GetString("cmd.dig.end", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to start digging block.. /// diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index 73e81493..120249c5 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -2028,4 +2028,10 @@ Logging in... Connected to proxy {0}:{1} + + Mining of the block located at {0} ends. + + + Cancel mining the block located at {0}. + \ No newline at end of file From 950d9bcfdc1804c4e6b27bbedc71c1a0b4291936 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Mon, 16 Jan 2023 03:00:37 +0800 Subject: [PATCH 35/44] Fix entity handle & Try fix message singing again --- .../Protocol/Handlers/DataTypes.cs | 87 +++++-- .../Handlers/Packet/s2c/DeclareCommands.cs | 3 + .../Protocol/Handlers/Protocol18.cs | 238 +++++++++--------- 3 files changed, 198 insertions(+), 130 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index e804a7d0..716312ec 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -684,23 +684,73 @@ namespace MinecraftClient.Protocol.Handlers case 15: // Particle // Currecutly not handled. Reading data only int ParticleID = ReadNextVarInt(cache); - switch (ParticleID) + // Need to check the exact version where the change occurred!!!!! + if (protocolversion >= Protocol18Handler.MC_1_19_3_Version) { - case 3: - ReadNextVarInt(cache); - break; - case 14: - ReadNextFloat(cache); - ReadNextFloat(cache); - ReadNextFloat(cache); - ReadNextFloat(cache); - break; - case 23: - ReadNextVarInt(cache); - break; - case 32: - ReadNextItemSlot(cache, itemPalette); - break; + switch (ParticleID) + { + case 2: + ReadNextVarInt(cache); + break; + case 3: + ReadNextVarInt(cache); + break; + case 14: + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + break; + case 15: + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + break; + case 24: + ReadNextVarInt(cache); + break; + case 35: + ReadNextItemSlot(cache, itemPalette); + break; + case 36: + string positionSourceType = ReadNextString(cache); + if (positionSourceType == "minecraft:block") + { + ReadNextLocation(cache); + } + else if (positionSourceType == "minecraft:entity") + { + ReadNextVarInt(cache); + ReadNextFloat(cache); + } + ReadNextVarInt(cache); + break; + } + } + else + { + switch (ParticleID) + { + case 3: + ReadNextVarInt(cache); + break; + case 14: + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + ReadNextFloat(cache); + break; + case 23: + ReadNextVarInt(cache); + break; + case 32: + ReadNextItemSlot(cache, itemPalette); + break; + } } break; case 16: // Villager Data (3x VarInt) @@ -730,7 +780,10 @@ namespace MinecraftClient.Protocol.Handlers if (protocolversion <= Protocol18Handler.MC_1_19_Version) value = ReadNextVarInt(cache); else - value = null; // Dimension and blockPos, currently not in use + { + // Dimension and blockPos, currently not in use + value = new Tuple(ReadNextString(cache), ReadNextLocation(cache)); + } break; case 22: // Painting Variant value = ReadNextVarInt(cache); diff --git a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs index b2a9600c..245432bc 100644 --- a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs +++ b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs @@ -104,6 +104,9 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c private static void CollectSignArguments(int NodeIdx, string command, List> arguments) { + if (Nodes.Length <= NodeIdx) + return; + CommandNode node = Nodes[NodeIdx]; string last_arg = command; switch (node.Flags & 0x03) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index d5f07f97..745245f9 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -75,6 +75,7 @@ namespace MinecraftClient.Protocol.Handlers private readonly BlockingCollection>> packetQueue = new(); private float LastYaw, LastPitch; + private object MessageSigningLock = new(); private Guid chatUuid = Guid.Empty; private int pendingAcknowledgments = 0, messageIndex = 0; private LastSeenMessagesCollector lastSeenMessagesCollector; @@ -696,11 +697,15 @@ namespace MinecraftClient.Protocol.Handlers } ChatMessage chat = new(message, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - if (isOnlineMode && !chat.LacksSender() && messageSignature != null) + lock (MessageSigningLock) Acknowledge(chat); handler.OnTextReceived(chat); } break; + case PacketTypesIn.HideMessage: + byte[] hideMessageSignature = dataTypes.ReadNextByteArray(packetData); + ConsoleIO.WriteLine($"HideMessage was not processed! (SigLen={hideMessageSignature.Length})"); + break; case PacketTypesIn.SystemChat: string systemMessage = dataTypes.ReadNextString(packetData); if (protocolVersion >= MC_1_19_3_Version) @@ -2524,13 +2529,14 @@ namespace MinecraftClient.Protocol.Handlers { if (protocolVersion >= MC_1_19_3_Version) { - lastSeenMessagesCollector.Add_1_19_3(entry, true); - lastReceivedMessage = null; - if (lastSeenMessagesCollector.messageCount > 64) + if (lastSeenMessagesCollector.Add_1_19_3(entry, true)) { - int messageCount = lastSeenMessagesCollector.ResetMessageCount(); - if (messageCount > 0) - SendMessageAcknowledgment(messageCount); + if (lastSeenMessagesCollector.messageCount > 64) + { + int messageCount = lastSeenMessagesCollector.ResetMessageCount(); + if (messageCount > 0) + SendMessageAcknowledgment(messageCount); + } } } else @@ -2561,74 +2567,77 @@ namespace MinecraftClient.Protocol.Handlers try { - LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = - (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; - - (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = - (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); - - List fields = new(); - - // Command: String - fields.AddRange(dataTypes.GetString(command)); - - // Timestamp: Instant(Long) - DateTimeOffset timeNow = DateTimeOffset.UtcNow; - fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); - List>? needSigned = null; // List< Argument Name, Argument Value > if (playerKeyPair != null && isOnlineMode && protocolVersion >= MC_1_19_Version && Config.Signature.LoginWithSecureProfile && Config.Signature.SignMessageInCommand) needSigned = DeclareCommands.CollectSignArguments(command); - if (needSigned == null || needSigned!.Count == 0) + lock (MessageSigningLock) { - fields.AddRange(DataTypes.GetLong(0)); // Salt: Long - fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt - } - else - { - Guid uuid = handler.GetUserUuid(); - byte[] salt = GenerateSalt(); - fields.AddRange(salt); // Salt: Long - fields.AddRange(DataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt - foreach ((string argName, string message) in needSigned) + LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = + (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; + + (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = + (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); + + List fields = new(); + + // Command: String + fields.AddRange(dataTypes.GetString(command)); + + // Timestamp: Instant(Long) + DateTimeOffset timeNow = DateTimeOffset.UtcNow; + fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); + + if (needSigned == null || needSigned!.Count == 0) { - fields.AddRange(dataTypes.GetString(argName)); // Argument name: String - - byte[] sign; - if (protocolVersion == MC_1_19_Version) - sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); - else if (protocolVersion == MC_1_19_2_Version) - sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); - else // protocolVersion >= MC_1_19_3_Version - sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); - - if (protocolVersion <= MC_1_19_2_Version) - fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt - - fields.AddRange(sign); // Signature: Byte Array + fields.AddRange(DataTypes.GetLong(0)); // Salt: Long + fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt } + else + { + Guid uuid = handler.GetUserUuid(); + byte[] salt = GenerateSalt(); + fields.AddRange(salt); // Salt: Long + fields.AddRange(DataTypes.GetVarInt(needSigned.Count)); // Signature Length: VarInt + foreach ((string argName, string message) in needSigned) + { + fields.AddRange(dataTypes.GetString(argName)); // Argument name: String + + byte[] sign; + if (protocolVersion == MC_1_19_Version) + sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt); + else if (protocolVersion == MC_1_19_2_Version) + sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); + else // protocolVersion >= MC_1_19_3_Version + sign = playerKeyPair!.PrivateKey.SignMessage(message, uuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); + + if (protocolVersion <= MC_1_19_2_Version) + fields.AddRange(DataTypes.GetVarInt(sign.Length)); // Signature length: VarInt + + fields.AddRange(sign); // Signature: Byte Array + } + } + + if (protocolVersion <= MC_1_19_2_Version) + fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean + + if (protocolVersion == MC_1_19_2_Version) + { + // Message Acknowledgment (1.19.2) + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); + } + else if (protocolVersion >= MC_1_19_3_Version) + { + // message count + fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); + + // Acknowledged: BitSet + fields.AddRange(bitset_1_19_3); + } + + SendPacket(PacketTypesOut.ChatCommand, fields); } - - if (protocolVersion <= MC_1_19_2_Version) - fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean - - if (protocolVersion == MC_1_19_2_Version) - { - // Message Acknowledgment (1.19.2) - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); - } - else if (protocolVersion >= MC_1_19_3_Version) - { - // message count - fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); - - // Acknowledged: BitSet - fields.AddRange(bitset_1_19_3); - } - - SendPacket(PacketTypesOut.ChatCommand, fields); return true; } catch (SocketException) { return false; } @@ -2660,62 +2669,65 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_19_Version) { - LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = + lock (MessageSigningLock) + { + LastSeenMessageList.Acknowledgment? acknowledgment_1_19_2 = (protocolVersion == MC_1_19_2_Version) ? ConsumeAcknowledgment() : null; - (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = - (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); + (LastSeenMessageList.AcknowledgedMessage[] acknowledgment_1_19_3, byte[] bitset_1_19_3, int messageCount_1_19_3) = + (protocolVersion >= MC_1_19_3_Version) ? lastSeenMessagesCollector.Collect_1_19_3() : new(Array.Empty(), Array.Empty(), 0); - // Timestamp: Instant(Long) - DateTimeOffset timeNow = DateTimeOffset.UtcNow; - fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); + // Timestamp: Instant(Long) + DateTimeOffset timeNow = DateTimeOffset.UtcNow; + fields.AddRange(DataTypes.GetLong(timeNow.ToUnixTimeMilliseconds())); - if (!isOnlineMode || playerKeyPair == null || !Config.Signature.LoginWithSecureProfile || !Config.Signature.SignChat) - { - fields.AddRange(DataTypes.GetLong(0)); // Salt: Long - if (protocolVersion < MC_1_19_3_Version) - fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) + if (!isOnlineMode || playerKeyPair == null || !Config.Signature.LoginWithSecureProfile || !Config.Signature.SignChat) + { + fields.AddRange(DataTypes.GetLong(0)); // Salt: Long + if (protocolVersion < MC_1_19_3_Version) + fields.AddRange(DataTypes.GetVarInt(0)); // Signature Length: VarInt (1.19 - 1.19.2) + else + fields.AddRange(dataTypes.GetBool(false)); // Has signature: bool (1.19.3) + } else - fields.AddRange(dataTypes.GetBool(false)); // Has signature: bool (1.19.3) - } - else - { - // Salt: Long - byte[] salt = GenerateSalt(); - fields.AddRange(salt); + { + // Salt: Long + byte[] salt = GenerateSalt(); + fields.AddRange(salt); - // Signature Length & Signature: (VarInt) and Byte Array - Guid playerUuid = handler.GetUserUuid(); - byte[] sign; - if (protocolVersion == MC_1_19_Version) // 1.19.1 or lower - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt); - else if (protocolVersion == MC_1_19_2_Version) // 1.19.2 - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); - else // protocolVersion >= MC_1_19_3_Version - sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); + // Signature Length & Signature: (VarInt) and Byte Array + Guid playerUuid = handler.GetUserUuid(); + byte[] sign; + if (protocolVersion == MC_1_19_Version) // 1.19.1 or lower + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt); + else if (protocolVersion == MC_1_19_2_Version) // 1.19.2 + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, timeNow, ref salt, acknowledgment_1_19_2!.lastSeen); + else // protocolVersion >= MC_1_19_3_Version + sign = playerKeyPair.PrivateKey.SignMessage(message, playerUuid, chatUuid, messageIndex++, timeNow, ref salt, acknowledgment_1_19_3); + + if (protocolVersion >= MC_1_19_3_Version) + fields.AddRange(dataTypes.GetBool(true)); + else + fields.AddRange(DataTypes.GetVarInt(sign.Length)); + fields.AddRange(sign); + } + + if (protocolVersion <= MC_1_19_2_Version) + fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean if (protocolVersion >= MC_1_19_3_Version) - fields.AddRange(dataTypes.GetBool(true)); - else - fields.AddRange(DataTypes.GetVarInt(sign.Length)); - fields.AddRange(sign); - } + { + // message count + fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); - if (protocolVersion <= MC_1_19_2_Version) - fields.AddRange(dataTypes.GetBool(false)); // Signed Preview: Boolean - - if (protocolVersion >= MC_1_19_3_Version) - { - // message count - fields.AddRange(DataTypes.GetVarInt(messageCount_1_19_3)); - - // Acknowledged: BitSet - fields.AddRange(bitset_1_19_3); - } - else if (protocolVersion == MC_1_19_2_Version) - { - // Message Acknowledgment - fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); + // Acknowledged: BitSet + fields.AddRange(bitset_1_19_3); + } + else if (protocolVersion == MC_1_19_2_Version) + { + // Message Acknowledgment + fields.AddRange(dataTypes.GetAcknowledgment(acknowledgment_1_19_2!, isOnlineMode && Config.Signature.LoginWithSecureProfile)); + } } } SendPacket(PacketTypesOut.ChatMessage, fields); From 30e95f2d231250a1879ae9a27c9cf3f90310805d Mon Sep 17 00:00:00 2001 From: BruceChen Date: Mon, 16 Jan 2023 04:04:56 +0800 Subject: [PATCH 36/44] Bug fix --- MinecraftClient/McClient.cs | 9 ++++++++- .../Protocol/Handlers/Packet/s2c/DeclareCommands.cs | 3 --- MinecraftClient/Protocol/Handlers/Protocol18.cs | 3 +++ MinecraftClient/Protocol/IMinecraftComHandler.cs | 2 ++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index e61d2350..d4c8a739 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2434,7 +2434,9 @@ namespace MinecraftClient if (protocolversion >= Protocol18Handler.MC_1_19_3_Version && playerKeyPair != null) handler.SendPlayerSession(playerKeyPair); - CanSendMessage = true; + + if (protocolversion < Protocol18Handler.MC_1_19_3_Version) + CanSendMessage = true; if (inventoryHandlingRequested) { @@ -3480,6 +3482,11 @@ namespace MinecraftClient ConsoleIO.OnAutoCompleteDone(transactionId, result); } + public void OnDeclareCommands() + { + CanSendMessage = true; + } + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom diff --git a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs index 245432bc..b2a9600c 100644 --- a/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs +++ b/MinecraftClient/Protocol/Handlers/Packet/s2c/DeclareCommands.cs @@ -104,9 +104,6 @@ namespace MinecraftClient.Protocol.Handlers.packet.s2c private static void CollectSignArguments(int NodeIdx, string command, List> arguments) { - if (Nodes.Length <= NodeIdx) - return; - CommandNode node = Nodes[NodeIdx]; string last_arg = command; switch (node.Flags & 0x03) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 745245f9..39f0ed18 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -466,7 +466,10 @@ namespace MinecraftClient.Protocol.Handlers break; case PacketTypesIn.DeclareCommands: if (protocolVersion >= MC_1_19_Version) + { DeclareCommands.Read(dataTypes, packetData, protocolVersion); + handler.OnDeclareCommands(); + } break; case PacketTypesIn.ChatMessage: int messageType = 0; diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index e128507d..1adc9d65 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -466,6 +466,8 @@ namespace MinecraftClient.Protocol /// All commands. public void OnAutoCompleteDone(int transactionId, string[] result); + public void OnDeclareCommands(); + /// /// Send a click container button packet to the server. /// Used for Enchanting table, Lectern, stone cutter and loom From c9c4d8dd6273335848c080e6429e0cd67f60dac5 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Mon, 16 Jan 2023 14:42:25 +0800 Subject: [PATCH 37/44] [SKIP_BUILD] Update ConsoleInteractive --- ConsoleInteractive | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConsoleInteractive b/ConsoleInteractive index 42074c44..70bd2633 160000 --- a/ConsoleInteractive +++ b/ConsoleInteractive @@ -1 +1 @@ -Subproject commit 42074c449b8cf32e035f982bd83af6dcf75bc764 +Subproject commit 70bd2633b4ec6da62a2554f80a769e8178d825f2 From 92a911ce99da26968e1051df092e5fd91c46ac61 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Mon, 16 Jan 2023 14:47:17 +0800 Subject: [PATCH 38/44] Disable forge by default --- MinecraftClient/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 4407f937..be3e711d 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -525,7 +525,7 @@ namespace MinecraftClient public string MinecraftVersion = "auto"; [TomlInlineComment("$Main.Advanced.mc_forge$")] - public ForgeConfigType EnableForge = ForgeConfigType.auto; + public ForgeConfigType EnableForge = ForgeConfigType.no; [TomlInlineComment("$Main.Advanced.brand_info$")] public BrandInfoType BrandInfo = BrandInfoType.mcc; From 1f54a7c247602ff604147169962982c6ed21281a Mon Sep 17 00:00:00 2001 From: BruceChen Date: Tue, 17 Jan 2023 20:16:35 +0800 Subject: [PATCH 39/44] Fix 1.19.3 key exchange in offline mode --- MinecraftClient/ChatBots/AutoAttack.cs | 2 +- MinecraftClient/McClient.cs | 4 ++-- MinecraftClient/Protocol/Handlers/Protocol18.cs | 4 ++-- MinecraftClient/Protocol/IMinecraftComHandler.cs | 2 +- MinecraftClient/Protocol/ProfileKey/PublicKey.cs | 6 ++++++ 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 9225e370..4b203497 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -100,7 +100,7 @@ namespace MinecraftClient.ChatBots private Double attackSpeed = 4; private Double attackCooldownSeconds; private readonly bool overrideAttackSpeed = false; - private readonly int attackRange = 4; + private readonly double attackRange = 4.0; private Double serverTPS; private float health = 100; private readonly bool attackHostile = true; diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index d4c8a739..afd78df0 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2415,7 +2415,7 @@ namespace MinecraftClient /// /// Called when a server was successfully joined /// - public void OnGameJoined() + public void OnGameJoined(bool isOnlineMode) { string? bandString = Config.Main.Advanced.BrandInfo.ToBrandString(); if (!String.IsNullOrWhiteSpace(bandString)) @@ -2432,7 +2432,7 @@ namespace MinecraftClient (byte)Config.MCSettings.MainHand); if (protocolversion >= Protocol18Handler.MC_1_19_3_Version - && playerKeyPair != null) + && playerKeyPair != null && isOnlineMode) handler.SendPlayerSession(playerKeyPair); if (protocolversion < Protocol18Handler.MC_1_19_3_Version) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 39f0ed18..022aa14b 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -367,7 +367,7 @@ namespace MinecraftClient.Protocol.Handlers SendPacket(PacketTypesOut.Pong, packetData); break; case PacketTypesIn.JoinGame: - handler.OnGameJoined(); + handler.OnGameJoined(isOnlineMode); int playerEntityID = dataTypes.ReadNextInt(packetData); handler.OnReceivePlayerEntityID(playerEntityID); @@ -3383,7 +3383,7 @@ namespace MinecraftClient.Protocol.Handlers public bool SendPlayerSession(PlayerKeyPair? playerKeyPair) { - if (playerKeyPair == null) + if (playerKeyPair == null || !isOnlineMode) return false; if (protocolVersion >= MC_1_19_3_Version) diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 1adc9d65..549d070d 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -80,7 +80,7 @@ namespace MinecraftClient.Protocol /// /// Called when a server was successfully joined /// - void OnGameJoined(); + void OnGameJoined(bool isOnlineMode); /// /// Received chat/system message from the server diff --git a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs index 4fd1c176..2208e04e 100644 --- a/MinecraftClient/Protocol/ProfileKey/PublicKey.cs +++ b/MinecraftClient/Protocol/ProfileKey/PublicKey.cs @@ -24,6 +24,12 @@ namespace MinecraftClient.Protocol.ProfileKey if (!string.IsNullOrEmpty(sigV2)) SignatureV2 = Convert.FromBase64String(sigV2!); + + if (SignatureV2 == null || SignatureV2.Length == 0) + SignatureV2 = Signature; + + if (Signature == null || Signature.Length == 0) + Signature = SignatureV2; } public PublicKey(byte[] key, byte[] signature) From e52fe6cb8b7c142640dd1e0de8c8076fd8b76fc2 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Thu, 19 Jan 2023 17:54:22 +0800 Subject: [PATCH 40/44] Catch and throw exceptions --- MinecraftClient/Program.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 868c2ea2..dd9dcbc0 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -625,7 +625,12 @@ namespace MinecraftClient { throw; } - catch (Exception) { } + catch (Exception e) + { + ConsoleIO.WriteLine(e.Message); + ConsoleIO.WriteLine(e.StackTrace ?? ""); + HandleFailure(); // Other error + } } else HandleFailure(Translations.error_determine, true); } From cb57db8328c1acb84b7546bcd1df567213134729 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:02:34 +0800 Subject: [PATCH 41/44] ChatParser: Catch exception in download language file --- MinecraftClient/Protocol/Message/ChatParser.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 54df413b..2c308cd4 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; @@ -297,6 +298,15 @@ namespace MinecraftClient.Protocol.Message { ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_save_fail, languageFilePath), acceptnewlines: true); } + catch (Exception e) + { + ConsoleIO.WriteLineFormatted("§8" + Translations.chat_fail, acceptnewlines: true); + if (Config.Logging.DebugMessages) + { + ConsoleIO.WriteLine(e.Message); + ConsoleIO.WriteLine(e.StackTrace ?? ""); + } + } finally { httpClient.Dispose(); From cc92cd66d4932fdb3535415448207f38a909741f Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:20:45 +0800 Subject: [PATCH 42/44] Switch to https for translation file download --- MinecraftClient/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index be3e711d..6b6bb5b9 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -35,7 +35,7 @@ namespace MinecraftClient // Other Settings public const string TranslationsFile_Version = "1.19.3"; public const string TranslationsFile_Website_Index = "https://piston-meta.mojang.com/v1/packages/c492375ded5da34b646b8c5c0842a0028bc69cec/2.json"; - public const string TranslationsFile_Website_Download = "http://resources.download.minecraft.net"; + public const string TranslationsFile_Website_Download = "https://resources.download.minecraft.net"; public const string TranslationProjectUrl = "https://crwd.in/minecraft-console-client"; From b79dd1d379645f50a4aa8c226da1a9b8cdd8b660 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 21 Jan 2023 01:34:05 +0800 Subject: [PATCH 43/44] Bug fix --- .../Protocol/Message/ChatParser.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/MinecraftClient/Protocol/Message/ChatParser.cs b/MinecraftClient/Protocol/Message/ChatParser.cs index 2c308cd4..dad15d21 100644 --- a/MinecraftClient/Protocol/Message/ChatParser.cs +++ b/MinecraftClient/Protocol/Message/ChatParser.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; @@ -238,7 +239,7 @@ namespace MinecraftClient.Protocol.Message string languageFilePath = "lang" + Path.DirectorySeparatorChar + Config.Main.Advanced.Language + ".json"; - // Load the external dictionnary of translation rules or display an error message + // Load the external dictionary of translation rules or display an error message if (File.Exists(languageFilePath)) { try @@ -272,18 +273,18 @@ namespace MinecraftClient.Protocol.Message if (Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted(string.Format(Translations.chat_request, translation_file_location)); - Task fetch_file = httpClient.GetStreamAsync(translation_file_location); - fetch_file.Wait(); - TranslationRules = JsonSerializer.Deserialize>(fetch_file.Result)!; - fetch_file.Dispose(); + Task?> fetckFileTask = httpClient.GetFromJsonAsync>(translation_file_location); + fetckFileTask.Wait(); + if (fetckFileTask.Result != null && fetckFileTask.Result.Count > 0) + { + TranslationRules = fetckFileTask.Result; + TranslationRules["Version"] = TranslationsFile_Version; + File.WriteAllText(languageFilePath, JsonSerializer.Serialize(TranslationRules, typeof(Dictionary)), Encoding.UTF8); - TranslationRules["Version"] = TranslationsFile_Version; - - File.WriteAllText(languageFilePath, JsonSerializer.Serialize(TranslationRules, typeof(Dictionary)), Encoding.UTF8); - - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_done, languageFilePath)); - - return; + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.chat_done, languageFilePath)); + return; + } + fetckFileTask.Dispose(); } else { From 70d1f70cca40af3f1382ea3bb2e1a0bd4561f5c1 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 28 Jan 2023 15:03:31 +0800 Subject: [PATCH 44/44] Update Crowdin related github action --- .github/workflows/build-and-release.yml | 2 +- .github/workflows/deploy-doc-only.yml | 6 +++--- ...translations-only.yml => update-translations-only.yml} | 8 ++++---- .gitignore | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) rename .github/workflows/{sync-translations-only.yml => update-translations-only.yml} (79%) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 5e74359a..3f48dbb0 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -26,7 +26,7 @@ jobs: submodules: 'true' - name: Download translations from crowdin - uses: crowdin/github-action@1.5.0 + uses: crowdin/github-action@1.6.0 with: upload_sources: true upload_translations: false diff --git a/.github/workflows/deploy-doc-only.yml b/.github/workflows/deploy-doc-only.yml index 52707f29..b794284f 100644 --- a/.github/workflows/deploy-doc-only.yml +++ b/.github/workflows/deploy-doc-only.yml @@ -17,10 +17,10 @@ jobs: fetch-depth: 0 submodules: 'true' - - name: Sync translations from crowdin - uses: crowdin/github-action@1.5.0 + - name: Download translations from crowdin + uses: crowdin/github-action@1.6.0 with: - upload_sources: true + upload_sources: false upload_translations: false download_translations: true diff --git a/.github/workflows/sync-translations-only.yml b/.github/workflows/update-translations-only.yml similarity index 79% rename from .github/workflows/sync-translations-only.yml rename to .github/workflows/update-translations-only.yml index 104876b5..c47ea685 100644 --- a/.github/workflows/sync-translations-only.yml +++ b/.github/workflows/update-translations-only.yml @@ -1,4 +1,4 @@ -name: Sync Translations +name: Upload translation sources on: workflow_dispatch: @@ -15,12 +15,12 @@ jobs: fetch-depth: 0 submodules: 'true' - - name: Sync translations from crowdin - uses: crowdin/github-action@1.5.0 + - name: Upload translation sources to crowdin + uses: crowdin/github-action@1.6.0 with: upload_sources: true upload_translations: false - download_translations: true + download_translations: false localization_branch_name: l10n_master create_pull_request: false diff --git a/.gitignore b/.gitignore index 22033cc8..fc8f35e0 100644 --- a/.gitignore +++ b/.gitignore @@ -415,3 +415,4 @@ FodyWeavers.xsd !/docs/.vuepress/translations/en.json /docs/l10n/ +/docs/.vuepress/public/MCC-README/