Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement plugin api version check #23

Merged
merged 9 commits into from
Aug 23, 2024
62 changes: 21 additions & 41 deletions src/main/java/org/sculk/plugin/Plugin.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.sculk.plugin;

import com.google.common.base.Preconditions;
import lombok.Getter;
import org.apache.logging.log4j.Logger;
import org.sculk.Server;

Expand All @@ -10,6 +11,7 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Getter
public abstract class Plugin {
protected boolean enabled = false;
private PluginData description;
Expand All @@ -19,9 +21,6 @@ public abstract class Plugin {
private File dataFolder;
private boolean initialized = false;

public Plugin() {
}

protected final void init(PluginData description, Server server, File pluginFile) {
Preconditions.checkArgument(!this.initialized, "Plugin has been already initialized!");
this.initialized = true;
Expand All @@ -31,8 +30,10 @@ protected final void init(PluginData description, Server server, File pluginFile

this.pluginFile = pluginFile;
this.dataFolder = new File(Server.getInstance().getDataPath() + "/plugins/" + description.getName().toLowerCase() + "/");
if (!this.dataFolder.exists()) {
this.dataFolder.mkdirs();

if (this.dataFolder.mkdirs()) {
this.logger.info("Created plugin data folder");

}
}

Expand All @@ -45,52 +46,31 @@ public void onDisable() {
}

public InputStream getResourceFile(String filename) {
try {
JarFile pluginJar = new JarFile(this.pluginFile);
JarEntry entry = pluginJar.getJarEntry(filename);
return pluginJar.getInputStream(entry);
try(JarFile jar = new JarFile(this.pluginFile)) {
JarEntry entry = jar.getJarEntry(filename);
return jar.getInputStream(entry);
} catch (IOException e) {
return null;
}
return null;
}

public boolean isEnabled() {
return this.enabled;
}

public void setEnabled(boolean enabled) {
if (this.enabled == enabled) {
return;
}
this.enabled = enabled;
try {
if (enabled) {
this.onEnable();
} else {
this.onDisable();
if (this.enabled != enabled) {
this.enabled = enabled;

try {
if (enabled) {
this.onEnable();
} else {
this.onDisable();
}
} catch (Exception e) {
this.logger.error("Error while enabling/disabling plugin " + this.getName() + ": " + e);
}
} catch (Exception e) {
this.logger.error("Error while enabling/disabling plugin " + this.getName() + ": " + e);
}
}

public PluginData getDescription() {
return this.description;
}

public String getName() {
return this.description.getName();
}

public Server getServer() {
return this.server;
}

public Logger getLogger() {
return this.logger;
}

public File getDataFolder() {
return this.dataFolder;
}
}
1 change: 1 addition & 0 deletions src/main/java/org/sculk/plugin/PluginClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected Class<?> findClass(String name, boolean checkGlobal) throws ClassNotFo
if (result == null && (result = super.findClass(name)) != null) {
this.pluginManager.cacheClass(name, result);
}

this.classes.put(name, result);
return result;
}
Expand Down
30 changes: 11 additions & 19 deletions src/main/java/org/sculk/plugin/PluginData.java
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
package org.sculk.plugin;

import lombok.Getter;
import lombok.ToString;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;

@Getter
@ToString
public class PluginData{

public class PluginData {
public String name;
public String version;
public String author;
public String main;
public String api;
public List<String> apis;
public List<String> depends;

public String getAuthor() {
return this.author;
}

public String getMain() {
return this.main;
}

public String getName() {
return this.name;
}

public String getVersion() {
return this.version;
}
public List<String> getApi() {
if(apis == null) apis = new ArrayList<>();
if(api != null && !apis.contains(api)) apis.add(api);

public List<String> getDepends() {
return this.depends;
return apis;
}
}
15 changes: 9 additions & 6 deletions src/main/java/org/sculk/plugin/PluginLoader.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.sculk.plugin;

import lombok.extern.log4j.Log4j2;
import org.yaml.snakeyaml.Yaml;
import org.sculk.Sculk;

import java.io.File;
import java.io.InputStream;
Expand Down Expand Up @@ -49,9 +49,10 @@ protected Plugin loadPluginJAR(PluginData pluginConfig, File pluginJar, PluginCl
return null;
}

protected PluginData loadPluginData(File file, Yaml yaml) {
protected PluginData loadPluginData(File file) {
try (JarFile pluginJar = new JarFile(file)) {
JarEntry configEntry = pluginJar.getJarEntry("plugin.yml");
JarEntry configEntry = pluginJar.getJarEntry("sculk.yml");

if (configEntry == null) {
configEntry = pluginJar.getJarEntry("plugin.yml");
}
Expand All @@ -62,13 +63,15 @@ protected PluginData loadPluginData(File file, Yaml yaml) {
}

try (InputStream fileStream = pluginJar.getInputStream(configEntry)) {
PluginData pluginConfig = yaml.loadAs(fileStream, PluginData.class);
if (pluginConfig.getMain() != null && pluginConfig.getName() != null) {
PluginData pluginConfig = PluginManager.yamlLoader.loadAs(fileStream, PluginData.class);
if (pluginConfig.getMain() != null && pluginConfig.getName() != null && pluginConfig.getApi().contains(Sculk.CODE_VERSION.replace("v", ""))) {
// Valid plugin.yml, main and name set
return pluginConfig;
}
}
log.warn("Invalid plugin.yml for " + file.getName() + ": main and/or name property missing");

log.warn("Invalid plugin.yml for " + file.getName() + ": main and/or name property missing, incompactible api version");

} catch (Exception e) {
log.error("Can not load plugin files in " + file.getPath(), e);
}
Expand Down
65 changes: 37 additions & 28 deletions src/main/java/org/sculk/plugin/PluginManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair;
import lombok.Getter;

import org.sculk.Server;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
Expand All @@ -22,12 +24,14 @@

public class PluginManager {
public static final Yaml yamlLoader;

static {
Representer representer = new Representer(new DumperOptions());
representer.getPropertyUtils().setSkipMissingProperties(true);
yamlLoader = new Yaml(new CustomClassLoaderConstructor(PluginManager.class.getClassLoader(), new LoaderOptions()), representer);
}

@Getter
private final Server server;
private final PluginLoader pluginLoader;

Expand Down Expand Up @@ -59,7 +63,7 @@ private void loadPluginsInside(Path folderPath) throws IOException {
};

Map<PluginData, Path> plugins = new TreeMap<>(comparator);
try (Stream<Path> stream = Files.walk(folderPath)){
try (Stream<Path> stream = Files.walk(folderPath)) {
stream.filter(Files::isRegularFile).filter(PluginLoader::isJarFile).forEach(jarPath -> {
PluginData config = this.loadPluginConfig(jarPath);
if (config != null) {
Expand All @@ -80,7 +84,8 @@ private PluginData loadPluginConfig(Path path) {
if (!pluginFile.exists()) {
return null;
}
return this.pluginLoader.loadPluginData(pluginFile, yamlLoader);

return this.pluginLoader.loadPluginData(pluginFile);
}

private PluginClassLoader registerClassLoader(PluginData config, Path path) {
Expand Down Expand Up @@ -151,31 +156,42 @@ public void enableAllPlugins() {
return;
}

StringBuilder builder = new StringBuilder("§cFailed to load plugins: §e");
while (failed.peek() != null) {
Plugin plugin = failed.poll();
builder.append(plugin.getName());
if (failed.peek() != null) {
builder.append(", ");
}
}
server.getLogger().warn(builder.toString());
server.getLogger().warn("§cFailed to load plugins: §e" + String.join(", ", failed.stream()
.map(Plugin::getName)
.toList()));
}

public boolean enablePlugin(Plugin plugin, String parent) {
if (plugin.isEnabled()) return true;
String pluginName = plugin.getName();
if (plugin.isEnabled()) {
return true;
}

if (plugin.getDescription().getDepends() != null && !this.checkDependencies(plugin, parent)) {
return false;
}

try {
plugin.setEnabled(true);
} catch (Exception e) {
server.getLogger().error(e.getMessage());
return false;
}
return true;
}

private boolean checkDependencies(Plugin plugin, String parent) {
String pluginName = plugin.getName();
if (plugin.getDescription().getDepends() != null) {
for (String depend : plugin.getDescription().getDepends()) {
if (depend.equals(parent)) {
server.getLogger().warn("§cCan not enable plugin " + pluginName + " circular dependency " + parent + "!");
server.getLogger().warn("§cCannot enable plugin " + pluginName + ", circular dependency " + parent + "!");
return false;
}

Plugin dependPlugin = this.getPluginByName(depend);
if (dependPlugin == null) {
server.getLogger().warn("§cCan not enable plugin " + pluginName + " missing dependency " + depend + "!");
server.getLogger().warn("§cCannot enable plugin " + pluginName + ", missing dependency " + depend + "!");

return false;
}

Expand All @@ -184,13 +200,7 @@ public boolean enablePlugin(Plugin plugin, String parent) {
}
}
}

try {
plugin.setEnabled(true);
} catch (Exception e) {
server.getLogger().error(e.getMessage());
return false;
}

return true;
}

Expand All @@ -213,11 +223,14 @@ public Class<?> getClassFromCache(String className) {

for (PluginClassLoader loader : this.pluginClassLoaders.values()) {
try {
if ((clazz = loader.findClass(className, false)) != null) {

clazz = loader.findClass(className, false);
if (clazz != null) {
this.cachedClasses.put(className, clazz); // Cache the found class
return clazz;
}
} catch (ClassNotFoundException e) {
//ignore
// Ignore
}
}
return null;
Expand All @@ -242,8 +255,4 @@ public Collection<PluginClassLoader> getPluginClassLoaders() {
public Plugin getPluginByName(String pluginName) {
return this.pluginMap.getOrDefault(pluginName, null);
}

public Server getServer() {
return this.server;
}
}
Loading