Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 11 additions & 12 deletions src/main/java/ch/njol/skript/Skript.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import ch.njol.skript.command.Commands;
import ch.njol.skript.doc.Documentation;
import ch.njol.skript.events.EvtSkript;
import ch.njol.skript.examples.CoreExampleScripts;
import ch.njol.skript.examples.ExampleScriptManager;
import ch.njol.skript.expressions.arithmetic.ExprArithmetic;
import ch.njol.skript.hooks.Hook;
import ch.njol.skript.lang.*;
Expand Down Expand Up @@ -185,6 +187,8 @@ public final class Skript extends JavaPlugin implements Listener {
private static boolean disabled = false;
private static boolean partDisabled = false;

public static @Nullable ExampleScriptManager exampleManager;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably be package-private and have a getter to enforce usage through SkriptAddon


public static Skript getInstance() {
if (instance == null)
throw new IllegalStateException();
Expand Down Expand Up @@ -420,11 +424,9 @@ public void onEnable() {
if (!scriptsFolder.isDirectory() || !config.exists() || !features.exists() || !lang.exists() || !aliasesFolder.exists()) {
ZipFile f = null;
try {
boolean populateExamples = false;
if (!scriptsFolder.isDirectory()) {
if (!scriptsFolder.mkdirs())
throw new IOException("Could not create the directory " + scriptsFolder);
populateExamples = true;
}

boolean populateLanguageFiles = false;
Expand All @@ -444,15 +446,9 @@ public void onEnable() {
if (e.isDirectory())
continue;
File saveTo = null;
if (populateExamples && e.getName().startsWith(SCRIPTSFOLDER + "/")) {
String fileName = e.getName().substring(e.getName().indexOf("/") + 1);
// All example scripts must be disabled for jar security.
if (!fileName.startsWith(ScriptLoader.DISABLED_SCRIPT_PREFIX))
fileName = ScriptLoader.DISABLED_SCRIPT_PREFIX + fileName;
saveTo = new File(scriptsFolder, fileName);
} else if (populateLanguageFiles
&& e.getName().startsWith("lang/")
&& !e.getName().endsWith("default.lang")) {
if (populateLanguageFiles
&& e.getName().startsWith("lang/")
&& !e.getName().endsWith("default.lang")) {
String fileName = e.getName().substring(e.getName().lastIndexOf("/") + 1);
saveTo = new File(lang, fileName);
} else if (e.getName().equals("config.sk")) {
Expand All @@ -476,7 +472,7 @@ public void onEnable() {
}
}
}
info("Successfully generated the config and the example scripts.");
info("Successfully generated the config.");
} catch (ZipException ignored) {} catch (IOException e) {
error("Error generating the default files: " + ExceptionUtils.toString(e));
} finally {
Expand All @@ -488,6 +484,9 @@ public void onEnable() {
}
}

exampleManager = new ExampleScriptManager();
exampleManager.installExamples("Skript", CoreExampleScripts.all(), scriptsFolder);

// initialize the modern Skript instance
skript = org.skriptlang.skript.Skript.of(getClass(), getName());
unmodifiableSkript = new ModernSkriptBridge.SpecialUnmodifiableSkript(skript);
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/ch/njol/skript/SkriptAddon.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -11,6 +12,7 @@
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable;

import ch.njol.skript.examples.ExampleScript;
import ch.njol.skript.util.Utils;
import ch.njol.skript.util.Version;
import org.jetbrains.annotations.ApiStatus;
Expand Down Expand Up @@ -93,6 +95,23 @@ public String getLanguageFileDirectory() {
return localizer().languageFileDirectory();
}

/**
* Registers example scripts with Skript for this addon.
*
* @param scripts Example scripts to install
* @throws IOException If an I/O error occurs while installing the scripts
*/
public void registerExampleScripts(ExampleScript... scripts) throws IOException {
if (Skript.exampleManager == null)
throw new IllegalStateException("Example script manager is not initialized");

Skript.exampleManager.installExamples(
plugin.getName(),
Arrays.asList(scripts),
Skript.getInstance().getScriptsFolder()
);
}

@Nullable
private File file;

Expand Down
43 changes: 43 additions & 0 deletions src/main/java/ch/njol/skript/examples/CoreExampleScripts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ch.njol.skript.examples;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;

public final class CoreExampleScripts {

public static final List<ExampleScript> EXAMPLES = List.of(
load("chest menus.sk"),
load("commands.sk"),
load("events.sk"),
load("experimental features/for loops.sk"),
load("experimental features/queues.sk"),
load("experimental features/script reflection.sk"),
load("functions.sk"),
load("loops.sk"),
load("options and meta.sk"),
load("text formatting.sk"),
load("timings.sk"),
load("variables.sk")
Comment on lines +12 to +23
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'd be nice to separate the bukkit-based examples out into a different list where feasible

);

private CoreExampleScripts() {}

public static Collection<ExampleScript> all() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static Collection<ExampleScript> all() {
public static Collection<ExampleScript> values() {

this method should probably be moved to ExampleScript if it becomes an interface

return EXAMPLES;
}

private static ExampleScript load(String name) {
String path = "scripts/-examples/" + name;
try (InputStream in = CoreExampleScripts.class.getClassLoader().getResourceAsStream(path)) {
if (in == null)
throw new IllegalStateException("Missing example script " + path);
String content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
return new ExampleScript(name, content);
} catch (IOException e) {
throw new RuntimeException("Failed to load example script " + path, e);
}
}
}
31 changes: 31 additions & 0 deletions src/main/java/ch/njol/skript/examples/ExampleScript.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ch.njol.skript.examples;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import org.bukkit.plugin.java.JavaPlugin;

/**
* Represents an example script bundled with Skript or an addon.
*/
public record ExampleScript(String name, String content) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this could be an interface where all the example scripts are registered? this can then be extended by addons and registered with SkriptAddon


/**
* Loads an example script from a resource contained within an addon JAR.
*
* @param plugin The plugin providing the resource
* @param resourcePath The path to the resource inside the plugin
* @param outputName The name of the file to install the example as
* @return A new {@link ExampleScript} containing the resource's content
* @throws IOException If the resource cannot be found or read
*/
public static ExampleScript fromResource(JavaPlugin plugin, String resourcePath, String outputName) throws IOException {
try (InputStream in = plugin.getResource(resourcePath)) {
if (in == null)
throw new IOException("Resource not found: " + resourcePath);
String content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
return new ExampleScript(outputName, content);
}
}
}
104 changes: 104 additions & 0 deletions src/main/java/ch/njol/skript/examples/ExampleScriptManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package ch.njol.skript.examples;

import ch.njol.skript.Skript;
import org.bukkit.Bukkit;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.DosFileAttributeView;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

public final class ExampleScriptManager {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs javadocs in this class

private Set<String> installed;
private File installedFile;

public ExampleScriptManager() {}

private void loadInstalled(File scriptsDir) {
File parent = scriptsDir.getParentFile();
installedFile = new File(parent == null ? scriptsDir : parent, ".loaded_examples");
installed = new LinkedHashSet<>();
if (!installedFile.exists())
return;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(installedFile), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.isEmpty())
installed.add(line);
}
} catch (IOException e) {
throw new RuntimeException("Failed to load installed examples", e);
}
}

private void flushInstalled() {
if (installedFile == null || installed == null)
return;
File parent = installedFile.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) // failed to create directory for installed examples
return;
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path installedPath = installedFile.toPath();
DosFileAttributeView dosView = null;
if (isWindows && Files.exists(installedPath)) {
dosView = Files.getFileAttributeView(installedPath, DosFileAttributeView.class);
if (dosView != null) {
try {
if (dosView.readAttributes().isHidden())
dosView.setHidden(false);
} catch (IOException ignored) {}
}
}

try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(installedFile), StandardCharsets.UTF_8))) {
for (String entry : installed) {
writer.write(entry);
writer.newLine();
}
} catch (IOException e) { // failed to save installed examples
return;
}

if (isWindows) {
try {
if (dosView != null) {
dosView.setHidden(true);
} else {
Files.setAttribute(installedPath, "dos:hidden", true);
}
} catch (Exception ignored) {}
}
}

public void installExamples(String plugin, Collection<ExampleScript> scripts, File scriptsDir) {
loadInstalled(scriptsDir);
boolean dirty = false;
File baseDir = new File(scriptsDir, "-examples/" + plugin);
for (ExampleScript script : scripts) {
String key = plugin + "/" + script.name();
if (installed.add(key)) {
dirty = true;
File file = new File(baseDir, script.name());
file.getParentFile().mkdirs();
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
writer.write(script.content());
} catch (IOException e) {
throw new RuntimeException("Failed to write example script " + file, e);
}
}
}
if (dirty)
flushInstalled();
}
}