Skip to content

Commit

Permalink
🚀 New module API
Browse files Browse the repository at this point in the history
  • Loading branch information
ItsTheSky committed Jan 14, 2025
1 parent 7d5a977 commit 5ee0a9d
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 93 deletions.
10 changes: 10 additions & 0 deletions src/main/java/info/itsthesky/disky/DiSky.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ch.njol.skript.Skript;
import ch.njol.skript.SkriptAddon;
import ch.njol.skript.util.Version;
import info.itsthesky.disky.api.emojis.EmojiStore;
import info.itsthesky.disky.api.generator.DocBuilder;
import info.itsthesky.disky.api.modules.DiSkyModule;
Expand All @@ -21,6 +22,7 @@
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.registration.SyntaxRegistry;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -199,4 +201,12 @@ public static void runtimeError(String description, @Nullable Object... data) {
getInstance().getLogger().severe(ChatColor.GOLD + " - " + ChatColor.YELLOW + data[i] + ChatColor.GRAY + ": " + ChatColor.WHITE + data[i + 1]);
}
}

public static SyntaxRegistry syntaxRegistry() {
return getAddonInstance().syntaxRegistry();
}

public static Version getVersion() {
return new Version(getInstance().getDescription().getVersion());
}
}
108 changes: 58 additions & 50 deletions src/main/java/info/itsthesky/disky/api/modules/DiSkyModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,105 @@

import ch.njol.skript.SkriptAddon;
import info.itsthesky.disky.DiSky;
import info.itsthesky.disky.api.DiSkyType;
import info.itsthesky.disky.api.events.SimpleDiSkyEvent;
import info.itsthesky.disky.core.Bot;
import info.itsthesky.disky.core.SkriptUtils;
import net.dv8tion.jda.api.JDA;
import org.bukkit.event.Event;
import org.skriptlang.skript.util.ClassLoader;

import java.io.File;
import java.net.URLClassLoader;
import java.util.function.Function;

