documentation:fabric_loader
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
documentation:fabric_loader [2019/06/07 14:33] – [Entrypoints] Primarily clear up the first paragraph jamieswhiteshirt | documentation:fabric_loader [2023/12/27 12:57] (current) – ↷ Links adapted because of a move operation 194.190.81.32 | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Fabric Loader ====== | ====== Fabric Loader ====== | ||
- | Fabric Loader is Fabric' | + | Fabric Loader is Fabric' |
Fabric Loader has services to allow mods to have some code executed during initialization, | Fabric Loader has services to allow mods to have some code executed during initialization, | ||
- | ==== Mods ==== | + | For each Fabric Loader version, there is Javadoc available at |
+ | < | ||
+ | For example, Fabric Loader 0.11.1' | ||
- | A mod is a jar with a [[documentation: | + | ===== Features ===== |
- | Mods are loaded | + | ==== Mods ==== |
+ | |||
+ | A mod is a jar with a [[documentation: | ||
Fabric Loader makes all mods equally capable of modifying the game. As an example, anything Fabric API does can be done by any other mod. | Fabric Loader makes all mods equally capable of modifying the game. As an example, anything Fabric API does can be done by any other mod. | ||
- | ==== Nested jars ==== | + | Mods are loaded both from the classpath and from the mods directory. They are expected to match the mappings in the current environment, |
- | A mod may bundle a number of other mods within its jar. See the [[https:// | + | ==== Nested |
- | Nested | + | Nested |
- | When loaded, nested jars are loaded | + | Nested JARs are not extracted, they are instead |
==== Entrypoints ==== | ==== Entrypoints ==== | ||
- | Entrypoints are a way for mods to expose code objects such as classes, fields and methods for usage by Fabric Loader or other mods. Fabric Loader uses entrypoints for mod initialization, | ||
- | An entrypoints | + | Fabric Loader has an [[documentation: |
- | Fabric Loader has three built-in entrypoint types for mod initialization in relation to physical sides (see [[tutorial: | + | ==== Mixin ==== |
- | * **main**: The first entrypoint | + | Mixin allows mods to transform Minecraft classes |
- | * **client**: Will run after main only in a physical client. Expects | + | |
- | * **server**: Will run after main only in a physical server. Expects | + | |
- | The default language adapter is designed | + | Mixin was not specifically made for Fabric, so Fabric Loader uses a slightly modified version of Mixin. However, the documentation |
- | * **Class**: '' | + | ==== Mappings ==== |
- | * **Method**: '' | + | |
- | * **Field**: '' | + | |
- | References | + | Fabric Loader provides an API to determine names of classes, fields |
- | Language adapters for other languages can be implemented by mods. [[https:// | + | ===== Fabric Loader internals ===== |
- | + | ||
- | ==== Mixin ==== | + | |
- | + | ||
- | Mixin allows mods to transform Minecraft classes and even mod classes, and is the only method of class transformation that Fabric Loader allows. A mod can declare its own mixin configuration which enables the use of Mixin. Fabric Loader uses a slightly modified version of Mixin, but the documentation of the unmodified version is still mostly valid. The modifications are mostly related to making it work without LegacyLauncher/ | + | |
==== Deobfuscation ==== | ==== Deobfuscation ==== | ||
When launched in a non-development environment, | When launched in a non-development environment, | ||
- | |||
- | ==== Mappings ==== | ||
- | |||
- | Fabric Loader provides an API to determine names of classes, fields and methods with respect to the different environments that mods may be loaded in. This can be used to support reflection in any environment provided Fabric Loader has access to mappings to resolve the name. | ||
==== Class loading and transformation ==== | ==== Class loading and transformation ==== | ||
Line 64: | Line 55: | ||
==== Launchers ==== | ==== Launchers ==== | ||
- | Knot is the default launcher included in Fabric Loader designed specifically for Fabric Loader' | + | A launcher (not to be confused with the game launcher) is something provides a method to use Fabric Loader in a Java process. A launcher must provide a few features to support Fabric Loader' |
+ | |||
+ | Knot is the default launcher included in Fabric Loader, designed specifically for Fabric Loader' | ||
When launching a server using Knot in a production environment, | When launching a server using Knot in a production environment, | ||
- | Fabric Loader can also be launched with LegacyLauncher/ | + | Fabric Loader can also be launched with LegacyLauncher/ |
+ | |||
+ | ===== Integrating Fabric Loader into a dedicated application ===== | ||
+ | |||
+ | As stated above, Fabric Loader can be used outside of Minecraft to add mod loading for Java applications. This section will briefly summarize what do you need to do to integrate Fabric loader to your Java game/ | ||
+ | |||
+ | ==== Gradle dependencies ==== | ||
+ | |||
+ | First, you'll need to add Fabric Loader as a dependency, and its dependencies in you '' | ||
+ | |||
+ | <code groovy> | ||
+ | repositories { | ||
+ | // your other repositories... | ||
+ | |||
+ | maven { | ||
+ | url " | ||
+ | } | ||
+ | maven { | ||
+ | url " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | dependencies { | ||
+ | // your dependencies... | ||
+ | |||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | implementation " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Launching your app with Fabric Loader ==== | ||
+ | |||
+ | If you've launched your app before through your own '' | ||
+ | |||
+ | ==== Setting up GameProvider ==== | ||
+ | |||
+ | **GameProvider** is an interface that lets Fabric Loader locate your game, configure Fabric launcher, and then finally launch the game. For Fabric Loader to instantiate your implementation of **GameProvider**, | ||
+ | |||
+ | < | ||
+ | net.developer.app.fabric.AppGameProvider | ||
+ | </ | ||
+ | |||
+ | This should allow Fabric Loader find your **GameProvider**. Now let's breakdown **GameProvider** interface. | ||
+ | |||
+ | === Implementing GameProvider === | ||
+ | |||
+ | For your game actually to launch, you need to carefully implement **GameProvider** in a specific way. Here is a breakdown of each method does: | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | An example **GameProvider** would look like this: | ||
+ | |||
+ | <code java> | ||
+ | public class AppGameProvider implements GameProvider | ||
+ | { | ||
+ | private Arguments arguments; | ||
+ | private Path gameDirectory; | ||
+ | private GameTransformer transformer = new AppGameTransformer(); | ||
+ | |||
+ | @Override | ||
+ | public String getGameId() | ||
+ | { | ||
+ | return " | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public String getGameName() | ||
+ | { | ||
+ | return " | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public String getRawGameVersion() | ||
+ | { | ||
+ | return App.FULL_VERSION; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public String getNormalizedGameVersion() | ||
+ | { | ||
+ | return App.VERSION; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Collection< | ||
+ | { | ||
+ | return Collections.emptyList(); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public String getEntrypoint() | ||
+ | { | ||
+ | return " | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Path getLaunchDirectory() | ||
+ | { | ||
+ | return this.gameDirectory; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean isObfuscated() | ||
+ | { | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean requiresUrlClassLoader() | ||
+ | { | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean isEnabled() | ||
+ | { | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean locateGame(FabricLauncher launcher, String[] args) | ||
+ | { | ||
+ | this.arguments = new Arguments(); | ||
+ | this.arguments.parse(args); | ||
+ | |||
+ | this.gameDirectory = Paths.get(this.arguments.get(" | ||
+ | |||
+ | return Files.isDirectory(this.gameDirectory); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void initialize(FabricLauncher launcher) | ||
+ | {} | ||
+ | |||
+ | @Override | ||
+ | public GameTransformer getEntrypointTransformer() | ||
+ | { | ||
+ | return this.transformer; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void unlockClassPath(FabricLauncher launcher) | ||
+ | { | ||
+ | for (Path path : this.jars) | ||
+ | { | ||
+ | launcher.addToClassPath(path); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void launch(ClassLoader loader) | ||
+ | { | ||
+ | try | ||
+ | { | ||
+ | Class main = loader.loadClass(this.getEntrypoint()); | ||
+ | Method method = main.getMethod(" | ||
+ | |||
+ | method.invoke(null, | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Arguments getArguments() | ||
+ | { | ||
+ | return this.arguments; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public String[] getLaunchArguments(boolean sanitize) | ||
+ | { | ||
+ | return this.arguments.toArray(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This class example assumes that argument '' | ||
+ | |||
+ | ==== Final touches ==== | ||
+ | |||
+ | Once you get Fabric Loading up and running in your app, you would see extra log in the console upon launching your app (if you did everything correctly): | ||
+ | |||
+ | < | ||
+ | [14:20:00] [INFO] [FabricLoader/ | ||
+ | [14:20:00] [INFO] [FabricLoader/ | ||
+ | - fabricloader 0.13.0 | ||
+ | - java 8 | ||
+ | [14:20:01] [WARN] [FabricLoader/ | ||
+ | [14:20:01] [INFO] [FabricLoader/ | ||
+ | [14:20:01] [INFO] [FabricLoader/ | ||
+ | </ | ||
+ | |||
+ | That's a good sign. However, that's not all. | ||
+ | |||
+ | === Initializing mods === | ||
+ | |||
+ | If you would try making a mod for your app either as a separate project or as a submodule in your project, there could be a couple of things that may not work either in development or in release. First thing is mods won't initialize. I.e. '' | ||
+ | |||
+ | Something like this: | ||
+ | |||
+ | <code java> | ||
+ | public MainApp(App app) | ||
+ | { | ||
+ | super(); | ||
+ | |||
+ | this.development = app.development; | ||
+ | |||
+ | this.events.register(this); | ||
+ | |||
+ | this.registerCore(this, | ||
+ | this.registerFactories(); | ||
+ | this.registerFoundation(); | ||
+ | this.registerMiscellaneous(); | ||
+ | |||
+ | Hooks.startClient(app.gameDirectory, | ||
+ | |||
+ | if (this.development) | ||
+ | { | ||
+ | this.watchDog = new WatchDog(this.assetsFolder); | ||
+ | this.watchDog.register(this.textures); | ||
+ | this.watchDog.register(this.models); | ||
+ | this.watchDog.register(this.sounds); | ||
+ | this.watchDog.start(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Mixins don't work? === | ||
+ | |||
+ | And finally, even if you registered Mixins in your mod, they may load, but they won't be able to actually patch their target class. If you have following message in the log: | ||
+ | |||
+ | < | ||
+ | [12:20:05] [WARN] [FabricLoader/ | ||
+ | - Expected game class loader: net.fabricmc.loader.impl.launch.knot.KnotClassLoader@446a1e84 | ||
+ | - Actual game class loader: null | ||
+ | Could not find the expected class loader in game class loader parents! | ||
+ | </ | ||
+ | |||
+ | Then it's the root of the issue. To fix this, you need to add your app's jar, or folder (in development) to '' | ||
+ | |||
+ | <code java> | ||
+ | public class AppGameProvider implements GameProvider | ||
+ | { | ||
+ | /* ... */ | ||
+ | private List< | ||
+ | private Set< | ||
+ | |||
+ | public AppGameProvider() | ||
+ | { | ||
+ | this.jarFiles = new HashSet< | ||
+ | |||
+ | this.jarFiles.add(" | ||
+ | this.jarFiles.add(" | ||
+ | } | ||
+ | |||
+ | /* ... */ | ||
+ | |||
+ | @Override | ||
+ | public boolean locateGame(FabricLauncher launcher, String[] args) | ||
+ | { | ||
+ | this.arguments = new Arguments(); | ||
+ | this.arguments.parse(args); | ||
+ | |||
+ | this.gameDirectory = Paths.get(this.arguments.get(" | ||
+ | |||
+ | for (Path path : launcher.getClassPath()) | ||
+ | { | ||
+ | if (this.isHostApp(path)) | ||
+ | { | ||
+ | this.jars.add(path); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | return Files.isDirectory(this.gameDirectory); | ||
+ | } | ||
+ | |||
+ | private boolean isHostApp(Path path) | ||
+ | { | ||
+ | if (Files.isDirectory(path)) | ||
+ | { | ||
+ | for (String string : this.jarFiles) | ||
+ | { | ||
+ | if (Files.exists(path.resolve(string))) | ||
+ | { | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | return false; | ||
+ | } | ||
+ | |||
+ | try (ZipFile zip = new ZipFile(path.toFile())) | ||
+ | { | ||
+ | for (String string : this.jarFiles) | ||
+ | { | ||
+ | if (zip.getEntry(string) != null) | ||
+ | { | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | catch (Exception e) | ||
+ | { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | |||
+ | return false; | ||
+ | } | ||
+ | |||
+ | /* ... */ | ||
+ | |||
+ | @Override | ||
+ | public void unlockClassPath(FabricLauncher launcher) | ||
+ | { | ||
+ | for (Path path : this.jars) | ||
+ | { | ||
+ | launcher.addToClassPath(path); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /* ... */ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | After detecting the app's jar(s)/ | ||
+ | |||
+ | === Loading mods from classpath === | ||
+ | |||
+ | If you're testing the mod from the submodule that has your app and Fabric Loader in the classpath, instead of compiling the mod and placing it into app's launch directory, for Fabric Loader to load your mod from classpath, you need to add '' | ||
+ | |||
+ | For more reference check out [[https:// |
documentation/fabric_loader.1559917991.txt.gz · Last modified: 2019/06/07 14:33 by jamieswhiteshirt