Big upgrades
build / build (push) Failing after 1s

This commit is contained in:
2026-07-04 05:10:54 -07:00
parent 9ff19ae67b
commit c3bbf9af4b
7 changed files with 210 additions and 74 deletions
+3 -4
View File
@@ -71,15 +71,14 @@ public class SkybotClient implements ClientModInitializer {
TASK_QUEUE.tick(); TASK_QUEUE.tick();
float delta = client.getRenderTickCounter().getTickProgress(false); // float delta = client.getRenderTickCounter().getTickProgress(false);
// Controller.tickLook(client, delta);
Controller.tickLook(client, delta);
} }
}); });
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.world != null && !client.isPaused()) { if (client.world != null && !client.isPaused()) {
Controller.tickInput(client); Controller.tick(client);
while (STOP_KEY.wasPressed()) { while (STOP_KEY.wasPressed()) {
if (botEnabled) { if (botEnabled) {
+104 -46
View File
@@ -1,10 +1,13 @@
package skybot.controller; package skybot.controller;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Consumer;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.option.GameOptions; import net.minecraft.client.option.GameOptions;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import net.minecraft.util.math.MathHelper; import net.minecraft.client.util.InputUtil;
/** /**
* Owns simulated input state (movement keys + look direction) and applies it * Owns simulated input state (movement keys + look direction) and applies it
@@ -23,9 +26,14 @@ public class Controller {
private final static KeyOverride attack = new KeyOverride(); // Left click private final static KeyOverride attack = new KeyOverride(); // Left click
private final static KeyOverride use = new KeyOverride(); // Right click private final static KeyOverride use = new KeyOverride(); // Right click
private static Double targetYaw = null; // Overrides for arbitrary physical keys, keyed by whatever KeyBinding(s) the
private static Double targetPitch = null; // player currently has bound to that key (same dispatch GLFW's real key
private static double turnSpeed = 10.0; // max degrees per tick // callback uses), so any bindable key works without a dedicated field.
private final static Map<InputUtil.Key, KeyOverride> keyOverrides = new HashMap<>();
// private static Double targetYaw = null;
// private static Double targetPitch = null;
// private static double turnSpeed = 10.0; // max degrees per tick
public static void setForward(Boolean pressed) { public static void setForward(Boolean pressed) {
forward.set(pressed); forward.set(pressed);
@@ -63,20 +71,44 @@ public class Controller {
use.set(pressed); use.set(pressed);
} }
public static void lookAt(double yaw, double pitch) { /**
targetYaw = yaw; * Overrides an arbitrary physical key, e.g.
targetPitch = MathHelper.clamp(pitch, -90.0, 90.0); * Controller.setKey(InputUtil.GLFW_KEY_KP_7, true).
*/
public static void setKey(InputUtil.Key key, Boolean pressed) {
keyOverrides.computeIfAbsent(key, k -> new KeyOverride()).set(pressed);
} }
public static void setTurnSpeed(double degreesPerTick) { /**
turnSpeed = degreesPerTick; * Overrides an arbitrary physical key by its GLFW key code, e.g.
* InputUtil.GLFW_KEY_KP_7.
*/
public static void setKey(int glfwKeyCode, Boolean pressed) {
setKey(InputUtil.Type.KEYSYM.createFromCode(glfwKeyCode), pressed);
} }
public static void clearLook() { /**
targetYaw = null; * A setter bound to one physical key, e.g.
targetPitch = null; * new ClickButtonTask(Controller.key(InputUtil.GLFW_KEY_KP_7)).
*/
public static Consumer<Boolean> key(int glfwKeyCode) {
return pressed -> setKey(glfwKeyCode, pressed);
} }
// public static void lookAt(double yaw, double pitch) {
// targetYaw = yaw;
// targetPitch = MathHelper.clamp(pitch, -90.0, 90.0);
// }
// public static void setTurnSpeed(double degreesPerTick) {
// turnSpeed = degreesPerTick;
// }
// public static void clearLook() {
// targetYaw = null;
// targetPitch = null;
// }
/** /**
* Clears every override, handing all input back to the player. Call on * Clears every override, handing all input back to the player. Call on
* disconnect. * disconnect.
@@ -91,28 +123,45 @@ public class Controller {
sneaking.set(null); sneaking.set(null);
attack.set(null); attack.set(null);
use.set(null); use.set(null);
clearLook();
for (InputUtil.Key key : keyOverrides.keySet()) {
KeyBinding.setKeyPressed(key, false);
}
keyOverrides.clear();
// clearLook();
} }
/** /**
* Run this inside ClientTickEvents.END_CLIENT_TICK (20Hz). * Run this inside ClientTickEvents.END_CLIENT_TICK (20Hz).
* Handles keyboard state overrides aligned with game engine updates. * Handles keyboard state overrides aligned with game engine updates.
*/ */
public static void tickInput(MinecraftClient client) { public static void tick(MinecraftClient client) {
if (client.player == null) if (client.player == null)
return; return;
GameOptions options = client.options; GameOptions options = client.options;
forward.apply(options.forwardKey); forward.apply(options.forwardKey::setPressed);
back.apply(options.backKey); back.apply(options.backKey::setPressed);
left.apply(options.leftKey); left.apply(options.leftKey::setPressed);
right.apply(options.rightKey); right.apply(options.rightKey::setPressed);
jumping.apply(options.jumpKey); jumping.apply(options.jumpKey::setPressed);
sprinting.apply(options.sprintKey); sprinting.apply(options.sprintKey::setPressed);
sneaking.apply(options.sneakKey); sneaking.apply(options.sneakKey::setPressed);
attack.apply(options.attackKey); attack.apply(options.attackKey::setPressed);
use.apply(options.useKey); use.apply(options.useKey::setPressed);
for (Iterator<Map.Entry<InputUtil.Key, KeyOverride>> it = keyOverrides.entrySet().iterator(); it
.hasNext();) {
Map.Entry<InputUtil.Key, KeyOverride> entry = it.next();
InputUtil.Key key = entry.getKey();
KeyOverride override = entry.getValue();
override.apply(pressed -> KeyBinding.setKeyPressed(key, pressed));
if (override.isIdle()) {
it.remove();
}
}
} }
/** /**
@@ -122,30 +171,34 @@ public class Controller {
* @param tickDelta The fractional time elapsed since the last game tick (0.0 - * @param tickDelta The fractional time elapsed since the last game tick (0.0 -
* 1.0) * 1.0)
*/ */
public static void tickLook(MinecraftClient client, float tickDelta) { // public static void tickLook(MinecraftClient client, float tickDelta) {
ClientPlayerEntity player = client.player; // ClientPlayerEntity player = client.player;
if (player == null || targetYaw == null || targetPitch == null) // if (player == null || targetYaw == null || targetPitch == null)
return; // 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();
// Scale turnSpeed using tickDelta so aiming remains consistent regardless of // // Scale turnSpeed using tickDelta so aiming remains consistent regardless of
// FPS // // FPS
double scaledTurnSpeed = turnSpeed * tickDelta; // double scaledTurnSpeed = turnSpeed * tickDelta;
// Fallback case: if tickDelta is 0 (e.g., game paused or frame drop), fallback // // Fallback case: if tickDelta is 0 (e.g., game paused or frame drop),
// to a tiny step // fallback
if (scaledTurnSpeed <= 0) { // // to a tiny step
scaledTurnSpeed = turnSpeed * 0.1; // if (scaledTurnSpeed <= 0) {
} // scaledTurnSpeed = turnSpeed * 0.1;
// }
double yawStep = MathHelper.clamp(yawDiff, -scaledTurnSpeed, scaledTurnSpeed); // double yawStep = MathHelper.clamp(yawDiff, -scaledTurnSpeed,
double pitchStep = MathHelper.clamp(pitchDiff, -scaledTurnSpeed, 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 * Tracks one key's override state. null means "no override, leave real input
@@ -163,14 +216,19 @@ public class Controller {
state = pressed; state = pressed;
} }
void apply(KeyBinding binding) { void apply(Consumer<Boolean> sink) {
if (state != null) { if (state != null) {
binding.setPressed(state); sink.accept(state);
controlled = true; controlled = true;
} else if (controlled) { } else if (controlled) {
binding.setPressed(false); sink.accept(false);
controlled = false; controlled = false;
} }
} }
/** True once released and the forced-up frame has already been sent. */
boolean isIdle() {
return state == null && !controlled;
}
} }
} }
@@ -0,0 +1,51 @@
package skybot.controller.task;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
/** Clicks a button (press then release) held for a log-normally distributed duration. */
public class ClickButtonTask implements Task {
private final Consumer<Boolean> setter;
private long endTime;
public ClickButtonTask(Consumer<Boolean> setter) {
this.setter = setter;
}
@Override
public void start() {
endTime = System.currentTimeMillis() + sampleLogNormalDistribution();
setter.accept(true);
}
@Override
public void tick() {
}
@Override
public boolean isDone() {
if (endTime == 0) {
return false;
}
return System.currentTimeMillis() >= endTime;
}
@Override
public void stop() {
setter.accept(false);
}
private long sampleLogNormalDistribution() {
long physicalLimitShift = 30;
// Adjust mu so our median tail addition is roughly 50ms
// (30ms baseline + 50ms average tail = ~80ms human click hold time)
double mu = Math.log(50);
double sigma = 0.35; // Slightly wider variance for natural pacing inconsistencies
long randomTail = (long) Math.exp(ThreadLocalRandom.current().nextGaussian() * sigma + mu);
return physicalLimitShift + randomTail;
}
}
@@ -13,4 +13,9 @@ public interface Task {
/** Called once, right after isDone() first returns true, before the next task starts. */ /** Called once, right after isDone() first returns true, before the next task starts. */
default void stop() { default void stop() {
} }
/** Short name for this task, used for logging (e.g. via ChatMenu). */
default String name() {
return getClass().getSimpleName();
}
} }
@@ -19,6 +19,9 @@ public class TaskQueue {
// Track the exact random delay we rolled for the active task // Track the exact random delay we rolled for the active task
private long rolledDelay = 0; private long rolledDelay = 0;
// Track when the active task actually started, to report how long it ran
private long taskStartTime = 0;
public TaskQueue add(Task task) { public TaskQueue add(Task task) {
pending.add(task); pending.add(task);
return this; return this;
@@ -27,6 +30,10 @@ public class TaskQueue {
public void tick() { public void tick() {
if (current != null && current.isDone()) { if (current != null && current.isDone()) {
current.stop(); current.stop();
if (currentStarted) {
ChatMenu.send(
String.format("%s finished in %dms", current.name(), System.currentTimeMillis() - taskStartTime));
}
current = null; current = null;
currentStarted = false; currentStarted = false;
} }
@@ -46,10 +53,11 @@ public class TaskQueue {
long totalDelta = rolledDelay + actualOverhead; long totalDelta = rolledDelay + actualOverhead;
ChatMenu.send( ChatMenu.send(
String.format("Starting new job... (delayUntil: %dms)", totalDelta)); String.format("Starting %s... (delayUntil: %dms)", current.name(), totalDelta));
current.start(); current.start();
currentStarted = true; currentStarted = true;
taskStartTime = System.currentTimeMillis();
} }
current.tick(); current.tick();
@@ -1,6 +1,8 @@
package skybot.farmers; package skybot.farmers;
import net.minecraft.client.util.InputUtil;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.controller.task.ClickButtonTask;
import skybot.controller.task.HoldKeyTask; import skybot.controller.task.HoldKeyTask;
import skybot.controller.task.PauseTask; import skybot.controller.task.PauseTask;
import skybot.controller.task.PressButtonTask; import skybot.controller.task.PressButtonTask;
@@ -13,29 +15,40 @@ public class CarrotFarmer implements Farmer {
public void enqueue(TaskQueue queue) { public void enqueue(TaskQueue queue) {
queue.clear(); queue.clear();
queue.add(new PauseTask(1998));
queue.add(new ClickButtonTask(Controller.key(InputUtil.GLFW_KEY_KP_7)));
int EPOCHS = 2;
for (int i = 0; i < EPOCHS; i++) {
queue.add(new PauseTask(2021));
queue.add(new PressButtonTask(Controller::setLeftClick)); queue.add(new PressButtonTask(Controller::setLeftClick));
queue.add(new HoldKeyTask(Controller::setLeft, 119555)); queue.add(new HoldKeyTask(Controller::setLeft, 119555));
queue.add(new ReleaseButtonTask(Controller::setLeftClick)); queue.add(new ReleaseButtonTask(Controller::setLeftClick));
queue.add(new PauseTask(2000)); queue.add(new PauseTask(2015));
queue.add(new PressButtonTask(Controller::setLeftClick)); queue.add(new PressButtonTask(Controller::setLeftClick));
queue.add(new HoldKeyTask(Controller::setRight, 119202)); queue.add(new HoldKeyTask(Controller::setRight, 119402));
queue.add(new ReleaseButtonTask(Controller::setLeftClick)); queue.add(new ReleaseButtonTask(Controller::setLeftClick));
queue.add(new PauseTask(2000)); queue.add(new PauseTask(1998));
queue.add(new PressButtonTask(Controller::setLeftClick)); queue.add(new PressButtonTask(Controller::setLeftClick));
queue.add(new HoldKeyTask(Controller::setLeft, 112023)); queue.add(new HoldKeyTask(Controller::setLeft, 119223));
queue.add(new ReleaseButtonTask(Controller::setLeftClick)); queue.add(new ReleaseButtonTask(Controller::setLeftClick));
queue.add(new PauseTask(2000)); queue.add(new PauseTask(2203));
queue.add(new PressButtonTask(Controller::setLeftClick)); queue.add(new PressButtonTask(Controller::setLeftClick));
queue.add(new HoldKeyTask(Controller::setRight, 119355)); queue.add(new HoldKeyTask(Controller::setRight, 119198));
queue.add(new ReleaseButtonTask(Controller::setLeftClick)); queue.add(new ReleaseButtonTask(Controller::setLeftClick));
queue.add(new PauseTask(2000)); queue.add(new PauseTask(1998));
queue.add(new PressButtonTask(Controller::setLeftClick)); queue.add(new PressButtonTask(Controller::setLeftClick));
queue.add(new HoldKeyTask(Controller::setLeft, 120155)); queue.add(new HoldKeyTask(Controller::setLeft, 120155));
queue.add(new ReleaseButtonTask(Controller::setLeftClick)); queue.add(new ReleaseButtonTask(Controller::setLeftClick));
queue.add(new PauseTask(2000)); queue.add(new PauseTask(2126));
queue.add(new ClickButtonTask(Controller.key(InputUtil.GLFW_KEY_KP_7)));
}
} }
} }
@@ -1,8 +1,9 @@
package skybot.farmers; package skybot.farmers;
import net.minecraft.client.util.InputUtil;
import skybot.controller.Controller; import skybot.controller.Controller;
import skybot.controller.task.ClickButtonTask;
import skybot.controller.task.PauseTask; import skybot.controller.task.PauseTask;
import skybot.controller.task.PressButtonTask;
import skybot.controller.task.TaskQueue; import skybot.controller.task.TaskQueue;
public class TestFarmer implements Farmer { public class TestFarmer implements Farmer {
@@ -10,7 +11,8 @@ public class TestFarmer implements Farmer {
@Override @Override
public void enqueue(TaskQueue queue) { public void enqueue(TaskQueue queue) {
queue.clear(); queue.clear();
queue.add(new PressButtonTask(Controller::setLeftClick)); queue.add(new ClickButtonTask(Controller.key(InputUtil.GLFW_KEY_D)));
queue.add(new PauseTask(5000)); queue.add(new PauseTask(2000));
queue.add(new ClickButtonTask(Controller.key(InputUtil.GLFW_KEY_D)));
} }
} }