Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.google.gson.GsonBuilder;
import meteordevelopment.meteorclient.MeteorClient;
import meteordevelopment.meteorclient.events.world.ChunkDataEvent;
import meteordevelopment.meteorclient.events.world.TickEvent;
import meteordevelopment.meteorclient.gui.GuiTheme;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.widgets.WWidget;
Expand All @@ -22,23 +23,44 @@
import meteordevelopment.meteorclient.systems.modules.Categories;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.utils.Utils;
import meteordevelopment.meteorclient.utils.misc.Keybind;
import meteordevelopment.meteorclient.utils.render.color.SettingColor;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.utils.render.MeteorToast;
import meteordevelopment.meteorclient.utils.render.RenderUtils;
import meteordevelopment.meteorclient.utils.misc.text.RunnableClickEvent;
import meteordevelopment.meteorclient.utils.player.ChatUtils;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.block.Block;
import net.minecraft.block.entity.*;
import net.minecraft.item.Items;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;

import java.io.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class StashFinder extends Module {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private final Map<ChunkPos, Vec3d> tracePositions = new HashMap<>();
private final Set<ChunkPos> disabledTracePositions = new HashSet<>();

private final SettingGroup sgGeneral = settings.getDefaultGroup();
private final SettingGroup sgRender = settings.createGroup("Render");

private final Setting<List<BlockEntityType<?>>> storageBlocks = sgGeneral.add(new StorageBlockListSetting.Builder()
.name("storage-blocks")
Expand All @@ -56,6 +78,13 @@ public class StashFinder extends Module {
.build()
);

private final Setting<List<Block>> blacklistedBlocks = sgGeneral.add(new BlockListSetting.Builder()
.name("blacklisted-support-blocks")
.description("Blocks that prevent counting a storage block entity when it sits on them.")
.defaultValue(new ArrayList<>())
.build()
);

private final Setting<Integer> minimumDistance = sgGeneral.add(new IntSetting.Builder()
.name("minimum-distance")
.description("The minimum distance you must be from spawn to record a certain chunk.")
Expand All @@ -80,6 +109,65 @@ public class StashFinder extends Module {
.build()
);

private final Setting<Boolean> renderTrace = sgRender.add(new BoolSetting.Builder()
.name("render-trace")
.description("Renders a tracer to the last found stash.")
.defaultValue(true)
.build()
);

private final Setting<SettingColor> traceColor = sgRender.add(new ColorSetting.Builder()
.name("trace-color")
.description("Color of the stash tracer.")
.defaultValue(new SettingColor(255, 215, 0, 255))
.visible(renderTrace::get)
.build()
);

private final Setting<Boolean> renderTraceColumn = sgRender.add(new BoolSetting.Builder()
.name("render-trace-column")
.description("Renders a vertical column at the center of traced chunks.")
.defaultValue(false)
.build()
);

private final Setting<SettingColor> traceColumnColor = sgRender.add(new ColorSetting.Builder()
.name("trace-column-color")
.description("Color of the stash tracer column.")
.defaultValue(new SettingColor(255, 215, 0, 100))
.visible(renderTraceColumn::get)
.build()
);

private final Setting<Integer> traceArrivalDistance = sgRender.add(new IntSetting.Builder()
.name("trace-arrival-distance")
.description("Hide the trace when you are this close to the stash.")
.defaultValue(16)
.min(1)
.sliderMin(1)
.sliderMax(50)
.visible(renderTrace::get)
.build()
);

private final Setting<Integer> traceMaxDistance = sgRender.add(new IntSetting.Builder()
.name("trace-max-distance")
.description("Hide the trace when you are farther than this distance from the stash.")
.defaultValue(2000)
.min(10)
.sliderMin(50)
.sliderMax(10000)
.visible(renderTrace::get)
.build()
);

private final Setting<Keybind> clearTracesBind = sgRender.add(new KeybindSetting.Builder()
.name("clear-traces-bind")
.description("Keybind to clear all stash traces.")
.defaultValue(Keybind.none())
.build()
);

public List<Chunk> chunks = new ArrayList<>();

public StashFinder() {
Expand All @@ -91,6 +179,13 @@ public void onActivate() {
load();
}

@EventHandler
private void onTick(TickEvent.Post event) {
if (!clearTracesBind.get().isPressed()) return;
tracePositions.clear();
disabledTracePositions.clear();
}

@EventHandler
private void onChunkData(ChunkDataEvent event) {
// Check the distance.
Expand All @@ -100,8 +195,17 @@ private void onChunkData(ChunkDataEvent event) {

Chunk chunk = new Chunk(event.chunk().getPos());

List<Block> blockBlacklist = blacklistedBlocks.get();

for (BlockEntity blockEntity : event.chunk().getBlockEntities().values()) {
if (!storageBlocks.get().contains(blockEntity.getType())) continue;
BlockEntityType<?> type = blockEntity.getType();

if (!storageBlocks.get().contains(type)) continue;

if (!blockBlacklist.isEmpty()) {
BlockPos below = blockEntity.getPos().down();
if (blockBlacklist.contains(event.chunk().getBlockState(below).getBlock())) continue;
}

if (blockEntity instanceof ChestBlockEntity) chunk.chests++;
else if (blockEntity instanceof BarrelBlockEntity) chunk.barrels++;
Expand All @@ -119,18 +223,23 @@ private void onChunkData(ChunkDataEvent event) {
if (i < 0) chunks.add(chunk);
else prevChunk = chunks.set(i, chunk);

double y = mc.player != null ? mc.player.getEyeY() : 0.0;
if (!disabledTracePositions.contains(chunk.chunkPos)) {
tracePositions.put(chunk.chunkPos, new Vec3d(chunk.x, y, chunk.z));
}

saveJson();
saveCsv();

if (sendNotifications.get() && (!chunk.equals(prevChunk) || !chunk.countsEqual(prevChunk))) {
switch (notificationMode.get()) {
case Chat -> info("Found stash at (highlight)%s(default), (highlight)%s(default).", chunk.x, chunk.z);
case Chat -> sendChatNotification(chunk);
case Toast -> {
MeteorToast toast = new MeteorToast.Builder(title).icon(Items.CHEST).text("Found Stash!").build();
mc.getToastManager().add(toast);
}
case Both -> {
info("Found stash at (highlight)%s(default), (highlight)%s(default).", chunk.x, chunk.z);
sendChatNotification(chunk);
MeteorToast toast = new MeteorToast.Builder(title).icon(Items.CHEST).text("Found Stash!").build();
mc.getToastManager().add(toast);
}
Expand All @@ -148,13 +257,23 @@ public WWidget getWidget(GuiTheme theme) {

// Clear
WButton clear = list.add(theme.button("Clear")).widget();
WButton clearTraces = list.add(theme.button("Clear Traces")).widget();

WTable table = new WTable();
if (!chunks.isEmpty()) list.add(table);

clear.action = () -> {
chunks.clear();
table.clear();
tracePositions.clear();
disabledTracePositions.clear();
};

clearTraces.action = () -> {
tracePositions.clear();
disabledTracePositions.clear();
table.clear();
fillTable(theme, table);
};

// Chunks
Expand All @@ -174,9 +293,25 @@ private void fillTable(GuiTheme theme, WTable table) {
WButton gotoBtn = table.add(theme.button("Goto")).widget();
gotoBtn.action = () -> PathManagers.get().moveTo(new BlockPos(chunk.x, 0, chunk.z), true);

WButton traceToggle = table.add(theme.button("Marker:")).widget();
updateMarkerButton(traceToggle, tracePositions.containsKey(chunk.chunkPos));
traceToggle.action = () -> {
if (tracePositions.containsKey(chunk.chunkPos)) {
tracePositions.remove(chunk.chunkPos);
disabledTracePositions.add(chunk.chunkPos);
} else {
double y = mc.player != null ? mc.player.getEyeY() : 0.0;
tracePositions.put(chunk.chunkPos, new Vec3d(chunk.x, y, chunk.z));
disabledTracePositions.remove(chunk.chunkPos);
}
updateMarkerButton(traceToggle, tracePositions.containsKey(chunk.chunkPos));
};

WMinus delete = table.add(theme.minus()).widget();
delete.action = () -> {
if (chunks.remove(chunk)) {
tracePositions.remove(chunk.chunkPos);
disabledTracePositions.remove(chunk.chunkPos);
table.clear();
fillTable(theme, table);

Expand Down Expand Up @@ -277,6 +412,69 @@ public String getInfoString() {
return String.valueOf(chunks.size());
}

private void sendChatNotification(Chunk chunk) {
TextColor meteorPurple = TextColor.fromRgb(MeteorClient.ADDON.color.getPacked());
MutableText coords = Text.literal(chunk.x + ", " + chunk.z)
.setStyle(Style.EMPTY
.withColor(meteorPurple)
.withFormatting(Formatting.UNDERLINE)
.withHoverEvent(new HoverEvent.ShowText(Text.literal("Path to stash")))
.withClickEvent(new RunnableClickEvent(() -> PathManagers.get().moveTo(new BlockPos(chunk.x, 0, chunk.z), true))));

MutableText message = Text.literal("Found stash at ")
.formatted(Formatting.GRAY)
.append(Text.literal("[").formatted(Formatting.GRAY))
.append(coords)
.append(Text.literal("]").formatted(Formatting.GRAY))
.append(Text.literal(".").formatted(Formatting.GRAY));

ChatUtils.sendMsg(message);
}

@EventHandler
private void onRender3D(Render3DEvent event) {
if (tracePositions.isEmpty() || mc.player == null) return;

double eyeY = mc.player.getEyeY();
double playerX = mc.player.getX();
double playerZ = mc.player.getZ();
int bottomY = mc.world.getBottomY();
int topY = bottomY + mc.world.getDimension().height();

tracePositions.entrySet().removeIf(entry -> {
Vec3d pos = entry.getValue();
double horizontalDist = Math.hypot(pos.x - playerX, pos.z - playerZ);
return horizontalDist <= traceArrivalDistance.get();
});

if (!renderTrace.get() && !renderTraceColumn.get()) return;

for (Vec3d pos : tracePositions.values()) {
double horizontalDist = Math.hypot(pos.x - playerX, pos.z - playerZ);
if (horizontalDist > traceMaxDistance.get()) continue;

if (renderTrace.get()) {
event.renderer.line(RenderUtils.center.x, RenderUtils.center.y, RenderUtils.center.z, pos.x, eyeY, pos.z, traceColor.get());
}

if (renderTraceColumn.get()) {
double x1 = pos.x - 0.5;
double x2 = pos.x + 0.5;
double z1 = pos.z - 0.5;
double z2 = pos.z + 0.5;

event.renderer.line(x1, bottomY, z1, x1, topY, z1, traceColumnColor.get());
event.renderer.line(x1, bottomY, z2, x1, topY, z2, traceColumnColor.get());
event.renderer.line(x2, bottomY, z1, x2, topY, z1, traceColumnColor.get());
event.renderer.line(x2, bottomY, z2, x2, topY, z2, traceColumnColor.get());
}
}
}

private void updateMarkerButton(WButton button, boolean enabled) {
button.set(enabled ? "Marker: On" : "Marker: Off");
}

public enum Mode {
Chat,
Toast,
Expand Down