From 684dd3ac64635022c1d20e7a88acc44b77c6a88b Mon Sep 17 00:00:00 2001 From: Thomas Hopkins Date: Mon, 15 Jun 2026 17:38:35 -0400 Subject: [PATCH 1/2] Create new world instead of opening an existing one --- .../com/mineagent/ClientEventHandler.java | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/forge/src/main/java/com/mineagent/ClientEventHandler.java b/forge/src/main/java/com/mineagent/ClientEventHandler.java index b3d26ed..151fa0b 100644 --- a/forge/src/main/java/com/mineagent/ClientEventHandler.java +++ b/forge/src/main/java/com/mineagent/ClientEventHandler.java @@ -5,6 +5,7 @@ import java.nio.ByteBuffer; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.gui.screens.AccessibilityOnboardingScreen; import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.DeathScreen; import net.minecraft.client.gui.screens.PauseScreen; @@ -19,7 +20,10 @@ import org.lwjgl.opengl.GL11; import org.slf4j.Logger; -/** Handles client-side game events and coordinates input injection with observations. */ +/** + * Handles client-side game events and coordinates input injection with + * observations. + */ public class ClientEventHandler { private static final Logger LOGGER = LogUtils.getLogger(); private static final DataBridge dataBridge = DataBridge.getInstance(); @@ -42,33 +46,42 @@ public static void onClientTick(TickEvent.ClientTickEvent event) { } Minecraft mc = Minecraft.getInstance(); boolean inWorld = mc.level != null && mc.player != null; + boolean onAccessibilityScreen = mc.screen instanceof AccessibilityOnboardingScreen; boolean onTitleScreen = mc.screen instanceof TitleScreen; boolean onDeathScreen = mc.screen instanceof DeathScreen; boolean onPauseScreen = mc.screen instanceof PauseScreen; boolean isMenu = mc.screen != null; boolean inWorldWithOverlay = mc.level != null && mc.screen != null; - - if (onTitleScreen) { - mc.createWorldOpenFlows().openWorld("New World", () -> { - mc.forceSetScreen(new TitleScreen()); - }); + LOGGER.info(mc.screen.toString()); + + if (onTitleScreen || onAccessibilityScreen) { + // mc.createWorldOpenFlows().openWorld("New World", () -> { + // mc.forceSetScreen(new TitleScreen()); + // }); + mc.createWorldOpenFlows().createFreshLevel( + "new-world", // String: folder name for the save + "New World", // String: human-readable world name + WorldPresets.NORMAL, // or FLAT, AMPLIFIED, etc. -- ResourceKey + null, // @Nullable Screen to return to on cancel + null // @Nullable WorldCreationContext overrides + ); // final ResetInput resetInput = dataBridge.getLatestResetInput(); // if (resetInput != null) { - // // TODO: Open new world? This currently only opens existing world - // // Minecraft mc = Minecraft.getInstance(); - // // createFreshLevel opens the world creation flow. - // // For a quick programmatic world with defaults: - // // mc.createWorldOpenFlows().createFreshLevel( - // // saveName, // String: folder name for the save - // // displayName, // String: human-readable world name - // // WorldPresets.NORMAL, // or FLAT, AMPLIFIED, etc. -- ResourceKey - // // null, // @Nullable Screen to return to on cancel - // // null // @Nullable WorldCreationContext overrides - // // ); + // // TODO: Open new world? This currently only opens existing world + // // Minecraft mc = Minecraft.getInstance(); + // // createFreshLevel opens the world creation flow. + // // For a quick programmatic world with defaults: + // // mc.createWorldOpenFlows().createFreshLevel( + // // saveName, // String: folder name for the save + // // displayName, // String: human-readable world name + // // WorldPresets.NORMAL, // or FLAT, AMPLIFIED, etc. -- + // ResourceKey + // // null, // @Nullable Screen to return to on cancel + // // null // @Nullable WorldCreationContext overrides + // // ); // } return; - } - else if (onPauseScreen) { + } else if (onPauseScreen) { mc.setScreen(null); return; } @@ -96,14 +109,17 @@ else if (onPauseScreen) { // This fires press events and sets KeyMapping states for held buttons dataBridge.getInputInjector().maintainButtonState(); - // Observations only while the local player exists (no capture on death screen). Extrinsic - // reward queued during death therefore attaches to the first frame after respawn if needed. + // Observations only while the local player exists (no capture on death screen). + // Extrinsic + // reward queued during death therefore attaches to the first frame after + // respawn if needed. double reward = dataBridge.takeExtrinsicReward(); dataBridge.sendObservation(new Observation(reward, captureFrame())); } /** - * Handles input suppression when a Python client is connected. Disables the system cursor to + * Handles input suppression when a Python client is connected. Disables the + * system cursor to * prevent real mouse input from interfering. */ private static void handleInputSuppression(Minecraft mc) { @@ -146,7 +162,9 @@ public static void onPlayerDeath(LivingDeathEvent event) { } } - /** The player the agent controls on this machine (not other players or mobs). */ + /** + * The player the agent controls on this machine (not other players or mobs). + */ private static boolean isClientControlledPlayer(LivingEntity entity) { return entity instanceof LocalPlayer p && p == Minecraft.getInstance().player; } From 2df3ea21badcf7ae247ab5aa45b4e37d1a3b423e Mon Sep 17 00:00:00 2001 From: Thomas Hopkins Date: Wed, 24 Jun 2026 17:58:55 -0400 Subject: [PATCH 2/2] Add configurable new world and world name --- .../com/mineagent/ClientEventHandler.java | 57 ++++++++++--------- forge/src/main/java/com/mineagent/Config.java | 19 +++++++ 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/forge/src/main/java/com/mineagent/ClientEventHandler.java b/forge/src/main/java/com/mineagent/ClientEventHandler.java index 151fa0b..8946b0f 100644 --- a/forge/src/main/java/com/mineagent/ClientEventHandler.java +++ b/forge/src/main/java/com/mineagent/ClientEventHandler.java @@ -4,12 +4,19 @@ import com.mojang.logging.LogUtils; import java.nio.ByteBuffer; import net.minecraft.client.Minecraft; -import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.gui.screens.AccessibilityOnboardingScreen; -import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.DeathScreen; import net.minecraft.client.gui.screens.PauseScreen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.Difficulty; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.WorldDataConfiguration; +import net.minecraft.world.level.levelgen.WorldOptions; +import net.minecraft.world.level.levelgen.presets.WorldPresets; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; @@ -52,34 +59,28 @@ public static void onClientTick(TickEvent.ClientTickEvent event) { boolean onPauseScreen = mc.screen instanceof PauseScreen; boolean isMenu = mc.screen != null; boolean inWorldWithOverlay = mc.level != null && mc.screen != null; - LOGGER.info(mc.screen.toString()); if (onTitleScreen || onAccessibilityScreen) { - // mc.createWorldOpenFlows().openWorld("New World", () -> { - // mc.forceSetScreen(new TitleScreen()); - // }); - mc.createWorldOpenFlows().createFreshLevel( - "new-world", // String: folder name for the save - "New World", // String: human-readable world name - WorldPresets.NORMAL, // or FLAT, AMPLIFIED, etc. -- ResourceKey - null, // @Nullable Screen to return to on cancel - null // @Nullable WorldCreationContext overrides - ); - // final ResetInput resetInput = dataBridge.getLatestResetInput(); - // if (resetInput != null) { - // // TODO: Open new world? This currently only opens existing world - // // Minecraft mc = Minecraft.getInstance(); - // // createFreshLevel opens the world creation flow. - // // For a quick programmatic world with defaults: - // // mc.createWorldOpenFlows().createFreshLevel( - // // saveName, // String: folder name for the save - // // displayName, // String: human-readable world name - // // WorldPresets.NORMAL, // or FLAT, AMPLIFIED, etc. -- - // ResourceKey - // // null, // @Nullable Screen to return to on cancel - // // null // @Nullable WorldCreationContext overrides - // // ); - // } + String worldName = Config.WORLD_NAME.get(); + if (Config.CREATE_NEW_WORLD.get()) { + String folderName = worldName.toLowerCase().replace(' ', '-'); + mc.createWorldOpenFlows() + .createFreshLevel( + folderName, + new LevelSettings( + worldName, + GameType.SURVIVAL, + false, + Difficulty.NORMAL, + false, + new GameRules(WorldDataConfiguration.DEFAULT.enabledFeatures()), + WorldDataConfiguration.DEFAULT), + WorldOptions.defaultWithRandomSeed(), + WorldPresets::createNormalWorldDimensions, + new TitleScreen()); + } else { + mc.createWorldOpenFlows().openWorld(worldName, () -> mc.forceSetScreen(new TitleScreen())); + } return; } else if (onPauseScreen) { mc.setScreen(null); diff --git a/forge/src/main/java/com/mineagent/Config.java b/forge/src/main/java/com/mineagent/Config.java index c769aa3..fce0510 100644 --- a/forge/src/main/java/com/mineagent/Config.java +++ b/forge/src/main/java/com/mineagent/Config.java @@ -23,6 +23,9 @@ public class Config { public static final ForgeConfigSpec.ConfigValue EXTRINSIC_DAMAGE_PER_POINT; public static final ForgeConfigSpec.ConfigValue DEATH_PENALTY; + public static final ForgeConfigSpec.ConfigValue CREATE_NEW_WORLD; + public static final ForgeConfigSpec.ConfigValue WORLD_NAME; + // Built Configuration Specification public static final ForgeConfigSpec SPEC; @@ -110,6 +113,22 @@ public class Config { BUILDER.pop(); + BUILDER.comment("World Configuration"); + BUILDER.push("world"); + + CREATE_NEW_WORLD = + BUILDER + .comment( + "If true, create a new world on startup; if false, load an existing save with the given name") + .define("create_new_world", false); + + WORLD_NAME = + BUILDER + .comment("Display name of the world to load or create (e.g. \"New World\")") + .define("world_name", "New World"); + + BUILDER.pop(); + // Build the specification after all values are defined SPEC = BUILDER.build(); }