Skip to content

Commit 1ec6afe

Browse files
Merge branch 'dev/patch' into dev/feature
2 parents df7c044 + 702e348 commit 1ec6afe

8 files changed

Lines changed: 108 additions & 63 deletions

File tree

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
44
import java.time.LocalTime
55

66
plugins {
7-
id 'com.gradleup.shadow' version '9.4.2'
7+
id 'com.gradleup.shadow' version '9.4.3'
88
id 'maven-publish'
99
id 'java'
1010
id 'checkstyle'

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ org.gradle.parallel=true
55

66
groupid=ch.njol
77
name=skript
8-
version=2.15.3
8+
version=2.15.4
99
jarName=Skript.jar
1010
testEnv=java25/paper-26.1.2
1111
testEnvJavaVersion=25

src/main/java/ch/njol/skript/ScriptLoader.java

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import ch.njol.skript.config.Node;
55
import ch.njol.skript.config.SectionNode;
66
import ch.njol.skript.config.SimpleNode;
7-
import ch.njol.skript.events.bukkit.PreScriptLoadEvent;
87
import ch.njol.skript.lang.*;
98
import ch.njol.skript.lang.parser.ParserInstance;
109
import ch.njol.skript.log.CountingLogHandler;
@@ -21,6 +20,7 @@
2120
import ch.njol.util.StringUtils;
2221
import org.bukkit.Bukkit;
2322
import org.jetbrains.annotations.ApiStatus;
23+
import org.jetbrains.annotations.NotNull;
2424
import org.jetbrains.annotations.Nullable;
2525
import org.jetbrains.annotations.UnknownNullability;
2626
import org.skriptlang.skript.bukkit.text.TextComponentParser;
@@ -111,7 +111,7 @@ private static ParserInstance getParser() {
111111
* All loaded scripts.
112112
*/
113113
@SuppressWarnings("null")
114-
private static final Set<Script> loadedScripts = Collections.synchronizedSortedSet(new TreeSet<>(new Comparator<Script>() {
114+
private static final Set<Script> loadedScripts = Collections.synchronizedSortedSet(new TreeSet<>(new Comparator<>() {
115115
@Override
116116
public int compare(Script s1, Script s2) {
117117
File f1 = s1.getConfig().getFile();
@@ -157,6 +157,13 @@ private boolean isSubDir(File directory, File subDir) {
157157
public static Script getScript(File file) {
158158
if (!file.isFile())
159159
throw new IllegalArgumentException("Something other than a file was provided.");
160+
try {
161+
file = file.getCanonicalFile();
162+
} catch (IOException e) {
163+
//noinspection ThrowableNotThrown
164+
Skript.exception(e, "An exception occurred while trying to get the canonical file of: " + file);
165+
return null;
166+
}
160167
for (Script script : loadedScripts) {
161168
if (file.equals(script.getConfig().getFile()))
162169
return script;
@@ -284,7 +291,6 @@ public static boolean isParallel() {
284291

285292
/**
286293
* Returns the executor used for submitting tasks based on the user config.sk settings.
287-
*
288294
* The thread count will be based on the value of {@link #asyncLoaderSize}.
289295
* <p>
290296
* You may also use class {@link ch.njol.skript.util.Task} and the appropriate constructor
@@ -319,7 +325,7 @@ public static void setAsyncLoaderSize(int size) throws IllegalStateException {
319325

320326
// Remove threads
321327
while (loaderThreads.size() > size) {
322-
AsyncLoaderThread thread = loaderThreads.remove(loaderThreads.size() - 1);
328+
AsyncLoaderThread thread = loaderThreads.removeLast();
323329
thread.cancelExecution();
324330
}
325331
// Add threads
@@ -334,7 +340,7 @@ public static void setAsyncLoaderSize(int size) throws IllegalStateException {
334340
private final AtomicInteger threadId = new AtomicInteger(0);
335341

336342
@Override
337-
public Thread newThread(Runnable runnable) {
343+
public Thread newThread(@NotNull Runnable runnable) {
338344
Thread thread = new Thread(asyncLoaderThreadGroup, runnable, "Skript async loaders thread " + threadId.incrementAndGet());
339345
thread.setDaemon(true);
340346
return thread;
@@ -447,6 +453,26 @@ private static <T> CompletableFuture<T> makeFuture(Supplier<T> supplier, OpenClo
447453
* Script Loading Methods
448454
*/
449455

456+
private static final Set<File> trackedFiles = new HashSet<>();
457+
458+
/**
459+
* Tracks that a file should be considered as loading.
460+
* @param file The file to track.
461+
* @return Whether this file was already being tracked.
462+
*/
463+
private static boolean track(File file) {
464+
return !trackedFiles.add(file);
465+
}
466+
467+
/**
468+
* Stops tracking that a script (its file) should be considered as loading.
469+
* @param script The script to stop tracking.
470+
*/
471+
private static void untrack(Script script) {
472+
// Loaded scripts are created with canonical files
473+
trackedFiles.remove(script.getConfig().getFile());
474+
}
475+
450476
/**
451477
* Loads the Script present at the file using {@link #loadScripts(List, OpenCloseable)},
452478
* sending info/error messages when done.
@@ -483,15 +509,14 @@ public static CompletableFuture<ScriptInfo> loadScripts(Set<File> files, OpenClo
483509
* and closed after the {@link Structure#postLoad()} stage.
484510
* @return Info on the loaded scripts.
485511
*/
486-
@SuppressWarnings("removal")
487512
private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, OpenCloseable openCloseable) {
488513
if (configs.isEmpty()) // Nothing to load
489514
return CompletableFuture.completedFuture(new ScriptInfo());
490515

491516
eventRegistry().events(ScriptPreInitEvent.class)
492517
.forEach(event -> event.onPreInit(configs));
493-
//noinspection deprecation - we still need to call it
494-
Bukkit.getPluginManager().callEvent(new PreScriptLoadEvent(configs));
518+
//noinspection removal - we still need to call it
519+
Bukkit.getPluginManager().callEvent(new ch.njol.skript.events.bukkit.PreScriptLoadEvent(configs));
495520

496521
ScriptInfo scriptInfo = new ScriptInfo();
497522

@@ -513,7 +538,7 @@ private static CompletableFuture<ScriptInfo> loadScripts(List<Config> configs, O
513538
}
514539

515540
return CompletableFuture.allOf(scriptInfoFutures.toArray(new CompletableFuture[0]))
516-
.thenApply(unused -> {
541+
.thenApply(ignored -> {
517542
// TODO in the future this won't work when parallel loading is fixed
518543
// It does now though so let's avoid calling getParser() a bunch.
519544
ParserInstance parser = getParser();
@@ -630,28 +655,18 @@ record LoadingStructure (LoadingScriptInfo loadingScriptInfo, Structure structur
630655
} finally {
631656
parser.setInactive();
632657

658+
for (LoadingScriptInfo info : scripts) {
659+
untrack(info.script);
660+
}
661+
633662
openCloseable.close();
634663
}
635664
}).exceptionally(t -> {
636665
throw Skript.exception(t);
637666
});
638667
}
639668

640-
private static class LoadingScriptInfo {
641-
642-
public final Script script;
643-
644-
public final List<Structure> structures;
645-
646-
public final Map<Structure, Node> nodeMap;
647-
648-
public LoadingScriptInfo(Script script, List<Structure> structures, Map<Structure, Node> nodeMap) {
649-
this.script = script;
650-
this.structures = structures;
651-
this.nodeMap = nodeMap;
652-
}
653-
654-
}
669+
private record LoadingScriptInfo(Script script, List<Structure> structures, Map<Structure, Node> nodeMap) { }
655670

656671
/**
657672
* Creates a script and loads the provided config into it.
@@ -810,6 +825,8 @@ private static Config loadStructure(File file) {
810825
return null;
811826
}
812827

828+
track(file);
829+
813830
try {
814831
String name = Skript.getInstance().getDataFolder().toPath().toAbsolutePath()
815832
.resolve(Skript.SCRIPTSFOLDER).relativize(file.toPath().toAbsolutePath()).toString();
@@ -857,6 +874,10 @@ private static Config loadStructure(InputStream source, String name) {
857874
* This data is calculated by using {@link ScriptInfo#add(ScriptInfo)}.
858875
*/
859876
public static ScriptInfo unloadScripts(Set<Script> scripts) {
877+
scripts = scripts.stream()
878+
.filter(script -> !track(script.getConfig().getFile()))
879+
.collect(Collectors.toSet());
880+
860881
// ensure unloaded scripts are not being unloaded
861882
for (Script script : scripts) {
862883
if (!loadedScripts.contains(script))
@@ -920,6 +941,8 @@ record UnloadingStructure (Script script, Structure structure) {}
920941
File scriptFile = script.getConfig().getFile();
921942
assert scriptFile != null;
922943
disabledScripts.add(new File(scriptFile.getParentFile(), DISABLED_SCRIPT_PREFIX + scriptFile.getName()));
944+
945+
untrack(script);
923946
}
924947

925948
return info;
@@ -1041,6 +1064,7 @@ public static ArrayList<TriggerItem> loadItems(SectionNode node) {
10411064
items.add(item);
10421065
} else if (subNode instanceof SectionNode subSection) {
10431066

1067+
//noinspection resource - manual management is intentional
10441068
RetainingLogHandler handler = SkriptLogger.startRetainingLog();
10451069
find_section:
10461070
try {
@@ -1177,15 +1201,15 @@ public static Script createDummyScript(String name, @Nullable File file) {
11771201
* Any changes to loaded scripts will not be reflected in the returned set.
11781202
*/
11791203
public static Set<Script> getLoadedScripts() {
1180-
return Collections.unmodifiableSet(new HashSet<>(loadedScripts));
1204+
return Set.copyOf(loadedScripts);
11811205
}
11821206

11831207
/**
11841208
* @return An unmodifiable set containing a snapshot of the currently disabled scripts.
11851209
* Any changes to disabled scripts will not be reflected in the returned set.
11861210
*/
11871211
public static Set<File> getDisabledScripts() {
1188-
return Collections.unmodifiableSet(new HashSet<>(disabledScripts));
1212+
return Set.copyOf(disabledScripts);
11891213
}
11901214

11911215
/**
@@ -1316,7 +1340,7 @@ public static File getScriptFromName(String script, File directory) {
13161340
script = script.replace('/', File.separatorChar).replace('\\', File.separatorChar);
13171341
} else if (!StringUtils.endsWithIgnoreCase(script, ".sk")) {
13181342
int dot = script.lastIndexOf('.');
1319-
if (dot > 0 && !script.substring(dot + 1).equals(""))
1343+
if (dot > 0 && !script.substring(dot + 1).isEmpty())
13201344
return null;
13211345
script = script + ".sk";
13221346
}

src/main/java/ch/njol/skript/conditions/CondScriptLoaded.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ public boolean check(Event event) {
6262
return ScriptLoader.getLoadedScripts().contains(currentScript) ^ isNegated();
6363
return scripts.check(event, scriptName -> {
6464
File scriptFile = ScriptLoader.getScriptFromName(scriptName);
65-
return scriptFile != null && ScriptLoader.getLoadedScripts().contains(ScriptLoader.getScript(scriptFile));
65+
if (scriptFile == null) {
66+
return false;
67+
}
68+
Script script = ScriptLoader.getScript(scriptFile);
69+
return script != null && ScriptLoader.getLoadedScripts().contains(script);
6670
}, isNegated());
6771
}
6872

src/main/java/ch/njol/skript/effects/EffScriptFile.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ch.njol.skript.effects;
22

33
import ch.njol.skript.ScriptLoader;
4+
import ch.njol.skript.ScriptLoader.ScriptInfo;
45
import ch.njol.skript.Skript;
56
import ch.njol.skript.SkriptCommand;
67
import ch.njol.skript.command.ScriptCommand;
@@ -123,7 +124,8 @@ private void handle(@Nullable File scriptFile, @Nullable String name, OpenClosea
123124
FileFilter filter = ScriptLoader.getDisabledScriptsFilter();
124125
switch (mark) {
125126
case ENABLE:
126-
if (ScriptLoader.getLoadedScripts().contains(ScriptLoader.getScript(scriptFile)))
127+
Script script = ScriptLoader.getScript(scriptFile);
128+
if (script != null && ScriptLoader.getLoadedScripts().contains(script))
127129
return;
128130
if (filter.accept(scriptFile)) {
129131
try {
@@ -148,7 +150,9 @@ private void handle(@Nullable File scriptFile, @Nullable String name, OpenClosea
148150
if (filter.accept(scriptFile))
149151
return;
150152

151-
this.unloadScripts(scriptFile);
153+
if (!this.unloadScripts(scriptFile)) {
154+
return;
155+
}
152156

153157
ScriptLoader.loadScripts(scriptFile, openCloseable);
154158
break;
@@ -164,7 +168,9 @@ private void handle(@Nullable File scriptFile, @Nullable String name, OpenClosea
164168
if (filter.accept(scriptFile))
165169
return;
166170

167-
this.unloadScripts(scriptFile);
171+
if (!this.unloadScripts(scriptFile)) {
172+
return;
173+
}
168174

169175
try {
170176
FileUtils.move(
@@ -183,21 +189,25 @@ private void handle(@Nullable File scriptFile, @Nullable String name, OpenClosea
183189
}
184190
}
185191

186-
private void unloadScripts(File file) {
187-
Set<Script> loaded = ScriptLoader.getLoadedScripts();
192+
private boolean unloadScripts(File file) {
188193
if (file.isDirectory()) {
189-
Set<Script> scripts = ScriptLoader.getScripts(file);
190-
if (scripts.isEmpty())
191-
return;
192-
scripts.retainAll(loaded); // skip any that are not loaded (avoid throwing error)
193-
ScriptLoader.unloadScripts(scripts);
194-
} else {
195-
Script script = ScriptLoader.getScript(file);
196-
if (!loaded.contains(script))
197-
return; // don't need to unload if not loaded (avoid throwing error)
198-
if (script != null)
199-
ScriptLoader.unloadScript(script);
194+
ScriptInfo info = ScriptLoader.unloadScripts(ScriptLoader.getScripts(file));
195+
if (info.files == 0) {
196+
error("Failed to unload one or more scripts within '" + file.getName() + "'. Were they already loading or unloading?");
197+
return false;
198+
}
199+
return true;
200+
}
201+
Script script = ScriptLoader.getScript(file);
202+
if (script == null) {
203+
return true; // we want the file to still be marked disabled
200204
}
205+
ScriptInfo info = ScriptLoader.unloadScript(script);
206+
if (info.files == 0) {
207+
error("Failed to unload the script '" + script.nameAndPath() + "'. Was it already loading or unloading?");
208+
return false;
209+
}
210+
return true;
201211
}
202212

203213
@Override

src/main/java/org/skriptlang/skript/bukkit/entity/player/elements/effects/EffBan.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
7777

7878
@Override
7979
protected void execute(Event event) {
80-
Component reason = this.reason == null ? null : this.reason.getSingle(event); // don't check for null, just ignore an invalid reason
80+
Component reason = this.reason == null ? null : this.reason.getSingle(event);
8181
Timespan duration = this.expires == null ? null : this.expires.getSingle(event);
8282
Date expires = duration == null ? null : new Date(System.currentTimeMillis() + duration.getAs(TimePeriod.MILLISECOND));
8383
for (Object object : players.getArray(event)) {
@@ -91,16 +91,14 @@ protected void execute(Event event) {
9191
String ip = address.getAddress().getHostAddress();
9292
var banList = Bukkit.getBanList(Type.IP);
9393
if (ban) {
94-
String legacyReason = TextComponentParser.instance().toLegacyString(reason);
95-
banList.addBan(ip, legacyReason, expires, SKRIPT_BAN_SOURCE);
94+
banList.addBan(ip, toLegacyString(reason), expires, SKRIPT_BAN_SOURCE);
9695
} else {
9796
banList.pardon(ip);
9897
}
9998
} else {
10099
var banList = Bukkit.getBanList(Type.NAME);
101100
if (ban) {
102-
String legacyReason = TextComponentParser.instance().toLegacyString(reason);
103-
banList.addBan(player.getName(), legacyReason, expires, SKRIPT_BAN_SOURCE); // FIXME [UUID] ban UUID
101+
banList.addBan(player.getName(), toLegacyString(reason), expires, SKRIPT_BAN_SOURCE); // FIXME [UUID] ban UUID
104102
} else {
105103
banList.pardon(player.getName());
106104
}
@@ -116,8 +114,7 @@ protected void execute(Event event) {
116114
}
117115
var banList = Bukkit.getBanList(Type.NAME);
118116
if (ban) {
119-
String legacyReason = TextComponentParser.instance().toLegacyString(reason);
120-
banList.addBan(name, legacyReason, expires, SKRIPT_BAN_SOURCE);
117+
banList.addBan(name, toLegacyString(reason), expires, SKRIPT_BAN_SOURCE);
121118
} else {
122119
banList.pardon(name);
123120
}
@@ -126,7 +123,7 @@ protected void execute(Event event) {
126123
var ipBanList = Bukkit.getBanList(Type.IP);
127124
var nameBanList = Bukkit.getBanList(Type.NAME);
128125
if (ban) {
129-
String legacyReason = TextComponentParser.instance().toLegacyString(reason);
126+
String legacyReason = toLegacyString(reason);
130127
ipBanList.addBan(ip, legacyReason, expires, SKRIPT_BAN_SOURCE);
131128
nameBanList.addBan(ip, legacyReason, expires, SKRIPT_BAN_SOURCE);
132129
} else {
@@ -151,4 +148,11 @@ public String toString(@Nullable Event event, boolean debug) {
151148
.toString();
152149
}
153150

151+
private static @Nullable String toLegacyString(@Nullable Component component) {
152+
if (component == null) {
153+
return null;
154+
}
155+
return TextComponentParser.instance().toLegacyString(component);
156+
}
157+
154158
}

0 commit comments

Comments
 (0)