Volver al índice
src/main/java/ar/com/companeros/Commands.java
package ar.com.companeros;
import ar.com.companeros.runtime.AgentRuntime;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.event.RegisterCommandsEvent;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
/** Comandos de jugadores autónomos y compatibilidad con los compañeros del prototipo. */
public final class Commands {
private Commands() { }
public static void register(RegisterCommandsEvent event) {
var root = literal("raps").requires(source ->
!(source.getEntity() instanceof net.minecraftforge.common.util.FakePlayer)
&& !(source.getEntity() instanceof net.minecraft.server.level.ServerPlayer player
&& ar.com.companeros.identity.AgentRegistry.get(source.getServer()).find(player.getUUID()).isPresent()));
root.executes(context -> {
context.getSource().sendSuccess(() -> Component.literal(
"Unir uno: /Raps join bot <nombre> · Unir todos: /Raps join bots · Ver bots: /Raps estado · Ayuda: /Raps ayuda"), false);
return 1;
});
// Jugadores registrados: el runtime comprueba identidad, permisos y población.
root.then(literal("crear").requires(source -> source.hasPermission(2))
.then(argument("nombre", StringArgumentType.word())
.executes(context -> AgentRuntime.create(context.getSource(), name(context)))));
root.then(literal("conectar").then(argument("nombre", StringArgumentType.word())
.executes(context -> AgentRuntime.connect(context.getSource(), name(context)))));
root.then(literal("join").requires(source -> source.hasPermission(2))
.then(literal("bot").then(argument("nombre", StringArgumentType.word())
.suggests((context, builder) -> net.minecraft.commands.SharedSuggestionProvider.suggest(
AgentRuntime.joinableNames(context.getSource().getServer()), builder))
.executes(context -> AgentRuntime.connect(context.getSource(), name(context)))))
.then(literal("bots").executes(context -> AgentRuntime.connectAll(context.getSource()))));
root.then(literal("ayuda").executes(context -> {
for (String line : new String[]{
"/Raps join bot Neon — conecta un bot guardado",
"/Raps join bots — conecta todos los bots disponibles",
"/Raps crear Nombre — crea un bot nuevo",
"/Raps rol Neon investigador · /Raps rol Xenon generalista",
"/Raps skin Neon CuentaMinecraft · /Raps skin Neon default",
"/Raps autonomo Nombre — permite decidir a la IA cuando esté habilitada",
"/Raps estado · /Raps inventario Nombre",
"/Raps pausar Nombre — guarda y desconecta",
"/Raps tools estado Nombre · /Raps tools proponer Nombre JSON",
"/Raps herramienta Nombre create_banner JSON · place_banner JSON",
"/Raps mimicer estado · meteorito · infectar Nombre · etapa 0..4 · avanzar · spawn",
"/sensitivity mode enabled · /sensitivity mode disabled",
"/botmalo Nombre mensaje · /botmalo all mensaje"})
context.getSource().sendSuccess(() -> Component.literal(line), false);
return 1;
}));
root.then(literal("pausar").then(argument("nombre", StringArgumentType.word())
.executes(context -> registered(context) ? AgentRuntime.pause(context.getSource(), name(context))
: changeLegacyMode(context, "quieto"))));
// El cuerpo inicial sigue disponible para comparar y mantener mundos anteriores.
root.then(literal("crear_prototipo").requires(source -> source.hasPermission(2))
.then(argument("nombre", StringArgumentType.word())
.executes(context -> createPrototype(context, 24))
.then(argument("radio", IntegerArgumentType.integer(8, 32))
.executes(context -> createPrototype(context, IntegerArgumentType.getInteger(context, "radio"))))));
root.then(literal("campamento").then(argument("nombre", StringArgumentType.word()).executes(Commands::camp)));
root.then(literal("estado").executes(Commands::list));
root.then(literal("inventario").then(argument("nombre", StringArgumentType.word()).executes(Commands::inventory)));
root.then(literal("entregar").then(argument("nombre", StringArgumentType.word()).executes(Commands::handOver)));
for (String mode : new String[]{"autonomo", "seguir", "quieto"}) {
root.then(literal(mode).then(argument("nombre", StringArgumentType.word()).executes(context -> {
if (registered(context)) return AgentRuntime.setMode(context.getSource(), name(context), mode);
return changeLegacyMode(context, mode);
})));
}
root.then(literal("retirar").requires(source -> source.hasPermission(2))
.then(argument("nombre", StringArgumentType.word()).executes(Commands::retire)));
// Herramientas supervisadas y escalones de población permitidos para las pruebas.
root.then(literal("herramientas").executes(context -> {
var tools = AgentRuntime.toolNames(context.getSource().getServer());
for (String tool : tools) context.getSource().sendSuccess(() -> Component.literal(tool), false);
if (tools.isEmpty()) context.getSource().sendSuccess(
() -> Component.literal("Todavía no hay herramientas disponibles."), false);
return tools.size();
}));
root.then(literal("herramienta").requires(source -> source.hasPermission(2))
.then(argument("nombre", StringArgumentType.word())
.then(argument("herramienta", StringArgumentType.word())
.executes(context -> runTool(context, "{}"))
.then(argument("argumentos", StringArgumentType.greedyString())
.executes(context -> runTool(context, StringArgumentType.getString(context, "argumentos")))))));
var population = literal("fase").requires(source -> source.hasPermission(2));
for (int limit : new int[]{2, 4, 6, 10})
population.then(literal(Integer.toString(limit)).executes(
context -> AgentRuntime.setPopulationLimit(context.getSource(), limit)));
root.then(population);
// Controles de aprendizaje: propuestas con autor real, nunca aprobación por chat.
root.then(literal("tools").requires(source -> source.hasPermission(2))
.then(literal("estado").then(argument("nombre", StringArgumentType.word())
.executes(context -> AgentRuntime.toolState(context.getSource(), name(context)))))
.then(literal("proponer").then(argument("nombre", StringArgumentType.word())
.then(argument("manifiesto", StringArgumentType.greedyString()).executes(context ->
AgentRuntime.proposeTool(context.getSource(), name(context), StringArgumentType.getString(context, "manifiesto")))))));
// Especialización guardada; puede asignarse antes de conectar al jugador.
root.then(literal("rol").requires(source -> source.hasPermission(2))
.then(argument("nombre", StringArgumentType.word())
.then(literal("investigador").executes(c -> AgentRuntime.setRole(c.getSource(), name(c), ar.com.companeros.identity.AgentRoles.Role.INVESTIGADOR)))
.then(literal("generalista").executes(c -> AgentRuntime.setRole(c.getSource(), name(c), ar.com.companeros.identity.AgentRoles.Role.GENERALISTA)))));
ar.com.companeros.horror.HorrorModule.registerCommands(root);
root.then(literal("skin").requires(source -> source.hasPermission(2))
.then(argument("nombre", StringArgumentType.word())
.executes(c -> ar.com.companeros.identity.SkinProfiles.command(c.getSource(), name(c), null))
.then(argument("cuenta", StringArgumentType.word()).executes(c -> ar.com.companeros.identity.SkinProfiles.command(
c.getSource(), name(c), StringArgumentType.getString(c, "cuenta"))))));
ar.com.companeros.horror.HorrorModule.registerSensitivity(event.getDispatcher());
var registered = event.getDispatcher().register(root);
event.getDispatcher().register(literal("Raps").requires(registered.getRequirement()).executes(registered.getCommand()).redirect(registered));
event.getDispatcher().register(literal("companeros").requires(registered.getRequirement()).executes(registered.getCommand()).redirect(registered));
}
private static String name(CommandContext<CommandSourceStack> context) {
return StringArgumentType.getString(context, "nombre");
}
private static boolean registered(CommandContext<CommandSourceStack> context) {
return AgentRuntime.isRegisteredPlayer(context.getSource().getServer(), name(context));
}
private static int runTool(CommandContext<CommandSourceStack> context, String json) throws CommandSyntaxException {
return AgentRuntime.runTool(context.getSource(), name(context),
StringArgumentType.getString(context, "herramienta"), json);
}
// Adaptación de controles: primero jugadores reales; después el inventario/cuerpo legado.
private static int changeLegacyMode(CommandContext<CommandSourceStack> context, String mode) throws CommandSyntaxException {
Companion bot = owned(context);
if (bot == null) return 0;
bot.mode = mode; bot.resource = null; bot.miningProgress = 0;
bot.getNavigation().stop(); bot.setTarget(null);
context.getSource().sendSuccess(() -> Component.literal(bot.getName().getString() + ": " + mode), false);
return 1;
}
private static int handOver(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
if (registered(context)) return AgentRuntime.handOver(context.getSource(), name(context));
Companion bot = owned(context);
if (bot == null) return 0;
var player = context.getSource().getPlayerOrException();
if (bot.distanceToSqr(player) > 16) {
context.getSource().sendFailure(Component.literal("Acercate al compañero para recibir sus objetos.")); return 0;
}
for (int i = 0; i < bot.bag.getContainerSize(); i++) {
var stack = bot.bag.removeItemNoUpdate(i);
if (!stack.isEmpty()) {
var drop = bot.spawnAtLocation(stack);
if (drop != null) { drop.setTarget(player.getUUID()); drop.setThrower(player.getUUID()); }
}
}
bot.setItemSlot(net.minecraft.world.entity.EquipmentSlot.MAINHAND, net.minecraft.world.item.ItemStack.EMPTY);
context.getSource().sendSuccess(() -> Component.literal("Objetos entregados."), false);
return 1;
}
private static int retire(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
if (registered(context)) return AgentRuntime.retire(context.getSource(), name(context));
Companion bot = owned(context);
if (bot == null) return 0;
bot.releaseInventory(); WorkArea.get(bot.serverLevel()).forget(bot.getUUID());
var registry = ar.com.companeros.identity.AgentRegistry.get(bot.serverLevel().getServer());
registry.find(bot.getUUID()).ifPresent(identity -> registry.markState(identity.id(), ar.com.companeros.identity.AgentState.RETIRED));
bot.discard();
context.getSource().sendSuccess(() -> Component.literal("Compañero retirado."), false);
return 1;
}
private static int createPrototype(CommandContext<CommandSourceStack> context, int radius) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
var level = player.serverLevel();
String name = StringArgumentType.getString(context, "nombre");
if (!name.matches("[A-Za-z0-9_]{1,16}")) {
context.getSource().sendFailure(Component.literal("Usá un nombre de hasta 16 letras, números o guiones bajos."));
return 0;
}
long existing = WorkArea.get(level).companionCount();
if (AgentRuntime.isRegisteredPlayer(context.getSource().getServer(), name)
|| existing >= 4 || level.getEntities(Companeros.COMPANION.get(), bot -> bot.getName().getString().equals(name)).size() > 0) {
context.getSource().sendFailure(Component.literal("Ya existe ese nombre o hay cuatro compañeros en esta dimensión."));
return 0;
}
Companion bot = Companeros.COMPANION.get().create(level);
if (bot == null) return 0;
bot.moveTo(player.getX() + 1.5, player.getY(), player.getZ(), player.getYRot(), 0);
if (!level.noCollision(bot) || !level.getFluidState(bot.blockPosition()).isEmpty()) {
context.getSource().sendFailure(Component.literal("Necesitás espacio libre a tu lado.")); return 0;
}
bot.owner = player.getUUID(); bot.home = bot.blockPosition(); bot.radius = radius;
bot.mode = "quieto"; bot.activity = "Esperando campamento";
bot.setCustomName(Component.literal(name)); bot.setCustomNameVisible(true);
if (!level.addFreshEntity(bot)) return 0;
context.getSource().sendSuccess(() -> Component.literal(name + " se sumó a la aventura."), false);
return 1;
}
private static Companion owned(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
var player = context.getSource().getPlayerOrException();
String name = StringArgumentType.getString(context, "nombre");
for (Companion bot : player.serverLevel().getEntitiesOfClass(Companion.class,
player.getBoundingBox().inflate(96), entity -> entity.getName().getString().equals(name))) {
if (player.getUUID().equals(bot.owner) || context.getSource().hasPermission(2)) return bot;
}
context.getSource().sendFailure(Component.literal("No encontré un compañero tuyo con ese nombre cerca."));
return null;
}
private static int camp(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
if (registered(context)) return AgentRuntime.setCamp(context.getSource(), name(context));
Companion bot = owned(context);
if (bot == null) return 0;
var home = context.getSource().getPlayerOrException().blockPosition();
if (!bot.serverLevel().getEntitiesOfClass(Companion.class, new AABB(home).inflate(8),
other -> other != bot && other.building && other.home != null && other.home.distSqr(home) < 64).isEmpty()) {
context.getSource().sendFailure(Component.literal("Separá los campamentos por al menos ocho bloques.")); return 0;
}
if (!Shelter.clearSite(bot, home)) {
context.getSource().sendFailure(Component.literal("Elegí un terreno plano con 5×5 bloques libres y cuatro de altura."));
return 0;
}
bot.home = home.immutable(); bot.building = true; bot.shelterStep = 0;
bot.resource = null; bot.searchCursor = 0; bot.blocked.clear(); bot.mode = "autonomo";
context.getSource().sendSuccess(() -> Component.literal("Campamento de " + bot.getName().getString() + " marcado."), false);
return 1;
}
private static int list(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
var server = context.getSource().getServer();
var descriptions = AgentRuntime.describe(server);
int total = descriptions.size();
for (String description : descriptions)
context.getSource().sendSuccess(() -> Component.literal(description), false);
var seen = new java.util.HashSet<java.util.UUID>();
for (var level : server.getAllLevels()) {
var bots = level.getEntities(Companeros.COMPANION.get(), bot ->
!AgentRuntime.isRegisteredPlayer(server, bot.getName().getString()));
for (Companion bot : bots) {
if (!seen.add(bot.getUUID())) continue;
total++;
context.getSource().sendSuccess(() -> Component.literal(
bot.getName().getString() + " · " + bot.activity + " · Salud " + (int) bot.getHealth()
+ "/20 · Comida " + bot.hunger + "/20 · " + bot.blockPosition().toShortString()), false);
}
}
if (total == 0) context.getSource().sendSuccess(() -> Component.literal("No hay compañeros."), false);
return total;
}
private static int inventory(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
if (registered(context)) return AgentRuntime.inventory(context.getSource(), name(context));
Companion bot = owned(context);
if (bot == null) return 0;
java.util.List<String> items = new java.util.ArrayList<>();
for (int i = 0; i < bot.bag.getContainerSize(); i++) {
var stack = bot.bag.getItem(i);
if (!stack.isEmpty()) items.add(stack.getCount() + " " + stack.getHoverName().getString());
}
context.getSource().sendSuccess(() -> Component.literal(bot.getName().getString() + ": "
+ (items.isEmpty() ? "Inventario vacío" : String.join(", ", items))), false);
return 1;
}
}