From 977e857d2bc2355fb4b1b76b73f82f638ed08c0e Mon Sep 17 00:00:00 2001 From: yuanlu <2573580691@qq.com> Date: Sun, 5 Nov 2023 08:01:19 +0800 Subject: [PATCH 1/5] Auto Torch Signed-off-by: yuanlu <2573580691@qq.com> --- .../java/net/wurstclient/hack/HackList.java | 1 + .../wurstclient/hacks/AutoLightingHack.java | 399 ++++++++++++++++++ .../resources/assets/wurst/lang/en_us.json | 1 + .../resources/assets/wurst/lang/zh_cn.json | 1 + 4 files changed, 402 insertions(+) create mode 100644 src/main/java/net/wurstclient/hacks/AutoLightingHack.java diff --git a/src/main/java/net/wurstclient/hack/HackList.java b/src/main/java/net/wurstclient/hack/HackList.java index e55ca896dc..f3cbf31490 100644 --- a/src/main/java/net/wurstclient/hack/HackList.java +++ b/src/main/java/net/wurstclient/hack/HackList.java @@ -48,6 +48,7 @@ public final class HackList implements UpdateListener public final AutoDropHack autoDropHack = new AutoDropHack(); public final AutoLeaveHack autoLeaveHack = new AutoLeaveHack(); public final AutoLibrarianHack autoLibrarianHack = new AutoLibrarianHack(); + public final AutoLightingHack autoLightingHack = new AutoLightingHack(); public final AutoEatHack autoEatHack = new AutoEatHack(); public final AutoFarmHack autoFarmHack = new AutoFarmHack(); public final AutoFishHack autoFishHack = new AutoFishHack(); diff --git a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java new file mode 100644 index 0000000000..57dd6149aa --- /dev/null +++ b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2014-2023 Wurst-Imperium and contributors. + * + * This source code is subject to the terms of the GNU General Public + * License, version 3. If a copy of the GPL was not distributed with this + * file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt + */ +package net.wurstclient.hacks; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.EntityType; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.LightType; +import net.wurstclient.Category; +import net.wurstclient.SearchTags; +import net.wurstclient.events.RenderListener; +import net.wurstclient.events.UpdateListener; +import net.wurstclient.hack.Hack; +import net.wurstclient.settings.CheckboxSetting; +import net.wurstclient.settings.SliderSetting; +import net.wurstclient.util.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL11; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SearchTags({"auto lighting", "AutoLighting", "auto torch", "AutoTorch"}) +public final class AutoLightingHack extends Hack implements UpdateListener, RenderListener { + private static final int[] lvl2blocks = new int[16]; + private static final ConcurrentHashMap item2lvl = new ConcurrentHashMap<>(); + + static { + Arrays.fill(lvl2blocks, -1); + item2lvl.put(Items.TORCH, 14); + } + + private final SliderSetting range1 = + new SliderSetting("RangePlace", 4.25, 1, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); + private final SliderSetting range2 = + new SliderSetting("RangeSearch", 8, 1, 10, 0.05, SliderSetting.ValueDisplay.DECIMAL); + private final CheckboxSetting debug = new CheckboxSetting("debug", "Output Debug Message", false); + private final CheckboxSetting crp = new CheckboxSetting("Compare RP", "Compare Pos with RangePlace", false); + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private Future currentSearchTask = null; + private @Nullable BlockPos putPos = null; + private Status status = Status.Idle; + private int idleCooldown = 0; + + public AutoLightingHack() { + super("AutoLighting"); + setCategory(Category.BLOCKS); + + addSetting(this.range1); + addSetting(this.range2); + addSetting(this.debug); + addSetting(this.crp); + } + + /** + * 获取光照等级为v的光源最多能点亮多少方块 + * + * @return 最大光照方块 + */ + public static int getMaxLight(int v) { + if (v < lvl2blocks.length) { + if (lvl2blocks[v] > -1) return lvl2blocks[v]; + synchronized (lvl2blocks) { + if (lvl2blocks[v] > -1) return lvl2blocks[v]; + lvl2blocks[v] = getLight(0, 0, 0, v, null).size(); + return lvl2blocks[v]; + } + } + return Integer.MAX_VALUE; + } + + /** + * 获取光照等级为v的光源最多能点亮多少方块 + * + * @return 最大光照方块 + */ + public static int getMaxLight(Item v) { + return getMaxLight(item2lvl.getOrDefault(v, 14)); + } + + /** + * 获取能够点亮的方块数量 + * + * @param x 光源位置 + * @param y 光源位置 + * @param z 光源位置 + * @param v 光源位置 + * @return 能够照亮几个方块 + */ + + @Contract(value = "_,_,_,_,_->new", pure = true) + private static @NotNull ArrayList getLight(int x, int y, int z, int v, @Nullable Set needs) { + if (v <= 0) return new ArrayList<>(); + final var w = MC.world; + final boolean is_real = needs != null; + if (is_real && w == null) return new ArrayList<>(); + final var count = new ArrayList(); + int searched = 0; + Queue queue = new ArrayDeque<>(); + Set visited = new HashSet<>(); + + final var fst = new ValuePos(x, y, z, v); + queue.add(fst); + visited.add(fst.toXYZ()); + + while (!queue.isEmpty()) { + final var point = queue.poll(); + if (is_real && searched++ > getMaxLight(lvl2blocks.length - 1)) break; + final var pt = point.toXYZ(); + + if (is_real) { + if (w.getBlockState(pt).isOpaque()) continue; + final var lvl = w.getLightLevel(LightType.BLOCK, pt); + if (lvl >= point.v) continue; + if (needs.contains(pt)) count.add(pt); + } else { + count.add(pt); + } + + if (point.v > 0) { + v = point.v - 1; + for (int i = 0; i < 6; i++) { + final var to = point.move(i); + if (visited.contains(to)) continue; + visited.add(to); + queue.add(new ValuePos(to.getX(), to.getY(), to.getZ(), v)); + } + } + + } + + return count; + } + + private void message(Object msg, boolean is_debug) { + if (is_debug && !this.debug.isChecked()) return; + ChatUtils.message("[%s] %s".formatted(getName(), msg)); + } + + private void message(Object msg) { + message(msg, false); + } + + @Override + public String getRenderName() { + return super.getRenderName() + " [" + this.status.name() + "]"; + } + + private int getTorch() { + var inv = Objects.requireNonNull(MC.player.getInventory()); + for (int i = 0; i < 9; i++) { + ItemStack stack = inv.getStack(i); + if (stack.isEmpty() || !(stack.getItem().equals(Items.TORCH))) continue; + return i; + } + return -1; + } + + /** + * 尝试放置方块 + * + * @return 是否要刷新 + */ + private boolean placeBlock() { + if (this.putPos == null) return false; + var newSlot = getTorch(); + if (newSlot < 0) { + this.status = Status.NoTorch; + return false; + } + this.status = Status.Waiting; + Vec3d eyesPos = RotationUtils.getEyesPos(); + BlockPos down = this.putPos.offset(Direction.DOWN); + if (!BlockUtils.canBeClicked(down)) return true; + Direction up = Direction.DOWN.getOpposite(); + Vec3d hitVec = Vec3d.ofCenter(down).add(Vec3d.of(up.getVector()).multiply(0.5)); + var distanceSq = eyesPos.squaredDistanceTo(hitVec); + if (distanceSq > this.range2.getValueSq()) return true; + if (distanceSq > this.range1.getValueSq()) return false; + // place block + message("placeBlock = " + this.putPos, true); + + this.status = Status.Putting; + int oldSlot = MC.player.getInventory().selectedSlot; + MC.player.getInventory().selectedSlot = newSlot; + + var rotation = RotationUtils.getNeededRotations(hitVec); + MC.player.networkHandler.sendPacket( + new PlayerMoveC2SPacket.LookAndOnGround(rotation.getYaw(), rotation.getPitch(), MC.player.isOnGround())); + IMC.getInteractionManager().rightClickBlock(down, up, hitVec); + MC.player.swingHand(Hand.MAIN_HAND); + MC.itemUseCooldown = 4; + + MC.player.getInventory().selectedSlot = oldSlot; + + this.idleCooldown = 5; + + return true; + } + + @Override + protected void onEnable() { + EVENTS.add(UpdateListener.class, this); + EVENTS.add(RenderListener.class, this); + this.status = Status.Idle; + } + + @Override + protected void onDisable() { + EVENTS.remove(UpdateListener.class, this); + EVENTS.remove(RenderListener.class, this); + if (this.currentSearchTask != null) this.currentSearchTask.cancel(true); + this.currentSearchTask = null; + } + + @Override + public void onUpdate() { + if (this.currentSearchTask != null && !this.currentSearchTask.isDone()) { + this.status = Status.Searching; + return; + } + if (this.idleCooldown > 0) { + this.idleCooldown--; + this.status = Status.Idle; + return; + } + + if (this.currentSearchTask != null) { + try { + this.putPos = this.currentSearchTask.get(); + } catch (Exception e) { + e.printStackTrace(); + ChatUtils.error("[" + super.getName() + "] Can not search: " + e); + super.setEnabled(false); + } + this.currentSearchTask = null; + } + + if (this.putPos == null) this.currentSearchTask = this.executorService.submit(this::search); + + if (placeBlock()) this.putPos = null; + } + + private BlockPos search() { + final int level = 14 - 1; + final double range1Sq = this.range1.getValueSq(); + final double range2Sq = this.range2.getValueSq(); + Vec3d eyesVec = RotationUtils.getEyesPos(); + ClientWorld world = Objects.requireNonNull(MC.world); + @SuppressWarnings("all") Stream stream =// + BlockUtils.getAllInBoxStream(BlockPos.ofFloored(eyesVec), range2.getValueCeil())// + .filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range2Sq)// + .sorted(Comparator.comparingDouble(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos))))// + .filter(pos -> { + BlockState state = world.getBlockState(pos); + + if (state.blocksMovement()) return false; + if (!state.getFluidState().isEmpty()) return false; + + BlockState stateDown = world.getBlockState(pos.down()); + if (!stateDown.allowsSpawning(world, pos.down(), EntityType.ZOMBIE)) return false; + + return world.getLightLevel(LightType.BLOCK, pos) < 1; + })// + ; + final var blocks = stream.toList(); + final var blocksSet = new HashSet<>(blocks); + final var blocksR1Set = + blocks.stream().filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range1Sq) + .collect(Collectors.toSet()); + + BlockPos bestPos = null; + + { + var bestLights = 0; + var bestLights_r1 = 0; + for (final var p : blocks) { + if (!BlockUtils.canBeClicked(p.offset(Direction.DOWN))) continue; + if (!world.getBlockState(p).isAir()) continue; + final var light_count = getLight(p.getX(), p.getY(), p.getZ(), level, blocksSet); + var l = light_count.size(); + light_count.retainAll(blocksR1Set); + var l_r1 = light_count.size(); + if (l_r1 > bestLights_r1 || (l > bestLights && (l_r1 == bestLights_r1 || !this.crp.isChecked()))) { + bestLights_r1 = l_r1; + bestLights = l; + bestPos = p; + } + } + } + if (bestPos != null) message("Search done " + bestPos, true); + return bestPos; + /* + * 如果周围全都是刷怪区,那么就放在脚下 + * 如果距离更大的能覆盖更多,且在r1范围不可点亮区不减少,则选择更远的。 + * + * + */ + + } + + @Override + public void onRender(MatrixStack matrixStack, float partialTicks) { + final var pos = this.putPos; + if (pos == null) return; + + float scale = 7F / 8F; + double offset = (1D - scale) / 2D; + Vec3d eyesPos = RotationUtils.getEyesPos(); + double rangeSq = Math.pow(this.range1.getValue(), 2); + + // GL settings + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_CULL_FACE); + RenderSystem.setShaderColor(0F, 0F, 0F, 0.5F); + + matrixStack.push(); + + RegionPos region = RenderUtils.getCameraRegion(); + RenderUtils.applyRegionalRenderOffset(matrixStack, region); + + RenderSystem.setShader(GameRenderer::getPositionProgram); + + matrixStack.push(); + matrixStack.translate(pos.getX() - region.x(), pos.getY(), pos.getZ() - region.z()); + matrixStack.translate(offset, offset, offset); + matrixStack.scale(scale, scale, scale); + + Vec3d posVec = Vec3d.ofCenter(pos); + + drawBox(matrixStack, eyesPos.squaredDistanceTo(posVec) <= rangeSq); + + matrixStack.pop(); + + matrixStack.pop(); + + // GL resets + GL11.glDisable(GL11.GL_BLEND); + RenderSystem.setShaderColor(1, 1, 1, 1); + } + + private void drawBox(MatrixStack matrixStack, boolean isGreen) { + GL11.glDepthMask(false); + RenderSystem.setShaderColor(isGreen ? 0F : 1F, isGreen ? 1F : 0F, 0F, 0.15F); + RenderUtils.drawSolidBox(matrixStack); + GL11.glDepthMask(true); + + RenderSystem.setShaderColor(0F, 0F, 0F, 0.5F); + RenderUtils.drawOutlinedBox(matrixStack); + } + + private enum Status { + Idle, Searching, Waiting, Putting, NoTorch + } + + private record ValuePos(int x, int y, int z, int v) { + private static final int[] dx = {-1, 1, 0, 0, 0, 0}; + private static final int[] dy = {0, 0, -1, 1, 0, 0}; + private static final int[] dz = {0, 0, 0, 0, -1, 1}; + + @Contract(value = " -> new", pure = true) + public @NotNull BlockPos toXYZ() {return new BlockPos(this.x, this.y, this.z);} + + @Contract(value = "_, _, _ -> new", pure = true) + public @NotNull BlockPos move(int x, int y, int z) { + return new BlockPos(this.x + x, this.y + y, this.z + z); + } + + @Contract(value = "_ -> new", pure = true) + public @NotNull BlockPos move(int i) { + return move(dx[i], dy[i], dz[i]); + } + } + +} diff --git a/src/main/resources/assets/wurst/lang/en_us.json b/src/main/resources/assets/wurst/lang/en_us.json index 69366c0f6f..2498ee204c 100644 --- a/src/main/resources/assets/wurst/lang/en_us.json +++ b/src/main/resources/assets/wurst/lang/en_us.json @@ -32,6 +32,7 @@ "description.wurst.setting.autoeat.allow_chorus": "Eating chorus fruit teleports you to a random location.\nNot recommended.", "description.wurst.hack.autofarm": "Harvests and replants crops automatically.\nWorks with wheat, carrots, potatoes, beetroots, pumpkins, melons, cacti, sugar canes, kelp, bamboo, nether warts, and cocoa beans.", "description.wurst.hack.autofish": "Automatically catches fish using your best fishing rod. If it finds a better rod while fishing, it will automatically switch to it.", + "description.wurst.hack.autolighting": "Automatically places torches in mob spawning areas.", "description.wurst.hack.automine": "Automatically mines any block that you look at.", "description.wurst.hack.autopotion": "Automatically throws splash potions of instant health when your health is low.", "description.wurst.hack.autoreconnect": "Automatically reconnects when you get kicked from the server.", diff --git a/src/main/resources/assets/wurst/lang/zh_cn.json b/src/main/resources/assets/wurst/lang/zh_cn.json index b25644c0a7..86abd3416a 100644 --- a/src/main/resources/assets/wurst/lang/zh_cn.json +++ b/src/main/resources/assets/wurst/lang/zh_cn.json @@ -7,6 +7,7 @@ "description.wurst.hack.antihunger": "在你走路时减缓你的饥饿状态。", "description.wurst.hack.antiknockback": "保护你不被其他生物或玩家推动和用剑击退。", "description.wurst.hack.antispam": "将重复的刷屏改为计数器显示。", + "description.wurst.hack.autolighting": "自动将火把放置在生物生成区域。", "description.wurst.hack.antiwaterpush": "防止你被水流推动。", "description.wurst.hack.antiwobble": "禁用由反胃和传送门动画引起的视觉扭曲。", "description.wurst.hack.arrowdmg": "大幅增加箭的伤害,但也会消耗大量饥饿值并降低准确性。\n\n不适用于弩,并且似乎已在Paper类服务器上修复。", From 7c8a7c5757f9e3fa48fe62ebe0117f12b1de594e Mon Sep 17 00:00:00 2001 From: yuanlu <2573580691@qq.com> Date: Sun, 5 Nov 2023 12:22:25 +0800 Subject: [PATCH 2/5] Optimize placement location selection logic Signed-off-by: yuanlu <2573580691@qq.com> --- .../wurstclient/hacks/AutoLightingHack.java | 129 ++++++++++++++---- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java index 57dd6149aa..c5f048bd16 100644 --- a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java +++ b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java @@ -20,6 +20,7 @@ import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.LightType; import net.wurstclient.Category; @@ -53,12 +54,41 @@ public final class AutoLightingHack extends Hack implements UpdateListener, Rend item2lvl.put(Items.TORCH, 14); } + /** + * Placement range. Only blocks will be placed within this range.
+ * 放置范围。只会在此范围内放置方块。 + */ private final SliderSetting range1 = new SliderSetting("RangePlace", 4.25, 1, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); + /** + * Search range. The placement position will be calculated within this range.
+ * 搜索范围。会在此范围内计算放置位置。 + */ private final SliderSetting range2 = new SliderSetting("RangeSearch", 8, 1, 10, 0.05, SliderSetting.ValueDisplay.DECIMAL); + /** + * Include range. Use this range to calculate the newly illuminated blocks.
+ * 包含范围。用此范围计算可新照亮的方块。 + */ + private final SliderSetting range3 = + new SliderSetting("RangeInclude", 30, 10, 15 * 4, 0.05, SliderSetting.ValueDisplay.DECIMAL); private final CheckboxSetting debug = new CheckboxSetting("debug", "Output Debug Message", false); private final CheckboxSetting crp = new CheckboxSetting("Compare RP", "Compare Pos with RangePlace", false); + /** + * Forward path radius. Set to 0 to calculate only within the RangeSearch range. Otherwise, Search is calculated as: + * the range with a radius less than RangePlace + + * starts from the player eye forward (ignoring the pitch, only keeping yaw) to radius less than RangeSearch.
+ * 向前路径半径。设为0则只在RangeSearch范围内计算。否则Search计算为: + * 半径小于RangePlace的范围 + 玩家从眼睛开始向前(忽略pitch,只保留yaw)到半径小于RangeSearch。 + */ + private final SliderSetting frontpath = + new SliderSetting("Front Path Distance", 3, 0, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); + /** + * The cooling time after placing a torch once (seconds). The minimum unit is 1 tick (i.e. 0.05 seconds).
+ * 放置一次火把后的冷却时长(秒)。最小单位为1tick(即0.05秒)。 + */ + private final SliderSetting cooldown = + new SliderSetting("Place Cooldown(s)", 0.25, 1.0 / 2, 2, 1.0 / 20, SliderSetting.ValueDisplay.DECIMAL); private final ExecutorService executorService = Executors.newSingleThreadExecutor(); private Future currentSearchTask = null; private @Nullable BlockPos putPos = null; @@ -71,8 +101,11 @@ public AutoLightingHack() { addSetting(this.range1); addSetting(this.range2); - addSetting(this.debug); + addSetting(this.range3); + addSetting(this.cooldown); + addSetting(this.frontpath); addSetting(this.crp); + addSetting(this.debug); } /** @@ -216,7 +249,7 @@ private boolean placeBlock() { MC.player.getInventory().selectedSlot = oldSlot; - this.idleCooldown = 5; + this.idleCooldown = (int)(this.cooldown.getValue() * 20); return true; } @@ -264,15 +297,23 @@ public void onUpdate() { if (placeBlock()) this.putPos = null; } - private BlockPos search() { + private Vec3d ofBottom(BlockPos place) { + return Vec3d.add(place, 0.5, 0, 0.5); + } + + private @Nullable BlockPos search() { final int level = 14 - 1; - final double range1Sq = this.range1.getValueSq(); + final double range1 = this.range1.getValue(); + final double range1Sq = MathHelper.square(range1); final double range2Sq = this.range2.getValueSq(); + final double range3Sq = this.range3.getValueSq(); + final double frontpath = this.frontpath.getValue(); + final double frontpathSq = MathHelper.square(frontpath); Vec3d eyesVec = RotationUtils.getEyesPos(); ClientWorld world = Objects.requireNonNull(MC.world); @SuppressWarnings("all") Stream stream =// - BlockUtils.getAllInBoxStream(BlockPos.ofFloored(eyesVec), range2.getValueCeil())// - .filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range2Sq)// + BlockUtils.getAllInBoxStream(BlockPos.ofFloored(eyesVec), range3.getValueCeil())// + .filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range3Sq)// .sorted(Comparator.comparingDouble(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos))))// .filter(pos -> { BlockState state = world.getBlockState(pos); @@ -286,31 +327,67 @@ private BlockPos search() { return world.getLightLevel(LightType.BLOCK, pos) < 1; })// ; - final var blocks = stream.toList(); - final var blocksSet = new HashSet<>(blocks); - final var blocksR1Set = - blocks.stream().filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range1Sq) + final var blocksAll = stream.toList(); + final var blocksAllSet = new HashSet<>(blocksAll); + final var blocksPlaceSet = + blocksAll.stream().filter(pos -> eyesVec.squaredDistanceTo(ofBottom(pos)) <= range1Sq) .collect(Collectors.toSet()); + if (blocksPlaceSet.isEmpty()) return null; + var blocksSearchStream = blocksAll.stream(); + if (frontpath > 0.03) { + // final var currentYaw = MathHelper.wrapDegrees(WurstClient.MC.player.getYaw()); + // var rd = MC.player.getRotationVector(); + // rd = new Vec3d(rd.x, 0, rd.z); + + float g = -MC.player.getYaw() * ((float)Math.PI / 180); + final var rayDir = new Vec3d(MathHelper.sin(g), 0, MathHelper.cos(g)); + final var rayStart = RotationUtils.getEyesPos(); + + blocksSearchStream = blocksSearchStream.filter(pos -> { + final var btn = ofBottom(pos); + if (eyesVec.squaredDistanceTo(btn) < range1Sq) { + // final var needed = RotationUtils.getNeededRotations(btn).getYaw(); + // final var diffYaw = MathHelper.wrapDegrees(currentYaw - needed); + // return Math.abs(diffYaw) < 60; + return true; + } + if (eyesVec.squaredDistanceTo(btn) < range2Sq) { + final var v = btn.subtract(rayStart); + // 计算点到射线的投影向量 + double dotProduct = v.dotProduct(rayDir); + if (dotProduct < 0) return false; + Vec3d projection = rayDir.multiply(dotProduct / rayDir.dotProduct(rayDir)); + // 计算垂线的长度(点到射线的距离) + double distance = v.subtract(projection).lengthSquared(); + //在远端应该仍然可以直接到达 + return distance < frontpathSq; + } + return false; + }); + } else { + blocksSearchStream = blocksSearchStream.filter(pos -> eyesVec.squaredDistanceTo(ofBottom(pos)) < range2Sq); + } + final var blocksSearch = blocksSearchStream.toList(); BlockPos bestPos = null; - { - var bestLights = 0; - var bestLights_r1 = 0; - for (final var p : blocks) { - if (!BlockUtils.canBeClicked(p.offset(Direction.DOWN))) continue; - if (!world.getBlockState(p).isAir()) continue; - final var light_count = getLight(p.getX(), p.getY(), p.getZ(), level, blocksSet); - var l = light_count.size(); - light_count.retainAll(blocksR1Set); - var l_r1 = light_count.size(); - if (l_r1 > bestLights_r1 || (l > bestLights && (l_r1 == bestLights_r1 || !this.crp.isChecked()))) { - bestLights_r1 = l_r1; - bestLights = l; - bestPos = p; - } - } + var bestLights = 0; + var bestLights_r1 = 0; + for (final var p : blocksSearch) { + if (!BlockUtils.canBeClicked(p.offset(Direction.DOWN))) continue; + if (!world.getBlockState(p).isAir()) continue; + final var light_count = getLight(p.getX(), p.getY(), p.getZ(), level, blocksAllSet); + final var l = light_count.size(); + if (this.crp.isChecked()) { + light_count.retainAll(blocksPlaceSet); + final var l_r1 = light_count.size(); + if (!(l_r1 > bestLights_r1 || (l > bestLights && (l_r1 == bestLights_r1)))) continue; + bestLights_r1 = l_r1; + } else if (!(l > bestLights)) continue; + bestLights = l; + bestPos = p; } + if (bestPos != null) message("Search done " + bestPos, true); return bestPos; /* From 80f84f7bb8b868c0971d0090e8fbd8206268db92 Mon Sep 17 00:00:00 2001 From: yuanlu <2573580691@qq.com> Date: Mon, 15 Jan 2024 23:46:21 +0800 Subject: [PATCH 3/5] fix trans Signed-off-by: yuanlu <2573580691@qq.com> --- .../wurstclient/hacks/AutoLightingHack.java | 114 ++++++++++-------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java index c5f048bd16..31f3051e1a 100644 --- a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java +++ b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java @@ -46,7 +46,10 @@ @SearchTags({"auto lighting", "AutoLighting", "auto torch", "AutoTorch"}) public final class AutoLightingHack extends Hack implements UpdateListener, RenderListener { + /**Maps each light level to the maximum number of blocks it can illuminate.*/ private static final int[] lvl2blocks = new int[16]; + + /**Maps items to their corresponding light levels.*/ private static final ConcurrentHashMap item2lvl = new ConcurrentHashMap<>(); static { @@ -54,47 +57,48 @@ public final class AutoLightingHack extends Hack implements UpdateListener, Rend item2lvl.put(Items.TORCH, 14); } - /** - * Placement range. Only blocks will be placed within this range.
- * 放置范围。只会在此范围内放置方块。 - */ + /**Defines the maximum distance for placing blocks.*/ private final SliderSetting range1 = new SliderSetting("RangePlace", 4.25, 1, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); - /** - * Search range. The placement position will be calculated within this range.
- * 搜索范围。会在此范围内计算放置位置。 - */ + + /**Determines the distance within which the searches for potential block placement locations.*/ private final SliderSetting range2 = new SliderSetting("RangeSearch", 8, 1, 10, 0.05, SliderSetting.ValueDisplay.DECIMAL); - /** - * Include range. Use this range to calculate the newly illuminated blocks.
- * 包含范围。用此范围计算可新照亮的方块。 - */ + + /**Sets the range to include for calculating the effect of newly placed light sources.*/ private final SliderSetting range3 = new SliderSetting("RangeInclude", 30, 10, 15 * 4, 0.05, SliderSetting.ValueDisplay.DECIMAL); + + /**Toggles the output of debug messages.*/ private final CheckboxSetting debug = new CheckboxSetting("debug", "Output Debug Message", false); + + /**Option to compare positions within the placement range.*/ private final CheckboxSetting crp = new CheckboxSetting("Compare RP", "Compare Pos with RangePlace", false); - /** - * Forward path radius. Set to 0 to calculate only within the RangeSearch range. Otherwise, Search is calculated as: - * the range with a radius less than RangePlace + - * starts from the player eye forward (ignoring the pitch, only keeping yaw) to radius less than RangeSearch.
- * 向前路径半径。设为0则只在RangeSearch范围内计算。否则Search计算为: - * 半径小于RangePlace的范围 + 玩家从眼睛开始向前(忽略pitch,只保留yaw)到半径小于RangeSearch。 - */ + + /**Specifies the radius in front of the player for block placement calculations.*/ private final SliderSetting frontpath = new SliderSetting("Front Path Distance", 3, 0, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); - /** - * The cooling time after placing a torch once (seconds). The minimum unit is 1 tick (i.e. 0.05 seconds).
- * 放置一次火把后的冷却时长(秒)。最小单位为1tick(即0.05秒)。 - */ + + /**Sets the cooldown time after each torch placement.*/ private final SliderSetting cooldown = - new SliderSetting("Place Cooldown(s)", 0.25, 1.0 / 2, 2, 1.0 / 20, SliderSetting.ValueDisplay.DECIMAL); + new SliderSetting("Place Cooldown (s)", 0.25, 1.0 / 2, 2, 1.0 / 20, SliderSetting.ValueDisplay.DECIMAL); + + /**A executor service for handling asynchronous tasks.*/ private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + + /**Future task for the current search operation for block placement.*/ private Future currentSearchTask = null; + + /**Stores the position where a block needs to be placed next.*/ private @Nullable BlockPos putPos = null; + + /**Tracks the current status of the mod (e.g., idle, waiting, placing).*/ private Status status = Status.Idle; + + /**Counter for cooldown time when the mod is in idle state.*/ private int idleCooldown = 0; + public AutoLightingHack() { super("AutoLighting"); setCategory(Category.BLOCKS); @@ -109,9 +113,9 @@ public AutoLightingHack() { } /** - * 获取光照等级为v的光源最多能点亮多少方块 + * Calculates the maximum number of blocks that can be illuminated by a light source with a brightness level of v. * - * @return 最大光照方块 + * @return the maximum number of illuminated blocks */ public static int getMaxLight(int v) { if (v < lvl2blocks.length) { @@ -126,24 +130,27 @@ public static int getMaxLight(int v) { } /** - * 获取光照等级为v的光源最多能点亮多少方块 + * Retrieves the maximum number of blocks that can be illuminated by a light source with a light level corresponding to item 'v'. * - * @return 最大光照方块 + * @return The maximum number of blocks that can be illuminated */ public static int getMaxLight(Item v) { return getMaxLight(item2lvl.getOrDefault(v, 14)); } /** - * 获取能够点亮的方块数量 + * Calculates the illuminated blocks in a certain area based on a given light level and position. + * This method can operate in two modes: real and theoretical. In real mode, it checks actual light levels in the world; + * in theoretical mode, it assumes no obstructions to light. * - * @param x 光源位置 - * @param y 光源位置 - * @param z 光源位置 - * @param v 光源位置 - * @return 能够照亮几个方块 + * @param x the x-coordinate of the light source + * @param y the y-coordinate of the light source + * @param z the z-coordinate of the light source + * @param v the light level of the source + * @param needs a set of block positions to specifically check for illumination (optional, null for theoretical mode) + * @return a list of BlockPos objects representing all blocks illuminated by the light source + * @implNote This method is marked as pure, meaning it doesn't modify any state and returns a new object. */ - @Contract(value = "_,_,_,_,_->new", pure = true) private static @NotNull ArrayList getLight(int x, int y, int z, int v, @Nullable Set needs) { if (v <= 0) return new ArrayList<>(); @@ -202,6 +209,12 @@ public String getRenderName() { return super.getRenderName() + " [" + this.status.name() + "]"; } + /** + * Searches the player's inventory for a torch and returns its slot index. + * Iterates through the first 9 inventory slots (the hotbar) to find a torch item. + * + * @return the index of the torch in the player's hotbar, or -1 if no torch is found. + */ private int getTorch() { var inv = Objects.requireNonNull(MC.player.getInventory()); for (int i = 0; i < 9; i++) { @@ -213,9 +226,13 @@ private int getTorch() { } /** - * 尝试放置方块 + * Attempts to place a block at a predetermined position. + * This method first checks if there is a suitable position to place a block. + * If a torch is available in the inventory, it uses it for block placement. + * Adjusts player's aim and sends necessary packets to execute the block placement. * - * @return 是否要刷新 + * @return true if a block placement attempt is made, false otherwise. + * @implNote Adjusts the player's inventory selection and triggers network packets for block placement. */ private boolean placeBlock() { if (this.putPos == null) return false; @@ -301,6 +318,15 @@ private Vec3d ofBottom(BlockPos place) { return Vec3d.add(place, 0.5, 0, 0.5); } + /** + * Searches for the optimal block position to place a light source based on various criteria. + * The method evaluates blocks within a defined range, considering light levels and entity spawning conditions. + * Prioritizes blocks based on their distance, visibility, and potential to illuminate the area effectively. + * + * @return the optimal BlockPos for placing a light source, or null if no suitable position is found. + * @implNote Considers multiple ranges and conditions (e.g., line of sight, block state, entity spawning) + * to determine the best placement location. + */ private @Nullable BlockPos search() { final int level = 14 - 1; final double range1 = this.range1.getValue(); @@ -311,7 +337,7 @@ private Vec3d ofBottom(BlockPos place) { final double frontpathSq = MathHelper.square(frontpath); Vec3d eyesVec = RotationUtils.getEyesPos(); ClientWorld world = Objects.requireNonNull(MC.world); - @SuppressWarnings("all") Stream stream =// + Stream stream =// BlockUtils.getAllInBoxStream(BlockPos.ofFloored(eyesVec), range3.getValueCeil())// .filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range3Sq)// .sorted(Comparator.comparingDouble(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos))))// @@ -353,13 +379,12 @@ private Vec3d ofBottom(BlockPos place) { } if (eyesVec.squaredDistanceTo(btn) < range2Sq) { final var v = btn.subtract(rayStart); - // 计算点到射线的投影向量 + // Compute point-to-ray projection vector double dotProduct = v.dotProduct(rayDir); if (dotProduct < 0) return false; Vec3d projection = rayDir.multiply(dotProduct / rayDir.dotProduct(rayDir)); - // 计算垂线的长度(点到射线的距离) + // Calculate the length of the vertical line (distance from point to ray) double distance = v.subtract(projection).lengthSquared(); - //在远端应该仍然可以直接到达 return distance < frontpathSq; } return false; @@ -390,13 +415,6 @@ private Vec3d ofBottom(BlockPos place) { if (bestPos != null) message("Search done " + bestPos, true); return bestPos; - /* - * 如果周围全都是刷怪区,那么就放在脚下 - * 如果距离更大的能覆盖更多,且在r1范围不可点亮区不减少,则选择更远的。 - * - * - */ - } @Override From fda6dd1065f177371a6b51270c560f7211aac530 Mon Sep 17 00:00:00 2001 From: yuanlu <2573580691@qq.com> Date: Tue, 16 Jan 2024 00:14:59 +0800 Subject: [PATCH 4/5] ./gradlew :spotlessApply Signed-off-by: yuanlu <2573580691@qq.com> --- .../java/net/wurstclient/hack/HackList.java | 2 +- .../wurstclient/hacks/AutoLightingHack.java | 1030 ++++++++++------- 2 files changed, 585 insertions(+), 447 deletions(-) diff --git a/src/main/java/net/wurstclient/hack/HackList.java b/src/main/java/net/wurstclient/hack/HackList.java index c9a8641146..da01ea9ffd 100644 --- a/src/main/java/net/wurstclient/hack/HackList.java +++ b/src/main/java/net/wurstclient/hack/HackList.java @@ -48,7 +48,7 @@ public final class HackList implements UpdateListener public final AutoDropHack autoDropHack = new AutoDropHack(); public final AutoLeaveHack autoLeaveHack = new AutoLeaveHack(); public final AutoLibrarianHack autoLibrarianHack = new AutoLibrarianHack(); - public final AutoLightingHack autoLightingHack = new AutoLightingHack(); + public final AutoLightingHack autoLightingHack = new AutoLightingHack(); public final AutoEatHack autoEatHack = new AutoEatHack(); public final AutoFarmHack autoFarmHack = new AutoFarmHack(); public final AutoFishHack autoFishHack = new AutoFishHack(); diff --git a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java index 31f3051e1a..86e85903f9 100644 --- a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java +++ b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java @@ -45,450 +45,588 @@ import java.util.stream.Stream; @SearchTags({"auto lighting", "AutoLighting", "auto torch", "AutoTorch"}) -public final class AutoLightingHack extends Hack implements UpdateListener, RenderListener { - /**Maps each light level to the maximum number of blocks it can illuminate.*/ - private static final int[] lvl2blocks = new int[16]; - - /**Maps items to their corresponding light levels.*/ - private static final ConcurrentHashMap item2lvl = new ConcurrentHashMap<>(); - - static { - Arrays.fill(lvl2blocks, -1); - item2lvl.put(Items.TORCH, 14); - } - - /**Defines the maximum distance for placing blocks.*/ - private final SliderSetting range1 = - new SliderSetting("RangePlace", 4.25, 1, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); - - /**Determines the distance within which the searches for potential block placement locations.*/ - private final SliderSetting range2 = - new SliderSetting("RangeSearch", 8, 1, 10, 0.05, SliderSetting.ValueDisplay.DECIMAL); - - /**Sets the range to include for calculating the effect of newly placed light sources.*/ - private final SliderSetting range3 = - new SliderSetting("RangeInclude", 30, 10, 15 * 4, 0.05, SliderSetting.ValueDisplay.DECIMAL); - - /**Toggles the output of debug messages.*/ - private final CheckboxSetting debug = new CheckboxSetting("debug", "Output Debug Message", false); - - /**Option to compare positions within the placement range.*/ - private final CheckboxSetting crp = new CheckboxSetting("Compare RP", "Compare Pos with RangePlace", false); - - /**Specifies the radius in front of the player for block placement calculations.*/ - private final SliderSetting frontpath = - new SliderSetting("Front Path Distance", 3, 0, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); - - /**Sets the cooldown time after each torch placement.*/ - private final SliderSetting cooldown = - new SliderSetting("Place Cooldown (s)", 0.25, 1.0 / 2, 2, 1.0 / 20, SliderSetting.ValueDisplay.DECIMAL); - - /**A executor service for handling asynchronous tasks.*/ - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - - /**Future task for the current search operation for block placement.*/ - private Future currentSearchTask = null; - - /**Stores the position where a block needs to be placed next.*/ - private @Nullable BlockPos putPos = null; - - /**Tracks the current status of the mod (e.g., idle, waiting, placing).*/ - private Status status = Status.Idle; - - /**Counter for cooldown time when the mod is in idle state.*/ - private int idleCooldown = 0; - - - public AutoLightingHack() { - super("AutoLighting"); - setCategory(Category.BLOCKS); - - addSetting(this.range1); - addSetting(this.range2); - addSetting(this.range3); - addSetting(this.cooldown); - addSetting(this.frontpath); - addSetting(this.crp); - addSetting(this.debug); - } - - /** - * Calculates the maximum number of blocks that can be illuminated by a light source with a brightness level of v. - * - * @return the maximum number of illuminated blocks - */ - public static int getMaxLight(int v) { - if (v < lvl2blocks.length) { - if (lvl2blocks[v] > -1) return lvl2blocks[v]; - synchronized (lvl2blocks) { - if (lvl2blocks[v] > -1) return lvl2blocks[v]; - lvl2blocks[v] = getLight(0, 0, 0, v, null).size(); - return lvl2blocks[v]; - } - } - return Integer.MAX_VALUE; - } - - /** - * Retrieves the maximum number of blocks that can be illuminated by a light source with a light level corresponding to item 'v'. - * - * @return The maximum number of blocks that can be illuminated - */ - public static int getMaxLight(Item v) { - return getMaxLight(item2lvl.getOrDefault(v, 14)); - } - - /** - * Calculates the illuminated blocks in a certain area based on a given light level and position. - * This method can operate in two modes: real and theoretical. In real mode, it checks actual light levels in the world; - * in theoretical mode, it assumes no obstructions to light. - * - * @param x the x-coordinate of the light source - * @param y the y-coordinate of the light source - * @param z the z-coordinate of the light source - * @param v the light level of the source - * @param needs a set of block positions to specifically check for illumination (optional, null for theoretical mode) - * @return a list of BlockPos objects representing all blocks illuminated by the light source - * @implNote This method is marked as pure, meaning it doesn't modify any state and returns a new object. - */ - @Contract(value = "_,_,_,_,_->new", pure = true) - private static @NotNull ArrayList getLight(int x, int y, int z, int v, @Nullable Set needs) { - if (v <= 0) return new ArrayList<>(); - final var w = MC.world; - final boolean is_real = needs != null; - if (is_real && w == null) return new ArrayList<>(); - final var count = new ArrayList(); - int searched = 0; - Queue queue = new ArrayDeque<>(); - Set visited = new HashSet<>(); - - final var fst = new ValuePos(x, y, z, v); - queue.add(fst); - visited.add(fst.toXYZ()); - - while (!queue.isEmpty()) { - final var point = queue.poll(); - if (is_real && searched++ > getMaxLight(lvl2blocks.length - 1)) break; - final var pt = point.toXYZ(); - - if (is_real) { - if (w.getBlockState(pt).isOpaque()) continue; - final var lvl = w.getLightLevel(LightType.BLOCK, pt); - if (lvl >= point.v) continue; - if (needs.contains(pt)) count.add(pt); - } else { - count.add(pt); - } - - if (point.v > 0) { - v = point.v - 1; - for (int i = 0; i < 6; i++) { - final var to = point.move(i); - if (visited.contains(to)) continue; - visited.add(to); - queue.add(new ValuePos(to.getX(), to.getY(), to.getZ(), v)); - } - } - - } - - return count; - } - - private void message(Object msg, boolean is_debug) { - if (is_debug && !this.debug.isChecked()) return; - ChatUtils.message("[%s] %s".formatted(getName(), msg)); - } - - private void message(Object msg) { - message(msg, false); - } - - @Override - public String getRenderName() { - return super.getRenderName() + " [" + this.status.name() + "]"; - } - - /** - * Searches the player's inventory for a torch and returns its slot index. - * Iterates through the first 9 inventory slots (the hotbar) to find a torch item. - * - * @return the index of the torch in the player's hotbar, or -1 if no torch is found. - */ - private int getTorch() { - var inv = Objects.requireNonNull(MC.player.getInventory()); - for (int i = 0; i < 9; i++) { - ItemStack stack = inv.getStack(i); - if (stack.isEmpty() || !(stack.getItem().equals(Items.TORCH))) continue; - return i; - } - return -1; - } - - /** - * Attempts to place a block at a predetermined position. - * This method first checks if there is a suitable position to place a block. - * If a torch is available in the inventory, it uses it for block placement. - * Adjusts player's aim and sends necessary packets to execute the block placement. - * - * @return true if a block placement attempt is made, false otherwise. - * @implNote Adjusts the player's inventory selection and triggers network packets for block placement. - */ - private boolean placeBlock() { - if (this.putPos == null) return false; - var newSlot = getTorch(); - if (newSlot < 0) { - this.status = Status.NoTorch; - return false; - } - this.status = Status.Waiting; - Vec3d eyesPos = RotationUtils.getEyesPos(); - BlockPos down = this.putPos.offset(Direction.DOWN); - if (!BlockUtils.canBeClicked(down)) return true; - Direction up = Direction.DOWN.getOpposite(); - Vec3d hitVec = Vec3d.ofCenter(down).add(Vec3d.of(up.getVector()).multiply(0.5)); - var distanceSq = eyesPos.squaredDistanceTo(hitVec); - if (distanceSq > this.range2.getValueSq()) return true; - if (distanceSq > this.range1.getValueSq()) return false; - // place block - message("placeBlock = " + this.putPos, true); - - this.status = Status.Putting; - int oldSlot = MC.player.getInventory().selectedSlot; - MC.player.getInventory().selectedSlot = newSlot; - - var rotation = RotationUtils.getNeededRotations(hitVec); - MC.player.networkHandler.sendPacket( - new PlayerMoveC2SPacket.LookAndOnGround(rotation.getYaw(), rotation.getPitch(), MC.player.isOnGround())); - IMC.getInteractionManager().rightClickBlock(down, up, hitVec); - MC.player.swingHand(Hand.MAIN_HAND); - MC.itemUseCooldown = 4; - - MC.player.getInventory().selectedSlot = oldSlot; - - this.idleCooldown = (int)(this.cooldown.getValue() * 20); - - return true; - } - - @Override - protected void onEnable() { - EVENTS.add(UpdateListener.class, this); - EVENTS.add(RenderListener.class, this); - this.status = Status.Idle; - } - - @Override - protected void onDisable() { - EVENTS.remove(UpdateListener.class, this); - EVENTS.remove(RenderListener.class, this); - if (this.currentSearchTask != null) this.currentSearchTask.cancel(true); - this.currentSearchTask = null; - } - - @Override - public void onUpdate() { - if (this.currentSearchTask != null && !this.currentSearchTask.isDone()) { - this.status = Status.Searching; - return; - } - if (this.idleCooldown > 0) { - this.idleCooldown--; - this.status = Status.Idle; - return; - } - - if (this.currentSearchTask != null) { - try { - this.putPos = this.currentSearchTask.get(); - } catch (Exception e) { - e.printStackTrace(); - ChatUtils.error("[" + super.getName() + "] Can not search: " + e); - super.setEnabled(false); - } - this.currentSearchTask = null; - } - - if (this.putPos == null) this.currentSearchTask = this.executorService.submit(this::search); - - if (placeBlock()) this.putPos = null; - } - - private Vec3d ofBottom(BlockPos place) { - return Vec3d.add(place, 0.5, 0, 0.5); - } - - /** - * Searches for the optimal block position to place a light source based on various criteria. - * The method evaluates blocks within a defined range, considering light levels and entity spawning conditions. - * Prioritizes blocks based on their distance, visibility, and potential to illuminate the area effectively. - * - * @return the optimal BlockPos for placing a light source, or null if no suitable position is found. - * @implNote Considers multiple ranges and conditions (e.g., line of sight, block state, entity spawning) - * to determine the best placement location. - */ - private @Nullable BlockPos search() { - final int level = 14 - 1; - final double range1 = this.range1.getValue(); - final double range1Sq = MathHelper.square(range1); - final double range2Sq = this.range2.getValueSq(); - final double range3Sq = this.range3.getValueSq(); - final double frontpath = this.frontpath.getValue(); - final double frontpathSq = MathHelper.square(frontpath); - Vec3d eyesVec = RotationUtils.getEyesPos(); - ClientWorld world = Objects.requireNonNull(MC.world); - Stream stream =// - BlockUtils.getAllInBoxStream(BlockPos.ofFloored(eyesVec), range3.getValueCeil())// - .filter(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos)) <= range3Sq)// - .sorted(Comparator.comparingDouble(pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos))))// - .filter(pos -> { - BlockState state = world.getBlockState(pos); - - if (state.blocksMovement()) return false; - if (!state.getFluidState().isEmpty()) return false; - - BlockState stateDown = world.getBlockState(pos.down()); - if (!stateDown.allowsSpawning(world, pos.down(), EntityType.ZOMBIE)) return false; - - return world.getLightLevel(LightType.BLOCK, pos) < 1; - })// - ; - final var blocksAll = stream.toList(); - final var blocksAllSet = new HashSet<>(blocksAll); - final var blocksPlaceSet = - blocksAll.stream().filter(pos -> eyesVec.squaredDistanceTo(ofBottom(pos)) <= range1Sq) - .collect(Collectors.toSet()); - if (blocksPlaceSet.isEmpty()) return null; - var blocksSearchStream = blocksAll.stream(); - if (frontpath > 0.03) { - // final var currentYaw = MathHelper.wrapDegrees(WurstClient.MC.player.getYaw()); - // var rd = MC.player.getRotationVector(); - // rd = new Vec3d(rd.x, 0, rd.z); - - float g = -MC.player.getYaw() * ((float)Math.PI / 180); - final var rayDir = new Vec3d(MathHelper.sin(g), 0, MathHelper.cos(g)); - final var rayStart = RotationUtils.getEyesPos(); - - blocksSearchStream = blocksSearchStream.filter(pos -> { - final var btn = ofBottom(pos); - if (eyesVec.squaredDistanceTo(btn) < range1Sq) { - // final var needed = RotationUtils.getNeededRotations(btn).getYaw(); - // final var diffYaw = MathHelper.wrapDegrees(currentYaw - needed); - // return Math.abs(diffYaw) < 60; - return true; - } - if (eyesVec.squaredDistanceTo(btn) < range2Sq) { - final var v = btn.subtract(rayStart); - // Compute point-to-ray projection vector - double dotProduct = v.dotProduct(rayDir); - if (dotProduct < 0) return false; - Vec3d projection = rayDir.multiply(dotProduct / rayDir.dotProduct(rayDir)); - // Calculate the length of the vertical line (distance from point to ray) - double distance = v.subtract(projection).lengthSquared(); - return distance < frontpathSq; - } - return false; - }); - } else { - blocksSearchStream = blocksSearchStream.filter(pos -> eyesVec.squaredDistanceTo(ofBottom(pos)) < range2Sq); - } - final var blocksSearch = blocksSearchStream.toList(); - - BlockPos bestPos = null; - - var bestLights = 0; - var bestLights_r1 = 0; - for (final var p : blocksSearch) { - if (!BlockUtils.canBeClicked(p.offset(Direction.DOWN))) continue; - if (!world.getBlockState(p).isAir()) continue; - final var light_count = getLight(p.getX(), p.getY(), p.getZ(), level, blocksAllSet); - final var l = light_count.size(); - if (this.crp.isChecked()) { - light_count.retainAll(blocksPlaceSet); - final var l_r1 = light_count.size(); - if (!(l_r1 > bestLights_r1 || (l > bestLights && (l_r1 == bestLights_r1)))) continue; - bestLights_r1 = l_r1; - } else if (!(l > bestLights)) continue; - bestLights = l; - bestPos = p; - } - - if (bestPos != null) message("Search done " + bestPos, true); - return bestPos; - } - - @Override - public void onRender(MatrixStack matrixStack, float partialTicks) { - final var pos = this.putPos; - if (pos == null) return; - - float scale = 7F / 8F; - double offset = (1D - scale) / 2D; - Vec3d eyesPos = RotationUtils.getEyesPos(); - double rangeSq = Math.pow(this.range1.getValue(), 2); - - // GL settings - GL11.glEnable(GL11.GL_BLEND); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GL11.glDisable(GL11.GL_CULL_FACE); - RenderSystem.setShaderColor(0F, 0F, 0F, 0.5F); - - matrixStack.push(); - - RegionPos region = RenderUtils.getCameraRegion(); - RenderUtils.applyRegionalRenderOffset(matrixStack, region); - - RenderSystem.setShader(GameRenderer::getPositionProgram); - - matrixStack.push(); - matrixStack.translate(pos.getX() - region.x(), pos.getY(), pos.getZ() - region.z()); - matrixStack.translate(offset, offset, offset); - matrixStack.scale(scale, scale, scale); - - Vec3d posVec = Vec3d.ofCenter(pos); - - drawBox(matrixStack, eyesPos.squaredDistanceTo(posVec) <= rangeSq); - - matrixStack.pop(); - - matrixStack.pop(); - - // GL resets - GL11.glDisable(GL11.GL_BLEND); - RenderSystem.setShaderColor(1, 1, 1, 1); - } - - private void drawBox(MatrixStack matrixStack, boolean isGreen) { - GL11.glDepthMask(false); - RenderSystem.setShaderColor(isGreen ? 0F : 1F, isGreen ? 1F : 0F, 0F, 0.15F); - RenderUtils.drawSolidBox(matrixStack); - GL11.glDepthMask(true); - - RenderSystem.setShaderColor(0F, 0F, 0F, 0.5F); - RenderUtils.drawOutlinedBox(matrixStack); - } - - private enum Status { - Idle, Searching, Waiting, Putting, NoTorch - } - - private record ValuePos(int x, int y, int z, int v) { - private static final int[] dx = {-1, 1, 0, 0, 0, 0}; - private static final int[] dy = {0, 0, -1, 1, 0, 0}; - private static final int[] dz = {0, 0, 0, 0, -1, 1}; - - @Contract(value = " -> new", pure = true) - public @NotNull BlockPos toXYZ() {return new BlockPos(this.x, this.y, this.z);} - - @Contract(value = "_, _, _ -> new", pure = true) - public @NotNull BlockPos move(int x, int y, int z) { - return new BlockPos(this.x + x, this.y + y, this.z + z); - } - - @Contract(value = "_ -> new", pure = true) - public @NotNull BlockPos move(int i) { - return move(dx[i], dy[i], dz[i]); - } - } - +public final class AutoLightingHack extends Hack + implements UpdateListener, RenderListener +{ + /** + * Maps each light level to the maximum number of blocks it can illuminate. + */ + private static final int[] lvl2blocks = new int[16]; + + /** Maps items to their corresponding light levels. */ + private static final ConcurrentHashMap item2lvl = + new ConcurrentHashMap<>(); + + static + { + Arrays.fill(lvl2blocks, -1); + item2lvl.put(Items.TORCH, 14); + } + + /** Defines the maximum distance for placing blocks. */ + private final SliderSetting range1 = new SliderSetting("RangePlace", 4.25, + 1, 6, 0.05, SliderSetting.ValueDisplay.DECIMAL); + + /** + * Determines the distance within which the searches for potential block + * placement locations. + */ + private final SliderSetting range2 = new SliderSetting("RangeSearch", 8, 1, + 10, 0.05, SliderSetting.ValueDisplay.DECIMAL); + + /** + * Sets the range to include for calculating the effect of newly placed + * light sources. + */ + private final SliderSetting range3 = new SliderSetting("RangeInclude", 30, + 10, 15 * 4, 0.05, SliderSetting.ValueDisplay.DECIMAL); + + /** Toggles the output of debug messages. */ + private final CheckboxSetting debug = + new CheckboxSetting("debug", "Output Debug Message", false); + + /** Option to compare positions within the placement range. */ + private final CheckboxSetting crp = + new CheckboxSetting("Compare RP", "Compare Pos with RangePlace", false); + + /** + * Specifies the radius in front of the player for block placement + * calculations. + */ + private final SliderSetting frontpath = + new SliderSetting("Front Path Distance", 3, 0, 6, 0.05, + SliderSetting.ValueDisplay.DECIMAL); + + /** Sets the cooldown time after each torch placement. */ + private final SliderSetting cooldown = + new SliderSetting("Place Cooldown (s)", 0.25, 1.0 / 2, 2, 1.0 / 20, + SliderSetting.ValueDisplay.DECIMAL); + + /** A executor service for handling asynchronous tasks. */ + private final ExecutorService executorService = + Executors.newSingleThreadExecutor(); + + /** Future task for the current search operation for block placement. */ + private Future currentSearchTask = null; + + /** Stores the position where a block needs to be placed next. */ + private @Nullable BlockPos putPos = null; + + /** Tracks the current status of the mod (e.g., idle, waiting, placing). */ + private Status status = Status.Idle; + + /** Counter for cooldown time when the mod is in idle state. */ + private int idleCooldown = 0; + + public AutoLightingHack() + { + super("AutoLighting"); + setCategory(Category.BLOCKS); + + addSetting(this.range1); + addSetting(this.range2); + addSetting(this.range3); + addSetting(this.cooldown); + addSetting(this.frontpath); + addSetting(this.crp); + addSetting(this.debug); + } + + /** + * Calculates the maximum number of blocks that can be illuminated by a + * light source with a brightness level of v. + * + * @return the maximum number of illuminated blocks + */ + public static int getMaxLight(int v) + { + if(v < lvl2blocks.length) + { + if(lvl2blocks[v] > -1) + return lvl2blocks[v]; + synchronized(lvl2blocks) + { + if(lvl2blocks[v] > -1) + return lvl2blocks[v]; + lvl2blocks[v] = getLight(0, 0, 0, v, null).size(); + return lvl2blocks[v]; + } + } + return Integer.MAX_VALUE; + } + + /** + * Retrieves the maximum number of blocks that can be illuminated by a light + * source with a light level corresponding to item 'v'. + * + * @return The maximum number of blocks that can be illuminated + */ + public static int getMaxLight(Item v) + { + return getMaxLight(item2lvl.getOrDefault(v, 14)); + } + + /** + * Calculates the illuminated blocks in a certain area based on a given + * light level and position. + * This method can operate in two modes: real and theoretical. In real mode, + * it checks actual light levels in the world; + * in theoretical mode, it assumes no obstructions to light. + * + * @param x + * the x-coordinate of the light source + * @param y + * the y-coordinate of the light source + * @param z + * the z-coordinate of the light source + * @param v + * the light level of the source + * @param needs + * a set of block positions to specifically check for + * illumination (optional, null for theoretical mode) + * @return a list of BlockPos objects representing all blocks illuminated by + * the light source + * @implNote This method is marked as pure, meaning it doesn't modify any + * state and returns a new object. + */ + @Contract(value = "_,_,_,_,_->new", pure = true) + private static @NotNull ArrayList getLight(int x, int y, int z, + int v, @Nullable Set needs) + { + if(v <= 0) + return new ArrayList<>(); + final var w = MC.world; + final boolean is_real = needs != null; + if(is_real && w == null) + return new ArrayList<>(); + final var count = new ArrayList(); + int searched = 0; + Queue queue = new ArrayDeque<>(); + Set visited = new HashSet<>(); + + final var fst = new ValuePos(x, y, z, v); + queue.add(fst); + visited.add(fst.toXYZ()); + + while(!queue.isEmpty()) + { + final var point = queue.poll(); + if(is_real && searched++ > getMaxLight(lvl2blocks.length - 1)) + break; + final var pt = point.toXYZ(); + + if(is_real) + { + if(w.getBlockState(pt).isOpaque()) + continue; + final var lvl = w.getLightLevel(LightType.BLOCK, pt); + if(lvl >= point.v) + continue; + if(needs.contains(pt)) + count.add(pt); + }else + { + count.add(pt); + } + + if(point.v > 0) + { + v = point.v - 1; + for(int i = 0; i < 6; i++) + { + final var to = point.move(i); + if(visited.contains(to)) + continue; + visited.add(to); + queue.add(new ValuePos(to.getX(), to.getY(), to.getZ(), v)); + } + } + + } + + return count; + } + + private void message(Object msg, boolean is_debug) + { + if(is_debug && !this.debug.isChecked()) + return; + ChatUtils.message("[%s] %s".formatted(getName(), msg)); + } + + private void message(Object msg) + { + message(msg, false); + } + + @Override + public String getRenderName() + { + return super.getRenderName() + " [" + this.status.name() + "]"; + } + + /** + * Searches the player's inventory for a torch and returns its slot index. + * Iterates through the first 9 inventory slots (the hotbar) to find a torch + * item. + * + * @return the index of the torch in the player's hotbar, or -1 if no torch + * is found. + */ + private int getTorch() + { + var inv = Objects.requireNonNull(MC.player.getInventory()); + for(int i = 0; i < 9; i++) + { + ItemStack stack = inv.getStack(i); + if(stack.isEmpty() || !(stack.getItem().equals(Items.TORCH))) + continue; + return i; + } + return -1; + } + + /** + * Attempts to place a block at a predetermined position. + * This method first checks if there is a suitable position to place a + * block. + * If a torch is available in the inventory, it uses it for block placement. + * Adjusts player's aim and sends necessary packets to execute the block + * placement. + * + * @return true if a block placement attempt is made, false otherwise. + * @implNote Adjusts the player's inventory selection and triggers network + * packets for block placement. + */ + private boolean placeBlock() + { + if(this.putPos == null) + return false; + var newSlot = getTorch(); + if(newSlot < 0) + { + this.status = Status.NoTorch; + return false; + } + this.status = Status.Waiting; + Vec3d eyesPos = RotationUtils.getEyesPos(); + BlockPos down = this.putPos.offset(Direction.DOWN); + if(!BlockUtils.canBeClicked(down)) + return true; + Direction up = Direction.DOWN.getOpposite(); + Vec3d hitVec = + Vec3d.ofCenter(down).add(Vec3d.of(up.getVector()).multiply(0.5)); + var distanceSq = eyesPos.squaredDistanceTo(hitVec); + if(distanceSq > this.range2.getValueSq()) + return true; + if(distanceSq > this.range1.getValueSq()) + return false; + // place block + message("placeBlock = " + this.putPos, true); + + this.status = Status.Putting; + int oldSlot = MC.player.getInventory().selectedSlot; + MC.player.getInventory().selectedSlot = newSlot; + + var rotation = RotationUtils.getNeededRotations(hitVec); + MC.player.networkHandler.sendPacket( + new PlayerMoveC2SPacket.LookAndOnGround(rotation.getYaw(), + rotation.getPitch(), MC.player.isOnGround())); + IMC.getInteractionManager().rightClickBlock(down, up, hitVec); + MC.player.swingHand(Hand.MAIN_HAND); + MC.itemUseCooldown = 4; + + MC.player.getInventory().selectedSlot = oldSlot; + + this.idleCooldown = (int)(this.cooldown.getValue() * 20); + + return true; + } + + @Override + protected void onEnable() + { + EVENTS.add(UpdateListener.class, this); + EVENTS.add(RenderListener.class, this); + this.status = Status.Idle; + } + + @Override + protected void onDisable() + { + EVENTS.remove(UpdateListener.class, this); + EVENTS.remove(RenderListener.class, this); + if(this.currentSearchTask != null) + this.currentSearchTask.cancel(true); + this.currentSearchTask = null; + } + + @Override + public void onUpdate() + { + if(this.currentSearchTask != null && !this.currentSearchTask.isDone()) + { + this.status = Status.Searching; + return; + } + if(this.idleCooldown > 0) + { + this.idleCooldown--; + this.status = Status.Idle; + return; + } + + if(this.currentSearchTask != null) + { + try + { + this.putPos = this.currentSearchTask.get(); + }catch(Exception e) + { + e.printStackTrace(); + ChatUtils + .error("[" + super.getName() + "] Can not search: " + e); + super.setEnabled(false); + } + this.currentSearchTask = null; + } + + if(this.putPos == null) + this.currentSearchTask = this.executorService.submit(this::search); + + if(placeBlock()) + this.putPos = null; + } + + private Vec3d ofBottom(BlockPos place) + { + return Vec3d.add(place, 0.5, 0, 0.5); + } + + /** + * Searches for the optimal block position to place a light source based on + * various criteria. + * The method evaluates blocks within a defined range, considering light + * levels and entity spawning conditions. + * Prioritizes blocks based on their distance, visibility, and potential to + * illuminate the area effectively. + * + * @return the optimal BlockPos for placing a light source, or null if no + * suitable position is found. + * @implNote Considers multiple ranges and conditions (e.g., line of sight, + * block state, entity spawning) + * to determine the best placement location. + */ + private @Nullable BlockPos search() + { + final int level = 14 - 1; + final double range1 = this.range1.getValue(); + final double range1Sq = MathHelper.square(range1); + final double range2Sq = this.range2.getValueSq(); + final double range3Sq = this.range3.getValueSq(); + final double frontpath = this.frontpath.getValue(); + final double frontpathSq = MathHelper.square(frontpath); + Vec3d eyesVec = RotationUtils.getEyesPos(); + ClientWorld world = Objects.requireNonNull(MC.world); + Stream stream = // + BlockUtils + .getAllInBoxStream(BlockPos.ofFloored(eyesVec), + range3.getValueCeil())// + .filter(pos -> eyesVec + .squaredDistanceTo(Vec3d.ofCenter(pos)) <= range3Sq)// + .sorted(Comparator.comparingDouble( + pos -> eyesVec.squaredDistanceTo(Vec3d.ofCenter(pos))))// + .filter(pos -> { + BlockState state = world.getBlockState(pos); + + if(state.blocksMovement()) + return false; + if(!state.getFluidState().isEmpty()) + return false; + + BlockState stateDown = world.getBlockState(pos.down()); + if(!stateDown.allowsSpawning(world, pos.down(), + EntityType.ZOMBIE)) + return false; + + return world.getLightLevel(LightType.BLOCK, pos) < 1; + })// + ; + final var blocksAll = stream.toList(); + final var blocksAllSet = new HashSet<>(blocksAll); + final var blocksPlaceSet = blocksAll.stream() + .filter(pos -> eyesVec.squaredDistanceTo(ofBottom(pos)) <= range1Sq) + .collect(Collectors.toSet()); + if(blocksPlaceSet.isEmpty()) + return null; + var blocksSearchStream = blocksAll.stream(); + if(frontpath > 0.03) + { + // final var currentYaw = + // MathHelper.wrapDegrees(WurstClient.MC.player.getYaw()); + // var rd = MC.player.getRotationVector(); + // rd = new Vec3d(rd.x, 0, rd.z); + + float g = -MC.player.getYaw() * ((float)Math.PI / 180); + final var rayDir = + new Vec3d(MathHelper.sin(g), 0, MathHelper.cos(g)); + final var rayStart = RotationUtils.getEyesPos(); + + blocksSearchStream = blocksSearchStream.filter(pos -> { + final var btn = ofBottom(pos); + if(eyesVec.squaredDistanceTo(btn) < range1Sq) + { + // final var needed = + // RotationUtils.getNeededRotations(btn).getYaw(); + // final var diffYaw = MathHelper.wrapDegrees(currentYaw - + // needed); + // return Math.abs(diffYaw) < 60; + return true; + } + if(eyesVec.squaredDistanceTo(btn) < range2Sq) + { + final var v = btn.subtract(rayStart); + // Compute point-to-ray projection vector + double dotProduct = v.dotProduct(rayDir); + if(dotProduct < 0) + return false; + Vec3d projection = + rayDir.multiply(dotProduct / rayDir.dotProduct(rayDir)); + // Calculate the length of the vertical line (distance from + // point to ray) + double distance = v.subtract(projection).lengthSquared(); + return distance < frontpathSq; + } + return false; + }); + }else + { + blocksSearchStream = blocksSearchStream.filter( + pos -> eyesVec.squaredDistanceTo(ofBottom(pos)) < range2Sq); + } + final var blocksSearch = blocksSearchStream.toList(); + + BlockPos bestPos = null; + + var bestLights = 0; + var bestLights_r1 = 0; + for(final var p : blocksSearch) + { + if(!BlockUtils.canBeClicked(p.offset(Direction.DOWN))) + continue; + if(!world.getBlockState(p).isAir()) + continue; + final var light_count = + getLight(p.getX(), p.getY(), p.getZ(), level, blocksAllSet); + final var l = light_count.size(); + if(this.crp.isChecked()) + { + light_count.retainAll(blocksPlaceSet); + final var l_r1 = light_count.size(); + if(!(l_r1 > bestLights_r1 + || (l > bestLights && (l_r1 == bestLights_r1)))) + continue; + bestLights_r1 = l_r1; + }else if(!(l > bestLights)) + continue; + bestLights = l; + bestPos = p; + } + + if(bestPos != null) + message("Search done " + bestPos, true); + return bestPos; + } + + @Override + public void onRender(MatrixStack matrixStack, float partialTicks) + { + final var pos = this.putPos; + if(pos == null) + return; + + float scale = 7F / 8F; + double offset = (1D - scale) / 2D; + Vec3d eyesPos = RotationUtils.getEyesPos(); + double rangeSq = Math.pow(this.range1.getValue(), 2); + + // GL settings + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_CULL_FACE); + RenderSystem.setShaderColor(0F, 0F, 0F, 0.5F); + + matrixStack.push(); + + RegionPos region = RenderUtils.getCameraRegion(); + RenderUtils.applyRegionalRenderOffset(matrixStack, region); + + RenderSystem.setShader(GameRenderer::getPositionProgram); + + matrixStack.push(); + matrixStack.translate(pos.getX() - region.x(), pos.getY(), + pos.getZ() - region.z()); + matrixStack.translate(offset, offset, offset); + matrixStack.scale(scale, scale, scale); + + Vec3d posVec = Vec3d.ofCenter(pos); + + drawBox(matrixStack, eyesPos.squaredDistanceTo(posVec) <= rangeSq); + + matrixStack.pop(); + + matrixStack.pop(); + + // GL resets + GL11.glDisable(GL11.GL_BLEND); + RenderSystem.setShaderColor(1, 1, 1, 1); + } + + private void drawBox(MatrixStack matrixStack, boolean isGreen) + { + GL11.glDepthMask(false); + RenderSystem.setShaderColor(isGreen ? 0F : 1F, isGreen ? 1F : 0F, 0F, + 0.15F); + RenderUtils.drawSolidBox(matrixStack); + GL11.glDepthMask(true); + + RenderSystem.setShaderColor(0F, 0F, 0F, 0.5F); + RenderUtils.drawOutlinedBox(matrixStack); + } + + private enum Status + { + Idle, + Searching, + Waiting, + Putting, + NoTorch + } + + private record ValuePos(int x, int y, int z, int v) + { + + private static final int[] dx = {-1, 1, 0, 0, 0, 0}; + private static final int[] dy = {0, 0, -1, 1, 0, 0}; + private static final int[] dz = {0, 0, 0, 0, -1, 1}; + + @Contract(value = " -> new", pure = true) + public @NotNull BlockPos toXYZ() + { + return new BlockPos(this.x, this.y, this.z); + } + + @Contract(value = "_, _, _ -> new", pure = true) + public @NotNull BlockPos move(int x, int y, int z) + { + return new BlockPos(this.x + x, this.y + y, this.z + z); + } + + @Contract(value = "_ -> new", pure = true) + public @NotNull BlockPos move(int i) + { + return move(dx[i], dy[i], dz[i]); + } + } + } From b9a25d49dffc30f1bb6ac1b9cad2b896be007d53 Mon Sep 17 00:00:00 2001 From: yuanlu <2573580691@qq.com> Date: Tue, 16 Jan 2024 00:19:15 +0800 Subject: [PATCH 5/5] fix merge bug Signed-off-by: yuanlu <2573580691@qq.com> --- src/main/java/net/wurstclient/hacks/AutoLightingHack.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java index 86e85903f9..f84913f524 100644 --- a/src/main/java/net/wurstclient/hacks/AutoLightingHack.java +++ b/src/main/java/net/wurstclient/hacks/AutoLightingHack.java @@ -329,9 +329,9 @@ private boolean placeBlock() MC.player.getInventory().selectedSlot = newSlot; var rotation = RotationUtils.getNeededRotations(hitVec); - MC.player.networkHandler.sendPacket( - new PlayerMoveC2SPacket.LookAndOnGround(rotation.getYaw(), - rotation.getPitch(), MC.player.isOnGround())); + MC.player.networkHandler + .sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(rotation.yaw(), + rotation.pitch(), MC.player.isOnGround())); IMC.getInteractionManager().rightClickBlock(down, up, hitVec); MC.player.swingHand(Hand.MAIN_HAND); MC.itemUseCooldown = 4;