Proper conditions

This commit is contained in:
JFronny 2021-01-04 16:26:13 +01:00
parent b07e0a0671
commit f825b73e38
14 changed files with 243 additions and 50 deletions

View File

@ -46,6 +46,7 @@ There have been issues with LibCD in development, so it might be smart not to us
Respackopts allows creating conditional resources by creating a file named `{targetFile}.rpo`.\
This file is a json file that contains an array named "conditions". The resource will be ignored if any of the conditions are not met.\
This allows (for example) overrides for textures to only be loaded if the user enables them through the config.\
It also allows the following operations: `and`, `equal`, `nor`/`not`, `or`, `xor`
As an example can be seen [here](https://gitlab.com/JFronny/respackopts/-/tree/master/run/resourcepacks/lumi/assets/minecraft/lang)
## Mod developers
All data is available in static HashMaps in `io.gitlab.jfronny.respackopts.Respackopts`.\

View File

@ -1,5 +1,10 @@
{
"conditions": [
"lumi:subcategoryTest.enableLang"
"lumi:subcategoryTest.enableLang",
{
"not": [
"lumi:subcategoryTest.enableLangForceDisable"
]
}
]
}

View File

@ -31,7 +31,8 @@
"default": 16,
"max": 20
},
"enableLang": true
"enableLang": true,
"enableLangForceDisable": false
}
}
}

View File

@ -134,27 +134,4 @@ public class Respackopts implements ClientModInitializer {
}
}
}
public static boolean matchStringCondition(String condition) throws SyntaxError {
if (condition == null) {
throw new SyntaxError("Condition must not be null");
}
if (!condition.contains(":")) {
throw new SyntaxError("You must include you resource pack ID in conditions (format: pack:some.key)");
}
AtomicBoolean found = new AtomicBoolean(false);
AtomicBoolean output = new AtomicBoolean(false);
Respackopts.resPackMetas.forEach((r, v) -> {
String sourcePack = condition.split(":")[0];
String name = condition.substring(condition.indexOf(':') + 1);
if (Objects.equals(v.id, sourcePack)) {
found.set(true);
output.set(Respackopts.boolVals.get(sourcePack).get(name));
}
});
if (!found.get()) {
throw new SyntaxError("Could not find pack with specified ID");
}
return output.get();
}
}

View File

@ -0,0 +1,28 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import java.util.LinkedHashSet;
import java.util.Set;
public class AndCondition implements Condition {
@Override
public boolean evaluate(JsonElement node) throws SyntaxError {
if (!node.isJsonArray())
throw new SyntaxError("\"and\" condition requires an array of conditions");
for (JsonElement jsonElement : node.getAsJsonArray()) {
if (!ConditionEvaluator.evaluate(jsonElement))
return false;
}
return true;
}
@Override
public Set<String> getKeys() {
Set<String> strings = new LinkedHashSet<>();
strings.add("add");
strings.add("&");
strings.add("conditions"); // This is also the root condition
return strings;
}
}

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import java.util.Set;
public interface Condition {
boolean evaluate(JsonElement node) throws SyntaxError;
Set<String> getKeys();
}

View File

@ -0,0 +1,61 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import io.gitlab.jfronny.respackopts.Respackopts;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
public class ConditionEvaluator {
private static Set<Condition> conditions;
static {
conditions = new LinkedHashSet<>();
conditions.add(new AndCondition());
conditions.add(new OrCondition());
conditions.add(new XorCondition());
conditions.add(new EqualityCondition());
conditions.add(new NorCondition());
}
public static boolean evaluate(JsonElement condition) throws SyntaxError {
if (condition.isJsonPrimitive() && condition.getAsJsonPrimitive().isString())
return evaluate(condition.getAsString());
if (condition.isJsonObject() && condition.getAsJsonObject().size() == 1) {
for (Map.Entry<String, JsonElement> entry : condition.getAsJsonObject().entrySet()) {
for (Condition c : conditions) {
if (c.getKeys().contains(entry.getKey())) {
return c.evaluate(entry.getValue());
}
}
throw new SyntaxError("Could not find condition: " + entry.getKey());
}
}
throw new SyntaxError("Condition entries may only be json objects containing one key and one value or strings");
}
public static boolean evaluate(String condition) throws SyntaxError {
if (condition == null) {
throw new SyntaxError("Condition must not be null");
}
if (!condition.contains(":")) {
throw new SyntaxError("You must include you resource pack ID in conditions (format: pack:some.key)");
}
AtomicBoolean found = new AtomicBoolean(false);
AtomicBoolean output = new AtomicBoolean(false);
Respackopts.resPackMetas.forEach((r, v) -> {
String sourcePack = condition.split(":")[0];
String name = condition.substring(condition.indexOf(':') + 1);
if (Objects.equals(v.id, sourcePack)) {
found.set(true);
output.set(Respackopts.boolVals.get(sourcePack).get(name));
}
});
if (!found.get()) {
throw new SyntaxError("Could not find pack with specified ID");
}
return output.get();
}
}

View File

