Add /lw hud debug command to toggle region HUD without compass

/lw hud toggles a persistent per-player flag stored in LivingWorldBootstrap.
When enabled, the action-bar HUD (Eco/Poll/Soil/Wat) is shown every 20 ticks
alongside the existing compass-based trigger. Flag is cleared on server stop.
Requires operator permission level 2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
George
2026-06-07 20:02:38 +01:00
parent c9f927b265
commit 67a1e07b82
3 changed files with 50 additions and 7 deletions
@@ -235,8 +235,11 @@ public class LivingWorldMod {
} }
} }
// Step 2: Compass HUD — display region health while holding a compass. // Step 2: Compass HUD — display region health while holding a compass, or debug HUD is on.
if (player.getMainHandItem().is(Items.COMPASS) || player.getOffhandItem().is(Items.COMPASS)) { boolean showHud = player.getMainHandItem().is(Items.COMPASS)
|| player.getOffhandItem().is(Items.COMPASS)
|| bootstrap.isHudEnabled(player.getUUID());
if (showHud) {
Optional<RegionMetrics> metricsOpt = bootstrap.getMetricsAt(dimId, player.getX(), player.getZ()); Optional<RegionMetrics> metricsOpt = bootstrap.getMetricsAt(dimId, player.getX(), player.getZ());
metricsOpt.ifPresent(m -> player.displayClientMessage(buildHud(coord, m), true)); metricsOpt.ifPresent(m -> player.displayClientMessage(buildHud(coord, m), true));
} }
@@ -48,10 +48,13 @@ import com.mojang.brigadier.CommandDispatcher;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.Set;
import java.util.UUID;
import com.livingworld.regions.RegionMetrics; import com.livingworld.regions.RegionMetrics;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
@@ -70,6 +73,7 @@ public final class LivingWorldBootstrap {
private double windAngle = 0.0; private double windAngle = 0.0;
private final Random windRandom = new Random(); private final Random windRandom = new Random();
private final Set<UUID> hudEnabledPlayers = new HashSet<>();
private PlatformAdapter platformAdapter; private PlatformAdapter platformAdapter;
private Path worldSaveDirectory; private Path worldSaveDirectory;
@@ -199,6 +203,7 @@ public final class LivingWorldBootstrap {
} }
regionManager.flushAll(); regionManager.flushAll();
moduleRegistry.shutdownAll(); moduleRegistry.shutdownAll();
hudEnabledPlayers.clear();
serverReady = false; serverReady = false;
LivingWorldLogger.info( LivingWorldLogger.info(
DiagnosticCategory.BOOTSTRAP, DiagnosticCategory.BOOTSTRAP,
@@ -413,13 +418,25 @@ public final class LivingWorldBootstrap {
} }
} }
/** Toggles the debug HUD for the given player. Returns true if HUD is now enabled. */
public boolean toggleHud(UUID playerId) {
if (hudEnabledPlayers.remove(playerId)) return false;
hudEnabledPlayers.add(playerId);
return true;
}
public boolean isHudEnabled(UUID playerId) {
return hudEnabledPlayers.contains(playerId);
}
public void registerCommands(CommandDispatcher<CommandSourceStack> dispatcher) { public void registerCommands(CommandDispatcher<CommandSourceStack> dispatcher) {
requireInitialized(); requireInitialized();
LivingWorldCommandRoot.registerDeferred( LivingWorldCommandRoot.registerDeferred(
dispatcher, dispatcher,
() -> requireService(regionManager, "regionManager"), () -> requireService(regionManager, "regionManager"),
() -> requireService(moduleRegistry, "moduleRegistry"), () -> requireService(moduleRegistry, "moduleRegistry"),
() -> requireService(simulationManager, "simulationManager")); () -> requireService(simulationManager, "simulationManager"),
this::toggleHud);
} }
public Path getWorldSaveDirectory() { public Path getWorldSaveDirectory() {
@@ -1,14 +1,18 @@
package com.livingworld.commands; package com.livingworld.commands;
import java.util.stream.Collectors; import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands; import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import com.livingworld.core.simulation.SimulationManager; import com.livingworld.core.simulation.SimulationManager;
import com.livingworld.modules.ModuleRegistry; import com.livingworld.modules.ModuleRegistry;
@@ -33,14 +37,16 @@ public final class LivingWorldCommandRoot {
dispatcher, dispatcher,
() -> regionManager, () -> regionManager,
() -> moduleRegistry, () -> moduleRegistry,
() -> simulationManager); () -> simulationManager,
uuid -> false);
} }
public static void registerDeferred( public static void registerDeferred(
CommandDispatcher<CommandSourceStack> dispatcher, CommandDispatcher<CommandSourceStack> dispatcher,
Supplier<RegionManager> regionManager, Supplier<RegionManager> regionManager,
Supplier<ModuleRegistry> moduleRegistry, Supplier<ModuleRegistry> moduleRegistry,
Supplier<SimulationManager> simulationManager) { Supplier<SimulationManager> simulationManager,
Function<UUID, Boolean> hudToggle) {
if (dispatcher == null) { if (dispatcher == null) {
throw new IllegalArgumentException("dispatcher must not be null"); throw new IllegalArgumentException("dispatcher must not be null");
} }
@@ -96,7 +102,24 @@ public final class LivingWorldCommandRoot {
.executes(context -> SimulateCommand.execute( .executes(context -> SimulateCommand.execute(
context.getSource(), context.getSource(),
requireService(simulationManager, "simulationManager"), requireService(simulationManager, "simulationManager"),
IntegerArgumentType.getInteger(context, "ticks")))))); IntegerArgumentType.getInteger(context, "ticks")))))
.then(Commands.literal("hud")
.executes(context -> toggleHud(context.getSource(), hudToggle))));
}
private static int toggleHud(CommandSourceStack source, Function<UUID, Boolean> hudToggle) {
ServerPlayer player;
try {
player = source.getPlayerOrException();
} catch (CommandSyntaxException e) {
source.sendFailure(Component.literal("/lw hud can only be used by a player"));
return 0;
}
boolean nowEnabled = hudToggle.apply(player.getUUID());
source.sendSuccess(
() -> Component.literal("Region HUD " + (nowEnabled ? "enabled" : "disabled")),
false);
return 1;
} }
private static <T> T requireService(Supplier<T> supplier, String name) { private static <T> T requireService(Supplier<T> supplier, String name) {