public abstract class DiSkyModule {

private final String name;
private final String version;
private final String author;
private final DiSkyModuleInfo info;
private final File moduleJar;
private final ModuleManager manager;
private final ModuleOrigin origin;

private URLClassLoader loader;

public DiSkyModule(String name, String author, String version, File moduleJar) {
this.name = name;
this.author = author;
this.version = version;
public DiSkyModule(DiSkyModuleInfo info, File moduleJar) {
this.info = info;
this.moduleJar = moduleJar;

this.manager = DiSky.getModuleManager();
this.origin = new ModuleOrigin(this);
}

public abstract void init(final DiSky instance, final SkriptAddon addon);

/**
* Get the version of the module.
* @return The version
* @deprecated Use {@link DiSkyModuleInfo#version} instead
*/
@Deprecated(forRemoval = true)
public String getVersion() {
return info.version.toString();
}

/**
* Get the required minimum version of DiSky for this module.
* @return The required minimum version
* @deprecated Use {@link DiSkyModuleInfo#name} ()} instead
*/
@Deprecated(forRemoval = true)
public String getName() {
return name;
return info.name;
}

/**
* Get the author of the module.
* @return The author
* @deprecated Use {@link DiSkyModuleInfo#author} instead
*/
@Deprecated(forRemoval = true)
public String getAuthor() {
return author;
return info.author;
}

public String getVersion() {
return version;
/**
* Get further information about the module.
* @return The module info
*/
public DiSkyModuleInfo getModuleInfo() {
return info;
}

/**
* @return The module jar file
*/
public File getModuleJar() {
return moduleJar;
}

public <B extends Event, T> void registerValue(Class<B> bukkitClass,
Class<T> entityClass,
Function<B, T> function,
int time) {
SkriptUtils.registerValue(bukkitClass, entityClass, function, time);
}

public <B extends Event, T> void registerValue(Class<B> bukkitClass, Class<T> entityClass, Function<B, T> function) {
registerValue(bukkitClass, entityClass, function, 0);
}

public <E extends net.dv8tion.jda.api.events.Event, B extends SimpleDiSkyEvent<E>> void registerBotValue(Class<B> bukkitClass) {
registerValue(bukkitClass, Bot.class, e -> {
final JDA jda = e.getJDAEvent().getJDA();
return DiSky.getManager().fromJDA(jda);
});
}

protected <T> void registerType(Class<T> clazz, String codeName, Function<T, String> toString) {
final DiSkyType<T> type = new DiSkyType<>(clazz, codeName, toString, null);
type.register();
}

protected <T extends Enum<T>> void registerType(Class<T> clazz, String codeName) {
final DiSkyType<T> type = DiSkyType.fromEnum(clazz, codeName, codeName);
type.register();
/**
* Get the origin for syntax registration, representing this module.
* @return The origin
*/
public ModuleOrigin getOrigin() {
return origin;
}

/**
* @return The URL class loader for this module
*/
public URLClassLoader getLoader() {
return loader;
}

/**
* Change the loader of this module. This should only be called when
* the module is being instantiated for the first time; otherwise, it
* may cause issues with class loading.
* @param loader The new loader
* @return The module, for chaining
*/
public DiSkyModule setLoader(URLClassLoader loader) {
this.loader = loader;
return this;
}

public void disable() {
manager.disable(this);
}

/**
* Load classes from the given base package and sub-packages.
* @param basePackage The base package to load from
* @param subPackages The sub-packages to load from
*/
protected void loadClasses(String basePackage, String... subPackages) {
ClassLoader.builder()
.basePackage(basePackage)
Expand All @@ -103,4 +110,5 @@ protected void loadClasses(String basePackage, String... subPackages) {
.build()
.loadClasses(getClass(), getModuleJar());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package info.itsthesky.disky.api.modules;

import ch.njol.skript.util.Version;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Represents information about a <code>module.yml</code> file of a
* DiSky module. This is usually deserialized from the module's JAR file
* and shouldn't be manipulated/instantiated in any other way.
*
* @since 4.21.0
* @see DiSkyModule
*/
public class DiSkyModuleInfo {

/**
* The main class of the module.
*/
public final @NotNull String mainClass;

/**
* The name of the module.
*/
public final @NotNull String name;

/**
* The author of this module.
*/
public final @NotNull String author;

/**
* The version of this module.
*/
public final @NotNull Version version;

/**
* The minimal DiSky version required to run this module.
* If no version is specified in the module.yml, it will default
* to 4.21.0 as it's the first version to support the new module system.
*/
public final @NotNull Version requiredMinVersion;

private DiSkyModuleInfo(@NotNull String mainClass,
@NotNull String name,
@NotNull String author,
@NotNull Version version,
@Nullable Version requiredMinVersion) {
this.mainClass = mainClass;
this.name = name;
this.author = author;
this.version = version;

// We default to the oldest DiSky version that
// supports the new module system, which is 4.21.0
this.requiredMinVersion = requiredMinVersion == null
? new Version(4, 21, 0) : requiredMinVersion;
}

/**
* Create a new instance of {@link DiSkyModuleInfo} from a given
* {@link YamlConfiguration} object.
* @param configuration The configuration to deserialize
* @return The deserialized module info
*/
public static DiSkyModuleInfo fromYaml(YamlConfiguration configuration) {
return new DiSkyModuleInfo(
configuration.getString("main"),
configuration.getString("name"),
configuration.getString("author"),
new Version(configuration.getString("version")),
configuration.contains("required-version") ? new Version(configuration.getString("required-version")) : null
);
}
}
63 changes: 21 additions & 42 deletions src/main/java/info/itsthesky/disky/api/modules/ModuleManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import info.itsthesky.disky.api.generator.DocBuilder;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.Nullable;

import java.io.BufferedReader;
import java.io.File;
Expand Down Expand Up @@ -51,56 +52,30 @@ private DiSkyModule loadModule(final File file) throws IOException, ClassNotFoun

final YamlConfiguration config = new YamlConfiguration();
config.loadFromString(moduleYml);

final String classPath = config.getString("MainClass");

final String name = config.getString("Name");
final String author = config.getString("Author");
final String version = config.getString("Version");
final DiSkyModuleInfo info = DiSkyModuleInfo.fromYaml(config);
if (DiSky.getVersion().isSmallerThan(info.requiredMinVersion)) {
getLogger().severe("The module '"+info.name+"' v"+info.version+" by '"+info.author+"' requires at least DiSky v"+info.requiredMinVersion+" to work! (You're using v"+DiSky.getVersion()+")");
return null;
}

final URL[] urls = {new URL("jar:file:"+file.getAbsolutePath()+"!/")};
final URLClassLoader loader = new URLClassLoader(urls, getClass().getClassLoader());
final Class<DiSkyModule> clazz = (Class<DiSkyModule>) loader.loadClass(classPath);
final Class<DiSkyModule> clazz = (Class<DiSkyModule>) loader.loadClass(info.mainClass);

final Constructor<DiSkyModule> constructor = clazz.getDeclaredConstructor(String.class, String.class, String.class, File.class);
return constructor.newInstance(name, author, version, file)
final Constructor<DiSkyModule> constructor = clazz.getDeclaredConstructor(DiSkyModuleInfo.class, File.class);
return constructor.newInstance(info, file)
.setLoader(loader);
}

public void reload(DiSkyModule module) {
getLogger().severe("It's now impossible to reload a module, please restart the server to reload it.");
}

public void disable(DiSkyModule module) {
getLogger().severe("It's now impossible to disable a module, please restart the server to disable it.");
}

private void unregisterSyntaxes(DiSkyModule module) {
final Collection<SyntaxElementInfo<?>> effects = Skript
.getEffects().stream().filter(i -> DocBuilder.isFromModule(i, module)).collect(Collectors.toList());
final Collection<SyntaxElementInfo<?>> conditions = Skript
.getConditions().stream().filter(i -> DocBuilder.isFromModule(i, module)).collect(Collectors.toList());
final Collection<SyntaxElementInfo<?>> sections = Skript
.getSections().stream().filter(i -> DocBuilder.isFromModule(i, module)).collect(Collectors.toList());
final Collection<SyntaxElementInfo<?>> events = Skript
.getEvents().stream().filter(i -> DocBuilder.isFromModule(i, module)).collect(Collectors.toList());
final Collection<SyntaxElementInfo<?>> expressions = ((List<ExpressionInfo<?, ?>>) ReflectionUtils.getField(Skript.class, null, "expressions"))
.stream()
.filter(i -> DocBuilder.isFromModule(i, module)).collect(Collectors.toList());

ReflectionUtils.setFinalCollection(Skript.class, "effects", effects);
ReflectionUtils.setFinalCollection(Skript.class, "conditions", conditions);
ReflectionUtils.setFinalCollection(Skript.class, "sections", sections);
ReflectionUtils.setFinalCollection(Skript.class, "events", events);
ReflectionUtils.setFinalCollection(Skript.class, "expressions", new ArrayList<>(expressions));
}

public void loadModules() throws IOException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvalidConfigurationException {
final File[] modulesFile = this.moduleFolder.listFiles();
assert modulesFile != null;
for (final File moduleFile : modulesFile) {
if (moduleFile.isDirectory() || !moduleFile.getName().endsWith(".jar"))
if (moduleFile.isDirectory() || !moduleFile.getName().endsWith(".jar")) {
getLogger().warning("Skipping file '"+moduleFile.getPath()+"' as it's not a valid module file.");
continue;
}

getLogger().warning("Loading module from file '"+moduleFile.getPath()+"'...");
final DiSkyModule module;
try {
Expand All @@ -110,16 +85,20 @@ public void loadModules() throws IOException, ClassNotFoundException, Invocation
getLogger().severe("Unable to initialize module '"+moduleFile.getPath()+"'! Maybe a wrong Java version?");
return;
}
getLogger().info("Successfully loaded module '"+module.getName()+"' v"+module.getVersion()+" by '"+module.getAuthor()+"'! Enabling ...");
if (module == null) // If the module is null, it means the version is not compatible
continue;


getLogger().info("Successfully loaded module '"+module.getModuleInfo().name+"' v"+module.getModuleInfo().version+" by '"+module.getModuleInfo().author+"'! Enabling ...");
try {
module.init(this.instance, this.addon);
} catch (Exception ex) {
ex.printStackTrace();
getLogger().severe("Failed to enable module '"+module.getName()+"' v"+module.getVersion()+" by '"+module.getAuthor()+"':");
getLogger().severe("Failed to enable module '"+module.getModuleInfo().name+"' v"+module.getModuleInfo().version+" by '"+module.getModuleInfo().author+"':");
continue;
}
modules.put(module.getName(), module);
getLogger().info("Successfully enabled module '"+module.getName()+"'!");
modules.put(module.getModuleInfo().name, module);
getLogger().info("Successfully enabled module '"+module.getModuleInfo().name+"'!");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public ModuleOrigin(@NotNull DiSkyModule module) {

@Override
public String name() {
return module.getName() + " [DiSky Module]";
return module.getModuleInfo().name + " [DiSky Module]";
}

}

0 comments on commit 5ee0a9d

Please sign in to comment.