2020-10-24 22:57:38 +02:00
|
|
|
package net.minestom.server.extensions;
|
|
|
|
|
2021-03-24 17:16:29 +01:00
|
|
|
import net.minestom.server.MinecraftServer;
|
2021-03-24 13:39:47 +01:00
|
|
|
import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader;
|
2021-03-24 17:16:29 +01:00
|
|
|
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
2020-10-24 22:57:38 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2020-11-03 21:26:46 +01:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
2020-11-18 09:24:59 +01:00
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
2020-10-24 22:57:38 +02:00
|
|
|
|
|
|
|
import java.io.File;
|
2020-10-25 19:58:19 +01:00
|
|
|
import java.net.URL;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2020-10-24 22:57:38 +02:00
|
|
|
|
2021-03-24 16:23:56 +01:00
|
|
|
/**
|
|
|
|
* Represents an extension from an `extension.json` that is capable of powering an Extension object.
|
|
|
|
*
|
|
|
|
* This has no constructor as its properties are set via GSON.
|
|
|
|
*/
|
2021-03-23 16:35:52 +01:00
|
|
|
public final class DiscoveredExtension {
|
2020-11-18 09:24:59 +01:00
|
|
|
|
2021-03-24 16:23:56 +01:00
|
|
|
/** Static logger for this class. */
|
|
|
|
public static final Logger LOGGER = LoggerFactory.getLogger(DiscoveredExtension.class);
|
2020-11-18 09:24:59 +01:00
|
|
|
|
2021-03-24 16:23:56 +01:00
|
|
|
/** The regex that this name must pass. If it doesn't, it will not be accepted. */
|
2020-10-25 10:41:51 +01:00
|
|
|
public static final String NAME_REGEX = "[A-Za-z][_A-Za-z0-9]+";
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** Name of the DiscoveredExtension. Unique for all extensions. */
|
2020-10-24 22:57:38 +02:00
|
|
|
private String name;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** Main class of this DiscoveredExtension, must extend Extension. */
|
2020-10-24 22:57:38 +02:00
|
|
|
private String entrypoint;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** Version of this extension, highly reccomended to set it. */
|
2020-10-25 10:41:51 +01:00
|
|
|
private String version;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** Points to sponge mixin config in resources folder. */
|
2020-10-25 10:41:51 +01:00
|
|
|
private String mixinConfig;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** People who have made this extension. */
|
2020-10-25 10:41:51 +01:00
|
|
|
private String[] authors;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** All code modifiers (the classes they point to) */
|
2020-10-25 10:41:51 +01:00
|
|
|
private String[] codeModifiers;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** List of extension names that this depends on. */
|
2020-10-25 16:45:28 +01:00
|
|
|
private String[] dependencies;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** List of Repositories and URLs that this depends on. */
|
2020-10-25 16:45:28 +01:00
|
|
|
private ExternalDependencies externalDependencies;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** A list of any missing code modifiers to be used for logging. */
|
2021-03-23 16:35:52 +01:00
|
|
|
private final List<String> missingCodeModifiers = new LinkedList<>();
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** If this extension couldn't load its mixin configuration. */
|
2021-02-04 19:11:43 +01:00
|
|
|
private boolean failedToLoadMixin = false;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** All files of this extension */
|
2020-10-25 19:58:19 +01:00
|
|
|
transient List<URL> files = new LinkedList<>();
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** The load status of this extension -- LOAD_SUCCESS is the only good one. */
|
2020-10-25 10:41:51 +01:00
|
|
|
transient LoadStatus loadStatus = LoadStatus.LOAD_SUCCESS;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** The original jar this is from. */
|
2020-11-03 21:26:46 +01:00
|
|
|
transient private File originalJar;
|
2021-03-24 16:23:56 +01:00
|
|
|
|
|
|
|
/** The class loader that powers it. */
|
2021-03-24 13:39:47 +01:00
|
|
|
transient private MinestomExtensionClassLoader minestomExtensionClassLoader;
|
2020-10-24 22:57:38 +02:00
|
|
|
|
|
|
|
@NotNull
|
|
|
|
public String getName() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
@NotNull
|
2020-10-25 16:45:28 +01:00
|
|
|
public String getEntrypoint() {
|
|
|
|
return entrypoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
@NotNull
|
|
|
|
public String getVersion() {
|
|
|
|
return version;
|
2020-10-24 22:57:38 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
@NotNull
|
2020-10-24 22:57:38 +02:00
|
|
|
public String getMixinConfig() {
|
|
|
|
return mixinConfig;
|
|
|
|
}
|
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
@NotNull
|
2020-10-24 22:57:38 +02:00
|
|
|
public String[] getAuthors() {
|
|
|
|
return authors;
|
|
|
|
}
|
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
@NotNull
|
2020-10-25 16:45:28 +01:00
|
|
|
public String[] getCodeModifiers() {
|
|
|
|
if (codeModifiers == null) {
|
|
|
|
codeModifiers = new String[0];
|
|
|
|
}
|
|
|
|
return codeModifiers;
|
2020-10-24 22:57:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@NotNull
|
2020-10-25 16:45:28 +01:00
|
|
|
public String[] getDependencies() {
|
|
|
|
return dependencies;
|
2020-10-24 22:57:38 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
@NotNull
|
2020-10-25 16:45:28 +01:00
|
|
|
public ExternalDependencies getExternalDependencies() {
|
|
|
|
return externalDependencies;
|
2020-10-24 22:57:38 +02:00
|
|
|
}
|
2020-10-25 10:41:51 +01:00
|
|
|
|
2021-03-24 16:23:56 +01:00
|
|
|
public void setOriginalJar(@Nullable File file) {
|
2020-11-03 21:26:46 +01:00
|
|
|
originalJar = file;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Nullable
|
2021-03-23 16:35:52 +01:00
|
|
|
public File getOriginalJar() {
|
2020-11-03 21:26:46 +01:00
|
|
|
return originalJar;
|
|
|
|
}
|
|
|
|
|
2021-03-24 20:55:48 +01:00
|
|
|
MinestomExtensionClassLoader removeMinestomExtensionClassLoader() {
|
2021-03-24 13:39:47 +01:00
|
|
|
MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader();
|
|
|
|
setMinestomExtensionClassLoader(null);
|
|
|
|
return oldClassLoader;
|
|
|
|
}
|
|
|
|
|
2021-03-24 20:55:48 +01:00
|
|
|
void setMinestomExtensionClassLoader(@Nullable MinestomExtensionClassLoader minestomExtensionClassLoader) {
|
2021-03-24 13:39:47 +01:00
|
|
|
this.minestomExtensionClassLoader = minestomExtensionClassLoader;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
public MinestomExtensionClassLoader getMinestomExtensionClassLoader() {
|
|
|
|
return this.minestomExtensionClassLoader;
|
|
|
|
}
|
|
|
|
|
2021-03-24 16:23:56 +01:00
|
|
|
/**
|
|
|
|
* Ensures that all properties of this extension are properly set if they aren't
|
|
|
|
*
|
|
|
|
* @param extension The extension to verify
|
|
|
|
*/
|
|
|
|
public static void verifyIntegrity(@NotNull DiscoveredExtension extension) {
|
2020-10-25 10:41:51 +01:00
|
|
|
if (extension.name == null) {
|
|
|
|
StringBuilder fileList = new StringBuilder();
|
2020-10-25 19:58:19 +01:00
|
|
|
for (URL f : extension.files) {
|
|
|
|
fileList.append(f.toExternalForm()).append(", ");
|
2020-10-25 10:41:51 +01:00
|
|
|
}
|
2020-11-18 09:24:59 +01:00
|
|
|
LOGGER.error("Extension with no name. (at {}})", fileList);
|
|
|
|
LOGGER.error("Extension at ({}) will not be loaded.", fileList);
|
2020-10-25 10:41:51 +01:00
|
|
|
extension.loadStatus = DiscoveredExtension.LoadStatus.INVALID_NAME;
|
|
|
|
|
|
|
|
// To ensure @NotNull: name = INVALID_NAME
|
|
|
|
extension.name = extension.loadStatus.name();
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
if (!extension.name.matches(NAME_REGEX)) {
|
2020-11-18 09:24:59 +01:00
|
|
|
LOGGER.error("Extension '{}' specified an invalid name.", extension.name);
|
|
|
|
LOGGER.error("Extension '{}' will not be loaded.", extension.name);
|
2020-10-25 10:41:51 +01:00
|
|
|
extension.loadStatus = DiscoveredExtension.LoadStatus.INVALID_NAME;
|
|
|
|
|
|
|
|
// To ensure @NotNull: name = INVALID_NAME
|
|
|
|
extension.name = extension.loadStatus.name();
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
if (extension.entrypoint == null) {
|
2020-11-18 09:24:59 +01:00
|
|
|
LOGGER.error("Extension '{}' did not specify an entry point (via 'entrypoint').", extension.name);
|
|
|
|
LOGGER.error("Extension '{}' will not be loaded.", extension.name);
|
2020-10-25 10:41:51 +01:00
|
|
|
extension.loadStatus = DiscoveredExtension.LoadStatus.NO_ENTRYPOINT;
|
|
|
|
|
|
|
|
// To ensure @NotNull: entrypoint = NO_ENTRYPOINT
|
|
|
|
extension.entrypoint = extension.loadStatus.name();
|
|
|
|
return;
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
// Handle defaults
|
|
|
|
// If we reach this code, then the extension will most likely be loaded:
|
|
|
|
if (extension.version == null) {
|
2020-11-18 09:24:59 +01:00
|
|
|
LOGGER.warn("Extension '{}' did not specify a version.", extension.name);
|
|
|
|
LOGGER.warn("Extension '{}' will continue to load but should specify a plugin version.", extension.name);
|
2020-10-25 10:41:51 +01:00
|
|
|
extension.version = "Unspecified";
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
if (extension.mixinConfig == null) {
|
|
|
|
extension.mixinConfig = "";
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
if (extension.authors == null) {
|
|
|
|
extension.authors = new String[0];
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
if (extension.codeModifiers == null) {
|
|
|
|
extension.codeModifiers = new String[0];
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 10:41:51 +01:00
|
|
|
// No dependencies were specified
|
|
|
|
if (extension.dependencies == null) {
|
2020-10-25 16:45:28 +01:00
|
|
|
extension.dependencies = new String[0];
|
|
|
|
}
|
2021-03-23 16:50:42 +01:00
|
|
|
|
2020-10-25 16:45:28 +01:00
|
|
|
// No external dependencies were specified;
|
|
|
|
if (extension.externalDependencies == null) {
|
|
|
|
extension.externalDependencies = new ExternalDependencies();
|
2020-10-25 10:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-02-04 19:11:43 +01:00
|
|
|
public void addMissingCodeModifier(String codeModifierClass) {
|
|
|
|
missingCodeModifiers.add(codeModifierClass);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setFailedToLoadMixinFlag() {
|
|
|
|
failedToLoadMixin = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<String> getMissingCodeModifiers() {
|
|
|
|
return missingCodeModifiers;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasFailedToLoadMixin() {
|
|
|
|
return failedToLoadMixin;
|
|
|
|
}
|
|
|
|
|
2021-03-24 17:16:29 +01:00
|
|
|
public MinestomExtensionClassLoader makeClassLoader() {
|
|
|
|
final URL[] urls = this.files.toArray(new URL[0]);
|
|
|
|
|
|
|
|
MinestomRootClassLoader root = MinestomRootClassLoader.getInstance();
|
|
|
|
|
|
|
|
MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(this.getName(), this.getEntrypoint(), urls, root);
|
|
|
|
|
2021-03-27 00:32:18 +01:00
|
|
|
if (this.getDependencies().length == 0 || MinecraftServer.getExtensionManager() == null) {
|
2021-03-24 17:16:29 +01:00
|
|
|
// orphaned extension, we can insert it directly
|
|
|
|
root.addChild(loader);
|
|
|
|
} else {
|
|
|
|
// add children to the dependencies
|
|
|
|
for (String dependency : this.getDependencies()) {
|
|
|
|
if (MinecraftServer.getExtensionManager().hasExtension(dependency.toLowerCase())) {
|
|
|
|
MinestomExtensionClassLoader parentLoader = MinecraftServer.getExtensionManager().getExtension(dependency.toLowerCase()).getOrigin().getMinestomExtensionClassLoader();
|
|
|
|
|
|
|
|
// TODO should never happen but replace with better throws error.
|
|
|
|
assert parentLoader != null;
|
|
|
|
|
|
|
|
parentLoader.addChild(loader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return loader;
|
|
|
|
}
|
|
|
|
|
2021-03-24 16:23:56 +01:00
|
|
|
/**
|
|
|
|
* The status this extension has, all are breakpoints.
|
|
|
|
*
|
|
|
|
* LOAD_SUCCESS is the only valid one.
|
|
|
|
*/
|
2020-10-25 10:41:51 +01:00
|
|
|
enum LoadStatus {
|
|
|
|
LOAD_SUCCESS("Actually, it did not fail. This message should not have been printed."),
|
|
|
|
MISSING_DEPENDENCIES("Missing dependencies, check your logs."),
|
|
|
|
INVALID_NAME("Invalid name."),
|
|
|
|
NO_ENTRYPOINT("No entrypoint specified."),
|
2020-11-03 10:26:31 +01:00
|
|
|
FAILED_TO_SETUP_CLASSLOADER("Extension classloader could not be setup."),
|
|
|
|
LOAD_FAILED("Load failed. See logs for more information."),
|
2020-10-25 10:41:51 +01:00
|
|
|
;
|
|
|
|
|
|
|
|
private final String message;
|
|
|
|
|
|
|
|
LoadStatus(@NotNull String message) {
|
|
|
|
this.message = message;
|
|
|
|
}
|
|
|
|
|
|
|
|
@NotNull
|
|
|
|
public String getMessage() {
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:50:42 +01:00
|
|
|
public static final class ExternalDependencies {
|
2020-10-25 10:41:51 +01:00
|
|
|
Repository[] repositories = new Repository[0];
|
|
|
|
String[] artifacts = new String[0];
|
|
|
|
|
2021-03-23 16:50:42 +01:00
|
|
|
public static class Repository {
|
2020-10-25 10:41:51 +01:00
|
|
|
String name = "";
|
|
|
|
String url = "";
|
|
|
|
}
|
|
|
|
}
|
2020-10-24 22:57:38 +02:00
|
|
|
}
|