Skip to content

Commit

Permalink
Internal Events API (#5552)
Browse files Browse the repository at this point in the history
* Combine activity events and add unload event

* Add global script events

* Add EventRegister API

* Javadoc improvements

* Refactoring

* Cleanup and additional refactoring

* Add ScriptLoadEvent and ScriptInitEvent

* Add PreScriptInitEvent

This is an alternative for the now-deprecated Bukkit implementation

* Minor javadoc corrections

* Oopsie! add license headers

* Requested formatting changes

* Javadoc tweaks

* Fix script load triggering

* Remove license headers

* Update class and method names

* Formatting tweaks

* Refactor event classes into appropriate locations

The event classes have been refactored as inner classes of the classes they concern. This is to help accomdate future API changes (e.g. to ParserInstance, ScriptLoader, etc.) where the events may need to change.

* Add Event parent class for type restriction

---------

Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com>
  • Loading branch information
APickledWalrus and sovdeeth authored Oct 13, 2024
1 parent 322b81c commit 751c102
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 145 deletions.
123 changes: 121 additions & 2 deletions src/main/java/ch/njol/skript/ScriptLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.util.event.EventRegistry;
import org.skriptlang.skript.lang.script.Script;
import org.skriptlang.skript.lang.structure.Structure;

Expand All @@ -58,6 +59,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
Expand Down Expand Up @@ -488,6 +490,9 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
if (configs.isEmpty()) // Nothing to load
return CompletableFuture.completedFuture(new ScriptInfo());

eventRegistry().events(ScriptPreInitEvent.class)
.forEach(event -> event.onPreInit(configs));
//noinspection deprecation - we still need to call it
Bukkit.getPluginManager().callEvent(new PreScriptLoadEvent(configs));

ScriptInfo scriptInfo = new ScriptInfo();
Expand Down Expand Up @@ -605,6 +610,20 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
});
parser.setInactive();

// trigger events
scripts.forEach(loadingInfo -> {
Script script = loadingInfo.script;

parser.setActive(script);
parser.setNode(script.getConfig().getMainNode());

ScriptLoader.eventRegistry().events(ScriptLoadEvent.class)
.forEach(event -> event.onLoad(parser, script));
script.eventRegistry().events(ScriptLoadEvent.class)
.forEach(event -> event.onLoad(parser, script));
});
parser.setInactive();

