Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/main' into 1.20.4-f…
Browse files Browse the repository at this point in the history
…abric
  • Loading branch information
melontini committed May 10, 2024
2 parents 0916aea + 6c58aca commit 1a28644
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 45 deletions.
26 changes: 3 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,6 @@

User Changes:

This update extends the expression syntax to support nbt and block properties.

* Returned to downloading mappings at startup.
* Compound and List NBT tags are now properly supported.
* Added `nbt` field to item stacks, entities and block entities.

This Allows you to access NBT data like so: `this_entity.nbt.Air`. You should avoid NBT access, as it can get extremely slow.

* Added `properties` field to *states.

This can be used to access block state properties like so: `block_state.properties.candles`.

* Added lots of new functions. Consult the wiki for more info!
* Added `short_circuit` to `defaulted`, `all_of`, `any_of`. If true, commands will terminate immediately upon the condition failing.
* `hasContext` and `structContainsKey` now accept VarArgs.
* Removed arbitrary map support, as it was pretty poorly implemented.
* Constants are now case-sensitive.

Dev Changes:

* Moved Command codecs to MapCodec.
* Added BooleanExpression, similar to Arithmtica.
* Added `runTriState` to EventExecutors.
* Fixed `arrayAllMatch` acting like `arrayAnyMatch`.
* Added `attributes` field to living entities.
* Loot Context access in expressions is now cached.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.gradle.caching=true
minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3
# Mod Properties
mod_version=0.4.0
mod_version=0.4.1
maven_group=me.melontini
archives_base_name=commander
51 changes: 34 additions & 17 deletions src/main/java/me/melontini/commander/impl/expression/EvalUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.DataResult;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import lombok.SneakyThrows;
import me.melontini.commander.impl.event.data.types.ExtractionTypes;
import me.melontini.commander.impl.expression.extensions.ProxyMap;
import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter;
import me.melontini.commander.impl.expression.functions.*;
import me.melontini.commander.impl.expression.functions.arrays.*;
Expand All @@ -30,10 +31,10 @@
import org.jetbrains.annotations.Nullable;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class EvalUtils {
Expand All @@ -51,7 +52,7 @@ public class EvalUtils {
.singleQuoteStringLiteralsAllowed(true);

var fd = ExpressionConfiguration.defaultConfiguration().getFunctionDictionary();
Map<String, FunctionIfc> functions = new HashMap<>(((MapBasedFunctionDictionaryAccessor) fd)
Map<String, FunctionIfc> functions = new Object2ReferenceOpenHashMap<>(((MapBasedFunctionDictionaryAccessor) fd)
.commander$getFunctions().entrySet().stream()
.collect(Collectors.toMap(e -> CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, e.getKey()), Map.Entry::getValue)));
functions.put("random", new RangedRandomFunction());
Expand Down Expand Up @@ -109,7 +110,7 @@ public static EvaluationValue evaluate(LootContext context, Expression exp) {
public static DataResult<Expression> parseExpression(String expression) {
try {
Expression exp = new Expression(expression, CONFIGURATION);
((ExpressionAccessor) exp).commander$constants(new HashMap<>(CONFIGURATION.getDefaultConstants()));
((ExpressionAccessor) exp).commander$constants(new Object2ReferenceOpenHashMap<>(CONFIGURATION.getDefaultConstants()));
exp.validate();
return DataResult.success(exp);
} catch (Throwable throwable) {
Expand All @@ -119,44 +120,60 @@ public static DataResult<Expression> parseExpression(String expression) {

public static class LootContextDataAccessor implements DataAccessorIfc {

private static final Map<Identifier, Function<LootContext, Object>> overrides = ImmutableMap.of(
private static final Map<Identifier, Function<LootContext, Object>> overrides = new Object2ReferenceOpenHashMap<>(Map.of(
new Identifier("level"), LootContext::getWorld,
new Identifier("luck"), LootContext::getLuck
);
));
public static final ThreadLocal<LootContext> LOCAL = new ThreadLocal<>();
private final Map<String, EvaluationValue> parameters = new HashMap<>();
private final Map<String, EvaluationValue> parameters = new Object2ReferenceOpenHashMap<>();
//In most cases the expression is reused, so caching this helps us avoid some big overhead.
private final Map<String, Supplier<EvaluationValue>> varCache = new Object2ReferenceOpenHashMap<>();

@Override
public @Nullable EvaluationValue getData(String variable) {
var localParam = parameters.get(variable);
if (localParam != null) return localParam;
var supplier = varCache.get(variable);
if (supplier != null) return supplier.get(); //Parameters are cached by setData, so this is fine.

var r = Identifier.validate(variable);
if (r.error().isPresent()) {
throw new CmdEvalException("%s - %s".formatted(variable, r.error().orElseThrow().message()));
throw new CmdEvalException("%s - no such variable or %s".formatted(variable, r.error().orElseThrow().message()));
}

var id = r.result().orElseThrow();
var func = overrides.get(id);
if (func != null) return CONFIGURATION.getEvaluationValueConverter().convertObject(func.apply(LOCAL.get()), CONFIGURATION);
if (func != null) {
supplier = () -> ProxyMap.convert(func.apply(LOCAL.get()));
varCache.put(variable, supplier);
return supplier.get();
}

var param = ExtractionTypes.getParameter(id);
if (param == null) throw new CmdEvalException("%s is not a registered loot context parameter!".formatted(id));

var object = LOCAL.get().get(param);
if (object == null) return null;
return CONFIGURATION.getEvaluationValueConverter().convertObject(object, CONFIGURATION);
if (param == null)
throw new CmdEvalException("%s is not a registered loot context parameter, variable or override!".formatted(id));
supplier = () -> {
var object = LOCAL.get().get(param);
if (object == null) return null;
return ProxyMap.convert(object);
};
varCache.put(variable, supplier);
return supplier.get();
}

@Override
public void setData(String variable, EvaluationValue value) {
parameters.put(variable, value);

if (value == null) {
varCache.remove(variable);
} else {
varCache.put(variable, () -> parameters.get(variable)); //We're already here, so might as well cache.
}
}
}

public static class SimpleFunctionDictionary implements FunctionDictionaryIfc {

private final Map<String, FunctionIfc> functions = new Object2ObjectOpenHashMap<>();
private final Map<String, FunctionIfc> functions = new Object2ReferenceOpenHashMap<>();

public static FunctionDictionaryIfc ofFunctions(Map<String, FunctionIfc> functions) {
FunctionDictionaryIfc dictionary = new SimpleFunctionDictionary();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package me.melontini.commander.impl.expression.extensions;

import me.melontini.commander.impl.expression.extensions.convert.attributes.EntityAttributesStruct;
import me.melontini.commander.impl.expression.extensions.convert.states.StateStruct;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.state.State;
Expand All @@ -22,5 +24,6 @@ static void init() {
ReflectiveMapStructure.addField(BlockEntity.class, "nbt", BlockEntity::createNbtWithIdentifyingData);

ReflectiveMapStructure.addField(State.class, "properties", StateStruct::new);
ReflectiveMapStructure.addField(LivingEntity.class, "attributes", e -> new EntityAttributesStruct(e.getAttributes()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,19 @@ public boolean containsKey(Object obj) {
}

private static @Nullable Tuple<Class<?>, Function<Object, Object>> findFieldOrMethod(Class<?> cls, String name) {
var keeper = Commander.get().mappingKeeper();
String mapped;
Class<?> target = cls;
do {
if ((mapped = Commander.get().mappingKeeper().getFieldOrMethod(target, name)) != null) return findAccessor(target, mapped);
if ((mapped = keeper.getFieldOrMethod(target, name)) != null) return findAccessor(target, mapped);
var targetItfs = target.getInterfaces();
if (targetItfs.length == 0) continue;

Queue<Class<?>> interfaces = new ArrayDeque<>(List.of(targetItfs));
while (!interfaces.isEmpty()) {
var itf = interfaces.poll();

if ((mapped = Commander.get().mappingKeeper().getFieldOrMethod(itf, name)) != null) return findAccessor(itf, mapped);
if ((mapped = keeper.getFieldOrMethod(itf, name)) != null) return findAccessor(itf, mapped);
if ((targetItfs = itf.getInterfaces()).length > 0) interfaces.addAll(List.of(targetItfs));
}
} while ((target = target.getSuperclass()) != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public class ReflectiveValueConverter implements EvaluationValueConverterIfc {

static List<ConverterIfc> converters = Arrays.asList(
private static final List<ConverterIfc> converters = Arrays.asList(
new NumberConverter(),
new StringConverter(),
new BooleanConverter(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package me.melontini.commander.impl.expression.extensions.convert.attributes;

import com.ezylang.evalex.data.EvaluationValue;
import me.melontini.commander.impl.expression.extensions.ProxyMap;
import net.minecraft.entity.attribute.AttributeContainer;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;

import java.math.BigDecimal;

public class EntityAttributesStruct extends ProxyMap {

private final AttributeContainer container;

public EntityAttributesStruct(AttributeContainer container) {
this.container = container;
}

@Override
public boolean containsKey(Object key) {
if (!(key instanceof String s)) return false;
var attr = Registries.ATTRIBUTE.get(new Identifier(s));
if (attr == null) return false;
return container.hasAttribute(attr);
}

@Override
public EvaluationValue get(Object key) {
if (!(key instanceof String s)) return EvaluationValue.nullValue();
return EvaluationValue.numberValue(BigDecimal.valueOf(container.getValue(Registries.ATTRIBUTE.get(new Identifier(s)))));
}

@Override
public String toString() {
return String.valueOf(container.toNbt());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ public EvaluationValue evaluate(Expression expression, Token functionToken, Eval
List<EvaluationValue> array = par[0].getArrayValue();
ASTNode predicate = par[1].getExpressionNode();

return array.stream().anyMatch(value -> runLambda(expression, value, predicate).getBooleanValue()) ? EvalUtils.TRUE : EvalUtils.FALSE;
return array.stream().allMatch(value -> runLambda(expression, value, predicate).getBooleanValue()) ? EvalUtils.TRUE : EvalUtils.FALSE;
}
}

0 comments on commit 1a28644

Please sign in to comment.