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 [2021/02/07 13:47] – [Fabric Loader] liach | documentation:fabric_loader [2023/12/27 12:57] (current) – ↷ Links adapted because of a move operation 194.190.81.32 | ||
---|---|---|---|
Line 23: | Line 23: | ||
Nested JARs allow a mod to provide its own dependencies, | Nested JARs allow a mod to provide its own dependencies, | ||
- | Nested JARs are not extracted, they are instead loaded in in-memory file system using jimfs. See the [[https:// | + | Nested JARs are not extracted, they are instead loaded in in-memory file system using jimfs. See the [[tutorial: |
==== Entrypoints ==== | ==== Entrypoints ==== | ||
Line 62: | Line 62: | ||
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.1612705649.txt.gz · Last modified: 2021/02/07 13:47 by liach