@@ -1,59 +1,99 @@
|
|||||||
package skybot;
|
package skybot;
|
||||||
|
|
||||||
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
|
|
||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
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.networking.v1.ClientPlayConnectionEvents;
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||||
import net.minecraft.client.network.ServerInfo;
|
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.option.KeyBinding;
|
||||||
|
import net.minecraft.client.util.InputUtil;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import skybot.commands.CommandManager;
|
||||||
import skybot.controller.Controller;
|
import skybot.controller.Controller;
|
||||||
import skybot.controller.task.HoldKeyTask;
|
|
||||||
import skybot.controller.task.PauseTask;
|
|
||||||
import skybot.controller.task.TaskQueue;
|
import skybot.controller.task.TaskQueue;
|
||||||
|
import skybot.farmers.Farmer;
|
||||||
import skybot.utilities.ChatMenu;
|
import skybot.utilities.ChatMenu;
|
||||||
|
|
||||||
public class SkybotClient implements ClientModInitializer {
|
public class SkybotClient implements ClientModInitializer {
|
||||||
public static final String HYPIXEL_URL = "hypixel.net";
|
|
||||||
|
|
||||||
private static final TaskQueue TASK_QUEUE = new TaskQueue();
|
private static final TaskQueue TASK_QUEUE = new TaskQueue();
|
||||||
|
private static volatile Farmer activeFarmer = null;
|
||||||
|
private static volatile boolean botEnabled = false;
|
||||||
|
|
||||||
|
private static final KeyBinding STOP_KEY = new KeyBinding(
|
||||||
|
"key.skybot.stop",
|
||||||
|
InputUtil.Type.KEYSYM,
|
||||||
|
GLFW.GLFW_KEY_CAPS_LOCK,
|
||||||
|
KeyBinding.Category.create(Identifier.of("skybot", "general")));
|
||||||
|
|
||||||
|
public static boolean isBotEnabled() {
|
||||||
|
return botEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stopBot() {
|
||||||
|
activeFarmer = null;
|
||||||
|
botEnabled = false;
|
||||||
|
TASK_QUEUE.clear();
|
||||||
|
Controller.releaseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean startBot(Farmer strategy) {
|
||||||
|
if (isBotEnabled()) {
|
||||||
|
ChatMenu.send("§cFailed to start: Another routine is already running.§r");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopBot();
|
||||||
|
|
||||||
|
activeFarmer = strategy;
|
||||||
|
botEnabled = true;
|
||||||
|
|
||||||
|
activeFarmer.enqueue(TASK_QUEUE);
|
||||||
|
ChatMenu.send("§aFarmer started successfully.§r");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
KeyBindingHelper.registerKeyBinding(STOP_KEY);
|
||||||
|
|
||||||
|
WorldRenderEvents.END_MAIN.register(context -> {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client.world != null && !client.isPaused()) {
|
||||||
|
if (TASK_QUEUE.isEmpty() && botEnabled) {
|
||||||
|
ChatMenu.send("Farmer is idle.");
|
||||||
|
stopBot();
|
||||||
|
}
|
||||||
|
|
||||||
TASK_QUEUE.tick();
|
TASK_QUEUE.tick();
|
||||||
Controller.tick(client);
|
|
||||||
|
float delta = client.getRenderTickCounter().getTickProgress(false);
|
||||||
|
|
||||||
|
Controller.tickLook(client, delta);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
|
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||||
dispatcher.register(literal("test").executes(context -> {
|
if (client.world != null && !client.isPaused()) {
|
||||||
TASK_QUEUE.clear();
|
Controller.tickInput(client);
|
||||||
TASK_QUEUE.add(new HoldKeyTask(Controller::setForward, 200));
|
|
||||||
TASK_QUEUE.add(new PauseTask(400));
|
while (STOP_KEY.wasPressed()) {
|
||||||
TASK_QUEUE.add(new HoldKeyTask(Controller::setRight, 400));
|
if (botEnabled) {
|
||||||
ChatMenu.send("running: forward 2s -> pause 4s -> right 4s");
|
ChatMenu.send("§cBot stopped via hotkey.§r");
|
||||||
return 1;
|
stopBot();
|
||||||
}));
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
ClientCommandRegistrationCallback.EVENT.register(CommandManager::register);
|
||||||
client.execute(() -> {
|
|
||||||
if (client.player == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ServerInfo server = client.getCurrentServerEntry();
|
|
||||||
if (server == null || !server.address.contains(HYPIXEL_URL))
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
|
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
|
||||||
TASK_QUEUE.clear();
|
stopBot();
|
||||||
Controller.releaseAll();
|
|
||||||
|
|
||||||
ServerInfo server = client.getCurrentServerEntry();
|
|
||||||
if (server == null || !server.address.contains(HYPIXEL_URL))
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package skybot.commands;
|
||||||
|
|
||||||
|
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||||
|
import net.minecraft.command.CommandRegistryAccess;
|
||||||
|
import skybot.SkybotClient;
|
||||||
|
import skybot.farmers.CarrotFarmer;
|
||||||
|
import skybot.farmers.TestFarmer;
|
||||||
|
import skybot.utilities.ChatMenu;
|
||||||
|
|
||||||
|
public class CommandManager {
|
||||||
|
|
||||||
|
public static void register(
|
||||||
|
CommandDispatcher<FabricClientCommandSource> dispatcher,
|
||||||
|
CommandRegistryAccess registryAccess) {
|
||||||
|
|
||||||
|
dispatcher.register(literal("farm")
|
||||||
|
.then(literal("test").executes(context -> {
|
||||||
|
if (SkybotClient.startBot(new TestFarmer())) {
|
||||||
|
ChatMenu.send("§aStarting Test Farmer...§r");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}))
|
||||||
|
.then(literal("carrots").executes(context -> {
|
||||||
|
if (SkybotClient.startBot(new CarrotFarmer())) {
|
||||||
|
ChatMenu.send("§aStarting Carrot Farmer...§r");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
})));
|
||||||
|
|
||||||
|
dispatcher.register(literal("skybot")
|
||||||
|
.then(literal("status").executes(context -> {
|
||||||
|
String msg = SkybotClient.isBotEnabled() ? "§aRunning" : "§cIdle";
|
||||||
|
ChatMenu.send("Status: " + msg);
|
||||||
|
return 1;
|
||||||
|
}))
|
||||||
|
.then(literal("stop").executes(context -> {
|
||||||
|
SkybotClient.stopBot();
|
||||||
|
ChatMenu.send("§cBot stopped immediately.§r");
|
||||||
|
return 1;
|
||||||
|
})));
|
||||||
|
|
||||||
|
// You can easily add more commands here as your framework expands:
|
||||||
|
// CropSelectionCommand.register(dispatcher);
|
||||||
|
// ConfigCommand.register(dispatcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,9 @@ public class Controller {
|
|||||||
private final static KeyOverride sprinting = new KeyOverride();
|
private final static KeyOverride sprinting = new KeyOverride();
|
||||||
private final static KeyOverride sneaking = new KeyOverride();
|
private final static KeyOverride sneaking = new KeyOverride();
|
||||||
|
|
||||||
|
private final static KeyOverride attack = new KeyOverride(); // Left click
|
||||||
|
private final static KeyOverride use = new KeyOverride(); // Right click
|
||||||
|
|
||||||
private static Double targetYaw = null;
|
private static Double targetYaw = null;
|
||||||
private static Double targetPitch = null;
|
private static Double targetPitch = null;
|
||||||
private static double turnSpeed = 10.0; // max degrees per tick
|
private static double turnSpeed = 10.0; // max degrees per tick
|
||||||
@@ -52,6 +55,14 @@ public class Controller {
|
|||||||
sneaking.set(pressed);
|
sneaking.set(pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setLeftClick(Boolean pressed) {
|
||||||
|
attack.set(pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRightClick(Boolean pressed) {
|
||||||
|
use.set(pressed);
|
||||||
|
}
|
||||||
|
|
||||||
public static void lookAt(double yaw, double pitch) {
|
public static void lookAt(double yaw, double pitch) {
|
||||||
targetYaw = yaw;
|
targetYaw = yaw;
|
||||||
targetPitch = MathHelper.clamp(pitch, -90.0, 90.0);
|
targetPitch = MathHelper.clamp(pitch, -90.0, 90.0);
|
||||||
@@ -66,7 +77,10 @@ public class Controller {
|
|||||||
targetPitch = null;
|
targetPitch = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears every override, handing all input back to the player. Call on disconnect. */
|
/**
|
||||||
|
* Clears every override, handing all input back to the player. Call on
|
||||||
|
* disconnect.
|
||||||
|
*/
|
||||||
public static void releaseAll() {
|
public static void releaseAll() {
|
||||||
forward.set(null);
|
forward.set(null);
|
||||||
back.set(null);
|
back.set(null);
|
||||||
@@ -75,12 +89,17 @@ public class Controller {
|
|||||||
jumping.set(null);
|
jumping.set(null);
|
||||||
sprinting.set(null);
|
sprinting.set(null);
|
||||||
sneaking.set(null);
|
sneaking.set(null);
|
||||||
|
attack.set(null);
|
||||||
|
use.set(null);
|
||||||
clearLook();
|
clearLook();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tick(MinecraftClient client) {
|
/**
|
||||||
ClientPlayerEntity player = client.player;
|
* Run this inside ClientTickEvents.END_CLIENT_TICK (20Hz).
|
||||||
if (player == null)
|
* Handles keyboard state overrides aligned with game engine updates.
|
||||||
|
*/
|
||||||
|
public static void tickInput(MinecraftClient client) {
|
||||||
|
if (client.player == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GameOptions options = client.options;
|
GameOptions options = client.options;
|
||||||
@@ -92,26 +111,49 @@ public class Controller {
|
|||||||
sprinting.apply(options.sprintKey);
|
sprinting.apply(options.sprintKey);
|
||||||
sneaking.apply(options.sneakKey);
|
sneaking.apply(options.sneakKey);
|
||||||
|
|
||||||
if (targetYaw != null && targetPitch != null) {
|
attack.apply(options.attackKey);
|
||||||
applyLook(player);
|
use.apply(options.useKey);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyLook(ClientPlayerEntity player) {
|
/**
|
||||||
|
* Run this inside WorldRenderEvents.END_MAIN (Every Frame).
|
||||||
|
* Handles sub-tick camera adjustments smoothly using frame delta time.
|
||||||
|
*
|
||||||
|
* @param tickDelta The fractional time elapsed since the last game tick (0.0 -
|
||||||
|
* 1.0)
|
||||||
|
*/
|
||||||
|
public static void tickLook(MinecraftClient client, float tickDelta) {
|
||||||
|
ClientPlayerEntity player = client.player;
|
||||||
|
if (player == null || targetYaw == null || targetPitch == null)
|
||||||
|
return;
|
||||||
|
|
||||||
double yawDiff = MathHelper.wrapDegrees(targetYaw - player.getYaw());
|
double yawDiff = MathHelper.wrapDegrees(targetYaw - player.getYaw());
|
||||||
double pitchDiff = targetPitch - player.getPitch();
|
double pitchDiff = targetPitch - player.getPitch();
|
||||||
|
|
||||||
double yawStep = MathHelper.clamp(yawDiff, -turnSpeed, turnSpeed);
|
// Scale turnSpeed using tickDelta so aiming remains consistent regardless of
|
||||||
double pitchStep = MathHelper.clamp(pitchDiff, -turnSpeed, turnSpeed);
|
// FPS
|
||||||
|
double scaledTurnSpeed = turnSpeed * tickDelta;
|
||||||
|
|
||||||
|
// Fallback case: if tickDelta is 0 (e.g., game paused or frame drop), fallback
|
||||||
|
// to a tiny step
|
||||||
|
if (scaledTurnSpeed <= 0) {
|
||||||
|
scaledTurnSpeed = turnSpeed * 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double yawStep = MathHelper.clamp(yawDiff, -scaledTurnSpeed, scaledTurnSpeed);
|
||||||
|
double pitchStep = MathHelper.clamp(pitchDiff, -scaledTurnSpeed, scaledTurnSpeed);
|
||||||
|
|
||||||
player.setYaw((float) (player.getYaw() + yawStep));
|
player.setYaw((float) (player.getYaw() + yawStep));
|
||||||
player.setPitch((float) MathHelper.clamp(player.getPitch() + pitchStep, -90.0, 90.0));
|
player.setPitch((float) MathHelper.clamp(player.getPitch() + pitchStep, -90.0, 90.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks one key's override state. null means "no override, leave real input alone."
|
* Tracks one key's override state. null means "no override, leave real input
|
||||||
* When an override is released (goes back to null), forces the key up exactly once
|
* alone."
|
||||||
* so it doesn't stay latched from the last forced value, then stops touching it.
|
* When an override is released (goes back to null), forces the key up exactly
|
||||||
|
* once
|
||||||
|
* so it doesn't stay latched from the last forced value, then stops touching
|
||||||
|
* it.
|
||||||
*/
|
*/
|
||||||
private static class KeyOverride {
|
private static class KeyOverride {
|
||||||
private Boolean state;
|
private Boolean state;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package skybot.controller.task;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class PressButtonTask implements Task {
|
||||||
|
private final Consumer<Boolean> setter;
|
||||||
|
private boolean pressed = false;
|
||||||
|
|
||||||
|
public PressButtonTask(Consumer<Boolean> setter) {
|
||||||
|
this.setter = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
setter.accept(true);
|
||||||
|
pressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package skybot.controller.task;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ReleaseButtonTask implements Task {
|
||||||
|
|
||||||
|
private final Consumer<Boolean> setter;
|
||||||
|
private boolean released = false;
|
||||||
|
|
||||||
|
public ReleaseButtonTask(Consumer<Boolean> setter) {
|
||||||
|
this.setter = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
setter.accept(false);
|
||||||
|
released = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return released;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,8 +34,7 @@ public class TaskQueue {
|
|||||||
if (current == null) {
|
if (current == null) {
|
||||||
current = pending.poll();
|
current = pending.poll();
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
// Roll the random delay
|
rolledDelay = sampleLogNormalDistribution();
|
||||||
rolledDelay = ThreadLocalRandom.current().nextLong(51, 200);
|
|
||||||
delayUntil = System.currentTimeMillis() + rolledDelay;
|
delayUntil = System.currentTimeMillis() + rolledDelay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +46,7 @@ public class TaskQueue {
|
|||||||
long totalDelta = rolledDelay + actualOverhead;
|
long totalDelta = rolledDelay + actualOverhead;
|
||||||
|
|
||||||
ChatMenu.send(
|
ChatMenu.send(
|
||||||
String.format("Starting new job... (Target delay: %dms, Actual delta: %dms)", rolledDelay, totalDelta));
|
String.format("Starting new job... (delayUntil: %dms)", totalDelta));
|
||||||
|
|
||||||
current.start();
|
current.start();
|
||||||
currentStarted = true;
|
currentStarted = true;
|
||||||
@@ -57,7 +56,7 @@ public class TaskQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDone() {
|
public boolean isEmpty() {
|
||||||
return current == null && pending.isEmpty();
|
return current == null && pending.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,4 +66,18 @@ public class TaskQueue {
|
|||||||
delayUntil = 0;
|
delayUntil = 0;
|
||||||
rolledDelay = 0;
|
rolledDelay = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long sampleLogNormalDistribution() {
|
||||||
|
long physicalLimitShift = 80;
|
||||||
|
|
||||||
|
// Adjust mu so our median tail addition is roughly 70ms
|
||||||
|
// (130ms baseline + 70ms average tail = ~200ms real-world human action speed)
|
||||||
|
double mu = Math.log(70);
|
||||||
|
double sigma = 0.35; // Slightly wider variance for natural pacing inconsistencies
|
||||||
|
|
||||||
|
long randomTail = (long) Math.exp(ThreadLocalRandom.current().nextGaussian() * sigma + mu);
|
||||||
|
|
||||||
|
// Combine them: your delays will smoothly scale from 140ms up to 350ms+
|
||||||
|
return physicalLimitShift + randomTail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package skybot.farmers;
|
||||||
|
|
||||||
|
import skybot.controller.Controller;
|
||||||
|
import skybot.controller.task.HoldKeyTask;
|
||||||
|
import skybot.controller.task.PauseTask;
|
||||||
|
import skybot.controller.task.PressButtonTask;
|
||||||
|
import skybot.controller.task.ReleaseButtonTask;
|
||||||
|
import skybot.controller.task.TaskQueue;
|
||||||
|
|
||||||
|
public class CarrotFarmer implements Farmer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enqueue(TaskQueue queue) {
|
||||||
|
queue.clear();
|
||||||
|
|
||||||
|
queue.add(new PressButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new HoldKeyTask(Controller::setLeft, 119555));
|
||||||
|
queue.add(new ReleaseButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new PauseTask(2000));
|
||||||
|
|
||||||
|
queue.add(new PressButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new HoldKeyTask(Controller::setRight, 119202));
|
||||||
|
queue.add(new ReleaseButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new PauseTask(2000));
|
||||||
|
|
||||||
|
queue.add(new PressButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new HoldKeyTask(Controller::setLeft, 112023));
|
||||||
|
queue.add(new ReleaseButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new PauseTask(2000));
|
||||||
|
|
||||||
|
queue.add(new PressButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new HoldKeyTask(Controller::setRight, 119355));
|
||||||
|
queue.add(new ReleaseButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new PauseTask(2000));
|
||||||
|
|
||||||
|
queue.add(new PressButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new HoldKeyTask(Controller::setLeft, 120155));
|
||||||
|
queue.add(new ReleaseButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new PauseTask(2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package skybot.farmers;
|
||||||
|
|
||||||
|
import skybot.controller.task.TaskQueue;
|
||||||
|
|
||||||
|
public interface Farmer {
|
||||||
|
void enqueue(TaskQueue queue);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package skybot.farmers;
|
||||||
|
|
||||||
|
import skybot.controller.Controller;
|
||||||
|
import skybot.controller.task.PauseTask;
|
||||||
|
import skybot.controller.task.PressButtonTask;
|
||||||
|
import skybot.controller.task.TaskQueue;
|
||||||
|
|
||||||
|
public class TestFarmer implements Farmer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enqueue(TaskQueue queue) {
|
||||||
|
queue.clear();
|
||||||
|
queue.add(new PressButtonTask(Controller::setLeftClick));
|
||||||
|
queue.add(new PauseTask(5000));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user