diff --git a/.gitmodules b/.gitmodules index 5ef4ed0f..66e56c89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = ConsoleInteractive url = https://github.com/breadbyte/ConsoleInteractive branch = main +[submodule "MinecraftProtocolLibrary"] + path = MinecraftProtocolLibrary + url = https://github.com/BruceChenQAQ/Minecraft-Protocol-Library.git diff --git a/MinecraftClient.sln b/MinecraftClient.sln index 8f0049d8..cd117ea1 100644 --- a/MinecraftClient.sln +++ b/MinecraftClient.sln @@ -4,9 +4,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.3.32901.215 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinecraftClient", "MinecraftClient\MinecraftClient.csproj", "{1E2FACE4-F5CA-4323-9641-740C6A551770}" + ProjectSection(ProjectDependencies) = postProject + {B6EA6A06-0EF8-4931-93EA-68EB02D69FE9} = {B6EA6A06-0EF8-4931-93EA-68EB02D69FE9} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleInteractive", "ConsoleInteractive\ConsoleInteractive\ConsoleInteractive\ConsoleInteractive.csproj", "{93DA4D71-EFAD-4493-BE21-A105AF663660}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinecraftProtocolLibrary", "MinecraftProtocolLibrary\MinecraftProtocolLibrary\MinecraftProtocolLibrary.csproj", "{B6EA6A06-0EF8-4931-93EA-68EB02D69FE9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,13 +26,17 @@ Global {93DA4D71-EFAD-4493-BE21-A105AF663660}.Debug|Any CPU.Build.0 = Debug|Any CPU {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|Any CPU.ActiveCfg = Release|Any CPU {93DA4D71-EFAD-4493-BE21-A105AF663660}.Release|Any CPU.Build.0 = Release|Any CPU + {B6EA6A06-0EF8-4931-93EA-68EB02D69FE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6EA6A06-0EF8-4931-93EA-68EB02D69FE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6EA6A06-0EF8-4931-93EA-68EB02D69FE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6EA6A06-0EF8-4931-93EA-68EB02D69FE9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - RESX_ShowErrorsInErrorList = False - SolutionGuid = {6DED60F4-9CF4-4DB3-8966-582B2EBE8487} RESX_SortFileContentOnSave = False + SolutionGuid = {6DED60F4-9CF4-4DB3-8966-582B2EBE8487} + RESX_ShowErrorsInErrorList = False EndGlobalSection EndGlobal diff --git a/MinecraftClient/ChatBots/AutoAttack.cs b/MinecraftClient/ChatBots/AutoAttack.cs index 8cd8ee2f..85129b4b 100644 --- a/MinecraftClient/ChatBots/AutoAttack.cs +++ b/MinecraftClient/ChatBots/AutoAttack.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using MinecraftClient.Mapping; +using MinecraftClient.EntityHandler; using MinecraftClient.Scripting; using Tomlet.Attributes; diff --git a/MinecraftClient/ChatBots/AutoFishing.cs b/MinecraftClient/ChatBots/AutoFishing.cs index e185de10..8c4d8ffe 100644 --- a/MinecraftClient/ChatBots/AutoFishing.cs +++ b/MinecraftClient/ChatBots/AutoFishing.cs @@ -5,6 +5,7 @@ using System.Text; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.EntityHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using MinecraftClient.Scripting; diff --git a/MinecraftClient/ChatBots/FollowPlayer.cs b/MinecraftClient/ChatBots/FollowPlayer.cs index 0561bf0f..6833ca14 100644 --- a/MinecraftClient/ChatBots/FollowPlayer.cs +++ b/MinecraftClient/ChatBots/FollowPlayer.cs @@ -4,6 +4,7 @@ using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.EntityHandler; using MinecraftClient.Mapping; using MinecraftClient.Scripting; using Tomlet.Attributes; diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index fef48a90..5462fec3 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -356,11 +356,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 +369,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/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 8ed7cb42..010566c6 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -130,7 +130,7 @@ namespace MinecraftClient.ChatBots //Load the given file from the startup parameters if (LookForScript(ref file!)) { - lines = System.IO.File.ReadAllLines(file, Encoding.UTF8); + lines = File.ReadAllLines(file, Encoding.UTF8); csharp = file.EndsWith(".cs"); thread = null; diff --git a/MinecraftClient/ChatBots/TestBot.cs b/MinecraftClient/ChatBots/TestBot.cs index 8b103535..f1a8e622 100644 --- a/MinecraftClient/ChatBots/TestBot.cs +++ b/MinecraftClient/ChatBots/TestBot.cs @@ -1,4 +1,6 @@ -using MinecraftClient.Scripting; +using System; +using System.Threading.Tasks; +using MinecraftClient.Scripting; namespace MinecraftClient.ChatBots { @@ -8,6 +10,19 @@ namespace MinecraftClient.ChatBots public class TestBot : ChatBot { + //public override Tuple>[]? InitializeEventCallbacks() + //{ + // return new Tuple>[] + // { + // new(McClientEventType.ClientTick, async (object? o) => + // { + // await Task.CompletedTask; + // LogToConsole("test aaa"); + // throw new Exception("dwadwa"); + // }) + // }; + //} + public override void GetText(string text) { string message = ""; diff --git a/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs index fc3ce124..222459a8 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/EntityTypeArgumentType.cs @@ -5,7 +5,7 @@ using Brigadier.NET.ArgumentTypes; using Brigadier.NET.Context; using Brigadier.NET.Exceptions; using Brigadier.NET.Suggestion; -using MinecraftClient.Mapping; +using MinecraftClient.EntityHandler; namespace MinecraftClient.CommandHandler.ArgumentType { 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) diff --git a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs index b5092251..6bd0bd56 100644 --- a/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs +++ b/MinecraftClient/CommandHandler/ArgumentType/PlayerNameArgumentType.cs @@ -4,7 +4,7 @@ using Brigadier.NET; using Brigadier.NET.ArgumentTypes; using Brigadier.NET.Context; using Brigadier.NET.Suggestion; -using MinecraftClient.Mapping; +using MinecraftClient.EntityHandler; namespace MinecraftClient.CommandHandler.ArgumentType { diff --git a/MinecraftClient/CommandHandler/MccArguments.cs b/MinecraftClient/CommandHandler/MccArguments.cs index c659ee3e..fa0f0a21 100644 --- a/MinecraftClient/CommandHandler/MccArguments.cs +++ b/MinecraftClient/CommandHandler/MccArguments.cs @@ -1,6 +1,7 @@ using System; using Brigadier.NET.Context; using MinecraftClient.CommandHandler.ArgumentType; +using MinecraftClient.EntityHandler; namespace MinecraftClient.CommandHandler { @@ -31,9 +32,9 @@ namespace MinecraftClient.CommandHandler return new EntityTypeArgumentType(); } - public static Mapping.EntityType GetEntityType(CommandContext context, string name) + public static EntityType GetEntityType(CommandContext context, string name) { - return context.GetArgument(name); + return context.GetArgument(name); } public static ItemTypeArgumentType ItemType() diff --git a/MinecraftClient/Commands/Animation.cs b/MinecraftClient/Commands/Animation.cs index da0204a2..5cad8e17 100644 --- a/MinecraftClient/Commands/Animation.cs +++ b/MinecraftClient/Commands/Animation.cs @@ -49,7 +49,7 @@ namespace MinecraftClient.Commands private static int DoAnimation(CmdResult r, bool mainhand) { McClient handler = CmdResult.currentHandler!; - return r.SetAndReturn(handler.DoAnimation(mainhand ? 1 : 0).Result); + return r.SetAndReturn(handler.DoAnimationAsync(mainhand ? 1 : 0).Result); } } } diff --git a/MinecraftClient/Commands/Bed.cs b/MinecraftClient/Commands/Bed.cs index ad2edfda..9c76438a 100644 --- a/MinecraftClient/Commands/Bed.cs +++ b/MinecraftClient/Commands/Bed.cs @@ -57,7 +57,7 @@ namespace MinecraftClient.Commands private static int DoLeaveBed(CmdResult r) { McClient handler = CmdResult.currentHandler!; - return r.SetAndReturn(Translations.cmd_bed_leaving, handler.SendEntityAction(Protocol.EntityActionType.LeaveBed).Result); + return r.SetAndReturn(Translations.cmd_bed_leaving, handler.SendEntityActionAsync(Protocol.EntityActionType.LeaveBed).Result); } private static int DoSleepBedWithRadius(CmdResult r, double radius) @@ -113,7 +113,7 @@ namespace MinecraftClient.Commands return r.SetAndReturn(Status.FailChunkNotLoad, string.Format(Translations.cmd_move_chunk_not_loaded, bedLocation.X, bedLocation.Y, bedLocation.Z)); - if (handler.MoveTo(bedLocation)) + if (handler.MoveToAsync(bedLocation).Result) { Task.Factory.StartNew(() => { @@ -137,7 +137,7 @@ namespace MinecraftClient.Commands handler.Log.Info(string.Format(Translations.cmd_bed_moving, bedLocation.X, bedLocation.Y, bedLocation.Z)); - bool res = handler.PlaceBlock(bedLocation, Direction.Down).Result; + bool res = handler.PlaceBlockAsync(bedLocation, Direction.Down).Result; handler.Log.Info(string.Format( Translations.cmd_bed_trying_to_use, @@ -174,7 +174,7 @@ namespace MinecraftClient.Commands blockCenter.X, blockCenter.Y, blockCenter.Z, - handler.PlaceBlock(block, Direction.Down).Result ? Translations.cmd_bed_in : Translations.cmd_bed_not_in + handler.PlaceBlockAsync(block, Direction.Down).Result ? Translations.cmd_bed_in : Translations.cmd_bed_not_in )); } } diff --git a/MinecraftClient/Commands/ChangeSlot.cs b/MinecraftClient/Commands/ChangeSlot.cs index 5fb76c67..20641cb6 100644 --- a/MinecraftClient/Commands/ChangeSlot.cs +++ b/MinecraftClient/Commands/ChangeSlot.cs @@ -44,7 +44,7 @@ namespace MinecraftClient.Commands if (!handler.GetInventoryEnabled()) return r.SetAndReturn(Status.FailNeedInventory); - if (handler.ChangeSlot((short)(slot - 1)).Result) + if (handler.ChangeSlotAsync((short)(slot - 1)).Result) 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/Dig.cs b/MinecraftClient/Commands/Dig.cs index 21287c39..6d0af7a0 100644 --- a/MinecraftClient/Commands/Dig.cs +++ b/MinecraftClient/Commands/Dig.cs @@ -54,7 +54,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).Result) + else if (handler.DigBlockAsync(blockToBreak).Result) { blockToBreak = blockToBreak.ToCenter(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dig_dig, blockToBreak.X, blockToBreak.Y, blockToBreak.Z, block.GetTypeString())); @@ -74,7 +74,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).Result) + else if (handler.DigBlockAsync(blockLoc, lookAtBlock: false).Result) 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 32294db1..92ed576b 100644 --- a/MinecraftClient/Commands/DropItem.cs +++ b/MinecraftClient/Commands/DropItem.cs @@ -58,7 +58,7 @@ namespace MinecraftClient.Commands var p = inventories[inventoryId]; int[] targetItems = p.SearchItem(itemType); foreach (int slot in targetItems) - handler.DoWindowAction(inventoryId, slot, WindowActionType.DropItemStack).Wait(); + handler.DoWindowActionAsync(inventoryId, slot, WindowActionType.DropItemStack).Wait(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_dropItem_dropped, Item.GetTypeString(itemType), inventoryId)); } diff --git a/MinecraftClient/Commands/Entitycmd.cs b/MinecraftClient/Commands/Entitycmd.cs index 43608d1e..b00b7b93 100644 --- a/MinecraftClient/Commands/Entitycmd.cs +++ b/MinecraftClient/Commands/Entitycmd.cs @@ -5,6 +5,7 @@ using System.Text; using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; +using MinecraftClient.EntityHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using static MinecraftClient.CommandHandler.CmdResult; @@ -166,12 +167,12 @@ namespace MinecraftClient.Commands { if (action == ActionType.Attack) { - handler.InteractEntity(entity2.Key, InteractType.Attack).Wait(); + handler.InteractEntityAsync(entity2.Key, InteractType.Attack).Wait(); actionst = Translations.cmd_entityCmd_attacked; } else if (action == ActionType.Use) { - handler.InteractEntity(entity2.Key, InteractType.Interact).Wait(); + handler.InteractEntityAsync(entity2.Key, InteractType.Interact).Wait(); actionst = Translations.cmd_entityCmd_used; } actioncount++; @@ -251,7 +252,7 @@ namespace MinecraftClient.Commands { 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) + else 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) { string? displayName = item.DisplayName; if (string.IsNullOrEmpty(displayName)) @@ -311,10 +312,10 @@ namespace MinecraftClient.Commands switch (action) { case ActionType.Attack: - handler.InteractEntity(entity.ID, InteractType.Attack).Wait(); + handler.InteractEntityAsync(entity.ID, InteractType.Attack).Wait(); return Translations.cmd_entityCmd_attacked; case ActionType.Use: - handler.InteractEntity(entity.ID, InteractType.Interact).Wait(); + handler.InteractEntityAsync(entity.ID, InteractType.Interact).Wait(); return Translations.cmd_entityCmd_used; case ActionType.List: return GetEntityInfoDetailed(handler, entity); diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index afc8652a..db5cea1f 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -159,7 +159,7 @@ namespace MinecraftClient.Commands if (handler.GetGamemode() == 1) { - if (handler.DoCreativeGive(slot, itemType, count, null).Result) + if (handler.DoCreativeGiveAsync(slot, itemType, count, null).Result) return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_creative_done, itemType, count, slot)); else return r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_inventory_creative_fail); @@ -178,7 +178,7 @@ namespace MinecraftClient.Commands if (handler.GetGamemode() == 1) { - if (handler.DoCreativeGive(slot, ItemType.Null, 0, null).Result) + if (handler.DoCreativeGiveAsync(slot, ItemType.Null, 0, null).Result) 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); @@ -279,7 +279,7 @@ namespace MinecraftClient.Commands if (inventory == null) return r.SetAndReturn(CmdResult.Status.Fail, string.Format(Translations.cmd_inventory_not_exist, inventoryId)); - if (handler.CloseInventory(inventoryId.Value).Result) + if (handler.CloseInventoryAsync(inventoryId.Value).Result) 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)); @@ -355,7 +355,7 @@ namespace MinecraftClient.Commands }; handler.Log.Info(string.Format(Translations.cmd_inventory_clicking, keyName, slot, inventoryId)); - var task = handler.DoWindowAction(inventoryId.Value, slot, actionType); + var task = handler.DoWindowActionAsync(inventoryId.Value, slot, actionType); task.Wait(); return r.SetAndReturn(task.Result); } @@ -381,7 +381,7 @@ namespace MinecraftClient.Commands 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).Result) + if (handler.DoWindowActionAsync(inventoryId.Value, slot, actionType).Result) { if (actionType == WindowActionType.DropItemStack) return r.SetAndReturn(CmdResult.Status.Done, string.Format(Translations.cmd_inventory_drop_stack, slot)); diff --git a/MinecraftClient/Commands/Move.cs b/MinecraftClient/Commands/Move.cs index fa94a06b..87ac60d1 100644 --- a/MinecraftClient/Commands/Move.cs +++ b/MinecraftClient/Commands/Move.cs @@ -145,7 +145,7 @@ namespace MinecraftClient.Commands 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); + handler.MoveToAsync(currentCenter, allowDirectTeleport: true).Wait(); return r.SetAndReturn(Status.Done, string.Format(Translations.cmd_move_walk, currentCenter, current)); } @@ -162,7 +162,7 @@ namespace MinecraftClient.Commands if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction)) { - if (handler.MoveTo(goal, allowUnsafe: takeRisk)) + if (handler.MoveToAsync(goal, allowUnsafe: takeRisk).Result) 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); @@ -188,8 +188,8 @@ namespace MinecraftClient.Commands if (takeRisk || Movement.PlayerFitsHere(handler.GetWorld(), goal)) { if (current.ToFloor() == goal.ToFloor()) - handler.MoveTo(goal, allowDirectTeleport: true); - else if (!handler.MoveTo(goal, allowUnsafe: takeRisk)) + handler.MoveToAsync(goal, allowDirectTeleport: true).Wait(); + else if (!handler.MoveToAsync(goal, allowUnsafe: takeRisk).Result) 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)); } diff --git a/MinecraftClient/Commands/Respawn.cs b/MinecraftClient/Commands/Respawn.cs index f0abd2c1..f1f6bf80 100644 --- a/MinecraftClient/Commands/Respawn.cs +++ b/MinecraftClient/Commands/Respawn.cs @@ -39,7 +39,7 @@ namespace MinecraftClient.Commands private int DoRespawn(CmdResult r) { McClient handler = CmdResult.currentHandler!; - handler.SendRespawnPacket().Wait(); + handler.SendRespawnPacketAsync().Wait(); return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_respawn_done); } } diff --git a/MinecraftClient/Commands/Script.cs b/MinecraftClient/Commands/Script.cs index d2f310dd..31336e45 100644 --- a/MinecraftClient/Commands/Script.cs +++ b/MinecraftClient/Commands/Script.cs @@ -41,7 +41,7 @@ namespace MinecraftClient.Commands private int DoExecuteScript(CmdResult r, string command, Dictionary? localVars) { McClient handler = CmdResult.currentHandler!; - handler.BotLoad(new ChatBots.Script(command.Trim(), null, localVars)); + handler.BotLoad(new ChatBots.Script(command.Trim(), null, localVars)).Wait(); return r.SetAndReturn(CmdResult.Status.Done); } } diff --git a/MinecraftClient/Commands/Send.cs b/MinecraftClient/Commands/Send.cs index cce606f5..9ec6bff5 100644 --- a/MinecraftClient/Commands/Send.cs +++ b/MinecraftClient/Commands/Send.cs @@ -37,7 +37,7 @@ namespace MinecraftClient.Commands private int DoSendText(CmdResult r, string command) { McClient handler = CmdResult.currentHandler!; - handler.SendText(command).Wait(); + handler.SendTextAsync(command).Wait(); return r.SetAndReturn(CmdResult.Status.Done); } } diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index 3082b99c..b98eeff7 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -42,7 +42,7 @@ namespace MinecraftClient.Commands McClient handler = CmdResult.currentHandler!; if (sneaking) { - var result = handler.SendEntityAction(Protocol.EntityActionType.StopSneaking).Result; + var result = handler.SendEntityActionAsync(Protocol.EntityActionType.StopSneaking).Result; if (result) sneaking = false; if (result) @@ -52,7 +52,7 @@ namespace MinecraftClient.Commands } else { - var result = handler.SendEntityAction(Protocol.EntityActionType.StartSneaking).Result; + var result = handler.SendEntityActionAsync(Protocol.EntityActionType.StartSneaking).Result; if (result) sneaking = true; if (result) diff --git a/MinecraftClient/Commands/UseItem.cs b/MinecraftClient/Commands/UseItem.cs index 9291298f..5c15b131 100644 --- a/MinecraftClient/Commands/UseItem.cs +++ b/MinecraftClient/Commands/UseItem.cs @@ -43,7 +43,7 @@ namespace MinecraftClient.Commands if (!handler.GetInventoryEnabled()) return r.SetAndReturn(Status.FailNeedInventory); - handler.UseItemOnHand().Wait(); + handler.UseItemOnHandAsync().Wait(); return r.SetAndReturn(Status.Done, Translations.cmd_useitem_use); } } diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index bc1f8b47..14b60cea 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -48,7 +48,7 @@ namespace MinecraftClient.Commands Location current = handler.GetCurrentLocation(); block = block.ToAbsolute(current).ToFloor(); Location blockCenter = block.ToCenter(); - bool res = handler.PlaceBlock(block, Direction.Down).Result; + bool res = handler.PlaceBlockAsync(block, Direction.Down).Result; return r.SetAndReturn(string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"), res); } } diff --git a/MinecraftClient/EntityHandler/Effect.cs b/MinecraftClient/EntityHandler/Effect.cs new file mode 100644 index 00000000..b6148102 --- /dev/null +++ b/MinecraftClient/EntityHandler/Effect.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinecraftClient.EntityHandler +{ + public record Effect + { + public EffectType Type { init; get; } + + public int EffectLevel { init; get; } + + public ulong StartTick { init; get; } + + public int DurationInTick { init; get; } + + public bool IsFromBeacon { init; get; } + + public bool ShowParticles { init; get; } + + public bool ShowIcon { init; get; } + + public Dictionary? FactorData { init; get; } = null; + + /* Factor Data + Name Type + padding_duration TAG_INT + factor_start TAG_FLOAT + factor_target TAG_FLOAT + factor_current TAG_FLOAT + effect_changed_timestamp TAG_INT + factor_previous_frame TAG_FLOAT + had_effect_last_tick TAG_BOOLEAN + */ + } +} diff --git a/MinecraftClient/Inventory/Effects.cs b/MinecraftClient/EntityHandler/EffectType.cs similarity index 92% rename from MinecraftClient/Inventory/Effects.cs rename to MinecraftClient/EntityHandler/EffectType.cs index 7b5df3bd..a58ff749 100644 --- a/MinecraftClient/Inventory/Effects.cs +++ b/MinecraftClient/EntityHandler/EffectType.cs @@ -1,9 +1,9 @@ -namespace MinecraftClient.Inventory +namespace MinecraftClient.EntityHandler { /// /// Represents a Minecraft effects /// - public enum Effects + public enum EffectType { Speed = 1, Slowness = 2, diff --git a/MinecraftClient/Mapping/Entity.cs b/MinecraftClient/EntityHandler/Entity.cs similarity index 98% rename from MinecraftClient/Mapping/Entity.cs rename to MinecraftClient/EntityHandler/Entity.cs index ad9fbf7b..9ac9b604 100644 --- a/MinecraftClient/Mapping/Entity.cs +++ b/MinecraftClient/EntityHandler/Entity.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using MinecraftClient.Inventory; +using MinecraftClient.Mapping; using MinecraftClient.Protocol.Message; -namespace MinecraftClient.Mapping +namespace MinecraftClient.EntityHandler { /// /// Represents an entity evolving into a Minecraft world diff --git a/MinecraftClient/Mapping/EntityPose.cs b/MinecraftClient/EntityHandler/EntityPose.cs similarity index 82% rename from MinecraftClient/Mapping/EntityPose.cs rename to MinecraftClient/EntityHandler/EntityPose.cs index 1690b089..f0666e14 100644 --- a/MinecraftClient/Mapping/EntityPose.cs +++ b/MinecraftClient/EntityHandler/EntityPose.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.EntityHandler { public enum EntityPose { diff --git a/MinecraftClient/Mapping/EntityType.cs b/MinecraftClient/EntityHandler/EntityType.cs similarity index 98% rename from MinecraftClient/Mapping/EntityType.cs rename to MinecraftClient/EntityHandler/EntityType.cs index ddb8a6b2..8a8d83ed 100644 --- a/MinecraftClient/Mapping/EntityType.cs +++ b/MinecraftClient/EntityHandler/EntityType.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.EntityHandler { /// /// Represents Minecraft Entity Types diff --git a/MinecraftClient/Mapping/EntityTypeExtensions.cs b/MinecraftClient/EntityHandler/EntityTypeExtensions.cs similarity index 99% rename from MinecraftClient/Mapping/EntityTypeExtensions.cs rename to MinecraftClient/EntityHandler/EntityTypeExtensions.cs index e6546d22..9476b4e3 100644 --- a/MinecraftClient/Mapping/EntityTypeExtensions.cs +++ b/MinecraftClient/EntityHandler/EntityTypeExtensions.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.EntityHandler { public static class EntityTypeExtensions { diff --git a/MinecraftClient/Mapping/InteractType.cs b/MinecraftClient/EntityHandler/InteractType.cs similarity index 72% rename from MinecraftClient/Mapping/InteractType.cs rename to MinecraftClient/EntityHandler/InteractType.cs index f54a1014..f7726433 100644 --- a/MinecraftClient/Mapping/InteractType.cs +++ b/MinecraftClient/EntityHandler/InteractType.cs @@ -1,4 +1,4 @@ -namespace MinecraftClient.Mapping +namespace MinecraftClient.EntityHandler { public enum InteractType { diff --git a/MinecraftClient/Inventory/ItemMovingHelper.cs b/MinecraftClient/Inventory/ItemMovingHelper.cs index 3381ed35..0d71ad9a 100644 --- a/MinecraftClient/Inventory/ItemMovingHelper.cs +++ b/MinecraftClient/Inventory/ItemMovingHelper.cs @@ -39,8 +39,8 @@ namespace MinecraftClient.Inventory if (ValidateSlots(source, dest, destContainer) && HasItem(source) && ((destContainer != null && !HasItem(dest, destContainer)) || (destContainer == null && !HasItem(dest)))) - return mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick).Result - && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick).Result; + return mc.DoWindowActionAsync(c.ID, source, WindowActionType.LeftClick).Result + && mc.DoWindowActionAsync(destContainer == null ? c.ID : destContainer.ID, dest, WindowActionType.LeftClick).Result; else return false; } @@ -57,9 +57,9 @@ namespace MinecraftClient.Inventory if (ValidateSlots(slot1, slot2, destContainer) && HasItem(slot1) && (destContainer != null && HasItem(slot2, destContainer) || (destContainer == null && HasItem(slot2)))) - return mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick).Result - && mc.DoWindowAction(destContainer == null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick).Result - && mc.DoWindowAction(c.ID, slot1, WindowActionType.LeftClick).Result; + return mc.DoWindowActionAsync(c.ID, slot1, WindowActionType.LeftClick).Result + && mc.DoWindowActionAsync(destContainer == null ? c.ID : destContainer.ID, slot2, WindowActionType.LeftClick).Result + && mc.DoWindowActionAsync(c.ID, slot1, WindowActionType.LeftClick).Result; else return false; } @@ -104,14 +104,14 @@ namespace MinecraftClient.Inventory break; } } - mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick).Wait(); // grab item - mc.DoWindowAction(c.ID, -999, startDragging).Wait(); + mc.DoWindowActionAsync(c.ID, source, WindowActionType.LeftClick).Wait(); // grab item + mc.DoWindowActionAsync(c.ID, -999, startDragging).Wait(); foreach (var slot in availableSlots) { - mc.DoWindowAction(c.ID, slot, addDragging).Wait(); + mc.DoWindowActionAsync(c.ID, slot, addDragging).Wait(); } - mc.DoWindowAction(c.ID, -999, endDragging).Wait(); - mc.DoWindowAction(c.ID, source, WindowActionType.LeftClick).Wait(); // put down item left (if any) + mc.DoWindowActionAsync(c.ID, -999, endDragging).Wait(); + mc.DoWindowActionAsync(c.ID, source, WindowActionType.LeftClick).Wait(); // put down item left (if any) return true; } else return false; diff --git a/MinecraftClient/Logger/LoggerBase.cs b/MinecraftClient/Logger/LoggerBase.cs index 8569bfc9..2a655db4 100644 --- a/MinecraftClient/Logger/LoggerBase.cs +++ b/MinecraftClient/Logger/LoggerBase.cs @@ -78,17 +78,17 @@ protected virtual void Log(object msg) { - ConsoleIO.WriteLineFormatted(msg.ToString() ?? string.Empty); + ConsoleIO.WriteLineFormatted(msg.ToString() ?? string.Empty, true); } protected virtual void Log(string msg) { - ConsoleIO.WriteLineFormatted(msg); + ConsoleIO.WriteLineFormatted(msg, true); } protected virtual void Log(string msg, params object[] args) { - ConsoleIO.WriteLineFormatted(string.Format(msg, args)); + ConsoleIO.WriteLineFormatted(string.Format(msg, args), true); } } } diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs index d1e30c16..b08d4bd2 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs index 15d0dcd4..185e0d55 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette112.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs index d78a7240..37bfae10 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette113.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs index 403fcc00..dec2cc9f 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette114.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs index bd4887be..7467eee7 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette115.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1161.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1161.cs index 4de8bdca..1ef5f4ab 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1161.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1161.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1162.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1162.cs index 606e2d94..7ee9036c 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette1162.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette1162.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette117.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette117.cs index 1658aa33..2d9f9d97 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette117.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette117.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/EntityPalettes/EntityPalette119.cs b/MinecraftClient/Mapping/EntityPalettes/EntityPalette119.cs index f4e352d5..4e11e540 100644 --- a/MinecraftClient/Mapping/EntityPalettes/EntityPalette119.cs +++ b/MinecraftClient/Mapping/EntityPalettes/EntityPalette119.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping.EntityPalettes { diff --git a/MinecraftClient/Mapping/MapData.cs b/MinecraftClient/Mapping/MapData.cs new file mode 100644 index 00000000..edcec1b2 --- /dev/null +++ b/MinecraftClient/Mapping/MapData.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinecraftClient.Mapping +{ + public record MapData + { + public int MapId { init; get; } + + public byte Scale { init; get; } + + public bool TrackingPosition { init; get; } + + public bool Locked { init; get; } + + public List Icons { init; get; } = new(); + + public byte ColumnsUpdated { init; get; } + + public byte RowsUpdated { init; get; } + + public byte MapCoulmnX { init; get; } + + public byte MapRowZ { init; get; } + + public byte[]? Colors { init; get; } + } +} diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs index 2f74cfa6..ba50a89e 100644 --- a/MinecraftClient/Mapping/Movement.cs +++ b/MinecraftClient/Mapping/Movement.cs @@ -140,17 +140,10 @@ namespace MinecraftClient.Mapping /// How long to wait before stopping computation /// When location is unreachable, computation will reach timeout, then optionally fallback to a close location within maxOffset /// A list of locations, or null if calculation failed - public static Queue? CalculatePath(World world, Location start, Location goal, bool allowUnsafe, int maxOffset, int minOffset, TimeSpan timeout) + public static async Task?> CalculatePath(World world, Location start, Location goal, bool allowUnsafe, int maxOffset, int minOffset, TimeSpan timeout) { - CancellationTokenSource cts = new(); - Task?> pathfindingTask = Task.Factory.StartNew(() => Movement.CalculatePath(world, start, goal, allowUnsafe, maxOffset, minOffset, cts.Token)); - pathfindingTask.Wait(timeout); - if (!pathfindingTask.IsCompleted) - { - cts.Cancel(); - pathfindingTask.Wait(); - } - return pathfindingTask.Result; + CancellationTokenSource cts = new(timeout); + return await Task.Run(() => { return CalculatePath(world, start, goal, allowUnsafe, maxOffset, minOffset, cts.Token); }, cts.Token); } /// diff --git a/MinecraftClient/Mapping/RaycastHelper.cs b/MinecraftClient/Mapping/RaycastHelper.cs index 483b0b86..a2e98d9f 100644 --- a/MinecraftClient/Mapping/RaycastHelper.cs +++ b/MinecraftClient/Mapping/RaycastHelper.cs @@ -1,4 +1,5 @@ using System; +using MinecraftClient.EntityHandler; namespace MinecraftClient.Mapping { diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 683e3c00..844991e7 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -14,6 +14,7 @@ using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; using MinecraftClient.Inventory; using MinecraftClient.Logger; +using MinecraftClient.EntityHandler; using MinecraftClient.Mapping; using MinecraftClient.Protocol; using MinecraftClient.Protocol.Handlers.Forge; @@ -34,15 +35,14 @@ namespace MinecraftClient public static int ReconnectionAttemptsLeft = 0; public static CommandDispatcher dispatcher = new(); + private readonly Dictionary onlinePlayers = new(); - private static bool commandsLoaded = false; - private readonly ConcurrentQueue chatQueue = new(); - private static DateTime nextMessageSendTime = DateTime.MinValue; + private DateTime nextMessageSendTime = DateTime.MinValue; - private static readonly object inventoryLock = new(); - private static readonly Dictionary inventories = new(); + private readonly object inventoryLock = new(); + private readonly Dictionary inventories = new(); private readonly List registeredServerPluginChannels = new(); private readonly Dictionary> registeredBotPluginChannels = new(); @@ -53,7 +53,7 @@ namespace MinecraftClient private bool inventoryHandlingRequested = false; private bool entityHandlingEnabled; - private readonly object locationLock = new(); + private static SemaphoreSlim locationLock = new(1, 1); private bool locationReceived = false; private readonly World world = new(); private Queue? steps; @@ -108,6 +108,15 @@ namespace MinecraftClient private bool OldChatBotUpdateTrigger = false; private bool networkPacketCaptureEnabled = false; + // ChatBot Async Events + private static int EventTypeCount = typeof(McClientEventType).GetFields().Length; + private static SemaphoreSlim EventCallbackWriteLock = new(1, 1); + private static Task[][] ChatbotEventTasks = new Task[EventTypeCount][]; + private static Task[] WaitChatbotExecuteTask = new Task[EventTypeCount]; + private static SemaphoreSlim[] ChatbotEventTaskLocks = new SemaphoreSlim[EventTypeCount]; + private static Func[][] ChatbotEvents = new Func[EventTypeCount][]; + private static Dictionary>>> ChatbotRegisteredEvents = new(); + public int GetServerPort() { return port; } public string GetServerHost() { return host; } public string GetUsername() { return username; } @@ -141,6 +150,12 @@ namespace MinecraftClient public static void LoadCommandsAndChatbots() { + for (int i = 0; i < EventTypeCount; ++i) + { + ChatbotEventTaskLocks[i] = new(1, 1); + WaitChatbotExecuteTask[i] = Task.CompletedTask; + } + /* Load commands from the 'Commands' namespace */ Type[] cmds_classes = Program.GetTypesInNamespace("MinecraftClient.Commands"); foreach (Type type in cmds_classes) @@ -153,9 +168,11 @@ namespace MinecraftClient } /* Load ChatBots */ - botsOnHold = GetBotsToRegister(); + botsOnHold = GetChatbotsToRegister(); foreach (ChatBot bot in botsOnHold) bot.Initialize(); + + InitializeChatbotEventCallbacks(botsOnHold).Wait(); } /// @@ -194,6 +211,13 @@ namespace MinecraftClient Log.ChatEnabled = Config.Logging.ChatMessages; Log.WarnEnabled = Config.Logging.WarningMessages; Log.ErrorEnabled = Config.Logging.ErrorMessages; + + ClearInventories(); + + chatbots = botsOnHold; + botsOnHold = Array.Empty(); + foreach (ChatBot bot in chatbots) + bot.SetHandler(this); } public async Task Login(HttpClient httpClient, SessionToken session, PlayerKeyPair? playerKeyPair, int protocolversion, ForgeInfo? forgeInfo) @@ -222,14 +246,8 @@ namespace MinecraftClient { if (await handler.Login(httpClient, this.playerKeyPair, session)) { - chatbots = botsOnHold; - botsOnHold = Array.Empty(); - foreach (ChatBot bot in chatbots) - { - bot.SetHandler(this); - bot.AfterGameJoined(); - } - + DispatchBotEvent(bot => bot.AfterGameJoined()); + await TriggerEvent(McClientEventType.GameJoin, null); return; } @@ -274,9 +292,9 @@ namespace MinecraftClient await handler!.StartUpdating(); - ConsoleInteractive.ConsoleReader.StopReadThread(); - ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; ConsoleInteractive.ConsoleReader.MessageReceived -= ConsoleReaderOnMessageReceived; + ConsoleInteractive.ConsoleReader.OnInputChange -= ConsoleIO.AutocompleteHandler; + ConsoleInteractive.ConsoleReader.StopReadThread(); ConsoleIO.CancelAutocomplete(); ConsoleIO.WriteLine(string.Empty); @@ -285,7 +303,7 @@ namespace MinecraftClient /// /// Register bots /// - private static ChatBot[] GetBotsToRegister(bool reload = false) + private static ChatBot[] GetChatbotsToRegister(bool reload = false) { List chatbotList = new(); @@ -312,7 +330,8 @@ namespace MinecraftClient if (Config.ChatBot.ScriptScheduler.Enabled) { chatbotList.Add(new ScriptScheduler()); } if (Config.ChatBot.TelegramBridge.Enabled) { chatbotList.Add(new TelegramBridge()); } // Add your ChatBot here by uncommenting and adapting - // BotLoad(new ChatBots.YourBot()); + // chatbotList.Add(new ChatBots.YourBot()); + chatbotList.Add(new TestBot()); return chatbotList.ToArray(); } @@ -351,10 +370,7 @@ namespace MinecraftClient } catch (Exception e) { - if (e is not ThreadAbortException) - Log.Warn("Update: Got error from " + bot.ToString() + ": " + e.ToString()); - else - throw; //ThreadAbortException should not be caught + Log.Warn("Update: Got error from " + bot.ToString() + ": " + e.ToString()); } } } @@ -365,30 +381,29 @@ namespace MinecraftClient { for (int i = 0; i < Config.Main.Advanced.MovementSpeed / 2; i++) //Needs to run at 20 tps; MCC runs at 10 tps { - lock (locationLock) + await locationLock.WaitAsync(); + if (_yaw == null || _pitch == null) { - if (_yaw == null || _pitch == null) + if (steps != null && steps.Count > 0) { - if (steps != null && steps.Count > 0) - { - location = steps.Dequeue(); - } - else if (path != null && path.Count > 0) - { - Location next = path.Dequeue(); - steps = Movement.Move2Steps(location, next, ref motionY); - - if (Config.Main.Advanced.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers - UpdateLocation(location, next + new Location(0, 1, 0)); // Update yaw and pitch to look at next step - } - else - { - location = Movement.HandleGravity(world, location, ref motionY); - } + location = steps.Dequeue(); + } + else if (path != null && path.Count > 0) + { + Location next = path.Dequeue(); + steps = Movement.Move2Steps(location, next, ref motionY); + + if (Config.Main.Advanced.MoveHeadWhileWalking) // Disable head movements to avoid anti-cheat triggers + UpdateLocation(location, next + new Location(0, 1, 0)); // Update yaw and pitch to look at next step + } + else + { + location = Movement.HandleGravity(world, location, ref motionY); } - playerYaw = _yaw == null ? playerYaw : _yaw.Value; - playerPitch = _pitch == null ? playerPitch : _pitch.Value; } + playerYaw = _yaw == null ? playerYaw : _yaw.Value; + playerPitch = _pitch == null ? playerPitch : _pitch.Value; + locationLock.Release(); await handler!.SendLocationUpdate(location, Movement.IsOnGround(world, location), _yaw, _pitch); } // First 2 updates must be player position AND look, and player must not move (to conform with vanilla) @@ -400,8 +415,10 @@ namespace MinecraftClient if (Config.Main.Advanced.AutoRespawn && respawnTicks > 0) { if (--respawnTicks == 0) - await SendRespawnPacket(); + await SendRespawnPacketAsync(); } + + await TriggerEvent(McClientEventType.ClientTick, null); } #region Connection Lost and Disconnect from Server @@ -445,8 +462,20 @@ namespace MinecraftClient /// public void Disconnect() { + for (int i = 0; i < EventTypeCount; ++i) + { + ChatbotEventTaskLocks[i].Wait(); + WaitChatbotExecuteTask[i].Wait(); + ChatbotEventTaskLocks[i].Release(); + } + DispatchBotEvent(bot => bot.OnDisconnect(ChatBot.DisconnectReason.UserLogout, string.Empty)); + TriggerEvent(McClientEventType.ClientDisconnect, + new Tuple(ChatBot.DisconnectReason.UserLogout, string.Empty)).Wait(); + + WaitChatbotExecuteTask[(int)McClientEventType.ClientDisconnect].Wait(); + botsOnHold = chatbots; chatbots = Array.Empty(); @@ -518,16 +547,149 @@ namespace MinecraftClient #endregion + #region ChatBot event callback + + private async Task TriggerEvent(McClientEventType eventType, object? parameter) + { + int eventId = (int)eventType; + Func[] eventList = ChatbotEvents[eventId]; + if (eventList.Length > 0) + { + await ChatbotEventTaskLocks[eventId].WaitAsync(); + await WaitChatbotExecuteTask[eventId]; + for (int i = 0; i < eventList.Length; ++i) + ChatbotEventTasks[eventId][i] = eventList[i](parameter); + WaitChatbotExecuteTask[eventId] = WaitTaskAndHandleException(eventType); + ChatbotEventTaskLocks[eventId].Release(); + } + } + + private async Task WaitTaskAndHandleException(McClientEventType eventType) + { + Task[] taskList = ChatbotEventTasks[(int)eventType]; + for (int i = 0; i < taskList.Length; ++i) + { + try + { + await taskList[i]; + } + catch (Exception exception) + { + Log.Error(string.Format(Translations.mcc_chatbot_event_exception, eventType.ToString(), exception.ToString())); + } + } + } + + private static async Task InitializeChatbotEventCallbacks(IEnumerable chatbotList) + { + List>[] tmpCallbackList = new List>[EventTypeCount]; + for (int i = 0; i < EventTypeCount; ++i) + tmpCallbackList[i] = new(); + + foreach (ChatBot bot in chatbotList) + { + Tuple>[]? botEvents = bot.InitializeEventCallbacks(); + if (botEvents != null) + { + ChatbotRegisteredEvents[bot] = new(botEvents); + foreach ((McClientEventType eventType, Func callback) in botEvents) + tmpCallbackList[(int)eventType].Add(callback); + } + else + { + ChatbotRegisteredEvents[bot] = new(); + } + } + + await EventCallbackWriteLock.WaitAsync(); + for (int i = 0; i < EventTypeCount; ++i) + { + ChatbotEvents[i] = tmpCallbackList[i].ToArray(); + await UpdateChatbotEventTasksArray(i); + } + EventCallbackWriteLock.Release(); + } + + /// + /// + /// + /// + /// + /// + /// + public static async Task RegisterEventCallback(ChatBot bot, McClientEventType eventType, Func callback) + { + int eventId = (int)eventType; + await EventCallbackWriteLock.WaitAsync(); + + ChatbotEvents[eventId] = new List>(ChatbotEvents[eventId]) { callback }.ToArray(); + if (ChatbotRegisteredEvents.TryGetValue(bot, out var botEvents)) + botEvents.Add(new(eventType, callback)); + else + ChatbotRegisteredEvents[bot] = new() { new(eventType, callback) }; + await UpdateChatbotEventTasksArray(eventId); + EventCallbackWriteLock.Release(); + } + + /// + /// + /// + /// + /// + /// + /// + public static async Task UnregisterEventCallback(ChatBot bot, McClientEventType eventType, Func callback) + { + int eventId = (int)eventType; + await EventCallbackWriteLock.WaitAsync(); + + List> newList = new(ChatbotEvents[eventId]); + newList.RemoveAll(c => c == callback); + ChatbotEvents[eventId] = newList.ToArray(); + if (ChatbotRegisteredEvents.TryGetValue(bot, out var botEvents)) + botEvents.RemoveAll(c => c.Item1 == eventType && c.Item2 == callback); + await UpdateChatbotEventTasksArray(eventId); + EventCallbackWriteLock.Release(); + } + + public static async Task UnregisterChatbotEventCallback(ChatBot bot) + { + await EventCallbackWriteLock.WaitAsync(); + if (ChatbotRegisteredEvents.TryGetValue(bot, out var botEvents)) + { + foreach ((McClientEventType eventType, Func callback) in botEvents) + { + int eventId = (int)eventType; + List> newList = new(ChatbotEvents[eventId]); + newList.RemoveAll(c => c == callback); + ChatbotEvents[eventId] = newList.ToArray(); + await UpdateChatbotEventTasksArray(eventId); + } + ChatbotRegisteredEvents.Remove(bot); + } + EventCallbackWriteLock.Release(); + } + + private static async Task UpdateChatbotEventTasksArray(int eventId) + { + await ChatbotEventTaskLocks[eventId].WaitAsync(); + await WaitChatbotExecuteTask[eventId]; + ChatbotEventTasks[eventId] = new Task[ChatbotEvents[eventId].Length]; + ChatbotEventTaskLocks[eventId].Release(); + } + + #endregion + #region Command prompt and internal MCC commands - private void ConsoleReaderOnMessageReceived(object? sender, string e) + private void ConsoleReaderOnMessageReceived(object? sender, string text) { if (tcpClient!.Client == null) return; if (tcpClient.Client.Connected) - Task.Run(async () => { await HandleCommandPromptText(e); }); + Task.Run(async () => { await HandleCommandPromptText(text); }); else return; } @@ -560,14 +722,14 @@ namespace MinecraftClient && Config.Main.Advanced.InternalCmdChar == MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none && text[0] == '/') { - await SendText(text); + await SendTextAsync(text); } else if (text.Length > 2 && Config.Main.Advanced.InternalCmdChar != MainConfigHealper.MainConfig.AdvancedConfig.InternalCmdCharType.none && text[0] == Config.Main.Advanced.InternalCmdChar.ToChar() && text[1] == '/') { - await SendText(text[1..]); + await SendTextAsync(text[1..]); } else if (text.Length > 0) { @@ -578,7 +740,7 @@ namespace MinecraftClient string command = Config.Main.Advanced.InternalCmdChar.ToChar() == ' ' ? text : text[1..]; if (!PerformInternalCommand(Config.AppVar.ExpandVars(command), ref result, Settings.Config.AppVar.GetVariables()) && Config.Main.Advanced.InternalCmdChar.ToChar() == '/') { - await SendText(text); + await SendTextAsync(text); } else if (result.status != CmdResult.Status.NotRun && (result.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(result.result))) { @@ -587,7 +749,7 @@ namespace MinecraftClient } else { - await SendText(text); + await SendTextAsync(text); } } } @@ -668,14 +830,20 @@ namespace MinecraftClient { await UnloadAllBots(); - ChatBot[] bots = GetBotsToRegister(true); + ChatBot[] bots = GetChatbotsToRegister(true); + foreach (ChatBot bot in bots) { bot.SetHandler(this); bot.Initialize(); - if (handler != null) - bot.AfterGameJoined(); } + + await InitializeChatbotEventCallbacks(bots); + + if (handler != null) + foreach (ChatBot bot in bots) + bot.AfterGameJoined(); + chatbots = bots; } @@ -688,6 +856,11 @@ namespace MinecraftClient bot.OnUnload(); chatbots = Array.Empty(); registeredBotPluginChannels.Clear(); + + for (int i = 0; i < ChatbotEvents.Length; ++i) + ChatbotEvents[i] = Array.Empty>(); + ChatbotRegisteredEvents.Clear(); + await Task.CompletedTask; } @@ -698,12 +871,15 @@ namespace MinecraftClient /// /// Load a new bot /// - public void BotLoad(ChatBot bot, bool init = true) + public async Task BotLoad(ChatBot bot, bool init = true) { bot.SetHandler(this); chatbots = new List(chatbots) { bot }.ToArray(); if (init) + { bot.Initialize(); + await InitializeChatbotEventCallbacks(new ChatBot[] { bot }); + } if (handler != null) bot.AfterGameJoined(); } @@ -711,21 +887,22 @@ namespace MinecraftClient /// /// Unload a bot /// - public async Task BotUnLoad(ChatBot b) + public async Task BotUnLoad(ChatBot bot) { - b.OnUnload(); - List botList = new(); - botList.AddRange(from bot in chatbots - where !ReferenceEquals(bot, b) - select bot); + botList.AddRange(from botInList in chatbots + where !ReferenceEquals(botInList, bot) + select botInList); chatbots = botList.ToArray(); + bot.OnUnload(); + await UnregisterChatbotEventCallback(bot); + // 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(); + var botRegistrations = registeredBotPluginChannels.Where(entry => entry.Value.Contains(bot)).ToList(); foreach (var entry in botRegistrations) { - await UnregisterPluginChannel(entry.Key, b); + await UnregisterPluginChannelAsync(entry.Key, bot); } } @@ -992,23 +1169,25 @@ namespace MinecraftClient /// How long to wait until the path is evaluated (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 - public bool MoveTo(Location goal, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null) + public async Task MoveToAsync(Location goal, bool allowUnsafe = false, bool allowDirectTeleport = false, int maxOffset = 0, int minOffset = 0, TimeSpan? timeout = null) { - lock (locationLock) + if (handler == null) + return false; + + if (allowDirectTeleport) { - if (allowDirectTeleport) - { - // 1-step path to the desired location without checking anything - UpdateLocation(goal, goal); // Update yaw and pitch to look at next step - handler!.SendLocationUpdate(goal, Movement.IsOnGround(world, goal), _yaw, _pitch); - return true; - } - else - { - // Calculate path through pathfinding. Path contains a list of 1-block movement that will be divided into steps - path = Movement.CalculatePath(world, location, goal, allowUnsafe, maxOffset, minOffset, timeout ?? TimeSpan.FromSeconds(5)); - return path != null; - } + await locationLock.WaitAsync(); + // 1-step path to the desired location without checking anything + UpdateLocation(goal, goal); // Update yaw and pitch to look at next step + await handler.SendLocationUpdate(goal, Movement.IsOnGround(world, goal), _yaw, _pitch); + locationLock.Release(); + return true; + } + else + { + // Calculate path through pathfinding. Path contains a list of 1-block movement that will be divided into steps + path = await Movement.CalculatePath(world, location, goal, allowUnsafe, maxOffset, minOffset, timeout ?? TimeSpan.FromSeconds(5)); + return path != null; } } @@ -1016,8 +1195,11 @@ namespace MinecraftClient /// Send a chat message or command to the server /// /// Text to send to the server - public async Task SendText(string text) + public async Task SendTextAsync(string text) { + if (handler == null) + return; + if (string.IsNullOrEmpty(text)) return; @@ -1039,11 +1221,14 @@ namespace MinecraftClient chatQueue.Enqueue(text[..maxLength]); text = text[maxLength..]; } - chatQueue.Enqueue(text); + if (!string.IsNullOrEmpty(text)) + chatQueue.Enqueue(text); } } else + { chatQueue.Enqueue(text); + } await TrySendMessageToServer(); } @@ -1052,9 +1237,11 @@ namespace MinecraftClient /// Allow to respawn after death /// /// True if packet successfully sent - public async Task SendRespawnPacket() + public async Task SendRespawnPacketAsync() { - return await handler!.SendRespawnPacket(); + if (handler == null) + return false; + return await handler.SendRespawnPacket(); } /// @@ -1062,7 +1249,7 @@ namespace MinecraftClient /// /// The channel to register. /// The bot to register the channel for. - public async Task RegisterPluginChannel(string channel, ChatBot bot) + public async Task RegisterPluginChannelAsync(string channel, ChatBot bot) { if (registeredBotPluginChannels.TryGetValue(channel, out List? channelList)) { @@ -1070,12 +1257,9 @@ namespace MinecraftClient } else { - List bots = new() - { - bot - }; + List bots = new() { bot }; registeredBotPluginChannels[channel] = bots; - await SendPluginChannelMessage("REGISTER", Encoding.UTF8.GetBytes(channel), true); + await SendPluginChannelMessageAsync("REGISTER", Encoding.UTF8.GetBytes(channel), true); } } @@ -1084,7 +1268,7 @@ namespace MinecraftClient /// /// The channel to unregister. /// The bot to unregister the channel for. - public async Task UnregisterPluginChannel(string channel, ChatBot bot) + public async Task UnregisterPluginChannelAsync(string channel, ChatBot bot) { if (registeredBotPluginChannels.TryGetValue(channel, out List? channelList)) { @@ -1093,7 +1277,7 @@ namespace MinecraftClient if (registeredBots.Count == 0) { registeredBotPluginChannels.Remove(channel); - await SendPluginChannelMessage("UNREGISTER", Encoding.UTF8.GetBytes(channel), true); + await SendPluginChannelMessageAsync("UNREGISTER", Encoding.UTF8.GetBytes(channel), true); } } } @@ -1106,8 +1290,11 @@ namespace MinecraftClient /// The payload for the packet. /// Whether the packet should be sent even if the server or the client hasn't registered it yet. /// Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered. - public async Task SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false) + public async Task SendPluginChannelMessageAsync(string channel, byte[] data, bool sendEvenIfNotRegistered = false) { + if (handler == null) + return false; + if (!sendEvenIfNotRegistered) { if (!registeredBotPluginChannels.ContainsKey(channel)) @@ -1119,34 +1306,40 @@ namespace MinecraftClient return false; } } - return await handler!.SendPluginChannelPacket(channel, data); + return await handler.SendPluginChannelPacket(channel, data); } /// /// Send the Entity Action packet with the Specified ID /// /// TRUE if the item was successfully used - public async Task SendEntityAction(EntityActionType entityAction) + public async Task SendEntityActionAsync(EntityActionType entityAction) { - return await handler!.SendEntityAction(playerEntityID, (int)entityAction); + if (handler == null) + return false; + return await handler.SendEntityAction(playerEntityID, (int)entityAction); } /// /// Use the item currently in the player's hand /// /// TRUE if the item was successfully used - public async Task UseItemOnHand() + public async Task UseItemOnHandAsync() { - return await handler!.SendUseItem(0, sequenceId); + if (handler == null) + return false; + return await handler.SendUseItem(0, sequenceId); } /// /// Use the item currently in the player's left hand /// /// TRUE if the item was successfully used - public async Task UseItemOnLeftHand() + public async Task UseItemOnOffHandAsync() { - return await handler!.SendUseItem(1, sequenceId); + if (handler == null) + return false; + return await handler.SendUseItem(1, sequenceId); } /// @@ -1210,8 +1403,11 @@ namespace MinecraftClient /// Click a slot in the specified window /// /// TRUE if the slot was successfully clicked - public async Task DoWindowAction(int windowId, int slotId, WindowActionType action) + public async Task DoWindowActionAsync(int windowId, int slotId, WindowActionType action) { + if (handler == null) + return false; + Item? item = null; List> changedSlots = new(); // List lock (inventoryLock) @@ -1941,9 +2137,11 @@ namespace MinecraftClient /// Item count /// Item NBT /// TRUE if item given successfully - public async Task DoCreativeGive(int slot, ItemType itemType, int count, Dictionary? nbt = null) + public async Task DoCreativeGiveAsync(int slot, ItemType itemType, int count, Dictionary? nbt = null) { - return await handler!.SendCreativeInventoryAction(slot, itemType, count, nbt); + if (handler == null) + return false; + return await handler.SendCreativeInventoryAction(slot, itemType, count, nbt); } /// @@ -1951,9 +2149,11 @@ namespace MinecraftClient /// /// 0 for left arm, 1 for right arm /// TRUE if animation successfully done - public async Task DoAnimation(int animation) + public async Task DoAnimationAsync(int animation) { - return await handler!.SendAnimation(animation, playerEntityID); + if (handler == null) + return false; + return await handler.SendAnimation(animation, playerEntityID); } /// @@ -1962,8 +2162,11 @@ namespace MinecraftClient /// Window ID /// TRUE if the window was successfully closed /// Sending close window for inventory 0 can cause server to update our inventory if there are any item in the crafting area - public async Task CloseInventory(int windowId) + public async Task CloseInventoryAsync(int windowId) { + if (handler == null) + return false; + bool needCloseWindow = false; lock (inventoryLock) { @@ -1975,7 +2178,7 @@ namespace MinecraftClient } } if (needCloseWindow) - return await handler!.SendCloseWindow(windowId); + return await handler.SendCloseWindow(windowId); else return false; } @@ -1992,7 +2195,7 @@ namespace MinecraftClient lock (inventoryLock) { inventories.Clear(); - inventories[0] = new Container(0, ContainerType.PlayerInventory, "Player Inventory"); + inventories[0] = new Container(0, ContainerType.PlayerInventory, Translations.cmd_inventory_player_inventory); } return true; } @@ -2004,17 +2207,20 @@ namespace MinecraftClient /// Type of interaction (interact, attack...) /// Hand.MainHand or Hand.OffHand /// TRUE if interaction succeeded - public async Task InteractEntity(int entityID, InteractType type, Hand hand = Hand.MainHand) + public async Task InteractEntityAsync(int entityID, InteractType type, Hand hand = Hand.MainHand) { + if (handler == null) + return false; + if (entities.ContainsKey(entityID)) { if (type == InteractType.Interact) { - return await handler!.SendInteractEntity(entityID, (int)type, (int)hand); + return await handler.SendInteractEntity(entityID, (int)type, (int)hand); } else { - return await handler!.SendInteractEntity(entityID, (int)type); + return await handler.SendInteractEntity(entityID, (int)type); } } else return false; @@ -2026,9 +2232,11 @@ namespace MinecraftClient /// Location to place block to /// Block face (e.g. Direction.Down when clicking on the block below to place this block) /// TRUE if successfully placed - public async Task PlaceBlock(Location location, Direction blockFace, Hand hand = Hand.MainHand) + public async Task PlaceBlockAsync(Location location, Direction blockFace, Hand hand = Hand.MainHand) { - return await handler!.SendPlayerBlockPlacement((int)hand, location, blockFace, sequenceId); + if (handler == null) + return false; + return await handler.SendPlayerBlockPlacement((int)hand, location, blockFace, sequenceId); } /// @@ -2037,8 +2245,11 @@ namespace MinecraftClient /// Location of block to dig /// Also perform the "arm swing" animation /// Also look at the block before digging - public async Task DigBlock(Location location, bool swingArms = true, bool lookAtBlock = true) + public async Task DigBlockAsync(Location location, bool swingArms = true, bool lookAtBlock = true) { + if (handler == null) + return false; + if (!GetTerrainEnabled()) return false; @@ -2051,9 +2262,9 @@ namespace MinecraftClient // 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 (await handler!.SendPlayerDigging(0, location, blockFace, sequenceId)) - && (!swingArms || await DoAnimation((int)Hand.MainHand)) - && await handler!.SendPlayerDigging(2, location, blockFace, sequenceId); + return (await handler.SendPlayerDigging(0, location, blockFace, sequenceId)) + && (!swingArms || await DoAnimationAsync((int)Hand.MainHand)) + && await handler.SendPlayerDigging(2, location, blockFace, sequenceId); } /// @@ -2061,13 +2272,16 @@ namespace MinecraftClient /// /// Slot to activate (0 to 8) /// TRUE if the slot was changed - public async Task ChangeSlot(short slot) + public async Task ChangeSlotAsync(short slot) { + if (handler == null) + return false; + if (slot < 0 || slot > 8) return false; CurrentSlot = Convert.ToByte(slot); - return await handler!.SendHeldItemChange(slot); + return await handler.SendHeldItemChange(slot); } /// @@ -2078,19 +2292,23 @@ namespace MinecraftClient /// text two /// text three /// text1 four - public async Task UpdateSign(Location location, string line1, string line2, string line3, string line4) + public async Task UpdateSignAsync(Location location, string line1, string line2, string line3, string line4) { + if (handler == null) + return false; // TODO Open sign editor first https://wiki.vg/Protocol#Open_Sign_Editor - return await handler!.SendUpdateSign(location, line1, line2, line3, line4); + return await handler.SendUpdateSign(location, line1, line2, line3, line4); } /// /// Select villager trade /// /// The slot of the trade, starts at 0. - public async Task SelectTrade(int selectedSlot) + public async Task SelectTradeAsync(int selectedSlot) { - return await handler!.SelectTrade(selectedSlot); + if (handler == null) + return false; + return await handler.SelectTrade(selectedSlot); } /// @@ -2100,9 +2318,11 @@ namespace MinecraftClient /// command /// command block mode /// command block flags - public async Task UpdateCommandBlock(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags) + public async Task UpdateCommandBlockAsync(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags) { - return await handler!.UpdateCommandBlock(location, command, mode, flags); + if (handler == null) + return false; + return await handler.UpdateCommandBlock(location, command, mode, flags); } /// @@ -2110,11 +2330,11 @@ namespace MinecraftClient /// /// Player to teleport to /// Teleporting to other entityies is NOT implemented yet - public async Task Spectate(Entity entity) + public async Task SpectateAsync(Entity entity) { if (entity.Type == EntityType.Player) { - return await SpectateByUUID(entity.UUID); + return await SpectateByUuidAsync(entity.UUID); } else { @@ -2126,11 +2346,14 @@ namespace MinecraftClient /// Teleport to player/entity in spectator mode /// /// UUID of player/entity to teleport to - public async Task SpectateByUUID(Guid UUID) + public async Task SpectateByUuidAsync(Guid UUID) { + if (handler == null) + return false; + if (GetGamemode() == 3) { - return await handler!.SendSpectate(UUID); + return await handler.SendSpectate(UUID); } else { @@ -2186,22 +2409,30 @@ namespace MinecraftClient /// A copy of Packet Data /// The packet is login phase or playing phase /// The packet is received from server or sent by client - public void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) + public async Task OnNetworkPacketAsync(int packetID, byte[] packetData, bool isLogin, bool isInbound) { - DispatchBotEvent(bot => bot.OnNetworkPacket(packetID, packetData, isLogin, isInbound)); + if (networkPacketCaptureEnabled) + { + await TriggerEvent(McClientEventType.NetworkPacket, + new Tuple(packetID, packetData, isLogin, isInbound)); + DispatchBotEvent(bot => bot.OnNetworkPacket(packetID, new(packetData), isLogin, isInbound)); + } } /// /// Called when a server was successfully joined /// - public async Task OnGameJoined() + public async Task OnGameJoinedAsync() { + if (handler == null) + return; + string? bandString = Config.Main.Advanced.BrandInfo.ToBrandString(); - if (!String.IsNullOrWhiteSpace(bandString)) - await handler!.SendBrandInfo(bandString.Trim()); + if (!string.IsNullOrWhiteSpace(bandString)) + await handler.SendBrandInfo(bandString.Trim()); if (Config.MCSettings.Enabled) - await handler!.SendClientSettings( + await handler.SendClientSettings( Config.MCSettings.Locale, Config.MCSettings.RenderDistance, (byte)Config.MCSettings.Difficulty, @@ -2218,8 +2449,7 @@ namespace MinecraftClient Log.Info(Translations.extra_inventory_enabled); } - ClearInventories(); - + await TriggerEvent(McClientEventType.GameJoin, null); DispatchBotEvent(bot => bot.AfterGameJoined()); await ConsoleIO.InitCommandList(dispatcher); @@ -2228,7 +2458,7 @@ namespace MinecraftClient /// /// Called when the player respawns, which happens on login, respawn and world change. /// - public void OnRespawn() + public async Task OnRespawnAsync() { if (terrainAndMovementsRequested) { @@ -2244,6 +2474,8 @@ namespace MinecraftClient entities.Clear(); ClearInventories(); + + await TriggerEvent(McClientEventType.Respawn, null); DispatchBotEvent(bot => bot.OnRespawn()); } @@ -2262,7 +2494,7 @@ namespace MinecraftClient /// Current goal of movement. Location.Zero if not set. public Location GetCurrentMovementGoal() { - return (ClientIsMoving() || path == null) ? Location.Zero : path.Last(); + return (!ClientIsMoving() || path == null) ? Location.Zero : path.Last(); } /// @@ -2305,17 +2537,10 @@ namespace MinecraftClient /// /// The new location /// If true, the location is relative to the current location - public void UpdateLocation(Location location, bool relative) + public void UpdateLocation(Location location) { - lock (locationLock) - { - if (relative) - { - this.location += location; - } - else this.location = location; - locationReceived = true; - } + this.location = location; + locationReceived = true; } /// @@ -2329,7 +2554,7 @@ namespace MinecraftClient { _yaw = yaw; _pitch = pitch; - UpdateLocation(location, false); + UpdateLocation(location); } /// @@ -2394,7 +2619,7 @@ namespace MinecraftClient /// Received chat/system message from the server /// /// Message received - public void OnTextReceived(ChatMessage message) + public async Task OnTextReceivedAsync(ChatMessage message) { UpdateKeepAlive(); @@ -2421,6 +2646,8 @@ namespace MinecraftClient foreach (string link in links) Log.Chat(string.Format(Translations.mcc_link, link)); + await TriggerEvent(McClientEventType.TextReceive, + new Tuple(messageText, message.content)); DispatchBotEvent(bot => bot.GetText(messageText)); DispatchBotEvent(bot => bot.GetText(messageText, message.content)); } @@ -2438,7 +2665,7 @@ namespace MinecraftClient /// /// The inventory /// Inventory ID - public void OnInventoryOpen(int inventoryID, Container inventory) + public async Task OnInventoryOpenAsync(int inventoryID, Container inventory) { inventories[inventoryID] = inventory; @@ -2446,6 +2673,8 @@ namespace MinecraftClient { Log.Info(string.Format(Translations.extra_inventory_open, inventoryID, inventory.Title)); Log.Info(Translations.extra_inventory_interact); + + await TriggerEvent(McClientEventType.InventoryOpen, inventoryID); DispatchBotEvent(bot => bot.OnInventoryOpen(inventoryID)); } } @@ -2454,22 +2683,21 @@ namespace MinecraftClient /// When an inventory is close /// /// Inventory ID - public void OnInventoryClose(int inventoryID) + public async Task OnInventoryCloseAsync(int inventoryID) { lock (inventoryLock) { - if (inventories.ContainsKey(inventoryID)) - { - if (inventoryID == 0) - inventories[0].Items.Clear(); // Don't delete player inventory - else - inventories.Remove(inventoryID); - } + if (inventoryID == 0) + inventories[0].Items.Clear(); // Don't delete player inventory + else + inventories.Remove(inventoryID); } if (inventoryID != 0) { Log.Info(string.Format(Translations.extra_inventory_close, inventoryID)); + + await TriggerEvent(McClientEventType.InventoryClose, inventoryID); DispatchBotEvent(bot => bot.OnInventoryClose(inventoryID)); } } @@ -2482,7 +2710,7 @@ namespace MinecraftClient /// Inventory ID /// Property ID /// Property Value - public void OnWindowProperties(byte inventoryID, short propertyId, short propertyValue) + public async Task OnWindowPropertiesAsync(byte inventoryID, short propertyId, short propertyValue) { if (!inventories.TryGetValue(inventoryID, out Container? inventory)) return; @@ -2491,6 +2719,8 @@ namespace MinecraftClient inventory.Properties.Add(propertyId, propertyValue); + await TriggerEvent(McClientEventType.InventoryProperties, + new Tuple(inventoryID, propertyId, propertyValue)); DispatchBotEvent(bot => bot.OnInventoryProperties(inventoryID, propertyId, propertyValue)); if (inventory.Type == ContainerType.Enchantment) @@ -2557,6 +2787,8 @@ namespace MinecraftClient }; + await TriggerEvent(McClientEventType.Enchantments, lastEnchantment); + DispatchBotEvent(bot => bot.OnEnchantments( // Enchantments topEnchantment, @@ -2583,12 +2815,13 @@ namespace MinecraftClient /// /// Inventory ID /// Item list, key = slot ID, value = Item information - public void OnWindowItems(byte inventoryID, Dictionary itemList, int stateId) + public async Task OnWindowItemsAsync(byte inventoryID, Dictionary itemList, int stateId) { if (inventories.TryGetValue(inventoryID, out Container? container)) { container.Items = itemList; container.StateID = stateId; + await TriggerEvent(McClientEventType.InventoryUpdate, inventoryID); DispatchBotEvent(bot => bot.OnInventoryUpdate(inventoryID)); } } @@ -2599,7 +2832,7 @@ namespace MinecraftClient /// Window ID /// Slot ID /// Item (may be null for empty slot) - public void OnSetSlot(byte inventoryID, short slotID, Item? item, int stateId) + public async Task OnSetSlotAsync(byte inventoryID, short slotID, Item? item, int stateId) { lock (inventoryLock) { @@ -2632,6 +2865,7 @@ namespace MinecraftClient } } } + await TriggerEvent(McClientEventType.InventoryUpdate, inventoryID); DispatchBotEvent(bot => bot.OnInventoryUpdate(inventoryID)); } @@ -2648,7 +2882,7 @@ namespace MinecraftClient /// Triggered when a new player joins the game /// /// player info - public void OnPlayerJoin(PlayerInfo player) + public async Task OnPlayerJoinAsync(PlayerInfo player) { //Ignore placeholders eg 0000tab# from TabListPlus if (!ChatBot.IsValidName(player.Name)) @@ -2666,6 +2900,7 @@ namespace MinecraftClient onlinePlayers[player.Uuid] = player; } + await TriggerEvent(McClientEventType.PlayerJoin, player); DispatchBotEvent(bot => bot.OnPlayerJoin(player.Uuid, player.Name)); } @@ -2673,19 +2908,18 @@ namespace MinecraftClient /// Triggered when a player has left the game /// /// UUID of the player - public void OnPlayerLeave(Guid uuid) + public async Task OnPlayerLeaveAsync(Guid uuid) { - string? username = null; + PlayerInfo? playerInfo = null; lock (onlinePlayers) { - if (onlinePlayers.TryGetValue(uuid, out PlayerInfo? playerInfo)) - { - username = playerInfo.Name; + if (onlinePlayers.TryGetValue(uuid, out playerInfo)) onlinePlayers.Remove(uuid); - } } + await TriggerEvent(McClientEventType.PlayerLeave, + new Tuple(uuid, playerInfo)); DispatchBotEvent(bot => bot.OnPlayerLeave(uuid, username)); } @@ -2694,12 +2928,14 @@ namespace MinecraftClient /// /// Victim's entity /// Killer's entity - public void OnPlayerKilled(int killerEntityId, string chatMessage) + public async Task OnPlayerKilledAsync(int killerEntityId, string chatMessage) { - if (!entities.ContainsKey(killerEntityId)) + if (!entities.TryGetValue(killerEntityId, out Entity? killer)) return; - DispatchBotEvent(bot => bot.OnKilled(entities[killerEntityId], chatMessage)); + await TriggerEvent(McClientEventType.PlayerKilled, + new Tuple(killer, chatMessage)); + DispatchBotEvent(bot => bot.OnKilled(killer, chatMessage)); } /// @@ -2738,29 +2974,37 @@ namespace MinecraftClient /// /// Called when an entity spawned /// - public void OnSpawnEntity(Entity entity) + public async Task OnSpawnEntity(Entity entity) { // The entity should not already exist, but if it does, let's consider the previous one is being destroyed if (entities.ContainsKey(entity.ID)) - OnDestroyEntities(new[] { entity.ID }); + await OnDestroyEntities(new[] { entity.ID }); entities.Add(entity.ID, entity); + + await TriggerEvent(McClientEventType.EntitySpawn, entity); DispatchBotEvent(bot => bot.OnEntitySpawn(entity)); } /// /// Called when an entity effects /// - public void OnEntityEffect(int entityid, Effects effect, int amplifier, int duration, byte flags, bool hasFactorData, Dictionary? factorCodec) + public async Task OnEntityEffect(int entityid, Effect effect) { - if (entities.TryGetValue(entityid, out Entity? entity)) - DispatchBotEvent(bot => bot.OnEntityEffect(entity, effect, amplifier, duration, flags)); - } + if (!entities.TryGetValue(entityid, out Entity? entity)) + return; + await TriggerEvent(McClientEventType.EntityEffect, + new Tuple(entity, effect)); + + byte flag = (byte)((effect.IsFromBeacon ? 1 : 0) | (effect.ShowParticles ? 2 : 0) | (effect.ShowIcon ? 2 : 0)); + DispatchBotEvent(bot => bot.OnEntityEffect(entity, effect.Type, effect.EffectLevel - 1, effect.DurationInTick, flag)); + } + /// /// Called when a player spawns or enters the client's render distance /// - public void OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch) + public async Task OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch) { Entity playerEntity; if (onlinePlayers.TryGetValue(uuid, out PlayerInfo? player)) @@ -2770,7 +3014,7 @@ namespace MinecraftClient } else playerEntity = new(entityID, EntityType.Player, location, uuid, null, yaw, pitch); - OnSpawnEntity(playerEntity); + await OnSpawnEntity(playerEntity); } /// @@ -2779,14 +3023,17 @@ namespace MinecraftClient /// Entity ID /// Equipment slot. 0: main hand, 1: off hand, 2-5: armor slot (2: boots, 3: leggings, 4: chestplate, 5: helmet) /// Item) - public void OnEntityEquipment(int entityid, int slot, Item? item) + public async Task OnEntityEquipment(int entityid, int slot, Item? item) { if (entities.TryGetValue(entityid, out Entity? entity)) { entity.Equipment.Remove(slot); if (item != null) entity.Equipment[slot] = item; - DispatchBotEvent(bot => bot.OnEntityEquipment(entities[entityid], slot, item)); + + await TriggerEvent(McClientEventType.EntityEquipment, + new Tuple(entity, slot, item)); + DispatchBotEvent(bot => bot.OnEntityEquipment(entity, slot, item)); } } @@ -2807,6 +3054,9 @@ namespace MinecraftClient { if (playerInfo.Name == username) this.gamemode = gamemode; + + await TriggerEvent(McClientEventType.GamemodeUpdate, + new Tuple(playerInfo, gamemode)); DispatchBotEvent(bot => bot.OnGamemodeUpdate(playerInfo.Name, uuid, gamemode)); } } @@ -2814,12 +3064,13 @@ namespace MinecraftClient /// /// Called when entities dead/despawn. /// - public void OnDestroyEntities(int[] Entities) + public async Task OnDestroyEntities(int[] Entities) { foreach (int a in Entities) { if (entities.TryGetValue(a, out Entity? entity)) { + await TriggerEvent(McClientEventType.EntityDespawn, entity); DispatchBotEvent(bot => bot.OnEntityDespawn(entity)); entities.Remove(a); } @@ -2834,13 +3085,15 @@ namespace MinecraftClient /// /// /// - public void OnEntityPosition(int EntityID, Double Dx, Double Dy, Double Dz, bool onGround) + public async Task OnEntityPosition(int EntityID, Double Dx, Double Dy, Double Dz, bool onGround) { if (entities.TryGetValue(EntityID, out Entity? entity)) { entity.Location.X += Dx; entity.Location.Y += Dy; entity.Location.Z += Dz; + + await TriggerEvent(McClientEventType.EntityMove, entity); DispatchBotEvent(bot => bot.OnEntityMove(entity)); } } @@ -2853,11 +3106,13 @@ namespace MinecraftClient /// /// /// - public void OnEntityTeleport(int EntityID, Double X, Double Y, Double Z, bool onGround) + public async Task OnEntityTeleport(int EntityID, Double X, Double Y, Double Z, bool onGround) { if (entities.TryGetValue(EntityID, out Entity? entity)) { entity.Location = new Location(X, Y, Z); + + await TriggerEvent(McClientEventType.EntityMove, entity); DispatchBotEvent(bot => bot.OnEntityMove(entity)); } } @@ -2867,10 +3122,11 @@ namespace MinecraftClient /// /// /// - public void OnEntityProperties(int EntityID, Dictionary prop) + public async Task OnEntityProperties(int EntityID, Dictionary prop) { if (EntityID == playerEntityID) { + await TriggerEvent(McClientEventType.PlayerPropertyReceive, prop); DispatchBotEvent(bot => bot.OnPlayerProperty(prop)); } } @@ -2880,10 +3136,11 @@ namespace MinecraftClient /// /// Entity ID /// Status ID - public void OnEntityStatus(int entityID, byte status) + public async Task OnEntityStatus(int entityID, byte status) { if (entityID == playerEntityID) { + await TriggerEvent(McClientEventType.PlayerStatusUpdate, status); DispatchBotEvent(bot => bot.OnPlayerStatus(status)); } } @@ -2893,7 +3150,7 @@ namespace MinecraftClient /// /// /// - public void OnTimeUpdate(long WorldAge, long TimeOfDay) + public async Task OnTimeUpdate(long WorldAge, long TimeOfDay) { // TimeUpdate sent every server tick hence used as timeout detect UpdateKeepAlive(); @@ -2902,7 +3159,7 @@ namespace MinecraftClient { DateTime currentTime = DateTime.Now; long tickDiff = WorldAge - lastAge; - Double tps = tickDiff / (currentTime - lastTime).TotalSeconds; + double tps = tickDiff / (currentTime - lastTime).TotalSeconds; lastAge = WorldAge; lastTime = currentTime; if (tps <= 20 && tps > 0) @@ -2918,6 +3175,7 @@ namespace MinecraftClient sampleSum += tps; averageTPS = sampleSum / tpsSamples.Count; serverTPS = tps; + await TriggerEvent(McClientEventType.ServerTpsUpdate, tps); DispatchBotEvent(bot => bot.OnServerTpsUpdate(tps)); } } @@ -2926,6 +3184,9 @@ namespace MinecraftClient lastAge = WorldAge; lastTime = DateTime.Now; } + + await TriggerEvent(McClientEventType.TimeUpdate, + new Tuple(WorldAge, TimeOfDay)); DispatchBotEvent(bot => bot.OnTimeUpdate(WorldAge, TimeOfDay)); } @@ -2933,11 +3194,15 @@ namespace MinecraftClient /// Called when client player's health changed, e.g. getting attack /// /// Player current health - public void OnUpdateHealth(float health, int food) + public async Task OnUpdateHealth(float health, int food) { playerHealth = health; playerFoodSaturation = food; + await TriggerEvent(McClientEventType.HealthUpdate, + new Tuple(health, food)); + DispatchBotEvent(bot => bot.OnHealthUpdate(health, food)); + if (health <= 0) { if (Config.Main.Advanced.AutoRespawn) @@ -2949,10 +3214,10 @@ namespace MinecraftClient { Log.Info(string.Format(Translations.mcc_player_dead, Config.Main.Advanced.InternalCmdChar.ToLogString())); } + + await TriggerEvent(McClientEventType.Death, null); DispatchBotEvent(bot => bot.OnDeath()); } - - DispatchBotEvent(bot => bot.OnHealthUpdate(health, food)); } /// @@ -2961,10 +3226,13 @@ namespace MinecraftClient /// Between 0 and 1 /// Level /// Total Experience - public void OnSetExperience(float Experiencebar, int Level, int TotalExperience) + public async Task OnSetExperience(float Experiencebar, int Level, int TotalExperience) { playerLevel = Level; playerTotalExperience = TotalExperience; + + await TriggerEvent(McClientEventType.ExperienceChange, + new Tuple(Experiencebar, Level, TotalExperience)); DispatchBotEvent(bot => bot.OnSetExperience(Experiencebar, Level, TotalExperience)); } @@ -2974,8 +3242,10 @@ namespace MinecraftClient /// Explosion location /// Explosion strength /// Amount of affected blocks - public void OnExplosion(Location location, float strength, int affectedBlocks) + public async Task OnExplosion(Location location, float strength, int affectedBlocks) { + await TriggerEvent(McClientEventType.Explosion, + new Tuple(location, strength, affectedBlocks)); DispatchBotEvent(bot => bot.OnExplosion(location, strength, affectedBlocks)); } @@ -2984,22 +3254,19 @@ namespace MinecraftClient /// /// player uuid /// Latency - public void OnLatencyUpdate(Guid uuid, int latency) + public async Task OnLatencyUpdate(Guid uuid, int latency) { if (onlinePlayers.TryGetValue(uuid, out PlayerInfo? player)) { player.Ping = latency; + + await TriggerEvent(McClientEventType.PlayerLatencyUpdate, + new Tuple(player, latency)); + string playerName = player.Name; - foreach (KeyValuePair ent in entities) - { - if (ent.Value.UUID == uuid && ent.Value.Name == playerName) - { - ent.Value.Latency = latency; - DispatchBotEvent(bot => bot.OnLatencyUpdate(ent.Value, playerName, uuid, latency)); - break; - } - } DispatchBotEvent(bot => bot.OnLatencyUpdate(playerName, uuid, latency)); + if (player.entity != null) + DispatchBotEvent(bot => bot.OnLatencyUpdate(player.entity, playerName, uuid, latency)); } } @@ -3007,9 +3274,11 @@ namespace MinecraftClient /// Called when held item change /// /// item slot - public void OnHeldItemChange(byte slot) + public async Task OnHeldItemChange(byte slot) { CurrentSlot = slot; + + await TriggerEvent(McClientEventType.HeldItemChange, slot); DispatchBotEvent(bot => bot.OnHeldItemChange(slot)); } @@ -3027,9 +3296,19 @@ namespace MinecraftClient /// x offset of the westernmost column /// z offset of the northernmost row /// a byte array of colors on the map - public void OnMapData(int mapid, byte scale, bool trackingPosition, bool locked, List icons, byte columnsUpdated, byte rowsUpdated, byte mapCoulmnX, byte mapCoulmnZ, byte[]? colors) + public async Task OnMapData(MapData mapData) { - DispatchBotEvent(bot => bot.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapCoulmnZ, colors)); + await TriggerEvent(McClientEventType.MapDataReceive, mapData); + DispatchBotEvent(bot => bot.OnMapData(mapData.MapId, + mapData.Scale, + mapData.TrackingPosition, + mapData.Locked, + mapData.Icons, + mapData.ColumnsUpdated, + mapData.RowsUpdated, + mapData.MapCoulmnX, + mapData.MapRowZ, + mapData.Colors)); } /// @@ -3042,9 +3321,17 @@ namespace MinecraftClient /// Stay /// Fade Out /// json text - public void OnTitle(int action, string titletext, string subtitletext, string actionbartext, int fadein, int stay, int fadeout, string json) + public async Task OnTitle(TitlePacket title) { - DispatchBotEvent(bot => bot.OnTitle(action, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json)); + await TriggerEvent(McClientEventType.TitleReceive, title); + DispatchBotEvent(bot => bot.OnTitle(title.Action, + title.TitleText, + title.SubtitleText, + title.ActionbarText, + title.FadeIn, + title.Stay, + title.FadeOut, + title.JsonText)); } /// diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 3f4156ae..1eec5d6f 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -56,7 +56,6 @@ namespace MinecraftClient private static bool useMcVersionOnce = false; private static string settingsIniPath = "MinecraftClient.ini"; - private static int CurrentThreadId; private static bool RestartKeepSettings = false; private static int RestartAfter = -1, Exitcode = 0; @@ -93,6 +92,8 @@ namespace MinecraftClient if (BuildInfo != null) ConsoleIO.WriteLineFormatted("§8" + BuildInfo); + ConsoleIO.WriteLine("\u001b[33m\u001b[46m▀\u001b[0m"); + string? specifiedSettingFile = null; if (args.Length >= 1 && File.Exists(args[0]) && Settings.ToLowerIfNeed(Path.GetExtension(args[0])) == ".ini") { @@ -194,8 +195,6 @@ namespace MinecraftClient startupargs = args; - CurrentThreadId = Environment.CurrentManagedThreadId; - // Check for updates AsyncTaskHandler.CheckUpdate = Task.Run(async () => { @@ -419,7 +418,6 @@ namespace MinecraftClient } else if (!loadSucceed) { - ConsoleInteractive.ConsoleReader.StopReadThread(); while (true) { ConsoleIO.WriteLine(string.Empty); @@ -724,6 +722,7 @@ namespace MinecraftClient var getServerInfoTask = GetServerInfoAsync(loginHttpClient, loginTask); var refreshPlayerKeyTask = RefreshPlayerKeyPair(loginHttpClient, loginTask); + // Todo: Detailed status display of login steps. (result, session, playerKeyPair) = await loginTask; if (result == ProtocolHandler.LoginResult.Success && session != null) { @@ -808,11 +807,6 @@ namespace MinecraftClient } } - public static int GetMainThreadId() - { - return CurrentThreadId; - } - /// /// Reloads settings /// @@ -988,7 +982,7 @@ namespace MinecraftClient public static Type[] GetTypesInNamespace(string nameSpace, Assembly? assembly = null) { if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } - return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); + return assembly.GetTypes().Where(t => string.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); } /// @@ -998,7 +992,7 @@ namespace MinecraftClient { if (typeof(Program) .Assembly - .GetCustomAttributes(typeof(System.Reflection.AssemblyConfigurationAttribute), false) + .GetCustomAttributes(typeof(AssemblyConfigurationAttribute), false) .FirstOrDefault() is AssemblyConfigurationAttribute attribute) BuildInfo = attribute.Configuration; } diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 1f9771bd..0cdab85d 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -6,6 +6,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using MinecraftClient.EntityHandler; using MinecraftClient.Inventory; using MinecraftClient.Inventory.ItemPalettes; using MinecraftClient.Mapping; diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 098a5f61..c9035714 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -125,7 +125,7 @@ namespace MinecraftClient.Protocol.Handlers case 0x02: await ReadData(1); await ReadNextString(); await ReadNextString(); await ReadData(4); break; case 0x03: string message = await ReadNextString(); - handler.OnTextReceived(new ChatMessage(message, protocolversion >= 72, 0, Guid.Empty)); break; + await handler.OnTextReceivedAsync(new ChatMessage(message, protocolversion >= 72, 0, Guid.Empty)); break; case 0x04: await ReadData(16); break; case 0x05: await ReadData(6); await ReadNextItemSlot(); break; case 0x06: await ReadData(12); break; @@ -196,7 +196,7 @@ namespace MinecraftClient.Protocol.Handlers case 0xC9: string name = await ReadNextString(); bool online = await ReadNextByte() != 0x00; await ReadData(2); Guid FakeUUID = new(MD5.HashData(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); - if (online) { handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); } else { handler.OnPlayerLeave(FakeUUID); } + if (online) { await handler.OnPlayerJoinAsync(new PlayerInfo(name, FakeUUID)); } else { await handler.OnPlayerLeaveAsync(FakeUUID); } break; case 0xCA: if (protocolversion >= 72) { await ReadData(9); } else await ReadData(3); break; case 0xCB: diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index e78e50ba..501bad3d 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -16,6 +16,7 @@ using System.Threading; using System.Threading.Tasks; using MinecraftClient.Commands; using MinecraftClient.Crypto; +using MinecraftClient.EntityHandler; using MinecraftClient.Inventory; using MinecraftClient.Inventory.ItemPalettes; using MinecraftClient.Logger; @@ -71,6 +72,8 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_19_Version = 759; internal const int MC_1_19_2_Version = 760; + private ulong CurrentTick = 0; + private int autocomplete_transaction_id = 0; private readonly Dictionary window_actions = new(); private bool login_phase = true; @@ -218,6 +221,7 @@ namespace MinecraftClient.Protocol.Handlers { while (await periodicTimer.WaitForNextTickAsync(CancelToken) && !CancelToken.IsCancellationRequested) { + ++CurrentTick; try { await handler.OnUpdate(); @@ -226,7 +230,7 @@ namespace MinecraftClient.Protocol.Handlers { if (Config.Logging.DebugMessages) { - ConsoleIO.WriteLine($"{e.GetType().Name} when ticking: {e.Message}"); + ConsoleIO.WriteLine($"{e.GetType().Name} on tick: {e.Message}"); if (e.StackTrace != null) ConsoleIO.WriteLine(e.StackTrace); } @@ -314,7 +318,7 @@ namespace MinecraftClient.Protocol.Handlers await SendPacket(PacketTypesOut.Pong, packetData); break; case PacketTypesIn.JoinGame: - Task OnGameJoinedTask = handler.OnGameJoined(); + Task OnGameJoinedTask = handler.OnGameJoinedAsync(); int playerEntityID = await dataTypes.ReadNextIntAsync(packetData); handler.OnReceivePlayerEntityID(playerEntityID); @@ -439,7 +443,7 @@ namespace MinecraftClient.Protocol.Handlers else senderUUID = Guid.Empty; - handler.OnTextReceived(new(message, true, messageType, senderUUID)); + await handler.OnTextReceivedAsync(new(message, true, messageType, senderUUID)); } else if (protocolVersion == MC_1_19_Version) // 1.19 { @@ -477,7 +481,7 @@ namespace MinecraftClient.Protocol.Handlers } ChatMessage chat = new(signedChat, true, messageType, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, messageSignature, verifyResult); - handler.OnTextReceived(chat); + await handler.OnTextReceivedAsync(chat); } else // 1.19.1 + { @@ -554,7 +558,7 @@ namespace MinecraftClient.Protocol.Handlers ChatMessage chat = new(signedChat, false, chatTypeId, senderUUID, unsignedChatContent, senderDisplayName, senderTeamName, timestamp, headerSignature, verifyResult); if (isOnlineMode && !chat.LacksSender()) await Acknowledge(chat); - handler.OnTextReceived(chat); + await handler.OnTextReceivedAsync(chat); } break; case PacketTypesIn.CombatEvent: @@ -567,7 +571,7 @@ namespace MinecraftClient.Protocol.Handlers { await dataTypes.SkipNextVarIntAsync(packetData); - handler.OnPlayerKilled( + await handler.OnPlayerKilledAsync( await dataTypes.ReadNextIntAsync(packetData), ChatParser.ParseText(await dataTypes.ReadNextStringAsync(packetData)) ); @@ -578,7 +582,7 @@ namespace MinecraftClient.Protocol.Handlers case PacketTypesIn.DeathCombatEvent: await dataTypes.SkipNextVarIntAsync(packetData); - handler.OnPlayerKilled( + await handler.OnPlayerKilledAsync( await dataTypes.ReadNextIntAsync(packetData), ChatParser.ParseText(await dataTypes.ReadNextStringAsync(packetData)) ); @@ -673,7 +677,7 @@ namespace MinecraftClient.Protocol.Handlers await dataTypes.SkipNextLocationAsync(packetData); // Death location } } - handler.OnRespawn(); + await handler.OnRespawnAsync(); break; case PacketTypesIn.PlayerPositionAndLook: { @@ -907,7 +911,21 @@ namespace MinecraftClient.Protocol.Handlers colors = await dataTypes.ReadNextByteArrayAsync(packetData); } - handler.OnMapData(mapid, scale, trackingPosition, locked, icons, columnsUpdated, rowsUpdated, mapCoulmnX, mapRowZ, colors); + MapData mapData = new() + { + MapId = mapid, + Scale = scale, + TrackingPosition = trackingPosition, + Locked = locked, + Icons = icons, + ColumnsUpdated = columnsUpdated, + RowsUpdated = rowsUpdated, + MapCoulmnX = mapCoulmnX, + MapRowZ = mapRowZ, + Colors = colors, + }; + + await handler.OnMapData(mapData); break; case PacketTypesIn.TradeList: if (protocolVersion >= MC_1_14_Version && handler.GetInventoryEnabled()) @@ -985,7 +1003,20 @@ namespace MinecraftClient.Protocol.Handlers fadeout = await dataTypes.ReadNextIntAsync(packetData); } } - handler.OnTitle(action2, titletext, subtitletext, actionbartext, fadein, stay, fadeout, json); + + TitlePacket title = new() + { + Action = action2, + TitleText = titletext, + SubtitleText = subtitletext, + ActionbarText = actionbartext, + Stay = stay, + FadeIn = fadein, + FadeOut = fadeout, + JsonText = json, + }; + + await handler.OnTitle(title); } break; case PacketTypesIn.MultiBlockChange: @@ -1253,14 +1284,14 @@ namespace MinecraftClient.Protocol.Handlers } } - handler.OnPlayerJoin(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature)); + await handler.OnPlayerJoinAsync(new PlayerInfo(uuid, name, properties, gameMode, ping, displayName, keyExpiration, publicKey, signature)); break; case 0x01: //Update gamemode await handler.OnGamemodeUpdate(uuid, await dataTypes.ReadNextVarIntAsync(packetData)); break; case 0x02: //Update latency int latency = await dataTypes.ReadNextVarIntAsync(packetData); - handler.OnLatencyUpdate(uuid, latency); //Update latency; + await handler.OnLatencyUpdate(uuid, latency); //Update latency; break; case 0x03: //Update display name if (await dataTypes.ReadNextBoolAsync(packetData)) @@ -1273,7 +1304,7 @@ namespace MinecraftClient.Protocol.Handlers } break; case 0x04: //Player Leave - handler.OnPlayerLeave(uuid); + await handler.OnPlayerLeaveAsync(uuid); break; default: //Unknown player list item type @@ -1288,8 +1319,9 @@ namespace MinecraftClient.Protocol.Handlers short ping = await dataTypes.ReadNextShortAsync(packetData); Guid FakeUUID = new(MD5.HashData(Encoding.UTF8.GetBytes(name)).Take(16).ToArray()); if (online) - handler.OnPlayerJoin(new PlayerInfo(name, FakeUUID)); - else handler.OnPlayerLeave(FakeUUID); + await handler.OnPlayerJoinAsync(new PlayerInfo(name, FakeUUID)); + else + await handler.OnPlayerLeaveAsync(FakeUUID); } break; case PacketTypesIn.TabComplete: @@ -1344,7 +1376,7 @@ namespace MinecraftClient.Protocol.Handlers string title = await dataTypes.ReadNextStringAsync(packetData); byte slots = await dataTypes.ReadNextByteAsync(packetData); Container inventory = new(windowID, inventoryType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); + await handler.OnInventoryOpenAsync(windowID, inventory); } else { @@ -1353,7 +1385,7 @@ namespace MinecraftClient.Protocol.Handlers int windowType = await dataTypes.ReadNextVarIntAsync(packetData); string title = await dataTypes.ReadNextStringAsync(packetData); Container inventory = new(windowID, windowType, ChatParser.ParseText(title)); - handler.OnInventoryOpen(windowID, inventory); + await handler.OnInventoryOpenAsync(windowID, inventory); } } break; @@ -1362,7 +1394,7 @@ namespace MinecraftClient.Protocol.Handlers { byte windowID = await dataTypes.ReadNextByteAsync(packetData); lock (window_actions) { window_actions[windowID] = 0; } - handler.OnInventoryClose(windowID); + await handler.OnInventoryCloseAsync(windowID); } break; case PacketTypesIn.WindowItems: @@ -1395,7 +1427,7 @@ namespace MinecraftClient.Protocol.Handlers if (protocolVersion >= MC_1_17_1_Version) // Carried Item - 1.17.1 and above await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette); - handler.OnWindowItems(windowId, inventorySlots, stateId); + await handler.OnWindowItemsAsync(windowId, inventorySlots, stateId); } break; case PacketTypesIn.WindowProperty: @@ -1403,7 +1435,7 @@ namespace MinecraftClient.Protocol.Handlers short propertyId = await dataTypes.ReadNextShortAsync(packetData); short propertyValue = await dataTypes.ReadNextShortAsync(packetData); - handler.OnWindowProperties(containerId, propertyId, propertyValue); + await handler.OnWindowPropertiesAsync(containerId, propertyId, propertyValue); break; case PacketTypesIn.SetSlot: if (handler.GetInventoryEnabled()) @@ -1414,7 +1446,7 @@ namespace MinecraftClient.Protocol.Handlers stateId = await dataTypes.ReadNextVarIntAsync(packetData); // State ID - 1.17.1 and above short slotID = await dataTypes.ReadNextShortAsync(packetData); Item? item = await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette); - handler.OnSetSlot(windowID, slotID, item, stateId); + await handler.OnSetSlotAsync(windowID, slotID, item, stateId); } break; case PacketTypesIn.WindowConfirmation: @@ -1452,7 +1484,7 @@ namespace MinecraftClient.Protocol.Handlers if (handler.GetEntityHandlingEnabled()) { Entity entity = await dataTypes.ReadNextEntity(packetData, entityPalette, false); - handler.OnSpawnEntity(entity); + await handler.OnSpawnEntity(entity); } break; case PacketTypesIn.EntityEquipment: @@ -1469,14 +1501,14 @@ namespace MinecraftClient.Protocol.Handlers hasNext = bitsData >> 7 == 1; int slot2 = bitsData >> 1; Item? item = await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); + await handler.OnEntityEquipment(entityid, slot2, item); } while (hasNext); } else { int slot2 = await dataTypes.ReadNextVarIntAsync(packetData); Item? item = await dataTypes.ReadNextItemSlotAsync(packetData, itemPalette); - handler.OnEntityEquipment(entityid, slot2, item); + await handler.OnEntityEquipment(entityid, slot2, item); } } break; @@ -1487,7 +1519,7 @@ namespace MinecraftClient.Protocol.Handlers // 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); + await handler.OnSpawnEntity(entity); } break; case PacketTypesIn.SpawnPlayer: @@ -1503,17 +1535,17 @@ namespace MinecraftClient.Protocol.Handlers Location EntityLocation = new(X, Y, Z); - handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); + await handler.OnSpawnPlayer(EntityID, UUID, EntityLocation, Yaw, Pitch); } break; case PacketTypesIn.EntityEffect: if (handler.GetEntityHandlingEnabled()) { int entityid = await dataTypes.ReadNextVarIntAsync(packetData); - Effects effect = Effects.Speed; + EffectType effectType = EffectType.Speed; int effectId = protocolVersion >= MC_1_18_2_Version ? await dataTypes.ReadNextVarIntAsync(packetData) : await dataTypes.ReadNextByteAsync(packetData); - if (Enum.TryParse(effectId.ToString(), out effect)) + if (Enum.TryParse(effectId.ToString(), out effectType)) { int amplifier = await dataTypes.ReadNextByteAsync(packetData); int duration = await dataTypes.ReadNextVarIntAsync(packetData); @@ -1529,7 +1561,19 @@ namespace MinecraftClient.Protocol.Handlers factorCodec = await dataTypes.ReadNextNbtAsync(packetData); } - handler.OnEntityEffect(entityid, effect, amplifier, duration, flags, hasFactorData, factorCodec); + Effect effect = new() + { + Type = effectType, + EffectLevel = amplifier, + StartTick = CurrentTick, + DurationInTick = duration, + IsFromBeacon = (flags & 0x01) > 0, + ShowParticles = (flags & 0x02) > 0, + ShowIcon = (flags & 0x04) > 0, + FactorData = factorCodec, + }; + + await handler.OnEntityEffect(entityid, effect); } } break; @@ -1544,7 +1588,7 @@ namespace MinecraftClient.Protocol.Handlers { entityList[i] = await dataTypes.ReadNextVarIntAsync(packetData); } - handler.OnDestroyEntities(entityList); + await handler.OnDestroyEntities(entityList); } break; case PacketTypesIn.EntityPosition: @@ -1558,7 +1602,7 @@ namespace MinecraftClient.Protocol.Handlers DeltaX /= 128 * 32; DeltaY /= 128 * 32; DeltaZ /= 128 * 32; - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); + await handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); } break; case PacketTypesIn.EntityPositionAndRotation: @@ -1574,7 +1618,7 @@ namespace MinecraftClient.Protocol.Handlers DeltaX /= 128 * 32; DeltaY /= 128 * 32; DeltaZ /= 128 * 32; - handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); + await handler.OnEntityPosition(EntityID, DeltaX, DeltaY, DeltaZ, OnGround); } break; case PacketTypesIn.EntityProperties: @@ -1609,7 +1653,7 @@ namespace MinecraftClient.Protocol.Handlers if (op2.Count > 0) _value *= op2.Aggregate((a, _x) => a * _x); keys.Add(_key, _value); } - handler.OnEntityProperties(EntityID, keys); + await handler.OnEntityProperties(EntityID, keys); } break; case PacketTypesIn.EntityMetadata: @@ -1641,20 +1685,20 @@ namespace MinecraftClient.Protocol.Handlers { int entityId = await dataTypes.ReadNextIntAsync(packetData); byte status = await dataTypes.ReadNextByteAsync(packetData); - handler.OnEntityStatus(entityId, status); + await handler.OnEntityStatus(entityId, status); } break; case PacketTypesIn.TimeUpdate: long WorldAge = await dataTypes.ReadNextLongAsync(packetData); long TimeOfday = await dataTypes.ReadNextLongAsync(packetData); - handler.OnTimeUpdate(WorldAge, TimeOfday); + await handler.OnTimeUpdate(WorldAge, TimeOfday); break; case PacketTypesIn.SystemChat: string systemMessage = await dataTypes.ReadNextStringAsync(packetData); int msgType = await dataTypes.ReadNextVarIntAsync(packetData); if (msgType == 1 && !Config.Main.Advanced.ShowSystemMessages) break; - handler.OnTextReceived(new(systemMessage, true, msgType, Guid.Empty, true)); + await handler.OnTextReceivedAsync(new(systemMessage, true, msgType, Guid.Empty, true)); break; case PacketTypesIn.EntityTeleport: if (handler.GetEntityHandlingEnabled()) @@ -1666,7 +1710,7 @@ namespace MinecraftClient.Protocol.Handlers byte EntityYaw = await dataTypes.ReadNextByteAsync(packetData); byte EntityPitch = await dataTypes.ReadNextByteAsync(packetData); bool OnGround = await dataTypes.ReadNextBoolAsync(packetData); - handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround); + await handler.OnEntityTeleport(EntityID, X, Y, Z, OnGround); } break; case PacketTypesIn.UpdateHealth: @@ -1677,13 +1721,13 @@ namespace MinecraftClient.Protocol.Handlers else food = await dataTypes.ReadNextShortAsync(packetData); await dataTypes.SkipNextFloatAsync(packetData); // Food Saturation - handler.OnUpdateHealth(health, food); + await handler.OnUpdateHealth(health, food); break; case PacketTypesIn.SetExperience: float experiencebar = await dataTypes.ReadNextFloatAsync(packetData); int level = await dataTypes.ReadNextVarIntAsync(packetData); int totalexperience = await dataTypes.ReadNextVarIntAsync(packetData); - handler.OnSetExperience(experiencebar, level, totalexperience); + await handler.OnSetExperience(experiencebar, level, totalexperience); break; case PacketTypesIn.Explosion: Location explosionLocation = new(await dataTypes.ReadNextFloatAsync(packetData), await dataTypes.ReadNextFloatAsync(packetData), await dataTypes.ReadNextFloatAsync(packetData)); @@ -1700,11 +1744,11 @@ namespace MinecraftClient.Protocol.Handlers float playerVelocityY = await dataTypes.ReadNextFloatAsync(packetData); float playerVelocityZ = await dataTypes.ReadNextFloatAsync(packetData); - handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); + await handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); break; case PacketTypesIn.HeldItemChange: byte slot = await dataTypes.ReadNextByteAsync(packetData); - handler.OnHeldItemChange(slot); + await handler.OnHeldItemChange(slot); break; case PacketTypesIn.ScoreboardObjective: string objectivename = await dataTypes.ReadNextStringAsync(packetData); @@ -1771,6 +1815,15 @@ namespace MinecraftClient.Protocol.Handlers } } + /// + /// + /// + /// + public ulong GetCurrentTick() + { + return CurrentTick; + } + /// /// Disconnect from the server, cancel network reading. /// @@ -1805,8 +1858,7 @@ namespace MinecraftClient.Protocol.Handlers { if (handler.GetNetworkPacketCaptureEnabled()) { - List clone = packetData.ToList(); - handler.OnNetworkPacket(packetID, clone, login_phase, false); + await handler.OnNetworkPacketAsync(packetID, packetData.ToArray(), login_phase, false); } // log.Info("[C -> S] Sending packet " + packetID + " > " + dataTypes.ByteArrayToString(packetData.ToArray())); @@ -2248,7 +2300,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// Message acknowledgment /// True if properly sent - public async Task SendMessageAcknowledgment(LastSeenMessageList.Acknowledgment acknowledgment) + public async Task SendMessageAckAsync(LastSeenMessageList.Acknowledgment acknowledgment) { try { @@ -2279,7 +2331,7 @@ namespace MinecraftClient.Protocol.Handlers lastReceivedMessage = null; if (pendingAcknowledgments++ > 64) - await SendMessageAcknowledgment(ConsumeAcknowledgment()); + await SendMessageAckAsync(ConsumeAcknowledgment()); } } diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index e138cf4c..25f8452c 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MinecraftClient.EntityHandler; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; @@ -53,18 +54,18 @@ namespace MinecraftClient.Protocol /// A copy of Packet Data /// The packet is login phase or playing phase /// The packet is received from server or sent by client - void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound); + Task OnNetworkPacketAsync(int packetID, byte[] packetData, bool isLogin, bool isInbound); /// /// Called when a server was successfully joined /// - Task OnGameJoined(); + Task OnGameJoinedAsync(); /// /// Received chat/system message from the server /// /// Message received - public void OnTextReceived(ChatMessage message); + Task OnTextReceivedAsync(ChatMessage message); /// /// Will be called every animations of the hit and place block @@ -90,7 +91,7 @@ namespace MinecraftClient.Protocol /// /// This method is called when the protocol handler receives a title /// - void OnTitle(int action, string titletext, string subtitletext, string actionbartext, int fadein, int stay, int fadeout, string json); + Task OnTitle(TitlePacket title); /// /// Called when receiving a connection keep-alive from the server @@ -116,36 +117,36 @@ namespace MinecraftClient.Protocol /// /// Called when an inventory is opened /// - void OnInventoryOpen(int inventoryID, Container inventory); + Task OnInventoryOpenAsync(int inventoryID, Container inventory); /// /// Called when an inventory is closed /// - void OnInventoryClose(int inventoryID); + Task OnInventoryCloseAsync(int inventoryID); /// /// Called when the player respawns, which happens on login, respawn and world change. /// - void OnRespawn(); + Task OnRespawnAsync(); /// /// Triggered when a new player joins the game /// /// player info - public void OnPlayerJoin(PlayerInfo player); + Task OnPlayerJoinAsync(PlayerInfo player); /// /// This method is called when a player has left the game /// /// UUID of the player - void OnPlayerLeave(Guid uuid); + Task OnPlayerLeaveAsync(Guid uuid); /// /// This method is called when a player has been killed by another entity /// /// Killer's entity if /// message sent in chat when player is killed - void OnPlayerKilled(int killerEntityId, string chatMessage); + Task OnPlayerKilledAsync(int killerEntityId, string chatMessage); /// /// Called when the server sets the new location for the player @@ -161,7 +162,7 @@ namespace MinecraftClient.Protocol void OnConnectionLost(ChatBot.DisconnectReason reason, string message); /// - /// Called ~10 times per second (10 ticks per second) + /// Called ~20 times per second (20 ticks per second) /// Useful for updating bots in other parts of the program /// Task OnUpdate(); @@ -171,14 +172,14 @@ namespace MinecraftClient.Protocol /// /// The channel to register. /// The bot to register the channel for. - Task RegisterPluginChannel(string channel, ChatBot bot); + Task RegisterPluginChannelAsync(string channel, ChatBot bot); /// /// Unregisters the given plugin channel for the given bot. /// /// The channel to unregister. /// The bot to unregister the channel for. - Task UnregisterPluginChannel(string channel, ChatBot bot); + Task UnregisterPluginChannelAsync(string channel, ChatBot bot); /// /// Sends a plugin channel packet to the server. @@ -188,7 +189,7 @@ namespace MinecraftClient.Protocol /// The payload for the packet. /// Whether the packet should be sent even if the server or the client hasn't registered it yet. /// Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered. - Task SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false); + Task SendPluginChannelMessageAsync(string channel, byte[] data, bool sendEvenIfNotRegistered = false); /// /// Called when a plugin channel message was sent from the server. @@ -201,7 +202,7 @@ namespace MinecraftClient.Protocol /// Called when an entity has spawned /// /// Spawned entity - void OnSpawnEntity(Entity entity); + Task OnSpawnEntity(Entity entity); /// /// Called when an entity has spawned @@ -209,7 +210,7 @@ namespace MinecraftClient.Protocol /// Entity id /// Equipment slot. 0: main hand, 1: off hand, 2–5: armor slot (2: boots, 3: leggings, 4: chestplate, 5: helmet)/param> /// Item/param> - void OnEntityEquipment(int entityid, int slot, Item? item); + Task OnEntityEquipment(int entityid, int slot, Item? item); /// /// Called when a player spawns or enters the client's render distance @@ -219,13 +220,13 @@ namespace MinecraftClient.Protocol /// Entity location /// Player head yaw /// Player head pitch - void OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch); + Task OnSpawnPlayer(int entityID, Guid uuid, Location location, byte yaw, byte pitch); /// /// Called when entities have despawned /// /// List of Entity ID that have despawned - void OnDestroyEntities(int[] EntityID); + Task OnDestroyEntities(int[] EntityID); /// /// Called when an entity moved by coordinate offset @@ -235,7 +236,7 @@ namespace MinecraftClient.Protocol /// Y offset /// Z offset /// TRUE if on ground - void OnEntityPosition(int entityID, Double dx, Double dy, Double dz, bool onGround); + Task OnEntityPosition(int entityID, Double dx, Double dy, Double dz, bool onGround); /// /// Called when an entity moved to fixed coordinates @@ -245,28 +246,28 @@ namespace MinecraftClient.Protocol /// Y /// Z /// TRUE if on ground - void OnEntityTeleport(int entityID, Double x, Double y, Double z, bool onGround); + Task OnEntityTeleport(int entityID, Double x, Double y, Double z, bool onGround); /// /// Called when additional properties have been received for an entity /// /// Entity ID /// Dictionary of properties - void OnEntityProperties(int entityID, Dictionary prop); + Task OnEntityProperties(int entityID, Dictionary prop); /// /// Called when the status of an entity have been changed /// /// Entity ID /// Status ID - void OnEntityStatus(int entityID, byte status); + Task OnEntityStatus(int entityID, byte status); /// /// Called when the world age has been updated /// /// World age /// Time of Day - void OnTimeUpdate(long worldAge, long timeOfDay); + Task OnTimeUpdate(long worldAge, long timeOfDay); /// /// When received window properties from server. @@ -275,7 +276,7 @@ namespace MinecraftClient.Protocol /// Inventory ID /// Property ID /// Property Value - public void OnWindowProperties(byte inventoryID, short propertyId, short propertyValue); + Task OnWindowPropertiesAsync(byte inventoryID, short propertyId, short propertyValue); /// /// Called when inventory items have been received @@ -283,7 +284,7 @@ namespace MinecraftClient.Protocol /// Inventory ID /// Item list /// State ID - void OnWindowItems(byte inventoryID, Dictionary itemList, int stateId); + Task OnWindowItemsAsync(byte inventoryID, Dictionary itemList, int stateId); /// /// Called when a single slot has been updated inside an inventory @@ -292,14 +293,14 @@ namespace MinecraftClient.Protocol /// Slot ID /// Item (may be null for empty slot) /// State ID - void OnSetSlot(byte inventoryID, short slotID, Item? item, int stateId); + Task OnSetSlotAsync(byte inventoryID, short slotID, Item? item, int stateId); /// /// Called when player health or hunger changed. /// /// /// - void OnUpdateHealth(float health, int food); + Task OnUpdateHealth(float health, int food); /// /// Called when the health of an entity changed @@ -321,7 +322,7 @@ namespace MinecraftClient.Protocol /// Explosion location /// Explosion strength /// Amount of affected blocks - void OnExplosion(Location location, float strength, int affectedBlocks); + Task OnExplosion(Location location, float strength, int affectedBlocks); /// /// Called when a player's game mode has changed @@ -335,7 +336,7 @@ namespace MinecraftClient.Protocol /// /// Affected player's UUID /// latency - void OnLatencyUpdate(Guid uuid, int latency); + Task OnLatencyUpdate(Guid uuid, int latency); /// /// Called when Experience bar is updated @@ -343,14 +344,14 @@ namespace MinecraftClient.Protocol /// Experience bar level /// Player Level /// Total experience - void OnSetExperience(float Experiencebar, int Level, int TotalExperience); + Task OnSetExperience(float Experiencebar, int Level, int TotalExperience); /// /// Called when client need to change slot. /// /// Used for setting player slot after joining game /// - void OnHeldItemChange(byte slot); + Task OnHeldItemChange(byte slot); /// /// Called when an update of the map is sent by the server, take a look at https://wiki.vg/Protocol#Map_Data for more info on the fields @@ -366,7 +367,7 @@ namespace MinecraftClient.Protocol /// x offset of the westernmost column /// z offset of the northernmost row /// a byte array of colors on the map - void OnMapData(int mapid, byte scale, bool trackingPosition, bool locked, List icons, byte columnsUpdated, byte rowsUpdated, byte mapCoulmnX, byte mapRowZ, byte[]? colors); + Task OnMapData(MapData mapData); /// /// Called when the Player entity ID has been received from the server @@ -384,7 +385,7 @@ namespace MinecraftClient.Protocol /// effect flags /// has factor data /// factorCodec - void OnEntityEffect(int entityid, Effects effect, int amplifier, int duration, byte flags, bool hasFactorData, Dictionary? factorCodec); + Task OnEntityEffect(int entityid, Effect effect); /// /// Called when coreboardObjective diff --git a/MinecraftClient/Protocol/PacketPipeline/AesStream.cs b/MinecraftClient/Protocol/PacketPipeline/AesStream.cs index d8e0d0b6..63093819 100644 --- a/MinecraftClient/Protocol/PacketPipeline/AesStream.cs +++ b/MinecraftClient/Protocol/PacketPipeline/AesStream.cs @@ -15,6 +15,7 @@ namespace MinecraftClient.Protocol.PacketPipeline private const int BufferSize = 1024; public Socket Client; + public bool HwAccelerateEnable { init; get; } private bool inStreamEnded = false; private readonly IAesHandler Aes; @@ -44,11 +45,20 @@ namespace MinecraftClient.Protocol.PacketPipeline AesBufSend = new byte[BlockSize]; if (FasterAesX86.IsSupported()) + { + HwAccelerateEnable = true; Aes = new FasterAesX86(key); - else if (FasterAesArm.IsSupported()) + } + else if (false && FasterAesArm.IsSupported()) // Further testing required + { + HwAccelerateEnable = true; Aes = new FasterAesArm(key); + } else + { + HwAccelerateEnable = false; Aes = new BasicAes(key); + } key.CopyTo(InputBuf.Slice(0, BlockSize)); key.CopyTo(OutputBuf.Slice(0, BlockSize)); diff --git a/MinecraftClient/Protocol/PacketPipeline/SocketWrapper.cs b/MinecraftClient/Protocol/PacketPipeline/SocketWrapper.cs index e452b072..b96098ee 100644 --- a/MinecraftClient/Protocol/PacketPipeline/SocketWrapper.cs +++ b/MinecraftClient/Protocol/PacketPipeline/SocketWrapper.cs @@ -27,7 +27,7 @@ namespace MinecraftClient.Protocol.PacketPipeline public int CompressionThreshold { get; set; } = 0; - private SemaphoreSlim SendSemaphore = new SemaphoreSlim(1, 1); + private SemaphoreSlim SendSemaphore = new(1, 1); private Task LastSendTask = Task.CompletedTask; @@ -78,7 +78,8 @@ namespace MinecraftClient.Protocol.PacketPipeline /// data to send public async Task SendAsync(Memory buffer, CancellationToken cancellationToken = default) { - await SendSemaphore.WaitAsync(); + await SendSemaphore.WaitAsync(cancellationToken); + if (cancellationToken.IsCancellationRequested) return; await LastSendTask; LastSendTask = WriteStream.WriteAsync(buffer, cancellationToken).AsTask(); SendSemaphore.Release(); @@ -86,7 +87,6 @@ namespace MinecraftClient.Protocol.PacketPipeline public async Task> GetNextPacket(bool handleCompress, CancellationToken cancellationToken = default) { - // ConsoleIO.WriteLine("GetNextPacket"); if (packetStream != null) { await packetStream.DisposeAsync(); @@ -105,9 +105,18 @@ namespace MinecraftClient.Protocol.PacketPipeline ZlibBaseStream zlibBaseStream = new(AesStream ?? ReadStream, packetSize: packetSize - readed); ZLibStream zlibStream = new(zlibBaseStream, CompressionMode.Decompress, leaveOpen: false); - zlibBaseStream.BufferSize = 16; - (packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken); - zlibBaseStream.BufferSize = 512; + if (AesStream == null || AesStream.HwAccelerateEnable) + { + zlibBaseStream.BufferSize = 64; + (packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken); + zlibBaseStream.BufferSize = 1024; + } + else + { + zlibBaseStream.BufferSize = 16; + (packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken); + zlibBaseStream.BufferSize = 256; + } // ConsoleIO.WriteLine("packetID = " + packetID + ", readed = " + zlibBaseStream.packetReaded + ", size = " + packetSize + " -> " + sizeUncompressed); @@ -131,7 +140,7 @@ namespace MinecraftClient.Protocol.PacketPipeline byte[] b = new byte[1]; while (true) { - await stream.ReadAsync(b); + await stream.ReadAsync(b, cancellationToken); i |= (b[0] & 0x7F) << j++ * 7; if (j > 5) throw new OverflowException("VarInt too big"); if ((b[0] & 0x80) != 128) break; diff --git a/MinecraftClient/Protocol/PlayerInfo.cs b/MinecraftClient/Protocol/PlayerInfo.cs index 31e4300e..ab782d0c 100644 --- a/MinecraftClient/Protocol/PlayerInfo.cs +++ b/MinecraftClient/Protocol/PlayerInfo.cs @@ -22,7 +22,7 @@ namespace MinecraftClient.Protocol // Entity info - public Mapping.Entity? entity; + public EntityHandler.Entity? entity; // For message signature diff --git a/MinecraftClient/Protocol/TitlePacket.cs b/MinecraftClient/Protocol/TitlePacket.cs new file mode 100644 index 00000000..f1fe5d11 --- /dev/null +++ b/MinecraftClient/Protocol/TitlePacket.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinecraftClient.Protocol +{ + public record TitlePacket + { + public int Action { init; get; } + + public string TitleText { init; get; } = string.Empty; + + public string SubtitleText { init; get; } = string.Empty; + + public string ActionbarText { init; get; } = string.Empty; + + public int Stay { init; get; } + + public int FadeIn { init; get; } + + public int FadeOut { init; get; } + + public string JsonText { init; get; } = string.Empty; + } +} diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index dd3cd29d..d06a1716 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -3643,6 +3643,15 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to Player Inventory. + /// + internal static string cmd_inventory_player_inventory { + get { + return ResourceManager.GetString("cmd.inventory.player_inventory", resourceCulture); + } + } + /// /// Looks up a localized string similar to Right. /// @@ -4130,6 +4139,24 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to File {0} does not exist.. + /// + internal static string config_file_not_exist { + get { + return ResourceManager.GetString("config.file_not_exist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid file extension {0} in {1}, requires {2}.. + /// + internal static string config_invaild_file_extension { + get { + return ResourceManager.GetString("config.invaild_file_extension", resourceCulture); + } + } + /// /// Looks up a localized string similar to [Settings] The language code is invalid!. /// @@ -4148,6 +4175,15 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to You are using an older color scheme and some colors may not be displayed correctly. It is recommended that you switch to a modern terminal for a better display experience.. + /// + internal static string config_legacy_color { + get { + return ResourceManager.GetString("config.legacy_color", resourceCulture); + } + } + /// /// Looks up a localized string similar to Settings have been loaded from {0}. /// @@ -5130,6 +5166,16 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to An exception occurred while processing event {0}: + ///{1}. + /// + internal static string mcc_chatbot_event_exception { + get { + return ResourceManager.GetString("mcc.chatbot_event_exception", resourceCulture); + } + } + /// /// Looks up a localized string similar to Connecting to {0}.... /// diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index dd99ba28..5c69f145 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -2040,4 +2040,20 @@ Logging in... [Settings] Only Microsoft accounts support logging in using the browser method. + + Invalid file extension {0} in {1}, requires {2}. + + + File {0} does not exist. + + + You are using an older color scheme and some colors may not be displayed correctly. It is recommended that you switch to a modern terminal for a better display experience. + + + Player Inventory + + + An exception occurred while processing event {0}: +{1} + \ No newline at end of file diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 56c643ce..e3cbbb07 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Brigadier.NET; using MinecraftClient.CommandHandler; +using MinecraftClient.EntityHandler; using MinecraftClient.Inventory; using MinecraftClient.Mapping; using static MinecraftClient.Settings; @@ -37,7 +38,7 @@ namespace MinecraftClient.Scripting //Handler will be automatically set on bot loading, don't worry about this public void SetHandler(McClient handler) { _handler = handler; } protected void SetMaster(ChatBot master) { this.master = master; } - protected void LoadBot(ChatBot bot) { Handler.BotUnLoad(bot).Wait(); Handler.BotLoad(bot); } + protected void LoadBot(ChatBot bot) { Handler.BotUnLoad(bot).Wait(); Handler.BotLoad(bot).Wait(); } protected ChatBot[] GetLoadedChatBots() { return Handler.GetLoadedChatBots(); } protected void UnLoadBot(ChatBot bot) { Handler.BotUnLoad(bot).Wait(); } private McClient? _handler = null; @@ -105,6 +106,12 @@ namespace MinecraftClient.Scripting /// public virtual void Initialize() { } + /// + /// + /// + /// + public virtual Tuple>[]? InitializeEventCallbacks() { return null; } + /// /// This method is called when the bot is being unloaded, you can use it to free up resources like DB connections /// @@ -260,14 +267,6 @@ namespace MinecraftClient.Scripting /// New Game Mode (0: Survival, 1: Creative, 2: Adventure, 3: Spectator). public virtual void OnGamemodeUpdate(string playername, Guid uuid, int gamemode) { } - /// - /// Called when the Game Mode has been updated for a player - /// - /// Player Name - /// Player UUID - /// New Game Mode (0: Survival, 1: Creative, 2: Adventure, 3: Spectator). - public virtual async Task OnGamemodeUpdateAsync(string playername, Guid uuid, int gamemode) { await Task.CompletedTask; } - /// /// Called when the Latency has been updated for a player /// @@ -337,7 +336,7 @@ namespace MinecraftClient.Scripting /// effect amplifier /// effect duration /// effect flags - public virtual void OnEntityEffect(Entity entity, Effects effect, int amplifier, int duration, byte flags) { } + public virtual void OnEntityEffect(Entity entity, EffectType effect, int amplifier, int duration, byte flags) { } /// /// Called when a scoreboard objective updated @@ -518,7 +517,7 @@ namespace MinecraftClient.Scripting protected bool SendText(string text, bool sendImmediately = false) { LogToConsole("Sending '" + text + "'"); - Handler.SendText(text).Wait(); + Handler.SendTextAsync(text).Wait(); return true; } @@ -982,7 +981,7 @@ namespace MinecraftClient.Scripting /// Local variables for use in the Script protected void RunScript(string filename, string? playername = null, Dictionary? localVars = null) { - Handler.BotLoad(new ChatBots.Script(filename, playername, localVars)); + Handler.BotLoad(new ChatBots.Script(filename, playername, localVars)).Wait(); } /// @@ -991,7 +990,7 @@ namespace MinecraftClient.Scripting /// ChatBot to load protected void BotLoad(ChatBot chatBot) { - Handler.BotLoad(chatBot); + Handler.BotLoad(chatBot).Wait(); } /// @@ -1037,7 +1036,7 @@ namespace MinecraftClient.Scripting /// private bool SendEntityAction(Protocol.EntityActionType entityAction) { - return Handler.SendEntityAction(entityAction).Result; + return Handler.SendEntityActionAsync(entityAction).Result; } /// @@ -1045,7 +1044,7 @@ namespace MinecraftClient.Scripting /// private async Task SendEntityActionAsync(Protocol.EntityActionType entityAction) { - return await Handler.SendEntityAction(entityAction); + return await Handler.SendEntityActionAsync(entityAction); } /// @@ -1056,7 +1055,7 @@ namespace MinecraftClient.Scripting /// Also look at the block before digging protected bool DigBlock(Location location, bool swingArms = true, bool lookAtBlock = true) { - return Handler.DigBlock(location, swingArms, lookAtBlock).Result; + return Handler.DigBlockAsync(location, swingArms, lookAtBlock).Result; } /// @@ -1064,7 +1063,7 @@ namespace MinecraftClient.Scripting /// protected bool SetSlot(int slotNum) { - return Handler.ChangeSlot((short)slotNum).Result; + return Handler.ChangeSlotAsync((short)slotNum).Result; } /// @@ -1116,7 +1115,7 @@ namespace MinecraftClient.Scripting /// True if a path has been found 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); + return Handler.MoveToAsync(location, allowUnsafe, allowDirectTeleport, maxOffset, minOffset, timeout).Result; } /// @@ -1297,7 +1296,7 @@ namespace MinecraftClient.Scripting protected void RegisterPluginChannel(string channel) { registeredPluginChannels.Add(channel); - Handler.RegisterPluginChannel(channel, this).Wait(); + Handler.RegisterPluginChannelAsync(channel, this).Wait(); } /// @@ -1307,7 +1306,7 @@ namespace MinecraftClient.Scripting protected void UnregisterPluginChannel(string channel) { registeredPluginChannels.RemoveAll(chan => chan == channel); - Handler.UnregisterPluginChannel(channel, this).Wait(); + Handler.UnregisterPluginChannelAsync(channel, this).Wait(); } /// @@ -1327,7 +1326,7 @@ namespace MinecraftClient.Scripting return false; } } - return Handler.SendPluginChannelMessage(channel, data, sendEvenIfNotRegistered).Result; + return Handler.SendPluginChannelMessageAsync(channel, data, sendEvenIfNotRegistered).Result; } /// @@ -1349,7 +1348,7 @@ namespace MinecraftClient.Scripting [Obsolete("Prefer using InteractType enum instead of int for interaction type")] protected bool InteractEntity(int EntityID, int type, Hand hand = Hand.MainHand) { - return Handler.InteractEntity(EntityID, (InteractType)type, hand).Result; + return Handler.InteractEntityAsync(EntityID, (InteractType)type, hand).Result; } /// @@ -1361,7 +1360,7 @@ namespace MinecraftClient.Scripting /// TRUE in case of success protected bool InteractEntity(int EntityID, InteractType type, Hand hand = Hand.MainHand) { - return Handler.InteractEntity(EntityID, type, hand).Result; + return Handler.InteractEntityAsync(EntityID, type, hand).Result; } /// @@ -1375,7 +1374,7 @@ namespace MinecraftClient.Scripting /// TRUE if item given successfully protected bool CreativeGive(int slot, ItemType itemType, int count, Dictionary? nbt = null) { - return Handler.DoCreativeGive(slot, itemType, count, nbt).Result; + return Handler.DoCreativeGiveAsync(slot, itemType, count, nbt).Result; } /// @@ -1397,7 +1396,7 @@ namespace MinecraftClient.Scripting /// TRUE if animation successfully done public bool SendAnimation(Hand hand = Hand.MainHand) { - return Handler.DoAnimation((int)hand).Result; + return Handler.DoAnimationAsync((int)hand).Result; } /// @@ -1406,7 +1405,7 @@ namespace MinecraftClient.Scripting /// TRUE if successful protected bool UseItemInHand() { - return Handler.UseItemOnHand().Result; + return Handler.UseItemOnHandAsync().Result; } /// @@ -1415,7 +1414,7 @@ namespace MinecraftClient.Scripting /// TRUE if successful protected bool UseItemInLeftHand() { - return Handler.UseItemOnLeftHand().Result; + return Handler.UseItemOnOffHandAsync().Result; } /// @@ -1436,7 +1435,7 @@ namespace MinecraftClient.Scripting /// TRUE if successfully placed public bool SendPlaceBlock(Location location, Direction blockFace, Hand hand = Hand.MainHand) { - return Handler.PlaceBlock(location, blockFace, hand).Result; + return Handler.PlaceBlockAsync(location, blockFace, hand).Result; } /// @@ -1467,7 +1466,7 @@ namespace MinecraftClient.Scripting /// TRUE in case of success protected bool WindowAction(int inventoryId, int slot, WindowActionType actionType) { - return Handler.DoWindowAction(inventoryId, slot, actionType).Result; + return Handler.DoWindowActionAsync(inventoryId, slot, actionType).Result; } /// @@ -1487,7 +1486,7 @@ namespace MinecraftClient.Scripting /// True if success protected bool ChangeSlot(short slot) { - return Handler.ChangeSlot(slot).Result; + return Handler.ChangeSlotAsync(slot).Result; } /// @@ -1518,7 +1517,7 @@ namespace MinecraftClient.Scripting /// text1 four protected bool UpdateSign(Location location, string line1, string line2, string line3, string line4) { - return Handler.UpdateSign(location, line1, line2, line3, line4).Result; + return Handler.UpdateSignAsync(location, line1, line2, line3, line4).Result; } /// @@ -1527,7 +1526,7 @@ namespace MinecraftClient.Scripting /// Trade slot to select, starts at 0. protected bool SelectTrade(int selectedSlot) { - return Handler.SelectTrade(selectedSlot).Result; + return Handler.SelectTradeAsync(selectedSlot).Result; } /// @@ -1536,7 +1535,7 @@ namespace MinecraftClient.Scripting /// player to teleport to protected bool SpectatorTeleport(Entity entity) { - return Handler.Spectate(entity).Result; + return Handler.SpectateAsync(entity).Result; } /// @@ -1545,7 +1544,7 @@ namespace MinecraftClient.Scripting /// uuid of entity to teleport to protected bool SpectatorTeleport(Guid UUID) { - return Handler.SpectateByUUID(UUID).Result; + return Handler.SpectateByUuidAsync(UUID).Result; } /// @@ -1557,7 +1556,7 @@ namespace MinecraftClient.Scripting /// command block flags protected bool UpdateCommandBlock(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags) { - return Handler.UpdateCommandBlock(location, command, mode, flags).Result; + return Handler.UpdateCommandBlockAsync(location, command, mode, flags).Result; } /// @@ -1567,7 +1566,7 @@ namespace MinecraftClient.Scripting /// True if success protected bool CloseInventory(int inventoryID) { - return Handler.CloseInventory(inventoryID).Result; + return Handler.CloseInventoryAsync(inventoryID).Result; } /// @@ -1585,7 +1584,7 @@ namespace MinecraftClient.Scripting protected bool Respawn() { if (Handler.GetHealth() <= 0) - return Handler.SendRespawnPacket().Result; + return Handler.SendRespawnPacketAsync().Result; else return false; } diff --git a/MinecraftClient/Scripting/McClientEventType.cs b/MinecraftClient/Scripting/McClientEventType.cs new file mode 100644 index 00000000..2bee3fd9 --- /dev/null +++ b/MinecraftClient/Scripting/McClientEventType.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinecraftClient.Scripting +{ + public enum McClientEventType + { + /* Internal Events */ + Initialize, + ClientDisconnect, /* Tuple(reason, message) */ + ClientTick, + InternalCommand, + NetworkPacket, /* Tuple(packetID, packetData, isLogin, isInbound) */ + ServerTpsUpdate, /* double(tps) */ + + /* General in-game events */ + BlockBreakAnimation, + BlockChange, + Enchantments, /* EnchantmentData(lastEnchantment) */ + Explosion, /* Tuple(location, strength, affectedBlocks) */ + GameJoin, + MapDataReceive, + PluginMessage, + RainLevelChange, + ScoreboardUpdate, + ScoreUpdate, + TextReceive, /* Tuple(messageText, message.content) */ + ThunderLevelChange, + TimeUpdate, /* Tuple(WorldAge, TimeOfDay) */ + TitleReceive, /* TitlePacket(title) */ + TradeListReceive, + + /* Player Events */ + PlayerLatencyUpdate, /* Tuple(player, latency) */ + PlayerJoin, /* PlayerInfo(player) */ + PlayerKilled, /* Tuple(killer, chatMessage) */ + PlayerLeave, /* Tuple(uuid, playerInfo) */ + PlayerPropertyReceive, /* Dictionary(prop) */ + PlayerStatusUpdate, /* byte(status) */ + + /* Player's own events */ + Death, /* null */ + ExperienceChange, /* Tuple(Experiencebar, Level, TotalExperience) */ + GamemodeUpdate, /* Tuple(player, gamemode) */ + HealthUpdate, /* Tuple(health, food) */ + HeldItemChange, /* byte(slot) */ + Respawn, + + /* Inventory-related events */ + InventoryClose, /* int(inventoryID) */ + InventoryOpen, /* int(inventoryID) */ + InventoryProperties, /* Tuple(inventoryID, propertyId, propertyValue) */ + InventoryUpdate, /* int(inventoryID) */ + + /* Entity-related events */ + EntityAnimation, + EntityDespawn, /* Entity(entity) */ + EntityEffect, /* Tuple(entity, effect) */ + EntityEquipment, /* Tuple(entity, slot, item) */ + EntityHealth, + EntityMetadata, + EntityMove, /* Entity(entity) */ + EntitySpawn, /* Entity(entity) */ + }; +} diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 5f869f6e..d698cf92 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -129,7 +129,7 @@ namespace MinecraftClient public ChatBotConfig ChatBot { get { return ChatBotConfigHealper.Config; } - set { ChatBotConfigHealper.Config = value; } + set { ChatBotConfigHealper.Config = value; ChatBotConfigHealper.Config.OnSettingUpdate(); } } } @@ -797,6 +797,9 @@ namespace MinecraftClient public void OnSettingUpdate() { + if (General.ConsoleColorMode == ConsoleColorModeType.legacy_4bit) + ConsoleIO.WriteLineFormatted("§8" + Translations.config_legacy_color, true); + // Reader ConsoleInteractive.ConsoleReader.DisplayUesrInput = General.Display_Input; @@ -806,7 +809,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 @@ -1252,6 +1255,8 @@ namespace MinecraftClient [TomlDoNotInlineObject] public class ChatBotConfig { + public void OnSettingUpdate() { } + [TomlPrecedingComment("$ChatBot.Alerts$")] public ChatBots.Alerts.Configs Alerts {