Download altoclef/SpaceBridge.java from Erinaldordso/Minecraft-Friends-AI-ZeroGPU: direct link, hf CLI and curl.
- Browser
- Download file 18.5 kB
-
https://huggingface.co/spaces/Erinaldordso/Minecraft-Friends-AI-ZeroGPU/resolve/main/altoclef/SpaceBridge.java
- Command line
-
hf download hf://spaces/Erinaldordso/Minecraft-Friends-AI-ZeroGPU/altoclef/SpaceBridge.java
-
curl -L -o SpaceBridge.java https://huggingface.co/spaces/Erinaldordso/Minecraft-Friends-AI-ZeroGPU/resolve/main/altoclef/SpaceBridge.java
18.5 kB
| package space.minecraft; | |
| import com.sun.net.httpserver.HttpExchange; | |
| import com.sun.net.httpserver.HttpServer; | |
| import net.fabricmc.api.ClientModInitializer; | |
| import net.fabricmc.loader.api.FabricLoader; | |
| import net.fabricmc.loader.api.MappingResolver; | |
| import java.lang.reflect.*; | |
| import java.net.InetSocketAddress; | |
| import java.nio.charset.StandardCharsets; | |
| import java.util.*; | |
| import java.util.concurrent.*; | |
| import java.util.concurrent.atomic.AtomicBoolean; | |
| /** Local telemetry/control adapter. Gameplay remains in the upstream AltoClef mod. | |
| * | |
| * <p>Threading contract: every read or mutation of Minecraft client state | |
| * ({@link #snapshot}, {@link #connect}, AltoClef commands) runs ONLY on the | |
| * Minecraft client thread, reached through {@code Executor} dispatch (the | |
| * client implements {@code java.util.concurrent.Executor}) with a reflective | |
| * {@code execute(Runnable)} fallback. Dispatch is never run inline on a | |
| * telemetry or HTTP thread: a failed dispatch records a diagnostic and the | |
| * bridge keeps reporting a loading/error heartbeat WITHOUT claiming fresh | |
| * connected world observations. | |
| * | |
| * <p>Freshness contract for /state: {@code heartbeat_at} is refreshed by the | |
| * scheduler tick itself (bridge alive), while {@code observed_at} is refreshed | |
| * only by a snapshot that actually ran on the client thread. At most one | |
| * snapshot is ever queued, so rendering load cannot build a backlog. | |
| * | |
| * <p>Connect contract: the client starts at the normal TitleScreen (no | |
| * {@code --server/--port}, which would bypass it and leave upstream | |
| * uninitialized) and this bridge connects only on a Title/Disconnected | |
| * screen with no resource reload overlay (connecting mid-reload NPEs | |
| * WorldRenderer via ShaderProgram.addSampler), on the client thread only. | |
| */ | |
| public final class SpaceBridge implements ClientModInitializer { | |
| private static final MappingResolver MAP = FabricLoader.getInstance().getMappingResolver(); | |
| private volatile String state = "{\"connected\":false,\"loading\":true,\"heartbeat_at\":0}"; | |
| private volatile String lastError = ""; | |
| private volatile String lastCommand = ""; | |
| private final AtomicBoolean snapshotPending = new AtomicBoolean(false); | |
| private long connectAttempt; | |
| private long connectAttempts; | |
| private volatile String connectState = "startup"; | |
| private String lastGate = ""; | |
| private long creditsSince; | |
| private boolean creditsObserved; | |
| private boolean enteredEnd; | |
| private String previousDimension = ""; | |
| private static Class<?> mc(String intermediary) throws Exception { | |
| return Class.forName(MAP.mapClassName("intermediary", "net.minecraft." + intermediary)); | |
| } | |
| private static Method accessibleMethod(Class<?> owner, String name, Class<?>[] types) throws Exception { | |
| try { | |
| return owner.getMethod(name, types); | |
| } catch (NoSuchMethodException missing) { | |
| Method fallback = owner.getDeclaredMethod(name, types); | |
| fallback.setAccessible(true); | |
| return fallback; | |
| } | |
| } | |
| private static Object method(Object obj, String owner, String id, String desc, | |
| Class<?>[] types, Object... args) throws Exception { | |
| String name = MAP.mapMethodName("intermediary", "net.minecraft." + owner, id, desc); | |
| return accessibleMethod(mc(owner), name, types).invoke(obj, args); | |
| } | |
| private static Object call(Object obj, String name, Class<?>[] types, Object... args) throws Exception { | |
| return accessibleMethod(obj.getClass(), name, types).invoke(obj, args); | |
| } | |
| private static Object call(Object obj, String name) throws Exception { | |
| return call(obj, name, new Class<?>[0]); | |
| } | |
| private static Object client() throws Exception { | |
| return method(null, "class_310", "method_1551", "()Lnet/minecraft/class_310;", new Class<?>[0]); | |
| } | |
| /** Enqueue on the client thread. Never runs the task inline: throws on failure. */ | |
| private static void dispatchToClient(Object client, Runnable task) throws Exception { | |
| if (client instanceof Executor) { | |
| ((Executor) client).execute(task); | |
| return; | |
| } | |
| accessibleMethod(client.getClass(), "execute", new Class<?>[]{Runnable.class}).invoke(client, task); | |
| } | |
| private static Object screen(Object client) throws Exception { | |
| String name = MAP.mapFieldName("intermediary", "net.minecraft.class_310", "field_1755", "Lnet/minecraft/class_437;"); | |
| try { | |
| return mc("class_310").getField(name).get(client); | |
| } catch (NoSuchFieldException missing) { | |
| Field fallback = mc("class_310").getDeclaredField(name); | |
| fallback.setAccessible(true); | |
| return fallback.get(client); | |
| } | |
| } | |
| private static void respond(HttpExchange ex, int code, String body) throws Exception { | |
| byte[] data = body.getBytes(StandardCharsets.UTF_8); | |
| ex.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8"); | |
| ex.sendResponseHeaders(code, data.length); | |
| ex.getResponseBody().write(data); | |
| ex.close(); | |
| } | |
| /** Loading/error heartbeat: fresh heartbeat_at, no connected observations claimed. */ | |
| private void heartbeat(String detail) { | |
| Map<String, Object> data = new LinkedHashMap<>(); | |
| data.put("connected", false); | |
| data.put("loading", true); | |
| data.put("heartbeat_at", System.currentTimeMillis() / 1000.0); | |
| data.put("dispatch_error", detail == null ? "" : detail); | |
| data.put("error", lastError); | |
| data.put("last_command", lastCommand); | |
| state = json(data); | |
| } | |
| private void queueSnapshot() { | |
| // At most one pending snapshot: skip the tick when the client thread | |
| // has not drained the previous one (e.g. under rendering load). | |
| if (!snapshotPending.compareAndSet(false, true)) { | |
| heartbeat("snapshot still queued on client thread"); | |
| return; | |
| } | |
| boolean queued = false; | |
| try { | |
| dispatchToClient(client(), () -> { | |
| try { | |
| snapshot(); | |
| } finally { | |
| snapshotPending.set(false); | |
| } | |
| }); | |
| queued = true; | |
| } catch (Exception err) { | |
| lastError = "client dispatch failed: " + err; | |
| } finally { | |
| if (!queued) { | |
| snapshotPending.set(false); | |
| heartbeat(lastError); | |
| } | |
| } | |
| } | |
| public void onInitializeClient() { | |
| try { | |
| int port = Integer.parseInt(System.getenv().getOrDefault("ALTOCLEF_BRIDGE_PORT", "3002")); | |
| HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", port), 8); | |
| server.createContext("/state", ex -> { | |
| try { respond(ex, 200, state); } catch (Exception ignored) { ex.close(); } | |
| }); | |
| server.createContext("/command", ex -> { | |
| try { | |
| if (!"POST".equals(ex.getRequestMethod())) { respond(ex, 405, "{}"); return; } | |
| String command = new String(ex.getRequestBody().readNBytes(128), StandardCharsets.UTF_8).trim(); | |
| if (!Set.of("gamer", "stop", "status").contains(command)) { | |
| respond(ex, 400, "{\"error\":\"unsupported command\"}"); return; | |
| } | |
| try { | |
| dispatchToClient(client(), () -> { | |
| try { | |
| Class<?> alto = Class.forName("adris.altoclef.AltoClef"); | |
| Object executor = alto.getMethod("getCommandExecutor").invoke(null); | |
| if (executor == null) throw new IllegalStateException("AltoClef is not initialized"); | |
| executor.getClass().getMethod("executeWithPrefix", String.class).invoke(executor, command); | |
| lastCommand = command; | |
| lastError = ""; | |
| System.out.println("[space-altoclef] command=" + command); | |
| } catch (Exception err) { lastError = err.toString(); } | |
| }); | |
| } catch (Exception err) { | |
| lastError = "command dispatch failed: " + err; | |
| respond(ex, 503, json(Map.of("error", lastError))); | |
| return; | |
| } | |
| respond(ex, 202, "{\"accepted\":true}"); | |
| } catch (Exception err) { | |
| try { respond(ex, 503, json(Map.of("error", err.toString()))); } catch (Exception ignored) { ex.close(); } | |
| } | |
| }); | |
| server.setExecutor(Executors.newFixedThreadPool(2)); | |
| server.start(); | |
| Executors.newSingleThreadScheduledExecutor(r -> { | |
| Thread t = new Thread(r, "space-altoclef-telemetry"); t.setDaemon(true); return t; | |
| }).scheduleWithFixedDelay(() -> { | |
| try { | |
| queueSnapshot(); | |
| } catch (Throwable err) { | |
| lastError = "telemetry scheduler: " + err; | |
| heartbeat(lastError); | |
| } | |
| }, 2, 1, TimeUnit.SECONDS); | |
| System.out.println("[space-altoclef] local bridge ready on 127.0.0.1:" + port); | |
| } catch (Exception err) { throw new RuntimeException("Space bridge failed", err); } | |
| } | |
| private void connect(Object client, Object currentScreen) throws Exception { | |
| // Gated connect: NEVER while a resource/shader reload overlay | |
| // is up (that NPEs WorldRenderer via ShaderProgram.addSampler on a | |
| // half-built reload), never while vanilla is already connecting, and | |
| // never before upstream initialized on the TitleScreen. | |
| // Pinned Yarn 1.19.4+build.2: METHOD emh ()Lesx; aJ method_18506 getOverlay | |
| // (owner class_310 MinecraftClient, returns class_4071 Overlay). | |
| Object overlay = method(client, "class_310", "method_18506", "()Lnet/minecraft/class_4071;", | |
| new Class<?>[0]); | |
| boolean title = currentScreen != null | |
| && (mc("class_442").isInstance(currentScreen) || mc("class_419").isInstance(currentScreen)); | |
| boolean connecting = currentScreen != null && mc("class_412").isInstance(currentScreen); | |
| String screenName = currentScreen == null ? "<none>" : currentScreen.getClass().getName(); | |
| if (overlay != null) { | |
| gate("waiting resource reload (overlay=" + overlay.getClass().getName() + ")"); | |
| return; | |
| } | |
| if (connecting) { | |
| connectState = "connecting (vanilla ConnectScreen)"; | |
| return; | |
| } | |
| if (!title) { | |
| gate("waiting title screen (screen=" + screenName + ")"); | |
| return; | |
| } | |
| if (System.currentTimeMillis() - connectAttempt < 30000) { | |
| connectState = "retry waits 30s throttle (screen=" + screenName + ")"; | |
| return; | |
| } | |
| connectAttempt = System.currentTimeMillis(); | |
| connectAttempts++; | |
| String address = System.getenv().getOrDefault("ALTOCLEF_SERVER", "127.0.0.1:25568"); | |
| Object parsed = method(null, "class_639", "method_2950", "(Ljava/lang/String;)Lnet/minecraft/class_639;", | |
| new Class<?>[]{String.class}, address); | |
| Object info = mc("class_642").getConstructor(String.class, String.class, boolean.class) | |
| .newInstance("Minecraft Friends", address, false); | |
| method(null, "class_412", "method_36877", | |
| "(Lnet/minecraft/class_437;Lnet/minecraft/class_310;Lnet/minecraft/class_639;Lnet/minecraft/class_642;)V", | |
| new Class<?>[]{mc("class_437"), mc("class_310"), mc("class_639"), mc("class_642")}, | |
| screen(client), client, parsed, info); | |
| connectState = "connecting to " + address + " (attempt " + connectAttempts + ")"; | |
| System.out.println("[space-altoclef] connecting to " + address + " (attempt " + connectAttempts + ")"); | |
| } | |
| /** Throttled gate diagnostic: log only when the reason changes. */ | |
| private void gate(String reason) { | |
| connectState = reason; | |
| if (!reason.equals(lastGate)) { | |
| lastGate = reason; | |
| System.out.println("[space-altoclef] connect gated: " + reason); | |
| } | |
| } | |
| private String currentDimension() throws Exception { | |
| // Upstream WorldHelper.getCurrentDimension is package-private static; | |
| // resolve it as a declared method so snapshots survive visibility. | |
| Class<?> helper = Class.forName("adris.altoclef.util.helpers.WorldHelper"); | |
| return String.valueOf(accessibleMethod(helper, "getCurrentDimension", new Class<?>[0]).invoke(null)); | |
| } | |
| /** Runs ONLY on the Minecraft client thread. Refreshes observed_at. */ | |
| private void snapshot() { | |
| Map<String,Object> data = new LinkedHashMap<>(); | |
| try { | |
| Object client = client(); | |
| Class<?> alto = Class.forName("adris.altoclef.AltoClef"); | |
| Object mod = alto.getMethod("getInstance").invoke(null); | |
| Object executor = alto.getMethod("getCommandExecutor").invoke(null); | |
| Object player = mod == null ? null : call(mod, "getPlayer"); | |
| data.put("connected", player != null); | |
| data.put("initialized", executor != null); | |
| data.put("last_command", lastCommand); | |
| data.put("error", lastError); | |
| Object currentScreen = screen(client); | |
| boolean credits = currentScreen != null && mc("class_445").isInstance(currentScreen); | |
| if (credits) { | |
| if (enteredEnd) creditsObserved = true; | |
| if (creditsSince == 0) creditsSince = System.currentTimeMillis(); | |
| if (System.currentTimeMillis() - creditsSince > 12000) { | |
| method(currentScreen, "class_437", "method_25419", "()V", new Class<?>[0]); | |
| } | |
| } | |
| data.put("credits_screen", credits); | |
| data.put("credits_observed", creditsObserved); | |
| data.put("entered_end", enteredEnd); | |
| data.put("connect_state", connectState); | |
| data.put("connect_attempts", connectAttempts); | |
| if (player == null) { | |
| if (executor != null && !credits) connect(client, currentScreen); | |
| } else { | |
| String dimension = currentDimension(); | |
| if ("END".equals(dimension)) enteredEnd = true; | |
| data.put("dimension", dimension); | |
| data.put("returned_from_end", enteredEnd && "OVERWORLD".equals(dimension)); | |
| if (!dimension.equals(previousDimension)) { | |
| System.out.println("[space-altoclef] dimension=" + dimension); | |
| previousDimension = dimension; | |
| } | |
| data.put("position", Map.of("x", method(player,"class_1297","method_23317","()D",new Class<?>[0]), | |
| "y", method(player,"class_1297","method_23318","()D",new Class<?>[0]), | |
| "z", method(player,"class_1297","method_23321","()D",new Class<?>[0]))); | |
| data.put("health", method(player,"class_1309","method_6032","()F",new Class<?>[0])); | |
| Object controller = call(mod, "getController"); | |
| data.put("creative", method(controller,"class_636","method_2914","()Z",new Class<?>[0])); | |
| Object inventory = method(player,"class_1657","method_31548","()Lnet/minecraft/class_1661;",new Class<?>[0]); | |
| List<Object> items = new ArrayList<>(); | |
| for (int slot = 0; slot < 41; slot++) { | |
| Object stack = method(inventory,"class_1263","method_5438","(I)Lnet/minecraft/class_1799;",new Class<?>[]{int.class},slot); | |
| int count = (Integer) method(stack,"class_1799","method_7947","()I",new Class<?>[0]); | |
| if (count > 0) items.add(Map.of("slot",slot,"item",String.valueOf(method(stack,"class_1799","method_7909","()Lnet/minecraft/class_1792;",new Class<?>[0])),"count",count)); | |
| } | |
| data.put("inventory", items); | |
| Object chain = call(mod,"getUserTaskChain"); | |
| if (chain == null) { | |
| // Upstream builds the task chain on TitleScreen entry; a | |
| // null chain during loading reports initialized=false with | |
| // diagnostics, never an NPE, and never a fabricated | |
| // task_active. | |
| data.put("initialized", false); | |
| data.put("task_active", false); | |
| data.put("init_note", "taskchain not yet initialized (waiting TitleScreen entry)"); | |
| } else { | |
| data.put("task_active", call(chain,"isActive")); | |
| data.put("tasks", String.valueOf(call(chain,"getTasks"))); | |
| } | |
| data.put("entered_end", enteredEnd); | |
| lastError = ""; | |
| data.put("error", ""); | |
| } | |
| } catch (Exception err) { | |
| lastError = err.toString(); | |
| data.put("error", lastError); | |
| } | |
| data.put("heartbeat_at", System.currentTimeMillis() / 1000.0); | |
| data.put("observed_at", System.currentTimeMillis() / 1000.0); | |
| state = json(data); | |
| } | |
| private static String json(Object value) { | |
| if (value == null) return "null"; | |
| if (value instanceof Boolean || value instanceof Number) return value.toString(); | |
| if (value instanceof Map<?,?> map) { | |
| StringJoiner parts = new StringJoiner(",", "{", "}"); | |
| map.forEach((k,v) -> parts.add(json(String.valueOf(k)) + ":" + json(v))); | |
| return parts.toString(); | |
| } | |
| if (value instanceof Collection<?> list) { | |
| StringJoiner parts = new StringJoiner(",", "[", "]"); | |
| list.forEach(v -> parts.add(json(v))); return parts.toString(); | |
| } | |
| return "\"" + value.toString().replace("\\","\\\\").replace("\"","\\\"") | |
| .replace("\n","\\n").replace("\r","\\r").replace("\t","\\t") + "\""; | |
| } | |
| } | |