@ -0,0 +1,34 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
//TODO implement equality checking for string/number variables
public class EqualityCondition implements Condition {
@Override
public boolean evaluate(JsonElement node) throws SyntaxError {
if (!node.isJsonArray())
throw new SyntaxError("\"equal\" condition requires an array of conditions");
Optional<Boolean> v = Optional.empty();
for (JsonElement jsonElement : node.getAsJsonArray()) {
boolean current = ConditionEvaluator.evaluate(jsonElement);
if (!v.isPresent())
v = Optional.of(current);
if (current != v.get())
return false;
}
return true;
}
@Override
public Set<String> getKeys() {
Set<String> strings = new LinkedHashSet<>();
strings.add("==");
strings.add("=");
strings.add("equal");
strings.add("eq");
return strings;
}
}

View File

@ -0,0 +1,28 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import java.util.LinkedHashSet;
import java.util.Set;
public class NorCondition implements Condition {
@Override
public boolean evaluate(JsonElement node) throws SyntaxError {
if (!node.isJsonArray())
throw new SyntaxError("\"not\"/\"nor\" condition requires an array of conditions");
for (JsonElement jsonElement : node.getAsJsonArray()) {
if (ConditionEvaluator.evaluate(jsonElement))
return false;
}
return true;
}
@Override
public Set<String> getKeys() {
Set<String> strings = new LinkedHashSet<>();
strings.add("not");
strings.add("nor");
strings.add("!");
return strings;
}
}

View File

@ -0,0 +1,27 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import java.util.LinkedHashSet;
import java.util.Set;
public class OrCondition implements Condition {
@Override
public boolean evaluate(JsonElement node) throws SyntaxError {
if (!node.isJsonArray())
throw new SyntaxError("\"or\" condition requires an array of conditions");
for (JsonElement jsonElement : node.getAsJsonArray()) {
if (ConditionEvaluator.evaluate(jsonElement))
return true;
}
return false;
}
@Override
public Set<String> getKeys() {
Set<String> strings = new LinkedHashSet<>();
strings.add("or");
strings.add("|");
return strings;
}
}

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.data.RpoResourceEntry;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -23,18 +23,7 @@ public class ResourcePackFilter {
if (!containsFileBase.test(name + Respackopts.fileExtension))
return true;
try (InputStream stream = openFileBase.apply(name + Respackopts.fileExtension); Reader w = new InputStreamReader(stream)) {
RpoResourceEntry entry = Respackopts.g.fromJson(w, RpoResourceEntry.class);
if (entry.conditions != null) {
for (String condition : entry.conditions) {
if (!Respackopts.matchStringCondition(condition))
return false;
}
return true;
}
else {
Respackopts.logger.error("Conditions null for " + name);
return true;
}
return ConditionEvaluator.evaluate(Respackopts.g.fromJson(w, JsonElement.class));
}
catch (Throwable e) {
e.printStackTrace();

View File

@ -0,0 +1,28 @@
package io.gitlab.jfronny.respackopts.conditions;
import com.google.gson.JsonElement;
import java.util.LinkedHashSet;
import java.util.Set;
public class XorCondition implements Condition {
@Override
public boolean evaluate(JsonElement node) throws SyntaxError {
if (!node.isJsonArray())
throw new SyntaxError("\"xor\" condition requires an array of conditions");
boolean bl = false;
for (JsonElement jsonElement : node.getAsJsonArray()) {
if (ConditionEvaluator.evaluate(jsonElement))
bl = !bl;
}
return bl;
}
@Override
public Set<String> getKeys() {
Set<String> strings = new LinkedHashSet<>();
strings.add("^");
strings.add("xor");
return strings;
}
}

View File

@ -1,5 +0,0 @@
package io.gitlab.jfronny.respackopts.data;
public class RpoResourceEntry {
public String[] conditions;
}

View File

@ -1,9 +1,11 @@
package io.gitlab.jfronny.respackopts.integration;
import com.google.gson.JsonElement;
import io.github.cottonmc.libcd.api.CDSyntaxError;
import io.github.cottonmc.libcd.api.condition.ConditionManager;
import io.github.cottonmc.libcd.api.init.ConditionInitializer;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.conditions.ConditionEvaluator;
import io.gitlab.jfronny.respackopts.conditions.SyntaxError;
import net.minecraft.util.Identifier;
@ -11,15 +13,22 @@ public class LibCDCompat implements ConditionInitializer {
@Override
public void initConditions(ConditionManager conditionManager) {
conditionManager.registerCondition(new Identifier(Respackopts.ID, "cfg"), q -> {
if (!(q instanceof String)) {
throw new CDSyntaxError("Expected string");
if (q instanceof String) {
try {
return ConditionEvaluator.evaluate((String) q);
} catch (Throwable error) {
throw new CDSyntaxError(error.getMessage());
}
}
String s = (String) q;
try {
return Respackopts.matchStringCondition(s);
} catch (SyntaxError syntaxError) {
throw new CDSyntaxError(syntaxError.getMessage());
else if (q instanceof JsonElement) {
try {
return ConditionEvaluator.evaluate((JsonElement) q);
} catch (Throwable error) {
throw new CDSyntaxError(error.getMessage());
}
}
else
throw new CDSyntaxError("Expected Json element or string for rpo libcd conditions");
});
}
}