From f0b28375e6b85303a2f1192d422a0da6884d03c8 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 24 Jan 2026 09:15:48 -0800 Subject: [PATCH 01/10] Command - initial command work --- .../plugin/elements/events/Command.java | 215 ++++++++++++++++++ .../plugin/elements/events/EventHandler.java | 1 + 2 files changed, 216 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java new file mode 100644 index 00000000..39b694f4 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java @@ -0,0 +1,215 @@ +package com.github.skriptdev.skript.plugin.elements.events; + +import com.github.skriptdev.skript.plugin.HySk; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.command.system.AbstractCommand; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.CommandSender; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.file.FileSection; +import io.github.syst3ms.skriptparser.lang.CodeSection; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.SkriptEvent; +import io.github.syst3ms.skriptparser.lang.Statement; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.entries.SectionConfiguration; +import io.github.syst3ms.skriptparser.log.ErrorType; +import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.parsing.ParserState; +import io.github.syst3ms.skriptparser.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public class Command extends SkriptEvent { + + public static class ScriptCommandContext implements TriggerContext { + + private final String command; + private final CommandSender sender; + private final Player player; + private final World world; + + public ScriptCommandContext(String command, CommandSender sender, Player player, World world) { + this.command = command; + this.sender = sender; + this.player = player; + this.world = world; + } + + public String getCommand() { + return this.command; + } + + public CommandSender[] getSender() { + return new CommandSender[]{sender}; + } + + public World[] getWorld() { + return new World[]{world}; + } + + public Player[] getPlayer() { + return new Player[]{this.player}; + } + + @Override + public String getName() { + return "command context"; + } + } + + public static void register(SkriptRegistration registration) { + registration.newEvent(Command.class, + "[global] command <.+>", + "player command <.+>", + "world command <.+>") + .setHandledContexts(ScriptCommandContext.class) + .name("Command") + .description("Create a command.", + "- `Description` = The description for your command that will show in the commands gui (required).", + "- `Permission` = The permission required to execute the command (optional).") + .examples("command /kill:", + "\tdescription: Kill all the players", + "\ttrigger:", + "\t\tkill all players", + "", + "player command /clear:", + "\tpermission: my.script.command.clear", + "\tdescription: Clear your inventory", + "\ttrigger:", + "\t\tclear inventory of player", + "\t\tsend \"Your inventory has been cleared\" to player", + "", + "world command /spawn:", + "\tdescription: Will teleport all players to the world spawn", + "\ttrigger:", + "\t\tteleport all players to spawn location of context-world") + .since("INSERT VERSION") + .register(); + + registration.newContextValue(ScriptCommandContext.class, Player.class, true, + "player", ScriptCommandContext::getPlayer) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); + + registration.newContextValue(ScriptCommandContext.class, CommandSender.class, true, + "sender", ScriptCommandContext::getSender) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); + + registration.newContextValue(ScriptCommandContext.class, World.class, true, + "world", ScriptCommandContext::getWorld) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); + + registration.newContextValue(ScriptCommandContext.class, String.class, true, + "command", ct -> new String[]{ct.getCommand()}) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); + + } + + private final SectionConfiguration sec = new SectionConfiguration.Builder() + .addOptionalKey("permission") + .addOptionalKey("description") + .addSection("trigger") + .build(); + + private String command; + private int commandType; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { + this.command = parseContext.getMatches().getFirst().group(); + if (this.command.startsWith("/")) { + this.command = this.command.substring(1); + } + this.commandType = matchedPattern; + return true; + } + + @Override + public List loadSection(@NotNull FileSection section, @NotNull ParserState parserState, @NotNull SkriptLogger logger) { + this.sec.loadConfiguration(null, section, parserState, logger); + Optional triggerSec = this.sec.getSection("trigger"); + if (triggerSec.isEmpty()) return List.of(); + + CodeSection trigger = triggerSec.get(); + + Optional descOption = this.sec.getValue("description", String.class); + if (descOption.isEmpty()) { + logger.error("Description cannot be empty", ErrorType.SEMANTIC_ERROR); + return List.of(); + } + String description = descOption.get(); + + AbstractCommand hyCommand = switch (this.commandType) { + case 1 -> new AbstractPlayerCommand(this.command, description) { + @Override + protected void execute(@NotNull CommandContext commandContext, @NotNull Store store, + @NotNull Ref ref, @NotNull PlayerRef playerRef, @NotNull World world) { + + CommandSender sender = commandContext.sender(); + Player player = store.getComponent(ref, Player.getComponentType()); + Statement.runAll(trigger, new ScriptCommandContext(Command.this.command, sender, player, world)); + + } + }; + case 2 -> new AbstractWorldCommand(this.command, description) { + + @Override + protected void execute(@NotNull CommandContext commandContext, @NotNull World world, @NotNull Store store) { + Statement.runAll(trigger, new ScriptCommandContext(Command.this.command, commandContext.sender(), null, world)); + } + }; + default -> new AbstractCommand(this.command, description) { + + @Override + protected @Nullable CompletableFuture execute(@NotNull CommandContext commandContext) { + CompletableFuture.runAsync(() -> { + CommandSender sender = commandContext.sender(); + Player player = null; + if (sender instanceof Player p) player = p; + ScriptCommandContext ctx = new ScriptCommandContext(Command.this.command, sender, player, null); + + Statement.runAll(trigger, ctx); + }); + return null; + } + }; + }; + Optional perm = sec.getValue("permission", String.class); + perm.ifPresent(hyCommand::requirePermission); + HySk.getInstance().getCommandRegistry().registerCommand(hyCommand); + + return List.of(trigger); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return ctx instanceof ScriptCommandContext sctx && sctx.getCommand().equals(this.command); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = switch (this.commandType) { + case 1 -> "player"; + case 2 -> "world"; + default -> "global"; + }; + return type + " command /" + this.command; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java index f81dad85..1b0f1c38 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java @@ -5,6 +5,7 @@ public class EventHandler { public static void register(SkriptRegistration registration) { + Command.register(registration); EvtLoad.register(registration); EvtPlayerChat.register(registration); EvtPlayerJoin.register(registration); From f1fcc774030478b1f8f5042cc4638d878f4805f3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 24 Jan 2026 09:25:53 -0800 Subject: [PATCH 02/10] Command - doc update: - add the asterisk to remove the automatic "[on]" from the pattern - Add "Entries" in description --- .../skriptdev/skript/plugin/elements/events/Command.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java index 39b694f4..3a464452 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java @@ -72,12 +72,13 @@ public String getName() { public static void register(SkriptRegistration registration) { registration.newEvent(Command.class, - "[global] command <.+>", - "player command <.+>", - "world command <.+>") + "*[global] command <.+>", + "*player command <.+>", + "*world command <.+>") .setHandledContexts(ScriptCommandContext.class) .name("Command") .description("Create a command.", + "**Entries**:", "- `Description` = The description for your command that will show in the commands gui (required).", "- `Permission` = The permission required to execute the command (optional).") .examples("command /kill:", From c339ab6bc287c782b81b69bb69145d0be4370367 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 24 Jan 2026 17:08:56 -0800 Subject: [PATCH 03/10] Command - more stuff --- .../plugin/elements/events/Command.java | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java index 3a464452..719eac84 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java @@ -53,14 +53,15 @@ public String getCommand() { } public CommandSender[] getSender() { - return new CommandSender[]{sender}; + return new CommandSender[]{this.sender}; } public World[] getWorld() { - return new World[]{world}; + return new World[]{this.world}; } public Player[] getPlayer() { + if (this.player == null && this.sender instanceof Player p) return new Player[]{p}; return new Player[]{this.player}; } @@ -137,6 +138,13 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, P if (this.command.startsWith("/")) { this.command = this.command.substring(1); } + if (this.command.contains(" ")) { + this.command = this.command.substring(0, this.command.indexOf(" ")); + } + if (this.command.isEmpty()) { + parseContext.getLogger().error("Command cannot be empty", ErrorType.SEMANTIC_ERROR); + return false; + } this.commandType = matchedPattern; return true; } @@ -145,16 +153,28 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, P public List loadSection(@NotNull FileSection section, @NotNull ParserState parserState, @NotNull SkriptLogger logger) { this.sec.loadConfiguration(null, section, parserState, logger); Optional triggerSec = this.sec.getSection("trigger"); - if (triggerSec.isEmpty()) return List.of(); + if (triggerSec.isEmpty()) { + logger.error("Trigger section is missing", ErrorType.SEMANTIC_ERROR); + return List.of(); + } CodeSection trigger = triggerSec.get(); + if (trigger.getItems().isEmpty()) { + logger.warn("Trigger section should not be empty."); + return List.of(); + } Optional descOption = this.sec.getValue("description", String.class); if (descOption.isEmpty()) { logger.error("Description cannot be empty", ErrorType.SEMANTIC_ERROR); return List.of(); } - String description = descOption.get(); + + String description = trim(descOption.get()); + if (description.isEmpty()) { + logger.error("Description cannot be empty", ErrorType.SEMANTIC_ERROR); + return List.of(); + } AbstractCommand hyCommand = switch (this.commandType) { case 1 -> new AbstractPlayerCommand(this.command, description) { @@ -191,13 +211,31 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo } }; }; - Optional perm = sec.getValue("permission", String.class); - perm.ifPresent(hyCommand::requirePermission); + Optional permValue = sec.getValue("permission", String.class); + if (permValue.isPresent()) { + String perm = trim(permValue.get()); + if (!perm.isEmpty()) { + hyCommand.requirePermission(perm); + } else { + logger.warn("Permission is empty, will fallback to default permission."); + } + } HySk.getInstance().getCommandRegistry().registerCommand(hyCommand); return List.of(trigger); } + private String trim(String s) { + // In case someone puts quotes, let's remove them + if (s.startsWith("\"")) { + s = s.substring(1); + } + if (s.endsWith("\"")) { + s = s.substring(0, s.length() - 1); + } + return s.trim(); + } + @Override public boolean check(@NotNull TriggerContext ctx) { return ctx instanceof ScriptCommandContext sctx && sctx.getCommand().equals(this.command); From c0ff36a17071c7c02c7949103c86dbc1fdaf4003 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 24 Jan 2026 20:51:47 -0800 Subject: [PATCH 04/10] Command - more work - WE GOT ARGS NOW --- .../skript/api/command/ArgUtils.java | 54 +++++++++ .../skript/api/command/CommandArg.java | 100 +++++++++++++++++ .../plugin/elements/events/Command.java | 106 +++++++++++++++--- 3 files changed, 247 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/github/skriptdev/skript/api/command/ArgUtils.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/command/CommandArg.java diff --git a/src/main/java/com/github/skriptdev/skript/api/command/ArgUtils.java b/src/main/java/com/github/skriptdev/skript/api/command/ArgUtils.java new file mode 100644 index 00000000..19e910c2 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/command/ArgUtils.java @@ -0,0 +1,54 @@ +package com.github.skriptdev.skript.api.command; + +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; +import com.hypixel.hytale.server.npc.commands.NPCCommand; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Registration shortcuts for string to ArgumentType mappings. + */ +public class ArgUtils { + + private static final Map> TYPES_MAP = new TreeMap<>(); + + public static void init() { + // BASIC + register(ArgTypes.BOOLEAN, "boolean", "bool"); + register(ArgTypes.STRING, "string", "text"); + register(ArgTypes.UUID, "uuid"); + + // NUMBERS + register(ArgTypes.DOUBLE, "double"); + register(ArgTypes.FLOAT, "float"); + register(ArgTypes.INTEGER, "integer", "int"); + + // ENTITY + register(NPCCommand.NPC_ROLE, "role", "npcrole", "npc_role"); + register(ArgTypes.PLAYER_REF, "player_ref", "playerref"); + + // WORLD + register(ArgTypes.ROTATION, "rotation", "vector3f"); + register(ArgTypes.VECTOR3I, "vector3i"); + register(ArgTypes.WORLD, "world"); + } + + private static void register(ArgumentType type, String... names) { + for (String name : names) { + TYPES_MAP.put(name, type); + } + } + + /** + * Get an argument type by its name. + * + * @param name Name of the argument type. + * @return The argument type if found, otherwise null. + */ + public static ArgumentType getType(String name) { + return TYPES_MAP.get(name); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/command/CommandArg.java b/src/main/java/com/github/skriptdev/skript/api/command/CommandArg.java new file mode 100644 index 00000000..14103933 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/command/CommandArg.java @@ -0,0 +1,100 @@ +package com.github.skriptdev.skript.api.command; + +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; + +/** + * Represents a command argument which contains a name, argument type, description, and optional flag. + */ +public class CommandArg { + + private final String name; + private final String description; + private final ArgumentType type; + private final boolean optional; + + /** + * @param name Name of the argument + * @param description Description of the argument + * @param type Type of the argument + * @param optional Whether the argument is optional + */ + private CommandArg(String name, String description, ArgumentType type, boolean optional) { + this.name = name; + this.description = description; + this.type = type; + this.optional = optional; + } + + public String getName() { + return this.name; + } + + public String getDescription() { + return this.description; + } + + public ArgumentType getType() { + return this.type; + } + + public boolean isOptional() { + return this.optional; + } + + @Override + public String toString() { + return "CommandArg{" + + "name='" + this.name + '\'' + + ", description='" + this.description + '\'' + + ", type=" + this.type + + ", optional=" + this.optional + + '}'; + } + + /** Parse a string into a CommandArg. + * @param a String to parse in the format of [name:type:desc] or + * @return CommandArg + */ + public static CommandArg parseArg(String a) { + if (a.startsWith("[<") && a.endsWith(">]")) { + a = a.substring(2, a.length() - 2); + return parseArg(a, true); + } else if (a.startsWith("<") && a.endsWith(">")) { + a = a.substring(1, a.length() - 1); + return parseArg(a, false); + } else { + return null; + } + } + + private static CommandArg parseArg(String a, boolean optional) { + String name; + String description = ""; + ArgumentType type; + if (a.contains(":")) { + String[] split = a.split(":"); + if (split.length == 2) { + if (split[1].startsWith("\"")) { + name = split[0]; + type = ArgUtils.getType(split[0]); + description = split[1]; + } else { + name = split[0]; + type = ArgUtils.getType(split[1]); + } + } else { + name = split[0]; + type = ArgUtils.getType(split[1]); + description = split[2]; + } + } else { + name = a; + type = ArgUtils.getType(a); + } + if (description.startsWith("\"")) description = description.substring(1); + if (description.endsWith("\"")) description = description.substring(0, description.length() - 1); + if (type == null) return null; + return new CommandArg(name, description, type, optional); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java index 719eac84..61bec301 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java @@ -1,11 +1,16 @@ package com.github.skriptdev.skript.plugin.elements.events; +import com.github.skriptdev.skript.api.command.ArgUtils; +import com.github.skriptdev.skript.api.command.CommandArg; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.command.system.AbstractCommand; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.CommandSender; +import com.hypixel.hytale.server.core.command.system.arguments.system.Argument; +import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; +import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -25,10 +30,13 @@ import io.github.syst3ms.skriptparser.parsing.ParserState; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; +import io.github.syst3ms.skriptparser.variables.Variables; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -72,6 +80,7 @@ public String getName() { } public static void register(SkriptRegistration registration) { + ArgUtils.init(); registration.newEvent(Command.class, "*[global] command <.+>", "*player command <.+>", @@ -79,14 +88,39 @@ public static void register(SkriptRegistration registration) { .setHandledContexts(ScriptCommandContext.class) .name("Command") .description("Create a command.", + "**Command Format**:", + "- ` command /command_name (args)`", + "", + "**Argument Formats**:", + "- ``", + "- ``", + "- ``", + "- ``", + "- <> = Makes the argument required.", + "- [<>] = Makes the argument optional.", + "- Type = The type of argument to use (required).", + "- Name = The name of the argument, this will be used to create local variables (optional).", + "- Description = The description of the argument, this is show in the command GUI (optional).", + "", "**Entries**:", - "- `Description` = The description for your command that will show in the commands gui (required).", + "- `Description` = The description for your command that will show in the commands gui (optional).", "- `Permission` = The permission required to execute the command (optional).") .examples("command /kill:", "\tdescription: Kill all the players", "\ttrigger:", "\t\tkill all players", "", + "command /home []:", + "\ttrigger:", + "\t\tif {_name} is set:", + "\t\t\tteleport player to {homes::%{_name}%}", + "\t\telse:", + "\t\t\tteleport player to {homes::default}", + "", + "command /broadcast :", + "\ttrigger:", + "\t\tbroadcast {_message}", + "", "player command /clear:", "\tpermission: my.script.command.clear", "\tdescription: Clear your inventory", @@ -131,15 +165,28 @@ public static void register(SkriptRegistration registration) { private String command; private int commandType; + private final Map args = new HashMap<>(); + private final Map> argsFromCommand = new HashMap<>(); @Override public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { - this.command = parseContext.getMatches().getFirst().group(); - if (this.command.startsWith("/")) { - this.command = this.command.substring(1); + String commandLine = parseContext.getMatches().getFirst().group(); + if (commandLine.startsWith("/")) { + commandLine = commandLine.substring(1); } - if (this.command.contains(" ")) { - this.command = this.command.substring(0, this.command.indexOf(" ")); + if (commandLine.contains(" ")) { + String[] commandLineSplit = commandLine.split(" ", 2); + this.command = commandLineSplit[0]; + + String[] argSplit = commandLineSplit[1].split("(?<=[>\\]])\\s+(?=[<\\[])"); + for (String s : argSplit) { + CommandArg arg = CommandArg.parseArg(s); + if (arg == null) { + parseContext.getLogger().error("Invalid argument format: '" + s + "'", ErrorType.SEMANTIC_ERROR); + return false; + } + setupArg(arg); + } } if (this.command.isEmpty()) { parseContext.getLogger().error("Command cannot be empty", ErrorType.SEMANTIC_ERROR); @@ -166,14 +213,12 @@ public List loadSection(@NotNull FileSection section, @NotNull Parser Optional descOption = this.sec.getValue("description", String.class); if (descOption.isEmpty()) { - logger.error("Description cannot be empty", ErrorType.SEMANTIC_ERROR); - return List.of(); + descOption = Optional.of(""); } String description = trim(descOption.get()); if (description.isEmpty()) { - logger.error("Description cannot be empty", ErrorType.SEMANTIC_ERROR); - return List.of(); + description = ""; } AbstractCommand hyCommand = switch (this.commandType) { @@ -184,15 +229,18 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull Store new AbstractWorldCommand(this.command, description) { @Override protected void execute(@NotNull CommandContext commandContext, @NotNull World world, @NotNull Store store) { - Statement.runAll(trigger, new ScriptCommandContext(Command.this.command, commandContext.sender(), null, world)); + ScriptCommandContext context = new ScriptCommandContext(Command.this.command, commandContext.sender(), null, world); + createLocalVariables(commandContext, context); + Statement.runAll(trigger, context); } }; default -> new AbstractCommand(this.command, description) { @@ -205,12 +253,22 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo if (sender instanceof Player p) player = p; ScriptCommandContext ctx = new ScriptCommandContext(Command.this.command, sender, player, null); + createLocalVariables(commandContext, ctx); Statement.runAll(trigger, ctx); }); return null; } }; }; + this.args.forEach((key, arg) -> { + if (arg.isOptional()) { + OptionalArg optionalArg = hyCommand.withOptionalArg(arg.getName(), arg.getDescription(), arg.getType()); + this.argsFromCommand.put(key, optionalArg); + } else { + RequiredArg requiredArg = hyCommand.withRequiredArg(arg.getName(), arg.getDescription(), arg.getType()); + this.argsFromCommand.put(key, requiredArg); + } + }); Optional permValue = sec.getValue("permission", String.class); if (permValue.isPresent()) { String perm = trim(permValue.get()); @@ -251,4 +309,26 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return type + " command /" + this.command; } + private void setupArg(CommandArg arg) { + String name = arg.getName(); + if (this.args.containsKey(name)) { + for (int i = 1; i < 10; i++) { + String newName = name + (i + 1); + if (!this.args.containsKey(newName)) { + this.args.put(newName, arg); + return; + } + } + } else { + this.args.put(name, arg); + } + } + + private void createLocalVariables(CommandContext ctx, TriggerContext triggerContext) { + this.argsFromCommand.forEach((name, arg) -> { + Object o = ctx.get(arg); + if (o != null) Variables.setVariable(name, o, triggerContext, true); + }); + } + } From 1b332cd811408acec5f1ef099ade66f8389f4ab0 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 25 Jan 2026 07:16:02 -0800 Subject: [PATCH 05/10] ScriptCommand - repackage and rename --- .../Command.java => command/ScriptCommand.java} | 12 ++++++------ .../skript/plugin/elements/events/EventHandler.java | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) rename src/main/java/com/github/skriptdev/skript/plugin/elements/{events/Command.java => command/ScriptCommand.java} (97%) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java similarity index 97% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java index 61bec301..02fd6fd3 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/Command.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java @@ -1,4 +1,4 @@ -package com.github.skriptdev.skript.plugin.elements.events; +package com.github.skriptdev.skript.plugin.elements.command; import com.github.skriptdev.skript.api.command.ArgUtils; import com.github.skriptdev.skript.api.command.CommandArg; @@ -40,7 +40,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; -public class Command extends SkriptEvent { +public class ScriptCommand extends SkriptEvent { public static class ScriptCommandContext implements TriggerContext { @@ -81,7 +81,7 @@ public String getName() { public static void register(SkriptRegistration registration) { ArgUtils.init(); - registration.newEvent(Command.class, + registration.newEvent(ScriptCommand.class, "*[global] command <.+>", "*player command <.+>", "*world command <.+>") @@ -229,7 +229,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull Store store) { - ScriptCommandContext context = new ScriptCommandContext(Command.this.command, commandContext.sender(), null, world); + ScriptCommandContext context = new ScriptCommandContext(ScriptCommand.this.command, commandContext.sender(), null, world); createLocalVariables(commandContext, context); Statement.runAll(trigger, context); } @@ -251,7 +251,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo CommandSender sender = commandContext.sender(); Player player = null; if (sender instanceof Player p) player = p; - ScriptCommandContext ctx = new ScriptCommandContext(Command.this.command, sender, player, null); + ScriptCommandContext ctx = new ScriptCommandContext(ScriptCommand.this.command, sender, player, null); createLocalVariables(commandContext, ctx); Statement.runAll(trigger, ctx); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java index 1b0f1c38..b5ffe00c 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java @@ -1,11 +1,12 @@ package com.github.skriptdev.skript.plugin.elements.events; +import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; public class EventHandler { public static void register(SkriptRegistration registration) { - Command.register(registration); + ScriptCommand.register(registration); EvtLoad.register(registration); EvtPlayerChat.register(registration); EvtPlayerJoin.register(registration); From f801b6a1f728f53adc95fcd1cfa568cd6abedd85 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 25 Jan 2026 07:16:56 -0800 Subject: [PATCH 06/10] ScriptCommand - fix sorting issue for args - When args have no name/same type ... they fall out of order for local var naming --- .../skript/plugin/elements/command/ScriptCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java index 02fd6fd3..213e4279 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java @@ -34,7 +34,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -165,8 +165,8 @@ public static void register(SkriptRegistration registration) { private String command; private int commandType; - private final Map args = new HashMap<>(); - private final Map> argsFromCommand = new HashMap<>(); + private final Map args = new LinkedHashMap<>(); + private final Map> argsFromCommand = new LinkedHashMap<>(); @Override public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { From 72bb83769b83b10984b9833a468e34c42a7db180 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 25 Jan 2026 07:21:04 -0800 Subject: [PATCH 07/10] ScriptCommand - clear local vars after execution - I dunno if the parser does this already --- .../skript/plugin/elements/command/ScriptCommand.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java index 213e4279..bab4a3fe 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java @@ -232,6 +232,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull Store new AbstractWorldCommand(this.command, description) { @@ -241,6 +242,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo ScriptCommandContext context = new ScriptCommandContext(ScriptCommand.this.command, commandContext.sender(), null, world); createLocalVariables(commandContext, context); Statement.runAll(trigger, context); + Variables.clearLocalVariables(context); } }; default -> new AbstractCommand(this.command, description) { @@ -251,10 +253,11 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo CommandSender sender = commandContext.sender(); Player player = null; if (sender instanceof Player p) player = p; - ScriptCommandContext ctx = new ScriptCommandContext(ScriptCommand.this.command, sender, player, null); + ScriptCommandContext context = new ScriptCommandContext(ScriptCommand.this.command, sender, player, null); - createLocalVariables(commandContext, ctx); - Statement.runAll(trigger, ctx); + createLocalVariables(commandContext, context); + Statement.runAll(trigger, context); + Variables.clearLocalVariables(context); }); return null; } From bfa62347946b31ede5313317e1618687c8f95aa6 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 25 Jan 2026 07:25:38 -0800 Subject: [PATCH 08/10] ScriptCommand - add aliases --- .../plugin/elements/command/ScriptCommand.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java index bab4a3fe..aca63f5a 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java @@ -104,7 +104,8 @@ public static void register(SkriptRegistration registration) { "", "**Entries**:", "- `Description` = The description for your command that will show in the commands gui (optional).", - "- `Permission` = The permission required to execute the command (optional).") + "- `Permission` = The permission required to execute the command (optional).", + "- `Aliases` = A list of aliases for the command (optional).") .examples("command /kill:", "\tdescription: Kill all the players", "\ttrigger:", @@ -160,6 +161,7 @@ public static void register(SkriptRegistration registration) { private final SectionConfiguration sec = new SectionConfiguration.Builder() .addOptionalKey("permission") .addOptionalKey("description") + .addOptionalList("aliases") .addSection("trigger") .build(); @@ -272,7 +274,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo this.argsFromCommand.put(key, requiredArg); } }); - Optional permValue = sec.getValue("permission", String.class); + Optional permValue = this.sec.getValue("permission", String.class); if (permValue.isPresent()) { String perm = trim(permValue.get()); if (!perm.isEmpty()) { @@ -281,6 +283,12 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo logger.warn("Permission is empty, will fallback to default permission."); } } + Optional aliases = this.sec.getStringList("aliases"); + if (aliases.isPresent()) { + for (String alias : aliases.get()) { + hyCommand.addAliases(trim(alias)); + } + } HySk.getInstance().getCommandRegistry().registerCommand(hyCommand); return List.of(trigger); From d72ccbdb0e6c5ad1a19d7095a2a6961e4821c009 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 25 Jan 2026 07:29:05 -0800 Subject: [PATCH 09/10] ScriptCommand - fix naming of args - use key from map instead of original name --- .../skript/plugin/elements/command/ScriptCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java index aca63f5a..d94fa739 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java @@ -267,10 +267,10 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo }; this.args.forEach((key, arg) -> { if (arg.isOptional()) { - OptionalArg optionalArg = hyCommand.withOptionalArg(arg.getName(), arg.getDescription(), arg.getType()); + OptionalArg optionalArg = hyCommand.withOptionalArg(key, arg.getDescription(), arg.getType()); this.argsFromCommand.put(key, optionalArg); } else { - RequiredArg requiredArg = hyCommand.withRequiredArg(arg.getName(), arg.getDescription(), arg.getType()); + RequiredArg requiredArg = hyCommand.withRequiredArg(key, arg.getDescription(), arg.getType()); this.argsFromCommand.put(key, requiredArg); } }); From 0eab9ca07edef5dcf5650eac7aaf19d5ab57f9b9 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 25 Jan 2026 07:34:02 -0800 Subject: [PATCH 10/10] ElementRegistration - move script command registration to here --- .../skriptdev/skript/plugin/elements/ElementRegistration.java | 4 ++++ .../skriptdev/skript/plugin/elements/events/EventHandler.java | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java index 190e0eba..eea98fad 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.elements; import com.github.skriptdev.skript.plugin.Skript; +import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import com.github.skriptdev.skript.plugin.elements.conditions.ConditionHandler; import com.github.skriptdev.skript.plugin.elements.effects.EffectHandler; import com.github.skriptdev.skript.plugin.elements.events.EventHandler; @@ -43,6 +44,9 @@ public void registerElements() { // EVENTS EventHandler.register(this.registration); + + // COMMAND + ScriptCommand.register(this.registration); } public ListenerHandler getListenerHandler() { diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java index b5ffe00c..f81dad85 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java @@ -1,12 +1,10 @@ package com.github.skriptdev.skript.plugin.elements.events; -import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; public class EventHandler { public static void register(SkriptRegistration registration) { - ScriptCommand.register(registration); EvtLoad.register(registration); EvtPlayerChat.register(registration); EvtPlayerJoin.register(registration);