Skip to content

Commit

Permalink
Start working towards proto 1.5 (#1379)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Herrera <[email protected]>
  • Loading branch information
Pablete1234 authored Sep 26, 2024
1 parent fccf7ba commit 0da8f1c
Show file tree
Hide file tree
Showing 48 changed files with 804 additions and 717 deletions.
15 changes: 7 additions & 8 deletions core/src/main/java/tc/oc/pgm/action/ActionModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.Set;
import java.util.logging.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
Expand All @@ -15,6 +16,7 @@
import tc.oc.pgm.api.module.exception.ModuleLoadException;
import tc.oc.pgm.filters.FilterMatchModule;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.XMLUtils;
import tc.oc.pgm.variables.VariablesModule;

public class ActionModule implements MapModule<ActionMatchModule> {
Expand Down Expand Up @@ -57,17 +59,14 @@ public ActionModule parse(MapFactory factory, Logger logger, Document doc)
throws InvalidXMLException {
ActionParser parser = new ActionParser(factory);

for (Element actions : doc.getRootElement().getChildren("actions")) {
for (Element action : actions.getChildren()) {
if (parser.isAction(action)) parser.parse(action, null);
}
for (Element action :
XMLUtils.flattenElements(doc.getRootElement(), Set.of("actions"), parser.actionTypes())) {
parser.parse(action, null);
}

ImmutableList.Builder<Trigger<?>> triggers = ImmutableList.builder();
for (Element actions : doc.getRootElement().getChildren("actions")) {
for (Element rule : actions.getChildren("trigger")) {
triggers.add(parser.parseTrigger(rule));
}
for (Element rule : XMLUtils.flattenElements(doc.getRootElement(), "actions", "trigger")) {
triggers.add(parser.parseTrigger(rule));
}

return new ActionModule(triggers.build());
Expand Down
81 changes: 59 additions & 22 deletions core/src/main/java/tc/oc/pgm/action/ActionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
Expand All @@ -36,12 +37,16 @@
import tc.oc.pgm.api.feature.FeatureValidation;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.api.filter.Filterables;
import tc.oc.pgm.api.filter.query.PartyQuery;
import tc.oc.pgm.api.map.MapProtos;
import tc.oc.pgm.api.map.factory.MapFactory;
import tc.oc.pgm.api.player.MatchPlayer;
import tc.oc.pgm.features.FeatureDefinitionContext;
import tc.oc.pgm.features.XMLFeatureReference;
import tc.oc.pgm.filters.Filterable;
import tc.oc.pgm.filters.matcher.StaticFilter;
import tc.oc.pgm.filters.matcher.player.ParticipatingFilter;
import tc.oc.pgm.filters.operator.AllFilter;
import tc.oc.pgm.filters.parse.DynamicFilterValidation;
import tc.oc.pgm.filters.parse.FilterParser;
import tc.oc.pgm.kits.Kit;
Expand All @@ -58,14 +63,15 @@
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
import tc.oc.pgm.util.xml.XMLUtils;
import tc.oc.pgm.variables.VariableDefinition;
import tc.oc.pgm.variables.Variable;
import tc.oc.pgm.variables.VariablesModule;

public class ActionParser {

private static final NumberFormat DEFAULT_FORMAT = NumberFormat.getIntegerInstance();

private final MapFactory factory;
private final boolean legacy;
private final FeatureDefinitionContext features;
private final FilterParser filters;
private final RegionParser regions;
Expand All @@ -74,6 +80,7 @@ public class ActionParser {

public ActionParser(MapFactory factory) {
this.factory = factory;
this.legacy = !factory.getProto().isNoOlderThan(MapProtos.ACTION_REVAMP);
this.features = factory.getFeatures();
this.filters = factory.getFilters();
this.regions = factory.getRegions();
Expand Down Expand Up @@ -106,8 +113,8 @@ public <B extends Filterable<?>> Action<? super B> parse(Element el, @Nullable C
return result;
}

public boolean isAction(Element el) {
return getParserFor(el) != null;
public Set<String> actionTypes() {
return methodParsers.keySet();
}

private boolean maybeReference(Element el) {
Expand Down Expand Up @@ -175,14 +182,6 @@ private <T, B extends Filterable<?>> Action<T> parseDynamic(Element el, Class<B>
}
}

public <T extends Filterable<?>> Trigger<T> parseTrigger(Element el) throws InvalidXMLException {
Class<T> cls = Filterables.parse(Node.fromRequiredAttr(el, "scope"));
return new Trigger<>(
cls,
filters.parseRequiredProperty(el, "filter", DynamicFilterValidation.of(cls)),
parseProperty(Node.fromRequiredChildOrAttr(el, "action", "trigger"), cls));
}

private <B extends Filterable<?>> Class<B> parseScope(Element el, Class<B> scope)
throws InvalidXMLException {
return parseScope(el, scope, "scope");
Expand All @@ -199,29 +198,63 @@ private <B extends Filterable<?>> Class<B> parseScope(Element el, Class<B> scope
return scope;
}

@MethodParser("action")
public <B extends Filterable<?>> ActionNode<? super B> parseAction(Element el, Class<B> scope)
private <B extends Filterable<?>> boolean includeObs(Element el, Class<B> scope)
throws InvalidXMLException {
return PartyQuery.class.isAssignableFrom(scope)
|| XMLUtils.parseBoolean(el.getAttribute("observers"), legacy);
}

private Filter wrapFilter(Filter outer, boolean includeObs) {
if (includeObs || outer == StaticFilter.DENY) return outer;
if (outer == StaticFilter.ALLOW) return ParticipatingFilter.PARTICIPATING;
return AllFilter.of(outer, ParticipatingFilter.PARTICIPATING);
}

// Parser for <trigger> elements
public <T extends Filterable<?>> Trigger<T> parseTrigger(Element el) throws InvalidXMLException {
Class<T> cls = Filterables.parse(Node.fromRequiredAttr(el, "scope"));
return new Trigger<>(
cls,
wrapFilter(
filters.parseRequiredProperty(el, "filter", DynamicFilterValidation.of(cls)),
includeObs(el, cls)),
parseProperty(Node.fromRequiredChildOrAttr(el, "action", "trigger"), cls));
}

// Generic action with N children parser
public <B extends Filterable<?>> ActionNode<? super B> parseAction(
Element el, Class<B> scope, boolean includeObs) throws InvalidXMLException {
scope = parseScope(el, scope);

ImmutableList.Builder<Action<? super B>> builder = ImmutableList.builder();
for (Element child : el.getChildren()) {
builder.add(parse(child, scope));
}

Filter filter = filters.parseFilterProperty(el, "filter", StaticFilter.ALLOW);
Filter untriggerFilter = filters.parseFilterProperty(el, "untrigger-filter", StaticFilter.DENY);
Filter filter =
wrapFilter(filters.parseFilterProperty(el, "filter", StaticFilter.ALLOW), includeObs);
Filter untriggerFilter = wrapFilter(
filters.parseFilterProperty(
el, "untrigger-filter", legacy ? StaticFilter.DENY : StaticFilter.ALLOW),
includeObs);

return new ActionNode<>(builder.build(), filter, untriggerFilter, scope);
}

// Parsers
@MethodParser("action")
public <B extends Filterable<?>> ActionNode<? super B> parseAction(Element el, Class<B> scope)
throws InvalidXMLException {
return parseAction(el, scope, true);
}

@MethodParser("switch-scope")
public <O extends Filterable<?>, I extends Filterable<?>> Action<? super O> parseSwitchScope(
Element el, Class<O> outer) throws InvalidXMLException {
outer = parseScope(el, outer, "outer");
Class<I> inner = parseScope(el, null, "inner");

ActionDefinition<? super I> child = parseAction(el, inner);
ActionDefinition<? super I> child = parseAction(el, inner, includeObs(el, inner));

Action<? super O> result = ScopeSwitchAction.of(child, outer, inner);
if (result == null) {
Expand Down Expand Up @@ -307,27 +340,30 @@ public SoundAction parseSoundAction(Element el, Class<?> scope) throws InvalidXM
@MethodParser("set")
public <T extends Filterable<?>> SetVariableAction<T> parseSetVariable(Element el, Class<T> scope)
throws InvalidXMLException {
VariableDefinition<?> var =
features.resolve(Node.fromRequiredAttr(el, "var"), VariableDefinition.class);
var node = Node.fromRequiredAttr(el, "var");
Variable<?> var = features.resolve(node, Variable.class);
scope = parseScope(el, scope);

if (!Filterables.isAssignable(scope, var.getScope()))
throw new InvalidXMLException(
"Wrong variable scope for '"
+ var.getId()
+ node.getValue()
+ "', expected "
+ var.getScope().getSimpleName()
+ " which cannot be found in "
+ scope.getSimpleName(),
el);

if (var.isReadonly())
throw new InvalidXMLException("You may not use a read-only variable in set", el);

Formula<T> formula =
Formula.of(Node.fromRequiredAttr(el, "value").getValue(), variables.getContext(scope));

if (var.isIndexed()) {
if (var.isIndexed() && var instanceof Variable.Indexed<?> indexedVar) {
Formula<T> idx =
Formula.of(Node.fromRequiredAttr(el, "index").getValue(), variables.getContext(scope));
return new SetVariableAction.Indexed<>(scope, var, idx, formula);
return new SetVariableAction.Indexed<>(scope, indexedVar, idx, formula);
}

return new SetVariableAction<>(scope, var, formula);
Expand Down Expand Up @@ -411,14 +447,15 @@ public Action<? super MatchPlayer> parseTeleport(Element el, Class<?> scope)
@MethodParser("paste-structure")
public <T extends Filterable<?>> PasteStructureAction<T> parseStructure(
Element el, Class<T> scope) throws InvalidXMLException {
scope = parseScope(el, scope);
Formula<T> xFormula =
Formula.of(Node.fromRequiredAttr(el, "x").getValue(), variables.getContext(scope));
Formula<T> yFormula =
Formula.of(Node.fromRequiredAttr(el, "y").getValue(), variables.getContext(scope));
Formula<T> zFormula =
Formula.of(Node.fromRequiredAttr(el, "z").getValue(), variables.getContext(scope));

XMLFeatureReference<StructureDefinition> structure =
var structure =
features.createReference(Node.fromRequiredAttr(el, "structure"), StructureDefinition.class);

return new PasteStructureAction<>(scope, xFormula, yFormula, zFormula, structure);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,38 @@

import tc.oc.pgm.filters.Filterable;
import tc.oc.pgm.util.math.Formula;
import tc.oc.pgm.variables.VariableDefinition;
import tc.oc.pgm.variables.types.IndexedVariable;
import tc.oc.pgm.variables.Variable;

public class SetVariableAction<T extends Filterable<?>> extends AbstractAction<T> {

protected final VariableDefinition<?> variable;
protected final Variable<?> variable;
protected final Formula<T> formula;

public SetVariableAction(Class<T> scope, VariableDefinition<?> variable, Formula<T> formula) {
public SetVariableAction(Class<T> scope, Variable<?> variable, Formula<T> formula) {
super(scope);
this.variable = variable;
this.formula = formula;
}

@Override
public void trigger(T t) {
variable.getVariable(t.getMatch()).setValue(t, formula.applyAsDouble(t));
variable.setValue(t, formula.applyAsDouble(t));
}

public static class Indexed<T extends Filterable<?>> extends SetVariableAction<T> {

private final Formula<T> idx;

public Indexed(
Class<T> scope, VariableDefinition<?> variable, Formula<T> idx, Formula<T> formula) {
Class<T> scope, Variable.Indexed<?> variable, Formula<T> idx, Formula<T> formula) {
super(scope, variable, formula);
this.idx = idx;
}

@Override
@SuppressWarnings("unchecked")
public void trigger(T t) {
((IndexedVariable<T>) variable.getVariable(t.getMatch()))
((Variable.Indexed<T>) variable)
.setValue(t, (int) idx.applyAsDouble(t), formula.applyAsDouble(t));
}
}
Expand Down
14 changes: 10 additions & 4 deletions core/src/main/java/tc/oc/pgm/api/filter/ReactorFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.bukkit.event.Event;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.match.event.MatchLoadEvent;
import tc.oc.pgm.features.StateHolder;
import tc.oc.pgm.filters.FilterMatchModule;
import tc.oc.pgm.filters.Filterable;

Expand All @@ -11,7 +12,12 @@
* properly invalidate {@link Filterable}s. A filter can theoretically both depend on listening to
* some events AND have a reactor, but this is rarely the case.
*/
public interface ReactorFactory<R extends ReactorFactory.Reactor> extends FilterDefinition {
public interface ReactorFactory<R extends ReactorFactory.Reactor>
extends FilterDefinition, StateHolder<R> {

default void register(Match match, FilterMatchModule fmm) {
match.getFeatureContext().registerState(this, createReactor(match, fmm));
}

/**
* Get an instance of this filter's reactor. This will only be called once per match and always
Expand All @@ -23,9 +29,9 @@ public interface ReactorFactory<R extends ReactorFactory.Reactor> extends Filter
R createReactor(Match match, FilterMatchModule fmm);

/**
* A match scoped singleton responsible for match time invalidation of filterables that the {@link
* ReactorFactory} that created this might have changed its opinion about. This is created at the
* end of match load.
* A match scoped singleton responsible for match time invalidation of filterables that the
* {@link ReactorFactory} that created this might have changed its opinion about. This is created
* at the end of match load.
*
* @see FilterMatchModule#onMatchLoad(MatchLoadEvent)
*/
Expand Down
7 changes: 3 additions & 4 deletions core/src/main/java/tc/oc/pgm/api/filter/query/MatchQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import tc.oc.pgm.api.filter.ReactorFactory;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.match.MatchModule;
import tc.oc.pgm.filters.FilterMatchModule;
import tc.oc.pgm.features.StateHolder;
import tc.oc.pgm.filters.Filterable;

public interface MatchQuery extends Query {
Expand All @@ -27,8 +26,8 @@ default <T extends MatchModule> Optional<T> moduleOptional(Class<T> cls) {
return Optional.ofNullable(getMatch().getModule(cls));
}

default <T extends ReactorFactory.Reactor> T reactor(ReactorFactory<T> factory) {
return getMatch().needModule(FilterMatchModule.class).getReactor(factory);
default <T> T state(StateHolder<T> factory) {
return getMatch().getFeatureContext().getState(factory);
}

@Nullable
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/tc/oc/pgm/api/map/MapProtos.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public interface MapProtos {

// Various changes to support dynamic filters
Version DYNAMIC_FILTERS = new Version(1, 4, 2);

// Make several singletons have built-in default ids
Version FEATURE_SINGLETON_IDS = new Version(1, 5, 0);

// Several fixes to actions & scopes
Version ACTION_REVAMP = new Version(1, 5, 0);
}
Loading

0 comments on commit 0da8f1c

Please sign in to comment.