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

Experimental features & the using structure. #6552

Merged
merged 36 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a52c7d5
First pass at supporting SimpleNode for Structures
APickledWalrus Apr 11, 2024
2d92a20
Experimental features - the 'using' structure.
Moderocky Apr 11, 2024
dac0fac
Oops :grimacing:
Moderocky Apr 11, 2024
ebbea3c
Improve documentation and test coverage.
Moderocky Apr 11, 2024
8ed6bf5
Add shane's extra special one-line creation & registration (50% off t…
Moderocky Apr 11, 2024
11829da
Update src/main/java/ch/njol/skript/conditions/CondIsUsing.java
Moderocky Apr 12, 2024
44d071c
Apply suggestions from code review
Moderocky Apr 12, 2024
d1389b9
Add Ayham's changes (bye bye nice final modifiers :cry:)
Moderocky Apr 12, 2024
aad6901
Merge branch 'dev/feature' into experiments
Moderocky Apr 12, 2024
6aec06e
Add a test-only syntax contingent on a feature flag.
Moderocky Apr 12, 2024
705f6a6
Merge branch 'experiments' of https://github.com/Moderocky/Skript int…
Moderocky Apr 12, 2024
4fd80fa
Update src/main/java/org/skriptlang/skript/lang/experiment/Feature.java
Moderocky Apr 13, 2024
6df8a42
Merge branch 'dev/feature' into experiments
Moderocky Apr 13, 2024
acca1e4
Merge branch 'dev/feature' into experiments
Moderocky Apr 23, 2024
f740de3
Add todo notes and deprecate internal members.
Moderocky Apr 23, 2024
753c8ac
I made it string sovde :(
Moderocky Apr 23, 2024
4f6d6c6
Merge branch 'dev/feature' into experiments
APickledWalrus May 5, 2024
14eeb2e
Update src/main/java/org/skriptlang/skript/lang/structure/Structure.java
Moderocky May 5, 2024
84ad0a0
:(
Moderocky May 6, 2024
b6dca8e
Allow unregistering experiments.
Moderocky May 6, 2024
8830c0e
No brackets :(
Moderocky May 6, 2024
71001dc
Update src/main/java/ch/njol/skript/conditions/CondIsUsingFeature.java
Moderocky May 6, 2024
83a577c
Update src/main/java/ch/njol/skript/lang/SyntaxElement.java
Moderocky May 6, 2024
ffa1b6e
Move experiments to snapshot model.
Moderocky May 6, 2024
bea7867
Merge branch 'experiments' of https://github.com/Moderocky/Skript int…
Moderocky May 6, 2024
a11c58a
Add docs for needy walrus.
Moderocky May 6, 2024
2cca6c0
Update src/main/java/org/skriptlang/skript/lang/script/Script.java
Moderocky May 7, 2024
c5c2228
Apply suggestions from code review
Moderocky May 7, 2024
bff4b0d
Compromising my values for walrus
Moderocky May 7, 2024
a091725
Merge branch 'dev/feature' into experiments
Moderocky May 9, 2024
e743eab
Apply changes from code review.
Moderocky May 9, 2024
e6d3ec5
Change pattern method.
Moderocky May 16, 2024
4b75721
Apply suggestions from code review
Moderocky May 16, 2024
f7b9124
Move features to registrations.
Moderocky May 16, 2024
b1072ae
Merge branch 'experiments' of https://github.com/Moderocky/Skript int…
Moderocky May 16, 2024
c4b383e
Merge branch 'dev/feature' into experiments
Moderocky May 16, 2024
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
77 changes: 48 additions & 29 deletions src/main/java/ch/njol/skript/ScriptLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.BlockingQueue;
Expand Down Expand Up @@ -490,17 +492,17 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O

ScriptInfo scriptInfo = new ScriptInfo();

List<NonNullPair<Script, List<Structure>>> scripts = new ArrayList<>();
List<LoadingScriptInfo> scripts = new ArrayList<>();

List<CompletableFuture<Void>> scriptInfoFutures = new ArrayList<>();
for (Config config : configs) {
if (config == null)
throw new NullPointerException();

CompletableFuture<Void> future = makeFuture(() -> {
NonNullPair<Script, List<Structure>> pair = loadScript(config);
scripts.add(pair);
scriptInfo.add(new ScriptInfo(1, pair.getSecond().size()));
LoadingScriptInfo info = loadScript(config);
scripts.add(info);
scriptInfo.add(new ScriptInfo(1, info.structures.size()));
return null;
}, openCloseable);

Expand All @@ -518,10 +520,10 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O

// build sorted list
// this nest of pairs is terrible, but we need to keep the reference to the modifiable structures list
List<NonNullPair<NonNullPair<Script, List<Structure>>, Structure>> pairs = scripts.stream()
.flatMap(pair -> { // Flatten each entry down to a stream of Script-Structure pairs
return pair.getSecond().stream()
.map(structure -> new NonNullPair<>(pair, structure));
List<NonNullPair<LoadingScriptInfo, Structure>> pairs = scripts.stream()
.flatMap(info -> { // Flatten each entry down to a stream of Script-Structure pairs
return info.structures.stream()
.map(structure -> new NonNullPair<>(info, structure));
})
.sorted(Comparator.comparing(pair -> pair.getSecond().getPriority()))
.collect(Collectors.toCollection(ArrayList::new));
Expand All @@ -530,19 +532,19 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
pairs.removeIf(pair -> {
Structure structure = pair.getSecond();

parser.setActive(pair.getFirst().getFirst());
parser.setActive(pair.getFirst().script);
parser.setCurrentStructure(structure);
parser.setNode(structure.getEntryContainer().getSource());
parser.setNode(pair.getFirst().nodeMap.get(structure));

try {
if (!structure.preLoad()) {
pair.getFirst().getSecond().remove(structure);
pair.getFirst().structures.remove(structure);
return true;
}
} catch (Exception e) {
//noinspection ThrowableNotThrown
Skript.exception(e, "An error occurred while trying to preLoad a Structure.");
pair.getFirst().getSecond().remove(structure);
pair.getFirst().structures.remove(structure);
return true;
}
return false;
Expand All @@ -558,19 +560,19 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
pairs.removeIf(pair -> {
Structure structure = pair.getSecond();

parser.setActive(pair.getFirst().getFirst());
parser.setActive(pair.getFirst().script);
parser.setCurrentStructure(structure);
parser.setNode(structure.getEntryContainer().getSource());
parser.setNode(pair.getFirst().nodeMap.get(structure));

try {
if (!structure.load()) {
pair.getFirst().getSecond().remove(structure);
pair.getFirst().structures.remove(structure);
return true;
}
} catch (Exception e) {
//noinspection ThrowableNotThrown
Skript.exception(e, "An error occurred while trying to load a Structure.");
pair.getFirst().getSecond().remove(structure);
pair.getFirst().structures.remove(structure);
return true;
}
return false;
Expand All @@ -581,19 +583,19 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
pairs.removeIf(pair -> {
Structure structure = pair.getSecond();

parser.setActive(pair.getFirst().getFirst());
parser.setActive(pair.getFirst().script);
parser.setCurrentStructure(structure);
parser.setNode(structure.getEntryContainer().getSource());
parser.setNode(pair.getFirst().nodeMap.get(structure));

try {
if (!structure.postLoad()) {
pair.getFirst().getSecond().remove(structure);
pair.getFirst().structures.remove(structure);
return true;
}
} catch (Exception e) {
//noinspection ThrowableNotThrown
Skript.exception(e, "An error occurred while trying to postLoad a Structure.");
pair.getFirst().getSecond().remove(structure);
pair.getFirst().structures.remove(structure);
return true;
}
return false;
Expand All @@ -612,17 +614,34 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
});
}

private static class LoadingScriptInfo {

public final Script script;

public final List<Structure> structures;

public final Map<Structure, Node> nodeMap;

public LoadingScriptInfo(Script script, List<Structure> structures, Map<Structure, Node> nodeMap) {
this.script = script;
this.structures = structures;
this.nodeMap = nodeMap;
}

}

/**
* Creates a script and loads the provided config into it.
* @param config The config to load into a script.
* @return A pair containing the script that was loaded and a modifiable version of the structures list.
*/
// Whenever you call this method, make sure to also call PreScriptLoadEvent
private static NonNullPair<Script, List<Structure>> loadScript(Config config) {
private static LoadingScriptInfo loadScript(Config config) {
if (config.getFile() == null)
throw new IllegalArgumentException("A config must have a file to be loaded.");

ParserInstance parser = getParser();
Map<Structure, Node> nodeMap = new HashMap<>();
List<Structure> structures = new ArrayList<>();
Script script = new Script(config, structures);
parser.setActive(script);
Expand All @@ -632,31 +651,31 @@ private static NonNullPair<Script, List<Structure>> loadScript(Config config) {
SkriptConfig.configs.add(config);

try (CountingLogHandler ignored = new CountingLogHandler(SkriptLogger.SEVERE).start()) {
for (Node cnode : config.getMainNode()) {
if (!(cnode instanceof SectionNode)) {
Skript.error("invalid line - all code has to be put into triggers");
for (Node node : config.getMainNode()) {
if (!(node instanceof SimpleNode) && !(node instanceof SectionNode)) {
// unlikely to occur, but just in case
Skript.error("could not interpret line as a structure");
continue;
}

SectionNode node = ((SectionNode) cnode);
String line = node.getKey();
if (line == null)
continue;
line = replaceOptions(line); // replace options here before validation

if (!SkriptParser.validateLine(line))
continue;

if (Skript.logVeryHigh() && !Skript.debug())
Skript.info("loading trigger '" + line + "'");

line = replaceOptions(line);

Structure structure = Structure.parse(line, node, "Can't understand this structure: " + line);

if (structure == null)
continue;

structures.add(structure);
nodeMap.put(structure, node);
}

if (Skript.logHigh()) {
Expand Down Expand Up @@ -694,8 +713,8 @@ private static NonNullPair<Script, List<Structure>> loadScript(Config config) {
Skript.exception(e);
}
}
return new NonNullPair<>(script, structures);

return new LoadingScriptInfo(script, structures, nodeMap);
}

/*
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/ch/njol/skript/Skript.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
import ch.njol.util.Kleenean;
import ch.njol.util.NullableChecker;
import ch.njol.util.StringUtils;
import ch.njol.util.coll.CollectionUtils;
import ch.njol.util.coll.iterator.CheckedIterator;
import ch.njol.util.coll.iterator.EnumerationIterable;
import com.google.common.collect.Lists;
Expand All @@ -108,6 +107,7 @@
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.eclipse.jdt.annotation.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.junit.After;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
Expand All @@ -116,6 +116,8 @@
import org.skriptlang.skript.lang.converter.Converter;
import org.skriptlang.skript.lang.converter.Converters;
import org.skriptlang.skript.lang.entry.EntryValidator;
import org.skriptlang.skript.lang.experiment.ExperimentRegistry;
import org.skriptlang.skript.lang.experiment.Feature;
import org.skriptlang.skript.lang.script.Script;
import org.skriptlang.skript.lang.structure.Structure;
import org.skriptlang.skript.lang.structure.StructureInfo;
Expand Down Expand Up @@ -230,6 +232,8 @@ public static void updateMinecraftVersion() {

@Nullable
private static Version version = null;
@Deprecated(forRemoval = true) // TODO this field will be replaced by a proper registry later
private static @UnknownNullability ExperimentRegistry experimentRegistry;

public static Version getVersion() {
final Version v = version;
Expand Down Expand Up @@ -362,6 +366,13 @@ public static void disableHookRegistration(Class<? extends Hook<?>>... hooks) {
*/
private File scriptsFolder;

/**
* @return The manager for experimental, optional features.
*/
public static ExperimentRegistry experiments() {
return experimentRegistry;
}

/**
* @return The folder containing all Scripts.
*/
Expand Down Expand Up @@ -392,6 +403,8 @@ public void onEnable() {
} catch (Exception e) {
Skript.exception(e, "Update checker could not be initialized.");
}
experimentRegistry = new ExperimentRegistry(this);
Feature.registerAll(getAddonInstance(), experimentRegistry);

if (!getDataFolder().isDirectory())
getDataFolder().mkdirs();
Expand Down Expand Up @@ -1197,6 +1210,7 @@ public void onDisable() {
if (disabled)
return;
disabled = true;
this.experimentRegistry = null;

if (!partDisabled) {
beforeDisable();
Expand Down Expand Up @@ -1515,6 +1529,13 @@ public static <E extends Structure> void registerStructure(Class<E> c, String...
structures.add(structureInfo);
}

public static <E extends Structure> void registerSimpleStructure(Class<E> c, String... patterns) {
checkAcceptRegistrations();
String originClassPath = Thread.currentThread().getStackTrace()[2].getClassName();
StructureInfo<E> structureInfo = new StructureInfo<>(patterns, c, originClassPath, true);
structures.add(structureInfo);
}

public static <E extends Structure> void registerStructure(Class<E> c, EntryValidator entryValidator, String... patterns) {
checkAcceptRegistrations();
String originClassPath = Thread.currentThread().getStackTrace()[2].getClassName();
Expand Down
99 changes: 99 additions & 0 deletions src/main/java/ch/njol/skript/conditions/CondIsUsingFeature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.conditions;

import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Condition;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.VariableString;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import org.skriptlang.skript.lang.experiment.Experiment;
import org.skriptlang.skript.lang.script.Script;

@Name("Is Using Experimental Feature")
@Description("Checks whether a script is using an experimental feature by name.")
@Examples({"the script is using \"example feature\"",
"on load:",
"\tif the script is using \"example feature\":",
"\t\tbroadcast \"You're using an experimental feature!\""})
@Since("INSERT VERSION")
public class CondIsUsingFeature extends Condition {
static {
Skript.registerCondition(CondIsUsingFeature.class,
"[the] [current] script is using %strings%",
"[the] [current] script is(n't| not) using %strings%");
}
Moderocky marked this conversation as resolved.
Show resolved Hide resolved

private @UnknownNullability Expression<String> names;
private @UnknownNullability Script script;
Moderocky marked this conversation as resolved.
Show resolved Hide resolved
private @Nullable Boolean knownResult;

@SuppressWarnings("null")
@Override
public boolean init(Expression<?>[] expressions, int pattern, Kleenean delayed, ParseResult result) {
//noinspection unchecked
this.names = (Expression<String>) expressions[0];
this.setNegated(pattern == 1);
this.script = this.getParser().getCurrentScript();
// if this is a 'simple' variablestring with no inputs we can check it during parse time
if (names instanceof VariableString) {
VariableString string = (VariableString) names;
if (string.isSimple()) {
String value = string.toString(null);
knownResult = this.isNegated() ^ this.hasExperiment(value);
}
}
return true;
}

@Override
public boolean check(Event event) {
if (knownResult != null) // we checked this in advance during init
return knownResult;
String[] array = names.getArray(event);
if (array.length == 0)
return true;
boolean isUsing = true;
for (@NotNull String object : array) {
isUsing &= this.hasExperiment(object);
}
return this.isNegated() ^ isUsing;
Moderocky marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public boolean hasExperiment(String name) {
Experiment experiment = Skript.experiments().find(name);
return script.hasExperiment(experiment);
}

@Override
public String toString(@Nullable Event event, boolean debug) {
return "the current script " + (isNegated() ? " isn't" : " is") + " using " + names.toString(event, debug);
Moderocky marked this conversation as resolved.
Show resolved Hide resolved
}

}
Loading
Loading