From c7597e8822f5f956f65d624990ee4c3e9b58e5c1 Mon Sep 17 00:00:00 2001 From: Anon Date: Tue, 16 May 2023 23:19:31 +0200 Subject: [PATCH 1/4] Added slab handling 1.13-1.19.4 --- MinecraftClient/Commands/Sneak.cs | 38 +- MinecraftClient/Mapping/BlockExtension.cs | 412 ++++++++++++++++++ MinecraftClient/Mapping/MaterialExtensions.cs | 72 ++- MinecraftClient/Mapping/Movement.cs | 399 ++++++++--------- MinecraftClient/McClient.cs | 31 +- 5 files changed, 713 insertions(+), 239 deletions(-) create mode 100644 MinecraftClient/Mapping/BlockExtension.cs diff --git a/MinecraftClient/Commands/Sneak.cs b/MinecraftClient/Commands/Sneak.cs index 01d470fafc..f6538736b2 100644 --- a/MinecraftClient/Commands/Sneak.cs +++ b/MinecraftClient/Commands/Sneak.cs @@ -6,10 +6,9 @@ namespace MinecraftClient.Commands { public class Sneak : Command { - private bool sneaking = false; - public override string CmdName { get { return "sneak"; } } - public override string CmdUsage { get { return "sneak"; } } - public override string CmdDesc { get { return Translations.cmd_sneak_desc; } } + public override string CmdName => "sneak"; + public override string CmdUsage => "sneak"; + public override string CmdDesc => Translations.cmd_sneak_desc; public override void RegisterCommand(CommandDispatcher dispatcher) { @@ -39,27 +38,22 @@ private int GetUsage(CmdResult r, string? cmd) private int DoSneak(CmdResult r) { - McClient handler = CmdResult.currentHandler!; - if (sneaking) - { - var result = handler.SendEntityAction(Protocol.EntityActionType.StopSneaking); - if (result) - sneaking = false; - if (result) - return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_off); - else - return r.SetAndReturn(CmdResult.Status.Fail); - } - else + var handler = CmdResult.currentHandler!; + + if (handler.IsSneaking) { - var result = handler.SendEntityAction(Protocol.EntityActionType.StartSneaking); - if (result) - sneaking = true; - if (result) - return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_on); - else + if (!handler.SendEntityAction(Protocol.EntityActionType.StopSneaking)) return r.SetAndReturn(CmdResult.Status.Fail); + + handler.IsSneaking = false; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_off); } + + if (!handler.SendEntityAction(Protocol.EntityActionType.StartSneaking)) + return r.SetAndReturn(CmdResult.Status.Fail); + + handler.IsSneaking = true; + return r.SetAndReturn(CmdResult.Status.Done, Translations.cmd_sneak_on); } } } \ No newline at end of file diff --git a/MinecraftClient/Mapping/BlockExtension.cs b/MinecraftClient/Mapping/BlockExtension.cs new file mode 100644 index 0000000000..f644279032 --- /dev/null +++ b/MinecraftClient/Mapping/BlockExtension.cs @@ -0,0 +1,412 @@ +using MinecraftClient.Protocol.Handlers; + +namespace MinecraftClient.Mapping; + +public static class BlockExtension +{ + public static bool IsTopSlab(this Block block, int protocolVersion) + { + //if (block.BlockId != 0) + // ConsoleIO.WriteLine("Checking is block: " + block + " is a top slab, protocol: " + protocolVersion); + + if (protocolVersion >= Protocol18Handler.MC_1_19_4_Version) + { + switch (block.BlockId) + { + case 11018: // OakSlab + case 11024: // SpruceSlab + case 11030: // BirchSlab + case 11036: // JungleSlab + case 11042: // AcaciaSlab + case 11054: // DarkOakSlab + case 11060: // MangroveSlab + case 18510: // CrimsonSlab + case 18516: // WarpedSlab + case 11078: // StoneSlab + case 11108: // CobblestoneSlab + case 13948: // MossyCobblestoneSlab + case 11084: // SmoothStoneSlab + case 11120: // StoneBrickSlab + case 13936: // MossyStoneBrickSlab + case 13972: // GraniteSlab + case 13924: // PolishedGraniteSlab + case 13996: // DioriteSlab + case 13942: // PolishedDioriteSlab + case 13978: // AndesiteSlab + case 13990: // PolishedAndesiteSlab + case 22132: // CobbledDeepslateSlab + case 22543: // PolishedDeepslateSlab + case 23365: // DeepslateBrickSlab + case 22954: // DeepslateTileSlab + case 11114: // BrickSlab + case 11126: // MudBrickSlab + case 11090: // SandstoneSlab + case 13960: // SmoothSandstoneSlab + case 11096: // CutSandstoneSlab + case 11144: // RedSandstoneSlab + case 13930: // SmoothRedSandstoneSlab + case 11150: // CutRedSandstoneSlab + case 10562: // PrismarineSlab + case 10568: // PrismarineBrickSlab + case 10574: // DarkPrismarineSlab + case 11132: // NetherBrickSlab + case 13984: // RedNetherBrickSlab + case 19707: // BlackstoneSlab + case 20208: // PolishedBlackstoneSlab + case 19717: // PolishedBlackstoneBrickSlab + case 13954: // EndStoneBrickSlab + case 11156: // PurpurSlab + case 11138: // QuartzSlab + case 13966: // SmoothQuartzSlab + case 21510: // CutCopperSlab + case 21504: // ExposedCutCopperSlab + case 21498: // WeatheredCutCopperSlab + case 21492: // OxidizedCutCopperSlab + case 21862: // WaxedCutCopperSlab + case 21856: // WaxedExposedCutCopperSlab + case 21850: // WaxedWeatheredCutCopperSlab + case 21844: // WaxedOxidizedCutCopperSlab + return true; + } + } + else if (protocolVersion == Protocol18Handler.MC_1_13_Version) + { + switch (block.BlockId) + { + case 19257: // CutCopperSlab + case 19251: // ExposedCutCopperSlab + case 19245: // WeatheredCutCopperSlab + case 19239: // OxidizedCutCopperSlab + case 19609: // WaxedCutCopperSlab + case 19603: // WaxedExposedCutCopperSlab + case 19597: // WaxedWeatheredCutCopperSlab + case 19591: // WaxedOxidizedCutCopperSlab + case 9042: // OakSlab + case 9048: // SpruceSlab + case 9054: // BirchSlab + case 9060: // JungleSlab + case 9066: // AcaciaSlab + case 9072: // DarkOakSlab + case 9078: // MangroveSlab + case 16257: // CrimsonSlab + case 16263: // WarpedSlab + case 9084: // StoneSlab + case 9090: // SmoothStoneSlab + case 9096: // SandstoneSlab + case 9102: // CutSandstoneSlab + case 9108: // PetrifiedOakSlab + case 9114: // CobblestoneSlab + case 9120: // BrickSlab + case 9126: // StoneBrickSlab + case 9132: // MudBrickSlab + case 9138: // NetherBrickSlab + case 9144: // QuartzSlab + case 9150: // RedSandstoneSlab + case 9156: // CutRedSandstoneSlab + case 9162: // PurpurSlab + case 8586: // PrismarineSlab + case 8592: // PrismarineBrickSlab + case 8598: // DarkPrismarineSlab + case 11671: // PolishedGraniteSlab + case 11677: // SmoothRedSandstoneSlab + case 11683: // MossyStoneBrickSlab + case 11689: // PolishedDioriteSlab + case 11695: // MossyCobblestoneSlab + case 11701: // EndStoneBrickSlab + case 11707: // SmoothSandstoneSlab + case 11713: // SmoothQuartzSlab + case 11719: // GraniteSlab + case 11725: // AndesiteSlab + case 11731: // RedNetherBrickSlab + case 11737: // PolishedAndesiteSlab + case 11743: // DioriteSlab + case 19863: // CobbledDeepslateSlab + case 20274: // PolishedDeepslateSlab + case 21096: // DeepslateBrickSlab + case 20685: // DeepslateTileSlab + case 17454: // BlackstoneSlab + case 17955: // PolishedBlackstoneSlab + case 17464: // PolishedBlackstoneBrickSlab + return true; + } + } + else if (protocolVersion >= Protocol18Handler.MC_1_19_Version) + { + switch (block.BlockId) + { + case 19257: // CutCopperSlab + case 19251: // ExposedCutCopperSlab + case 19245: // WeatheredCutCopperSlab + case 19239: // OxidizedCutCopperSlab + case 19609: // WaxedCutCopperSlab + case 19603: // WaxedExposedCutCopperSlab + case 19597: // WaxedWeatheredCutCopperSlab + case 19591: // WaxedOxidizedCutCopperSlab + case 9042: // OakSlab + case 9048: // SpruceSlab + case 9054: // BirchSlab + case 9060: // JungleSlab + case 9066: // AcaciaSlab + case 9072: // DarkOakSlab + case 9078: // MangroveSlab + case 16257: // CrimsonSlab + case 16263: // WarpedSlab + case 9084: // StoneSlab + case 9090: // SmoothStoneSlab + case 9096: // SandstoneSlab + case 9102: // CutSandstoneSlab + case 9108: // PetrifiedOakSlab + case 9114: // CobblestoneSlab + case 9120: // BrickSlab + case 9126: // StoneBrickSlab + case 9132: // MudBrickSlab + case 9138: // NetherBrickSlab + case 9144: // QuartzSlab + case 9150: // RedSandstoneSlab + case 9156: // CutRedSandstoneSlab + case 9162: // PurpurSlab + case 8586: // PrismarineSlab + case 8592: // PrismarineBrickSlab + case 8598: // DarkPrismarineSlab + case 11671: // PolishedGraniteSlab + case 11677: // SmoothRedSandstoneSlab + case 11683: // MossyStoneBrickSlab + case 11689: // PolishedDioriteSlab + case 11695: // MossyCobblestoneSlab + case 11701: // EndStoneBrickSlab + case 11707: // SmoothSandstoneSlab + case 11713: // SmoothQuartzSlab + case 11719: // GraniteSlab + case 11725: // AndesiteSlab + case 11731: // RedNetherBrickSlab + case 11737: // PolishedAndesiteSlab + case 11743: // DioriteSlab + case 19863: // CobbledDeepslateSlab + case 20274: // PolishedDeepslateSlab + case 21096: // DeepslateBrickSlab + case 20685: // DeepslateTileSlab + case 17454: // BlackstoneSlab + case 17955: // PolishedBlackstoneSlab + case 17464: // PolishedBlackstoneBrickSlab + return true; + } + } + else if (protocolVersion >= Protocol18Handler.MC_1_17_Version) + { + switch (block.BlockId) + { + case 18163: // CutCopperSlab + case 18157: // ExposedCutCopperSlab + case 18151: // WeatheredCutCopperSlab + case 18145: // OxidizedCutCopperSlab + case 18515: // WaxedCutCopperSlab + case 18509: // WaxedExposedCutCopperSlab + case 18503: // WaxedWeatheredCutCopperSlab + case 18497: // WaxedOxidizedCutCopperSlab + case 8551: // OakSlab + case 8557: // SpruceSlab + case 8563: // BirchSlab + case 8569: // JungleSlab + case 8575: // AcaciaSlab + case 8581: // DarkOakSlab + case 15302: // CrimsonSlab + case 15308: // WarpedSlab + case 8587: // StoneSlab + case 8593: // SmoothStoneSlab + case 8599: // SandstoneSlab + case 8605: // CutSandstoneSlab + case 8611: // PetrifiedOakSlab + case 8617: // CobblestoneSlab + case 8623: // BrickSlab + case 8629: // StoneBrickSlab + case 8635: // NetherBrickSlab + case 8641: // QuartzSlab + case 8647: // RedSandstoneSlab + case 8653: // CutRedSandstoneSlab + case 8659: // PurpurSlab + case 8095: // PrismarineSlab + case 8101: // PrismarineBrickSlab + case 8107: // DarkPrismarineSlab + case 11040: // PolishedGraniteSlab + case 11046: // SmoothRedSandstoneSlab + case 11052: // MossyStoneBrickSlab + case 11058: // PolishedDioriteSlab + case 11064: // MossyCobblestoneSlab + case 11070: // EndStoneBrickSlab + case 11076: // SmoothSandstoneSlab + case 11082: // SmoothQuartzSlab + case 11088: // GraniteSlab + case 11094: // AndesiteSlab + case 11100: // RedNetherBrickSlab + case 11106: // PolishedAndesiteSlab + case 11112: // DioriteSlab + case 18768: // CobbledDeepslateSlab + case 19179: // PolishedDeepslateSlab + case 20001: // DeepslateBrickSlab + case 19590: // DeepslateTileSlab + case 16499: // BlackstoneSlab + case 17000: // PolishedBlackstoneSlab + case 16509: // PolishedBlackstoneBrickSlab + return true; + } + } + else if (protocolVersion >= Protocol18Handler.MC_1_16_Version) + { + switch (block.BlockId) + { + case 8305: // OakSlab + case 8311: // SpruceSlab + case 8317: // BirchSlab + case 8323: // JungleSlab + case 8329: // AcaciaSlab + case 8335: // DarkOakSlab + case 15056: // CrimsonSlab + case 15062: // WarpedSlab + case 8341: // StoneSlab + case 8347: // SmoothStoneSlab + case 8353: // SandstoneSlab + case 8359: // CutSandstoneSlab + case 8365: // PetrifiedOakSlab + case 8371: // CobblestoneSlab + case 8377: // BrickSlab + case 8383: // StoneBrickSlab + case 8389: // NetherBrickSlab + case 8395: // QuartzSlab + case 8401: // RedSandstoneSlab + case 8407: // CutRedSandstoneSlab + case 8413: // PurpurSlab + case 7849: // PrismarineSlab + case 7855: // PrismarineBrickSlab + case 7861: // DarkPrismarineSlab + case 10794: // PolishedGraniteSlab + case 10800: // SmoothRedSandstoneSlab + case 10806: // MossyStoneBrickSlab + case 10812: // PolishedDioriteSlab + case 10818: // MossyCobblestoneSlab + case 10824: // EndStoneBrickSlab + case 10830: // SmoothSandstoneSlab + case 10836: // SmoothQuartzSlab + case 10842: // GraniteSlab + case 10848: // AndesiteSlab + case 10854: // RedNetherBrickSlab + case 10860: // PolishedAndesiteSlab + case 10866: // DioriteSlab + case 16253: // BlackstoneSlab + case 16754: // PolishedBlackstoneSlab + case 16263: // PolishedBlackstoneBrickSlab + return true; + } + } + else if (protocolVersion >= Protocol18Handler.MC_1_15_Version) + { + switch (block.BlockId) + { + case 7765: // OakSlab + case 7771: // SpruceSlab + case 7777: // BirchSlab + case 7783: // JungleSlab + case 7789: // AcaciaSlab + case 7795: // DarkOakSlab + case 7801: // StoneSlab + case 7807: // SmoothStoneSlab + case 7813: // SandstoneSlab + case 7819: // CutSandstoneSlab + case 7825: // PetrifiedOakSlab + case 7831: // CobblestoneSlab + case 7837: // BrickSlab + case 7843: // StoneBrickSlab + case 7849: // NetherBrickSlab + case 7855: // QuartzSlab + case 7861: // RedSandstoneSlab + case 7867: // CutRedSandstoneSlab + case 7873: // PurpurSlab + case 7309: // PrismarineSlab + case 7321: // DarkPrismarineSlab + case 10254: // PolishedGraniteSlab + case 10260: // SmoothRedSandstoneSlab + case 10266: // MossyStoneBrickSlab + case 10272: // PolishedDioriteSlab + case 10278: // MossyCobblestoneSlab + case 10284: // EndStoneBrickSlab + case 10290: // SmoothSandstoneSlab + case 10296: // SmoothQuartzSlab + case 10302: // GraniteSlab + case 10308: // AndesiteSlab + case 10314: // RedNetherBrickSlab + case 10320: // PolishedAndesiteSlab + case 10326: // DioriteSlab + return true; + } + } + else if (protocolVersion >= Protocol18Handler.MC_1_14_Version) + { + switch (block.BlockId) + { + case 7765: // OakSlab + case 7771: // SpruceSlab + case 7777: // BirchSlab + case 7783: // JungleSlab + case 7789: // AcaciaSlab + case 7795: // DarkOakSlab + case 7801: // StoneSlab + case 7807: // SmoothStoneSlab + case 7813: // SandstoneSlab + case 7819: // CutSandstoneSlab + case 7825: // PetrifiedOakSlab + case 7831: // CobblestoneSlab + case 7837: // BrickSlab + case 7843: // StoneBrickSlab + case 7849: // NetherBrickSlab + case 7855: // QuartzSlab + case 7861: // RedSandstoneSlab + case 7867: // CutRedSandstoneSlab + case 7873: // PurpurSlab + case 7309: // PrismarineSlab + case 7315: // PrismarineBrickSlab + case 7321: // DarkPrismarineSlab + case 10254: // PolishedGraniteSlab + case 10260: // SmoothRedSandstoneSlab + case 10266: // MossyStoneBrickSlab + case 10272: // PolishedDioriteSlab + case 10278: // MossyCobblestoneSlab + case 10284: // EndStoneBrickSlab + case 10290: // SmoothSandstoneSlab + case 10296: // SmoothQuartzSlab + case 10302: // GraniteSlab + case 10308: // AndesiteSlab + case 10314: // RedNetherBrickSlab + case 10320: // PolishedAndesiteSlab + case 10326: // DioriteSlab + return true; + } + } + else if (protocolVersion >= Protocol18Handler.MC_1_13_Version) + { + switch (block.BlockId) + { + case 7258: // OakSlab + case 7264: // SpruceSlab + case 7270: // BirchSlab + case 7276: // JungleSlab + case 7282: // AcaciaSlab + case 7288: // DarkOakSlab + case 7294: // StoneSlab + case 7300: // SandstoneSlab + case 7306: // PetrifiedOakSlab + case 7312: // CobblestoneSlab + case 7318: // BrickSlab + case 7324: // StoneBrickSlab + case 7330: // NetherBrickSlab + case 7336: // QuartzSlab + case 7342: // RedSandstoneSlab + case 7348: // PurpurSlab + case 6802: // PrismarineSlab + case 6808: // PrismarineBrickSlab + case 6814: // DarkPrismarineSlab + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/MinecraftClient/Mapping/MaterialExtensions.cs b/MinecraftClient/Mapping/MaterialExtensions.cs index e961ebde73..2e62d69520 100644 --- a/MinecraftClient/Mapping/MaterialExtensions.cs +++ b/MinecraftClient/Mapping/MaterialExtensions.cs @@ -728,6 +728,7 @@ public static bool IsSolid(this Material m) return false; } } + /// /// Check if contact with the provided material can harm players /// @@ -817,5 +818,74 @@ public static bool IsBed(this Material m) return false; } } + + /// + /// Check if the provided material is a slab + /// + /// Material to test + /// True if the material is a slab + public static bool IsSlab(this Material m) + { + switch (m) + { + case Material.AcaciaSlab: + case Material.AndesiteSlab: + case Material.BirchSlab: + case Material.BlackstoneSlab: + case Material.BrickSlab: + case Material.CobbledDeepslateSlab: + case Material.CobblestoneSlab: + case Material.CrimsonSlab: + case Material.CutCopperSlab: + case Material.CutRedSandstoneSlab: + case Material.CutSandstoneSlab: + case Material.DarkOakSlab: + case Material.DarkPrismarineSlab: + case Material.DeepslateBrickSlab: + case Material.DeepslateTileSlab: + case Material.DioriteSlab: + case Material.EndStoneBrickSlab: + case Material.ExposedCutCopperSlab: + case Material.GraniteSlab: + case Material.JungleSlab: + case Material.MangroveSlab: + case Material.MossyCobblestoneSlab: + case Material.MossyStoneBrickSlab: + case Material.MudBrickSlab: + case Material.NetherBrickSlab: + case Material.OakSlab: + case Material.OxidizedCutCopperSlab: + case Material.PetrifiedOakSlab: + case Material.PolishedAndesiteSlab: + case Material.PolishedBlackstoneBrickSlab: + case Material.PolishedBlackstoneSlab: + case Material.PolishedDeepslateSlab: + case Material.PolishedDioriteSlab: + case Material.PolishedGraniteSlab: + case Material.PrismarineBrickSlab: + case Material.PrismarineSlab: + case Material.PurpurSlab: + case Material.QuartzSlab: + case Material.RedNetherBrickSlab: + case Material.RedSandstoneSlab: + case Material.SandstoneSlab: + case Material.SmoothQuartzSlab: + case Material.SmoothRedSandstoneSlab: + case Material.SmoothSandstoneSlab: + case Material.SmoothStoneSlab: + case Material.SpruceSlab: + case Material.StoneBrickSlab: + case Material.StoneSlab: + case Material.WarpedSlab: + case Material.WaxedCutCopperSlab: + case Material.WaxedExposedCutCopperSlab: + case Material.WaxedOxidizedCutCopperSlab: + case Material.WaxedWeatheredCutCopperSlab: + case Material.WeatheredCutCopperSlab: + return true; + default: + return false; + } + } } -} +} \ No newline at end of file diff --git a/MinecraftClient/Mapping/Movement.cs b/MinecraftClient/Mapping/Movement.cs index 2f74cfa60d..caed454d0b 100644 --- a/MinecraftClient/Mapping/Movement.cs +++ b/MinecraftClient/Mapping/Movement.cs @@ -30,15 +30,17 @@ public static Location HandleGravity(World world, Location location, ref double belowFoots = location; belowFoots.Y = Math.Truncate(location.Y); } + if (!IsOnGround(world, location) && !IsSwimming(world, location)) { while (!IsOnGround(world, belowFoots) && belowFoots.Y >= 1 + World.GetDimension().minY) belowFoots = Move(belowFoots, Direction.Down); location = Move2Steps(location, belowFoots, ref motionY, true).Dequeue(); } - else if (!(world.GetBlock(onFoots).Type.IsSolid())) + else if (!world.GetBlock(onFoots).Type.IsSolid()) location = Move2Steps(location, onFoots, ref motionY, true).Dequeue(); } + return location; } @@ -46,10 +48,11 @@ public static Location HandleGravity(World world, Location location, ref double /// Return a list of possible moves for the player /// /// World the player is currently located in - /// Location the player is currently at + /// Location the player is currently at /// Allow possible but unsafe locations /// A list of new locations the player can move to - public static IEnumerable GetAvailableMoves(World world, Location originLocation, bool allowUnsafe = false) + public static IEnumerable GetAvailableMoves(World world, Location originLocation, + bool allowUnsafe = false) { Location location = originLocation.ToCenter(); List availableMoves = new(); @@ -65,10 +68,12 @@ public static IEnumerable GetAvailableMoves(World world, Location orig else { foreach (Direction dir in new[] { Direction.East, Direction.West, Direction.North, Direction.South }) - if (CanMove(world, location, dir) && IsOnGround(world, Move(location, dir)) && (allowUnsafe || IsSafe(world, Move(location, dir)))) + if (CanMove(world, location, dir) && IsOnGround(world, Move(location, dir)) && + (allowUnsafe || IsSafe(world, Move(location, dir)))) availableMoves.Add(Move(location, dir)); availableMoves.Add(Move(location, Direction.Down)); } + return availableMoves; } @@ -85,7 +90,8 @@ public static IEnumerable GetAvailableMoves(World world, Location orig /// Specify if performing falling steps /// Amount of steps by block /// A list of locations corresponding to the requested steps - public static Queue Move2Steps(Location start, Location goal, ref double motionY, bool falling = false, int stepsByBlock = 8) + public static Queue Move2Steps(Location start, Location goal, ref double motionY, + bool falling = false, int stepsByBlock = 8) { if (stepsByBlock <= 0) stepsByBlock = 1; @@ -93,17 +99,17 @@ public static Queue Move2Steps(Location start, Location goal, ref doub if (falling) { //Use MC-Like falling algorithm - double Y = start.Y; + double y = start.Y; Queue fallSteps = new(); fallSteps.Enqueue(start); - double motionPrev = motionY; motionY -= 0.08D; motionY *= 0.9800000190734863D; - Y += motionY; - if (Y < goal.Y) + y += motionY; + + if (y < goal.Y) return new Queue(new[] { goal }); - else - return new Queue(new[] { new Location(start.X, Y, start.Z) }); + + return new Queue(new[] { new Location(start.X, y, start.Z) }); } else { @@ -132,6 +138,7 @@ public static Queue Move2Steps(Location start, Location goal, ref doub /// Based on the A* pathfinding algorithm described on Wikipedia /// /// + /// World /// Start location /// Destination location /// Allow possible but unsafe locations @@ -140,16 +147,19 @@ public static Queue Move2Steps(Location start, Location goal, ref doub /// 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 Queue? 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)); + Task?> pathfindingTask = Task.Factory.StartNew(() => + CalculatePath(world, start, goal, allowUnsafe, maxOffset, minOffset, cts.Token)); pathfindingTask.Wait(timeout); if (!pathfindingTask.IsCompleted) { cts.Cancel(); pathfindingTask.Wait(); } + return pathfindingTask.Result; } @@ -160,6 +170,7 @@ public static Queue Move2Steps(Location start, Location goal, ref doub /// Based on the A* pathfinding algorithm described on Wikipedia /// /// + /// World /// Start location /// Destination location /// Allow possible but unsafe locations @@ -167,7 +178,8 @@ public static Queue Move2Steps(Location start, Location goal, ref doub /// Do not get closer of destination than specified distance /// Token for stopping computation after a certain time /// 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, CancellationToken ct) + public static Queue? CalculatePath(World world, Location start, Location goal, bool allowUnsafe, + int maxOffset, int minOffset, CancellationToken ct) { // This is a bad configuration if (minOffset > maxOffset) @@ -181,12 +193,10 @@ public static Queue Move2Steps(Location start, Location goal, ref doub minOffset *= minOffset; maxOffset *= maxOffset; - ///---/// // Prepare variables and datastructures for A* - ///---/// // Dictionary that contains the relation between all coordinates and resolves the final path - Dictionary CameFrom = new(); + Dictionary cameFrom = new(); // Create a Binary Heap for all open positions => Allows fast access to Nodes with lowest scores BinaryHeap openSet = new(); // Dictionary to keep track of the G-Score of every location @@ -197,9 +207,7 @@ public static Queue Move2Steps(Location start, Location goal, ref doub gScoreDict[startLower] = 0; BinaryHeap.Node? current = null; - ///---/// // Start of A* - ///---/// // Execute while we have nodes to process and we are not cancelled while (openSet.Count() > 0 && !ct.IsCancellationRequested) @@ -209,10 +217,9 @@ public static Queue Move2Steps(Location start, Location goal, ref doub current = openSet.GetRootLocation(); // Return if goal found and no maxOffset was given OR current node is between minOffset and maxOffset - if ((current.Location == goalLower && maxOffset <= 0) || (maxOffset > 0 && current.H_score >= minOffset && current.H_score <= maxOffset)) - { - return ReconstructPath(CameFrom, current.Location, start, goal); - } + if ((current.Location == goalLower && maxOffset <= 0) || + (maxOffset > 0 && current.HScore >= minOffset && current.HScore <= maxOffset)) + return ReconstructPath(cameFrom, current.Location, start, goal); // Discover neighbored blocks foreach (Location neighbor in GetAvailableMoves(world, current.Location, allowUnsafe)) @@ -221,14 +228,15 @@ public static Queue Move2Steps(Location start, Location goal, ref doub if (ct.IsCancellationRequested) break; - // tentative_gScore is the distance from start to the neighbor through current - int tentativeGScore = current.G_score + (int)current.Location.DistanceSquared(neighbor); + // tentative_GScore is the distance from start to the neighbor through current + int tentativeGScore = current.GScore + (int)current.Location.DistanceSquared(neighbor); - // If the neighbor is not in the gScoreDict OR its current tentativeGScore is lower than the previously saved one: - if (!gScoreDict.ContainsKey(neighbor) || (gScoreDict.ContainsKey(neighbor) && tentativeGScore < gScoreDict[neighbor])) + // If the neighbor is not in the GScoreDict OR its current tentativeGScore is lower than the previously saved one: + if (!gScoreDict.ContainsKey(neighbor) || + (gScoreDict.ContainsKey(neighbor) && tentativeGScore < gScoreDict[neighbor])) { // Save the new relation between the neighbored block and the current one - CameFrom[neighbor] = current.Location; + cameFrom[neighbor] = current.Location; gScoreDict[neighbor] = tentativeGScore; // If this location is not already included in the Binary Heap: save it @@ -238,47 +246,51 @@ public static Queue Move2Steps(Location start, Location goal, ref doub } } - //// Goal could not be reached. Set the path to the closest location if close enough - if (current != null && openSet.MinH_ScoreNode != null && (maxOffset == int.MaxValue || openSet.MinH_ScoreNode.H_score <= maxOffset)) - return ReconstructPath(CameFrom, openSet.MinH_ScoreNode.Location, start, goal); - else - return null; + // Goal could not be reached. Set the path to the closest location if close enough + if (current != null && openSet.MinHScoreNode != null && + (maxOffset == int.MaxValue || openSet.MinHScoreNode.HScore <= maxOffset)) + return ReconstructPath(cameFrom, openSet.MinHScoreNode.Location, start, goal); + + return null; } /// /// Helper function for CalculatePath(). Backtrack from goal to start to reconstruct a step-by-step path. /// - /// The collection of Locations that leads back to the start + /// The collection of Locations that leads back to the start /// Endpoint of our later walk + /// Start location + /// End location /// the path that leads to current from the start position - private static Queue ReconstructPath(Dictionary Came_From, Location current, Location start, Location end) + private static Queue ReconstructPath(Dictionary cameFrom, Location current, + Location start, Location end) { int midPathCnt = 0; - List total_path = new(); + List totalPath = new(); // Move from the center of the block to the final position if (current != end && current == end.ToFloor()) - total_path.Add(end); + totalPath.Add(end); // Generate intermediate paths - total_path.Add(current.ToCenter()); - while (Came_From.ContainsKey(current)) + totalPath.Add(current.ToCenter()); + while (cameFrom.ContainsKey(current)) { ++midPathCnt; - current = Came_From[current]; - total_path.Add(current.ToCenter()); + current = cameFrom[current]; + totalPath.Add(current.ToCenter()); } if (midPathCnt <= 2 && start.DistanceSquared(end) < 2.0) - return new Queue(new Location[] { end }); + return new Queue(new[] { end }); else { // Move to the center of the block first if (current != start && current == start.ToFloor()) - total_path.Add(start.ToCenter()); + totalPath.Add(start.ToCenter()); - total_path.Reverse(); - return new Queue(total_path); + totalPath.Reverse(); + return new Queue(totalPath); } } @@ -297,73 +309,80 @@ public class BinaryHeap public class Node { // Distance to start - public int G_score; + public int GScore; + // Distance to Goal - public int H_score; - public int F_score { get { return H_score + G_score; } } + public int HScore; + + public int FScore + { + get { return HScore + GScore; } + } public Location Location; - public Node(int g_score, int h_score, Location loc) + public Node(int gScore, int hScore, Location loc) { - G_score = g_score; - H_score = h_score; + this.GScore = gScore; + this.HScore = hScore; Location = loc; } } // List which contains all nodes in form of a Binary Heap private readonly List heapList; + // Hashset for quick checks of locations included in the heap private readonly HashSet locationList; - public Node? MinH_ScoreNode; + public Node? MinHScoreNode; public BinaryHeap() { heapList = new List(); locationList = new HashSet(); - MinH_ScoreNode = null; + MinHScoreNode = null; } /// /// Insert a new location in the heap /// - /// G-Score of the location - /// H-Score of the location + /// G-Score of the location + /// H-Score of the location /// The location - public void Insert(int newG_Score, int newH_Score, Location loc) + public void Insert(int newGScore, int newHScore, Location loc) { // Begin at the end of the list int i = heapList.Count; // Temporarily save the node created with the parameters to allow comparisons - Node newNode = new(newG_Score, newH_Score, loc); + Node newNode = new(newGScore, newHScore, loc); // Add new note to the end of the list heapList.Add(newNode); locationList.Add(loc); // Save node with the smallest H-Score => Distance to goal - if (MinH_ScoreNode == null || newNode.H_score < MinH_ScoreNode.H_score) - MinH_ScoreNode = newNode; + if (MinHScoreNode == null || newNode.HScore < MinHScoreNode.HScore) + MinHScoreNode = newNode; + + if (i == 0) + return; // There is no need of sorting for one node. - if (i > 0) + // Go up the heap from child to parent and move parent down... + // while we are not looking at the root node AND the new node has better attributes than the parent node ((i - 1) / 2) + while (i > 0 && FirstNodeBetter(newNode /* Current Child */, + heapList[(i - 1) / 2] /* Corresponding Parent */)) { - /// Go up the heap from child to parent and move parent down... - // while we are not looking at the root node AND the new node has better attributes than the parent node ((i - 1) / 2) - while (i > 0 && FirstNodeBetter(newNode /* Current Child */, heapList[(i - 1) / 2] /* Coresponding Parent */)) - { - // Move parent down and replace current child -> New free space is created - heapList[i] = heapList[(i - 1) / 2]; - // Select the next parent to check - i = (i - 1) / 2; - } - - /// Nodes were moved down at position I there is now a free space at the correct position for our new node: - // Insert new node in position - heapList[i] = newNode; + // Move parent down and replace current child -> New free space is created + heapList[i] = heapList[(i - 1) / 2]; + // Select the next parent to check + i = (i - 1) / 2; } + + // Nodes were moved down at position I there is now a free space at the correct position for our new node: + // Insert new node in position + heapList[i] = newNode; } /// @@ -375,16 +394,14 @@ public Node GetRootLocation() { // The heap is empty. There is nothing to return. if (heapList.Count == 0) - { throw new InvalidOperationException("The heap is empty."); - } // Save the root node - Node rootNode = heapList[0]; + var rootNode = heapList[0]; locationList.Remove(rootNode.Location); // Temporarirly store the last item's value. - Node lastNode = heapList[^1]; + var lastNode = heapList[^1]; // Remove the last value. heapList.RemoveAt(heapList.Count - 1); @@ -392,17 +409,18 @@ public Node GetRootLocation() if (heapList.Count > 0) { // Start at the first index. - int currentParentPos = 0; + var currentParentPos = 0; - /// Go through the heap from root to bottom... + // Go through the heap from root to bottom... // Continue until the halfway point of the heap. while (currentParentPos < heapList.Count / 2) { // Select the left child of the current parent - int currentChildPos = (2 * currentParentPos) + 1; + var currentChildPos = (2 * currentParentPos) + 1; // If the currently selected child is not the last entry of the list AND right child has better attributes - if ((currentChildPos < heapList.Count - 1) && FirstNodeBetter(heapList[currentChildPos + 1], heapList[currentChildPos])) + if ((currentChildPos < heapList.Count - 1) && FirstNodeBetter(heapList[currentChildPos + 1], + heapList[currentChildPos])) { // Select the right child currentChildPos++; @@ -411,15 +429,14 @@ public Node GetRootLocation() // If the last item is smaller than both siblings at the // current height, break. if (FirstNodeBetter(lastNode, heapList[currentChildPos])) - { break; - } // Move the item at index j up one level. heapList[currentParentPos] = heapList[currentChildPos]; // Move index i to the appropriate branch. currentParentPos = currentChildPos; } + // Insert the last node into the currently free position heapList[currentParentPos] = lastNode; } @@ -432,13 +449,13 @@ public Node GetRootLocation() /// /// First node to compare /// Second node to compare - /// True if the first node has a more promissing position to the goal than the second + /// True if the first node has a more promising position to the goal than the second private static bool FirstNodeBetter(Node firstNode, Node secondNode) { - // Is the F_score smaller? - return (firstNode.F_score < secondNode.F_score) || - // If F_score is equal, evaluate the h-score - (firstNode.F_score == secondNode.F_score && firstNode.H_score < secondNode.H_score); + // Is the FScore smaller? + return (firstNode.FScore < secondNode.FScore) || + // If FScore is equal, evaluate the h-score + (firstNode.FScore == secondNode.FScore && firstNode.HScore < secondNode.HScore); } /// @@ -478,120 +495,63 @@ public static bool IsOnGround(World world, Location location) return true; // avoid moving downward in a not loaded chunk Location down = Move(location, Direction.Down); - Material currentMaterial = world.GetBlock(down).Type; - bool result = currentMaterial.IsSolid() - || currentMaterial == Material.TwistingVines || currentMaterial == Material.TwistingVinesPlant - || currentMaterial == Material.WeepingVines || currentMaterial == Material.WeepingVinesPlant - || currentMaterial == Material.Vine; + var result = currentMaterial.IsSolid() + || currentMaterial == Material.TwistingVines || currentMaterial == Material.TwistingVinesPlant + || currentMaterial == Material.WeepingVines || currentMaterial == Material.WeepingVinesPlant + || currentMaterial == Material.Vine; - bool northCheck = 1 + Math.Floor(down.Z) - down.Z > 0.7; - bool eastCheck = down.X - Math.Floor(down.X) > 0.7; - bool southCheck = down.Z - Math.Floor(down.Z) > 0.7; - bool westCheck = 1 + Math.Floor(down.X) - down.X > 0.7; + var northCheck = 1 + Math.Floor(down.Z) - down.Z > 0.7; + var eastCheck = down.X - Math.Floor(down.X) > 0.7; + var southCheck = down.Z - Math.Floor(down.Z) > 0.7; + var westCheck = 1 + Math.Floor(down.X) - down.X > 0.7; if (!result && northCheck) - { - Location locationDownNorth = Move(down, Direction.North); - result |= world.GetBlock(locationDownNorth).Type.IsSolid() - || world.GetBlock(locationDownNorth).Type == Material.TwistingVines - || world.GetBlock(locationDownNorth).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownNorth).Type == Material.WeepingVines - || world.GetBlock(locationDownNorth).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownNorth).Type == Material.Vine; - } + result |= IsSolidOrVine(world, Move(down, Direction.North)); if (!result && northCheck && eastCheck) - { - Location locationDownNorthEast = Move(down, Direction.NorthEast); - result |= world.GetBlock(locationDownNorthEast).Type.IsSolid() - || world.GetBlock(locationDownNorthEast).Type == Material.TwistingVines - || world.GetBlock(locationDownNorthEast).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownNorthEast).Type == Material.WeepingVines - || world.GetBlock(locationDownNorthEast).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownNorthEast).Type == Material.Vine; - } + result |= IsSolidOrVine(world, Move(down, Direction.NorthEast)); if (!result && eastCheck) - { - Location locationDownEast = Move(down, Direction.East); - result |= world.GetBlock(locationDownEast).Type.IsSolid() - || world.GetBlock(locationDownEast).Type == Material.TwistingVines - || world.GetBlock(locationDownEast).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownEast).Type == Material.WeepingVines - || world.GetBlock(locationDownEast).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownEast).Type == Material.Vine; - } + result |= IsSolidOrVine(world, Move(down, Direction.East)); if (!result && eastCheck && southCheck) - { - Location locationDownSouthEast = Move(down, Direction.SouthEast); - result |= world.GetBlock(locationDownSouthEast).Type.IsSolid() - || world.GetBlock(locationDownSouthEast).Type == Material.TwistingVines - || world.GetBlock(locationDownSouthEast).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownSouthEast).Type == Material.WeepingVines - || world.GetBlock(locationDownSouthEast).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownSouthEast).Type == Material.Vine; - } + result |= IsSolidOrVine(world, Move(down, Direction.SouthEast)); if (!result && southCheck) - { - Location locationDownSouth = Move(down, Direction.South); - result |= world.GetBlock(locationDownSouth).Type.IsSolid() - || world.GetBlock(locationDownSouth).Type == Material.TwistingVines - || world.GetBlock(locationDownSouth).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownSouth).Type == Material.WeepingVines - || world.GetBlock(locationDownSouth).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownSouth).Type == Material.Vine; - } + result |= IsSolidOrVine(world, Move(down, Direction.South)); if (!result && southCheck && westCheck) - { - Location locationDownSouthWest = Move(down, Direction.SouthWest); - result |= world.GetBlock(locationDownSouthWest).Type.IsSolid() - || world.GetBlock(locationDownSouthWest).Type == Material.TwistingVines - || world.GetBlock(locationDownSouthWest).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownSouthWest).Type == Material.WeepingVines - || world.GetBlock(locationDownSouthWest).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownSouthWest).Type == Material.Vine; - } - + result |= IsSolidOrVine(world, Move(down, Direction.SouthWest)); if (!result && westCheck) - { - Location locationDownWest = Move(down, Direction.West); - result |= world.GetBlock(locationDownWest).Type.IsSolid() - || world.GetBlock(locationDownWest).Type == Material.TwistingVines - || world.GetBlock(locationDownWest).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownWest).Type == Material.WeepingVines - || world.GetBlock(locationDownWest).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownWest).Type == Material.Vine; - } - + result |= IsSolidOrVine(world, Move(down, Direction.West)); if (!result && westCheck && northCheck) - { - Location locationDownNorthWest = Move(down, Direction.NorthWest); - result |= world.GetBlock(locationDownNorthWest).Type.IsSolid() - || world.GetBlock(locationDownNorthWest).Type == Material.TwistingVines - || world.GetBlock(locationDownNorthWest).Type == Material.TwistingVinesPlant - || world.GetBlock(locationDownNorthWest).Type == Material.WeepingVines - || world.GetBlock(locationDownNorthWest).Type == Material.WeepingVinesPlant - || world.GetBlock(locationDownNorthWest).Type == Material.Vine; - - } + result |= IsSolidOrVine(world, Move(down, Direction.NorthWest)); return result && (location.Y <= Math.Truncate(location.Y) + 0.0001); } + private static bool IsSolidOrVine(World world, Location location) + { + var block = world.GetBlock(location); + return block.Type.IsSolid() + || block.Type == Material.TwistingVines + || block.Type == Material.TwistingVinesPlant + || block.Type == Material.WeepingVines + || block.Type == Material.WeepingVinesPlant + || block.Type == Material.Vine; + } + /// /// Check if the specified location implies swimming /// /// World for performing check /// Location to check /// True if the specified location implies swimming - public static bool IsSwimming(World world, Location location) + private static bool IsSwimming(World world, Location location) { return world.GetBlock(location).Type.IsLiquid(); } @@ -602,7 +562,7 @@ public static bool IsSwimming(World world, Location location) /// World for performing check /// Location to check /// True if the specified location can be climbed on - public static bool IsClimbing(World world, Location location) + private static bool IsClimbing(World world, Location location) { return world.GetBlock(location).Type.CanBeClimbedOn(); } @@ -613,18 +573,21 @@ public static bool IsClimbing(World world, Location location) /// World for performing check /// Location to check /// True if the destination location won't directly harm the player - public static bool IsSafe(World world, Location location) + private static bool IsSafe(World world, Location location) { return - //No block that can harm the player - !world.GetBlock(location).Type.CanHarmPlayers() + //No block that can harm the player + !world.GetBlock(location).Type.CanHarmPlayers() && !world.GetBlock(Move(location, Direction.Up)).Type.CanHarmPlayers() && !world.GetBlock(Move(location, Direction.Down)).Type.CanHarmPlayers() //No fall from a too high place - && (world.GetBlock(Move(location, Direction.Down)).Type.IsSolid() || IsClimbing(world, Move(location, Direction.Down)) - || world.GetBlock(Move(location, Direction.Down, 2)).Type.IsSolid() || IsClimbing(world, Move(location, Direction.Down, 2)) - || world.GetBlock(Move(location, Direction.Down, 3)).Type.IsSolid() || IsClimbing(world, Move(location, Direction.Down, 3))) + && (world.GetBlock(Move(location, Direction.Down)).Type.IsSolid() || + IsClimbing(world, Move(location, Direction.Down)) + || world.GetBlock(Move(location, Direction.Down, 2)).Type.IsSolid() || + IsClimbing(world, Move(location, Direction.Down, 2)) + || world.GetBlock(Move(location, Direction.Down, 3)).Type.IsSolid() || + IsClimbing(world, Move(location, Direction.Down, 3))) //Not an underwater location && !(world.GetBlock(Move(location, Direction.Up)).Type.IsLiquid()); @@ -647,11 +610,12 @@ public static bool CanMove(World world, Location location, Direction direction) case Direction.Down: return IsClimbing(world, Move(location, Direction.Down)) || !IsOnGround(world, location); case Direction.Up: - bool nextTwoBlocks = !world.GetBlock(Move(Move(location, Direction.Up), Direction.Up)).Type.IsSolid(); + bool nextTwoBlocks = + !world.GetBlock(Move(Move(location, Direction.Up), Direction.Up)).Type.IsSolid(); // Check if the current block can be climbed on if (IsClimbing(world, location)) - // Check if next block after the next one can be climbed uppon + // Check if next block after the next one can be climbed upon return IsClimbing(world, Move(location, Direction.Up)) || nextTwoBlocks; return (IsOnGround(world, location) || IsSwimming(world, location)) && nextTwoBlocks; @@ -665,13 +629,21 @@ public static bool CanMove(World world, Location location, Direction direction) // Move diagonal case Direction.NorthEast: - return PlayerFitsHere(world, Move(location, Direction.North)) && PlayerFitsHere(world, Move(location, Direction.East)) && PlayerFitsHere(world, Move(location, direction)); + return PlayerFitsHere(world, Move(location, Direction.North)) && + PlayerFitsHere(world, Move(location, Direction.East)) && + PlayerFitsHere(world, Move(location, direction)); case Direction.SouthEast: - return PlayerFitsHere(world, Move(location, Direction.South)) && PlayerFitsHere(world, Move(location, Direction.East)) && PlayerFitsHere(world, Move(location, direction)); + return PlayerFitsHere(world, Move(location, Direction.South)) && + PlayerFitsHere(world, Move(location, Direction.East)) && + PlayerFitsHere(world, Move(location, direction)); case Direction.SouthWest: - return PlayerFitsHere(world, Move(location, Direction.South)) && PlayerFitsHere(world, Move(location, Direction.West)) && PlayerFitsHere(world, Move(location, direction)); + return PlayerFitsHere(world, Move(location, Direction.South)) && + PlayerFitsHere(world, Move(location, Direction.West)) && + PlayerFitsHere(world, Move(location, direction)); case Direction.NorthWest: - return PlayerFitsHere(world, Move(location, Direction.North)) && PlayerFitsHere(world, Move(location, Direction.West)) && PlayerFitsHere(world, Move(location, direction)); + return PlayerFitsHere(world, Move(location, Direction.North)) && + PlayerFitsHere(world, Move(location, Direction.West)) && + PlayerFitsHere(world, Move(location, direction)); default: throw new ArgumentException("Unknown direction", nameof(direction)); @@ -686,8 +658,16 @@ public static bool CanMove(World world, Location location, Direction direction) /// True if a player is able to stand in this location public static bool PlayerFitsHere(World world, Location location) { - return (IsClimbing(world, location) && IsClimbing(world, Move(location, Direction.Up))) - || !world.GetBlock(location).Type.IsSolid() && !world.GetBlock(Move(location, Direction.Up)).Type.IsSolid(); + var canClimb = IsClimbing(world, location) && IsClimbing(world, Move(location, Direction.Up)); + var isNotSolid = !world.GetBlock(location).Type.IsSolid() && + !world.GetBlock(Move(location, Direction.Up)).Type.IsSolid(); + + // Handle slabs + if (!isNotSolid && world.GetBlock(Move(location, Direction.Up)) + .IsTopSlab(McClient.Instance!.GetProtocolVersion())) + isNotSolid = true; + + return canClimb || isNotSolid; } /// @@ -707,39 +687,28 @@ public static Location Move(Location location, Direction direction, int length = /// /// Direction to move to /// A location delta for moving in that direction - public static Location Move(Direction direction) + private static Location Move(Direction direction) { - switch (direction) + return direction switch { // Move vertical - case Direction.Down: - return new Location(0, -1, 0); - case Direction.Up: - return new Location(0, 1, 0); + Direction.Down => new Location(0, -1, 0), + Direction.Up => new Location(0, 1, 0), // Move horizontal straight - case Direction.East: - return new Location(1, 0, 0); - case Direction.West: - return new Location(-1, 0, 0); - case Direction.South: - return new Location(0, 0, 1); - case Direction.North: - return new Location(0, 0, -1); + Direction.East => new Location(1, 0, 0), + Direction.West => new Location(-1, 0, 0), + Direction.South => new Location(0, 0, 1), + Direction.North => new Location(0, 0, -1), // Move horizontal diagonal - case Direction.NorthEast: - return Move(Direction.North) + Move(Direction.East); - case Direction.SouthEast: - return Move(Direction.South) + Move(Direction.East); - case Direction.SouthWest: - return Move(Direction.South) + Move(Direction.West); - case Direction.NorthWest: - return Move(Direction.North) + Move(Direction.West); + Direction.NorthEast => Move(Direction.North) + Move(Direction.East), + Direction.SouthEast => Move(Direction.South) + Move(Direction.East), + Direction.SouthWest => Move(Direction.South) + Move(Direction.West), + Direction.NorthWest => Move(Direction.North) + Move(Direction.West), - default: - throw new ArgumentException("Unknown direction", nameof(direction)); - } + _ => throw new ArgumentException("Unknown direction", nameof(direction)) + }; } /// @@ -751,7 +720,7 @@ public static Location Move(Direction direction) /// Is loading complete public static bool CheckChunkLoading(World world, Location start, Location dest) { - ChunkColumn? chunkColumn = world.GetChunkColumn(dest); + var chunkColumn = world.GetChunkColumn(dest); if (chunkColumn == null || chunkColumn.FullyLoaded == false) return false; @@ -762,4 +731,4 @@ public static bool CheckChunkLoading(World world, Location start, Location dest) return true; } } -} +} \ No newline at end of file diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index ba803e8876..5f3fc8199a 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -9,6 +9,7 @@ using MinecraftClient.ChatBots; using MinecraftClient.CommandHandler; using MinecraftClient.CommandHandler.Patch; +using MinecraftClient.Commands; using MinecraftClient.Inventory; using MinecraftClient.Logger; using MinecraftClient.Mapping; @@ -97,6 +98,11 @@ public enum MovementType { Sneak, Walk, Sprint } private int playerLevel; private int playerTotalExperience; private byte CurrentSlot = 0; + + // Sneaking + public bool IsSneaking { get; set; } = false; + private bool isUnderSlab = false; + private DateTime nextSneakingUpdate = DateTime.Now; // Entity handling private readonly Dictionary entities = new(); @@ -144,7 +150,9 @@ public enum MovementType { Sneak, Walk, Sprint } Tuple? timeoutdetector = null; public ILogger Log; - + + private static IMinecraftComHandler? instance; + public static IMinecraftComHandler? Instance => instance; /// /// Starts the main chat client, wich will login to the server using the MinecraftCom class. /// @@ -157,6 +165,8 @@ public enum MovementType { Sneak, Walk, Sprint } public McClient(SessionToken session, PlayerKeyPair? playerKeyPair, string server_ip, ushort port, int protocolversion, ForgeInfo? forgeInfo) { CmdResult.currentHandler = this; + instance = this; + terrainAndMovementsEnabled = Config.Main.Advanced.TerrainAndMovements; inventoryHandlingEnabled = Config.Main.Advanced.InventoryHandling; entityHandlingEnabled = Config.Main.Advanced.EntityHandling; @@ -328,6 +338,25 @@ public void OnUpdate() } } + if (nextSneakingUpdate < DateTime.Now) + { + if (world.GetBlock(new Location(location.X, location.Y + 1, location.Z)).IsTopSlab(protocolversion) && !IsSneaking) + { + isUnderSlab = true; + SendEntityAction(EntityActionType.StartSneaking); + } + else + { + if (isUnderSlab && !IsSneaking) + { + isUnderSlab = false; + SendEntityAction(EntityActionType.StopSneaking); + } + } + + nextSneakingUpdate = DateTime.Now.AddMilliseconds(300); + } + lock (chatQueue) { TrySendMessageToServer(); From 97af063d79f776ebfccd4d7989ee780b4ea3acc1 Mon Sep 17 00:00:00 2001 From: Anon Date: Tue, 16 May 2023 23:25:29 +0200 Subject: [PATCH 2/4] Removed a comment --- MinecraftClient/Mapping/BlockExtension.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/MinecraftClient/Mapping/BlockExtension.cs b/MinecraftClient/Mapping/BlockExtension.cs index f644279032..683f1dc8d1 100644 --- a/MinecraftClient/Mapping/BlockExtension.cs +++ b/MinecraftClient/Mapping/BlockExtension.cs @@ -6,9 +6,6 @@ public static class BlockExtension { public static bool IsTopSlab(this Block block, int protocolVersion) { - //if (block.BlockId != 0) - // ConsoleIO.WriteLine("Checking is block: " + block + " is a top slab, protocol: " + protocolVersion); - if (protocolVersion >= Protocol18Handler.MC_1_19_4_Version) { switch (block.BlockId) From 852be6e90d745ad7fb0b8235a48ad3c1755c1640 Mon Sep 17 00:00:00 2001 From: Anon Date: Sat, 20 May 2023 13:11:37 +0200 Subject: [PATCH 3/4] Fixed 1.19.3 --- MinecraftClient/Mapping/BlockExtension.cs | 111 +++++++++++----------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/MinecraftClient/Mapping/BlockExtension.cs b/MinecraftClient/Mapping/BlockExtension.cs index 683f1dc8d1..1236ee1c6c 100644 --- a/MinecraftClient/Mapping/BlockExtension.cs +++ b/MinecraftClient/Mapping/BlockExtension.cs @@ -66,64 +66,63 @@ public static bool IsTopSlab(this Block block, int protocolVersion) return true; } } - else if (protocolVersion == Protocol18Handler.MC_1_13_Version) + else if (protocolVersion == Protocol18Handler.MC_1_19_3_Version) { switch (block.BlockId) { - case 19257: // CutCopperSlab - case 19251: // ExposedCutCopperSlab - case 19245: // WeatheredCutCopperSlab - case 19239: // OxidizedCutCopperSlab - case 19609: // WaxedCutCopperSlab - case 19603: // WaxedExposedCutCopperSlab - case 19597: // WaxedWeatheredCutCopperSlab - case 19591: // WaxedOxidizedCutCopperSlab - case 9042: // OakSlab - case 9048: // SpruceSlab - case 9054: // BirchSlab - case 9060: // JungleSlab - case 9066: // AcaciaSlab - case 9072: // DarkOakSlab - case 9078: // MangroveSlab - case 16257: // CrimsonSlab - case 16263: // WarpedSlab - case 9084: // StoneSlab - case 9090: // SmoothStoneSlab - case 9096: // SandstoneSlab - case 9102: // CutSandstoneSlab - case 9108: // PetrifiedOakSlab - case 9114: // CobblestoneSlab - case 9120: // BrickSlab - case 9126: // StoneBrickSlab - case 9132: // MudBrickSlab - case 9138: // NetherBrickSlab - case 9144: // QuartzSlab - case 9150: // RedSandstoneSlab - case 9156: // CutRedSandstoneSlab - case 9162: // PurpurSlab - case 8586: // PrismarineSlab - case 8592: // PrismarineBrickSlab - case 8598: // DarkPrismarineSlab - case 11671: // PolishedGraniteSlab - case 11677: // SmoothRedSandstoneSlab - case 11683: // MossyStoneBrickSlab - case 11689: // PolishedDioriteSlab - case 11695: // MossyCobblestoneSlab - case 11701: // EndStoneBrickSlab - case 11707: // SmoothSandstoneSlab - case 11713: // SmoothQuartzSlab - case 11719: // GraniteSlab - case 11725: // AndesiteSlab - case 11731: // RedNetherBrickSlab - case 11737: // PolishedAndesiteSlab - case 11743: // DioriteSlab - case 19863: // CobbledDeepslateSlab - case 20274: // PolishedDeepslateSlab - case 21096: // DeepslateBrickSlab - case 20685: // DeepslateTileSlab - case 17454: // BlackstoneSlab - case 17955: // PolishedBlackstoneSlab - case 17464: // PolishedBlackstoneBrickSlab + case 10686: // OakSlab + case 10692: // SpruceSlab + case 10698: // BirchSlab + case 10704: // JungleSlab + case 10710: // AcaciaSlab + case 10716: // DarkOakSlab + case 10722: // MangroveSlab + case 18041: // CrimsonSlab + case 18047: // WarpedSlab + case 10740: // StoneSlab + case 10770: // CobblestoneSlab + case 13479: // MossyCobblestoneSlab + case 10746: // SmoothStoneSlab + case 10782: // StoneBrickSlab + case 13467: // MossyStoneBrickSlab + case 13503: // GraniteSlab + case 13455: // PolishedGraniteSlab + case 13527: // DioriteSlab + case 13473: // PolishedDioriteSlab + case 13509: // AndesiteSlab + case 13521: // PolishedAndesiteSlab + case 21647: // CobbledDeepslateSlab + case 22058: // PolishedDeepslateSlab + case 22880: // DeepslateBrickSlab + case 22469: // DeepslateTileSlab + case 10776: // BrickSlab + case 10788: // MudBrickSlab + case 10752: // SandstoneSlab + case 13491: // SmoothSandstoneSlab + case 10758: // CutSandstoneSlab + case 10806: // RedSandstoneSlab + case 13461: // SmoothRedSandstoneSlab + case 10812: // CutRedSandstoneSlab + case 10230: // PrismarineSlab + case 10236: // PrismarineBrickSlab + case 10242: // DarkPrismarineSlab + case 10794: // NetherBrickSlab + case 13515: // RedNetherBrickSlab + case 19238: // BlackstoneSlab + case 19739: // PolishedBlackstoneSlab + case 19248: // PolishedBlackstoneBrickSlab + case 13485: // EndStoneBrickSlab + case 10818: // PurpurSlab + case 10800: // QuartzSlab + case 13497: // SmoothQuartzSlab + case 21041: // CutCopperSlab + case 21035: // ExposedCutCopperSlab + case 21029: // WeatheredCutCopperSlab + case 21023: // OxidizedCutCopperSlab + case 21393: // WaxedCutCopperSlab + case 21387: // WaxedExposedCutCopperSlab + case 21381: // WaxedWeatheredCutCopperSlab + case 21375: // WaxedOxidizedCutCopperSlab return true; } } @@ -393,7 +392,7 @@ public static bool IsTopSlab(this Block block, int protocolVersion) case 7312: // CobblestoneSlab case 7318: // BrickSlab case 7324: // StoneBrickSlab - case 7330: // NetherBrickSlab + case 7330: // NetherBrickSlabw case 7336: // QuartzSlab case 7342: // RedSandstoneSlab case 7348: // PurpurSlab From 64eb48f46d02b94b66c05dc171db494a3b01aa76 Mon Sep 17 00:00:00 2001 From: Anon Date: Sat, 20 May 2023 13:28:24 +0200 Subject: [PATCH 4/4] Removed 1.13, because it's not possible to walk under slabs --- MinecraftClient/Mapping/BlockExtension.cs | 26 ----------------------- 1 file changed, 26 deletions(-) diff --git a/MinecraftClient/Mapping/BlockExtension.cs b/MinecraftClient/Mapping/BlockExtension.cs index 1236ee1c6c..9aeb193a2f 100644 --- a/MinecraftClient/Mapping/BlockExtension.cs +++ b/MinecraftClient/Mapping/BlockExtension.cs @@ -376,32 +376,6 @@ public static bool IsTopSlab(this Block block, int protocolVersion) return true; } } - else if (protocolVersion >= Protocol18Handler.MC_1_13_Version) - { - switch (block.BlockId) - { - case 7258: // OakSlab - case 7264: // SpruceSlab - case 7270: // BirchSlab - case 7276: // JungleSlab - case 7282: // AcaciaSlab - case 7288: // DarkOakSlab - case 7294: // StoneSlab - case 7300: // SandstoneSlab - case 7306: // PetrifiedOakSlab - case 7312: // CobblestoneSlab - case 7318: // BrickSlab - case 7324: // StoneBrickSlab - case 7330: // NetherBrickSlabw - case 7336: // QuartzSlab - case 7342: // RedSandstoneSlab - case 7348: // PurpurSlab - case 6802: // PrismarineSlab - case 6808: // PrismarineBrickSlab - case 6814: // DarkPrismarineSlab - return true; - } - } return false; }