diff --git a/.github/workflows/docs/generate-docs/action.yml b/.github/workflows/docs/generate-docs/action.yml index 2470f852450..f730f3ad098 100644 --- a/.github/workflows/docs/generate-docs/action.yml +++ b/.github/workflows/docs/generate-docs/action.yml @@ -52,10 +52,8 @@ runs: find $1 -type f -exec sed -i -e "s/$2/$3/g" {} \; } - # this should be replaced with a more reliable jq command, - # but it can't be right now because docs.json is actually not valid json. get_skript_version_of_directory() { - grep skriptVersion "$1/docs.json" | cut -d\" -f 4 + jq ".source.version" "$1/docs.json" } if [ -d "${DOCS_REPO_DIR}/docs/templates" ] diff --git a/src/main/java/ch/njol/skript/SkriptCommand.java b/src/main/java/ch/njol/skript/SkriptCommand.java index d27ec05ed45..5592fed5537 100644 --- a/src/main/java/ch/njol/skript/SkriptCommand.java +++ b/src/main/java/ch/njol/skript/SkriptCommand.java @@ -386,8 +386,9 @@ public boolean onCommand(CommandSender sender, Command command, String label, St outputDir.mkdirs(); Skript.info(sender, "Generating docs..."); - JSONGenerator jsonGenerator = new JSONGenerator(templateDir, outputDir); - jsonGenerator.generate(); + + JSONGenerator.of(Skript.instance()) + .generate(outputDir.toPath().resolve("docs.json")); if (!templateDir.exists()) { Skript.info(sender, "JSON-only documentation generated!"); diff --git a/src/main/java/ch/njol/skript/doc/DocumentationGenerator.java b/src/main/java/ch/njol/skript/doc/DocumentationGenerator.java index 949e59f3ca5..1f253d71e63 100644 --- a/src/main/java/ch/njol/skript/doc/DocumentationGenerator.java +++ b/src/main/java/ch/njol/skript/doc/DocumentationGenerator.java @@ -1,10 +1,12 @@ package ch.njol.skript.doc; import java.io.File; +import java.nio.file.Path; /** - * Represents a class which generates a documentation format (like HTML or JSON) + * @deprecated Use {@link JSONGenerator} instead. */ +@Deprecated(forRemoval = true, since = "INSERT VERSION") public abstract class DocumentationGenerator { protected File templateDir; @@ -16,8 +18,9 @@ public DocumentationGenerator(File templateDir, File outputDir) { } /** - * Generates the documentation file + * Use {@link JSONGenerator#generate(Path)} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public abstract void generate(); } diff --git a/src/main/java/ch/njol/skript/doc/DocumentationIdProvider.java b/src/main/java/ch/njol/skript/doc/DocumentationIdProvider.java index 35a01c143b3..a46028ed476 100644 --- a/src/main/java/ch/njol/skript/doc/DocumentationIdProvider.java +++ b/src/main/java/ch/njol/skript/doc/DocumentationIdProvider.java @@ -12,6 +12,7 @@ import ch.njol.skript.lang.function.Functions; import ch.njol.skript.registrations.Classes; import org.skriptlang.skript.lang.structure.Structure; +import org.skriptlang.skript.registration.SyntaxInfo; import java.util.Arrays; import java.util.Iterator; @@ -62,7 +63,16 @@ private static int calculateCollisionCount(Iterator potentialCo * @return the ID of the syntax element */ public static String getId(SyntaxElementInfo syntaxInfo) { - Class syntaxClass = syntaxInfo.getElementClass(); + return getId((SyntaxInfo) syntaxInfo); + } + + /** + * Gets the documentation ID of a syntax element + * @param syntaxInfo the SyntaxInfo to get the ID of + * @return the ID of the syntax element + */ + public static String getId(SyntaxInfo syntaxInfo) { + Class syntaxClass = syntaxInfo.type(); Iterator> syntaxElementIterator; if (Effect.class.isAssignableFrom(syntaxClass)) { syntaxElementIterator = Skript.getEffects().iterator(); @@ -79,7 +89,7 @@ public static String getId(SyntaxElementInfo syntaxInfo) { } int collisionCount = calculateCollisionCount(syntaxElementIterator, elementInfo -> elementInfo.getElementClass() == syntaxClass, - elementInfo -> Arrays.equals(elementInfo.getPatterns(), syntaxInfo.getPatterns())); + elementInfo -> Arrays.equals(elementInfo.getPatterns(), syntaxInfo.patterns().toArray(new String[0]))); DocumentationId documentationIdAnnotation = syntaxClass.getAnnotation(DocumentationId.class); if (documentationIdAnnotation == null) { return addCollisionSuffix(syntaxClass.getSimpleName(), collisionCount); diff --git a/src/main/java/ch/njol/skript/doc/HTMLGenerator.java b/src/main/java/ch/njol/skript/doc/HTMLGenerator.java index 163a57b2824..ad9666ebab6 100644 --- a/src/main/java/ch/njol/skript/doc/HTMLGenerator.java +++ b/src/main/java/ch/njol/skript/doc/HTMLGenerator.java @@ -37,7 +37,6 @@ /** * Template engine, primarily used for generating Skript documentation * pages by combining data from annotations and templates. - * */ public class HTMLGenerator extends DocumentationGenerator { diff --git a/src/main/java/ch/njol/skript/doc/JSONGenerator.java b/src/main/java/ch/njol/skript/doc/JSONGenerator.java index d30d0f039ac..4e15967ae9b 100644 --- a/src/main/java/ch/njol/skript/doc/JSONGenerator.java +++ b/src/main/java/ch/njol/skript/doc/JSONGenerator.java @@ -2,31 +2,39 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.lang.SkriptEventInfo; import ch.njol.skript.lang.SyntaxElement; -import ch.njol.skript.lang.SyntaxElementInfo; import ch.njol.skript.lang.function.Functions; import ch.njol.skript.lang.function.JavaFunction; import ch.njol.skript.registrations.Classes; import ch.njol.skript.registrations.EventValues; import ch.njol.skript.registrations.EventValues.EventValueInfo; import ch.njol.skript.util.Version; +import com.google.common.base.Preconditions; import com.google.common.collect.Multimap; import com.google.gson.*; +import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.block.BlockCanBuildEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.registration.BukkitRegistryKeys; +import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; import org.skriptlang.skript.lang.structure.Structure; -import org.skriptlang.skript.lang.structure.StructureInfo; +import org.skriptlang.skript.registration.DefaultSyntaxInfos; +import org.skriptlang.skript.registration.SyntaxInfo; +import org.skriptlang.skript.registration.SyntaxRegistry; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.stream.Stream; +import java.util.Map.Entry; /** * Generates JSON docs @@ -36,7 +44,7 @@ public class JSONGenerator extends DocumentationGenerator { /** * The current version of the JSON generator */ - public static final Version JSON_VERSION = new Version(1, 1); + public static final Version JSON_VERSION = new Version(2, 0); private static final Gson GSON = new GsonBuilder() .disableHtmlEscaping() @@ -44,8 +52,34 @@ public class JSONGenerator extends DocumentationGenerator { .serializeNulls() .create(); + /** + * Creates a {@link JSONGenerator} for the specified source. + * + * @param source The addon to use as source. + * @return The created {@link JSONGenerator}. + */ + @Contract("_ -> new") + public static JSONGenerator of(@NotNull SkriptAddon source) { + return new JSONGenerator(source); + } + + private final @NotNull SkriptAddon source; + + private JSONGenerator(@NotNull SkriptAddon source) { + super(Documentation.getDocsTemplateDirectory(), Documentation.getDocsOutputDirectory()); + + Preconditions.checkNotNull(source, "addon cannot be null"); + + this.source = source; + } + + /** + * @deprecated Use {@link #of(SkriptAddon)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public JSONGenerator(File templateDir, File outputDir) { super(templateDir, outputDir); + source = Skript.instance(); } /** @@ -80,8 +114,8 @@ private static JsonArray convertToJsonArray(String @Nullable ... strings) { * @param syntaxInfo the syntax info element to generate the documentation object of * @return the JsonObject representing the documentation of the provided syntax element */ - private static JsonObject generatedAnnotatedElement(SyntaxElementInfo syntaxInfo) { - Class syntaxClass = syntaxInfo.getElementClass(); + private static JsonObject generatedAnnotatedElement(SyntaxInfo syntaxInfo) { + Class syntaxClass = syntaxInfo.type(); Name name = syntaxClass.getAnnotation(Name.class); if (name == null || syntaxClass.getAnnotation(NoDoc.class) != null) return null; @@ -99,7 +133,7 @@ private static JsonObject generatedAnnotatedElement(SyntaxElementInfo syntaxI Description description = syntaxClass.getAnnotation(Description.class); syntaxJsonObject.add("description", description == null ? null : convertToJsonArray(description.value())); - syntaxJsonObject.add("patterns", cleanPatterns(syntaxInfo.getPatterns())); + syntaxJsonObject.add("patterns", cleanPatterns(syntaxInfo.patterns().toArray(new String[0]))); if (syntaxClass.isAnnotationPresent(Examples.class)) { @NotNull Examples examplesAnnotation = syntaxClass.getAnnotation(Examples.class); @@ -117,8 +151,7 @@ private static JsonObject generatedAnnotatedElement(SyntaxElementInfo syntaxI syntaxJsonObject.add("examples", null); } - Events events = syntaxClass.getAnnotation(Events.class); - syntaxJsonObject.add("events", events == null ? null : convertToJsonArray(events.value())); + syntaxJsonObject.add("events", getAnnotatedEvents(syntaxClass.getAnnotation(Events.class))); RequiredPlugins requirements = syntaxClass.getAnnotation(RequiredPlugins.class); syntaxJsonObject.add("requirements", requirements == null ? null : convertToJsonArray(requirements.value())); @@ -126,28 +159,72 @@ private static JsonObject generatedAnnotatedElement(SyntaxElementInfo syntaxI Keywords keywords = syntaxClass.getAnnotation(Keywords.class); syntaxJsonObject.add("keywords", keywords == null ? null : convertToJsonArray(keywords.value())); + if (syntaxInfo instanceof SyntaxInfo.Expression expression) { + syntaxJsonObject.add("returns", getExpressionReturnTypes(expression)); + } + return syntaxJsonObject; } + /** + * Returns the formatted events based on the {@link Events} annotation. + * + * @param events The events annotation. + * @return A json array with the formatted events value, or null if there is no annotation. + */ + private static @Nullable JsonArray getAnnotatedEvents(Events events) { + if (events == null || events.value() == null) { + return null; + } + + JsonArray array = new JsonArray(); + + for (String event : events.value()) { + JsonObject object = new JsonObject(); + + object.addProperty("id", event); + object.addProperty("name", event); + + array.add(object); + } + + return array; + } + + /** + * Gets an {@link DefaultSyntaxInfos.Expression}'s return type. + * + * @param expression The expression class. + * @return An object with the return type. + */ + private static @NotNull JsonObject getExpressionReturnTypes(DefaultSyntaxInfos.Expression expression) { + ClassInfo exact = Classes.getSuperClassInfo(expression.returnType()); + + JsonObject object = new JsonObject(); + object.addProperty("id", exact.getCodeName()); + object.addProperty("name", exact.getName().getSingular()); + return object; + } + /** * Generates the documentation JsonObject for an event * * @param info the event to generate the documentation object for * @return a documentation JsonObject for the event */ - private static JsonObject generateEventElement(SkriptEventInfo info) { + private static JsonObject generateEventElement(BukkitSyntaxInfos.Event info) { JsonObject syntaxJsonObject = new JsonObject(); syntaxJsonObject.addProperty("id", DocumentationIdProvider.getId(info)); - syntaxJsonObject.addProperty("name", info.getName()); + syntaxJsonObject.addProperty("name", info.name()); syntaxJsonObject.addProperty("cancellable", isCancellable(info)); - syntaxJsonObject.add("since", convertToJsonArray(info.getSince())); - syntaxJsonObject.add("patterns", cleanPatterns(info.getPatterns())); - syntaxJsonObject.add("description", convertToJsonArray(info.getDescription())); - syntaxJsonObject.add("requirements", convertToJsonArray(info.getRequiredPlugins())); - syntaxJsonObject.add("examples", convertToJsonArray(info.getExamples())); + syntaxJsonObject.add("since", convertToJsonArray(info.since().toArray(new String[0]))); + syntaxJsonObject.add("patterns", cleanPatterns(info.patterns().toArray(new String[0]))); + syntaxJsonObject.add("description", convertToJsonArray(info.description().toArray(new String[0]))); + syntaxJsonObject.add("requirements", convertToJsonArray(info.requiredPlugins().toArray(new String[0]))); + syntaxJsonObject.add("examples", convertToJsonArray(info.examples().toArray(new String[0]))); syntaxJsonObject.add("eventValues", getEventValues(info)); - syntaxJsonObject.add("keywords", convertToJsonArray(info.getKeywords())); + syntaxJsonObject.add("keywords", convertToJsonArray(info.keywords().toArray(new String[0]))); return syntaxJsonObject; } @@ -158,55 +235,54 @@ private static JsonObject generateEventElement(SkriptEventInfo info) { * @param info the event to generate the event values of * @return a JsonArray containing the documentation JsonObjects for each event value */ - private static JsonArray getEventValues(SkriptEventInfo info) { + private static JsonArray getEventValues(BukkitSyntaxInfos.Event info) { Set eventValues = new HashSet<>(); Multimap, EventValueInfo> allEventValues = EventValues.getPerEventEventValues(); - for (Class supportedEvent : info.events) { - for (Class event : allEventValues.keySet()) { + for (Class supportedEvent : info.events()) { + for (Entry, EventValueInfo> entry : allEventValues.entries()) { + Class event = entry.getKey(); + EventValueInfo eventValueInfo = entry.getValue(); + + if (event == null) { + continue; + } + if (!event.isAssignableFrom(supportedEvent)) { continue; } - Collection> eventValueInfos = allEventValues.get(event); - - for (EventValueInfo eventValueInfo : eventValueInfos) { - Class[] excludes = eventValueInfo.excludes(); - if (excludes != null && Set.of(excludes).contains(event)) { - continue; - } - - Class valueClass = eventValueInfo.valueClass(); - ClassInfo classInfo; - if (valueClass.isArray()) { - classInfo = Classes.getExactClassInfo(valueClass.componentType()); - } else { - classInfo = Classes.getExactClassInfo(valueClass); - } - - if (classInfo == null) { - continue; - } - - String name = classInfo.getName().getSingular(); - if (valueClass.isArray()) { - name = classInfo.getName().getPlural(); - } - if (name.isBlank()) { - continue; - } - - if (eventValueInfo.time() == EventValues.TIME_PAST) { - name = "past " + name; - } else if (eventValueInfo.time() == EventValues.TIME_FUTURE) { - name = "future " + name; - } - - JsonObject object = new JsonObject(); - object.addProperty("id", DocumentationIdProvider.getId(classInfo)); - object.addProperty("name", name.toLowerCase(Locale.ENGLISH)); - eventValues.add(object); + Class[] excludes = eventValueInfo.excludes(); + if (excludes != null && Set.of(excludes).contains(event)) { + continue; + } + + Class valueClass = eventValueInfo.valueClass(); + ClassInfo classInfo; + if (valueClass.isArray()) { + classInfo = Classes.getSuperClassInfo(valueClass.componentType()); + } else { + classInfo = Classes.getSuperClassInfo(valueClass); + } + + String name = classInfo.getName().getSingular(); + if (valueClass.isArray()) { + name = classInfo.getName().getPlural(); + } + if (name.isBlank()) { + continue; } + + if (eventValueInfo.time() == EventValues.TIME_PAST) { + name = "past " + name; + } else if (eventValueInfo.time() == EventValues.TIME_FUTURE) { + name = "future " + name; + } + + JsonObject object = new JsonObject(); + object.addProperty("id", DocumentationIdProvider.getId(classInfo)); + object.addProperty("name", name.toLowerCase(Locale.ENGLISH)); + eventValues.add(object); } } @@ -227,9 +303,9 @@ private static JsonArray getEventValues(SkriptEventInfo info) { * @param info the event to check * @return true if the event is cancellable, false otherwise */ - private static boolean isCancellable(SkriptEventInfo info) { + private static boolean isCancellable(BukkitSyntaxInfos.Event info) { boolean cancellable = false; - for (Class event : info.events) { + for (Class event : info.events()) { if (Cancellable.class.isAssignableFrom(event) || BlockCanBuildEvent.class.isAssignableFrom(event)) { cancellable = true; break; @@ -245,10 +321,10 @@ private static boolean isCancellable(SkriptEventInfo info) { * @param infos the structures to generate documentation for * @return a JsonArray containing the documentation JsonObjects for each structure */ - private static > JsonArray generateStructureElementArray(Iterator infos) { + private static > JsonArray generateStructureElementArray(Collection infos) { JsonArray syntaxArray = new JsonArray(); - infos.forEachRemaining(info -> { - if (info instanceof SkriptEventInfo eventInfo) { + infos.forEach(info -> { + if (info instanceof BukkitSyntaxInfos.Event eventInfo) { syntaxArray.add(generateEventElement(eventInfo)); } else { JsonObject structureElementJsonObject = generatedAnnotatedElement(info); @@ -265,9 +341,9 @@ private static > JsonArray generate * @param infos the syntax elements to generate documentation for * @return a JsonArray containing the documentation JsonObjects for each syntax element */ - private static > JsonArray generateSyntaxElementArray(Iterator infos) { + private static > JsonArray generateSyntaxElementArray(Collection infos) { JsonArray syntaxArray = new JsonArray(); - infos.forEachRemaining(info -> { + infos.forEach(info -> { JsonObject syntaxJsonObject = generatedAnnotatedElement(info); if (syntaxJsonObject != null) syntaxArray.add(syntaxJsonObject); @@ -325,7 +401,7 @@ private static JsonObject generateFunctionElement(JavaFunction function) { functionJsonObject.addProperty("id", DocumentationIdProvider.getId(function)); functionJsonObject.addProperty("name", function.getName()); functionJsonObject.addProperty("since", function.getSince()); - functionJsonObject.add("returnType", getReturnType(function)); + functionJsonObject.add("returns", getFunctionReturnType(function)); functionJsonObject.add("description", convertToJsonArray(function.getDescription())); functionJsonObject.add("examples", convertToJsonArray(function.getExamples())); @@ -341,7 +417,7 @@ private static JsonObject generateFunctionElement(JavaFunction function) { * @param function the JavaFunction to get the return type of * @return the JsonObject representing the return type of the JavaFunction */ - private static JsonObject getReturnType(JavaFunction function) { + private static JsonObject getFunctionReturnType(JavaFunction function) { JsonObject object = new JsonObject(); ClassInfo returnType = function.getReturnType(); @@ -351,6 +427,7 @@ private static JsonObject getReturnType(JavaFunction function) { object.addProperty("id", DocumentationIdProvider.getId(returnType)); object.addProperty("name", Objects.requireNonNullElse(returnType.getDocName(), returnType.getCodeName())); + return object; } @@ -383,40 +460,66 @@ private static JsonArray cleanPatterns(String... strings) { } /** - * Writes the documentation JsonObject to an output path + * Gets the json object representing the addon. * - * @param outputPath the path to write the documentation to - * @param jsonDocs the documentation JsonObject + * @return The json object representing the addon. */ - private void saveDocs(Path outputPath, JsonObject jsonDocs) { - try { - Files.writeString(outputPath, GSON.toJson(jsonDocs)); - } catch (IOException exception) { - //noinspection ThrowableNotThrown - Skript.exception(exception, "An error occurred while trying to generate JSON documentation"); + private JsonObject getSource() { + JsonObject object = new JsonObject(); + + object.addProperty("name", source.name()); + + Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(source.name()); + if (plugin == null) { + try { + plugin = JavaPlugin.getProvidingPlugin(source.source()); + } catch (Exception ignored) { } } + object.addProperty("version", plugin == null ? null : plugin.getDescription().getVersion()); + + return object; } - @Override - public void generate() { + /** + * Generates the json documentation for this addon at the specified path. + * + * @param path The output path. + */ + public void generate(@NotNull Path path) throws IOException { + Preconditions.checkNotNull(path, "path cannot be null"); + JsonObject jsonDocs = new JsonObject(); - jsonDocs.addProperty("skriptVersion", Skript.getVersion().toString()); jsonDocs.add("version", getVersion()); - jsonDocs.add("conditions", generateSyntaxElementArray(Skript.getConditions().iterator())); - jsonDocs.add("effects", generateSyntaxElementArray(Skript.getEffects().iterator())); - jsonDocs.add("expressions", generateSyntaxElementArray(Skript.getExpressions())); - jsonDocs.add("events", generateStructureElementArray(Skript.getEvents().iterator())); - jsonDocs.add("classes", generateClassInfoArray(Classes.getClassInfos().iterator())); - - Stream> structuresExcludingEvents = Skript.getStructures().stream() - .filter(structureInfo -> !(structureInfo instanceof SkriptEventInfo)); - jsonDocs.add("structures", generateStructureElementArray(structuresExcludingEvents.iterator())); - jsonDocs.add("sections", generateSyntaxElementArray(Skript.getSections().iterator())); - + jsonDocs.add("source", getSource()); + jsonDocs.add("conditions", generateSyntaxElementArray(source.syntaxRegistry().syntaxes(SyntaxRegistry.CONDITION))); + jsonDocs.add("effects", generateSyntaxElementArray(source.syntaxRegistry().syntaxes(SyntaxRegistry.EFFECT))); + jsonDocs.add("expressions", generateSyntaxElementArray(source.syntaxRegistry().syntaxes(SyntaxRegistry.EXPRESSION))); + jsonDocs.add("events", generateStructureElementArray(source.syntaxRegistry().syntaxes(BukkitRegistryKeys.EVENT))); + jsonDocs.add("structures", generateStructureElementArray(source.syntaxRegistry().syntaxes(SyntaxRegistry.STRUCTURE))); + jsonDocs.add("sections", generateSyntaxElementArray(source.syntaxRegistry().syntaxes(SyntaxRegistry.SECTION))); + jsonDocs.add("types", generateClassInfoArray(Classes.getClassInfos().iterator())); jsonDocs.add("functions", generateFunctionArray(Functions.getJavaFunctions().iterator())); - saveDocs(outputDir.toPath().resolve("docs.json"), jsonDocs); + try { + Files.writeString(path, GSON.toJson(jsonDocs)); + } catch (IOException ex) { + Skript.exception(ex, "An error occurred while trying to generate JSON documentation"); + throw new IOException(ex); + } + } + + /** + * @deprecated Use {@link #generate(Path)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") + @Override + public void generate() { + try { + generate(outputDir.toPath()); + } catch (IOException ex) { + Skript.exception(ex, "An error occurred while trying to generate JSON documentation"); + } } } diff --git a/src/main/java/ch/njol/skript/structures/StructEvent.java b/src/main/java/ch/njol/skript/structures/StructEvent.java index 5033a0be5fa..7f5576fda5b 100644 --- a/src/main/java/ch/njol/skript/structures/StructEvent.java +++ b/src/main/java/ch/njol/skript/structures/StructEvent.java @@ -1,7 +1,7 @@ package ch.njol.skript.structures; import ch.njol.skript.Skript; -import ch.njol.skript.doc.NoDoc; +import ch.njol.skript.doc.*; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptEvent; import ch.njol.skript.lang.SkriptEvent.ListeningBehavior; @@ -15,7 +15,45 @@ import java.util.Locale; -@NoDoc +@Name("Event") +@Description(""" + The structure used for listening to events. + + Optionally allows specifying whether to listen to events that have been cancelled, \ + and allows specifying with which priority to listen to events. \ + Events are called in the following order of priorities. + + ``` + lowest -> low -> normal -> high -> highest -> monitor + ``` + + Modifying event-values or cancelling events is not supported when using the 'monitor' priority. \ + It should only be used for monitoring the outcome of an event. + """) +@Example(""" + on load: + broadcast "loading!" + """) +@Example(""" + on join: + if {first-join::%player's uuid%} is not set: + set {first-join::%player's uuid%} to now + """) +@Example(""" + cancelled block break: + send "You can't break that here" to player + """) +@Example(""" + on join with priority lowest: + # called first + + on join: + # called second + + on join with priority highest: + # called last + """) +@Since({"1.0", "2.6 (per-event priority)", "2.9 (listening to cancellable events)"}) public class StructEvent extends Structure { static {