Skip to content

Commit

Permalink
Implement player replacements in message actions (#1409)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Herrera <pabloherrerapalacio@gmail.com>
  • Loading branch information
Pablete1234 committed Nov 30, 2024
1 parent a9798e4 commit 511161f
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 19 deletions.
25 changes: 9 additions & 16 deletions core/src/main/java/tc/oc/pgm/action/ActionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import tc.oc.pgm.util.MethodParsers;
import tc.oc.pgm.util.inventory.ItemMatcher;
import tc.oc.pgm.util.math.Formula;
import tc.oc.pgm.util.named.NameStyle;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
import tc.oc.pgm.util.xml.XMLFluentParser;
Expand Down Expand Up @@ -287,6 +288,13 @@ private <T extends Filterable<?>> MessageAction.Replacement<T> parseReplacement(
NumberFormat format =
formatNode != null ? new DecimalFormat(formatNode.getValue()) : DEFAULT_FORMAT;
return (T filterable) -> text(format.format(formula.applyAsDouble(filterable)));
case "player":
var variable = parser.variable(el, "var").scope(MatchPlayer.class).singleExclusive();
var fallback = XMLUtils.parseFormattedText(el, "fallback", empty());
var nameStyle = parser.parseEnum(NameStyle.class, el, "style").optional(NameStyle.VERBOSE);

return (T filterable) ->
variable.getHolder(filterable).map(mp -> mp.getName(nameStyle)).orElse(fallback);
default:
throw new InvalidXMLException("Unknown replacement type", el);
}
Expand All @@ -312,23 +320,8 @@ 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 {
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 '"
+ 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);

Variable<?> var = parser.variable(el, "var").bound(scope).writtable().required();
Formula<T> formula = parser.formula(scope, el, "value").required();

if (var.isIndexed() && var instanceof Variable.Indexed<?> indexedVar) {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/tc/oc/pgm/util/xml/XMLFluentParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import tc.oc.pgm.util.xml.parsers.PrimitiveBuilder;
import tc.oc.pgm.util.xml.parsers.ReferenceBuilder;
import tc.oc.pgm.util.xml.parsers.RegionBuilder;
import tc.oc.pgm.util.xml.parsers.VariableBuilder;
import tc.oc.pgm.variables.VariablesModule;

public class XMLFluentParser {
Expand Down Expand Up @@ -90,6 +91,10 @@ public <T extends FeatureDefinition> ReferenceBuilder<T> reference(
return new ReferenceBuilder<T>(features, clazz, el, prop);
}

public VariableBuilder<?> variable(Element el, String... prop) throws InvalidXMLException {
return new VariableBuilder<>(features, el, prop);
}

public <T extends Filterable<?>> Builder.Generic<Action<? super T>> action(
Class<T> clazz, Element el, String... prop) throws InvalidXMLException {
return new Builder.Generic<>(el, prop) {
Expand Down
82 changes: 82 additions & 0 deletions core/src/main/java/tc/oc/pgm/util/xml/parsers/VariableBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package tc.oc.pgm.util.xml.parsers;

import org.jdom2.Element;
import tc.oc.pgm.api.filter.Filterables;
import tc.oc.pgm.features.FeatureDefinitionContext;
import tc.oc.pgm.filters.Filterable;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
import tc.oc.pgm.variables.Variable;

public class VariableBuilder<T extends Filterable<?>>
extends Builder<Variable<T>, VariableBuilder<T>> {
private final FeatureDefinitionContext features;

public VariableBuilder(FeatureDefinitionContext features, Element el, String... prop) {
super(el, prop);
this.features = features;
}

public VariableBuilder<T> bound(Class<? extends Filterable<?>> scope) {
validate((var, n) -> {
if (!Filterables.isAssignable(scope, var.getScope()))
throw new InvalidXMLException(
"Wrong variable scope for '"
+ n.getValue()
+ "', required "
+ scope.getSimpleName()
+ " or higher, but was "
+ var.getScope().getSimpleName(),
el);
});
return this;
}

public <S extends Filterable<?>> VariableBuilder<S> scope(Class<S> scope) {
validate((var, n) -> {
if (scope != var.getScope())
throw new InvalidXMLException(
"Wrong variable scope for '"
+ n.getValue()
+ "', required "
+ scope.getSimpleName()
+ " but variable was "
+ var.getScope().getSimpleName(),
n);
});
return (VariableBuilder<S>) this;
}

public VariableBuilder<T> writtable() {
validate((var, n) -> {
if (var.isReadonly())
throw new InvalidXMLException("Variable was readonly when write access is required", n);
});
return this;
}

public Variable.Exclusive<T> singleExclusive() throws InvalidXMLException {
validate((var, n) -> {
if (!var.isExclusive() || ((Variable.Exclusive<T>) var).getCardinality() != 1)
throw new InvalidXMLException("Variable with exclusive=1 required", n);
});
return (Variable.Exclusive<T>) required();
}

public Variable.Exclusive<T> exclusive() throws InvalidXMLException {
validate((var, n) -> {
if (!var.isExclusive()) throw new InvalidXMLException("Variable with exclusive required", n);
});
return (Variable.Exclusive<T>) required();
}

@Override
protected Variable<T> parse(Node node) throws InvalidXMLException {
return features.resolve(node, Variable.class);
}

@Override
protected VariableBuilder<T> getThis() {
return this;
}
}
19 changes: 19 additions & 0 deletions core/src/main/java/tc/oc/pgm/variables/Variable.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package tc.oc.pgm.variables;

import java.util.Collection;
import java.util.Optional;
import tc.oc.pgm.api.feature.FeatureDefinition;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.filters.Filterable;
Expand All @@ -24,6 +26,10 @@ default boolean isReadonly() {
return false;
}

default boolean isExclusive() {
return false;
}

default void load(Match match) {}

/**
Expand All @@ -45,4 +51,17 @@ default boolean isIndexed() {

int size();
}

interface Exclusive<T extends Filterable<?>> extends Variable<T> {
@Override
default boolean isExclusive() {
return getCardinality() != null;
}

Integer getCardinality();

Optional<T> getHolder(Filterable<?> context);

Collection<T> getHolders(Filterable<?> context);
}
}
26 changes: 23 additions & 3 deletions core/src/main/java/tc/oc/pgm/variables/types/DummyVariable.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package tc.oc.pgm.variables.types;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.features.StateHolder;
import tc.oc.pgm.filters.FilterMatchModule;
import tc.oc.pgm.filters.Filterable;
import tc.oc.pgm.variables.Variable;

public class DummyVariable<T extends Filterable<?>> extends AbstractVariable<T>
implements StateHolder<DummyVariable<T>.Values> {
implements StateHolder<DummyVariable<T>.Values>, Variable.Exclusive<T> {

private final double def;
private final Integer exclusive;
Expand Down Expand Up @@ -46,6 +48,24 @@ protected void setValueImpl(T obj, double value) {
obj.moduleRequire(FilterMatchModule.class).invalidate(obj);
}

@Override
public Integer getCardinality() {
return exclusive;
}

@Override
public Optional<T> getHolder(Filterable<?> obj) {
if (!isExclusive()) throw new UnsupportedOperationException();
T[] keys = ((LimitedValues) obj.state(this)).additions;
return Optional.ofNullable(keys[0]);
}

@Override
public Collection<T> getHolders(Filterable<?> obj) {
if (!isExclusive()) throw new UnsupportedOperationException();
return obj.state(this).values.keySet();
}

class Values {
protected final Map<T, Double> values = new HashMap<>();

Expand All @@ -56,7 +76,7 @@ protected void setValue(T obj, double value) {

class LimitedValues extends Values {
// Circular buffer of last additions, head marks next location to replace
private final @Nullable T[] additions;
private final T[] additions;
private int head = 0;

public LimitedValues() {
Expand Down

0 comments on commit 511161f

Please sign in to comment.