Skip to content

Commit d2debda

Browse files
committed
Start work on addon support
1 parent dd4a409 commit d2debda

File tree

5 files changed

+268
-0
lines changed

5 files changed

+268
-0
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.github.skriptdev.skript.api.skript.addon;
2+
3+
import com.github.skriptdev.skript.plugin.HySk;
4+
import com.hypixel.hytale.codec.ExtraInfo;
5+
import com.hypixel.hytale.codec.util.RawJsonReader;
6+
import io.github.syst3ms.skriptparser.log.ErrorType;
7+
import io.github.syst3ms.skriptparser.log.SkriptLogger;
8+
import org.bson.BsonDocument;
9+
import org.bson.BsonValue;
10+
11+
import java.io.File;
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.io.InputStreamReader;
15+
import java.net.URL;
16+
import java.net.URLClassLoader;
17+
import java.nio.charset.StandardCharsets;
18+
import java.nio.file.Path;
19+
import java.util.Arrays;
20+
import java.util.jar.JarEntry;
21+
import java.util.jar.JarFile;
22+
23+
public class AddonLoader {
24+
25+
private final SkriptLogger logger;
26+
27+
public AddonLoader(SkriptLogger logger) {
28+
this.logger = logger;
29+
}
30+
31+
public void loadAddonsFromFolder() {
32+
Path resolve = HySk.getInstance().getDataDirectory().resolve("addons");
33+
File addonFolder = resolve.toFile();
34+
if (!addonFolder.exists()) {
35+
if (!addonFolder.mkdirs()) {
36+
this.logger.error("Failed to create addons folder", ErrorType.STRUCTURE_ERROR);
37+
return;
38+
}
39+
} else if (!addonFolder.isDirectory()) {
40+
this.logger.error("Addons folder is not a directory", ErrorType.STRUCTURE_ERROR);
41+
return;
42+
}
43+
File[] files = addonFolder.listFiles();
44+
if (files == null) {
45+
this.logger.error("Failed to list files in addons folder", ErrorType.STRUCTURE_ERROR);
46+
return;
47+
}
48+
49+
for (File file : Arrays.stream(files)
50+
.filter(File::isFile)
51+
.filter(f -> f.getName().endsWith(".jar"))
52+
.toList()) {
53+
loadAddon(file);
54+
}
55+
}
56+
57+
private void loadAddon(File file) {
58+
try (JarFile jarFile = new JarFile(file)) {
59+
JarEntry jarEntry = jarFile.getJarEntry("manifest.json");
60+
if (jarEntry == null) {
61+
this.logger.error("Manifest.json not found in addon " + file.getName(), ErrorType.STRUCTURE_ERROR);
62+
return;
63+
}
64+
65+
InputStream inputStream = jarFile.getInputStream(jarEntry);
66+
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
67+
char[] buffer = RawJsonReader.READ_BUFFER.get();
68+
RawJsonReader rawJsonReader = new RawJsonReader(reader, buffer);
69+
70+
Manifest manifest = Manifest.CODEC.decodeJson(rawJsonReader, new ExtraInfo());
71+
if (manifest == null) {
72+
this.logger.error("Failed to decode manifest.json in addon " + file.getName(), ErrorType.STRUCTURE_ERROR);
73+
return;
74+
}
75+
76+
URL[] urls = {file.toURI().toURL()};
77+
URLClassLoader classLoader = new URLClassLoader(urls, HySk.getInstance().getClassLoader());
78+
Class<?> externalClass;
79+
try {
80+
externalClass = classLoader.loadClass(manifest.getMainClass());
81+
} catch (ClassNotFoundException e) {
82+
this.logger.error("Main class not found in addon " + file.getName(), ErrorType.STRUCTURE_ERROR);
83+
return;
84+
}
85+
Object mainClassIntance;
86+
try {
87+
mainClassIntance = externalClass.getDeclaredConstructor(String.class).newInstance(manifest.getName());
88+
} catch (ReflectiveOperationException e) {
89+
this.logger.error("Failed to create instance of addon " + file.getName(), ErrorType.EXCEPTION);
90+
return;
91+
}
92+
if (mainClassIntance instanceof HySkriptAddon addon) {
93+
addon.setManifest(manifest);
94+
addon.onLoad();
95+
}
96+
} catch (IOException e) {
97+
this.logger.error("Failed to load addon " + file.getName(), ErrorType.EXCEPTION);
98+
}
99+
}
100+
101+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.github.skriptdev.skript.api.skript.addon;
2+
3+
import com.hypixel.hytale.server.core.Message;
4+
import org.jetbrains.annotations.Nullable;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
public abstract class HySkriptAddon extends io.github.syst3ms.skriptparser.registration.SkriptAddon {
10+
11+
private Manifest manifest;
12+
13+
public HySkriptAddon(String name) {
14+
super(name);
15+
}
16+
17+
/**
18+
* Called when the addon starts to load.
19+
* This is a good time to set up your syntaxes.
20+
*/
21+
abstract public void onLoad();
22+
23+
void setManifest(Manifest manifest) {
24+
this.manifest = manifest;
25+
}
26+
27+
public Manifest getManifest() {
28+
return manifest;
29+
}
30+
31+
public Message[] getInfo() {
32+
List<Message> info = new ArrayList<>();
33+
info.add(Message.raw("Version: " + this.manifest.getVersion()));
34+
35+
String description = this.manifest.getDescription();
36+
if (description != null) info.add(Message.raw("Description: " + description));
37+
38+
@Nullable String[] authors = this.manifest.getAuthors();
39+
if (authors != null) info.add(Message.raw("Authors: " + String.join(", ", authors)));
40+
41+
String website = this.manifest.getWebsite();
42+
if (website != null) info.add(Message.raw("Website: ").insert(Message.raw(website).link(website)));
43+
44+
return info.toArray(Message[]::new);
45+
}
46+
47+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.github.skriptdev.skript.api.skript.addon;
2+
3+
import com.hypixel.hytale.codec.Codec;
4+
import com.hypixel.hytale.codec.KeyedCodec;
5+
import com.hypixel.hytale.codec.builder.BuilderCodec;
6+
import org.jetbrains.annotations.NotNull;
7+
import org.jetbrains.annotations.Nullable;
8+
9+
import java.util.Arrays;
10+
11+
public class Manifest {
12+
13+
private static final BuilderCodec.Builder<Manifest> BUILDER = BuilderCodec.builder(Manifest.class, Manifest::new);
14+
public static final Codec<Manifest> CODEC = BUILDER
15+
.append(new KeyedCodec<>("Main", Codec.STRING),
16+
((manifest, string) -> manifest.mainClass = string),
17+
(manifest -> manifest.mainClass))
18+
.add().append(new KeyedCodec<>("Name", Codec.STRING),
19+
((manifest, string) -> manifest.name = string),
20+
(manifest -> manifest.name))
21+
.add().append(new KeyedCodec<>("Version", Codec.STRING),
22+
((manifest, string) -> manifest.version = string),
23+
(manifest -> manifest.version))
24+
.add().append(new KeyedCodec<>("Description", Codec.STRING),
25+
((manifest, string) -> manifest.description = string),
26+
(manifest -> manifest.description))
27+
.add().append(new KeyedCodec<>("Authors", Codec.STRING_ARRAY),
28+
((manifest, strings) -> manifest.authors = strings),
29+
(manifest -> manifest.authors))
30+
.add().append(new KeyedCodec<>("Website", Codec.STRING),
31+
((manifest, string) -> manifest.website = string),
32+
(manifest -> manifest.website))
33+
.add().build();
34+
private String mainClass;
35+
private String name;
36+
private String version;
37+
private String description;
38+
private String[] authors;
39+
private String website;
40+
41+
public String getMainClass() {
42+
return this.mainClass;
43+
}
44+
45+
public String getName() {
46+
return this.name;
47+
}
48+
49+
public String getVersion() {
50+
return this.version;
51+
}
52+
53+
public String getDescription() {
54+
return this.description;
55+
}
56+
57+
public String[] getAuthors() {
58+
return this.authors;
59+
}
60+
61+
public String getWebsite() {
62+
return this.website;
63+
}
64+
65+
@Override
66+
public String toString() {
67+
return "Manifest{" +
68+
"name='" + name + '\'' +
69+
", version='" + version + '\'' +
70+
", description='" + description + '\'' +
71+
", authors=" + Arrays.toString(authors) +
72+
", website='" + website + '\'' +
73+
'}';
74+
}
75+
76+
}

src/main/java/com/github/skriptdev/skript/plugin/Skript.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.skriptdev.skript.plugin;
22

33
import com.github.skriptdev.skript.api.skript.ScriptsLoader;
4+
import com.github.skriptdev.skript.api.skript.addon.AddonLoader;
45
import com.github.skriptdev.skript.api.skript.command.ArgUtils;
56
import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration;
67
import com.github.skriptdev.skript.api.skript.variables.JsonVariableStorage;
@@ -35,6 +36,7 @@ public class Skript extends SkriptAddon {
3536
private final SkriptLogger logger;
3637
private SkriptRegistration registration;
3738
private ElementRegistration elementRegistration;
39+
private AddonLoader addonLoader;
3840
private ScriptsLoader scriptsLoader;
3941

4042
Skript(HySk hySk) {
@@ -69,6 +71,12 @@ private void setup() {
6971
printSyntaxCount();
7072
Utils.log("HySkript setup complete!");
7173

74+
// LOAD ADDONS
75+
Utils.log("Loading addons...");
76+
this.addonLoader = new AddonLoader(this.logger);
77+
this.addonLoader.loadAddonsFromFolder();
78+
Utils.log("Finished loading addons!");
79+
7280
// LOAD VARIABLES
7381
loadVariables();
7482

@@ -183,4 +191,13 @@ public ScriptsLoader getScriptsLoader() {
183191
return this.scriptsLoader;
184192
}
185193

194+
/**
195+
* Get an instance of Skript.
196+
*
197+
* @return Instance of Skript.
198+
*/
199+
public static Skript getInstance() {
200+
return INSTANCE;
201+
}
202+
186203
}

src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.skriptdev.skript.plugin.command;
22

3+
import com.github.skriptdev.skript.api.skript.addon.HySkriptAddon;
34
import com.github.skriptdev.skript.api.skript.docs.JsonDocPrinter;
45
import com.github.skriptdev.skript.api.skript.docs.MarkdownDocPrinter;
56
import com.github.skriptdev.skript.api.utils.Utils;
@@ -10,6 +11,7 @@
1011
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
1112
import com.hypixel.hytale.server.core.command.system.CommandContext;
1213
import com.hypixel.hytale.server.core.command.system.CommandRegistry;
14+
import com.hypixel.hytale.server.core.command.system.CommandSender;
1315
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
1416
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
1517
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
@@ -39,6 +41,7 @@ public SkriptCommand(CommandRegistry registry) {
3941
addAliases("sk");
4042

4143
// Keep these in alphabetical order
44+
addSubCommand(addonsCommand());
4245
addSubCommand(new DocsCommand());
4346
addSubCommand(infoCommand());
4447
addSubCommand(new ReloadCommand());
@@ -139,6 +142,30 @@ protected CompletableFuture<Void> execute(@NotNull CommandContext commandContext
139142
};
140143
}
141144

145+
private AbstractCommand addonsCommand() {
146+
return new AbstractCommand("addons", "Get info about loaded addons.") {
147+
@Override
148+
protected CompletableFuture<Void> execute(@NotNull CommandContext commandContext) {
149+
return CompletableFuture.runAsync(() -> {
150+
List<SkriptAddon> addons = SkriptAddon.getAddons().stream()
151+
.filter(addon -> !addon.getAddonName().equalsIgnoreCase("skript-parser") && !addon.getAddonName().equalsIgnoreCase("HySkript"))
152+
.toList();
153+
if (addons.isEmpty()) return;
154+
CommandSender sender = commandContext.sender();
155+
Utils.sendMessage(sender, "Loaded Addons:");
156+
addons.forEach(addon -> {
157+
if (addon instanceof HySkriptAddon hySkriptAddon) {
158+
Utils.sendMessage(sender, " - %s:", hySkriptAddon.getAddonName());
159+
for (Message s : hySkriptAddon.getInfo()) {
160+
sender.sendMessage(Message.raw(" ").insert(s));
161+
}
162+
}
163+
});
164+
});
165+
}
166+
};
167+
}
168+
142169
private void printInfo(IMessageReceiver sender) {
143170
Utils.sendMessage(sender, "HySkript Version: %s", HySk.getInstance().getManifest().getVersion());
144171
Utils.sendMessage(sender, "Hytale Version: %s (%s)", ManifestUtil.getImplementationVersion(), ManifestUtil.getPatchline());

0 commit comments

Comments
 (0)