return scriptInfo;
} catch (Exception e) {
// Something went wrong, we need to make sure the exception is printed
Expand Down Expand Up @@ -700,10 +719,13 @@ private static LoadingScriptInfo loadScript(Config config) {
assert file != null;
File disabledFile = new File(file.getParentFile(), DISABLED_SCRIPT_PREFIX + file.getName());
disabledScripts.remove(disabledFile);

// Add to loaded files to use for future reloads
loadedScripts.add(script);


ScriptLoader.eventRegistry().events(ScriptInitEvent.class)
.forEach(event -> event.onInit(script));

return null;
};
if (isAsync()) { // Need to delegate to main thread
Expand Down Expand Up @@ -848,6 +870,13 @@ public static ScriptInfo unloadScripts(Set<Script> scripts) {
// initial unload stage
for (Script script : scripts) {
parser.setActive(script);

// trigger unload event before beginning
eventRegistry().events(ScriptUnloadEvent.class)
.forEach(event -> event.onUnload(parser, script));
script.eventRegistry().events(ScriptUnloadEvent.class)
.forEach(event -> event.onUnload(parser, script));

for (Structure structure : script.getStructures())
structure.unload();
}
Expand Down Expand Up @@ -1043,6 +1072,96 @@ public static FileFilter getDisabledScriptsFilter() {
return disabledScriptFilter;
}

/*
* Global Script Event API
*/

// ScriptLoader Events

/**
* Used for listening to events involving a ScriptLoader.
* @see #eventRegistry()
*/
public interface LoaderEvent extends org.skriptlang.skript.util.event.Event { }

/**
* Called when {@link ScriptLoader} is preparing to load {@link Config}s into {@link Script}s.
* @see #loadScripts(File, OpenCloseable)
* @see #loadScripts(Set, OpenCloseable)
*/
@FunctionalInterface
public interface ScriptPreInitEvent extends LoaderEvent {

/**
* The method that is called when this event triggers.
* Modifications to the given collection will affect what is loaded.
* @param configs The Configs to be loaded.
*/
void onPreInit(Collection<Config> configs);

}

/**
* Called when a {@link Script} is created and preloaded in the {@link ScriptLoader}.
* The initializing script may contain {@link Structure}s that are not fully loaded.
* @see #loadScripts(File, OpenCloseable)
* @see #loadScripts(Set, OpenCloseable)
*/
@FunctionalInterface
public interface ScriptInitEvent extends LoaderEvent {

/**
* The method that is called when this event triggers.
* @param script The Script being initialized.
*/
void onInit(Script script);

}

/**
* Called when a {@link Script} is loaded in the {@link ScriptLoader}.
* This event will trigger <b>after</b> the script is completely loaded ({@link Structure} initialization finished).
* @see #loadScripts(File, OpenCloseable)
* @see #loadScripts(Set, OpenCloseable)
*/
@FunctionalInterface
public interface ScriptLoadEvent extends LoaderEvent, Script.Event {

/**
* The method that is called when this event triggers.
* @param parser The ParserInstance handling the loading of <code>script</code>.
* @param script The Script being loaded.
*/
void onLoad(ParserInstance parser, Script script);

}

/**
* Called when a {@link Script} is unloaded in the {@link ScriptLoader}.
* This event will trigger <b>before</b> the script is unloaded.
* @see #unloadScript(Script)
*/
@FunctionalInterface
public interface ScriptUnloadEvent extends LoaderEvent, Script.Event {

/**
* The method that is called when this event triggers.
* @param parser The ParserInstance handling the unloading of <code>script</code>.
* @param script The Script being unloaded.
*/
void onUnload(ParserInstance parser, Script script);

}

private static final EventRegistry<LoaderEvent> eventRegistry = new EventRegistry<>();

/**
* @return An EventRegistry for the ScriptLoader's events.
*/
public static EventRegistry<LoaderEvent> eventRegistry() {
return eventRegistry;
}

/*
* Deprecated stuff
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@

import ch.njol.skript.config.Config;
import ch.njol.util.Validate;
import ch.njol.skript.ScriptLoader;

/**
* This event has no guarantee of being on the main thread.
* Please do not use bukkit api before checking {@link Bukkit#isPrimaryThread()}
* @deprecated Use {@link ScriptLoader.ScriptPreInitEvent}.
*/

@Deprecated
public class PreScriptLoadEvent extends Event {

private final List<Config> scripts;
Expand Down
44 changes: 36 additions & 8 deletions src/main/java/ch/njol/skript/lang/parser/ParserInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.skriptlang.skript.lang.experiment.ExperimentSet;
import org.skriptlang.skript.lang.experiment.Experimented;
import org.skriptlang.skript.lang.script.Script;
import org.skriptlang.skript.lang.script.ScriptEvent;
import org.skriptlang.skript.lang.structure.Structure;

import java.io.File;
Expand Down Expand Up @@ -130,12 +129,18 @@ private void setCurrentScript(@Nullable Script currentScript) {
);

// "Script" events
if (previous != null)
previous.getEvents(ScriptEvent.ScriptInactiveEvent.class)
.forEach(eventHandler -> eventHandler.onInactive(currentScript));
if (currentScript != null)
currentScript.getEvents(ScriptEvent.ScriptActiveEvent.class)
.forEach(eventHandler -> eventHandler.onActive(previous));
if (previous != null) { // 'previous' is becoming inactive
ScriptLoader.eventRegistry().events(ScriptActivityChangeEvent.class)
.forEach(event -> event.onActivityChange(this, previous, false, currentScript));
previous.eventRegistry().events(ScriptActivityChangeEvent.class)
.forEach(event -> event.onActivityChange(this, previous, false, currentScript));
}
if (currentScript != null) { // 'currentScript' is becoming active
ScriptLoader.eventRegistry().events(ScriptActivityChangeEvent.class)
.forEach(event -> event.onActivityChange(this, currentScript, true, previous));
currentScript.eventRegistry().events(ScriptActivityChangeEvent.class)
.forEach(event -> event.onActivityChange(this, currentScript, true, previous));
}
}

/**
Expand Down Expand Up @@ -483,7 +488,7 @@ protected final ParserInstance getParser() {
}

/**
* @deprecated See {@link ScriptEvent}.
* @deprecated See {@link ScriptLoader.LoaderEvent}.
*/
@Deprecated
public void onCurrentScriptChange(@Nullable Config currentScript) { }
Expand Down Expand Up @@ -540,6 +545,29 @@ private List<? extends Data> getDataInstances() {
return dataList;
}

/**
* Called when a {@link Script} is made active or inactive in a {@link ParserInstance}.
* This event will trigger <b>after</b> the change in activity has occurred.
* @see #isActive()
*/
@FunctionalInterface
public interface ScriptActivityChangeEvent extends ScriptLoader.LoaderEvent, Script.Event {

/**
* The method that is called when this event triggers.
* @param parser The ParserInstance where the activity change occurred.
* @param script The Script this event was registered for.
* @param active Whether <code>script</code> became active or inactive within <code>parser</code>.
* @param other The Script that was made active or inactive.
* Whether it was made active or inactive is the negation of the <code>active</code>.
* That is to say, if <code>script</code> became active, then <code>other</code> became inactive.
* Null if <code>parser</code> was inactive (meaning no script became inactive)
* or became inactive (meaning no script became active).
*/
void onActivityChange(ParserInstance parser, Script script, boolean active, @Nullable Script other);

}

// Backup API

/**
Expand Down
78 changes: 8 additions & 70 deletions src/main/java/org/skriptlang/skript/lang/script/Script.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
/**
* 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 org.skriptlang.skript.lang.script;

import ch.njol.skript.config.Config;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Unmodifiable;
import org.skriptlang.skript.util.event.EventRegistry;
import org.skriptlang.skript.lang.structure.Structure;

import java.util.Collections;
Expand All @@ -31,7 +14,6 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
* Scripts are the primary container of all code.
Expand Down Expand Up @@ -160,63 +142,19 @@ public <Value extends ScriptData> Value getData(Class<? extends Value> dataType,

// Script Events

private final Set<ScriptEvent> eventHandlers = new HashSet<>(5);

/**
* <b>This API is experimental and subject to change.</b>
* Adds the provided event to this Script.
* @param event The event to add.
*/
@ApiStatus.Experimental
public void registerEvent(ScriptEvent event) {
eventHandlers.add(event);
}

/**
* <b>This API is experimental and subject to change.</b>
* Adds the provided event to this Script.
* @param eventType The type of event being added. This is useful for registering the event through lambdas.
* @param event The event to add.
*/
@ApiStatus.Experimental
public <T extends ScriptEvent> void registerEvent(Class<T> eventType, T event) {
eventHandlers.add(event);
}

/**
* <b>This API is experimental and subject to change.</b>
* Removes the provided event from this Script.
* @param event The event to remove.
* Used for listening to events involving a Script.
* @see #eventRegistry()
*/
@ApiStatus.Experimental
public void unregisterEvent(ScriptEvent event) {
eventHandlers.remove(event);
}
public interface Event extends org.skriptlang.skript.util.event.Event { }

/**
* <b>This API is experimental and subject to change.</b>
* @return An unmodifiable set of all events.
*/
@ApiStatus.Experimental
@Unmodifiable
public Set<ScriptEvent> getEvents() {
return Collections.unmodifiableSet(eventHandlers);
}
private final EventRegistry<Event> eventRegistry = new EventRegistry<>();

/**
* <b>This API is experimental and subject to change.</b>
* @param type The type of events to get.
* @return An unmodifiable set of all events of the specified type.
* @return An EventRegistry for this Script's events.
*/
@ApiStatus.Experimental
@Unmodifiable
@SuppressWarnings("unchecked")
public <T extends ScriptEvent> Set<T> getEvents(Class<T> type) {
return Collections.unmodifiableSet(
(Set<T>) eventHandlers.stream()
.filter(event -> type.isAssignableFrom(event.getClass()))
.collect(Collectors.toSet())
);
public EventRegistry<Event> eventRegistry() {
return eventRegistry;
}

}
Loading

0 comments on commit 751c102

Please sign in to comment.