├── .nojekyll ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── icon │ │ │ └── icon.png │ ├── pathseeker.mixins.json │ ├── fabric.mod.json │ └── pathseeker-addon.accesswidener │ └── java │ └── dev │ └── journey │ └── PathSeeker │ ├── utils │ ├── Type.java │ ├── BaritoneHelper.java │ ├── Update │ │ ├── UserConfig.java │ │ ├── UpdateScreen.java │ │ └── UpdateChecker.java │ ├── ApiHandler.java │ ├── ShulkerInfo.java │ └── PathSeekerUtil.java │ ├── events │ ├── OffGroundSpeedEvent.java │ └── ScreenRenderEvent.java │ ├── mixin │ ├── accessor │ │ └── ClientConnectionAccessor.java │ ├── PlayerEntityMixin.java │ ├── LivingEntityMixin.java │ ├── EntityMixin.java │ └── MixinHandledScreen.java │ ├── commands │ ├── MeteorFolderCommand.java │ ├── ScreenshotFolderCommand.java │ ├── CoordinateConverterCommand.java │ └── Stats2b2t.java │ ├── modules │ ├── utility │ │ ├── GrimDuraFirework.java │ │ ├── StackedMinecartsDetector.java │ │ ├── Firework.java │ │ └── Pitch40Util.java │ ├── render │ │ ├── OldChunkNotifier.java │ │ ├── InventoryInfoModule.java │ │ ├── PotESP.java │ │ ├── VanityESP.java │ │ ├── MobGearESP.java │ │ ├── EntityClusterESP.java │ │ └── DroppedItemESP.java │ └── automation │ │ ├── ElytraSwap.java │ │ ├── AreaLoader.java │ │ ├── TridentAura.java │ │ ├── AFKVanillaFly.java │ │ ├── TridentDupe.java │ │ └── AutoPortal.java │ └── PathSeeker.java ├── settings.gradle ├── .gitignore ├── gradle.properties ├── .github └── workflows │ └── dev_build.yml ├── gradlew.bat ├── gradlew ├── README.md └── LICENSE /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FaxHack/PathSeeker/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FaxHack/PathSeeker/HEAD/src/main/resources/assets/icon/icon.png -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/Type.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils; 2 | 3 | public enum Type { 4 | COMPACT, 5 | FULL 6 | } 7 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/events/OffGroundSpeedEvent.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.events; 2 | 3 | public class OffGroundSpeedEvent { 4 | public static final OffGroundSpeedEvent INSTANCE = new OffGroundSpeedEvent(); 5 | 6 | public float speed; 7 | 8 | public static OffGroundSpeedEvent get(float speed) { 9 | INSTANCE.speed = speed; 10 | return INSTANCE; 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/resources/pathseeker.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "dev.journey.PathSeeker.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "client": [ 6 | "MixinHandledScreen" 7 | ], 8 | "injectors": { 9 | "defaultRequire": 1 10 | }, 11 | "mixins": [ 12 | "EntityMixin", 13 | "LivingEntityMixin", 14 | "PlayerEntityMixin", 15 | "accessor.ClientConnectionAccessor" 16 | ] 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | src/main/java/dev/journey/PathSeeker/modules/automation/ChestIndex.java 35 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/mixin/accessor/ClientConnectionAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.mixin.accessor; 2 | 3 | import io.netty.channel.Channel; 4 | import net.minecraft.network.ClientConnection; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ClientConnection.class) 9 | public interface ClientConnectionAccessor { 10 | @Accessor 11 | Channel getChannel(); 12 | } 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2G 2 | 3 | # Fabric (https://fabricmc.net/versions.html) 4 | minecraft_version=1.21.1 5 | yarn_mappings=1.21.1+build.3 6 | loader_version=0.16.9 7 | 8 | # Mod Properties 9 | mod_version=1.0.8-1.21.1 10 | maven_group=dev.journey 11 | archives_base_name=1path-seeker 12 | 13 | # Xearos 14 | xaeroplus_version=2.26.3+fabric-1.21.1 15 | xaeros_worldmap_version=1.39.4_Fabric_1.21 16 | xaeros_minimap_version=25.1.0_Fabric_1.21 17 | 18 | # Baritone (https://maven.meteordev.org) 19 | baritone_version=1.21.1-SNAPSHOT 20 | 21 | # Meteor (https://maven.meteordev.org/) 22 | meteor_version=0.5.9-SNAPSHOT -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/events/ScreenRenderEvent.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.events; 2 | 3 | import meteordevelopment.meteorclient.utils.Utils; 4 | import net.minecraft.client.gui.DrawContext; 5 | 6 | public class ScreenRenderEvent { 7 | private static final ScreenRenderEvent INSTANCE = new ScreenRenderEvent(); 8 | 9 | public DrawContext drawContext; 10 | public double frameTime; 11 | public float tickDelta; 12 | 13 | public static ScreenRenderEvent get(DrawContext drawContext, float tickDelta) { 14 | INSTANCE.drawContext = drawContext; 15 | INSTANCE.frameTime = Utils.frameTime; 16 | INSTANCE.tickDelta = tickDelta; 17 | return INSTANCE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/BaritoneHelper.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils; 2 | 3 | import baritone.api.BaritoneAPI; 4 | import baritone.api.pathing.goals.GoalBlock; 5 | import baritone.api.utils.Helper; 6 | 7 | public class BaritoneHelper { 8 | 9 | public static void walkTo(int x, int y, int z) { 10 | BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalBlock(x, y, z)); 11 | } 12 | 13 | public static void cancelPath() { 14 | BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior().cancelEverything(); 15 | } 16 | 17 | public static void log(String message) { 18 | Helper.HELPER.logDirect(message); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "pathseeker-addon", 4 | "version": "1.0.8+mc${mc_version}", 5 | "name": "PathSeeker", 6 | "description": "PathSeeker Inspired by Trouser Streak", 7 | "authors": [ 8 | "2b2tJourney" 9 | ], 10 | "contact": { 11 | "repo": "https://github.com/FaxHack/PathSeeker" 12 | }, 13 | "icon": "assets/icon/icon.png", 14 | "environment": "client", 15 | "entrypoints": { 16 | "meteor": [ 17 | "dev.journey.PathSeeker.PathSeeker" 18 | ] 19 | }, 20 | "accessWidener": "pathseeker-addon.accesswidener", 21 | "mixins": [ 22 | "pathseeker.mixins.json" 23 | ], 24 | "custom": { 25 | "meteor-client:color": "255,0,255" 26 | }, 27 | "depends": { 28 | "java": ">=21", 29 | "minecraft": ">=${mc_version}", 30 | "meteor-client": "*" 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/mixin/PlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.mixin; 2 | 3 | import dev.journey.PathSeeker.events.OffGroundSpeedEvent; 4 | import meteordevelopment.meteorclient.MeteorClient; 5 | import net.minecraft.entity.player.PlayerEntity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | 11 | @Mixin(PlayerEntity.class) 12 | public class PlayerEntityMixin { 13 | @Inject(method = "getOffGroundSpeed", at = @At("RETURN"), cancellable = true) 14 | private void onGetOffGroundSpeed(CallbackInfoReturnable cir) { 15 | cir.setReturnValue(MeteorClient.EVENT_BUS.post(OffGroundSpeedEvent.get(cir.getReturnValueF())).speed); 16 | } 17 | } -------------------------------------------------------------------------------- /.github/workflows/dev_build.yml: -------------------------------------------------------------------------------- 1 | name: Publish Development Build 2 | on: 3 | push: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | permissions: write-all 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout Repository 12 | uses: actions/checkout@v4 13 | - name: Set up Java 14 | uses: actions/setup-java@v4 15 | with: 16 | java-version: 21 17 | distribution: adopt 18 | - name: Build with Gradle 19 | run: ./gradlew build 20 | - name: Grab Release Tag 21 | id: branch_tag 22 | run: | 23 | echo "tag=$(echo v${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV 24 | - name: Release 25 | uses: marvinpinto/action-automatic-releases@latest 26 | with: 27 | repo_token: '${{ secrets.GITHUB_TOKEN }}' 28 | automatic_release_tag: '${{ env.tag }}' 29 | prerelease: true 30 | title: 'Dev Build for ${{ env.tag }}' 31 | files: | 32 | ./build/libs/*.jar 33 | -------------------------------------------------------------------------------- /src/main/resources/pathseeker-addon.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | 3 | accessible class net/minecraft/world/chunk/PalettedContainer$Data 4 | accessible field net/minecraft/world/chunk/PalettedContainer data Lnet/minecraft/world/chunk/PalettedContainer$Data; 5 | accessible method net/minecraft/world/chunk/PalettedContainer$Data palette ()Lnet/minecraft/world/chunk/Palette; 6 | accessible method net/minecraft/client/network/ClientPlayerEntity getPermissionLevel ()I 7 | accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractType 8 | accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractAtHandler 9 | accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler 10 | accessible field net/minecraft/block/spawner/MobSpawnerLogic spawnDelay I 11 | accessible field net/minecraft/block/spawner/MobSpawnerLogic spawnEntry Lnet/minecraft/block/spawner/MobSpawnerEntry; 12 | accessible field net/minecraft/block/entity/DecoratedPotBlockEntity stack Lnet/minecraft/item/ItemStack; 13 | accessible field net/minecraft/entity/LivingEntity jumpingCooldown I -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/Update/UserConfig.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils.Update; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.util.Properties; 7 | 8 | public class UserConfig { 9 | private static final String CONFIG_FILE = "pathseeker_config.properties"; 10 | private static final Properties properties = new Properties(); 11 | 12 | static { 13 | try (FileInputStream fis = new FileInputStream(CONFIG_FILE)) { 14 | properties.load(fis); 15 | } catch (IOException e) { 16 | // If the file doesn't exist, it will be created when saving. 17 | } 18 | } 19 | 20 | public static boolean isUpdateCheckDisabled() { 21 | return Boolean.parseBoolean(properties.getProperty("updateCheckDisabled", "false")); 22 | } 23 | 24 | public static void setUpdateCheckDisabled(boolean disabled) { 25 | properties.setProperty("updateCheckDisabled", Boolean.toString(disabled)); 26 | try (FileOutputStream fos = new FileOutputStream(CONFIG_FILE)) { 27 | properties.store(fos, "Pathseeker Addon Config"); 28 | } catch (IOException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/ApiHandler.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | 5 | import java.net.URI; 6 | import java.net.http.HttpClient; 7 | import java.net.http.HttpRequest; 8 | import java.net.http.HttpResponse; 9 | import java.time.Duration; 10 | 11 | public class ApiHandler { 12 | public static final String API_2B2T_URL = "https://api.2b2t.vc"; 13 | private static final HttpClient client = HttpClient.newBuilder() 14 | .connectTimeout(Duration.ofSeconds(10)) 15 | .build(); 16 | 17 | public String fetchResponse(String url) { 18 | try { 19 | HttpRequest request = HttpRequest.newBuilder() 20 | .uri(URI.create(url)) 21 | .header("User-Agent", "PathSeeker/1.0") 22 | .GET() 23 | .timeout(Duration.ofSeconds(10)) 24 | .build(); 25 | 26 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 27 | 28 | if (response.statusCode() == 204) { 29 | return "204 Undocumented"; 30 | } 31 | 32 | return response.body(); 33 | } catch (Exception e) { 34 | PathSeeker.LOG.error("[ApiHandler] Failed to fetch response: {}", e.getMessage()); 35 | return null; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/commands/MeteorFolderCommand.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import meteordevelopment.meteorclient.commands.Command; 5 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.command.CommandSource; 8 | import net.minecraft.text.Text; 9 | 10 | import java.awt.*; 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | public class MeteorFolderCommand extends Command { 15 | public MeteorFolderCommand() { 16 | super("meteorfolder", "Opens the meteor folder."); 17 | } 18 | 19 | @Override 20 | public void build(LiteralArgumentBuilder builder) { 21 | builder.executes(context -> { 22 | { 23 | ChatUtils.sendMsg(Text.of("Opening meteor folder.")); 24 | } 25 | File meteor = new File(MinecraftClient.getInstance().runDirectory, "meteor-client"); 26 | if (Desktop.isDesktopSupported()) { 27 | try { 28 | Desktop.getDesktop().open(meteor); 29 | } catch (IOException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } else { 33 | // Fallback or error handling 34 | ChatUtils.sendMsg(Text.of("Desktop API not supported on this system.")); 35 | } 36 | return SINGLE_SUCCESS; 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/commands/ScreenshotFolderCommand.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.commands; 2 | 3 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 4 | import meteordevelopment.meteorclient.commands.Command; 5 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.command.CommandSource; 8 | import net.minecraft.text.Text; 9 | 10 | import java.awt.*; 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | public class ScreenshotFolderCommand extends Command { 15 | public ScreenshotFolderCommand() { 16 | super("screenshotfolder", "Opens the screenshot folder."); 17 | } 18 | 19 | @Override 20 | public void build(LiteralArgumentBuilder builder) { 21 | builder.executes(context -> { 22 | { 23 | ChatUtils.sendMsg(Text.of("Opening screenshot folder.")); 24 | } 25 | File screenshots = new File(MinecraftClient.getInstance().runDirectory, "screenshots"); 26 | if (Desktop.isDesktopSupported()) { 27 | try { 28 | Desktop.getDesktop().open(screenshots); 29 | } catch (IOException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } else { 33 | // Fallback or error handling 34 | ChatUtils.sendMsg(Text.of("Desktop API not supported on this system.")); 35 | } 36 | return SINGLE_SUCCESS; 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/mixin/LivingEntityMixin.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.mixin; 2 | 3 | import dev.journey.PathSeeker.modules.utility.ElytraFlyPlusPlus; 4 | import meteordevelopment.meteorclient.systems.modules.Modules; 5 | import net.minecraft.entity.LivingEntity; 6 | import net.minecraft.entity.ai.brain.Brain; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | import static meteordevelopment.meteorclient.MeteorClient.mc; 15 | 16 | @Mixin(LivingEntity.class) 17 | public abstract class LivingEntityMixin { 18 | ElytraFlyPlusPlus efly = Modules.get().get(ElytraFlyPlusPlus.class); 19 | @Shadow 20 | private int jumpingCooldown; 21 | 22 | @Shadow 23 | public abstract Brain getBrain(); 24 | 25 | @Inject(at = @At("HEAD"), method = "Lnet/minecraft/entity/LivingEntity;tickMovement()V") 26 | private void tickMovement(CallbackInfo ci) { 27 | if (mc.player != null && mc.player.getBrain().equals(this.getBrain()) && efly != null && efly.enabled()) { 28 | this.jumpingCooldown = 0; 29 | } 30 | } 31 | 32 | @Inject(at = @At("HEAD"), method = "Lnet/minecraft/entity/LivingEntity;isFallFlying()Z", cancellable = true) 33 | private void isFallFlying(CallbackInfoReturnable cir) { 34 | if (mc.player != null && mc.player.getBrain().equals(this.getBrain()) && efly != null && efly.enabled()) { 35 | cir.setReturnValue(true); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/mixin/EntityMixin.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.mixin; 2 | 3 | import dev.journey.PathSeeker.modules.utility.ElytraFlyPlusPlus; 4 | import meteordevelopment.meteorclient.systems.modules.Modules; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.entity.EntityPose; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | import java.util.UUID; 15 | 16 | import static meteordevelopment.meteorclient.MeteorClient.mc; 17 | 18 | @Mixin(Entity.class) 19 | public class EntityMixin { 20 | @Shadow 21 | protected UUID uuid; 22 | 23 | ElytraFlyPlusPlus efly = Modules.get().get(ElytraFlyPlusPlus.class); 24 | 25 | @Inject(at = @At("HEAD"), method = "Lnet/minecraft/entity/Entity;getPose()Lnet/minecraft/entity/EntityPose;", cancellable = true) 26 | private void getPose(CallbackInfoReturnable cir) { 27 | if (efly != null && efly.enabled() && this.uuid == mc.player.getUuid()) { 28 | cir.setReturnValue(EntityPose.STANDING); 29 | } 30 | } 31 | 32 | @Inject(at = @At("HEAD"), method = "Lnet/minecraft/entity/Entity;isSprinting()Z", cancellable = true) 33 | private void isSprinting(CallbackInfoReturnable cir) { 34 | if (efly != null && efly.enabled() && this.uuid == mc.player.getUuid()) { 35 | cir.setReturnValue(true); 36 | } 37 | } 38 | 39 | @Inject(at = @At("HEAD"), method = "pushAwayFrom", cancellable = true) 40 | private void pushAwayFrom(Entity entity, CallbackInfo ci) { 41 | if (mc.player != null && this.uuid == mc.player.getUuid() && efly != null && efly.enabled() && !entity.getUuid().equals(this.uuid)) { 42 | ci.cancel(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/mixin/MixinHandledScreen.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.mixin; 2 | 3 | import dev.journey.PathSeeker.events.ScreenRenderEvent; 4 | import dev.journey.PathSeeker.modules.render.InventoryInfoModule; 5 | import meteordevelopment.meteorclient.MeteorClient; 6 | import meteordevelopment.meteorclient.systems.modules.Modules; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.minecraft.client.gui.DrawContext; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import net.minecraft.client.gui.screen.ingame.HandledScreen; 11 | import net.minecraft.text.Text; 12 | import net.minecraft.util.math.Vec2f; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 18 | 19 | @Mixin(HandledScreen.class) 20 | public class MixinHandledScreen extends Screen { 21 | protected MixinHandledScreen(Text title) { 22 | super(title); 23 | } 24 | 25 | @Inject(method = "drawMouseoverTooltip", at = @At("HEAD")) 26 | private void drawMouseoverTooltipHook(DrawContext context, int x, int y, CallbackInfo ci) { 27 | MeteorClient.EVENT_BUS.post(ScreenRenderEvent.get(context, MinecraftClient.getInstance().getRenderTickCounter().getTickDelta(true))); 28 | } 29 | 30 | @Inject(method = "mouseClicked", at = @At("HEAD")) 31 | private void click(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { 32 | if (button != 0) return; 33 | InventoryInfoModule m = Modules.get().get(InventoryInfoModule.class); 34 | if (!m.isActive()) return; 35 | Modules.get().get(InventoryInfoModule.class).setClicked(new Vec2f((float) mouseX, (float) mouseY)); 36 | } 37 | 38 | @Override 39 | public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { 40 | InventoryInfoModule m = Modules.get().get(InventoryInfoModule.class); 41 | if (!m.isActive() && verticalAmount == 0) 42 | return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); 43 | m.setOffset((int) (m.getOffset() + Math.ceil(verticalAmount) * 18)); 44 | return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/Update/UpdateScreen.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils.Update; 2 | 3 | import net.minecraft.client.gui.DrawContext; 4 | import net.minecraft.client.gui.screen.Screen; 5 | import net.minecraft.client.gui.widget.ButtonWidget; 6 | import net.minecraft.text.Text; 7 | 8 | import java.awt.*; 9 | import java.net.URI; 10 | 11 | public class UpdateScreen extends Screen { 12 | private final String latestVersion; 13 | 14 | public UpdateScreen(String latestVersion) { 15 | super(Text.literal("Update Available")); 16 | this.latestVersion = latestVersion; 17 | } 18 | 19 | @Override 20 | protected void init() { 21 | int centerX = this.width / 2; 22 | int centerY = this.height / 2; 23 | 24 | // "Update Now" button 25 | this.addDrawableChild(ButtonWidget.builder(Text.literal("Update Now"), button -> { 26 | openURL("https://github.com/FaxHack/PathSeeker/releases"); 27 | this.close(); 28 | }) 29 | .dimensions(centerX - 100, centerY + 20, 200, 20) 30 | .build()); 31 | 32 | // "Remind Me Later" button 33 | this.addDrawableChild(ButtonWidget.builder(Text.literal("Remind Me Later"), button -> 34 | this.close()) 35 | .dimensions(centerX - 100, centerY + 45, 200, 20) 36 | .build()); 37 | 38 | // "Don't Show Again" button 39 | this.addDrawableChild(ButtonWidget.builder(Text.literal("Don't Show Again"), button -> { 40 | UserConfig.setUpdateCheckDisabled(true); 41 | this.close(); 42 | }) 43 | .dimensions(centerX - 100, centerY + 70, 200, 20) 44 | .build()); 45 | } 46 | 47 | @Override 48 | public void render(DrawContext context, int mouseX, int mouseY, float delta) { 49 | super.renderBackground(context, mouseX, mouseY, delta); 50 | 51 | context.drawCenteredTextWithShadow( 52 | this.textRenderer, 53 | Text.literal("A new update (" + latestVersion + ") is available!"), 54 | this.width / 2, 55 | this.height / 2 - 40, 56 | 0xFFFFFF 57 | ); 58 | 59 | super.render(context, mouseX, mouseY, delta); 60 | } 61 | 62 | private void openURL(String urlString) { 63 | try { 64 | Desktop.getDesktop().browse(new URI(urlString)); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/utility/GrimDuraFirework.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.utility; 2 | 3 | /* 4 | * Ported from https://github.com/miles352/meteor-mod/blob/master/src/main/java/com/example/addon/modules/GrimDuraFirework.java 5 | */ 6 | 7 | import dev.journey.PathSeeker.PathSeeker; 8 | import meteordevelopment.meteorclient.events.entity.player.InteractItemEvent; 9 | import meteordevelopment.meteorclient.events.world.TickEvent; 10 | import meteordevelopment.meteorclient.settings.IntSetting; 11 | import meteordevelopment.meteorclient.settings.Setting; 12 | import meteordevelopment.meteorclient.settings.SettingGroup; 13 | import meteordevelopment.meteorclient.systems.modules.Module; 14 | import meteordevelopment.meteorclient.utils.player.InvUtils; 15 | import meteordevelopment.orbit.EventHandler; 16 | import net.minecraft.item.FireworkRocketItem; 17 | import net.minecraft.item.ItemStack; 18 | import net.minecraft.util.ActionResult; 19 | import net.minecraft.util.Hand; 20 | 21 | import static dev.journey.PathSeeker.utils.PathSeekerUtil.firework; 22 | 23 | 24 | public class GrimDuraFirework extends Module { 25 | 26 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 27 | 28 | private final Setting fireworkDelay = sgGeneral.add(new IntSetting.Builder() 29 | .name("Firework Tick Delay") 30 | .description("The delay between possible firework uses.") 31 | .defaultValue(10) 32 | .build() 33 | ); 34 | int fireworkTickDelay = 0; 35 | int elytraSwapSlot = -1; 36 | boolean currentlyFiring = false; 37 | 38 | public GrimDuraFirework() { 39 | super(PathSeeker.Utility, "GrimFirework", "Swaps to your elytra so fireworks actually work."); 40 | } 41 | 42 | @EventHandler 43 | private void onTick(TickEvent.Post event) { 44 | if (fireworkTickDelay > 0) fireworkTickDelay--; 45 | if (elytraSwapSlot != -1) { 46 | InvUtils.swap(elytraSwapSlot, true); 47 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 48 | InvUtils.swapBack(); 49 | elytraSwapSlot = -1; 50 | } 51 | } 52 | 53 | // currentlyFiring is true when a firework is being fired by firework() 54 | 55 | @EventHandler 56 | private void onInteractItem(InteractItemEvent event) { 57 | ItemStack itemStack = mc.player.getStackInHand(event.hand); 58 | 59 | if (itemStack.getItem() instanceof FireworkRocketItem) { 60 | if (!currentlyFiring && fireworkTickDelay <= 0) { 61 | currentlyFiring = true; 62 | int result = firework(mc); 63 | if (result != 200 && result != -1) { 64 | elytraSwapSlot = result; 65 | } 66 | } 67 | // let the firework through if its a valid one 68 | else if (currentlyFiring) { 69 | currentlyFiring = false; 70 | fireworkTickDelay = fireworkDelay.get(); 71 | return; 72 | } 73 | event.toReturn = ActionResult.PASS; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/commands/CoordinateConverterCommand.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.commands; 2 | 3 | import com.mojang.brigadier.arguments.IntegerArgumentType; 4 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 5 | import meteordevelopment.meteorclient.commands.Command; 6 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 7 | import net.minecraft.command.CommandSource; 8 | 9 | import java.awt.*; 10 | import java.awt.datatransfer.StringSelection; 11 | 12 | public class CoordinateConverterCommand extends Command { 13 | public CoordinateConverterCommand() { 14 | super("coords", "Converts coordinates between Overworld and Nether."); 15 | } 16 | 17 | @Override 18 | public void build(LiteralArgumentBuilder builder) { 19 | builder.then(literal("overworld") 20 | .then(argument("x", IntegerArgumentType.integer()) 21 | .then(argument("y", IntegerArgumentType.integer()) 22 | .then(argument("z", IntegerArgumentType.integer()) 23 | .executes(context -> { 24 | int x = context.getArgument("x", Integer.class); 25 | int y = context.getArgument("y", Integer.class); 26 | int z = context.getArgument("z", Integer.class); 27 | 28 | int netherX = x / 8; 29 | int netherZ = z / 8; 30 | 31 | String result = String.format("Overworld: %d %d %d -> Nether: %d %d %d", x, y, z, netherX, y, netherZ); 32 | ChatUtils.info(result); 33 | copyToClipboard(result); 34 | return SINGLE_SUCCESS; 35 | }))))); 36 | 37 | builder.then(literal("nether") 38 | .then(argument("x", IntegerArgumentType.integer()) 39 | .then(argument("y", IntegerArgumentType.integer()) 40 | .then(argument("z", IntegerArgumentType.integer()) 41 | .executes(context -> { 42 | int x = context.getArgument("x", Integer.class); 43 | int y = context.getArgument("y", Integer.class); 44 | int z = context.getArgument("z", Integer.class); 45 | 46 | int overworldX = x * 8; 47 | int overworldZ = z * 8; 48 | 49 | String result = String.format("Nether: %d %d %d -> Overworld: %d %d %d", x, y, z, overworldX, y, overworldZ); 50 | ChatUtils.info(result); 51 | copyToClipboard(result); 52 | return SINGLE_SUCCESS; 53 | }))))); 54 | } 55 | 56 | private void copyToClipboard(String text) { 57 | Toolkit.getDefaultToolkit() 58 | .getSystemClipboard() 59 | .setContents(new StringSelection(text), null); 60 | ChatUtils.info("Coordinates copied to clipboard!"); 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/OldChunkNotifier.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.render; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.settings.Setting; 5 | import meteordevelopment.meteorclient.settings.SettingGroup; 6 | import meteordevelopment.meteorclient.settings.StringSetting; 7 | import meteordevelopment.meteorclient.systems.modules.Module; 8 | import xaeroplus.XaeroPlus; 9 | import xaeroplus.event.ChunkDataEvent; 10 | import xaeroplus.module.ModuleManager; 11 | import xaeroplus.module.impl.OldChunks; 12 | import xaeroplus.module.impl.PaletteNewChunks; 13 | 14 | import static dev.journey.PathSeeker.utils.PathSeekerUtil.sendWebhook; 15 | 16 | 17 | public class OldChunkNotifier extends Module { 18 | 19 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 20 | 21 | public final Setting webhookLink = sgGeneral.add(new StringSetting.Builder() 22 | .name("Webhook Link") 23 | .description("A discord webhook link. Looks like this: https://discord.com/api/webhooks/webhookUserId/webHookTokenOrSomething") 24 | .defaultValue("") 25 | .build() 26 | ); 27 | 28 | public final Setting discordId = sgGeneral.add(new StringSetting.Builder() 29 | .name("Discord ID") 30 | .description("Your discord ID") 31 | .defaultValue("") 32 | .build() 33 | ); 34 | 35 | public OldChunkNotifier() { 36 | super(PathSeeker.Render, "OldChunkNotifier", "Pings you on discord if you find old chunks"); 37 | } 38 | 39 | @Override 40 | public void onActivate() { 41 | XaeroPlus.EVENT_BUS.register(this); 42 | } 43 | 44 | @Override 45 | public void onDeactivate() { 46 | XaeroPlus.EVENT_BUS.unregister(this); 47 | } 48 | 49 | @net.lenni0451.lambdaevents.EventHandler(priority = -1) 50 | public void onChunkData(ChunkDataEvent event) { 51 | // avoid 2b2t end loading screen 52 | assert mc.player != null; 53 | if (mc.player.getAbilities().allowFlying) return; 54 | 55 | if (webhookLink.get().isEmpty() || discordId.get().isEmpty()) return; 56 | boolean is119NewChunk = ModuleManager.getModule(PaletteNewChunks.class) 57 | .isNewChunk( 58 | event.chunk().getPos().x, 59 | event.chunk().getPos().z, 60 | event.chunk().getWorld().getRegistryKey() 61 | ); 62 | 63 | boolean is112OldChunk = ModuleManager.getModule(OldChunks.class) 64 | .isOldChunk( 65 | event.chunk().getPos().x, 66 | event.chunk().getPos().z, 67 | event.chunk().getWorld().getRegistryKey() 68 | ); 69 | 70 | if (is119NewChunk && !is112OldChunk) return; 71 | 72 | String message; 73 | 74 | if (is112OldChunk && !is119NewChunk) { 75 | message = "1.12 Followed in 1.19+ Old Chunk Detected"; 76 | } else if (is112OldChunk) { 77 | message = "1.12 Unfollowed in 1.19+ Old Chunk Detected"; 78 | } else { 79 | message = "1.19+ Old Chunk Detected"; 80 | } 81 | String finalMessage = message; 82 | new Thread(() -> sendWebhook(webhookLink.get(), "Old Chunk Detected", finalMessage + " at " + mc.player.getPos().toString(), discordId.get(), mc.player.getGameProfile().getName())).start(); 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/automation/ElytraSwap.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.automation; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.settings.BoolSetting; 5 | import meteordevelopment.meteorclient.settings.Setting; 6 | import meteordevelopment.meteorclient.settings.SettingGroup; 7 | import meteordevelopment.meteorclient.systems.config.Config; 8 | import meteordevelopment.meteorclient.systems.modules.Module; 9 | import meteordevelopment.meteorclient.utils.player.InvUtils; 10 | import net.minecraft.entity.EquipmentSlot; 11 | import net.minecraft.item.ArmorItem; 12 | import net.minecraft.item.Item; 13 | import net.minecraft.item.Items; 14 | import net.minecraft.network.packet.c2s.play.CloseHandledScreenC2SPacket; 15 | 16 | public class ElytraSwap extends Module { 17 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 18 | 19 | private final Setting stayOn = sgGeneral.add(new BoolSetting.Builder() 20 | .name("stay-on") 21 | .description("Stays on and activates when you turn it off.") 22 | .defaultValue(false) 23 | .build() 24 | ); 25 | 26 | private final Setting closeInventory = sgGeneral.add(new BoolSetting.Builder() 27 | .name("close-inventory") 28 | .description("Sends inventory close after swap.") 29 | .defaultValue(false) 30 | .build() 31 | ); 32 | 33 | public ElytraSwap() { 34 | super(PathSeeker.Automation, "ElytraSwap", "Automatically swaps between a chestplate and an elytra."); 35 | } 36 | 37 | @Override 38 | public void onActivate() { 39 | swap(); 40 | if (!stayOn.get()) toggle(); 41 | } 42 | 43 | @Override 44 | public void onDeactivate() { 45 | if (stayOn.get()) swap(); 46 | } 47 | 48 | public void swap() { 49 | Item currentItem = mc.player.getEquippedStack(EquipmentSlot.CHEST).getItem(); 50 | 51 | if (currentItem == Items.ELYTRA) { 52 | equipChestplate(); 53 | } else if (currentItem instanceof ArmorItem && ((ArmorItem) currentItem).getSlotType() == EquipmentSlot.CHEST) { 54 | equipElytra(); 55 | } else { 56 | if (!equipChestplate()) equipElytra(); 57 | } 58 | } 59 | 60 | private boolean equipChestplate() { 61 | int bestSlot = -1; 62 | 63 | for (int i = 0; i < mc.player.getInventory().main.size(); i++) { 64 | Item item = mc.player.getInventory().main.get(i).getItem(); 65 | 66 | // Check if the item is a chestplate 67 | if (item instanceof ArmorItem && ((ArmorItem) item).getSlotType() == EquipmentSlot.CHEST) { 68 | bestSlot = i; 69 | break; // Stop as soon as we find a chestplate 70 | } 71 | } 72 | 73 | if (bestSlot != -1) equip(bestSlot); 74 | return bestSlot != -1; 75 | } 76 | 77 | private void equipElytra() { 78 | for (int i = 0; i < mc.player.getInventory().main.size(); i++) { 79 | Item item = mc.player.getInventory().main.get(i).getItem(); 80 | 81 | if (item == Items.ELYTRA) { 82 | equip(i); 83 | break; 84 | } 85 | } 86 | } 87 | 88 | private void equip(int slot) { 89 | InvUtils.move().from(slot).toArmor(2); 90 | if (closeInventory.get()) { 91 | // Notchian clients send a Close Window packet with Window ID 0 to close their inventory even though there is never an Open Screen packet for the inventory. 92 | mc.getNetworkHandler().sendPacket(new CloseHandledScreenC2SPacket(0)); 93 | } 94 | } 95 | 96 | @Override 97 | public void sendToggledMsg() { 98 | if (stayOn.get()) super.sendToggledMsg(); 99 | else if (Config.get().chatFeedback.get() && chatFeedback) info("Triggered (highlight)%s(default).", title); 100 | } 101 | 102 | public enum Chestplate { 103 | Diamond, 104 | Netherite 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/ShulkerInfo.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils; 2 | 3 | import dev.journey.PathSeeker.modules.render.InventoryInfoModule; 4 | import meteordevelopment.meteorclient.systems.modules.Modules; 5 | import net.minecraft.block.ShulkerBoxBlock; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.component.DataComponentTypes; 8 | import net.minecraft.component.type.ContainerComponent; 9 | import net.minecraft.item.BlockItem; 10 | import net.minecraft.item.Item; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.nbt.NbtCompound; 13 | import net.minecraft.nbt.NbtList; 14 | import net.minecraft.registry.RegistryWrapper; 15 | import net.minecraft.util.collection.DefaultedList; 16 | 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | public record ShulkerInfo(String name, Type type, int color, int slot, List stacks) { 22 | 23 | public static ShulkerInfo create(ItemStack stack, int slot) { 24 | if (!(stack.getItem() instanceof BlockItem) || !(((BlockItem) stack.getItem()).getBlock() instanceof ShulkerBoxBlock block)) 25 | return null; 26 | List items = DefaultedList.ofSize(27, ItemStack.EMPTY); 27 | Type type = Type.COMPACT; 28 | 29 | if (!Modules.get().get(InventoryInfoModule.class).compact.get()) type = Type.FULL; 30 | 31 | // Try the new data component system first, fallback to NBT if needed 32 | ContainerComponent container = stack.get(DataComponentTypes.CONTAINER); 33 | if (container != null) { 34 | // Use the new data component system 35 | items.clear(); 36 | int index = 0; 37 | for (ItemStack item : container.iterateNonEmpty()) { 38 | if (index < 27) { 39 | items.set(index, item); 40 | index++; 41 | } 42 | } 43 | } else { 44 | // Fallback to NBT system for backward compatibility 45 | NbtCompound nbt = stack.getComponents().getOrDefault(DataComponentTypes.CUSTOM_DATA, net.minecraft.component.type.NbtComponent.DEFAULT).copyNbt(); 46 | 47 | if (nbt.contains("BlockEntityTag", 10)) { 48 | nbt = nbt.getCompound("BlockEntityTag"); 49 | } 50 | 51 | if (nbt.contains("Items", 9)) { 52 | Item unstackable = null; 53 | NbtList nbt2 = nbt.getList("Items", 10); 54 | RegistryWrapper.WrapperLookup wrapperLookup = MinecraftClient.getInstance().world.getRegistryManager(); 55 | for (int i = 0; i < nbt2.size(); i++) { 56 | int slot2 = nbt2.getCompound(i).contains("Slot", 99) ? nbt2.getCompound(i).getByte("Slot") : i; 57 | ItemStack item = ItemStack.fromNbt(wrapperLookup, nbt2.getCompound(i)).orElse(ItemStack.EMPTY); 58 | items.set(slot2, item); 59 | if (item.getMaxCount() == 1) { 60 | if (unstackable != null && !item.getItem().equals(unstackable)) type = Type.FULL; 61 | unstackable = item.getItem(); 62 | } 63 | } 64 | } 65 | } 66 | 67 | if (type == Type.COMPACT) { 68 | Map map = new HashMap<>(); 69 | for (ItemStack item : items) { 70 | if (item.isEmpty()) continue; 71 | map.compute(item.getItem(), (k, v) -> { 72 | if (v == null) return item.getCount(); 73 | return v + item.getCount(); 74 | }); 75 | } 76 | items.clear(); 77 | int k = 0; 78 | for (Map.Entry entry : map.entrySet()) { 79 | items.set(k, new ItemStack(entry.getKey(), entry.getValue())); 80 | k++; 81 | } 82 | } 83 | 84 | int color = -1; 85 | if (block.getColor() != null) { 86 | // Use the new color system in 1.21.1 87 | net.minecraft.util.DyeColor dyeColor = block.getColor(); 88 | int colorValue = dyeColor.getMapColor().color; 89 | color = colorValue; 90 | } 91 | 92 | return new ShulkerInfo(stack.getName().getString(), type, color, slot, items); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/automation/AreaLoader.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.automation; 2 | 3 | import baritone.api.BaritoneAPI; 4 | import baritone.api.process.IElytraProcess; 5 | import dev.journey.PathSeeker.PathSeeker; 6 | import meteordevelopment.meteorclient.events.world.TickEvent; 7 | import meteordevelopment.meteorclient.settings.IntSetting; 8 | import meteordevelopment.meteorclient.settings.Setting; 9 | import meteordevelopment.meteorclient.settings.SettingGroup; 10 | import meteordevelopment.meteorclient.systems.modules.Module; 11 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 12 | import meteordevelopment.orbit.EventHandler; 13 | import net.minecraft.registry.entry.RegistryEntry; 14 | import net.minecraft.text.Text; 15 | import net.minecraft.util.math.BlockPos; 16 | import net.minecraft.world.dimension.DimensionType; 17 | 18 | 19 | public class AreaLoader extends Module { 20 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 21 | private final Setting gapDistance = sgGeneral.add(new IntSetting.Builder() 22 | .name("gap-distance") 23 | .description("Gap in chunks between each line of the spiral.") 24 | .defaultValue(16) 25 | .min(5) 26 | .sliderMax(256) 27 | .build() 28 | ); 29 | private final Setting flightLevel = sgGeneral.add(new IntSetting.Builder() 30 | .name("flight-level") 31 | .description("What y-level to (roughly) fly at. It will still navigate around terrain as necessary.") 32 | .defaultValue(180) 33 | .min(80) 34 | .sliderMax(255) 35 | .build() 36 | ); 37 | private final int[][] multipliers = { 38 | {0, 1}, {1, 0}, {0, -1}, {-1, 0} 39 | }; 40 | private IElytraProcess efly; 41 | private int sideDistance = 0; 42 | private int multiIndex = 0; 43 | private BlockPos lastDest = null; 44 | private RegistryEntry dimension; 45 | private boolean dimensionChanged = false; 46 | 47 | public AreaLoader() { 48 | super(PathSeeker.Hunting, "area-loader", "Spiral out from your position to load chunks in an area"); 49 | } 50 | 51 | private void reset() { 52 | multiIndex = 0; 53 | sideDistance = 16 * gapDistance.get(); 54 | efly = BaritoneAPI.getProvider().getPrimaryBaritone().getElytraProcess(); 55 | } 56 | 57 | @EventHandler 58 | public void onTick(TickEvent.Post event) { 59 | if (efly == null || !efly.isLoaded()) { 60 | this.toggle(); 61 | return; 62 | } 63 | 64 | if (mc.world.getDimensionEntry() != dimension) { 65 | if (dimensionChanged) return; 66 | 67 | dimensionChanged = true; 68 | ChatUtils.sendMsg("area-loader", Text.literal("Dimension changed, pausing.")); 69 | return; 70 | } else if (dimensionChanged) { 71 | dimensionChanged = false; 72 | ChatUtils.sendMsg("area-loader", Text.literal("Resuming.")); 73 | efly.pathTo(lastDest); 74 | } 75 | 76 | var pos = mc.player.getBlockPos(); 77 | var dest = efly.currentDestination(); 78 | if (dest == null) return; 79 | 80 | var deltaX = Math.abs(pos.getX() - dest.getX()); 81 | var deltaY = Math.abs(pos.getZ() - dest.getZ()); 82 | 83 | if (deltaX < 60 && deltaY < 60) { 84 | lastDest = getNextDestination(dest); 85 | efly.pathTo(lastDest); 86 | } 87 | } 88 | 89 | private BlockPos getNextDestination(BlockPos start) { 90 | var multi = multipliers[multiIndex]; 91 | multiIndex = (multiIndex + 1) % multipliers.length; 92 | 93 | var out = new BlockPos( 94 | start.getX() + (multi[0] * sideDistance), 95 | flightLevel.get(), 96 | start.getZ() + (multi[1] * sideDistance) 97 | ); 98 | 99 | sideDistance += gapDistance.get() * 16; 100 | 101 | return out; 102 | } 103 | 104 | @Override 105 | public void onDeactivate() { 106 | if (efly == null || !efly.isActive()) return; 107 | BaritoneAPI.getProvider().getPrimaryBaritone().getCommandManager().execute("stop"); 108 | } 109 | 110 | 111 | @Override 112 | public void onActivate() { 113 | this.reset(); 114 | dimension = mc.world.getDimensionEntry(); 115 | lastDest = getNextDestination(mc.player.getBlockPos()); 116 | efly.pathTo(lastDest); 117 | } 118 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/PathSeeker.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker; 2 | 3 | import dev.journey.PathSeeker.commands.CoordinateConverterCommand; 4 | import dev.journey.PathSeeker.commands.MeteorFolderCommand; 5 | import dev.journey.PathSeeker.commands.ScreenshotFolderCommand; 6 | import dev.journey.PathSeeker.commands.Stats2b2t; 7 | import dev.journey.PathSeeker.modules.automation.*; 8 | import dev.journey.PathSeeker.modules.exploration.BaseFinder; 9 | import dev.journey.PathSeeker.modules.exploration.BetterStashFinder; 10 | import dev.journey.PathSeeker.modules.exploration.NewerNewChunks; 11 | import dev.journey.PathSeeker.modules.exploration.TrailFollower; 12 | import dev.journey.PathSeeker.modules.render.*; 13 | import dev.journey.PathSeeker.modules.utility.*; 14 | import meteordevelopment.meteorclient.addons.MeteorAddon; 15 | import meteordevelopment.meteorclient.commands.Commands; 16 | import meteordevelopment.meteorclient.systems.modules.Category; 17 | import meteordevelopment.meteorclient.systems.modules.Modules; 18 | import net.fabricmc.loader.api.FabricLoader; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import static meteordevelopment.meteorclient.MeteorClient.EVENT_BUS; 23 | 24 | 25 | public class PathSeeker extends MeteorAddon { 26 | public static final Logger LOG = LoggerFactory.getLogger(PathSeeker.class); 27 | //public static final Category Main = new Category("PathSeeker"); 28 | public static final Category Hunting = new Category("PathHunting"); 29 | public static final Category Utility = new Category("PathUtils"); 30 | public static final Category Automation = new Category("PathAutomation"); 31 | public static final Category Render = new Category("PathRender"); 32 | 33 | @Override 34 | public void onInitialize() { 35 | LOG.info("Initializing Path-Seeker!"); 36 | 37 | EVENT_BUS.subscribe(this); 38 | 39 | //Hunting 40 | Modules.get().add(new ActivatedSpawnerDetector()); 41 | Modules.get().add(new StackedMinecartsDetector()); 42 | Modules.get().add(new NewerNewChunks()); 43 | Modules.get().add(new BaseFinder()); 44 | 45 | //Utility 46 | Modules.get().add(new GrimDuraFirework()); 47 | Modules.get().add(new SignHistorian()); 48 | Modules.get().add(new Pitch40Util()); 49 | Modules.get().add(new ElytraFlyPlusPlus()); 50 | Modules.get().add(new InventoryInfoModule()); 51 | 52 | //Render 53 | Modules.get().add(new HoleAndTunnelAndStairsESP()); 54 | Modules.get().add(new PotESP()); 55 | Modules.get().add(new MobGearESP()); 56 | Modules.get().add(new DroppedItemESP()); 57 | Modules.get().add(new EntityClusterESP()); 58 | Modules.get().add(new VanityESP()); 59 | 60 | //Automation 61 | //Modules.get().add(new SpawnerMine()); 62 | Modules.get().add(new ElytraSwap()); 63 | Modules.get().add(new TridentDupe()); 64 | Modules.get().add(new Firework()); 65 | Modules.get().add(new AutoEnchant()); 66 | Modules.get().add(new AreaLoader()); 67 | Modules.get().add(new AFKVanillaFly()); 68 | Modules.get().add(new TridentAura()); 69 | Modules.get().add(new AutoPortal()); 70 | Modules.get().add(new AutoSmithingTable()); 71 | 72 | /* To Release 73 | 74 | Modules.get().add(new ChestIndex()); 75 | 76 | */ 77 | 78 | //Commands 79 | Commands.add(new CoordinateConverterCommand()); 80 | Commands.add(new MeteorFolderCommand()); 81 | Commands.add(new ScreenshotFolderCommand()); 82 | Commands.add(new Stats2b2t()); 83 | 84 | if (FabricLoader.getInstance().isModLoaded("xaeroworldmap") && FabricLoader.getInstance().isModLoaded("xaerominimap")) { 85 | 86 | Modules.get().add(new BetterStashFinder()); 87 | Modules.get().add(new OldChunkNotifier()); 88 | Modules.get().add(new TrailFollower()); 89 | 90 | } else { 91 | LOG.info("Xaeros minimap and world map not found, disabling modules that require it."); 92 | } 93 | } 94 | 95 | @Override 96 | public void onRegisterCategories() { 97 | Modules.registerCategory(Hunting); 98 | Modules.registerCategory(Automation); 99 | Modules.registerCategory(Render); 100 | Modules.registerCategory(Utility); 101 | } 102 | 103 | public String getPackage() { 104 | return "dev.journey.PathSeeker"; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/Update/UpdateChecker.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils.Update; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.world.TickEvent; 5 | import meteordevelopment.orbit.EventHandler; 6 | import net.minecraft.client.MinecraftClient; 7 | import org.json.JSONObject; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.InputStreamReader; 11 | import java.net.HttpURLConnection; 12 | import java.net.URI; 13 | import java.net.URL; 14 | import java.util.concurrent.CompletableFuture; 15 | import java.util.stream.Collectors; 16 | 17 | import static meteordevelopment.meteorclient.MeteorClient.EVENT_BUS; 18 | 19 | public class UpdateChecker { 20 | private static final String GITHUB_URL = "https://api.github.com/repos/FaxHack/PathSeeker/releases/latest"; 21 | private static final String CURRENT_VERSION = "1.0.7"; 22 | private static final String USER_AGENT = "Mozilla/5.0"; 23 | private static boolean hasCheckedThisSession = false; 24 | private static boolean isRegistered = false; 25 | 26 | public static void checkForUpdate() { 27 | if (UserConfig.isUpdateCheckDisabled()) return; 28 | 29 | // Register the tick handler if not already registered 30 | if (!isRegistered) { 31 | EVENT_BUS.subscribe(UpdateChecker.class); 32 | isRegistered = true; 33 | } 34 | 35 | // Also do an immediate check 36 | doUpdateCheck(); 37 | } 38 | 39 | @EventHandler 40 | private static void onClientTick(TickEvent.Post event) { 41 | // Check for updates once when the client is fully loaded and player is in-game 42 | MinecraftClient mc = MinecraftClient.getInstance(); 43 | if (!hasCheckedThisSession && mc.world != null && mc.player != null) { 44 | doUpdateCheck(); 45 | hasCheckedThisSession = true; 46 | } 47 | } 48 | 49 | private static void doUpdateCheck() { 50 | CompletableFuture.runAsync(() -> { 51 | try { 52 | String latestVersion = fetchLatestVersion(); 53 | if (latestVersion != null && isNewerVersion(latestVersion)) { 54 | MinecraftClient.getInstance().execute(() -> 55 | MinecraftClient.getInstance().setScreen(new UpdateScreen(latestVersion)) 56 | ); 57 | } 58 | } catch (Exception e) { 59 | PathSeeker.LOG.error("Failed to check for updates", e); 60 | } 61 | }); 62 | } 63 | 64 | private static String fetchLatestVersion() { 65 | try { 66 | URL url = new URI(GITHUB_URL).toURL(); 67 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 68 | conn.setRequestMethod("GET"); 69 | conn.setRequestProperty("User-Agent", USER_AGENT); 70 | conn.setConnectTimeout(5000); 71 | conn.setReadTimeout(5000); 72 | 73 | if (conn.getResponseCode() != 200) { 74 | PathSeeker.LOG.error("Failed to check for updates: HTTP {}", conn.getResponseCode()); 75 | return null; 76 | } 77 | 78 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { 79 | String response = reader.lines().collect(Collectors.joining()); 80 | JSONObject json = new JSONObject(response); 81 | return json.getString("tag_name"); 82 | } 83 | } catch (Exception e) { 84 | PathSeeker.LOG.error("Failed to fetch latest version", e); 85 | return null; 86 | } 87 | } 88 | 89 | private static boolean isNewerVersion(String latest) { 90 | try { 91 | String[] latestParts = latest.replaceAll("[^0-9.]", "").split("\\."); 92 | String[] currentParts = UpdateChecker.CURRENT_VERSION.replaceAll("[^0-9.]", "").split("\\."); 93 | 94 | int length = Math.max(latestParts.length, currentParts.length); 95 | for (int i = 0; i < length; i++) { 96 | int l = i < latestParts.length ? Integer.parseInt(latestParts[i]) : 0; 97 | int c = i < currentParts.length ? Integer.parseInt(currentParts[i]) : 0; 98 | if (l > c) return true; 99 | if (l < c) return false; 100 | } 101 | return false; 102 | } catch (NumberFormatException e) { 103 | PathSeeker.LOG.error("Failed to parse version numbers", e); 104 | return false; 105 | } 106 | } 107 | 108 | // Reset the check status when game exits 109 | public static void resetCheckedStatus() { 110 | hasCheckedThisSession = false; 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/InventoryInfoModule.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.render; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import dev.journey.PathSeeker.events.ScreenRenderEvent; 5 | import dev.journey.PathSeeker.utils.ShulkerInfo; 6 | import dev.journey.PathSeeker.utils.Type; 7 | import meteordevelopment.meteorclient.events.world.TickEvent; 8 | import meteordevelopment.meteorclient.settings.BoolSetting; 9 | import meteordevelopment.meteorclient.settings.Setting; 10 | import meteordevelopment.meteorclient.settings.SettingGroup; 11 | import meteordevelopment.meteorclient.systems.modules.Module; 12 | import meteordevelopment.orbit.EventHandler; 13 | import net.minecraft.client.gui.screen.ingame.HandledScreen; 14 | import net.minecraft.item.ItemStack; 15 | import net.minecraft.screen.slot.Slot; 16 | import net.minecraft.screen.slot.SlotActionType; 17 | import net.minecraft.util.math.MathHelper; 18 | import net.minecraft.util.math.Vec2f; 19 | 20 | import java.awt.*; 21 | import java.util.ArrayDeque; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Queue; 25 | 26 | 27 | public class InventoryInfoModule extends Module { 28 | private static final Color BACKGROUND = new Color(0, 0, 0, 75); 29 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 30 | 31 | public final Setting compact = sgGeneral.add(new BoolSetting.Builder() 32 | .name("Compact") 33 | .defaultValue(true) 34 | .build() 35 | ); 36 | 37 | private final List info = new ArrayList<>(); 38 | private final Queue renderQueue = new ArrayDeque<>(); 39 | private int height, offset; 40 | private Vec2f clicked; 41 | 42 | public InventoryInfoModule() { 43 | super(PathSeeker.Utility, "inventory-info", "prigozhinplugg"); 44 | } 45 | 46 | @EventHandler 47 | private void onTick(TickEvent.Post event) { 48 | if (!(mc.currentScreen instanceof HandledScreen) || mc.player.age % 4 != 0) return; 49 | refresh((HandledScreen) mc.currentScreen); 50 | } 51 | 52 | @EventHandler 53 | private void onRenderScreen(ScreenRenderEvent event) { 54 | int y = 3 + offset; 55 | for (ShulkerInfo shulkerInfo : info) { 56 | int count = 0, x = 2, startY = y, maxX = 22; 57 | for (ItemStack stack : shulkerInfo.stacks()) { 58 | if (shulkerInfo.type() == Type.COMPACT && stack.isEmpty()) break; 59 | if (count > 0 && count % 9 == 0) { 60 | x = 2; 61 | y += 18; 62 | } 63 | int finalX = x, finalY = y; 64 | renderQueue.add(() -> { 65 | event.drawContext.drawItem(stack, finalX + 2, finalY); 66 | if (stack.getCount() > 999) { 67 | event.drawContext.drawItemInSlot(mc.textRenderer, stack, finalX + 2, finalY, "%.1fk".formatted(stack.getCount() / 1000f)); 68 | } else { 69 | event.drawContext.drawItemInSlot(mc.textRenderer, stack, finalX + 2, finalY); 70 | } 71 | }); 72 | x += 20; 73 | count++; 74 | if (x > maxX) maxX = x; 75 | } 76 | y += 18; 77 | if (clicked != null 78 | && clicked.x >= 2 && clicked.x <= maxX 79 | && clicked.y >= startY && clicked.y <= y) { 80 | renderQueue.add(() -> { 81 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, shulkerInfo.slot(), 0, SlotActionType.PICKUP, mc.player); 82 | }); 83 | setClicked(null); 84 | } 85 | event.drawContext.fill(2, startY, maxX, y, BACKGROUND.hashCode()); 86 | event.drawContext.fill(2, startY - 1, maxX, startY, shulkerInfo.color()); 87 | y += 2; 88 | } 89 | 90 | while (!renderQueue.isEmpty()) renderQueue.poll().run(); 91 | 92 | height = y - offset; 93 | setClicked(null); 94 | } 95 | 96 | private void refresh(HandledScreen screen) { 97 | info.clear(); 98 | for (Slot slot : screen.getScreenHandler().slots) { 99 | ShulkerInfo shulkerInfo = ShulkerInfo.create(slot.getStack(), slot.id); 100 | if (shulkerInfo == null) continue; 101 | info.add(shulkerInfo); 102 | } 103 | } 104 | 105 | public int getOffset() { 106 | return offset; 107 | } 108 | 109 | public void setOffset(int offset) { 110 | this.offset = MathHelper.clamp(offset, -Math.max(height - mc.getWindow().getScaledHeight(), 0), 0); 111 | } 112 | 113 | public void setClicked(Vec2f clicked) { 114 | this.clicked = clicked; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/commands/Stats2b2t.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.commands; 2 | 3 | import com.google.gson.Gson; 4 | import com.mojang.brigadier.arguments.StringArgumentType; 5 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 6 | import dev.journey.PathSeeker.PathSeeker; 7 | import dev.journey.PathSeeker.utils.ApiHandler; 8 | import dev.journey.PathSeeker.utils.PathSeekerUtil; 9 | import meteordevelopment.meteorclient.commands.Command; 10 | import meteordevelopment.meteorclient.utils.network.MeteorExecutor; 11 | import net.minecraft.client.network.ClientPlayerEntity; 12 | import net.minecraft.command.CommandSource; 13 | import net.minecraft.text.Text; 14 | 15 | public class Stats2b2t extends Command { 16 | private static final String API_ENDPOINT = "/stats/player?playerName="; 17 | private final PathSeekerUtil pathSeekerUtil; 18 | 19 | public Stats2b2t() { 20 | super("stats", "Fetch stats for a 2b2t player from api.2b2t.vc.", "2b2tstats"); 21 | this.pathSeekerUtil = new PathSeekerUtil(); 22 | } 23 | 24 | @Override 25 | public void build(LiteralArgumentBuilder builder) { 26 | builder.then(argument("player", StringArgumentType.word()).executes(ctx -> { 27 | MeteorExecutor.execute(() -> { 28 | ClientPlayerEntity player = mc.player; 29 | 30 | if (player == null) return; 31 | 32 | String playerString = ctx.getArgument("player", String.class); 33 | String requestString = ApiHandler.API_2B2T_URL + API_ENDPOINT + playerString.trim(); 34 | String response = new ApiHandler().fetchResponse(requestString); 35 | 36 | if (response == null) return; 37 | 38 | if (response.equals("204 Undocumented")) { 39 | player.sendMessage( 40 | Text.of( 41 | "§8<" + PathSeekerUtil.randomColorCode() + "§o✨" + "§r§8> §4§oPlayer not found§7..." 42 | ) 43 | ); 44 | } else { 45 | try { 46 | Gson gson = new Gson(); 47 | String colorCode = PathSeekerUtil.randomColorCode(); 48 | PlayerStats stats = gson.fromJson(response, PlayerStats.class); 49 | 50 | pathSeekerUtil.updateTimeInfo( 51 | stats.lastSeen, 52 | stats.firstSeen, 53 | stats.playtimeSeconds 54 | ); 55 | 56 | String formattedFirstSeen = pathSeekerUtil.getFormattedFirstSeen(); 57 | String formattedLastSeen = pathSeekerUtil.getFormattedLastSeen(); 58 | String formattedPlaytime = pathSeekerUtil.getFormattedPlaytime(); 59 | String formattedPlaytimeInMonth = PathSeekerUtil.formatPlaytime(stats.playtimeSecondsMonth); 60 | 61 | String kdRatio = String.format("%.2f", (float) stats.killCount / Math.max(1, stats.deathCount)); 62 | 63 | player.sendMessage( 64 | Text.of( 65 | "§8<" + colorCode + "§o✨" + "§r§8> §7§oStats for " + colorCode + "§o" + playerString + "§7§o:\n" + 66 | " §7Joins: " + colorCode + "§o" + stats.joinCount + "\n" + 67 | " §7Leaves: " + colorCode + "§o" + stats.leaveCount + "\n" + 68 | " §7K/D Ratio: " + colorCode + "§o" + kdRatio + "\n" + 69 | " §7Chats: " + colorCode + "§o" + stats.chatsCount + "\n" + 70 | " §7Prio: " + colorCode + "§o" + stats.prio + "\n" + 71 | " §7First Seen: " + colorCode + "§o" + formattedFirstSeen + "\n" + 72 | " §7Last Seen: " + colorCode + "§o" + formattedLastSeen + "\n" + 73 | " §7Playtime: " + colorCode + "§o" + formattedPlaytime + "\n" + 74 | " §7Playtime in last month: " + colorCode + "§o" + formattedPlaytimeInMonth 75 | ) 76 | ); 77 | } catch (Exception err) { 78 | PathSeeker.LOG.error("[Stats2b2t] Failed to deserialize JSON: {}", err.getMessage()); 79 | error("§7Failed to deserialize response from the server§4..!"); 80 | } 81 | } 82 | }); 83 | return SINGLE_SUCCESS; 84 | })); 85 | } 86 | 87 | private record PlayerStats( 88 | int joinCount, 89 | int leaveCount, 90 | int deathCount, 91 | int killCount, 92 | String firstSeen, 93 | String lastSeen, 94 | long playtimeSeconds, 95 | long playtimeSecondsMonth, 96 | int chatsCount, 97 | boolean prio 98 | ) { 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/automation/TridentAura.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.automation; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.world.TickEvent; 5 | import meteordevelopment.meteorclient.settings.BoolSetting; 6 | import meteordevelopment.meteorclient.settings.DoubleSetting; 7 | import meteordevelopment.meteorclient.settings.Setting; 8 | import meteordevelopment.meteorclient.settings.SettingGroup; 9 | import meteordevelopment.meteorclient.systems.modules.Module; 10 | import meteordevelopment.meteorclient.utils.player.InvUtils; 11 | import meteordevelopment.orbit.EventHandler; 12 | import net.minecraft.entity.Entity; 13 | import net.minecraft.entity.LivingEntity; 14 | import net.minecraft.entity.mob.HostileEntity; 15 | import net.minecraft.item.ItemStack; 16 | import net.minecraft.item.Items; 17 | import net.minecraft.util.Hand; 18 | 19 | 20 | public class TridentAura extends Module { 21 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 22 | 23 | private final Setting onlyHostile = sgGeneral.add(new BoolSetting.Builder() 24 | .name("Hostile Only Debug") 25 | .description("only targets hostile mobs.") 26 | .defaultValue(true) 27 | .build() 28 | ); 29 | 30 | private final Setting range = sgGeneral.add(new DoubleSetting.Builder() 31 | .name("Range") 32 | .description("max range") 33 | .defaultValue(50) 34 | .min(1) 35 | .sliderMax(100) 36 | .build() 37 | ); 38 | private final boolean justSwitched = false; 39 | private Entity currentTarget = null; 40 | private int cooldownTicks = 0; 41 | 42 | public TridentAura() { 43 | super(PathSeeker.Automation, "Trident Aura", "For trident spam"); 44 | } 45 | 46 | @Override 47 | public void onDeactivate() { 48 | currentTarget = null; 49 | } 50 | 51 | @EventHandler 52 | private void onTick(TickEvent.Pre event) { 53 | if (mc.player == null || mc.world == null) return; 54 | 55 | if (!mc.options.useKey.isPressed()) { 56 | currentTarget = null; 57 | cooldownTicks = 0; 58 | return; 59 | } 60 | 61 | if (cooldownTicks > 0) { 62 | cooldownTicks--; 63 | return; 64 | } 65 | 66 | 67 | double closestDistance = range.get(); 68 | currentTarget = null; 69 | 70 | for (Entity entity : mc.world.getEntities()) { 71 | if (!(entity instanceof LivingEntity)) continue; 72 | if (entity == mc.player) continue; 73 | if (onlyHostile.get() && !(entity instanceof HostileEntity)) continue; 74 | 75 | double distance = mc.player.distanceTo(entity); 76 | if (distance < closestDistance) { 77 | closestDistance = distance; 78 | currentTarget = entity; 79 | } 80 | } 81 | 82 | if (currentTarget == null) return; 83 | 84 | // aimbot math 85 | double dx = currentTarget.getX() - mc.player.getX(); 86 | double dz = currentTarget.getZ() - mc.player.getZ(); 87 | double dy = (currentTarget.getY() + currentTarget.getHeight() / 2.0) 88 | - (mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose())); 89 | double horizontalDist = Math.sqrt(dx * dx + dz * dz); 90 | double dropComp = 0.05 * Math.pow(horizontalDist / 2.5, 2); 91 | dy += dropComp; 92 | 93 | float targetYaw = (float) Math.toDegrees(Math.atan2(dz, dx)) - 90f; 94 | float targetPitch = (float) -Math.toDegrees(Math.atan2(dy, horizontalDist)); 95 | float currentYaw = mc.player.getYaw(); 96 | float currentPitch = mc.player.getPitch(); 97 | 98 | float smoothYaw = currentYaw + (targetYaw - currentYaw) * 0.3f; 99 | float smoothPitch = currentPitch + (targetPitch - currentPitch) * 0.3f; 100 | 101 | mc.player.setYaw(smoothYaw); 102 | mc.player.setPitch(smoothPitch); 103 | 104 | ItemStack stack = mc.player.getMainHandStack(); 105 | if (!stack.isOf(Items.TRIDENT)) { 106 | 107 | for (int i = 0; i < 9; i++) { 108 | ItemStack item = mc.player.getInventory().getStack(i); 109 | if (item.isOf(Items.TRIDENT)) { 110 | mc.player.getInventory().selectedSlot = i; 111 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 112 | return; 113 | } 114 | } 115 | // pull inventory 116 | for (int i = 9; i < mc.player.getInventory().size(); i++) { 117 | ItemStack invStack = mc.player.getInventory().getStack(i); 118 | if (invStack.isOf(Items.TRIDENT)) { 119 | for (int j = 0; j < 9; j++) { 120 | if (mc.player.getInventory().getStack(j).isEmpty()) { 121 | InvUtils.move().from(i).to(j); 122 | mc.player.getInventory().selectedSlot = j; 123 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 124 | return; 125 | } 126 | } 127 | } 128 | } 129 | return; 130 | } 131 | 132 | if (!mc.player.isUsingItem()) { 133 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 134 | } else if (mc.player.getItemUseTime() >= 10) { 135 | mc.interactionManager.stopUsingItem(mc.player); 136 | cooldownTicks = 2; 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/utility/StackedMinecartsDetector.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.utility; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 5 | import meteordevelopment.meteorclient.events.world.TickEvent; 6 | import meteordevelopment.meteorclient.renderer.ShapeMode; 7 | import meteordevelopment.meteorclient.settings.*; 8 | import meteordevelopment.meteorclient.systems.modules.Module; 9 | import meteordevelopment.meteorclient.utils.render.RenderUtils; 10 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 11 | import meteordevelopment.orbit.EventHandler; 12 | import net.minecraft.entity.Entity; 13 | import net.minecraft.entity.vehicle.AbstractMinecartEntity; 14 | import net.minecraft.util.math.BlockPos; 15 | import net.minecraft.util.math.Box; 16 | import net.minecraft.util.math.Vec3d; 17 | 18 | import java.util.*; 19 | 20 | public class StackedMinecartsDetector extends Module { 21 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 22 | private final SettingGroup sgRender = settings.createGroup("Render"); 23 | 24 | // General Settings 25 | private final Setting detectionRadius = sgGeneral.add(new IntSetting.Builder() 26 | .name("detection-radius") 27 | .description("Maximum distance between minecarts to consider them stacked") 28 | .defaultValue(1) 29 | .min(0) 30 | .sliderMax(5) 31 | .build() 32 | ); 33 | 34 | private final Setting minStackSize = sgGeneral.add(new IntSetting.Builder() 35 | .name("minimum-stack-size") 36 | .description("Minimum number of minecarts needed to trigger detection") 37 | .defaultValue(2) 38 | .min(2) 39 | .sliderRange(2, 10) 40 | .build() 41 | ); 42 | 43 | // Render Settings 44 | private final Setting render = sgRender.add(new BoolSetting.Builder() 45 | .name("render") 46 | .description("Renders a box around stacked minecarts") 47 | .defaultValue(true) 48 | .build() 49 | ); 50 | 51 | private final Setting shapeMode = sgRender.add(new EnumSetting.Builder() 52 | .name("shape-mode") 53 | .description("How the shapes are rendered") 54 | .defaultValue(ShapeMode.Both) 55 | .build() 56 | ); 57 | 58 | private final Setting boxColor = sgRender.add(new ColorSetting.Builder() 59 | .name("box-color") 60 | .description("Color of the rendered box") 61 | .defaultValue(new SettingColor(255, 0, 0, 100)) 62 | .build() 63 | ); 64 | 65 | private final Setting lineColor = sgRender.add(new ColorSetting.Builder() 66 | .name("line-color") 67 | .description("Color of the rendered lines") 68 | .defaultValue(new SettingColor(255, 0, 0, 255)) 69 | .build() 70 | ); 71 | 72 | private final Setting tracers = sgRender.add(new BoolSetting.Builder() 73 | .name("tracers") 74 | .description("Render tracers to stacked minecarts") 75 | .defaultValue(true) 76 | .build() 77 | ); 78 | 79 | private final Map stackedPositions = new HashMap<>(); 80 | private final Set toRemove = new HashSet<>(); 81 | 82 | public StackedMinecartsDetector() { 83 | super(PathSeeker.Utility, "stacked-minecarts", "Detects stacked minecarts in the world."); 84 | } 85 | 86 | @EventHandler 87 | private void onTick(TickEvent.Post event) { 88 | if (mc.world == null) return; 89 | 90 | stackedPositions.clear(); 91 | Map> minecartMap = new HashMap<>(); 92 | 93 | // Group minecarts by their block position 94 | for (Entity entity : mc.world.getEntities()) { 95 | if (entity instanceof AbstractMinecartEntity minecart) { 96 | BlockPos pos = minecart.getBlockPos(); 97 | minecartMap.computeIfAbsent(pos, k -> new ArrayList<>()).add(minecart); 98 | } 99 | } 100 | 101 | // Check for stacks 102 | for (Map.Entry> entry : minecartMap.entrySet()) { 103 | if (entry.getValue().size() >= minStackSize.get()) { 104 | stackedPositions.put(entry.getKey(), entry.getValue().size()); 105 | } 106 | } 107 | } 108 | 109 | @EventHandler 110 | private void onRender(Render3DEvent event) { 111 | if (!render.get()) return; 112 | 113 | for (Map.Entry entry : stackedPositions.entrySet()) { 114 | BlockPos pos = entry.getKey(); 115 | int count = entry.getValue(); 116 | 117 | Box box = new Box( 118 | pos.getX() + 0.1, pos.getY(), pos.getZ() + 0.1, 119 | pos.getX() + 0.9, pos.getY() + (0.2 * count), pos.getZ() + 0.9 120 | ); 121 | 122 | // Render box 123 | event.renderer.box( 124 | box.minX, box.minY, box.minZ, 125 | box.maxX, box.maxY, box.maxZ, 126 | boxColor.get(), lineColor.get(), 127 | shapeMode.get(), 128 | 0 129 | ); 130 | 131 | // Render tracers 132 | if (tracers.get()) { 133 | Vec3d center = RenderUtils.center; 134 | Vec3d minecartPos = new Vec3d(pos.getX() + 0.5, pos.getY() + 0.1, pos.getZ() + 0.5); 135 | event.renderer.line(center.x, center.y, center.z, 136 | minecartPos.x, minecartPos.y, minecartPos.z, 137 | lineColor.get() 138 | ); 139 | } 140 | } 141 | } 142 | 143 | public Map getStackedPositions() { 144 | return Collections.unmodifiableMap(stackedPositions); 145 | } 146 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/automation/AFKVanillaFly.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.automation; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import dev.journey.PathSeeker.utils.PathSeekerUtil; 5 | import meteordevelopment.meteorclient.events.world.TickEvent; 6 | import meteordevelopment.meteorclient.settings.BoolSetting; 7 | import meteordevelopment.meteorclient.settings.IntSetting; 8 | import meteordevelopment.meteorclient.settings.Setting; 9 | import meteordevelopment.meteorclient.settings.SettingGroup; 10 | import meteordevelopment.meteorclient.systems.modules.Module; 11 | import meteordevelopment.meteorclient.utils.player.FindItemResult; 12 | import meteordevelopment.meteorclient.utils.player.InvUtils; 13 | import meteordevelopment.orbit.EventHandler; 14 | import net.minecraft.item.Items; 15 | 16 | 17 | public class AFKVanillaFly extends Module { 18 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 19 | private final Setting fireworkDelay = sgGeneral.add(new IntSetting.Builder() 20 | .name("Timed Delay (ms)") 21 | .description("How long to wait between fireworks when using Timed Delay.") 22 | .defaultValue(4000) 23 | .sliderRange(0, 10000) 24 | .build() 25 | ); 26 | private final Setting useManualY = sgGeneral.add(new BoolSetting.Builder() 27 | .name("Use Manual Y Level") 28 | .description("Use a manually set Y level instead of the Y level when activated.") 29 | .defaultValue(false) 30 | .build() 31 | ); 32 | private long lastRocketUse = 0; 33 | private boolean launched = false; 34 | private double yTarget = -1; 35 | private final Setting manualYLevel = sgGeneral.add(new IntSetting.Builder() 36 | .name("Manual Y Level") 37 | .description("The Y level to maintain when using manual Y level.") 38 | .defaultValue(256) 39 | .sliderRange(-64, 320) 40 | .visible(useManualY::get) 41 | .onChanged(val -> yTarget = val) 42 | .build() 43 | ); 44 | private float targetPitch = 0; 45 | 46 | public AFKVanillaFly() { 47 | super(PathSeeker.Automation, "AFKVanillaFly", "Maintains a level Y-flight with fireworks and smooth pitch control."); 48 | } 49 | 50 | @Override 51 | public void onActivate() { 52 | launched = false; 53 | yTarget = -1; 54 | 55 | if (mc.player == null || !mc.player.isFallFlying()) { 56 | info("You must be flying before enabling AFKVanillaFly."); 57 | } 58 | } 59 | 60 | public void tickFlyLogic() { 61 | if (mc.player == null) return; 62 | 63 | double currentY = mc.player.getY(); 64 | 65 | if (mc.player.isFallFlying()) { 66 | if (yTarget == -1 || !launched) { 67 | if (useManualY.get()) { 68 | yTarget = manualYLevel.get(); 69 | } else { 70 | yTarget = currentY; 71 | } 72 | launched = true; 73 | } 74 | 75 | // will prevent from flying straight down into the ground - adjust y range if player moves vertical 76 | // but only if not using manual Y level 77 | if (!useManualY.get()) { 78 | double yDiffFromLock = currentY - yTarget; 79 | if (Math.abs(yDiffFromLock) > 10.0) { 80 | yTarget = currentY; // reset the current y-level to maintain 81 | info("Y-lock reset due to altitude deviation."); 82 | } 83 | } 84 | 85 | double yDiff = currentY - yTarget; 86 | 87 | if (Math.abs(yDiff) > 10.0) { 88 | targetPitch = (float) (Math.atan2(yDiff, 100) * (180 / Math.PI)); 89 | } else if (yDiff > 2.0) { 90 | targetPitch = 10f; 91 | } else if (yDiff < -2.0) { 92 | targetPitch = -10f; 93 | } else { 94 | targetPitch = 0f; 95 | } 96 | 97 | float currentPitch = mc.player.getPitch(); 98 | float pitchDiff = targetPitch - currentPitch; 99 | mc.player.setPitch(currentPitch + pitchDiff * 0.1f); 100 | 101 | if (System.currentTimeMillis() - lastRocketUse > fireworkDelay.get()) { 102 | tryUseFirework(); 103 | } 104 | } else { 105 | if (!launched) { 106 | mc.player.jump(); 107 | launched = true; 108 | } else if (System.currentTimeMillis() - lastRocketUse > 1000) { 109 | tryUseFirework(); 110 | } 111 | yTarget = -1; 112 | } 113 | } 114 | 115 | public void resetYLock() { 116 | yTarget = -1; 117 | launched = false; 118 | } 119 | 120 | @EventHandler 121 | private void onTick(TickEvent.Pre event) { 122 | tickFlyLogic(); 123 | } 124 | 125 | private void tryUseFirework() { 126 | FindItemResult hotbar = InvUtils.findInHotbar(Items.FIREWORK_ROCKET); 127 | if (!hotbar.found()) { 128 | FindItemResult inv = InvUtils.find(Items.FIREWORK_ROCKET); 129 | if (inv.found()) { 130 | int hotbarSlot = findEmptyHotbarSlot(); 131 | if (hotbarSlot != -1) { 132 | InvUtils.move().from(inv.slot()).to(hotbarSlot); 133 | } else { 134 | info("No empty hotbar slot available to move fireworks."); 135 | return; 136 | } 137 | } else { 138 | info("No fireworks found in hotbar or inventory."); 139 | return; 140 | } 141 | } 142 | PathSeekerUtil.firework(mc, false); 143 | lastRocketUse = System.currentTimeMillis(); 144 | } 145 | 146 | private int findEmptyHotbarSlot() { 147 | for (int i = 0; i < 9; i++) { 148 | if (mc.player.getInventory().getStack(i).isEmpty()) return i; 149 | } 150 | return -1; 151 | } 152 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | app_path=$0 4 | 5 | # Need this for daisy-chained symlinks. 6 | while 7 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 8 | [ -h "$app_path" ] 9 | do 10 | ls=$( ls -ld "$app_path" ) 11 | link=${ls#*' -> '} 12 | case $link in #( 13 | /*) app_path=$link ;; #( 14 | *) app_path=$APP_HOME$link ;; 15 | esac 16 | done 17 | 18 | # This is normally unused 19 | # shellcheck disable=SC2034 20 | APP_BASE_NAME=${0##*/} 21 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 22 | 23 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 24 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 25 | 26 | # Use the maximum available, or set MAX_FD != -1 to use that value. 27 | MAX_FD=maximum 28 | 29 | warn () { 30 | echo "$*" 31 | } >&2 32 | 33 | die () { 34 | echo 35 | echo "$*" 36 | echo 37 | exit 1 38 | } >&2 39 | 40 | # OS specific support (must be 'true' or 'false'). 41 | cygwin=false 42 | msys=false 43 | darwin=false 44 | nonstop=false 45 | case "$( uname )" in #( 46 | CYGWIN* ) cygwin=true ;; #( 47 | Darwin* ) darwin=true ;; #( 48 | MSYS* | MINGW* ) msys=true ;; #( 49 | NONSTOP* ) nonstop=true ;; 50 | esac 51 | 52 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 53 | 54 | 55 | # Determine the Java command to use to start the JVM. 56 | if [ -n "$JAVA_HOME" ] ; then 57 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 58 | # IBM's JDK on AIX uses strange locations for the executables 59 | JAVACMD=$JAVA_HOME/jre/sh/java 60 | else 61 | JAVACMD=$JAVA_HOME/bin/java 62 | fi 63 | if [ ! -x "$JAVACMD" ] ; then 64 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 65 | 66 | Please set the JAVA_HOME variable in your environment to match the 67 | location of your Java installation." 68 | fi 69 | else 70 | JAVACMD=java 71 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 72 | 73 | Please set the JAVA_HOME variable in your environment to match the 74 | location of your Java installation." 75 | fi 76 | 77 | # Increase the maximum file descriptors if we can. 78 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 79 | case $MAX_FD in #( 80 | max*) 81 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 82 | # shellcheck disable=SC3045 83 | MAX_FD=$( ulimit -H -n ) || 84 | warn "Could not query maximum file descriptor limit" 85 | esac 86 | case $MAX_FD in #( 87 | '' | soft) :;; #( 88 | *) 89 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 90 | # shellcheck disable=SC3045 91 | ulimit -n "$MAX_FD" || 92 | warn "Could not set maximum file descriptor limit to $MAX_FD" 93 | esac 94 | fi 95 | 96 | # Collect all arguments for the java command, stacking in reverse order: 97 | # * args from the command line 98 | # * the main class name 99 | # * -classpath 100 | # * -D...appname settings 101 | # * --module-path (only if needed) 102 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 103 | 104 | # For Cygwin or MSYS, switch paths to Windows format before running java 105 | if "$cygwin" || "$msys" ; then 106 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 107 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 108 | 109 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 110 | 111 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 112 | for arg do 113 | if 114 | case $arg in #( 115 | -*) false ;; # don't mess with options #( 116 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 117 | [ -e "$t" ] ;; #( 118 | *) false ;; 119 | esac 120 | then 121 | arg=$( cygpath --path --ignore --mixed "$arg" ) 122 | fi 123 | # Roll the args list around exactly as many times as the number of 124 | # args, so each arg winds up back in the position where it started, but 125 | # possibly modified. 126 | # 127 | # NB: a `for` loop captures its iteration list before it begins, so 128 | # changing the positional parameters here affects neither the number of 129 | # iterations, nor the values presented in `arg`. 130 | shift # remove old arg 131 | set -- "$@" "$arg" # push replacement arg 132 | done 133 | fi 134 | 135 | # Collect all arguments for the java command; 136 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 137 | # shell script including quotes and variable substitutions, so put them in 138 | # double quotes to make sure that they get re-expanded; and 139 | # * put everything else in single quotes, so that it's not re-expanded. 140 | 141 | set -- \ 142 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 143 | -classpath "$CLASSPATH" \ 144 | org.gradle.wrapper.GradleWrapperMain \ 145 | "$@" 146 | 147 | # Stop when "xargs" is not available. 148 | if ! command -v xargs >/dev/null 2>&1 149 | then 150 | die "xargs is not available" 151 | fi 152 | 153 | # Use "xargs" to parse quoted args. 154 | # 155 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 156 | # 157 | # In Bash we could simply go: 158 | # 159 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 160 | # set -- "${ARGS[@]}" "$@" 161 | # 162 | # but POSIX shell has neither arrays nor command substitution, so instead we 163 | # post-process each arg (as a line of input to sed) to backslash-escape any 164 | # character that might be a shell metacharacter, then use eval to reverse 165 | # that process (while maintaining the separation between arguments), and wrap 166 | # the whole thing up as a single "set" statement. 167 | # 168 | # This will of course break if any of these variables contains a newline or 169 | # an unmatched quote. 170 | # 171 | 172 | eval "set -- $( 173 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 174 | xargs -n1 | 175 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 176 | tr '\n' ' ' 177 | )" '"$@"' 178 | 179 | exec "$JAVACMD" "$@" -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/utility/Firework.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.utility; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.world.TickEvent; 5 | import meteordevelopment.meteorclient.settings.BoolSetting; 6 | import meteordevelopment.meteorclient.settings.DoubleSetting; 7 | import meteordevelopment.meteorclient.settings.Setting; 8 | import meteordevelopment.meteorclient.settings.SettingGroup; 9 | import meteordevelopment.meteorclient.systems.modules.Module; 10 | import meteordevelopment.orbit.EventHandler; 11 | import net.minecraft.item.Items; 12 | import net.minecraft.util.Hand; 13 | import net.minecraft.util.math.MathHelper; 14 | import net.minecraft.util.math.Vec3d; 15 | 16 | public class Firework extends Module { 17 | private static final int BOOST_DURATION = 30; 18 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 19 | private final SettingGroup sgVelocity = settings.createGroup("Velocity"); 20 | private final Setting onlyWhenHoldingRocket = sgGeneral.add(new BoolSetting.Builder() 21 | .name("only-when-holding") 22 | .description("Only auto-fire when holding a rocket.") 23 | .defaultValue(true) 24 | .build() 25 | ); 26 | private final Setting checkCooldown = sgGeneral.add(new BoolSetting.Builder() 27 | .name("check-cooldown") 28 | .description("Check item use cooldown before firing.") 29 | .defaultValue(true) 30 | .build() 31 | ); 32 | private final Setting onlyWhenFlying = sgGeneral.add(new BoolSetting.Builder() 33 | .name("only-when-flying") 34 | .description("Only fire rockets when using elytra.") 35 | .defaultValue(true) 36 | .build() 37 | ); 38 | private final Setting useDelay = sgGeneral.add(new DoubleSetting.Builder() 39 | .name("use-delay") 40 | .description("Delay in seconds between using rockets.") 41 | .defaultValue(.1) 42 | .min(0.1) 43 | .sliderMax(5.0) 44 | .build() 45 | ); 46 | private final Setting minSpeed = sgVelocity.add(new DoubleSetting.Builder() 47 | .name("minimum-speed") 48 | .description("Minimum speed threshold before using rockets.") 49 | .defaultValue(1.35) 50 | .min(0.1) 51 | .sliderMax(2.0) 52 | .build() 53 | ); 54 | private boolean isBoostActive = false; 55 | private int boostTicks = 0; 56 | private Vec3d lastPos = Vec3d.ZERO; 57 | private int ticksSinceLastUse = 0; 58 | 59 | public Firework() { 60 | super(PathSeeker.Automation, "Firework", "Automatically fires rockets when boost ends."); 61 | } 62 | 63 | @EventHandler 64 | private void onTick(TickEvent.Post event) { 65 | if (mc.player == null) return; 66 | 67 | ticksSinceLastUse++; 68 | 69 | if (isBoostActive) { 70 | boostTicks++; 71 | if (boostTicks >= BOOST_DURATION) { 72 | isBoostActive = false; 73 | boostTicks = 0; 74 | } 75 | } 76 | 77 | if (onlyWhenFlying.get() && !mc.player.isFallFlying()) { 78 | isBoostActive = false; 79 | return; 80 | } 81 | 82 | Vec3d currentPos = mc.player.getPos(); 83 | Vec3d velocity = currentPos.subtract(lastPos); 84 | lastPos = currentPos; 85 | 86 | float yaw = mc.player.getYaw(); 87 | float pitch = mc.player.getPitch(); 88 | Vec3d lookVec = getLookVector(yaw, pitch); 89 | 90 | double forwardSpeed = velocity.dotProduct(lookVec); 91 | int delayTicks = (int) (useDelay.get() * 20); 92 | 93 | if (!isBoostActive && forwardSpeed < minSpeed.get() && ticksSinceLastUse >= delayTicks) { 94 | // Check if player is holding a rocket 95 | boolean holdingRocket = mc.player.getMainHandStack().getItem() == Items.FIREWORK_ROCKET || 96 | mc.player.getOffHandStack().getItem() == Items.FIREWORK_ROCKET; 97 | // moving fw inventory pull logic to new module 98 | if (!holdingRocket) { 99 | int slot = -1; 100 | for (int i = 0; i < 9; i++) { 101 | if (mc.player.getInventory().getStack(i).getItem() == Items.FIREWORK_ROCKET) { 102 | slot = i; 103 | break; 104 | } 105 | } 106 | if (slot == -1) { 107 | for (int i = 9; i < mc.player.getInventory().size(); i++) { 108 | if (mc.player.getInventory().getStack(i).getItem() == Items.FIREWORK_ROCKET) { 109 | for (int j = 0; j < 9; j++) { 110 | if (mc.player.getInventory().getStack(j).isEmpty()) { 111 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, i + 36, j, 112 | net.minecraft.screen.slot.SlotActionType.SWAP, mc.player); 113 | slot = j; 114 | break; 115 | } 116 | } 117 | break; 118 | } 119 | } 120 | } 121 | 122 | holdingRocket = mc.player.getMainHandStack().getItem() == Items.FIREWORK_ROCKET || 123 | mc.player.getOffHandStack().getItem() == Items.FIREWORK_ROCKET; 124 | if (!holdingRocket && onlyWhenHoldingRocket.get()) return; 125 | } 126 | if (checkCooldown.get() && mc.player.getItemCooldownManager().isCoolingDown(Items.FIREWORK_ROCKET)) return; 127 | 128 | Hand hand = mc.player.getMainHandStack().getItem() == Items.FIREWORK_ROCKET 129 | ? Hand.MAIN_HAND 130 | : Hand.OFF_HAND; 131 | 132 | mc.interactionManager.interactItem(mc.player, hand); 133 | mc.player.swingHand(hand); 134 | isBoostActive = true; 135 | boostTicks = 0; 136 | ticksSinceLastUse = 0; 137 | } 138 | } 139 | 140 | private Vec3d getLookVector(float yaw, float pitch) { 141 | float f = pitch * 0.017453292F; 142 | float g = -yaw * 0.017453292F; 143 | float h = MathHelper.cos(g); 144 | float i = MathHelper.sin(g); 145 | float j = MathHelper.cos(f); 146 | float k = MathHelper.sin(f); 147 | return new Vec3d(i * j, -k, h * j).normalize(); 148 | } 149 | 150 | @Override 151 | public void onActivate() { 152 | lastPos = mc.player != null ? mc.player.getPos() : Vec3d.ZERO; 153 | isBoostActive = false; 154 | boostTicks = 0; 155 | ticksSinceLastUse = 0; 156 | } 157 | 158 | @Override 159 | public void onDeactivate() { 160 | isBoostActive = false; 161 | boostTicks = 0; 162 | ticksSinceLastUse = 0; 163 | } 164 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/utility/Pitch40Util.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.utility; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.world.TickEvent; 5 | import meteordevelopment.meteorclient.settings.*; 6 | import meteordevelopment.meteorclient.systems.modules.Module; 7 | import meteordevelopment.meteorclient.systems.modules.Modules; 8 | import meteordevelopment.meteorclient.systems.modules.movement.elytrafly.ElytraFly; 9 | import meteordevelopment.meteorclient.utils.player.InvUtils; 10 | import meteordevelopment.orbit.EventHandler; 11 | import net.minecraft.entity.player.PlayerEntity; 12 | import net.minecraft.util.Hand; 13 | 14 | import java.util.Optional; 15 | 16 | import static dev.journey.PathSeeker.utils.PathSeekerUtil.firework; 17 | 18 | public class Pitch40Util extends Module { 19 | private static final double DEFAULT_BOUND_GAP = 60.0; 20 | private static final double DEFAULT_VELOCITY_THRESHOLD = -0.05; 21 | private static final int DEFAULT_FIREWORK_COOLDOWN = 300; 22 | private static final double LOWER_BOUND_OFFSET = 10.0; 23 | private static final double Y_POSITION_OFFSET = 5.0; 24 | private static final float UPWARD_PITCH = -40.0f; 25 | 26 | // Settings groups 27 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 28 | private final SettingGroup sgFirework = settings.createGroup("Firework"); 29 | 30 | // General settings 31 | private final Setting boundGap = sgGeneral.add(new DoubleSetting.Builder() 32 | .name("bound-gap") 33 | .description("The gap between the upper and lower bounds") 34 | .defaultValue(DEFAULT_BOUND_GAP) 35 | .min(10.0) 36 | .sliderMax(100.0) 37 | .build() 38 | ); 39 | 40 | // Firework settings 41 | private final Setting autoFirework = sgFirework.add(new BoolSetting.Builder() 42 | .name("auto-firework") 43 | .description("Automatically uses fireworks when velocity is too low") 44 | .defaultValue(true) 45 | .build() 46 | ); 47 | 48 | private final Setting velocityThreshold = sgFirework.add(new DoubleSetting.Builder() 49 | .name("velocity-threshold") 50 | .description("Minimum velocity required for firework activation") 51 | .defaultValue(DEFAULT_VELOCITY_THRESHOLD) 52 | .visible(autoFirework::get) 53 | .build() 54 | ); 55 | 56 | private final Setting fireworkCooldownTicks = sgFirework.add(new IntSetting.Builder() 57 | .name("cooldown-ticks") 58 | .description("Ticks to wait between firework uses") 59 | .defaultValue(DEFAULT_FIREWORK_COOLDOWN) 60 | .min(20) 61 | .sliderMax(600) 62 | .visible(autoFirework::get) 63 | .build() 64 | ); 65 | 66 | // Instance variables 67 | private final Module elytraFlyModule; 68 | private int fireworkCooldown; 69 | private boolean goingUp; 70 | private int elytraSwapSlot; 71 | 72 | public Pitch40Util() { 73 | super(PathSeeker.Utility, "Pitch40Util", "Maintains pitch 40 flight on 2b2t and dynamically adjusts bounds"); 74 | this.elytraFlyModule = Modules.get().get(ElytraFly.class); 75 | reset(); 76 | } 77 | 78 | private void reset() { 79 | fireworkCooldown = 0; 80 | goingUp = true; 81 | elytraSwapSlot = -1; 82 | } 83 | 84 | @Override 85 | public void onActivate() { 86 | reset(); 87 | } 88 | 89 | @Override 90 | public void onDeactivate() { 91 | if (elytraFlyModule != null && elytraFlyModule.isActive()) { 92 | elytraFlyModule.toggle(); 93 | } 94 | reset(); 95 | } 96 | 97 | private Optional> getElytraFlySetting(String name) { 98 | try { 99 | return Optional.ofNullable((Setting) elytraFlyModule.settings.get(name)); 100 | } catch (ClassCastException e) { 101 | error("Failed to get ElytraFly setting: " + name); 102 | return Optional.empty(); 103 | } 104 | } 105 | 106 | private void resetBounds() { 107 | if (mc.player == null) return; 108 | 109 | double playerY = mc.player.getY(); 110 | double baseHeight = playerY - Y_POSITION_OFFSET; 111 | 112 | getElytraFlySetting("pitch40-upper-bounds").ifPresent(upper -> upper.set(baseHeight)); 113 | getElytraFlySetting("pitch40-lower-bounds").ifPresent(lower -> lower.set(baseHeight - boundGap.get())); 114 | } 115 | 116 | private void handleElytraSwap() { 117 | if (elytraSwapSlot != -1) { 118 | InvUtils.swap(elytraSwapSlot, true); 119 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 120 | InvUtils.swapBack(); 121 | elytraSwapSlot = -1; 122 | } 123 | } 124 | 125 | private boolean shouldUseFirework(PlayerEntity player, double upperBound) { 126 | return autoFirework.get() 127 | && fireworkCooldown == 0 128 | && player.getVelocity().y < velocityThreshold.get() 129 | && player.getY() < upperBound; 130 | } 131 | 132 | private void handleFirework() { 133 | int launchStatus = firework(mc); 134 | if (launchStatus >= 0) { 135 | fireworkCooldown = fireworkCooldownTicks.get(); 136 | if (launchStatus != 200) { 137 | elytraSwapSlot = launchStatus; 138 | } 139 | } 140 | } 141 | 142 | @EventHandler 143 | private void onTick(TickEvent.Pre event) { 144 | if (mc.player == null || !elytraFlyModule.isActive()) { 145 | if (mc.player != null && !mc.player.getAbilities().allowFlying) { 146 | elytraFlyModule.toggle(); 147 | resetBounds(); 148 | } 149 | return; 150 | } 151 | 152 | if (fireworkCooldown > 0) fireworkCooldown--; 153 | handleElytraSwap(); 154 | 155 | // Get current bounds 156 | Optional> lowerBoundSetting = getElytraFlySetting("pitch40-lower-bounds"); 157 | Optional> upperBoundSetting = getElytraFlySetting("pitch40-upper-bounds"); 158 | 159 | if (!lowerBoundSetting.isPresent() || !upperBoundSetting.isPresent()) return; 160 | 161 | double lowerBound = lowerBoundSetting.get().get(); 162 | double upperBound = upperBoundSetting.get().get(); 163 | 164 | // Check if player fell below bounds 165 | if (mc.player.getY() <= lowerBound - LOWER_BOUND_OFFSET) { 166 | resetBounds(); 167 | return; 168 | } 169 | 170 | // Handle upward flight 171 | if (Math.abs(mc.player.getPitch() - UPWARD_PITCH) < 0.1) { 172 | goingUp = true; 173 | if (shouldUseFirework(mc.player, upperBound)) { 174 | handleFirework(); 175 | } 176 | } 177 | // Handle peak detection 178 | else if (goingUp && mc.player.getVelocity().y <= 0) { 179 | goingUp = false; 180 | resetBounds(); 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/automation/TridentDupe.java: -------------------------------------------------------------------------------- 1 | // Credits to Killet, Laztec, and Ionar for contributing the source code for this module. 2 | // https://github.com/Killetx/TridentDupe 3 | // Credits to Journey For porting dupe to 1.21.1 4 | 5 | package dev.journey.PathSeeker.modules.automation; 6 | 7 | import dev.journey.PathSeeker.PathSeeker; 8 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 9 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 10 | import meteordevelopment.meteorclient.events.game.GameLeftEvent; 11 | import meteordevelopment.meteorclient.events.game.OpenScreenEvent; 12 | import meteordevelopment.meteorclient.events.packets.PacketEvent; 13 | import meteordevelopment.meteorclient.events.world.TickEvent; 14 | import meteordevelopment.meteorclient.settings.BoolSetting; 15 | import meteordevelopment.meteorclient.settings.DoubleSetting; 16 | import meteordevelopment.meteorclient.settings.Setting; 17 | import meteordevelopment.meteorclient.settings.SettingGroup; 18 | import meteordevelopment.meteorclient.systems.modules.Module; 19 | import meteordevelopment.orbit.EventHandler; 20 | import meteordevelopment.orbit.EventPriority; 21 | import net.minecraft.client.gui.screen.DisconnectedScreen; 22 | import net.minecraft.item.ItemStack; 23 | import net.minecraft.item.Items; 24 | import net.minecraft.network.packet.Packet; 25 | import net.minecraft.network.packet.c2s.play.*; 26 | import net.minecraft.screen.slot.SlotActionType; 27 | import net.minecraft.util.Hand; 28 | import net.minecraft.util.math.BlockPos; 29 | import net.minecraft.util.math.Direction; 30 | 31 | import java.util.ArrayList; 32 | import java.util.LinkedList; 33 | import java.util.List; 34 | import java.util.Queue; 35 | 36 | public class TridentDupe extends Module { 37 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 38 | private final Setting delay = sgGeneral.add(new DoubleSetting.Builder() 39 | .name("dupe-delay") 40 | .description("Raise this if it isn't working. This is how fast you'll dupe. 5 is good for most.") 41 | .defaultValue(5) 42 | .build() 43 | ); 44 | 45 | private final Setting dropTridents = sgGeneral.add(new BoolSetting.Builder() 46 | .name("dropTridents") 47 | .description("Drops tridents in your last hotbar slot.") 48 | .defaultValue(true) 49 | .build() 50 | ); 51 | 52 | private final Setting durabilityManagement = sgGeneral.add(new BoolSetting.Builder() 53 | .name("durabilityManagement") 54 | .description("(More AFKable) Attempts to dupe the highest durability trident in your hotbar.") 55 | .defaultValue(true) 56 | .build() 57 | ); 58 | 59 | private final Queue> delayedPackets = new LinkedList<>(); 60 | private final List scheduledTasks = new ArrayList<>(); 61 | private final List scheduledTasks2 = new ArrayList<>(); 62 | private boolean cancel = true; 63 | 64 | public TridentDupe() { 65 | super(PathSeeker.Automation, "trident-dupe", "Dupes tridents in first hotbar slot."); 66 | } 67 | 68 | @EventHandler(priority = EventPriority.HIGHEST + 1) 69 | private void onSendPacket(PacketEvent.Send event) { 70 | if (event.packet instanceof ClientCommandC2SPacket 71 | || event.packet instanceof PlayerMoveC2SPacket 72 | || event.packet instanceof CloseHandledScreenC2SPacket) 73 | return; 74 | 75 | if (!(event.packet instanceof ClickSlotC2SPacket) 76 | && !(event.packet instanceof PlayerActionC2SPacket)) 77 | return; 78 | 79 | if (!cancel) return; 80 | 81 | event.cancel(); 82 | } 83 | 84 | @Override 85 | public void onActivate() { 86 | if (mc.player == null) return; 87 | 88 | Int2ObjectMap modifiedStacks = new Int2ObjectOpenHashMap<>(); 89 | modifiedStacks.put(3, mc.player.getInventory().getStack(mc.player.getInventory().selectedSlot)); 90 | modifiedStacks.put(36, mc.player.getInventory().getStack(mc.player.getInventory().selectedSlot)); 91 | 92 | scheduledTasks.clear(); 93 | scheduledTasks2.clear(); 94 | dupe(); 95 | } 96 | 97 | private void dupe() { 98 | int delayInt = (int) (delay.get() * 100); 99 | 100 | int lowestHotbarSlot = 0; 101 | int lowestHotbarDamage = 1000; 102 | for (int i = 0; i < 9; i++) { 103 | if (mc.player.getInventory().getStack(i).isOf(Items.TRIDENT)) { 104 | int currentHotbarDamage = mc.player.getInventory().getStack(i).getDamage(); 105 | if (lowestHotbarDamage > currentHotbarDamage) { 106 | lowestHotbarSlot = i; 107 | lowestHotbarDamage = currentHotbarDamage; 108 | } 109 | } 110 | } 111 | 112 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 113 | cancel = true; 114 | 115 | int finalLowestHotbarSlot = lowestHotbarSlot; 116 | scheduleTask(() -> { 117 | cancel = false; 118 | 119 | if (durabilityManagement.get() && finalLowestHotbarSlot != 0) { 120 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 44, 0, SlotActionType.SWAP, mc.player); 121 | if (dropTridents.get()) { 122 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 44, 0, SlotActionType.THROW, mc.player); 123 | } 124 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 36 + finalLowestHotbarSlot, 0, SlotActionType.SWAP, mc.player); 125 | } 126 | 127 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 3, 0, SlotActionType.SWAP, mc.player); 128 | 129 | PlayerActionC2SPacket packet = new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.RELEASE_USE_ITEM, BlockPos.ORIGIN, Direction.DOWN, 0); 130 | mc.getNetworkHandler().sendPacket(packet); 131 | 132 | if (dropTridents.get()) { 133 | mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 44, 0, SlotActionType.THROW, mc.player); 134 | } 135 | 136 | cancel = true; 137 | scheduleTask2(() -> dupe(), delayInt); 138 | }, delayInt); 139 | } 140 | 141 | private void scheduleTask(Runnable task, long delayMillis) { 142 | scheduledTasks.add(new TimedTask(System.currentTimeMillis() + delayMillis, task)); 143 | } 144 | 145 | private void scheduleTask2(Runnable task, long delayMillis) { 146 | scheduledTasks2.add(new TimedTask(System.currentTimeMillis() + delayMillis, task)); 147 | } 148 | 149 | @EventHandler 150 | private void onTick(TickEvent.Pre event) { 151 | long currentTime = System.currentTimeMillis(); 152 | 153 | scheduledTasks.removeIf(task -> { 154 | if (task.executeTime <= currentTime) { 155 | task.task.run(); 156 | return true; 157 | } 158 | return false; 159 | }); 160 | 161 | scheduledTasks2.removeIf(task -> { 162 | if (task.executeTime <= currentTime) { 163 | task.task.run(); 164 | return true; 165 | } 166 | return false; 167 | }); 168 | } 169 | 170 | @EventHandler 171 | private void onGameLeft(GameLeftEvent event) { 172 | toggle(); 173 | } 174 | 175 | @EventHandler 176 | private void onScreenOpen(OpenScreenEvent event) { 177 | if (event.screen instanceof DisconnectedScreen) { 178 | toggle(); 179 | } 180 | } 181 | 182 | private static class TimedTask { 183 | private final long executeTime; 184 | private final Runnable task; 185 | 186 | TimedTask(long executeTime, Runnable task) { 187 | this.executeTime = executeTime; 188 | this.task = task; 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/PotESP.java: -------------------------------------------------------------------------------- 1 | //made by etianl :D 2 | package dev.journey.PathSeeker.modules.render; 3 | 4 | import dev.journey.PathSeeker.PathSeeker; 5 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 6 | import meteordevelopment.meteorclient.events.world.TickEvent; 7 | import meteordevelopment.meteorclient.renderer.ShapeMode; 8 | import meteordevelopment.meteorclient.settings.*; 9 | import meteordevelopment.meteorclient.systems.modules.Module; 10 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 11 | import meteordevelopment.meteorclient.utils.render.color.Color; 12 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 13 | import meteordevelopment.orbit.EventHandler; 14 | import net.minecraft.block.entity.BlockEntity; 15 | import net.minecraft.block.entity.DecoratedPotBlockEntity; 16 | import net.minecraft.item.Item; 17 | import net.minecraft.item.Items; 18 | import net.minecraft.text.Text; 19 | import net.minecraft.util.math.BlockPos; 20 | import net.minecraft.util.math.Box; 21 | import net.minecraft.util.math.ChunkPos; 22 | import net.minecraft.util.math.Vec3d; 23 | import net.minecraft.world.chunk.WorldChunk; 24 | 25 | import java.util.*; 26 | 27 | public class PotESP extends Module { 28 | private static final Set naturalPot = new HashSet<>(); 29 | 30 | static { 31 | naturalPot.add(Items.AIR); 32 | naturalPot.add(Items.STRING); 33 | naturalPot.add(Items.EMERALD); 34 | naturalPot.add(Items.EMERALD_BLOCK); 35 | naturalPot.add(Items.RAW_IRON_BLOCK); 36 | naturalPot.add(Items.IRON_INGOT); 37 | naturalPot.add(Items.TRIAL_KEY); 38 | naturalPot.add(Items.DIAMOND); 39 | naturalPot.add(Items.DIAMOND_BLOCK); 40 | naturalPot.add(Items.MUSIC_DISC_CREATOR_MUSIC_BOX); 41 | } 42 | 43 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 44 | private final SettingGroup sgRender = settings.createGroup("Render"); 45 | public final Setting renderDistance = sgRender.add(new IntSetting.Builder() 46 | .name("Render-Distance(Chunks)") 47 | .description("How many chunks from the character to render the detected chunks.") 48 | .defaultValue(32) 49 | .min(6) 50 | .sliderRange(6, 1024) 51 | .build() 52 | ); 53 | private final Setting potMessage = sgGeneral.add(new BoolSetting.Builder() 54 | .name("Extra Message") 55 | .description("Toggle the message reminding you about pots.") 56 | .defaultValue(true) 57 | .build() 58 | ); 59 | private final Setting displaycoords = sgGeneral.add(new BoolSetting.Builder() 60 | .name("Display Coords") 61 | .description("Displays coords of activated spawners in chat.") 62 | .defaultValue(true) 63 | .visible(() -> potMessage.get()) 64 | .build() 65 | ); 66 | private final Setting> junkItemList = sgGeneral.add(new ItemListSetting.Builder() 67 | .name("Junk Items") 68 | .description("Select the items to no look for. Decorated pots containing these items will not be highlighted.") 69 | .build() 70 | ); 71 | private final Setting removerenderdist = sgRender.add(new BoolSetting.Builder() 72 | .name("RemoveOutsideRenderDistance") 73 | .description("Removes the cached chunks when they leave the defined render distance.") 74 | .defaultValue(true) 75 | .build() 76 | ); 77 | private final Setting shapeMode = sgRender.add(new EnumSetting.Builder() 78 | .name("shape-mode") 79 | .description("How the shapes are rendered.") 80 | .defaultValue(ShapeMode.Both) 81 | .build() 82 | ); 83 | private final Setting potSideColor = sgRender.add(new ColorSetting.Builder() 84 | .name("pot-side-color") 85 | .description("Color of the dank pot.") 86 | .defaultValue(new SettingColor(255, 135, 125, 70)) 87 | .visible(() -> (shapeMode.get() == ShapeMode.Sides || shapeMode.get() == ShapeMode.Both)) 88 | .build() 89 | ); 90 | private final Setting potLineColor = sgRender.add(new ColorSetting.Builder() 91 | .name("pot-line-color") 92 | .description("Color of the dank pot.") 93 | .defaultValue(new SettingColor(255, 135, 125, 235)) 94 | .visible(() -> (shapeMode.get() == ShapeMode.Lines || shapeMode.get() == ShapeMode.Both)) 95 | .build() 96 | ); 97 | private final Set potLocations = Collections.synchronizedSet(new HashSet<>()); 98 | 99 | public PotESP() { 100 | super(PathSeeker.Render, "PotESP", "Finds the dank pots... In Minecraft (Locates decorated pots with un-natural items in them)"); 101 | } 102 | 103 | @Override 104 | public void onActivate() { 105 | potLocations.clear(); 106 | } 107 | 108 | @Override 109 | public void onDeactivate() { 110 | potLocations.clear(); 111 | } 112 | 113 | @EventHandler 114 | private void onPreTick(TickEvent.Pre event) { 115 | if (mc.world == null) return; 116 | 117 | int renderDistance = mc.options.getViewDistance().getValue(); 118 | ChunkPos playerChunkPos = new ChunkPos(mc.player.getBlockPos()); 119 | for (int chunkX = playerChunkPos.x - renderDistance; chunkX <= playerChunkPos.x + renderDistance; chunkX++) { 120 | for (int chunkZ = playerChunkPos.z - renderDistance; chunkZ <= playerChunkPos.z + renderDistance; chunkZ++) { 121 | WorldChunk chunk = mc.world.getChunk(chunkX, chunkZ); 122 | List blockEntities = new ArrayList<>(chunk.getBlockEntities().values()); 123 | 124 | for (BlockEntity blockEntity : blockEntities) { 125 | if (blockEntity instanceof DecoratedPotBlockEntity pot) { 126 | Item potItem = pot.stack.getItem(); 127 | 128 | BlockPos potLocation = pot.getPos(); 129 | if (!potLocations.contains(potLocation) && !naturalPot.contains(potItem) && !junkItemList.get().contains(potItem)) { 130 | if (potMessage.get()) { 131 | if (displaycoords.get()) 132 | ChatUtils.sendMsg(Text.of("Found a dank pot! It contains: " + potItem + " Location: " + potLocation)); 133 | else ChatUtils.sendMsg(Text.of("Found a dank pot! It contains: " + potItem)); 134 | } 135 | potLocations.add(potLocation); 136 | } 137 | } 138 | } 139 | } 140 | } 141 | if (removerenderdist.get()) removeChunksOutsideRenderDistance(); 142 | } 143 | 144 | @EventHandler 145 | private void onRender(Render3DEvent event) { 146 | if (potSideColor.get().a > 5 || potLineColor.get().a > 5) { 147 | synchronized (potLocations) { 148 | for (BlockPos pos : potLocations) { 149 | BlockPos playerPos = new BlockPos(mc.player.getBlockX(), pos.getY(), mc.player.getBlockZ()); 150 | if (pos != null && playerPos.isWithinDistance(pos, renderDistance.get() * 16)) { 151 | int startX = pos.getX(); 152 | int startY = pos.getY(); 153 | int startZ = pos.getZ(); 154 | int endX = pos.getX(); 155 | int endY = pos.getY(); 156 | int endZ = pos.getZ(); 157 | render(new Box(new Vec3d(startX + 1, startY + 1, startZ + 1), new Vec3d(endX, endY, endZ)), potSideColor.get(), potLineColor.get(), shapeMode.get(), event); 158 | } 159 | } 160 | } 161 | } 162 | } 163 | 164 | private void render(Box box, Color sides, Color lines, ShapeMode shapeMode, Render3DEvent event) { 165 | event.renderer.box(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ, sides, lines, shapeMode, 0); 166 | } 167 | 168 | private void removeChunksOutsideRenderDistance() { 169 | double renderDistanceBlocks = renderDistance.get() * 16; 170 | 171 | removeChunksOutsideRenderDistance(potLocations, renderDistanceBlocks); 172 | } 173 | 174 | private void removeChunksOutsideRenderDistance(Set chunkSet, double renderDistanceBlocks) { 175 | chunkSet.removeIf(blockPos -> { 176 | BlockPos playerPos = new BlockPos(mc.player.getBlockX(), blockPos.getY(), mc.player.getBlockZ()); 177 | return !playerPos.isWithinDistance(blockPos, renderDistanceBlocks); 178 | }); 179 | } 180 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PathSeeker 2 | 3 | *A Minecraft utility mod built on the foundation of TrouserStreak, reimagined and enhanced for advanced anarchy server 4 | gameplay. Whether you're hunting bases, exploring new lands, or managing your resources, PathSeeker provides the tools 5 | to stay ahead.* 6 | 7 | > [!IMPORTANT] 8 | > Make sure to install 9 | > Baritone, [1.21.1](https://maven.meteordev.org/snapshots/meteordevelopment/baritone/1.21.1-SNAPSHOT/baritone-1.21.1-20240826.213754-1.jar) [1.21.4](https://maven.meteordev.org/snapshots/meteordevelopment/baritone/1.21.4-SNAPSHOT/baritone-1.21.4-20250105.184728-1.jar) 10 | > else it will crash. 11 | 12 | --- 13 | 14 | ## Features 15 | 16 | PathSeeker comes packed with powerful features designed for Minecraft explorers and thrill-seekers: 17 | 18 | - **Base Finder** 19 | Uncover hidden bases and dungeons effortlessly, giving you the edge in anarchy gameplay. 20 | 21 | - **AutoPortal** Made By [xqyet](https://github.com/xqyet) 22 | Automatically places and lights a portal. 23 | 24 | - **Pitch40Util** 25 | Used alongside Meteor's pitch 40. Auto sets min and max bounds so that you continue to gain height. It also has an 26 | auto firework mode for when you lose 27 | velocity. (Ported From [Jeff-Mod](https://github.com/miles352/meteor-stashhunting-addon/blob/1.21.1/src/main/java/com/stash/hunt/modules/Pitch40Util.java)) 28 | 29 | - **TrailFollower** (Requires XaeroPlus) 30 | Follows trails in all dimensions using either pitch40 or baritone. It may break on path splits or other 31 | cases. (Ported From [Jeff-Mod](https://github.com/miles352/meteor-stashhunting-addon/blob/1.21.1/src/main/java/com/stash/hunt/modules/TrailFollower.java)) 32 | 33 | - **StackedMinecartDetector** 34 | Detects if a stacked spawner is in the area. 35 | 36 | - **EntityClusterESP** 37 | Highlights the centre point of entity clusters. 38 | 39 | - **DroppedItemESP** 40 | Highlights important dropped items. 41 | 42 | - **BetterStashFinder:** An upgraded stash finder with enhanced filtering 43 | options. (Ported From [Jeff-Mod](https://github.com/miles352/meteor-stashhunting-addon/blob/1.21.1/src/main/java/com/stash/hunt/modules/BetterStashFinder.java)) 44 | 45 | - **AutoEnchanter:** Automates enchanting at anvils, saving time and effort. 46 | 47 | - **GrimEfly:** Vanilla efly uses a chest-plate to ensure that elytra do not consume 48 | durability. (Ported From [Jeff-Mod](https://github.com/miles352/meteor-stashhunting-addon/blob/1.21.1/src/main/java/com/stash/hunt/modules/GrimEfly.java)) 49 | 50 | - **Trident Dupe:** Facilitates trident duplication (note: patched on most servers). 51 | 52 | - **TridentAura**: Spam Tridents. (Made By [xqyet](https://github.com/FaxHack/PathSeeker/commits?author=xqyet)) 53 | 54 | - **AutoFirework:** Automatically activates firework use for added flair. 55 | 56 | - **CollectorsESP**: Highlights maparts and banners. [xqyet](https://github.com/FaxHack/PathSeeker/commits?author=xqyet) 57 | 58 | - **BaritonePathMacro**: Easy macro to activate TrailFollower in the nether. Must deploy in the air. [xqyet](https://github.com/FaxHack/PathSeeker/commits?author=xqyet) 59 | 60 | - **ScreenshotFolderCommand** 61 | Open the Minecraft screenshot folder. 62 | 63 | - **MeteorFolderCommand** 64 | Open the meteor-client folder. 65 | 66 | > [!TIP] 67 | > Use Base Finder with NewerNewChunks to drastically improve your chances of locating uncharted bases. 68 | 69 | > [!WARNING] 70 | > Make sure your predefined item lists are accurate. Incorrect configurations may result in missing valuable loot. 71 | 72 | - **MobGearESP** 73 | Detects mobs carrying or wearing player 74 | equipment. (Ported From [windoid](https://github.com/windoid/MobGearESP/blob/master/src/main/java/com/mobgear/addon/modules/MobGearESP.java)) 75 | 76 | - **SignHistorian** 77 | Records & restores broken or modified 78 | signs. (Ported From [Stardust](https://github.com/0xTas/stardust/blob/64cd499c62d30be8e479b084a613e0c05b77c8d9/src/main/java/dev/stardust/modules/SignHistorian.java)) 79 | 80 | - **NewerNewChunks** 81 | Take control of chunk tracing! Manage chunk data across servers and dimensions with crash-resilient saving, easy 82 | sharing options, and pinpoint accuracy. 83 | 84 | > [!IMPORTANT] 85 | > To share your chunk data with friends, copy the `PathSeeker/NewChunks` folder and send it directly. 86 | 87 | - **Hole/Tunnel/StairsESP** 88 | Spot 1x1 holes, horizontal tunnels, and staircase tunnels like a pro. By default, it skips passable blocks like 89 | torches or water but can be configured to detect only air. (Credits to Meteor Client and etianl) 90 | 91 | - **PotESP** 92 | Detect Decorated Pots with unnatural contents and find out what's inside! Easily locate suspicious pots in your 93 | environment. (Ported From [TrouserStreak](https://github.com/etianl/Trouser-Streak/blob/1.21.1/src/main/java/pwn/noobs/trouserstreak/modules/PotESP.java)) 94 | 95 | - **PortalPatternFinder** 96 | Scan for shapes of broken or removed Nether Portals within cave air blocks. Great for tracking portal skips in the 97 | Nether for 1.13+ 98 | chunks. (Ported From [TrouserStreak](https://github.com/etianl/Trouser-Streak/blob/1.21.1/src/main/java/pwn/noobs/trouserstreak/modules/PortalPatternFinder.java)) 99 | 100 | > [!NOTE] 101 | > PortalPatternFinder works best when paired with advanced chunk detection tools. 102 | 103 | - **CaveDisturbanceDetector** 104 | Hunt for single air blocks hidden within cave air, helping you identify disturbances in 1.13+ underground 105 | structures. (Ported From [TrouserTreak](https://github.com/etianl/Trouser-Streak/blob/1.21.1/src/main/java/pwn/noobs/trouserstreak/modules/PortalPatternFinder.java)) 106 | 107 | > [!CAUTION] 108 | > Scanning large areas with CaveDisturbanceDetector may increase resource usage. Adjust your settings accordingly. 109 | 110 | --- 111 | 112 | ## Why Choose This Mod? 113 | 114 | PathSeeker brings the famed TrouserStreak to entirely new heights. With innovative features, this mod is the ideal companion for anarchy server gameplay. Whether 115 | If you're base raiding, chunk tracing, or treasure hunting, PathSeeker empowers your journey. 116 | 117 | --- 118 | 119 | ## Installation 120 | 121 | Getting started is simple: 122 | 123 | 1. **Download** the latest release from [Releases](https://github.com/FaxHack/PathSeeker/releases/). 124 | 2. **Place** the mod `.jar` file in your Minecraft `mods` folder. 125 | 3. **Launch** Minecraft and dive into the chaos! 126 | 127 | > [!NOTE] 128 | > Ensure you’re using the correct version of Minecraft (1.21.x or compatible) for the best experience. 129 | 130 | --- 131 | 132 | ## Requirements 133 | 134 | - **Minecraft 1.21.x** or compatible versions. 135 | - [Baritone](https://modrinth.com/mod/xaeros-minimap/versions) --> [1.21.1](https://maven.meteordev.org/snapshots/meteordevelopment/baritone/1.21.1-SNAPSHOT/baritone-1.21.1-20240826.213754-1.jar), [1.21.4](https://maven.meteordev.org/snapshots/meteordevelopment/baritone/1.21.4-SNAPSHOT/baritone-1.21.4-20250105.184728-1.jar) 136 | - [**ViaFabricPlus**](https://modrinth.com/mod/viafabricplus/versions?g=1.21.1&l=fabric&c=release) *(optional for 137 | cross-version compatibility)*. 138 | - [**XearosPlus**](https://modrinth.com/mod/xaeroplus/versions) 139 | - [**XearosWorldMap**](https://modrinth.com/mod/xaeros-world-map/versions) 140 | - [**XearosMiniMap**](https://modrinth.com/mod/xaeros-minimap/versions) 141 | 142 | --- 143 | 144 | ## Roadmap 145 | 146 | PathSeeker is always evolving! Here's what's planned: 147 | 148 | - [x] Core features for the initial release. 149 | - [x] New hunting modules for advanced gameplay. 150 | - [x] Enhanced automation tools for loot and base management. 151 | 152 | --- 153 | 154 | ## Credits 155 | 156 | A big shout-out to the creators and projects that inspired PathSeeker: 157 | 158 | - [**TrouserStreak**](https://github.com/etianl/Trouser-Streak) for laying the groundwork. 159 | - [**meteor-mod**](https://github.com/miles352/meteor-stashhunting-addon) for hunting modules. 160 | - [**Stardust**](https://github.com/0xTas/stardust) for additional features. 161 | 162 | --- 163 | 164 | ## Contributing 165 | 166 | We welcome contributions! If you have ideas, fixes, or new features to share, feel free to open issues or submit pull requests. 167 | 168 | > [!TIP] 169 | > Review our coding guidelines for smoother collaboration before submitting a pull request. 170 | 171 | --- 172 | 173 | ## License 174 | 175 | Licensed under **Apache-2.0**. 176 | 177 | --- 178 | 179 | ## Support 180 | 181 | Need help or have questions? 182 | 183 | - Join our **[Discord](https://discord.gg/SdH8ZF96mD)** community. 184 | - Open an issue directly on **GitHub**. 185 | ## Star History 186 | 187 | [![Star History Chart](https://api.star-history.com/svg?repos=FaxHack/PathSeeker&type=Date)](https://www.star-history.com/#FaxHack/PathSeeker&Date) 188 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/VanityESP.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.render; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 5 | import meteordevelopment.meteorclient.renderer.ShapeMode; 6 | import meteordevelopment.meteorclient.settings.BoolSetting; 7 | import meteordevelopment.meteorclient.settings.ColorSetting; 8 | import meteordevelopment.meteorclient.settings.Setting; 9 | import meteordevelopment.meteorclient.settings.SettingGroup; 10 | import meteordevelopment.meteorclient.systems.modules.Module; 11 | import meteordevelopment.meteorclient.utils.render.color.Color; 12 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 13 | import meteordevelopment.orbit.EventHandler; 14 | import net.minecraft.block.BannerBlock; 15 | import net.minecraft.block.BlockState; 16 | import net.minecraft.block.WallBannerBlock; 17 | import net.minecraft.block.entity.BannerBlockEntity; 18 | import net.minecraft.block.entity.BlockEntity; 19 | import net.minecraft.entity.decoration.ItemFrameEntity; 20 | import net.minecraft.util.math.BlockPos; 21 | import net.minecraft.util.math.Box; 22 | import net.minecraft.util.math.Direction; 23 | import net.minecraft.world.chunk.WorldChunk; 24 | 25 | // refactored to VanityESP 26 | public class VanityESP extends Module { 27 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 28 | private final SettingGroup sgColors = settings.createGroup("Colors"); 29 | 30 | private final Setting highlightItemFrames = sgGeneral.add(new BoolSetting.Builder() 31 | .name("item-frames") 32 | .description("Highlights item frames.") 33 | .defaultValue(true) 34 | .build() 35 | ); 36 | // Added mapOutline 37 | private final Setting mapOutlineColor = sgColors.add(new ColorSetting.Builder() 38 | .name("map-outline-color") 39 | .description("Outline color for item frames containing maps.") 40 | .defaultValue(new SettingColor(255, 255, 0, 255)) 41 | .build() 42 | ); 43 | 44 | private final Setting highlightBanners = sgGeneral.add(new BoolSetting.Builder() 45 | .name("banners") 46 | .description("Highlights banners.") 47 | .defaultValue(true) 48 | .build() 49 | ); 50 | 51 | private final Setting highlightSigns = sgGeneral.add(new BoolSetting.Builder() 52 | .name("signs") 53 | .description("Highlights signs.") 54 | .defaultValue(true) 55 | .build() 56 | ); 57 | 58 | private final Setting mapartColor = sgColors.add(new ColorSetting.Builder() 59 | .name("mapart-frame-color") 60 | .description("Color for item frames containing maps.") 61 | .defaultValue(new SettingColor(255, 255, 0, 50)) 62 | .build() 63 | ); 64 | 65 | private final Setting bannerColor = sgColors.add(new ColorSetting.Builder() 66 | .name("banner-color") 67 | .description("Color for banner ESP.") 68 | .defaultValue(new SettingColor(255, 0, 0, 50)) 69 | .build() 70 | ); 71 | 72 | private final Setting bannerOutlineColor = sgColors.add(new ColorSetting.Builder() 73 | .name("banner-outline") 74 | .description("Outline color for banners.") 75 | .defaultValue(new SettingColor(255, 0, 0, 255)) // Red outline 76 | .build() 77 | ); 78 | 79 | private final Setting signColor = sgColors.add(new ColorSetting.Builder() 80 | .name("sign-color") 81 | .description("Fill color for signs.") 82 | .defaultValue(new SettingColor(255, 165, 0, 50)) 83 | .build() 84 | ); 85 | 86 | private final Setting signOutlineColor = sgColors.add(new ColorSetting.Builder() 87 | .name("sign-outline-color") 88 | .description("Outline color for signs.") 89 | .defaultValue(new SettingColor(255, 165, 0, 255)) 90 | .build() 91 | ); 92 | 93 | public VanityESP() { 94 | super(PathSeeker.Render, "VanityESP", "Highlights maparts and banners"); 95 | } 96 | 97 | @EventHandler 98 | private void onRender(Render3DEvent event) { 99 | if (mc.world == null || mc.player == null) return; 100 | // fix for MapartESP depending on wall/floor angle 101 | if (highlightItemFrames.get()) { 102 | for (ItemFrameEntity frame : mc.world.getEntitiesByClass(ItemFrameEntity.class, mc.player.getBoundingBox().expand(64), e -> { 103 | String id = e.getHeldItemStack().getItem().getTranslationKey(); 104 | return id.equals("item.minecraft.filled_map") || id.contains("shulker_box"); 105 | })) { 106 | 107 | Box box; 108 | float pitch = frame.getPitch(); 109 | if (pitch == 90 || pitch == -90) { 110 | box = frame.getBoundingBox().expand(0.12, 0.01, 0.12); 111 | } else { 112 | box = frame.getBoundingBox().expand(0.12, 0.12, 0.01); 113 | } 114 | 115 | Color fill = new Color(mapartColor.get()); 116 | Color outline = new Color(mapOutlineColor.get()); 117 | 118 | event.renderer.box(box, fill, outline, ShapeMode.Both, 0); 119 | } 120 | } 121 | // redid some of my bannerESP logic to fit all wall-mounts and also added my positional logic for standing banners 122 | if (highlightBanners.get()) { 123 | int radius = 8; 124 | BlockPos playerPos = mc.player.getBlockPos(); 125 | 126 | for (int dx = -radius; dx <= radius; dx++) { 127 | for (int dz = -radius; dz <= radius; dz++) { 128 | WorldChunk chunk = mc.world.getChunk(playerPos.getX() / 16 + dx, playerPos.getZ() / 16 + dz); 129 | if (chunk == null) continue; 130 | 131 | for (BlockEntity be : chunk.getBlockEntities().values()) { 132 | if (!(be instanceof BannerBlockEntity banner)) continue; 133 | 134 | BlockPos pos = banner.getPos(); 135 | BlockState state = mc.world.getBlockState(pos); 136 | Box box; 137 | 138 | Color fill = new Color(bannerColor.get()); 139 | Color outline = new Color(bannerOutlineColor.get()); 140 | 141 | if (state.contains(WallBannerBlock.FACING)) { 142 | Direction facing = state.get(WallBannerBlock.FACING); 143 | double centerX = pos.getX() + 0.5; 144 | double centerZ = pos.getZ() + 0.5; 145 | double offset = 0.1; 146 | double depth = 0.03; 147 | double width = 0.45; 148 | double y1 = pos.getY() - 0.95; 149 | double y2 = pos.getY() + 0.85; 150 | 151 | switch (facing) { 152 | case NORTH: 153 | box = new Box(centerX - width, y1, pos.getZ() + 1 - offset - depth, centerX + width, y2, pos.getZ() + 1 - offset); 154 | break; 155 | case SOUTH: 156 | box = new Box(centerX - width, y1, pos.getZ() + offset, centerX + width, y2, pos.getZ() + offset + depth); 157 | break; 158 | case WEST: 159 | box = new Box(pos.getX() + 1 - offset - depth, y1, centerZ - width, pos.getX() + 1 - offset, y2, centerZ + width); 160 | break; 161 | case EAST: 162 | box = new Box(pos.getX() + offset, y1, centerZ - width, pos.getX() + offset + depth, y2, centerZ + width); 163 | break; 164 | default: 165 | continue; 166 | } 167 | 168 | event.renderer.box(box, fill, outline, ShapeMode.Both, 0); 169 | } else if (state.contains(BannerBlock.ROTATION)) { 170 | int rotation = state.get(BannerBlock.ROTATION); 171 | double centerX = pos.getX() + 0.5; 172 | double centerZ = pos.getZ() + 0.5; 173 | double y1 = pos.getY(); 174 | double y2 = pos.getY() + 1.85; 175 | 176 | if (rotation == 0 || rotation == 8) { 177 | double width = 0.45; 178 | double depth = 0.03; 179 | box = new Box(centerX - width, y1, centerZ - depth, centerX + width, y2, centerZ + depth); 180 | } else if (rotation == 4 || rotation == 12) { 181 | double width = 0.03; 182 | double depth = 0.45; 183 | box = new Box(centerX - width, y1, centerZ - depth, centerX + width, y2, centerZ + depth); 184 | } else { 185 | double size = 0.3; 186 | box = new Box(centerX - size, y1, centerZ - size, centerX + size, y2, centerZ + size); 187 | } 188 | 189 | event.renderer.box(box, fill, outline, ShapeMode.Both, 0); 190 | 191 | } 192 | } 193 | } 194 | } 195 | } 196 | } 197 | } 198 | 199 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/automation/AutoPortal.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.automation; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 5 | import meteordevelopment.meteorclient.events.world.TickEvent; 6 | import meteordevelopment.meteorclient.renderer.ShapeMode; 7 | import meteordevelopment.meteorclient.settings.*; 8 | import meteordevelopment.meteorclient.systems.modules.Module; 9 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 10 | import meteordevelopment.orbit.EventHandler; 11 | import net.minecraft.item.BlockItem; 12 | import net.minecraft.item.Items; 13 | import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; 14 | import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket; 15 | import net.minecraft.util.Hand; 16 | import net.minecraft.util.hit.BlockHitResult; 17 | import net.minecraft.util.math.BlockPos; 18 | import net.minecraft.util.math.Direction; 19 | import net.minecraft.util.math.Vec3d; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | public class AutoPortal extends Module { 25 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 26 | private final List waitingForBreak = new ArrayList<>(); 27 | 28 | 29 | private final Setting placeDelay = sgGeneral.add(new IntSetting.Builder() 30 | .name("place-delay") 31 | .description("Ticks between each obsidian placement.") 32 | .defaultValue(1) 33 | .sliderRange(1, 20) 34 | .build() 35 | ); 36 | 37 | private final Setting blocksPerTick = sgGeneral.add(new IntSetting.Builder() 38 | .name("blocks-per-tick") 39 | .description("How many blocks to place each tick.") 40 | .defaultValue(1) 41 | .sliderRange(1, 5) 42 | .build() 43 | ); 44 | 45 | private final Setting render = sgGeneral.add(new BoolSetting.Builder() 46 | .name("render") 47 | .description("Renders the portal frame as it's being placed.") 48 | .defaultValue(true) 49 | .build() 50 | ); 51 | 52 | private final Setting shapeMode = sgGeneral.add(new EnumSetting.Builder() 53 | .name("shape-mode") 54 | .description("How the box is rendered.") 55 | .defaultValue(ShapeMode.Both) 56 | .build() 57 | ); 58 | 59 | private final Setting sideColor = sgGeneral.add(new ColorSetting.Builder() 60 | .name("side-color") 61 | .defaultValue(new SettingColor(100, 100, 255, 10)) 62 | .build() 63 | ); 64 | 65 | private final Setting lineColor = sgGeneral.add(new ColorSetting.Builder() 66 | .name("line-color") 67 | .defaultValue(new SettingColor(100, 100, 255, 255)) 68 | .build() 69 | ); 70 | 71 | private final List portalBlocks = new ArrayList<>(); 72 | private int delay = 0; 73 | private int index = 0; 74 | 75 | public AutoPortal() { 76 | super(PathSeeker.Automation, "AutoPortal", "For the Base Hunter who has places to be."); 77 | } 78 | 79 | @Override 80 | public void onActivate() { 81 | int obsidianCount = 0; 82 | for (int i = 0; i < 36; i++) { 83 | if (mc.player.getInventory().getStack(i).getItem() == Items.OBSIDIAN) { 84 | obsidianCount += mc.player.getInventory().getStack(i).getCount(); 85 | } 86 | } 87 | 88 | if (obsidianCount < 10) { 89 | error("Not enough obsidian to build the portal (need at least 10)!"); 90 | toggle(); 91 | return; 92 | } 93 | portalBlocks.clear(); 94 | index = 0; 95 | delay = 0; 96 | 97 | // directly in front + block position check 98 | Direction forward = mc.player.getHorizontalFacing(); 99 | Direction right = forward.rotateYClockwise(); 100 | BlockPos standingPos = mc.player.getBlockPos(); // temp mutable ref 101 | BlockPos blockBelow = standingPos.down(); 102 | double blockHeight = mc.world.getBlockState(blockBelow).getCollisionShape(mc.world, blockBelow).getMax(Direction.Axis.Y); 103 | // (height < 1.0) 104 | if (blockHeight < 1.0) { 105 | standingPos = standingPos.up(); 106 | } 107 | BlockPos base = standingPos 108 | .offset(forward, 2) 109 | .offset(right, -1); 110 | // duplicate check 111 | int obsidianCheck = 0; 112 | 113 | List checkPositions = List.of( 114 | base.offset(right, 1), base.offset(right, 2), 115 | base.offset(right, 0).up(1), base.offset(right, 0).up(2), base.offset(right, 0).up(3), 116 | base.offset(right, 3).up(1), base.offset(right, 3).up(2), base.offset(right, 3).up(3), 117 | base.offset(right, 1).up(4), base.offset(right, 2).up(4) 118 | ); 119 | // block obstruction check (temporary until fixed) 120 | boolean obstructed = checkPositions.stream().anyMatch(pos -> !mc.world.getBlockState(pos).isReplaceable()); 121 | // will remove later once we fix portal block obstruction 122 | if (obstructed) { 123 | error("Portal area obstructed. Move and try again."); 124 | portalBlocks.clear(); 125 | portalBlocks.addAll(checkPositions); // just render blocked frame 126 | index = checkPositions.size(); // skip building 127 | return; 128 | } 129 | 130 | for (BlockPos checkPos : checkPositions) { 131 | if (mc.world.getBlockState(checkPos).getBlock().asItem() == Items.OBSIDIAN) { 132 | obsidianCheck++; 133 | } 134 | } 135 | 136 | if (obsidianCheck >= checkPositions.size()) { 137 | error("A portal already exists here!"); 138 | toggle(); 139 | return; 140 | } 141 | 142 | portalBlocks.add(base.offset(right, 1)); 143 | portalBlocks.add(base.offset(right, 2)); 144 | 145 | for (int i = 1; i <= 3; i++) { 146 | portalBlocks.add(base.offset(right, 0).up(i)); 147 | } 148 | 149 | for (int i = 1; i <= 3; i++) { 150 | portalBlocks.add(base.offset(right, 3).up(i)); 151 | } 152 | 153 | portalBlocks.add(base.offset(right, 1).up(4)); 154 | portalBlocks.add(base.offset(right, 2).up(4)); 155 | 156 | for (int i = 0; i < 9; i++) { 157 | if (mc.player.getInventory().getStack(i).getItem() == Items.OBSIDIAN) { 158 | mc.player.getInventory().selectedSlot = i; 159 | break; 160 | } 161 | } 162 | } 163 | 164 | @Override 165 | public void onDeactivate() { 166 | portalBlocks.clear(); 167 | index = 0; 168 | delay = 0; 169 | } 170 | 171 | @EventHandler 172 | private void onTick(TickEvent.Post event) { 173 | if (mc.player == null || mc.world == null) return; 174 | if (!(mc.player.getMainHandStack().getItem() instanceof BlockItem blockItem)) return; 175 | if (blockItem.getBlock().asItem() != Items.OBSIDIAN) return; 176 | 177 | if (index >= portalBlocks.size()) { 178 | toggle(); 179 | return; 180 | } 181 | 182 | delay++; 183 | if (delay < placeDelay.get()) return; 184 | for (int i = 0; i < blocksPerTick.get() && index < portalBlocks.size(); i++, index++) { 185 | BlockPos pos = portalBlocks.get(index); 186 | // prevent faulty portal placements (not being used due to boolean obstruction check above, but will fix in the future) 187 | if (!mc.world.getBlockState(pos).isReplaceable()) { 188 | if (!waitingForBreak.contains(pos) && mc.world.getBlockState(pos).getBlock().asItem() != Items.OBSIDIAN) { 189 | if (mc.interactionManager != null) { 190 | mc.interactionManager.attackBlock(pos, Direction.UP); 191 | mc.player.swingHand(Hand.MAIN_HAND); 192 | waitingForBreak.add(pos); 193 | } 194 | } 195 | index--; // loop again to finish placing 196 | return; 197 | } 198 | 199 | waitingForBreak.remove(pos); 200 | 201 | BlockHitResult bhr = new BlockHitResult(Vec3d.ofCenter(pos), Direction.UP, pos, false); 202 | 203 | mc.player.networkHandler.sendPacket(new PlayerActionC2SPacket( 204 | PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN)); 205 | mc.player.networkHandler.sendPacket(new PlayerInteractBlockC2SPacket( 206 | Hand.OFF_HAND, bhr, mc.player.currentScreenHandler.getRevision() + 2)); 207 | mc.player.networkHandler.sendPacket(new PlayerActionC2SPacket( 208 | PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.DOWN)); 209 | mc.player.swingHand(Hand.MAIN_HAND); 210 | } 211 | delay = 0; 212 | 213 | if (index >= portalBlocks.size()) { 214 | // auto light 215 | for (int i = 0; i < 9; i++) { 216 | if (mc.player.getInventory().getStack(i).getItem() == Items.FLINT_AND_STEEL) { 217 | mc.player.getInventory().selectedSlot = i; 218 | 219 | BlockPos firePos = portalBlocks.get(0).up(); 220 | BlockHitResult fireHit = new BlockHitResult(Vec3d.ofCenter(firePos), Direction.UP, firePos, false); 221 | 222 | mc.interactionManager.interactBlock(mc.player, Hand.MAIN_HAND, fireHit); 223 | mc.player.swingHand(Hand.MAIN_HAND); 224 | break; 225 | } 226 | } 227 | info("Portal complete. AutoPortal disabled."); 228 | toggle(); 229 | } 230 | } 231 | 232 | @EventHandler 233 | private void onRender(Render3DEvent event) { 234 | if (!render.get()) return; 235 | for (int i = index; i < portalBlocks.size(); i++) { 236 | BlockPos pos = portalBlocks.get(i); 237 | event.renderer.box(pos, sideColor.get(), lineColor.get(), shapeMode.get(), 0); 238 | } 239 | } 240 | } -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/utils/PathSeekerUtil.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.utils; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.utils.misc.input.Input; 5 | import meteordevelopment.meteorclient.utils.player.FindItemResult; 6 | import meteordevelopment.meteorclient.utils.player.InvUtils; 7 | import net.fabricmc.loader.api.FabricLoader; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.client.option.KeyBinding; 10 | import net.minecraft.item.Items; 11 | import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; 12 | import net.minecraft.text.ClickEvent; 13 | import net.minecraft.text.MutableText; 14 | import net.minecraft.text.Style; 15 | import net.minecraft.text.Text; 16 | import net.minecraft.util.Hand; 17 | import net.minecraft.util.math.Vec3d; 18 | 19 | import javax.annotation.Nullable; 20 | import java.awt.*; 21 | import java.io.File; 22 | import java.net.URI; 23 | import java.net.http.HttpClient; 24 | import java.net.http.HttpRequest; 25 | import java.net.http.HttpResponse; 26 | import java.time.Instant; 27 | import java.time.ZoneId; 28 | import java.time.ZonedDateTime; 29 | import java.time.format.DateTimeFormatter; 30 | import java.util.Locale; 31 | import java.util.concurrent.TimeUnit; 32 | 33 | public class PathSeekerUtil { 34 | private static final MinecraftClient mc = MinecraftClient.getInstance(); 35 | 36 | private String lastSeen; 37 | private String firstSeen; 38 | private long playtime; 39 | 40 | public PathSeekerUtil() { 41 | this.lastSeen = ""; 42 | this.firstSeen = ""; 43 | this.playtime = 0; 44 | } 45 | 46 | public static String randomColorCode() { 47 | String[] colorCodes = {"§4", "§c", "§6", "§e", "§2", "§a", "§b", "§3", "§1", "§9", "§d", "§5", "§7", "§8", "§0"}; 48 | return colorCodes[(int) (Math.random() * colorCodes.length)]; 49 | } 50 | 51 | public static void sendPlayerMessage(String message) { 52 | if (mc.player != null) { 53 | mc.player.sendMessage(Text.literal(message), false); 54 | } 55 | } 56 | 57 | public static void firework(MinecraftClient mc, boolean requireElytra) { 58 | if (mc.player == null) return; 59 | 60 | if (requireElytra && !mc.player.isFallFlying()) return; 61 | 62 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 63 | 64 | } 65 | 66 | public static void logError(String message) { 67 | System.err.println("[PathSeeker] " + message); 68 | } 69 | 70 | public static String formatDate(String timestamp) { 71 | try { 72 | Instant instant = Instant.parse(timestamp); 73 | ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault()); 74 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy", Locale.US); 75 | return zonedDateTime.format(formatter); 76 | } catch (Exception e) { 77 | logError("Failed to parse date: " + timestamp); 78 | return "Invalid date"; 79 | } 80 | } 81 | 82 | public static String formatPlaytime(long seconds) { 83 | if (seconds <= 0) return "none"; 84 | 85 | long days = TimeUnit.SECONDS.toDays(seconds); 86 | long hours = TimeUnit.SECONDS.toHours(seconds) % 24; 87 | long minutes = TimeUnit.SECONDS.toMinutes(seconds) % 60; 88 | 89 | if (days >= 30) { 90 | return (days / 30) + " months"; 91 | } else if (days >= 7) { 92 | return (days / 7) + " weeks"; 93 | } else if (days > 0) { 94 | return days + " days"; 95 | } else if (hours > 0) { 96 | return hours + " hours"; 97 | } else if (minutes > 0) { 98 | return minutes + " minutes"; 99 | } else { 100 | return seconds + " seconds"; 101 | } 102 | } 103 | 104 | public static boolean checkOrCreateFile(MinecraftClient mc, String fileName) { 105 | File file = FabricLoader.getInstance().getGameDir().resolve(fileName).toFile(); 106 | 107 | if (!file.exists()) { 108 | try { 109 | if (file.createNewFile()) { 110 | if (mc.player != null) { 111 | mc.player.sendMessage( 112 | Text.literal("§8<" + PathSeekerUtil.randomColorCode() + "§o✨§r§8> §7Created " + file.getName() + " in meteor-client folder.") 113 | ); 114 | MutableText msg = Text.literal("§8<" + PathSeekerUtil.randomColorCode() + "§o✨§r§8> §7Click §2§lhere §r§7to open the file."); 115 | Style style = Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, file.getAbsolutePath())); 116 | 117 | MutableText txt = msg.setStyle(style); 118 | mc.player.sendMessage(txt); 119 | } 120 | return true; 121 | } 122 | } catch (Exception err) { 123 | PathSeeker.LOG.error("[PathSeeker] Error creating" + file.getAbsolutePath() + "! - Why:\n" + err); 124 | } 125 | } else return true; 126 | 127 | return false; 128 | } 129 | 130 | public static void openFile(MinecraftClient ignoredMc, String fileName) { 131 | File file = FabricLoader.getInstance().getGameDir().resolve(fileName).toFile(); 132 | 133 | if (Desktop.isDesktopSupported()) { 134 | EventQueue.invokeLater(() -> { 135 | try { 136 | Desktop.getDesktop().open(file); 137 | } catch (Exception err) { 138 | PathSeeker.LOG.error("[PathSeeker] Failed to open " + file.getAbsolutePath() + "! - Why:\n" + err); 139 | } 140 | }); 141 | } else { 142 | PathSeeker.LOG.error("[PathSeeker] Desktop operations not supported."); 143 | } 144 | } 145 | 146 | public static int firework(MinecraftClient mc) { 147 | int elytraSwapSlot = -1; 148 | 149 | if (!mc.player.getInventory().getArmorStack(2).isOf(Items.ELYTRA)) { 150 | FindItemResult itemResult = InvUtils.findInHotbar(Items.ELYTRA); 151 | if (!itemResult.found()) { 152 | return -1; 153 | } else { 154 | elytraSwapSlot = itemResult.slot(); 155 | InvUtils.swap(itemResult.slot(), true); 156 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 157 | InvUtils.swapBack(); 158 | mc.getNetworkHandler().sendPacket(new ClientCommandC2SPacket(mc.player, ClientCommandC2SPacket.Mode.START_FALL_FLYING)); 159 | } 160 | } 161 | 162 | FindItemResult itemResult = InvUtils.findInHotbar(Items.FIREWORK_ROCKET); 163 | if (!itemResult.found()) return -1; 164 | 165 | if (itemResult.isOffhand()) { 166 | mc.interactionManager.interactItem(mc.player, Hand.OFF_HAND); 167 | mc.player.swingHand(Hand.OFF_HAND); 168 | } else { 169 | InvUtils.swap(itemResult.slot(), true); 170 | mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND); 171 | mc.player.swingHand(Hand.MAIN_HAND); 172 | InvUtils.swapBack(); 173 | } 174 | 175 | return elytraSwapSlot != -1 ? elytraSwapSlot : 200; 176 | } 177 | 178 | public static void sendWebhook(String webhookURL, String title, String message, String pingID, String playerName) { 179 | String json = ""; 180 | json += "{\"embeds\": [{" 181 | + "\"title\": \"" + title + "\"," 182 | + "\"description\": \"" + message + "\"," 183 | + "\"color\": 15258703," 184 | + "\"footer\": {" 185 | + "\"text\": \"From: " + playerName + "\"}" 186 | + "}]}"; 187 | sendWebhook(webhookURL, json, pingID); 188 | } 189 | 190 | public static void sendWebhook(String webhookURL, String jsonObject, String pingID) { 191 | sendRequest(webhookURL, jsonObject); 192 | 193 | if (pingID != null) { 194 | jsonObject = "{\"content\": \"<@" + pingID + ">\"}"; 195 | sendRequest(webhookURL, jsonObject); 196 | } 197 | } 198 | 199 | public static Vec3d yawToDirection(double yaw) { 200 | yaw = yaw * Math.PI / 180; 201 | double x = -Math.sin(yaw); 202 | double z = Math.cos(yaw); 203 | return new Vec3d(x, 0, z); 204 | } 205 | 206 | public static double angleOnAxis(double yaw) { 207 | if (yaw < 0) yaw += 360; 208 | return Math.round(yaw / 45.0f) * 45; 209 | } 210 | 211 | public static double distancePointToDirection(Vec3d point, Vec3d direction, @Nullable Vec3d start) { 212 | if (start == null) start = Vec3d.ZERO; 213 | 214 | point = point.multiply(new Vec3d(1, 0, 1)); 215 | start = start.multiply(new Vec3d(1, 0, 1)); 216 | direction = direction.multiply(new Vec3d(1, 0, 1)); 217 | 218 | Vec3d directionVec = point.subtract(start); 219 | 220 | double projectionLength = directionVec.dotProduct(direction) / direction.lengthSquared(); 221 | Vec3d projection = direction.multiply(projectionLength); 222 | Vec3d perp = directionVec.subtract(projection); 223 | return perp.length(); 224 | } 225 | 226 | private static void sendRequest(String webhookURL, String json) { 227 | try { 228 | HttpClient client = HttpClient.newHttpClient(); 229 | HttpRequest request = HttpRequest.newBuilder() 230 | .uri(new URI(webhookURL)) 231 | .header("Content-Type", "application/json") 232 | .header("User-Agent", "Mozilla") 233 | .POST(HttpRequest.BodyPublishers.ofString(json)) 234 | .build(); 235 | 236 | client.send(request, HttpResponse.BodyHandlers.ofString()); 237 | } catch (Exception e) { 238 | e.printStackTrace(); 239 | } 240 | } 241 | 242 | public static void setPressed(KeyBinding key, boolean pressed) { 243 | key.setPressed(pressed); 244 | Input.setKeyState(key, pressed); 245 | } 246 | 247 | public static Vec3d positionInDirection(Vec3d pos, double yaw, double distance) { 248 | Vec3d offset = (new Vec3d(Math.sin(-yaw * Math.PI / 180), 0, Math.cos(-yaw * Math.PI / 180)).normalize()).multiply(distance); 249 | return pos.add(offset); 250 | } 251 | 252 | public String getFormattedLastSeen() { 253 | return formatDate(this.lastSeen); 254 | } 255 | 256 | public String getFormattedFirstSeen() { 257 | return formatDate(this.firstSeen); 258 | } 259 | 260 | public String getFormattedPlaytime() { 261 | return formatPlaytime(this.playtime); 262 | } 263 | 264 | public void updateTimeInfo(String lastSeen, String firstSeen, long playtime) { 265 | this.lastSeen = lastSeen; 266 | this.firstSeen = firstSeen; 267 | this.playtime = playtime; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/MobGearESP.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.render; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 5 | import meteordevelopment.meteorclient.renderer.ShapeMode; 6 | import meteordevelopment.meteorclient.settings.*; 7 | import meteordevelopment.meteorclient.systems.modules.Module; 8 | import meteordevelopment.meteorclient.utils.entity.EntityUtils; 9 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 10 | import meteordevelopment.meteorclient.utils.player.PlayerUtils; 11 | import meteordevelopment.meteorclient.utils.render.RenderUtils; 12 | import meteordevelopment.meteorclient.utils.render.color.Color; 13 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 14 | import meteordevelopment.orbit.EventHandler; 15 | import net.minecraft.entity.Entity; 16 | import net.minecraft.entity.LivingEntity; 17 | import net.minecraft.item.Item; 18 | import net.minecraft.item.ItemStack; 19 | import net.minecraft.item.Items; 20 | import net.minecraft.text.Text; 21 | import net.minecraft.util.math.Box; 22 | import net.minecraft.util.math.MathHelper; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import java.util.*; 27 | 28 | public class MobGearESP extends Module { 29 | private static final Logger log = LoggerFactory.getLogger(MobGearESP.class); 30 | private final SettingGroup sgGeneral = this.settings.getDefaultGroup(); 31 | public final Setting shapeMode = sgGeneral.add(new EnumSetting.Builder() 32 | .name("shape-mode") 33 | .description("How the shapes are rendered.") 34 | .defaultValue(ShapeMode.Both) 35 | .build() 36 | ); 37 | public final Setting fillOpacity = sgGeneral.add(new DoubleSetting.Builder() 38 | .name("fill-opacity") 39 | .description("The opacity of the shape fill.") 40 | .defaultValue(0.3) 41 | .range(0, 1) 42 | .sliderMax(1) 43 | .build() 44 | ); 45 | public final Setting enchants = sgGeneral.add(new BoolSetting.Builder() 46 | .name("enforce-item-enchants") 47 | .description("Requires that armor and tools must be enchanted for module to detect.") 48 | .defaultValue(true) 49 | .build() 50 | ); 51 | private final SettingGroup sgColors = settings.createGroup("Colors"); 52 | public final Setting distance = sgColors.add(new BoolSetting.Builder() 53 | .name("distance-colors") 54 | .description("Changes the color of tracers depending on distance.") 55 | .defaultValue(false) 56 | .build() 57 | ); 58 | private final Setting monstersColor = sgColors.add(new ColorSetting.Builder() 59 | .name("monsters-color") 60 | .description("The mob's bounding box and tracer color.") 61 | .defaultValue(new SettingColor(255, 25, 25, 255)) 62 | .visible(() -> !distance.get()) 63 | .build() 64 | ); 65 | private final List defaultPlayerItems = new ArrayList<>(List.of( 66 | Items.DIAMOND_HELMET, 67 | Items.DIAMOND_CHESTPLATE, 68 | Items.DIAMOND_LEGGINGS, 69 | Items.DIAMOND_BOOTS, 70 | Items.NETHERITE_HELMET, 71 | Items.NETHERITE_CHESTPLATE, 72 | Items.NETHERITE_LEGGINGS, 73 | Items.NETHERITE_BOOTS, 74 | Items.ELYTRA, 75 | Items.DIAMOND_SWORD, 76 | Items.DIAMOND_AXE, 77 | Items.DIAMOND_PICKAXE, 78 | Items.DIAMOND_SHOVEL, 79 | Items.DIAMOND_HOE, 80 | Items.NETHERITE_SWORD, 81 | Items.NETHERITE_AXE, 82 | Items.NETHERITE_PICKAXE, 83 | Items.NETHERITE_SHOVEL, 84 | Items.NETHERITE_HOE, 85 | Items.ENCHANTED_GOLDEN_APPLE, 86 | Items.END_CRYSTAL, 87 | Items.ENDER_CHEST, 88 | Items.TOTEM_OF_UNDYING, 89 | Items.EXPERIENCE_BOTTLE, 90 | Items.SHULKER_BOX, 91 | Items.CHORUS_FRUIT 92 | )); 93 | private final Setting fadeDistance = sgGeneral.add(new DoubleSetting.Builder() 94 | .name("fade-distance") 95 | .description("The distance from an entity where the color begins to fade.") 96 | .defaultValue(3) 97 | .min(0) 98 | .sliderMax(12) 99 | .build() 100 | ); 101 | private final Setting> items = sgGeneral.add(new ItemListSetting.Builder() 102 | .name("item-checker") 103 | .description("Player-like items to check for on mob") 104 | .defaultValue(defaultPlayerItems) 105 | .build() 106 | ); 107 | private final Setting chatFeedback = sgGeneral.add(new BoolSetting.Builder() 108 | .name("Chat feedback") 109 | .description("Display info about mobs holding gear in chat") 110 | .defaultValue(true) 111 | .build() 112 | ); 113 | private final Setting coordsInChat = sgGeneral.add(new BoolSetting.Builder() 114 | .name("Display coords in chat") 115 | .description("Display coords of a detected mob") 116 | .visible(chatFeedback::get) 117 | .defaultValue(true) 118 | .build() 119 | ); 120 | private final Setting itemsInChat = sgGeneral.add(new BoolSetting.Builder() 121 | .name("Display found items in chat") 122 | .description("Display items detected by mod in chat") 123 | .defaultValue(true) 124 | .build() 125 | ); 126 | private final Setting tracers = sgGeneral.add(new BoolSetting.Builder() 127 | .name("Tracers") 128 | .description("Add tracers to mobs detected") 129 | .defaultValue(true) 130 | .build() 131 | ); 132 | private final Color lineColor = new Color(); 133 | private final Color sideColor = new Color(); 134 | private final Color baseColor = new Color(); 135 | private final Set scannedEntities = Collections.synchronizedSet(new HashSet<>()); 136 | private int count; 137 | 138 | public MobGearESP() { 139 | super(PathSeeker.Render, "MobGearESP", "ESP Module that highlights mobs likely wearing player gear."); 140 | } 141 | 142 | @EventHandler 143 | private void onRender3D(Render3DEvent event) { 144 | count = 0; 145 | assert mc.world != null; 146 | for (Entity entity : mc.world.getEntities()) { 147 | if (!(entity instanceof LivingEntity livingEntity)) continue; 148 | if (shouldSkip(livingEntity)) continue; 149 | if (!scannedEntities.contains(entity)) { // send chat msg if we haven't scanned mob before 150 | StringBuilder message = new StringBuilder(entity.getType().getName().getString() + " found most likely wearing player gear"); 151 | if (coordsInChat.get()) 152 | message.append(" at ").append(entity.getBlockX()).append(", ").append(entity.getBlockY()).append(", ").append(entity.getBlockZ()); 153 | if (itemsInChat.get()) { 154 | ArrayList playerItems = getPlayerItems(livingEntity); 155 | message.append(" holding "); 156 | for (Item item : playerItems) { 157 | message.append(item.getTranslationKey().split("\\.")[2]).append(", "); 158 | } 159 | message.setLength(message.length() - 2); // chop off ", " from end of chat message 160 | } 161 | ChatUtils.sendMsg(Text.of(message.toString())); 162 | } 163 | scannedEntities.add(entity); 164 | drawBoundingBox(event, entity); 165 | if (tracers.get()) drawTracer(event, entity); 166 | count++; 167 | } 168 | } 169 | 170 | @Override 171 | public void onActivate() { 172 | scannedEntities.clear(); 173 | } 174 | 175 | @Override 176 | public void onDeactivate() { 177 | scannedEntities.clear(); 178 | } 179 | 180 | private void drawBoundingBox(Render3DEvent event, Entity entity) { 181 | Color color = getColor(entity); 182 | if (color != null) { 183 | lineColor.set(color); 184 | sideColor.set(color).a((int) (sideColor.a * fillOpacity.get())); 185 | } 186 | 187 | double x = MathHelper.lerp(event.tickDelta, entity.lastRenderX, entity.getX()) - entity.getX(); 188 | double y = MathHelper.lerp(event.tickDelta, entity.lastRenderY, entity.getY()) - entity.getY(); 189 | double z = MathHelper.lerp(event.tickDelta, entity.lastRenderZ, entity.getZ()) - entity.getZ(); 190 | Box box = entity.getBoundingBox(); 191 | event.renderer.box(x + box.minX, y + box.minY, z + box.minZ, x + box.maxX, y + box.maxY, z + box.maxZ, sideColor, lineColor, shapeMode.get(), 0); 192 | } 193 | 194 | private void drawTracer(Render3DEvent event, Entity entity) { 195 | if (mc.options.hudHidden) return; 196 | Color color = monstersColor.get(); 197 | double x = entity.prevX + (entity.getX() - entity.prevX) * event.tickDelta; 198 | double y = entity.prevY + (entity.getY() - entity.prevY) * event.tickDelta; 199 | double z = entity.prevZ + (entity.getZ() - entity.prevZ) * event.tickDelta; 200 | double height = entity.getBoundingBox().maxY - entity.getBoundingBox().minY; 201 | y += height / 2; // target the body of entity 202 | event.renderer.line(RenderUtils.center.x, RenderUtils.center.y, RenderUtils.center.z, x, y, z, color); 203 | } 204 | 205 | // Utils 206 | 207 | // checks a mob for any player items and returns a list of them 208 | private ArrayList getPlayerItems(LivingEntity livingEntity) { 209 | ArrayList playerItems = new ArrayList<>(); 210 | for (ItemStack item : livingEntity.getArmorItems()) { 211 | // if we require enchants, the item is enchantable, and it has no enchants 212 | if (enchants.get() && item.isEnchantable() && item.getEnchantments().isEmpty()) continue; 213 | if (items.get().contains(item.getItem())) playerItems.add(item.getItem()); 214 | 215 | } 216 | // check held items 217 | for (ItemStack item : livingEntity.getHandItems()) { 218 | if (enchants.get() && item.isEnchantable() && item.getEnchantments().isEmpty()) continue; 219 | if (items.get().contains(item.getItem())) playerItems.add(item.getItem()); 220 | } 221 | return playerItems; 222 | } 223 | 224 | public boolean shouldSkip(LivingEntity entity) { 225 | if (entity.isPlayer()) return true; 226 | ArrayList playerItems = getPlayerItems(entity); 227 | if (entity == mc.cameraEntity && mc.options.getPerspective().isFirstPerson()) return true; 228 | return playerItems.isEmpty() || !EntityUtils.isInRenderDistance(entity); 229 | } 230 | 231 | public Color getColor(Entity entity) { 232 | double alpha = getFadeAlpha(entity); 233 | if (alpha == 0) return null; 234 | Color color = monstersColor.get(); 235 | return baseColor.set(color.r, color.g, color.b, (int) (color.a * alpha)); 236 | } 237 | 238 | private double getFadeAlpha(Entity entity) { 239 | double dist = PlayerUtils.squaredDistanceToCamera(entity.getX() + entity.getWidth() / 2, entity.getY() + entity.getEyeHeight(entity.getPose()), entity.getZ() + entity.getWidth() / 2); 240 | double fadeDist = Math.pow(fadeDistance.get(), 2); 241 | double alpha = 1; 242 | if (dist <= fadeDist * fadeDist) alpha = (float) (Math.sqrt(dist) / fadeDist); 243 | if (alpha <= 0.075) alpha = 0; 244 | return alpha; 245 | } 246 | 247 | @Override 248 | public String getInfoString() { 249 | return Integer.toString(count); 250 | } 251 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/EntityClusterESP.java: -------------------------------------------------------------------------------- 1 | /** 2 | * ItemESP 3 | * ======= 4 | * - Written by Nataani 5 | * - Pulled from the Meteorite module. 6 | * This module highlights the center-point of entity clusters. 7 | * You can configure a list of entities to consider to highlight, 8 | * customize the box appearance (fill opacity, outline, etc.), 9 | * and designate a range for the module to consider in its calculations. 10 | * The render box method was adapted from Etlian's ActivatedSpawnerDetector. 11 | * The list GUI and csv methods were adapted from Meteor's Stashfinder module.g 12 | * This module is particularly useful for identifying stacked storage vehicles like Minecart with Chest. 13 | */ 14 | 15 | package dev.journey.PathSeeker.modules.render; 16 | 17 | import com.google.common.reflect.TypeToken; 18 | import com.google.gson.Gson; 19 | import com.google.gson.GsonBuilder; 20 | import dev.journey.PathSeeker.PathSeeker; 21 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 22 | import meteordevelopment.meteorclient.events.world.TickEvent; 23 | import meteordevelopment.meteorclient.gui.GuiTheme; 24 | import meteordevelopment.meteorclient.gui.widgets.WWidget; 25 | import meteordevelopment.meteorclient.gui.widgets.containers.WTable; 26 | import meteordevelopment.meteorclient.gui.widgets.containers.WVerticalList; 27 | import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; 28 | import meteordevelopment.meteorclient.gui.widgets.pressable.WMinus; 29 | import meteordevelopment.meteorclient.pathing.PathManagers; 30 | import meteordevelopment.meteorclient.renderer.ShapeMode; 31 | import meteordevelopment.meteorclient.settings.*; 32 | import meteordevelopment.meteorclient.systems.modules.Module; 33 | import meteordevelopment.meteorclient.utils.Utils; 34 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 35 | import meteordevelopment.meteorclient.utils.render.MeteorToast; 36 | import meteordevelopment.meteorclient.utils.render.color.Color; 37 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 38 | import meteordevelopment.orbit.EventHandler; 39 | import net.fabricmc.loader.api.FabricLoader; 40 | import net.minecraft.entity.Entity; 41 | import net.minecraft.entity.EntityType; 42 | import net.minecraft.item.Items; 43 | import net.minecraft.text.Text; 44 | import net.minecraft.util.math.BlockPos; 45 | import net.minecraft.util.math.Vec3d; 46 | 47 | import java.io.*; 48 | import java.util.*; 49 | 50 | public class EntityClusterESP extends Module { 51 | public static final String MOD_ID = "PathSeeker"; 52 | public static final File FOLDER = FabricLoader.getInstance().getGameDir().resolve(MOD_ID).toFile(); 53 | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); 54 | 55 | 56 | private final SettingGroup sgGeneral = settings.getDefaultGroup(); 57 | private final SettingGroup sgRender = settings.createGroup("Render"); 58 | 59 | private final Setting>> entities = sgGeneral.add(new EntityTypeListSetting.Builder() 60 | .name("entities") 61 | .description("Select specific entities.") 62 | .defaultValue(EntityType.CHEST_MINECART) 63 | .build() 64 | ); 65 | 66 | private final Setting displayCoords = sgGeneral.add(new BoolSetting.Builder() 67 | .name("display-coords") 68 | .defaultValue(true) 69 | .build() 70 | ); 71 | 72 | private final Setting notifications = sgGeneral.add(new BoolSetting.Builder() 73 | .name("notifications") 74 | .defaultValue(true) 75 | .build() 76 | ); 77 | 78 | private final Setting minEntities = sgGeneral.add(new IntSetting.Builder() 79 | .name("number-of-entities") 80 | .defaultValue(5) 81 | .min(2) 82 | .build() 83 | ); 84 | 85 | private final Setting range = sgGeneral.add(new DoubleSetting.Builder() 86 | .name("block-range") 87 | .defaultValue(10.0) 88 | .min(1) 89 | .sliderMax(32) 90 | .build() 91 | ); 92 | 93 | private final Setting shapeMode = sgRender.add(new EnumSetting.Builder() 94 | .name("shape-mode") 95 | .defaultValue(ShapeMode.Both) 96 | .build() 97 | ); 98 | 99 | private final Setting sideColorSetting = sgRender.add(new ColorSetting.Builder() 100 | .name("side-color") 101 | .defaultValue(new SettingColor(0, 255, 255, 50)) 102 | .build() 103 | ); 104 | 105 | private final Setting lineColorSetting = sgRender.add(new ColorSetting.Builder() 106 | .name("line-color") 107 | .defaultValue(new SettingColor(0, 255, 255, 255)) 108 | .build() 109 | ); 110 | 111 | private final List clusters = new ArrayList<>(); 112 | private final Set knownCenters = new HashSet<>(); 113 | 114 | public EntityClusterESP() { 115 | super(PathSeeker.Render, "entity-cluster-ESP", "Highlights and logs clusters of chosen entities."); 116 | } 117 | 118 | @Override 119 | public void onActivate() { 120 | clusters.clear(); 121 | knownCenters.clear(); 122 | load(); 123 | } 124 | 125 | @Override 126 | public void onDeactivate() { 127 | clusters.clear(); 128 | knownCenters.clear(); 129 | } 130 | 131 | @EventHandler 132 | private void onTick(TickEvent.Post event) { 133 | if (mc.world == null || mc.player == null) return; 134 | 135 | List chosen = new ArrayList<>(); 136 | for (Entity e : mc.world.getEntities()) { 137 | if (entities.get().contains(e.getType())) { 138 | chosen.add(e); 139 | } 140 | } 141 | 142 | if (!chosen.isEmpty()) { 143 | checkForClusters(chosen); 144 | } 145 | } 146 | 147 | 148 | @EventHandler 149 | private void onRender(Render3DEvent event) { 150 | Color sideColor = sideColorSetting.get(); 151 | Color lineColor = lineColorSetting.get(); 152 | double half = range.get() / 2.0; 153 | for (Cluster c : clusters) { 154 | if (!knownCenters.contains(c.centerPos)) continue; 155 | double x1 = c.centerPos.getX() - half; 156 | double y1 = c.centerPos.getY() - half; 157 | double z1 = c.centerPos.getZ() - half; 158 | double x2 = c.centerPos.getX() + half; 159 | double y2 = c.centerPos.getY() + half; 160 | double z2 = c.centerPos.getZ() + half; 161 | event.renderer.box(x1, y1, z1, x2, y2, z2, sideColor, lineColor, shapeMode.get(), 0); 162 | } 163 | } 164 | 165 | private void checkForClusters(List vehicles) { 166 | double r = range.get(); 167 | int needed = minEntities.get(); 168 | for (int i = 0; i < vehicles.size(); i++) { 169 | Entity primary = vehicles.get(i); 170 | List cluster = new ArrayList<>(); 171 | cluster.add(primary.getPos()); 172 | for (int j = 0; j < vehicles.size(); j++) { 173 | if (j == i) continue; 174 | Entity other = vehicles.get(j); 175 | if (primary.squaredDistanceTo(other) <= r * r) { 176 | cluster.add(other.getPos()); 177 | } 178 | } 179 | if (cluster.size() >= needed) { 180 | Vec3d center = averagePos(cluster); 181 | int cx = (int) center.x; 182 | int cy = (int) center.y; 183 | int cz = (int) center.z; 184 | BlockPos centerPos = new BlockPos(cx, cy, cz); 185 | if (!knownCenters.contains(centerPos)) { 186 | knownCenters.add(centerPos); 187 | int size = cluster.size(); 188 | clusters.add(new Cluster(cx, cy, cz, size)); 189 | if (notifications.get()) { 190 | mc.getToastManager().add(new MeteorToast(Items.CHEST_MINECART, "EntityClusterESP", "Cluster of " + size + " @ " + centerPos)); 191 | ChatUtils.sendMsg(Text.of("Entity cluster of " + size + " found at " + centerPos)); 192 | } 193 | if (displayCoords.get()) { 194 | ChatUtils.info("EntityClusterESP", "Cluster of " + size + " found at " + centerPos); 195 | } 196 | saveCsv(); 197 | saveJson(); 198 | } 199 | } 200 | } 201 | } 202 | 203 | private Vec3d averagePos(List positions) { 204 | double sx = 0, sy = 0, sz = 0; 205 | for (Vec3d p : positions) { 206 | sx += p.x; 207 | sy += p.y; 208 | sz += p.z; 209 | } 210 | return new Vec3d(sx / positions.size(), sy / positions.size(), sz / positions.size()); 211 | } 212 | 213 | @Override 214 | public WWidget getWidget(GuiTheme theme) { 215 | clusters.sort(Comparator.comparingInt(a -> a.y)); 216 | WVerticalList list = theme.verticalList(); 217 | WButton clear = list.add(theme.button("Clear")).widget(); 218 | WTable table = new WTable(); 219 | if (!clusters.isEmpty()) list.add(table); 220 | clear.action = () -> { 221 | clusters.clear(); 222 | knownCenters.clear(); 223 | table.clear(); 224 | saveJson(); 225 | saveCsv(); 226 | }; 227 | fillTable(theme, table); 228 | return list; 229 | } 230 | 231 | private void fillTable(GuiTheme theme, WTable table) { 232 | for (Cluster c : clusters) { 233 | table.add(theme.label("Pos: " + c.x + ", " + c.y + ", " + c.z + " (Cnt: " + c.count + ")")); 234 | WButton gotoBtn = table.add(theme.button("Goto")).widget(); 235 | gotoBtn.action = () -> { 236 | PathManagers.get().moveTo(new BlockPos(c.x, c.y, c.z), true); 237 | }; 238 | WMinus delete = table.add(theme.minus()).widget(); 239 | delete.action = () -> { 240 | clusters.remove(c); 241 | knownCenters.remove(c.centerPos); 242 | table.clear(); 243 | fillTable(theme, table); 244 | saveJson(); 245 | saveCsv(); 246 | }; 247 | table.row(); 248 | } 249 | } 250 | 251 | private File getCsvFile() { 252 | return new File(new File(new File(EntityClusterESP.FOLDER, "EntityClusterESP"), Utils.getFileWorldName()), "EntityClusterESP.csv"); 253 | } 254 | 255 | private File getJsonFile() { 256 | return new File(new File(new File(EntityClusterESP.FOLDER, "EntityClusterESP"), Utils.getFileWorldName()), "EntityClusterESP.json"); 257 | } 258 | 259 | private void load() { 260 | File file = getJsonFile(); 261 | if (file.exists()) { 262 | try { 263 | FileReader reader = new FileReader(file); 264 | List data = GSON.fromJson(reader, new TypeToken>() { 265 | }.getType()); 266 | reader.close(); 267 | if (data != null) { 268 | clusters.addAll(data); 269 | for (Cluster c : data) { 270 | knownCenters.add(c.centerPos); 271 | } 272 | } 273 | } catch (Exception ignored) { 274 | } 275 | } else { 276 | file = getCsvFile(); 277 | if (file.exists()) { 278 | try { 279 | BufferedReader reader = new BufferedReader(new FileReader(file)); 280 | reader.readLine(); 281 | String line; 282 | while ((line = reader.readLine()) != null) { 283 | String[] values = line.split(","); 284 | Cluster cluster = new Cluster( 285 | Integer.parseInt(values[0]), 286 | Integer.parseInt(values[1]), 287 | Integer.parseInt(values[2]), 288 | Integer.parseInt(values[3]) 289 | ); 290 | clusters.add(cluster); 291 | knownCenters.add(cluster.centerPos); 292 | } 293 | reader.close(); 294 | } catch (Exception ignored) { 295 | } 296 | } 297 | } 298 | } 299 | 300 | private void saveCsv() { 301 | try { 302 | File file = getCsvFile(); 303 | file.getParentFile().mkdirs(); 304 | Writer writer = new FileWriter(file); 305 | writer.write("X,Y,Z,Count\n"); 306 | for (Cluster c : clusters) { 307 | writer.write(c.x + "," + c.y + "," + c.z + "," + c.count + "\n"); 308 | } 309 | writer.close(); 310 | } catch (IOException ignored) { 311 | } 312 | } 313 | 314 | private void saveJson() { 315 | try { 316 | File file = getJsonFile(); 317 | file.getParentFile().mkdirs(); 318 | Writer writer = new FileWriter(file); 319 | GSON.toJson(clusters, writer); 320 | writer.close(); 321 | } catch (IOException ignored) { 322 | } 323 | } 324 | 325 | private static class Cluster { 326 | public int x, y, z, count; 327 | public BlockPos centerPos; 328 | 329 | public Cluster(int x, int y, int z, int count) { 330 | this.x = x; 331 | this.y = y; 332 | this.z = z; 333 | this.count = count; 334 | this.centerPos = new BlockPos(x, y, z); 335 | } 336 | 337 | @Override 338 | public boolean equals(Object o) { 339 | if (this == o) return true; 340 | if (o == null || getClass() != o.getClass()) return false; 341 | Cluster cluster = (Cluster) o; 342 | return x == cluster.x && y == cluster.y && z == cluster.z; 343 | } 344 | 345 | @Override 346 | public int hashCode() { 347 | return Objects.hash(x, y, z); 348 | } 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/main/java/dev/journey/PathSeeker/modules/render/DroppedItemESP.java: -------------------------------------------------------------------------------- 1 | package dev.journey.PathSeeker.modules.render; 2 | 3 | import dev.journey.PathSeeker.PathSeeker; 4 | import meteordevelopment.meteorclient.events.render.Render3DEvent; 5 | import meteordevelopment.meteorclient.events.world.TickEvent; 6 | import meteordevelopment.meteorclient.renderer.ShapeMode; 7 | import meteordevelopment.meteorclient.settings.*; 8 | import meteordevelopment.meteorclient.systems.modules.Module; 9 | import meteordevelopment.meteorclient.utils.player.ChatUtils; 10 | import meteordevelopment.meteorclient.utils.player.PlayerUtils; 11 | import meteordevelopment.meteorclient.utils.render.RenderUtils; 12 | import meteordevelopment.meteorclient.utils.render.color.Color; 13 | import meteordevelopment.meteorclient.utils.render.color.SettingColor; 14 | import meteordevelopment.orbit.EventHandler; 15 | import net.minecraft.enchantment.Enchantment; 16 | import net.minecraft.enchantment.Enchantments; 17 | import net.minecraft.entity.Entity; 18 | import net.minecraft.entity.ItemEntity; 19 | import net.minecraft.item.*; 20 | import net.minecraft.registry.RegistryKey; 21 | import net.minecraft.text.Text; 22 | import net.minecraft.util.math.Box; 23 | import net.minecraft.util.math.MathHelper; 24 | 25 | import java.util.*; 26 | 27 | public class DroppedItemESP extends Module { 28 | private final SettingGroup sgGeneral = this.settings.getDefaultGroup(); 29 | public final Setting shapeMode = sgGeneral.add(new EnumSetting.Builder() 30 | .name("shape-mode") 31 | .description("How the shapes are rendered.") 32 | .defaultValue(ShapeMode.Both) 33 | .build() 34 | ); 35 | public final Setting fillOpacity = sgGeneral.add(new DoubleSetting.Builder() 36 | .name("fill-opacity") 37 | .description("The opacity of the shape fill.") 38 | .defaultValue(0.3) 39 | .range(0, 1) 40 | .sliderMax(1) 41 | .build() 42 | ); 43 | public final Setting enchants = sgGeneral.add(new BoolSetting.Builder() 44 | .name("enforce-item-enchants") 45 | .description("Requires that armor and tools must be enchanted for module to detect.") 46 | .defaultValue(true) 47 | .build() 48 | ); 49 | public final Setting certainenchants = sgGeneral.add(new BoolSetting.Builder() 50 | .name("find-certain-item-enchants") 51 | .description("Requires that armor and tools must be enchanted with these enchants.") 52 | .defaultValue(false) 53 | .visible(enchants::get) 54 | .build() 55 | ); 56 | private final Setting>> toolenchants = sgGeneral.add(new EnchantmentListSetting.Builder() 57 | .name("Mining Tool Enchants") 58 | .description("List of enchantments required.") 59 | .visible(() -> enchants.get() && certainenchants.get()) 60 | .defaultValue(Enchantments.EFFICIENCY, Enchantments.UNBREAKING, Enchantments.MENDING) 61 | .build()); 62 | private final Setting>> swordenchants = sgGeneral.add(new EnchantmentListSetting.Builder() 63 | .name("Sword Enchants") 64 | .description("List of enchantments required.") 65 | .visible(() -> enchants.get() && certainenchants.get()) 66 | .defaultValue(Enchantments.UNBREAKING, Enchantments.MENDING) 67 | .build()); 68 | private final Setting>> armorenchants = sgGeneral.add(new EnchantmentListSetting.Builder() 69 | .name("Armor Enchants") 70 | .description("List of enchantments required.") 71 | .visible(() -> enchants.get() && certainenchants.get()) 72 | .defaultValue(Enchantments.UNBREAKING, Enchantments.MENDING) 73 | .build()); 74 | private final Setting>> maceenchants = sgGeneral.add(new EnchantmentListSetting.Builder() 75 | .name("Mace Enchants") 76 | .description("List of enchantments required.") 77 | .visible(() -> enchants.get() && certainenchants.get()) 78 | .defaultValue(Enchantments.UNBREAKING, Enchantments.MENDING) 79 | .build()); 80 | private final Setting>> tridentenchants = sgGeneral.add(new EnchantmentListSetting.Builder() 81 | .name("Trident Enchants") 82 | .description("List of enchantments required.") 83 | .visible(() -> enchants.get() && certainenchants.get()) 84 | .defaultValue(Enchantments.UNBREAKING, Enchantments.MENDING) 85 | .build()); 86 | private final SettingGroup sgColors = settings.createGroup("Colors"); 87 | public final Setting distance = sgColors.add(new BoolSetting.Builder() 88 | .name("distance-colors") 89 | .description("Changes the color of tracers depending on distance.") 90 | .defaultValue(true) 91 | .build() 92 | ); 93 | private final Setting distantColor = sgColors.add(new ColorSetting.Builder() 94 | .name("distant-color") 95 | .description("The item's bounding box and tracer color when you are far away.") 96 | .defaultValue(new SettingColor(25, 255, 255, 255)) 97 | .visible(distance::get) 98 | .build() 99 | ); 100 | public final Setting distanceInt = sgColors.add(new IntSetting.Builder() 101 | .name("distance-colors-threshold") 102 | .description("The max distance for colors to change.") 103 | .defaultValue(128) 104 | .min(1) 105 | .sliderRange(1, 1024) 106 | .visible(distance::get) 107 | .build() 108 | ); 109 | private final List defaultPlayerItems = new ArrayList<>(List.of( 110 | Items.DIAMOND_HELMET, 111 | Items.DIAMOND_CHESTPLATE, 112 | Items.DIAMOND_LEGGINGS, 113 | Items.DIAMOND_BOOTS, 114 | Items.NETHERITE_HELMET, 115 | Items.NETHERITE_CHESTPLATE, 116 | Items.NETHERITE_LEGGINGS, 117 | Items.NETHERITE_BOOTS, 118 | Items.ELYTRA, 119 | Items.MACE, 120 | Items.TRIDENT, 121 | Items.DIAMOND_SWORD, 122 | Items.DIAMOND_AXE, 123 | Items.DIAMOND_PICKAXE, 124 | Items.DIAMOND_SHOVEL, 125 | Items.DIAMOND_HOE, 126 | Items.NETHERITE_SWORD, 127 | Items.NETHERITE_AXE, 128 | Items.NETHERITE_PICKAXE, 129 | Items.NETHERITE_SHOVEL, 130 | Items.NETHERITE_HOE, 131 | Items.ENCHANTED_GOLDEN_APPLE, 132 | Items.END_CRYSTAL, 133 | Items.ENDER_CHEST, 134 | Items.TOTEM_OF_UNDYING, 135 | Items.EXPERIENCE_BOTTLE, 136 | Items.SHULKER_BOX, 137 | Items.RED_SHULKER_BOX, 138 | Items.ORANGE_SHULKER_BOX, 139 | Items.YELLOW_SHULKER_BOX, 140 | Items.LIME_SHULKER_BOX, 141 | Items.GREEN_SHULKER_BOX, 142 | Items.CYAN_SHULKER_BOX, 143 | Items.LIGHT_BLUE_SHULKER_BOX, 144 | Items.BLUE_SHULKER_BOX, 145 | Items.PURPLE_SHULKER_BOX, 146 | Items.MAGENTA_SHULKER_BOX, 147 | Items.PINK_SHULKER_BOX, 148 | Items.WHITE_SHULKER_BOX, 149 | Items.LIGHT_GRAY_SHULKER_BOX, 150 | Items.GRAY_SHULKER_BOX, 151 | Items.BROWN_SHULKER_BOX, 152 | Items.BLACK_SHULKER_BOX 153 | )); 154 | private final Setting fadeDistance = sgGeneral.add(new DoubleSetting.Builder() 155 | .name("fade-distance") 156 | .description("The distance from an entity where the color begins to fade.") 157 | .defaultValue(3) 158 | .min(0) 159 | .sliderMax(12) 160 | .build() 161 | ); 162 | private final Setting> items = sgGeneral.add(new ItemListSetting.Builder() 163 | .name("item-checker") 164 | .description("Items to check for.") 165 | .defaultValue(defaultPlayerItems) 166 | .build() 167 | ); 168 | private final Setting chatFeedback = sgGeneral.add(new BoolSetting.Builder() 169 | .name("Chat feedback") 170 | .description("Display info about items in chat") 171 | .defaultValue(true) 172 | .build() 173 | ); 174 | private final Setting coordsInChat = sgGeneral.add(new BoolSetting.Builder() 175 | .name("Display coords in chat") 176 | .description("Display coords of a detected item") 177 | .visible(chatFeedback::get) 178 | .defaultValue(true) 179 | .build() 180 | ); 181 | private final Setting tracers = sgGeneral.add(new BoolSetting.Builder() 182 | .name("Tracers") 183 | .description("Add tracers to item detected") 184 | .defaultValue(true) 185 | .build() 186 | ); 187 | private final Setting monstersColor = sgColors.add(new ColorSetting.Builder() 188 | .name("items-color") 189 | .description("The item's bounding box and tracer color.") 190 | .defaultValue(new SettingColor(255, 25, 255, 255)) 191 | .build() 192 | ); 193 | private final Color lineColor = new Color(); 194 | private final Color sideColor = new Color(); 195 | private final Color baseColor = new Color(); 196 | private final Set scannedEntities = Collections.synchronizedSet(new HashSet<>()); 197 | private int count; 198 | 199 | public DroppedItemESP() { 200 | super(PathSeeker.Render, "DroppedItemESP", "ESP Module that highlights only certain items."); 201 | } 202 | 203 | @EventHandler 204 | private void onRender3D(Render3DEvent event) { 205 | count = 0; 206 | for (Entity entity : mc.world.getEntities()) { 207 | if (!(entity instanceof ItemEntity itemEntity)) continue; 208 | if (shouldSkip(itemEntity)) continue; 209 | if (!scannedEntities.contains(entity)) { 210 | StringBuilder message = new StringBuilder(itemEntity.getStack().getItem().getName().getString() + " found "); 211 | if (coordsInChat.get()) 212 | message.append(" at ").append(entity.getBlockX()).append(", ").append(entity.getBlockY()).append(", ").append(entity.getBlockZ()); 213 | ChatUtils.sendMsg(Text.of(message.toString())); 214 | } 215 | scannedEntities.add(entity); 216 | drawBoundingBox(event, entity); 217 | if (tracers.get()) drawTracer(event, entity); 218 | count++; 219 | } 220 | } 221 | 222 | @Override 223 | public void onActivate() { 224 | scannedEntities.clear(); 225 | } 226 | 227 | @Override 228 | public void onDeactivate() { 229 | scannedEntities.clear(); 230 | } 231 | 232 | private void drawBoundingBox(Render3DEvent event, Entity entity) { 233 | Color color = getColor(entity); 234 | if (color != null) { 235 | lineColor.set(color); 236 | sideColor.set(color).a((int) (sideColor.a * fillOpacity.get())); 237 | } 238 | 239 | double x = MathHelper.lerp(event.tickDelta, entity.lastRenderX, entity.getX()) - entity.getX(); 240 | double y = MathHelper.lerp(event.tickDelta, entity.lastRenderY, entity.getY()) - entity.getY(); 241 | double z = MathHelper.lerp(event.tickDelta, entity.lastRenderZ, entity.getZ()) - entity.getZ(); 242 | Box box = entity.getBoundingBox(); 243 | event.renderer.box(x + box.minX, y + box.minY, z + box.minZ, x + box.maxX, y + box.maxY, z + box.maxZ, sideColor, lineColor, shapeMode.get(), 0); 244 | } 245 | 246 | private void drawTracer(Render3DEvent event, Entity entity) { 247 | if (mc.options.hudHidden) return; 248 | 249 | Color baseColor = monstersColor.get(); 250 | if (distance.get()) { 251 | baseColor = getOpposingColor(baseColor, entity); 252 | } 253 | 254 | double x = entity.prevX + (entity.getX() - entity.prevX) * event.tickDelta; 255 | double y = entity.prevY + (entity.getY() - entity.prevY) * event.tickDelta; 256 | double z = entity.prevZ + (entity.getZ() - entity.prevZ) * event.tickDelta; 257 | double height = entity.getBoundingBox().maxY - entity.getBoundingBox().minY; 258 | y += height / 2; 259 | 260 | event.renderer.line(RenderUtils.center.x, RenderUtils.center.y, RenderUtils.center.z, x, y, z, baseColor); 261 | } 262 | 263 | private Color getOpposingColor(Color c, Entity e) { 264 | Color interpolatedColor; 265 | Color oppositeColor = distantColor.get(); 266 | 267 | double distance = Math.sqrt(mc.player.squaredDistanceTo(e)); 268 | 269 | double maxDistance = distanceInt.get(); 270 | double percent = MathHelper.clamp(distance / maxDistance, 0, 1); 271 | 272 | int r = (int) (c.r + (oppositeColor.r - c.r) * percent); 273 | int g = (int) (c.g + (oppositeColor.g - c.g) * percent); 274 | int b = (int) (c.b + (oppositeColor.b - c.b) * percent); 275 | int a = c.a; 276 | 277 | interpolatedColor = new Color(r, g, b, a); 278 | return interpolatedColor; 279 | } 280 | 281 | public boolean shouldSkip(ItemEntity entity) { 282 | boolean skip = false; 283 | if (enchants.get()) { 284 | if (!certainenchants.get() && (entity.getStack().getItem() instanceof MiningToolItem || entity.getStack().getItem() instanceof ArmorItem || entity.getStack().getItem() instanceof SwordItem || entity.getStack().getItem() instanceof FishingRodItem || entity.getStack().getItem() instanceof FlintAndSteelItem || entity.getStack().getItem() instanceof MaceItem || entity.getStack().getItem() instanceof ShearsItem || entity.getStack().getItem() instanceof ShieldItem || entity.getStack().getItem() instanceof TridentItem) && entity.getStack().isEnchantable() && entity.getStack().getEnchantments().isEmpty()) 285 | skip = true; 286 | else if (certainenchants.get()) { 287 | if (entity.getStack().getItem() instanceof MiningToolItem) { 288 | skip = compareEnchants(entity, toolenchants); 289 | } else if (entity.getStack().getItem() instanceof SwordItem) { 290 | skip = compareEnchants(entity, swordenchants); 291 | } else if (entity.getStack().getItem() instanceof ArmorItem) { 292 | skip = compareEnchants(entity, armorenchants); 293 | } else if (entity.getStack().getItem() instanceof MaceItem) { 294 | skip = compareEnchants(entity, maceenchants); 295 | } else if (entity.getStack().getItem() instanceof TridentItem) { 296 | skip = compareEnchants(entity, tridentenchants); 297 | } 298 | } 299 | } 300 | if (!items.get().contains(entity.getStack().getItem())) skip = true; 301 | return skip; 302 | } 303 | 304 | private boolean compareEnchants(ItemEntity entity, Setting>> enchantsetting) { 305 | boolean skip = false; 306 | Set> itemenchants = new HashSet<>(); 307 | entity.getStack().getEnchantments().getEnchantments().forEach(enchantment -> { 308 | itemenchants.add(enchantment.getKey().get()); 309 | }); 310 | for (RegistryKey enchantKey : enchantsetting.get()) { 311 | if (!itemenchants.contains(enchantKey)) { 312 | skip = true; 313 | break; 314 | } 315 | } 316 | return skip; 317 | } 318 | 319 | public Color getColor(Entity entity) { 320 | double alpha = getFadeAlpha(entity); 321 | if (alpha == 0) return null; 322 | Color color = monstersColor.get(); 323 | if (distance.get()) { 324 | color = getOpposingColor(color, entity); 325 | } 326 | return baseColor.set(color.r, color.g, color.b, (int) (color.a * alpha)); 327 | } 328 | 329 | private double getFadeAlpha(Entity entity) { 330 | double dist = PlayerUtils.squaredDistanceToCamera(entity.getX() + entity.getWidth() / 2, entity.getY() + entity.getEyeHeight(entity.getPose()), entity.getZ() + entity.getWidth() / 2); 331 | double fadeDist = Math.pow(fadeDistance.get(), 2); 332 | double alpha = 1; 333 | if (dist <= fadeDist * fadeDist) alpha = (float) (Math.sqrt(dist) / fadeDist); 334 | if (alpha <= 0.075) alpha = 0; 335 | return alpha; 336 | } 337 | 338 | @Override 339 | public String getInfoString() { 340 | return Integer.toString(count); 341 | } 342 | 343 | @EventHandler 344 | private void onPreTick(TickEvent.Pre event) { 345 | if (mc.world != null) { 346 | Iterable entities = mc.world.getEntities(); 347 | scannedEntities.removeIf(entity -> { 348 | Set entitySet = new HashSet<>(); 349 | entities.forEach(entitySet::add); 350 | return !entitySet.contains(entity); 351 | }); 352 | } 353 | } 354 | } 355 | --------------------------------------------------------------------------------