mirror of
https://github.com/SKCraft/Launcher.git
synced 2025-02-26 03:21:51 +01:00
Basic plugin loading framework
This commit is contained in:
parent
8612b951e1
commit
be299164de
@ -14,27 +14,42 @@ import com.skcraft.launcher.creator.dialog.WelcomeDialog;
|
||||
import com.skcraft.launcher.creator.model.creator.CreatorConfig;
|
||||
import com.skcraft.launcher.creator.model.creator.RecentEntry;
|
||||
import com.skcraft.launcher.creator.model.creator.Workspace;
|
||||
import com.skcraft.launcher.creator.plugin.CreatorPluginLoader;
|
||||
import com.skcraft.launcher.creator.plugin.CreatorToolsPlugin;
|
||||
import com.skcraft.launcher.persistence.Persistence;
|
||||
import com.skcraft.launcher.swing.SwingHelper;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Log
|
||||
public class Creator {
|
||||
|
||||
@Getter private final File dataDir;
|
||||
@Getter private final CreatorConfig config;
|
||||
@Getter private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
|
||||
@Getter private final List<CreatorToolsPlugin> plugins;
|
||||
|
||||
public Creator() {
|
||||
this.dataDir = getAppDataDir();
|
||||
this.config = Persistence.load(new File(dataDir, "config.json"), CreatorConfig.class);
|
||||
|
||||
// Load plugins
|
||||
CreatorPluginLoader pluginLoader = new CreatorPluginLoader();
|
||||
try {
|
||||
pluginLoader.walk(new File(dataDir, "plugins"));
|
||||
} catch (IOException e) {
|
||||
log.severe("Could not walk plugin directory, plugins have not been loaded");
|
||||
}
|
||||
this.plugins = pluginLoader.loadAll();
|
||||
|
||||
// Remove deleted workspaces
|
||||
List<RecentEntry> recentEntries = config.getRecentEntries();
|
||||
Iterator<RecentEntry> it = recentEntries.iterator();
|
||||
|
@ -791,7 +791,8 @@ public class PackManagerController {
|
||||
|
||||
String version = generateVersionFromDate();
|
||||
|
||||
PackBuilder builder = new PackBuilder(pack, webRoot, version, "staging.json", false, false);
|
||||
PackBuilder builder = new PackBuilder(creator, pack, webRoot, version, "staging.json",
|
||||
false, false);
|
||||
InstanceList.Enumerator enumerator = launcher.getInstances().createEnumerator();
|
||||
TestLauncher instanceLauncher = new TestLauncher(launcher, frame, pack.getCachedConfig().getName(), session);
|
||||
|
||||
@ -816,7 +817,8 @@ public class PackManagerController {
|
||||
|
||||
if (options != null) {
|
||||
ConsoleFrame.showMessages();
|
||||
PackBuilder builder = new PackBuilder(pack, options.getDestDir(), options.getVersion(), options.getManifestFilename(), false, true);
|
||||
PackBuilder builder = new PackBuilder(creator, pack, options.getDestDir(), options.getVersion(),
|
||||
options.getManifestFilename(), false, true);
|
||||
Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(builder), executor)
|
||||
.handleAsync(result -> {
|
||||
ConsoleFrame.hideMessages();
|
||||
|
@ -6,11 +6,15 @@
|
||||
|
||||
package com.skcraft.launcher.creator.controller.task;
|
||||
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.skcraft.concurrency.ProgressObservable;
|
||||
import com.skcraft.launcher.LauncherException;
|
||||
import com.skcraft.launcher.LauncherUtils;
|
||||
import com.skcraft.launcher.builder.PackageBuilder;
|
||||
import com.skcraft.launcher.creator.Creator;
|
||||
import com.skcraft.launcher.creator.model.creator.Pack;
|
||||
import com.skcraft.launcher.creator.plugin.CreatorToolsPlugin;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -18,8 +22,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PackBuilder implements Callable<PackBuilder>, ProgressObservable {
|
||||
|
||||
private final Creator creator;
|
||||
private final Pack pack;
|
||||
private final File outputDir;
|
||||
private final String version;
|
||||
@ -27,15 +33,6 @@ public class PackBuilder implements Callable<PackBuilder>, ProgressObservable {
|
||||
private final boolean clean;
|
||||
private final boolean downloadUrls;
|
||||
|
||||
public PackBuilder(Pack pack, File outputDir, String version, String manifestFilename, boolean clean, boolean downloadUrls) {
|
||||
this.pack = pack;
|
||||
this.outputDir = outputDir;
|
||||
this.version = version;
|
||||
this.manifestFilename = manifestFilename;
|
||||
this.clean = clean;
|
||||
this.downloadUrls = downloadUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackBuilder call() throws Exception {
|
||||
if (clean) {
|
||||
@ -57,13 +54,21 @@ public class PackBuilder implements Callable<PackBuilder>, ProgressObservable {
|
||||
outputDir.mkdirs();
|
||||
|
||||
System.setProperty("com.skcraft.builder.ignoreURLOverrides", downloadUrls ? "false" : "true");
|
||||
String[] args = {
|
||||
List<String> args = Lists.newArrayList(
|
||||
"--version", version,
|
||||
"--manifest-dest", new File(outputDir, manifestFilename).getAbsolutePath(),
|
||||
"-i", pack.getDirectory().getAbsolutePath(),
|
||||
"-o", outputDir.getAbsolutePath()
|
||||
};
|
||||
PackageBuilder.main(args);
|
||||
);
|
||||
|
||||
for (CreatorToolsPlugin plugin : creator.getPlugins()) {
|
||||
if (plugin.getBuilderPlugin() != null) {
|
||||
args.add("--plugin");
|
||||
args.add(plugin.getBuilderPlugin().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
PackageBuilder.main(args.toArray(new String[0]));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.skcraft.launcher.creator.plugin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CreatorPluginInfo {
|
||||
private String id;
|
||||
private String pluginClass;
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.skcraft.launcher.creator.plugin;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.skcraft.launcher.builder.BuilderUtils;
|
||||
import com.skcraft.launcher.builder.DirectoryWalker;
|
||||
import lombok.Data;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
@Log
|
||||
public class CreatorPluginLoader extends DirectoryWalker {
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
private List<PluginCandidate> candidates = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onFile(File file, String relPath) throws IOException {
|
||||
JarFile jarFile = new JarFile(file);
|
||||
|
||||
ZipEntry metaEntry = jarFile.getEntry("skcraftcreator.plugin.json");
|
||||
if (metaEntry != null) {
|
||||
InputStreamReader reader = new InputStreamReader(jarFile.getInputStream(metaEntry));
|
||||
CreatorPluginInfo pluginInfo = mapper.readValue(BuilderUtils.readStringFromStream(reader),
|
||||
CreatorPluginInfo.class);
|
||||
|
||||
log.info("Found plugin " + pluginInfo.getId());
|
||||
candidates.add(new PluginCandidate(pluginInfo, file.toURI().toURL()));
|
||||
}
|
||||
}
|
||||
|
||||
public List<CreatorToolsPlugin> loadAll() {
|
||||
URLClassLoader pluginClassLoader = new URLClassLoader(
|
||||
candidates.stream().map(PluginCandidate::getJarUrl).toArray(URL[]::new),
|
||||
this.getClass().getClassLoader()
|
||||
);
|
||||
|
||||
return candidates.stream()
|
||||
.map(candidate -> loadPlugin(pluginClassLoader, candidate))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private <T extends CreatorToolsPlugin> CreatorToolsPlugin loadPlugin(URLClassLoader classLoader, PluginCandidate candidate) {
|
||||
try {
|
||||
Class<T> pluginClass = (Class<T>) classLoader.loadClass(candidate.getInfo().getPluginClass());
|
||||
|
||||
return pluginClass.getConstructor().newInstance();
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.warning(candidate.format("Could not find plugin class %s for plugin %s"));
|
||||
} catch (ClassCastException e) {
|
||||
log.warning(candidate.format("Plugin main class %s (from plugin '%s') does not extend CreatorToolsPlugin!"));
|
||||
} catch (NoSuchMethodException e) {
|
||||
log.warning(candidate.format("Could not find constructor for class %s (of plugin '%s')!"));
|
||||
} catch (InstantiationException e) {
|
||||
log.warning(candidate.format("Could not instantiate class %s (from plugin '%s')!"));
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
log.warning(candidate.format("Error while initializing main class %s (from plugin '%s')!"));
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
log.warning(candidate.format("Could not access constructor for class %s (of plugin '%s')!"));
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class PluginCandidate {
|
||||
private final CreatorPluginInfo info;
|
||||
private final URL jarUrl;
|
||||
|
||||
public String format(String format) {
|
||||
return String.format(format, info.getPluginClass(), info.getId());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.skcraft.launcher.creator.plugin;
|
||||
|
||||
import com.skcraft.launcher.builder.plugin.BuilderPlugin;
|
||||
|
||||
public abstract class CreatorToolsPlugin {
|
||||
public Class<? extends BuilderPlugin> getBuilderPlugin() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import com.beust.jcommander.ParameterException;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BuilderOptions {
|
||||
@ -67,6 +68,10 @@ public class BuilderOptions {
|
||||
@Parameter(names = "--pretty-print")
|
||||
private boolean prettyPrinting;
|
||||
|
||||
// Plugins
|
||||
@Parameter(names = "--plugin-class")
|
||||
private List<String> pluginClasses;
|
||||
|
||||
public void choosePaths() throws ParameterException {
|
||||
if (configPath == null) {
|
||||
requireInputPath("--config");
|
||||
|
@ -20,6 +20,8 @@ import com.google.common.io.Files;
|
||||
import com.skcraft.launcher.Launcher;
|
||||
import com.skcraft.launcher.LauncherUtils;
|
||||
import com.skcraft.launcher.builder.loaders.*;
|
||||
import com.skcraft.launcher.builder.plugin.Builder;
|
||||
import com.skcraft.launcher.builder.plugin.BuilderPluginLoader;
|
||||
import com.skcraft.launcher.model.loader.BasicInstallProfile;
|
||||
import com.skcraft.launcher.model.minecraft.Library;
|
||||
import com.skcraft.launcher.model.minecraft.ReleaseList;
|
||||
@ -51,10 +53,11 @@ import static com.skcraft.launcher.util.HttpRequest.url;
|
||||
* Builds packages for the launcher.
|
||||
*/
|
||||
@Log
|
||||
public class PackageBuilder {
|
||||
public class PackageBuilder implements Builder {
|
||||
private final Properties properties;
|
||||
private final ObjectMapper mapper;
|
||||
private ObjectWriter writer;
|
||||
@Getter
|
||||
private final Manifest manifest;
|
||||
private final PropertiesApplicator applicator;
|
||||
@Getter
|
||||
@ -409,6 +412,11 @@ public class PackageBuilder {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
|
||||
|
||||
// Load plugins
|
||||
BuilderPluginLoader pluginLoader = new BuilderPluginLoader();
|
||||
pluginLoader.loadClasses(options.getPluginClasses());
|
||||
pluginLoader.dispatchAcceptOptions(args);
|
||||
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.setMinimumVersion(Manifest.MIN_PROTOCOL_VERSION);
|
||||
PackageBuilder builder = new PackageBuilder(mapper, manifest);
|
||||
@ -427,10 +435,15 @@ public class PackageBuilder {
|
||||
manifest.setLibrariesLocation(options.getLibrariesLocation());
|
||||
manifest.setObjectsLocation(options.getObjectsLocation());
|
||||
|
||||
pluginLoader.dispatchManifestCreated(manifest);
|
||||
|
||||
builder.scan(options.getFilesDir());
|
||||
builder.addFiles(options.getFilesDir(), options.getObjectsDir());
|
||||
builder.addLoaders(options.getLoadersDir(), options.getLibrariesDir());
|
||||
builder.downloadLibraries(options.getLibrariesDir());
|
||||
|
||||
pluginLoader.dispatchBuilderFinished(builder);
|
||||
|
||||
builder.writeManifest(options.getManifestPath());
|
||||
|
||||
logSection("Done");
|
||||
|
@ -0,0 +1,10 @@
|
||||
package com.skcraft.launcher.builder.plugin;
|
||||
|
||||
import com.skcraft.launcher.model.modpack.Manifest;
|
||||
|
||||
/**
|
||||
* Builder passed to plugins to allow restricted access
|
||||
*/
|
||||
public interface Builder {
|
||||
Manifest getManifest();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.skcraft.launcher.builder.plugin;
|
||||
|
||||
import com.skcraft.launcher.model.modpack.Manifest;
|
||||
|
||||
/**
|
||||
* Class that all builder plugins should extend.
|
||||
*/
|
||||
public abstract class BuilderPlugin {
|
||||
public void acceptOptions(String[] args) {}
|
||||
public void onManifestCreated(Manifest manifest) {}
|
||||
public void onBuilderFinished(Builder builder) {}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.skcraft.launcher.builder.plugin;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.skcraft.launcher.model.modpack.Manifest;
|
||||
import lombok.extern.java.Log;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
@Log
|
||||
public class BuilderPluginLoader {
|
||||
private List<BuilderPlugin> loadedPlugins = Lists.newArrayList();
|
||||
|
||||
public void loadClasses(List<String> classNames) {
|
||||
for (String pluginClassName : classNames) {
|
||||
try {
|
||||
BuilderPlugin plugin = loadClass(pluginClassName);
|
||||
|
||||
if (plugin != null) {
|
||||
loadedPlugins.add(plugin);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.severe("Failed to load plugin " + pluginClassName + ", is it on the classpath?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends BuilderPlugin> T loadClass(String pluginClassName) throws ClassNotFoundException {
|
||||
try {
|
||||
Class<T> pluginClass = (Class<T>) getClass().getClassLoader().loadClass(pluginClassName);
|
||||
|
||||
return pluginClass.getConstructor().newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
log.warning("Plugin class " + pluginClassName + " could not be created!");
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
log.warning("Plugin class " + pluginClassName + " could not be accessed!");
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
log.warning("Plugin class " + pluginClassName + " threw an error while initializing!");
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
log.warning("Plugin class " + pluginClassName + " is missing a primary constructor!");
|
||||
} catch (ClassCastException e) {
|
||||
log.warning("Plugin class" + pluginClassName + " does not extend BuilderPlugin!");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void dispatchAcceptOptions(String[] args) {
|
||||
for (BuilderPlugin plugin : loadedPlugins) {
|
||||
plugin.acceptOptions(args);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchManifestCreated(Manifest manifest) {
|
||||
for (BuilderPlugin plugin : loadedPlugins) {
|
||||
plugin.onManifestCreated(manifest);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchBuilderFinished(Builder builder) {
|
||||
for (BuilderPlugin plugin : loadedPlugins) {
|
||||
plugin.onBuilderFinished(builder);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user