Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start working towards proto 1.5 #1379

Merged
merged 4 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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