Compare commits

...

2 Commits

Author SHA1 Message Date
burkkyy 89a49e2d58 disconnect if healthcheck fails
build / build (push) Successful in 1m53s
2026-07-04 18:22:56 -07:00
burkkyy 5c94367584 healthcheck func impl 2026-07-04 18:12:16 -07:00
7 changed files with 138 additions and 0 deletions
+50
View File
@@ -6,17 +6,22 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents;
import net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.InputUtil;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import net.minecraft.text.Text;
import skybot.commands.CommandManager; import skybot.commands.CommandManager;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.farmers.Farmer; import skybot.farmers.Farmer;
import skybot.task.TaskQueue; import skybot.task.TaskQueue;
import skybot.utilities.ChatMenu; import skybot.utilities.ChatMenu;
import skybot.utilities.TickScheduler;
public class SkybotClient implements ClientModInitializer { public class SkybotClient implements ClientModInitializer {
@@ -24,6 +29,15 @@ public class SkybotClient implements ClientModInitializer {
private static volatile Farmer activeFarmer = null; private static volatile Farmer activeFarmer = null;
private static volatile boolean botEnabled = false; private static volatile boolean botEnabled = false;
private static long tickCount = 0;
private static final long HEALTHCHECK_INTERVAL_TICKS = 40;
private static volatile long lastBreakTime = 0;
private static volatile long lastMoveTime = 0;
private static Vec3d lastPosition = null;
private static final double MOVE_EPSILON_SQUARED = 1.0;
private static final KeyBinding.Category GENERAL_CATEGORY = KeyBinding.Category private static final KeyBinding.Category GENERAL_CATEGORY = KeyBinding.Category
.create(Identifier.of("skybot", "general")); .create(Identifier.of("skybot", "general"));
@@ -37,6 +51,14 @@ public class SkybotClient implements ClientModInitializer {
return botEnabled; return botEnabled;
} }
public static long getLastBreakTime() {
return lastBreakTime;
}
public static long getLastMoveTime() {
return lastMoveTime;
}
public static String botStatus() { public static String botStatus() {
if (!botEnabled) { if (!botEnabled) {
return "§cIdle"; return "§cIdle";
@@ -62,6 +84,9 @@ public class SkybotClient implements ClientModInitializer {
activeFarmer = strategy; activeFarmer = strategy;
botEnabled = true; botEnabled = true;
lastBreakTime = System.currentTimeMillis();
lastMoveTime = System.currentTimeMillis();
lastPosition = null;
activeFarmer.enqueue(TASK_QUEUE); activeFarmer.enqueue(TASK_QUEUE);
ChatMenu.send("§aFarmer started successfully.§r"); ChatMenu.send("§aFarmer started successfully.§r");
@@ -71,6 +96,10 @@ public class SkybotClient implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
KeyBindingHelper.registerKeyBinding(STOP_KEY); KeyBindingHelper.registerKeyBinding(STOP_KEY);
TickScheduler.register();
ClientPlayerBlockBreakEvents.AFTER
.register((world, player, pos, state) -> lastBreakTime = System.currentTimeMillis());
WorldRenderEvents.END_MAIN.register(context -> { WorldRenderEvents.END_MAIN.register(context -> {
MinecraftClient client = MinecraftClient.getInstance(); MinecraftClient client = MinecraftClient.getInstance();
@@ -91,12 +120,33 @@ public class SkybotClient implements ClientModInitializer {
if (client.world != null && !client.isPaused()) { if (client.world != null && !client.isPaused()) {
Controller.tick(client); Controller.tick(client);
if (client.player != null) {
Vec3d pos = client.player.getEntityPos();
if (lastPosition == null) {
lastPosition = pos;
lastMoveTime = System.currentTimeMillis();
} else if (lastPosition.squaredDistanceTo(pos) > MOVE_EPSILON_SQUARED) {
lastMoveTime = System.currentTimeMillis();
lastPosition = pos;
}
}
while (STOP_KEY.wasPressed()) { while (STOP_KEY.wasPressed()) {
if (botEnabled) { if (botEnabled) {
ChatMenu.send("§cBot stopped via hotkey.§r"); ChatMenu.send("§cBot stopped via hotkey.§r");
stopBot(); stopBot();
} }
} }
tickCount++;
if (botEnabled && tickCount % HEALTHCHECK_INTERVAL_TICKS == 0 && !activeFarmer.healthcheck()) {
ChatMenu.send("§cHealthcheck failed, stopping bot.§r");
ChatMenu.send("§cDisconnecting in 3 seconds...§r");
stopBot();
TickScheduler.schedule(
() -> MinecraftClient.getInstance().disconnect(Text.literal("Skybot healthcheck failed")),
60);
}
} }
}); });
@@ -1,5 +1,6 @@
package skybot.farmers; package skybot.farmers;
import skybot.SkybotClient;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.task.HoldKeyTask; import skybot.task.HoldKeyTask;
import skybot.task.PressButtonTask; import skybot.task.PressButtonTask;
@@ -9,6 +10,17 @@ import skybot.task.TaskQueue;
public class CarrotFarmer implements Farmer { public class CarrotFarmer implements Farmer {
private static final long HEALTHCHECK_TIMEOUT_MILLIS = 4500;
private static final long MOVEMENT_TIMEOUT_MILLIS = 5000;
@Override
public boolean healthcheck() {
long now = System.currentTimeMillis();
boolean brokeRecently = now - SkybotClient.getLastBreakTime() < HEALTHCHECK_TIMEOUT_MILLIS;
boolean movedRecently = now - SkybotClient.getLastMoveTime() < MOVEMENT_TIMEOUT_MILLIS;
return brokeRecently && movedRecently;
}
@Override @Override
public void enqueue(TaskQueue queue) { public void enqueue(TaskQueue queue) {
queue.clear(); queue.clear();
@@ -4,4 +4,9 @@ import skybot.task.TaskQueue;
public interface Farmer { public interface Farmer {
void enqueue(TaskQueue queue); void enqueue(TaskQueue queue);
/** Periodic sanity check while this farmer is running. Returning false stops the bot. */
default boolean healthcheck() {
return true;
}
} }
@@ -1,5 +1,6 @@
package skybot.farmers; package skybot.farmers;
import skybot.SkybotClient;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.task.HoldKeyTask; import skybot.task.HoldKeyTask;
import skybot.task.PressButtonTask; import skybot.task.PressButtonTask;
@@ -9,6 +10,17 @@ import skybot.task.TaskQueue;
public class NetherwartFarmer implements Farmer { public class NetherwartFarmer implements Farmer {
private static final long HEALTHCHECK_TIMEOUT_MILLIS = 4500;
private static final long MOVEMENT_TIMEOUT_MILLIS = 5000;
@Override
public boolean healthcheck() {
long now = System.currentTimeMillis();
boolean brokeRecently = now - SkybotClient.getLastBreakTime() < HEALTHCHECK_TIMEOUT_MILLIS;
boolean movedRecently = now - SkybotClient.getLastMoveTime() < MOVEMENT_TIMEOUT_MILLIS;
return brokeRecently && movedRecently;
}
@Override @Override
public void enqueue(TaskQueue queue) { public void enqueue(TaskQueue queue) {
queue.clear(); queue.clear();
@@ -1,5 +1,6 @@
package skybot.farmers; package skybot.farmers;
import skybot.SkybotClient;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.task.HoldKeyTask; import skybot.task.HoldKeyTask;
import skybot.task.PressButtonTask; import skybot.task.PressButtonTask;
@@ -9,6 +10,17 @@ import skybot.task.TaskQueue;
public class PotatoFarmer implements Farmer { public class PotatoFarmer implements Farmer {
private static final long HEALTHCHECK_TIMEOUT_MILLIS = 4500;
private static final long MOVEMENT_TIMEOUT_MILLIS = 5000;
@Override
public boolean healthcheck() {
long now = System.currentTimeMillis();
boolean brokeRecently = now - SkybotClient.getLastBreakTime() < HEALTHCHECK_TIMEOUT_MILLIS;
boolean movedRecently = now - SkybotClient.getLastMoveTime() < MOVEMENT_TIMEOUT_MILLIS;
return brokeRecently && movedRecently;
}
@Override @Override
public void enqueue(TaskQueue queue) { public void enqueue(TaskQueue queue) {
queue.clear(); queue.clear();
@@ -1,5 +1,6 @@
package skybot.farmers; package skybot.farmers;
import skybot.SkybotClient;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.task.HoldKeyTask; import skybot.task.HoldKeyTask;
import skybot.task.PressButtonTask; import skybot.task.PressButtonTask;
@@ -9,6 +10,17 @@ import skybot.task.TaskQueue;
public class WheatFarmer implements Farmer { public class WheatFarmer implements Farmer {
private static final long HEALTHCHECK_TIMEOUT_MILLIS = 4500;
private static final long MOVEMENT_TIMEOUT_MILLIS = 5000;
@Override
public boolean healthcheck() {
long now = System.currentTimeMillis();
boolean brokeRecently = now - SkybotClient.getLastBreakTime() < HEALTHCHECK_TIMEOUT_MILLIS;
boolean movedRecently = now - SkybotClient.getLastMoveTime() < MOVEMENT_TIMEOUT_MILLIS;
return brokeRecently && movedRecently;
}
@Override @Override
public void enqueue(TaskQueue queue) { public void enqueue(TaskQueue queue) {
queue.clear(); queue.clear();
@@ -0,0 +1,35 @@
package skybot.utilities;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import java.util.concurrent.ConcurrentLinkedQueue;
public class TickScheduler {
private record Task(Runnable action, int ticksRemaining) {
}
private static final ConcurrentLinkedQueue<Task> tasks = new ConcurrentLinkedQueue<>();
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> tick());
}
public static void schedule(Runnable action, int delayTicks) {
tasks.add(new Task(action, delayTicks));
}
private static void tick() {
int size = tasks.size();
for (int i = 0; i < size; i++) {
Task t = tasks.poll();
if (t == null)
break;
int remaining = t.ticksRemaining() - 1;
if (remaining <= 0) {
t.action().run();
} else {
tasks.add(new Task(t.action(), remaining));
}
}
}
}