Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 46 additions & 27 deletions forge/src/main/java/com/mineagent/ClientEventHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +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.TitleScreen;
import net.minecraft.client.gui.screens.AccessibilityOnboardingScreen;
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;
Expand All @@ -19,7 +27,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();
Expand All @@ -42,33 +53,36 @@ 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());
});
// 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<WorldPreset>
// // null, // @Nullable Screen to return to on cancel
// // null // @Nullable WorldCreationContext overrides
// // );
// }
if (onTitleScreen || onAccessibilityScreen) {
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) {
} else if (onPauseScreen) {
mc.setScreen(null);
return;
}
Expand Down Expand Up @@ -96,14 +110,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) {
Expand Down Expand Up @@ -146,7 +163,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;
}
Expand Down
19 changes: 19 additions & 0 deletions forge/src/main/java/com/mineagent/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class Config {
public static final ForgeConfigSpec.ConfigValue<Double> EXTRINSIC_DAMAGE_PER_POINT;
public static final ForgeConfigSpec.ConfigValue<Double> DEATH_PENALTY;

public static final ForgeConfigSpec.ConfigValue<Boolean> CREATE_NEW_WORLD;
public static final ForgeConfigSpec.ConfigValue<String> WORLD_NAME;

// Built Configuration Specification
public static final ForgeConfigSpec SPEC;

Expand Down Expand Up @@ -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();
}
Expand Down
Loading