mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-09 09:57:45 +01:00
Cleanup, renaming, and docs
This commit is contained in:
parent
5e92e04c29
commit
faa53b3d48
@ -74,10 +74,41 @@ public class ExtensionManager {
|
||||
this.loadOnStartup = loadOnStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all extensions in the extension folder into this server.
|
||||
*
|
||||
* Pipeline:
|
||||
*
|
||||
* Finds all .jar files in the extensions folder.
|
||||
*
|
||||
* Per each jar:
|
||||
* Turns its extension.json into a DiscoveredExtension object.
|
||||
* Verifies that all properties of extension.json are correctly set.
|
||||
*
|
||||
* It then sorts all those jars depending on its load order (extensions can have dependencies)
|
||||
*
|
||||
* Afterwards, it loads all external dependencies (sort of an outsourced dynamic shadowJar)
|
||||
*
|
||||
* Then removes any invalid extensions (Invalid being its Load Status isn't SUCCESS)
|
||||
*
|
||||
* After that, it set its classloaders so each extension is self-contained,
|
||||
*
|
||||
* Removes invalid extensions again,
|
||||
*
|
||||
* and loads all of those extensions into Minestom
|
||||
* (Extension fields are set via reflection after each extension is verified, then loaded.)
|
||||
*
|
||||
* If the extension successfully loads, add it to the global extension Map (Name to Extension)
|
||||
*
|
||||
* Then makes a scheduler to clean observers per extension.
|
||||
*
|
||||
*/
|
||||
public void loadExtensions() {
|
||||
Check.stateCondition(loaded, "Extensions are already loaded!");
|
||||
this.loaded = true;
|
||||
|
||||
// Initialize folders
|
||||
{
|
||||
// Make extensions folder if necessary
|
||||
if (!extensionFolder.exists()) {
|
||||
if (!extensionFolder.mkdirs()) {
|
||||
@ -93,13 +124,19 @@ public class ExtensionManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load extensions
|
||||
{
|
||||
// Get all extensions and order them accordingly.
|
||||
List<DiscoveredExtension> discoveredExtensions = discoverExtensions();
|
||||
discoveredExtensions = generateLoadOrder(discoveredExtensions);
|
||||
loadDependencies(discoveredExtensions);
|
||||
|
||||
// remove invalid extensions
|
||||
discoveredExtensions.removeIf(ext -> ext.loadStatus != DiscoveredExtension.LoadStatus.LOAD_SUCCESS);
|
||||
|
||||
// set class loaders for all extensions.
|
||||
for (DiscoveredExtension discoveredExtension : discoveredExtensions) {
|
||||
try {
|
||||
setupClassLoader(discoveredExtension);
|
||||
@ -115,15 +152,17 @@ public class ExtensionManager {
|
||||
discoveredExtensions.removeIf(ext -> ext.loadStatus != DiscoveredExtension.LoadStatus.LOAD_SUCCESS);
|
||||
setupCodeModifiers(discoveredExtensions);
|
||||
|
||||
// Load the extensions
|
||||
for (DiscoveredExtension discoveredExtension : discoveredExtensions) {
|
||||
try {
|
||||
attemptSingleLoad(discoveredExtension);
|
||||
loadExtension(discoveredExtension);
|
||||
} catch (Exception e) {
|
||||
discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.LOAD_FAILED;
|
||||
LOGGER.error("Failed to load extension {}", discoveredExtension.getName());
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// periodically cleanup observers
|
||||
MinecraftServer.getSchedulerManager().buildTask(() -> {
|
||||
@ -133,6 +172,11 @@ public class ExtensionManager {
|
||||
}).repeat(1L, TimeUnit.MINUTE).schedule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the class loader for this extension
|
||||
*
|
||||
* @param discoveredExtension The extension to initialize said extension's class loader.
|
||||
*/
|
||||
private void setupClassLoader(@NotNull DiscoveredExtension discoveredExtension) {
|
||||
final URL[] urls = discoveredExtension.files.toArray(new URL[0]);
|
||||
final MinestomExtensionClassLoader loader = newClassLoader(discoveredExtension, urls);
|
||||
@ -140,8 +184,15 @@ public class ExtensionManager {
|
||||
discoveredExtension.setMinestomExtensionClassLoader(loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an extension into Minestom.
|
||||
*
|
||||
* @param discoveredExtension The extension. Make sure to verify its integrity, set its class loader, and its files.
|
||||
*
|
||||
* @return An extension object made from this DiscoveredExtension
|
||||
*/
|
||||
@Nullable
|
||||
private Extension attemptSingleLoad(@NotNull DiscoveredExtension discoveredExtension) {
|
||||
private Extension loadExtension(@NotNull DiscoveredExtension discoveredExtension) {
|
||||
// Create ExtensionDescription (authors, version etc.)
|
||||
String extensionName = discoveredExtension.getName();
|
||||
String mainClass = discoveredExtension.getEntrypoint();
|
||||
@ -237,6 +288,13 @@ public class ExtensionManager {
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all extensions from the extensions folder and make them discovered.
|
||||
*
|
||||
* It skims the extension folder, discovers and verifies each extension, and returns those created DiscoveredExtensions.
|
||||
*
|
||||
* @return A list of discovered extensions from this folder.
|
||||
*/
|
||||
@NotNull
|
||||
private List<DiscoveredExtension> discoverExtensions() {
|
||||
List<DiscoveredExtension> extensions = new LinkedList<>();
|
||||
@ -287,11 +345,19 @@ public class ExtensionManager {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs a discovered extension from a jar.
|
||||
*
|
||||
* @param file The jar to grab it from (a .jar is a formatted .zip file)
|
||||
*
|
||||
* @return The created DiscoveredExtension.
|
||||
*/
|
||||
@Nullable
|
||||
private DiscoveredExtension discoverFromJar(@NotNull File file) {
|
||||
try (ZipFile f = new ZipFile(file);
|
||||
InputStreamReader reader = new InputStreamReader(f.getInputStream(f.getEntry("extension.json")))) {
|
||||
|
||||
// Initialize DiscoveredExtension from GSON.
|
||||
DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class);
|
||||
extension.setOriginalJar(file);
|
||||
extension.files.add(file.toURI().toURL());
|
||||
@ -372,7 +438,7 @@ public class ExtensionManager {
|
||||
|
||||
// While there are entries with no more elements (no more dependencies)
|
||||
while (!(
|
||||
loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> areAllDependenciesLoaded(entry.getValue())).collect(Collectors.toList())
|
||||
loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> isLoaded(entry.getValue())).collect(Collectors.toList())
|
||||
).isEmpty()
|
||||
) {
|
||||
// Get all "loadable" (not actually being loaded!) extensions and put them in the sorted list.
|
||||
@ -408,8 +474,16 @@ public class ExtensionManager {
|
||||
return sortedList;
|
||||
}
|
||||
|
||||
private boolean areAllDependenciesLoaded(@NotNull List<DiscoveredExtension> dependencies) {
|
||||
return dependencies.isEmpty() || dependencies.stream().allMatch(ext -> extensions.containsKey(ext.getName().toLowerCase()));
|
||||
/**
|
||||
* Checks if this list of extensions are loaded
|
||||
* @param extensions The list of extensions to check against.
|
||||
* @return If all of these extensions are loaded.
|
||||
*/
|
||||
private boolean isLoaded(@NotNull List<DiscoveredExtension> extensions) {
|
||||
return
|
||||
extensions.isEmpty() // Don't waste CPU on checking an empty array
|
||||
// Make sure the internal extensions list contains all of these.
|
||||
|| extensions.stream().allMatch(ext -> this.extensions.containsKey(ext.getName().toLowerCase()));
|
||||
}
|
||||
|
||||
private void loadDependencies(List<DiscoveredExtension> extensions) {
|
||||
@ -699,7 +773,7 @@ public class ExtensionManager {
|
||||
for (DiscoveredExtension toReload : extensionsToLoad) {
|
||||
// reload extensions
|
||||
LOGGER.info("Actually load extension {}", toReload.getName());
|
||||
Extension loadedExtension = attemptSingleLoad(toReload);
|
||||
Extension loadedExtension = loadExtension(toReload);
|
||||
if (loadedExtension != null) {
|
||||
newExtensions.add(loadedExtension);
|
||||
}
|
||||
@ -720,9 +794,11 @@ public class ExtensionManager {
|
||||
|
||||
public void unloadExtension(String extensionName) {
|
||||
Extension ext = extensions.get(extensionName.toLowerCase());
|
||||
|
||||
if (ext == null) {
|
||||
throw new IllegalArgumentException("Extension " + extensionName + " is not currently loaded.");
|
||||
}
|
||||
|
||||
List<String> dependents = new LinkedList<>(ext.getDependents()); // copy dependents list
|
||||
|
||||
for (String dependentID : dependents) {
|
||||
|
Loading…
Reference in New Issue
Block a user