-
-
Notifications
You must be signed in to change notification settings - Fork 438
Expand file tree
/
Copy pathSkriptConfig.java
More file actions
467 lines (399 loc) · 18.6 KB
/
SkriptConfig.java
File metadata and controls
467 lines (399 loc) · 18.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
package ch.njol.skript;
import ch.njol.skript.classes.EnumParser;
import ch.njol.skript.config.Config;
import ch.njol.skript.config.Option;
import ch.njol.skript.config.OptionSection;
import ch.njol.skript.hooks.Hook;
import ch.njol.skript.hooks.VaultHook;
import ch.njol.skript.hooks.regions.GriefPreventionHook;
import ch.njol.skript.hooks.regions.PreciousStonesHook;
import ch.njol.skript.hooks.regions.ResidenceHook;
import ch.njol.skript.hooks.regions.WorldGuardHook;
import ch.njol.skript.lang.function.Function;
import ch.njol.skript.localization.Language;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.log.Verbosity;
import ch.njol.skript.timings.SkriptTimings;
import ch.njol.skript.update.ReleaseChannel;
import ch.njol.skript.util.FileUtils;
import ch.njol.skript.util.Timespan;
import ch.njol.skript.util.Version;
import ch.njol.skript.variables.FlatFileStorage;
import ch.njol.skript.variables.Variables;
import co.aikar.timings.Timings;
import org.bukkit.event.EventPriority;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.bukkit.text.TextComponentParser;
import org.skriptlang.skript.bukkit.text.TextComponentParser.LinkParseMode;
import org.skriptlang.skript.util.event.EventRegistry;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Important: don't save values from the config, a '/skript reload config/configs/all' won't work correctly otherwise!
*
* @author Peter Güttinger
*/
@SuppressWarnings("unused")
public class SkriptConfig {
//<editor-fold desc="SkriptConfig events">
/**
* Used for listening to events involving Skript's configuration.
* @see #eventRegistry()
*/
public interface Event extends org.skriptlang.skript.util.event.Event { }
/**
* Called when Skript's configuration is successfully reloaded.
* This occurs when the reload process has finished, meaning the config is safe to reference.
*/
@FunctionalInterface
public interface ReloadEvent extends Event {
/**
* The method that is called when this event triggers.
*/
void onReload();
}
private static final EventRegistry<Event> eventRegistry = new EventRegistry<>();
/**
* @return An event registry for the configuration's events.
*/
public static EventRegistry<Event> eventRegistry() {
return eventRegistry;
}
//</editor-fold>
@Nullable
static Config mainConfig;
static Collection<Config> configs = new ArrayList<>();
static final Option<String> version = new Option<>("version", Skript.getVersion().toString())
.optional(true);
public static final Option<String> language = new Option<>("language", "english")
.optional(true)
.setter(s -> {
if (!Language.load(s)) {
Skript.error("No language file found for '" + s + "'!");
}
});
public static final Option<Boolean> checkForNewVersion = new Option<>("check for new version", false)
.setter(t -> {
SkriptUpdater updater = Skript.getInstance().getUpdater();
if (updater != null)
updater.setEnabled(t);
});
public static final Option<Timespan> updateCheckInterval = new Option<>("update check interval", new Timespan(12 * 60 * 60 * 1000))
.setter(t -> {
SkriptUpdater updater = Skript.getInstance().getUpdater();
if (updater != null)
updater.setCheckFrequency(t.getAs(Timespan.TimePeriod.TICK));
});
static final Option<Integer> updaterDownloadTries = new Option<>("updater download tries", 7)
.optional(true);
public static final Option<String> releaseChannel = new Option<>("release channel", "none")
.setter(t -> {
ReleaseChannel channel;
switch (t) {
case "alpha":
case "beta":
Skript.warning("'alpha' and 'beta' are no longer valid release channels. Use 'prerelease' instead.");
case "prerelease": // All development builds are valid
channel = new ReleaseChannel((name) -> true, t);
break;
case "stable":
// TODO a better option would be to check that it is not a pre-release through GH API
channel = new ReleaseChannel((name) -> !(name.contains("-")), t);
break;
case "none":
channel = new ReleaseChannel((name) -> false, t);
break;
default:
channel = new ReleaseChannel((name) -> false, t);
Skript.error("Unknown release channel '" + t + "'.");
break;
}
SkriptUpdater updater = Skript.getInstance().getUpdater();
if (updater != null) {
updater.setReleaseChannel(channel);
}
});
public static final Option<Boolean> enableEffectCommands = new Option<>("enable effect commands", false);
public static final Option<String> effectCommandToken = new Option<>("effect command token", "!");
public static final Option<Boolean> allowOpsToUseEffectCommands = new Option<>("allow ops to use effect commands", false);
public static final Option<Boolean> logEffectCommands = new Option<>("log effect commands", false);
public static final Option<Boolean> compressBackups = new Option<>("compress backups", false);
// everything handled by Variables
public static final OptionSection databases = new OptionSection("databases");
public static final Option<Boolean> usePlayerUUIDsInVariableNames = new Option<>("use player UUIDs in variable names", false); // TODO change to true later (as well as in the default config)
public static final Option<Boolean> enablePlayerVariableFix = new Option<>("player variable fix", true);
@SuppressWarnings("null")
private static final DateFormat shortDateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
public static final Option<DateFormat> dateFormat = new Option<>("date format", shortDateFormat, s -> {
try {
if (s.equalsIgnoreCase("default"))
return null;
return new SimpleDateFormat(s);
} catch (final IllegalArgumentException e) {
Skript.error("'" + s + "' is not a valid date format. Please refer to https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html for instructions on the format.");
}
return null;
});
public static String formatDate(final long timestamp) {
final DateFormat f = dateFormat.value();
synchronized (f) {
return "" + f.format(timestamp);
}
}
public static final Option<Verbosity> verbosity = new Option<>("verbosity", Verbosity.NORMAL, new EnumParser<>(Verbosity.class, "verbosity"))
.setter(SkriptLogger::setVerbosity);
public static final Option<EventPriority> defaultEventPriority = new Option<>("plugin priority", EventPriority.NORMAL, s -> {
try {
return EventPriority.valueOf(s.toUpperCase(Locale.ENGLISH));
} catch (final IllegalArgumentException e) {
Skript.error("The plugin priority has to be one of lowest, low, normal, high, or highest.");
return null;
}
});
/**
* Determines whether `on <event>` will be triggered by cancelled events or not.
*/
public static final Option<Boolean> listenCancelledByDefault = new Option<>("listen to cancelled events by default", false)
.optional(true);
/**
* Maximum number of digits to display after the period for floats and doubles
*/
public static final Option<Integer> numberAccuracy = new Option<>("number accuracy", 2);
public static final Option<Integer> maxTargetBlockDistance = new Option<>("maximum target block distance", 100);
public static final Option<Boolean> caseSensitive = new Option<>("case sensitive", false);
public static final Option<Boolean> allowFunctionsBeforeDefs = new Option<>("allow function calls before definations", false)
.optional(true);
public static final Option<Boolean> disableObjectCannotBeSavedWarnings = new Option<>("disable variable will not be saved warnings", false);
public static final Option<Boolean> disableMissingAndOrWarnings = new Option<>("disable variable missing and/or warnings", false);
public static final Option<Boolean> disableVariableStartingWithExpressionWarnings =
new Option<>("disable starting a variable's name with an expression warnings", false);
public static final Option<Boolean> disableColonInVariableWarnings =
new Option<>("disable single colon in variable name warnings", false);
public static final Option<Boolean> disableUnreachableCodeWarnings = new Option<>("disable unreachable code warnings", false);
@Deprecated(since = "2.3.0", forRemoval = true)
public static final Option<Boolean> enableScriptCaching = new Option<>("enable script caching", false)
.optional(true);
public static final Option<Boolean> keepConfigsLoaded = new Option<>("keep configs loaded", false)
.optional(true);
public static final Option<Boolean> addonSafetyChecks = new Option<>("addon safety checks", false)
.optional(true);
public static final Option<Boolean> apiSoftExceptions = new Option<>("soft api exceptions", false);
@SuppressWarnings("removal")
public static final Option<Boolean> enableTimings = new Option<>("enable timings", false)
.setter(t -> {
if (!Skript.classExists("co.aikar.timings.Timings")) { // Check for Timings
if (t) // Warn the server admin that timings won't work
Skript.warning("Timings cannot be enabled! You are running Bukkit/Spigot, but Paper is required.");
SkriptTimings.setEnabled(false); // Just to be sure, deactivate timings support completely
return;
}
if (Timings.class.isAnnotationPresent(Deprecated.class)) { // check for deprecated Timings
if (t) // Warn the server admin that timings won't work
Skript.warning("Timings cannot be enabled! Paper no longer supports Timings as of 1.19.4.");
SkriptTimings.setEnabled(false); // Just to be sure, deactivate timings support completely
return;
}
// If we get here, we can safely enable timings
if (t)
Skript.info("Timings support enabled!");
SkriptTimings.setEnabled(t); // Config option will be used
});
public static final Option<Boolean> caseInsensitiveVariables = new Option<>("case-insensitive variables", true)
.setter(t -> Variables.caseInsensitiveVariables = t);
public static final Option<Boolean> caseInsensitiveCommands = new Option<>("case-insensitive commands", false)
.optional(true);
public static final Option<String[]> safeTags = new Option<>("safe tags",
new String[]{"color", "decorations", "gradient", "rainbow", "reset", "transition", "pride", "shadowColor"},
raw -> raw.split(", "))
.setter(tags -> {
try {
TextComponentParser.instance().setSafeTags(tags);
} catch (Error e) {
// Ignore it, we're on unsupported server platform and class loading failed
}
});
public static final Option<String> parseLinks = new Option<>("parse links in chat messages", "disabled")
.setter(mode -> {
try {
TextComponentParser.instance().linkParseMode(switch (mode) {
case "false", "disabled" -> LinkParseMode.DISABLED;
case "true", "lenient" -> LinkParseMode.LENIENT;
case "strict" -> LinkParseMode.STRICT;
default -> {
Skript.warning("Unknown link parse mode: " + mode + ", please use disabled, strict or lenient");
yield LinkParseMode.DISABLED;
}
});
} catch (Error e) {
// Ignore it, we're on unsupported server platform and class loading failed
}
});
public static final Option<Boolean> colorResetCodes = new Option<>("color codes reset formatting", true)
.setter(causeReset -> {
try {
TextComponentParser.instance().colorsCauseReset(causeReset);
} catch (Error e) {
// Ignore it, we're on unsupported server platform and class loading failed
}
});
public static final Option<String> scriptLoaderThreadSize = new Option<>("script loader thread size", "0")
.setter(s -> {
int asyncLoaderSize;
if (s.equalsIgnoreCase("processor count")) {
asyncLoaderSize = Runtime.getRuntime().availableProcessors();
} else {
try {
asyncLoaderSize = Integer.parseInt(s);
} catch (NumberFormatException e) {
Skript.error("Invalid option: " + s);
return;
}
}
ScriptLoader.setAsyncLoaderSize(asyncLoaderSize);
})
.optional(true);
public static final Option<Boolean> useTypeProperties = new Option<>("use type properties", true)
.optional(false);
public static final Option<Boolean> allowUnsafePlatforms = new Option<>("allow unsafe platforms", false)
.optional(true);
public static final Option<Boolean> keepLastUsageDates = new Option<>("keep command last usage dates", false)
.optional(true);
public static final Option<Boolean> loadDefaultAliases = new Option<>("load default aliases", true)
.optional(true);
public static final Option<Boolean> executeFunctionsWithMissingParams = new Option<>("execute functions with missing parameters", true)
.optional(true)
.setter(t -> Function.executeWithNulls = t);
public final static Option<Boolean> disableHookVault = new Option<>("disable hooks.vault", false)
.optional(true)
.setter(value -> {
userDisableHooks(VaultHook.class, value);
});
public final static Option<Boolean> disableHookGriefPrevention = new Option<>("disable hooks.regions.grief prevention", false)
.optional(true)
.setter(value -> {
userDisableHooks(GriefPreventionHook.class, value);
});
public final static Option<Boolean> disableHookPreciousStones = new Option<>("disable hooks.regions.precious stones", false)
.optional(true)
.setter(value -> {
userDisableHooks(PreciousStonesHook.class, value);
});
public final static Option<Boolean> disableHookResidence = new Option<>("disable hooks.regions.residence", false)
.optional(true)
.setter(value -> {
userDisableHooks(ResidenceHook.class, value);
});
public final static Option<Boolean> disableHookWorldGuard = new Option<>("disable hooks.regions.worldguard", false)
.optional(true)
.setter(value -> {
userDisableHooks(WorldGuardHook.class, value);
});
/**
* Disables the specified hook depending on the option value, or gives an error if this isn't allowed at this time.
*/
private static void userDisableHooks(Class<? extends Hook<?>> hookClass, boolean value) {
if (Skript.isFinishedLoadingHooks()) {
Skript.error("Hooks cannot be disabled once the server has started. " +
"Please restart the server to disable the hooks.");
return;
}
if (value) {
Skript.disableHookRegistration(hookClass);
}
}
public final static Option<Pattern> playerNameRegexPattern = new Option<>("player name regex pattern", Pattern.compile("[a-zA-Z0-9_]{1,16}"), s -> {
try {
return Pattern.compile(s);
} catch (PatternSyntaxException e) {
Skript.error("Invalid player name regex pattern: " + e.getMessage());
return null;
}
}).optional(true);
public static final Option<Timespan> longParseTimeWarningThreshold = new Option<>("long parse time warning threshold", new Timespan(0));
public static final Option<Timespan> runtimeErrorFrameDuration = new Option<>("runtime errors.frame duration", new Timespan(Timespan.TimePeriod.SECOND, 1));
public static final Option<Integer> runtimeErrorLimitTotal = new Option<>("runtime errors.total errors per frame", 8);
public static final Option<Integer> runtimeWarningLimitTotal = new Option<>("runtime errors.total warnings per frame", 8);
public static final Option<Integer> runtimeErrorLimitLine = new Option<>("runtime errors.errors from one line per frame", 2);
public static final Option<Integer> runtimeWarningLimitLine = new Option<>("runtime errors.warnings from one line per frame", 2);
public static final Option<Integer> runtimeErrorLimitLineTimeout = new Option<>("runtime errors.error spam timeout limit", 4);
public static final Option<Integer> runtimeWarningLimitLineTimeout = new Option<>("runtime errors.warning spam timeout limit", 4);
public static final Option<Integer> runtimeErrorTimeoutDuration = new Option<>("runtime errors.error timeout length", 10);
public static final Option<Integer> runtimeWarningTimeoutDuration = new Option<>("runtime errors.warning timeout length", 10);
public static final Option<Integer> variableChangesUntilSave = new Option<>("variable changes until save", 1000)
.setter(FlatFileStorage::setRequiredChangesForResave);
// intentionally not present in the config presented to the user. Users must manually add this option to disable it.
public static final Option<Boolean> simplifySyntaxesOnParse = new Option<>("simplify syntax on parse", true)
.optional(true);
/**
* This should only be used in special cases
*/
public static @Nullable Config getConfig() {
return mainConfig;
}
// also used for reloading
static void load() {
if (mainConfig != null)
mainConfig.invalidate(); // todo
try {
File configFile = new File(Skript.getInstance().getDataFolder(), "config.sk");
if (!configFile.exists()) {
Skript.error("Config file 'config.sk' does not exist!");
return;
}
if (!configFile.canRead()) {
Skript.error("Config file 'config.sk' cannot be read!");
return;
}
Config mainConfig;
try {
mainConfig = new Config(configFile, false, false, ":");
} catch (IOException ex) {
Skript.exception(ex, "Could not load the main config");
return;
}
SkriptConfig.mainConfig = mainConfig;
String configVersion = mainConfig.getValue(version.key);
if (configVersion == null || Skript.getVersion().compareTo(new Version(configVersion)) != 0) {
if (!mainConfig.getMainNode().isValid()) {
Skript.error("Your config is outdated, but cannot be updated because it contains errors.");
return;
}
try (InputStream stream = Skript.getInstance().getResource("config.sk")) {
if (stream == null) {
Skript.error("Your config is outdated, but Skript couldn't find the newest config in its jar.");
return;
}
Config newConfig = new Config(stream, "Skript.jar/config.sk", false, false, ":");
File backup = FileUtils.backup(configFile);
boolean updated = mainConfig.updateNodes(newConfig);
mainConfig.getMainNode().set(version.key, Skript.getVersion().toString());
mainConfig.save(configFile);
SkriptConfig.mainConfig = mainConfig;
if (updated) {
Skript.info("Your configuration has been updated to the latest version. " +
"A backup of your old config file has been created as " + backup.getName());
} else {
Skript.info("Your configuration is outdated, but no changes were performed. " +
"A backup of your config file has been created as " + backup.getName());
}
} catch (IOException ex) {
Skript.exception(ex, "Could not update the main config");
return;
}
}
mainConfig.load(SkriptConfig.class);
} catch (RuntimeException ex) {
Skript.exception(ex, "An error occurred while loading the config");
}
// trigger reload event handlers
eventRegistry().events(ReloadEvent.class).forEach(ReloadEvent::onReload);
}
}