Choose cursor aim with /aero controls instead of a keybind

/aero controls keyboard|cursor_aim is open to every player, stored per player
and synced to the client, which switches on automatically when seated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVREx3uo7nz1FEhzveWTUh
This commit is contained in:
Claude committed 2026-11-11 18:29:27 +01:00
1 parent d66e8a83d2
commit 2f4f90c773
10 files changed
+133 -20

No files matched your search

@@ -7,6 +7,7 @@ import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import dev.lumareth.improvedAerodynamics.exhaust.NozzleCommands;
import dev.lumareth.improvedAerodynamics.flight.ControlMode;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
@@ -21,7 +22,8 @@ import java.util.List;
/**
* /improvedaerodynamics (or /aero) to read and change the most important server config values in game.
* {@code /aero} lists them, {@code /aero <option>} shows one, {@code /aero <option> <value>} sets and saves it.
* Needs operator rights, except for the owner of a singleplayer world, who may use them without cheats.
* The config options need operator rights, except for the owner of a singleplayer world, who may use them without cheats.
* {@code /aero controls keyboard|cursor_aim} is open to every player and picks how they fly.
* All of them are read live, so changes apply immediately.
*/
public final class ConfigCommands {
@@ -73,21 +75,47 @@ public final class ConfigCommands {
@SubscribeEvent
public static void onRegisterCommands(RegisterCommandsEvent event) {
// The root is open to everybody for /aero controls, the config options check for rights themselves
LiteralArgumentBuilder<CommandSourceStack> root = Commands.literal("improvedaerodynamics")
.requires(ConfigCommands::mayUse)
.executes(context -> list(context.getSource()));
.executes(context -> mayUse(context.getSource()) ? list(context.getSource()) : denied(context.getSource()));
for (Option<?> option : OPTIONS) {
root.then(optionNode(option));
root.then(optionNode(option).requires(ConfigCommands::mayUse));
}
root.then(SeedgenCommands.node());
root.then(NozzleCommands.node());
root.then(SeedgenCommands.node().requires(ConfigCommands::mayUse));
root.then(NozzleCommands.node().requires(ConfigCommands::mayUse));
root.then(controlsNode());
LiteralCommandNode<CommandSourceStack> node = event.getDispatcher().register(root);
event.getDispatcher().register(Commands.literal("aero")
.requires(ConfigCommands::mayUse)
.executes(context -> list(context.getSource()))
.executes(context -> mayUse(context.getSource()) ? list(context.getSource()) : denied(context.getSource()))
.redirect(node));
}
private static int denied(CommandSourceStack source) {
source.sendFailure(Component.literal("Use /aero controls keyboard or /aero controls cursor_aim"));
return 0;
}
/** /aero controls [keyboard|cursor_aim], for every player (permission level 0). */
private static LiteralArgumentBuilder<CommandSourceStack> controlsNode() {
return Commands.literal("controls")
.requires(source -> source.getEntity() instanceof ServerPlayer)
.executes(context -> {
ServerPlayer player = context.getSource().getPlayerOrException();
boolean cursorAim = ControlMode.isCursorAim(player);
context.getSource().sendSuccess(() -> Component.literal("Controls: " + (cursorAim ? "cursor_aim" : "keyboard")), false);
return 1;
})
.then(Commands.literal("keyboard").executes(context -> setControls(context.getSource(), false)))
.then(Commands.literal("cursor_aim").executes(context -> setControls(context.getSource(), true)));
}
private static int setControls(CommandSourceStack source, boolean cursorAim) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
ServerPlayer player = source.getPlayerOrException();
ControlMode.set(player, cursorAim);
source.sendSuccess(() -> Component.literal("Controls set to " + (cursorAim ? "cursor_aim" : "keyboard")), false);
return 1;
}
private static <T> LiteralArgumentBuilder<CommandSourceStack> optionNode(Option<T> option) {
return Commands.literal(option.name())
.executes(context -> {