From 30d6b700c737efc12340cd3959497bdbc853a673 Mon Sep 17 00:00:00 2001 From: Pablete1234 Date: Mon, 21 Nov 2022 23:42:46 +0100 Subject: [PATCH] Implement exposed actions Signed-off-by: Pablete1234 --- .../tc/oc/pgm/action/ActionMatchModule.java | 12 ++- .../java/tc/oc/pgm/action/ActionModule.java | 10 ++- .../java/tc/oc/pgm/action/ActionParser.java | 18 ++++- .../oc/pgm/action/actions/ExposedAction.java | 41 ++++++++++ .../java/tc/oc/pgm/command/ActionCommand.java | 79 +++++++++++++++++++ .../command/parsers/ExposedActionParser.java | 25 ++++++ .../tc/oc/pgm/command/util/CommandGraph.java | 7 +- 7 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/tc/oc/pgm/action/actions/ExposedAction.java create mode 100644 core/src/main/java/tc/oc/pgm/command/ActionCommand.java create mode 100644 core/src/main/java/tc/oc/pgm/command/parsers/ExposedActionParser.java diff --git a/core/src/main/java/tc/oc/pgm/action/ActionMatchModule.java b/core/src/main/java/tc/oc/pgm/action/ActionMatchModule.java index cf221e5ed1..3f6223be84 100644 --- a/core/src/main/java/tc/oc/pgm/action/ActionMatchModule.java +++ b/core/src/main/java/tc/oc/pgm/action/ActionMatchModule.java @@ -1,6 +1,7 @@ package tc.oc.pgm.action; import com.google.common.collect.ImmutableList; +import tc.oc.pgm.action.actions.ExposedAction; import tc.oc.pgm.api.match.Match; import tc.oc.pgm.api.match.MatchModule; import tc.oc.pgm.api.module.exception.ModuleLoadException; @@ -10,10 +11,15 @@ public class ActionMatchModule implements MatchModule { private final Match match; private final ImmutableList> triggers; + private final ImmutableList exposedActions; - public ActionMatchModule(Match match, ImmutableList> triggers) { + public ActionMatchModule( + Match match, + ImmutableList> triggers, + ImmutableList exposedActions) { this.match = match; this.triggers = triggers; + this.exposedActions = exposedActions; } @Override @@ -34,4 +40,8 @@ private > void setupTrigger(Trigger rule, FilterMatch else rule.getAction().untrigger(filterable); }); } + + public ImmutableList getExposedActions() { + return exposedActions; + } } diff --git a/core/src/main/java/tc/oc/pgm/action/ActionModule.java b/core/src/main/java/tc/oc/pgm/action/ActionModule.java index 18567c0829..807a70f306 100644 --- a/core/src/main/java/tc/oc/pgm/action/ActionModule.java +++ b/core/src/main/java/tc/oc/pgm/action/ActionModule.java @@ -6,6 +6,7 @@ import org.jdom2.Document; import org.jdom2.Element; import org.jetbrains.annotations.Nullable; +import tc.oc.pgm.action.actions.ExposedAction; import tc.oc.pgm.api.map.MapModule; import tc.oc.pgm.api.map.factory.MapFactory; import tc.oc.pgm.api.map.factory.MapModuleFactory; @@ -19,11 +20,18 @@ public class ActionModule implements MapModule { private final ImmutableList> triggers; + private ImmutableList actions; public ActionModule(ImmutableList> triggers) { this.triggers = triggers; } + @Override + public void postParse(MapFactory factory, Logger logger, Document doc) { + // Must be post-parsed, as other parts of the XML may create other actions + this.actions = ImmutableList.copyOf(factory.getFeatures().getAll(ExposedAction.class)); + } + @Nullable @Override public Collection> getHardDependencies() { @@ -33,7 +41,7 @@ public Collection> getHardDependencies() { @Nullable @Override public ActionMatchModule createMatchModule(Match match) throws ModuleLoadException { - return new ActionMatchModule(match, triggers); + return new ActionMatchModule(match, triggers, actions); } public static class Factory implements MapModuleFactory { diff --git a/core/src/main/java/tc/oc/pgm/action/ActionParser.java b/core/src/main/java/tc/oc/pgm/action/ActionParser.java index 0557a9d922..49ae7187f1 100644 --- a/core/src/main/java/tc/oc/pgm/action/ActionParser.java +++ b/core/src/main/java/tc/oc/pgm/action/ActionParser.java @@ -12,6 +12,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tc.oc.pgm.action.actions.ActionNode; +import tc.oc.pgm.action.actions.ExposedAction; import tc.oc.pgm.action.actions.KillEntitiesAction; import tc.oc.pgm.action.actions.MessageAction; import tc.oc.pgm.action.actions.ReplaceItemAction; @@ -21,6 +22,7 @@ import tc.oc.pgm.api.filter.Filter; import tc.oc.pgm.api.filter.Filterables; import tc.oc.pgm.api.map.factory.MapFactory; +import tc.oc.pgm.api.match.Match; import tc.oc.pgm.features.FeatureDefinitionContext; import tc.oc.pgm.features.XMLFeatureReference; import tc.oc.pgm.filters.Filterable; @@ -65,8 +67,22 @@ public > Action parse(Element el, @Nullable C Action result = parseDynamic(el, bound); if (bound != null) validate(result, ActionScopeValidation.of(bound), node); - if (result instanceof ActionDefinition) + if (result instanceof ActionDefinition) { + if (XMLUtils.parseBoolean(Node.fromAttr(el, "expose"), false)) { + + if (id == null) + throw new InvalidXMLException("Attribute 'id' is required for exposed actions", el); + + if (!result.getScope().isAssignableFrom(Match.class)) + throw new InvalidXMLException("Match scope is required for exposed actions", el); + + result = + (ActionDefinition) + new ExposedAction(id, (ActionDefinition) result); + } + features.addFeature(el, (ActionDefinition) result); + } return result; } diff --git a/core/src/main/java/tc/oc/pgm/action/actions/ExposedAction.java b/core/src/main/java/tc/oc/pgm/action/actions/ExposedAction.java new file mode 100644 index 0000000000..3eb9401d69 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/action/actions/ExposedAction.java @@ -0,0 +1,41 @@ +package tc.oc.pgm.action.actions; + +import tc.oc.pgm.action.ActionDefinition; +import tc.oc.pgm.api.feature.Feature; +import tc.oc.pgm.api.match.Match; +import tc.oc.pgm.features.SelfIdentifyingFeatureDefinition; + +/** + * Wraps an action definition to consider it exposed. This is an easy way to avoid needing each + * specialized action to implement a way to expose itself or not. + */ +public class ExposedAction extends SelfIdentifyingFeatureDefinition + implements ActionDefinition, Feature> { + + private final ActionDefinition delegate; + + public ExposedAction(String id, ActionDefinition delegate) { + super(id); + this.delegate = delegate; + } + + @Override + public ActionDefinition getDefinition() { + return delegate; + } + + @Override + public Class getScope() { + return Match.class; + } + + @Override + public void trigger(Match m) { + delegate.trigger(m); + } + + @Override + public void untrigger(Match m) { + delegate.untrigger(m); + } +} diff --git a/core/src/main/java/tc/oc/pgm/command/ActionCommand.java b/core/src/main/java/tc/oc/pgm/command/ActionCommand.java new file mode 100644 index 0000000000..c74e9adf8e --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/command/ActionCommand.java @@ -0,0 +1,79 @@ +package tc.oc.pgm.command; + +import static net.kyori.adventure.text.Component.text; + +import cloud.commandframework.annotations.Argument; +import cloud.commandframework.annotations.CommandDescription; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.annotations.CommandPermission; +import cloud.commandframework.annotations.Flag; +import cloud.commandframework.annotations.specifier.Greedy; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.CommandSender; +import tc.oc.pgm.action.ActionMatchModule; +import tc.oc.pgm.action.actions.ExposedAction; +import tc.oc.pgm.api.Permissions; +import tc.oc.pgm.api.match.Match; +import tc.oc.pgm.util.Audience; +import tc.oc.pgm.util.PrettyPaginatedComponentResults; +import tc.oc.pgm.util.text.TextFormatter; + +@CommandMethod("action|actions") +public class ActionCommand { + + @CommandMethod("list|page [page]") + @CommandDescription("Inspect variables for a player") + @CommandPermission(Permissions.GAMEPLAY) + public void showActions( + Audience audience, + CommandSender sender, + ActionMatchModule amm, + @Argument(value = "page", defaultValue = "1") int page, + @Flag(value = "query", aliases = "q") String query, + @Flag(value = "all", aliases = "a") boolean all) { + + List actions = + amm.getExposedActions().stream() + .filter(a -> query == null || a.getId().contains(query)) + .sorted(Comparator.comparing(ExposedAction::getId)) + .collect(Collectors.toList()); + + int resultsPerPage = all ? actions.size() : 8; + int pages = all ? 1 : (actions.size() + resultsPerPage - 1) / resultsPerPage; + + Component title = + TextFormatter.paginate( + text("Actions"), page, pages, NamedTextColor.DARK_AQUA, NamedTextColor.AQUA, true); + Component header = TextFormatter.horizontalLineHeading(sender, title, NamedTextColor.BLUE); + + PrettyPaginatedComponentResults.display( + audience, + actions, + page, + resultsPerPage, + header, + (v, i) -> text((i + 1) + ". ").append(text(v.getId(), NamedTextColor.AQUA))); + } + + @CommandMethod("trigger [action]") + @CommandDescription("Trigger a specific action") + @CommandPermission(Permissions.GAMEPLAY) + public void triggerAction( + Audience audience, Match match, @Argument("action") @Greedy ExposedAction action) { + action.trigger(match); + audience.sendMessage(text("Triggered " + action.getId())); + } + + @CommandMethod("untrigger [action]") + @CommandDescription("Untrigger a specific action") + @CommandPermission(Permissions.GAMEPLAY) + public void untriggerAction( + Audience audience, Match match, @Argument("action") @Greedy ExposedAction action) { + action.untrigger(match); + audience.sendMessage(text("Untriggered " + action.getId())); + } +} diff --git a/core/src/main/java/tc/oc/pgm/command/parsers/ExposedActionParser.java b/core/src/main/java/tc/oc/pgm/command/parsers/ExposedActionParser.java new file mode 100644 index 0000000000..7d985eabe1 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/command/parsers/ExposedActionParser.java @@ -0,0 +1,25 @@ +package tc.oc.pgm.command.parsers; + +import cloud.commandframework.arguments.parser.ParserParameters; +import cloud.commandframework.paper.PaperCommandManager; +import java.util.Collection; +import org.bukkit.command.CommandSender; +import tc.oc.pgm.action.ActionMatchModule; +import tc.oc.pgm.action.actions.ExposedAction; + +public class ExposedActionParser extends MatchObjectParser { + + public ExposedActionParser(PaperCommandManager manager, ParserParameters options) { + super(manager, options, ExposedAction.class, ActionMatchModule.class, "actions"); + } + + @Override + protected Collection objects(ActionMatchModule module) { + return module.getExposedActions(); + } + + @Override + protected String getName(ExposedAction obj) { + return obj.getId(); + } +} diff --git a/core/src/main/java/tc/oc/pgm/command/util/CommandGraph.java b/core/src/main/java/tc/oc/pgm/command/util/CommandGraph.java index 613f02b4dd..739cc78388 100644 --- a/core/src/main/java/tc/oc/pgm/command/util/CommandGraph.java +++ b/core/src/main/java/tc/oc/pgm/command/util/CommandGraph.java @@ -40,6 +40,7 @@ import net.kyori.adventure.util.ComponentMessageThrowable; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.Nullable; +import tc.oc.pgm.action.actions.ExposedAction; import tc.oc.pgm.api.Config; import tc.oc.pgm.api.PGM; import tc.oc.pgm.api.map.MapInfo; @@ -53,6 +54,7 @@ import tc.oc.pgm.api.setting.SettingKey; import tc.oc.pgm.api.setting.SettingValue; import tc.oc.pgm.classes.PlayerClass; +import tc.oc.pgm.command.ActionCommand; import tc.oc.pgm.command.AdminCommand; import tc.oc.pgm.command.CancelCommand; import tc.oc.pgm.command.ClassCommand; @@ -84,6 +86,7 @@ import tc.oc.pgm.command.injectors.TeamMatchModuleProvider; import tc.oc.pgm.command.parsers.DurationParser; import tc.oc.pgm.command.parsers.EnumParser; +import tc.oc.pgm.command.parsers.ExposedActionParser; import tc.oc.pgm.command.parsers.MapInfoParser; import tc.oc.pgm.command.parsers.MapPoolParser; import tc.oc.pgm.command.parsers.MatchPlayerParser; @@ -200,6 +203,7 @@ public CommandGraph(PGM pgm) throws Exception { // Commands public void registerCommands() { + register(new ActionCommand()); register(new AdminCommand()); register(new CancelCommand()); register(new ClassCommand()); @@ -210,6 +214,7 @@ public void registerCommands() { register(new JoinCommand()); register(new ListCommand()); register(new MapCommand()); + register(new MapDevCommand()); register(new MapOrderCommand()); register(new MapPoolCommand()); register(new MatchCommand()); @@ -222,7 +227,6 @@ public void registerCommands() { register(new TeamCommand()); register(new TimeLimitCommand()); register(new VotingCommand()); - register(new MapDevCommand()); if (pgm.getConfiguration().isCommunityMode()) { register(new ReportCommand()); @@ -284,6 +288,7 @@ protected void setupParsers() { registerParser(TypeFactory.parameterizedClass(Collection.class, Team.class), TeamsParser::new); registerParser(PlayerClass.class, PlayerClassParser::new); registerParser(Mode.class, ModeParser::new); + registerParser(ExposedAction.class, ExposedActionParser::new); registerParser( TypeFactory.parameterizedClass(Optional.class, VictoryCondition.class), new VictoryConditionParser());