Git Product home page Git Product logo

fabric-loader's Introduction

fabric-loader

The loader for mods under Fabric. It provides mod loading facilities and useful abstractions for other mods to use.

License

Licensed under the Apache License 2.0.

fabric-loader's People

Contributors

alexiil avatar altrisi avatar apple502j avatar arc-blroth avatar asiekierka avatar chocohead avatar earthcomputer avatar haykam821 avatar i509vcb avatar ims212 avatar jamierocks avatar jamieswhiteshirt avatar johni0702 avatar js6pak avatar juuxel avatar liach avatar llamalad7 avatar lushangkan avatar modmuss50 avatar nebelnidas avatar nerdthened avatar nikkyai avatar prospector avatar ramidzkh avatar sfplayer1 avatar shadowfacts avatar shartte avatar thecatcore avatar winplay02 avatar zeroeightysix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fabric-loader's Issues

Let's talk privately

Many modders have raised the issues of covering things Mixin doesn't do adequately. One of them is covering the domain of "access transformers", or ways to achieve the following:

  • access private/package-private fields and methods,
  • override private/package-private methods,
  • creating classes using private/package-private constructors.

There are two disadvantages of access transformation in general worth bringing up:

  • It breaks compatibility with a 100% obfuscated "official" non-intermediary environment, as often private obfuscated methods share identifiers with public obfuscated methods in child classes. However, for fabric-loader, this is entirely irrelevant.
  • It exposes internal implementation details which some mods (FastCraft) are interested in deleting altogether. However, those mods could just parse access transformation information and disable the conflicting patches.

There are many approaches we could take. Of course, they should not be evaluated as competitors - some of them only cover part of the problem domain.

Approach 1: Do nothing.

Advantages:

  • Less work!
  • Mixins can already be used to shadow non-static, private/package-private fields and methods. See, for instance, FabricBlockSettings.

Disadvantages:

  • Mixins cannot be used to override private/package-private methods in children classes.
  • Likewise, mixins cannot let you instantiate non-public classes and private/package-private ctors.
  • Shadowing static fields and methods is likewise hacky, requiring creation of a dummy version of the class, casting it and calling it - just to get a static thing! Ouch.
  • Every mod adds their own shadows => bloat! Owie.

Approach 2: Just add access transformers.

Advantages:

  • Modders are already used to it.
  • The most powerful - it lets you do all of those things trivially.
  • Access transformers can be merged into one list for faster processing.

Disadvantages:

  • Requires every mod to keep a local copy of the game .JAR and remap it on any edit, causing a major slowdown in processing.
    • This can be worked around, though: simply copy the prepared .JAR, only edit the classes which have changed, then only decompile those classes! Brilliant
  • Doesn't let you do likewise on other mods, but is this a disadvantage, really?

Approach 3: "OverridePrivate" annotation

Advantages:

  • Actually easier to use than ATs
  • Can be statically checked, and even integrated with IDEs for convienence features

Disadvantages:

  • Only covers the specific part of overriding private/package-private methods.

Approach 4: Accessor.field(MyClass.class, "field"), Accessor.invoke(MyClass.class, "myMethod", a, b)

Advantages:

  • No need to rebuild JARs/sources for every environment
  • A slightly higher barrier to entry could reduce unnecessary usage

Disadvantages:

  • Gets messy really quickly, at least according to kashike
  • Static checking/IDE convienence is complicated - for instance, method arguments wouldn't be visible when writing...
  • Still doesn't solve creating children classes with private/package-private constructors.

Let's talk versioning

I'm going to write up a rough concept I had for discussion:

The goal of a good versioning system would be to make things easy for modpack developers, users and modders. That's important.

Let's take some notes from SemVer: MAJOR.MINOR.PATCH-BUILD, to put it in a very, very shortened version. But let's change things up a bit.

  • An increment of PATCH means that the JAR is compatible on either side of the game. That is, a 1.2.1 version of a mod and a 1.2.0 version of the mod should not only provide the same external APIs; they should also provide the same registry entries/content and, crucially, the same networking protocol. This means that server and client users can patch a mod on their side only to, say, fix critical crashes and duplication bugs without waiting on a modpack update.
  • An increment of MINOR means that the JAR is compatible, but only if both sides are updated simultaneously. A 1.3.0 version of a mod won't talk to a 1.2.1 version of the mod, but if you update both the client and server to 1.3.0 the world is guaranteed to not break in a way which harms the player. (Whether gameplay differences that are cleanly updated count as breaking is subject to debate.) It also means new API methods could have been added, but none removed.
  • An increment of MAJOR means that the JAR may not be compatible when updating worlds or with other mods. It doesn't mean it has to be incompatible - it could just signify a major update overall.

Another approach would be to have separate versioning for API (SemVer), user-facing mod (whatever parseable) and network protocol (integer). However, I have concerns about proper maintaining of the network protocol version by modders in particular - if we had a higher-level networking system in FabricAPI we could perhaps check some kind of immutable schema, but eh...

Unable to launch server with LaunchWrapper

Knot is the recommended launcher now, but fabric-loader is supposed to be compatible with LaunchWrapper.

Trying to launch the server in a fabric-loader environment with LaunchWrapper results in this error:

java.lang.NullPointerException: null
	at java.io.File.<init>(File.java:277) ~[?:1.8.0_162]
	at net.fabricmc.loader.launch.FabricTweaker.injectIntoClassLoader(FabricTweaker.java:84) ~[fabric-loader_main/:?]
	at net.minecraft.launchwrapper.Launch.launch(Launch.java:115) [launchwrapper-1.12.jar:?]
	at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.12.jar:?]

This is the run configuration:
image

It seems during the addition of Knot the launcher mechanism started deleting the --runDir option too early.

Verify/Fix CodeSource, Manifest retrieval

I believe https://github.com/FabricMC/fabric-loader/blob/master/src/main/java/net/fabricmc/loader/launch/knot/KnotClassDelegate.java#L86 doesn't yield the correct code source, at least for non-jars.

The correct code source should be:

  • jar url: location of the jar file containing the class
  • file url: base directory where baseDir/pkg/to/asd.class locates the class file

It should never point to a class file directly.

https://github.com/FabricMC/fabric-loader/blob/master/src/main/java/net/fabricmc/loader/util/UrlUtil.java#L40 appears to yield the class file instead of the base dir.

Additionally KnotClassDelegate.getMetadata could obtain the Manifest from the JarURLConnection directly and reliably.

Search for mods recursively

I'd love to be able to organize my mods however I like within the "mods/" folder, so for example I could maintain a structure like this:

minecraft/
| mods/
  | lib/
    | fabric.jar
    | fabric-language-kotlin.jar
    | cloth.jar
    | crochet.jar
  | core/
    | roughly-enough-items.jar
    | modmenu.jar
  | content/
    | level-up-hp.jar
    | some-really-cool-mod.jar
  | utility/
    | lead-villagers.jar
    | some-tweaks-here.jar

Add mod sorting

Mod sorting is currently a stub in fabric. It can be done with a topological sort.

Examples of topological sort I wrote is at https://github.com/KyoriPowered/lunar/tree/master/src/main/java/net/kyori/lunar/graph and https://github.com/MinecraftForge/MinecraftForge/blob/e164b2f5d54798716a5f4914d781dacbbf4a0e47/src/fmllauncher/java/net/minecraftforge/fml/loading/toposort/TopologicalSort.java#L87-L123. Out of the two, the forge one should be concise enough.

It also incorporates a cycle reporting functionality based on tarjan's strongly connected components algorithm, but that part takes a few more lines. If you think the cycle reporting is too verbose, we can simply throw a regular IllegalArgumentException instead.

Properly defining a mod metadata format

As you might've noticed, in 0.1.x-0.3.x the fabric.mod.json format is effectively "defined" by the Java class - this doesn't really make for good documentation or specification.

This is an issue opened to discuss things which should be covered by a fabric.mod.json standard ("schemaVersion": 1) which I hope to add in 0.4.0 - with the prior JSON files being implied as "schemaVersion": 0.

These are the existing fields in the mod.json format as of 0.3.6:

	// Required
	private String id;
	private String version;

	// Optional (environment)
	private DependencyMap requires = new DependencyMap();
	private DependencyMap conflicts = new DependencyMap();
	private String languageAdapter = "net.fabricmc.loader.language.JavaLanguageAdapter";
	private Mixins mixins = Mixins.EMPTY;
	private Side side = Side.UNIVERSAL;
	private boolean lazilyLoaded = false;
	private String initializer;
	private String[] initializers;

	// Optional (metadata)
	private String name;
	private String description = "";
	private Links links = Links.EMPTY;
	private DependencyMap recommends = new DependencyMap();
	private Person[] authors = new Person[0];
	private Person[] contributors = new Person[0];
	private String license = "";

Some prior art:

  • Forge/Sponge's mcmod.info (duh)
  • craftson spec - this is what we based the initial mod.json format on back in 2016, but have since deviated
  • Spigot's plugin.yml
  • MCubed's craft.json - designed for project formats, but could be useful here

I'd also like to call in @peterix as he might have lots of prior art from his work on MultiMC.

before="" / after="" needs to be treated as no dependency OR special error

People are going to do it out of habit, so we might as well avoid any weird cyclic dependencies caused by "" allegedly being a thing.

Alternatively, if we want to break the habit of that, spew an error if someone tries to use "" as an empty list instead of {}, and tell them to use {} if there is genuinely nothing to depend on.

Of course, there are very few things which will actually have no dependency.

My advice, use "fabric_internal_nodep_pretrap1" < "fabric_internal_nodep_pretrap2" and make everything within fabric-base depend on "fabric_internal_nodep_pretrap2". That way we can catch things out as early as possible.

And of course, at the same time, spew an informative error every time someone does "" out of habit.

It's much easier than trying to decipher where the cycle is in the hookchain, but we'll have to do that anyway.

Let's talk options and configuration structure

This issue is for discussion of the structure and layout of configuration files. If you are looking for the issue on the configuration file format (JSON, HOCON, YAML), see issue #30.

The issue I'd like to discuss here is that there is not one type of mod configuration, but three:

  • Set A) Server configuration: Configuration which must be enforced on all clients, that modify gameplay and would be harmful if clients could easily modify for themselves, such as enabling/disabling certain blocks or core features, or numeric values such as how much energy a machine uses.
  • Set B) Client configuration: Configuration which the server has no reason to care about or enforce, such as graphical options like themes, fonts, colors, or formatting styles, as well as options like disabling cosmetics for mods in cosmetic mods.
  • Set C) Server-enforceable configuration: Configuration which the server may wish to place certain restrictions on, but otherwise should allow the client to configure. Examples include minimap scale (servers may want to enforce some maximum scale, while letting users choose to make it smaller) or whether to allow minimaps in caves (some servers may want to force this to off, while others may allow caves, but some users may not like caves and want to be able to turn them off)

Set A will include breaking changes that, if modified, will require a restart of the client or server. Thus, it is logical to place these in a "cfg" folder, and additionally have a separate Mod Configuration menu in the Main Menu that users can use to modify configuration values.

However, for those options in Set B, including these options in the Mod Configuration menu makes it hard for users to determine what is safe for them to change. Additionally, if the Mod Configuration menu is accessible in-game, that can cause problems as options that are not meant to be changed mid-game are changed, whereas if the Mod Config menu is only available from the main menu, that excludes options that should be allowed to be changed mid-match.

I would like to see mod configuration split into two logical categories.

  • Config properties (Set A) would be breaking changes, and require the user to access them either through modification of a config file in the mod folder or through some GUI accessible only when the user is not logged into the world.

  • Options (Set B) would be treated semantically differently. They should hook into the vanilla options menu, being added to either a special "mod options menu" (which would not be preferred since users might confuse them with immutable config properties) or, preferably, the appropriate submenu in the options menu. Integrating the options fully would make them feel "natural" and make them more user friendly.

For those options in Set C, the Server (upon the client connecting) would send a packet containing the server-operator-defined range of allowable values for the client, with a chosen default value for those clients which are not in compliance. The client would receive these, and temporarily adjust those options to the provided defaults if they are not in range.

Note that including comments and labels in the config itself as an object, this is not extendable, especially to multiple languages. Names and descriptions (for both config properties and options) should be read from lang files using translation keys:
config.<modname>.<configkey>.<configsubkey>.title and config.<modname>.<configkey>.<configsubkey>.desc

Additionally, it may also be appropriate to place config properties in the data folder, allowing replacement of them with data packs.

Let's talk mod loading

The current way of mod loading uses a list of initializer classes in the .JSON which can provide various interfaces. Specially annotated interfaces are, then, indexed in an accessible manner for queries - Fabric queries them for instances of "ModInitializer". This has a few issues, however.

Client/Server environment specific code

(As of 18w49a, there is only really client environment specific code worth worrying about, but for the sake of argument...)

Many classes exist solely on the client and accessing them on the dedicated server is... a bad idea, to say the least. My proposal here would be to add a "ClientModInitializer/ServerModInitializer" interface - with the caveat that those classes only get loaded on the specific environment. Another option would be marking classes as client/server-side-specific in the JSON file. Of course, we could also replace ModInitializer with something else entirely.

Using the JSON interface list for mod add-on resolution

Is this a good idea? Say, if JEI were to add plugins, it would just add a JeiPlugin interface which can be implemented by classes added to the JSON initializer list. For this, we could extend the former to also not load classes if the interfaces on them are not present in the classpath after all mods are added to said classpath.

META-INF/services vs JSON

Someone once said we should use the former versus a custom list format for classes. Just bringing it up, although honestly I'm not a huge fan.

Advantages:

  • IDE tooling support

Disadvantages:

  • Features such as Kotlin singletons are not supported, unlike our custom JSON format where implementing them is possible.
  • Every ServiceLoader for a given interface re-instantiates the class in question, which is a bit less comfortable for certain mod class patterns, but probably not a huge deal.

Install Wiki Instructions

I wanted to see if a local client would work, and was confused when it did not:

Put both .JARs in the same directory. From the command line, run:

java -Xmx[RAM amount, for example 2G] -jar fabric-loader-[version].jar [minecraft server JAR] [arguments…]

This should let you run the server.

If you don't want to (or can't) provide the Minecraft server .JAR as an argument, it's optional - however, the JAR must then be named “server.jar".

I changed fabric-loader to fabric-installer. I also tried a variety of ways to provide the Minecraft server .JAR. The responses were:
Loading Fabric Installer: 0.3.0.17
No handler found for server.jar see help

I could not find the help, here or on fabricmc.net. I know forge installations can require extra options or clicks; I may be missing an installation step.

Extra initializers for net.minecraft.data.Main

In 1.13+, vanilla adds net.minecraft.data.Main entry point for both clients and servers to allow easy data generation. This generation appears useful to modders as it can create jsons with little code and is almost always up-to-date.

Can fabric loader add a hook for the data generation, so that mods can register their stuff and then generate content for data packs correctly?

Let's talk mod loading again

I don't believe the current implementation from #26 is as effective as it could be.

Currently, mod initializers are declared using the "initializer" string field or the "initializers" string array field in the fabric.mod.json file. The strings are fully qualified names of classes that fabric-loader will create singleton instances of. An initializer class can implement any number of interfaces to expose itself. The same instance is used for each interface the initializer implements. ModInitializer, ClientModInitializer and DedicatedServerModInitializer are used by fabric-loader. It is also possible to use additional initializer interfaces and get them via FabricLoader#getInitializers(Class) for add-on resolution.

Implementing ClientModInitializer/DedicatedServerModInitializer may have unexpected effects if implemented in a class that also implements another initializer interface. To avoid class loading issues in client/dedicated server environments, fabric-loader may avoid loading an initializer. Currently, depending on the environment, fabric will refuse to load initializers if they implement ClientModInitializer or DedicatedServerModInitializer respectively. When an initializer is not loaded, it will not be exposed for any of its interfaces. This means a class implementing both ClientModInitializer and ModInitializer cannot be exposed as a ModInitializer. Analyzing classes with ASM to avoid loading issues is something I think we should avoid.

One bug is that the test for implementation of ClientModInitializer/DedicatedServerModInitializer does not match the exposure, meaning there are ways to implement ClientModInitializer/DedicatedServerModInitializer and having it loaded when it should not be. Fixing this bug would make things even more dependent on ASM analysis.

There is also currently no way to declare dependently loaded interfaces for add-on resolution. I imagine initializers used by a mod like JEI might contain environment specific parts.

I propose that we declare common, client and dedicated server initializers separately instead of declaring them all together. We can implement the dependent loading mechanism at that level instead of inspecting initializer classes. This would also make ClientModInitializer and DedicatedServerModInitializer obsolete, as mods can declare a client-only secondary initializer implementing ModInitializer.

Split into fabric-loader and fabric-base

fabric-loader should rely solely on launchwrapper and the mixin library as to be includeable in Weave and versioned outside of Minecraft itself; fabric-base should depend on it and provide "essential" Minecraft-related functionality (kicking players on fatal mod list mismatches, for example).

java.lang.LinkageError: loader constraint violation with built mod

When using a built version of my mod, the client crashes with the following message:
java.lang.LinkageError: loader constraint violation:

Full log: https://gist.github.com/Blayyke/8830bee84b9ab84b83ce2aef029158c6
(this was run with other mods present but i have tested just with mine and it happens)

java -version output:

openjdk version "1.8.0_202"
OpenJDK Runtime Environment (build 1.8.0_202-b26)
OpenJDK 64-Bit Server VM (build 25.202-b26, mixed mode)

Edit:
This only seems to happen when I'm shading in org.spongepowered:configurate-hocon:3.6.

Remove fabric-loader's reliance on Intermediary

Motivation

As we need very few generic hooks, in this way, we could boot fabric-loader on arbitrary environments, mappings, Minecraft version, etc. blindly. Currently, we rely on the Intermediary names matching. Meddle's DynamicMappings, as well as some experiments we did in 2016, can be considered prior art.

To be done

  • Move EnvironmentHandlers to Fabric API instead of Loader,
  • Un-revert the MixinLoader/FabricLoader-unifying commit,
  • Add transformer logic to identify the true client/server class (follow main.Main),
  • Replace MixinClientBrandRetriever/MixinMinecraftServerBrand with hand-made hooks following the above-mentioned information,
  • Replace entrypoint hooks with hand-made hooks following the above-mentioned information.

ConcurrentModificationException in ModResolver

Ever since loader 0.4.0, I've been getting a ConcurrentModificationException about 60-70% of the time when I try to run the game in a dev environment. It happens both with the intellij run configuration and with the runClient gradle task. Here's a sample stack trace:

**Connected to the target VM, address: '127.0.0.1:36833', transport: 'socket'
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
[23:04:14] [ForkJoinPool-1-worker-5/WARN]: Mod ID fabric uses outdated schema version: 0 < 1
Exception in thread "main" java.lang.RuntimeException: Mod resolution failed!
	at net.fabricmc.loader.discovery.ModResolver.resolve(ModResolver.java:470)
	at net.fabricmc.loader.FabricLoader.load(FabricLoader.java:144)
	at net.fabricmc.loader.launch.knot.Knot.init(Knot.java:120)
	at net.fabricmc.loader.launch.knot.KnotClient.main(KnotClient.java:26)
Caused by: java.util.ConcurrentModificationException: java.util.ConcurrentModificationException
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)
	at java.base/java.util.concurrent.ForkJoinTask.getException(ForkJoinTask.java:933)
	at net.fabricmc.loader.discovery.ModResolver.resolve(ModResolver.java:453)
	... 3 more
Caused by: java.util.ConcurrentModificationException
	at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1134)
	at net.fabricmc.loader.discovery.ModResolver$UrlProcessAction.compute(ModResolver.java:379)
	at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:189)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Disconnected from the target VM, address: '127.0.0.1:36833', transport: 'socket'

Process finished with exit code 1**

Let's Talk: Code Permissions

Implementing a permission system would increase the security of Minecraft, making it harder for a rouge mod to leave the sandbox of the JVM and damage the host system.

My current idea for the permissions module are as follows:

  • During mod discovery permissions are checked
  • New permissions open a dialog (in-game would be best) asking if it is okay
  • Different levels of permissions, dangerous ones require explicit grants? (Raw FS, network, reflection, etc)
  • Permission for configs?
  • Permission for a "private" data area with limited size?
  • Permission for a "shared" data area?
  • Permission for networking?
  • Allow mods to specify permissions (the JVM security model should limit "side channel" permission escalation)

For implementation details, I am not sure. Would probably be best to use some sort of JSON configuration file.

Better error message on missing initializer

This is the current exception stacktrace which is not at all helpful:

java.lang.RuntimeException: net.fabricmc.loader.language.LanguageAdapterException: I/O error!
	at net.fabricmc.loader.InstanceStorage.instantiate(InstanceStorage.java:45)
	at net.fabricmc.loader.FabricLoader.initializeMods(FabricLoader.java:458)
	at net.fabricmc.loader.FabricLoader.onModsPopulated(FabricLoader.java:283)
	at net.fabricmc.loader.FabricLoader.load(FabricLoader.java:264)
	at net.fabricmc.loader.FabricLoader.load(FabricLoader.java:158)
	at net.minecraft.client.MinecraftClient.handler$init$zzb000(MinecraftClient.java:2238)
	at net.minecraft.client.MinecraftClient.init(MinecraftClient.java)
	at net.minecraft.client.MinecraftClient.start(MinecraftClient.java:360)
	at net.minecraft.client.main.Main.main(Main.java:126)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at net.fabricmc.loader.launch.knot.Knot.init(Knot.java:320)
	at net.fabricmc.loader.launch.knot.KnotClient.main(KnotClient.java:26)
Caused by: net.fabricmc.loader.language.LanguageAdapterException: I/O error!
	at net.fabricmc.loader.language.LanguageAdapter.createInstance(LanguageAdapter.java:36)
	at net.fabricmc.loader.InstanceStorage.instantiate(InstanceStorage.java:40)
	... 14 more
Caused by: java.io.IOException: Class not found
	at org.objectweb.asm.ClassReader.readStream(ClassReader.java:300)
	at org.objectweb.asm.ClassReader.<init>(ClassReader.java:273)
	at net.fabricmc.loader.language.JavaLanguageAdapter.getClass(JavaLanguageAdapter.java:59)
	at net.fabricmc.loader.language.LanguageAdapter.createInstance(LanguageAdapter.java:29)
	... 15 more

Realms is broken

Apparently the Realms JAR does actually rely on obfuscated Minecraft code (which is bad news).

This means I can't get away with just not breaking its deobfuscated mappings, but will have to remap it alongside the Minecraft JAR. This will be done in Loader 0.4.3.

Environment stripping

The @Environment annotation is used in a development environment to indicate that something exists only in the dedicated server or client environment. It currently has no effect but it could be used for static verification.

The problem is that a runtime server/client environment is not truly simulated in a development environment. You can easily do something that works in a development environment but not in a production environment.

Examples are Block#addInformation and Item#buildTooltip (yarn:18w50a.59). TooltipOptions is an @Environment(EnvType.CLIENT) class used as a parameter type in both methods. Trying to reflect on the Block/Item class will load the TooltipOptions class. Works in a development environment but explodes in a dedicated server environment.

The class verifier may also trip on implicit casts. As an example, you could have a field/variable/parameter with type MinecraftServer. If you attempt to assign this something like new IntegratedServer, the class verifier will attempt to verify that IntegratedServer is a MinecraftServer, even if the expression is guarded using something like if (environmentHandler.getEnvironmentType() == EnvType.CLIENT). The IntegratedServer class will be loaded. Works in a development environment but explodes in a dedicated server environment.

We should strip methods, fields and classes in the class loader depending on the @Environment annotation. Both net.minecraft and mod classes could use this.

Sided -> Environment

The Sided annotation is misleading:

  • the "client" distribution contains server functionality just like the dedicated "server", there's no clear side to at least the former
  • there are actual sides, client and server, that map to the established networked notations. they are not represented by the current annotation
  • sides don't imply absence, only context
  • other similar projects saw avoidable confusion and bugs by "side proxies" acting as the client in the integrated server context

A more appropriate name is Environment/Env/EnvOnly, with an entry for each distribution.

Schema v2

Things to discuss:

  • Localizable mod descriptions
  • Links to mod dependencies. (Ideally I'd love a full metadata for them but that's too crazy to ask lol. A link to their page for the Suggestions and Recommendations seems good though)

Wrong sounds

For example, lever makes "Leash knot tied" sound, instead of it's normal sound.
Chicken makes "Chest open" sound, etc.
When other player walks on stone I hear button clicks instead of stone steps, other player hears stone step.
It works fine when fabric-loader is not installed.

Classloader doesn't set the Package correctly

When loading a Fabric mod I noticed that all classes have no package information.
To reproduce just print out the package in "onInitialize":
LOG.info("Package: " + this.getClass().getPackage());
[Server thread/INFO]: Package: null

In my case this crashes snakeyaml because there one class tries to create a logger with the package as name.

Duplicate mod ID from old builds in `build/libs`

in the dev environment all jars in build/libs are loaded as mods
when those include old builds of the project it leads to this kind of crash

java.lang.RuntimeException: Duplicate mod ID: fabric-example-mod-kotlin! (kotlin-example-1.0.0.jar, kotlin-example-1.0.1.jar)
	at net.fabricmc.loader.FabricLoader.addMod(FabricLoader.java:305)
	at net.fabricmc.loader.FabricLoader.load(FabricLoader.java:209)
	at net.fabricmc.loader.FabricLoader.load(FabricLoader.java:143)
	at net.minecraft.client.MinecraftClient.handler$init$zzb000(MinecraftClient.java:2169)
	at net.minecraft.client.MinecraftClient.init(MinecraftClient.java)
	at net.minecraft.client.MinecraftClient.start(MinecraftClient.java:358)
	at net.minecraft.client.main.Main.main(Main.java:126)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
	at net.minecraft.launchwrapper.Launch.main(Launch.java:28)

I think build/libs should not load mods, and instead there should be utility to populate mods/ with any dependencies (as part of modCompile ?)

Loading of non-mod jars

I would like to use a separate non-mod library with a mod. It seems currently the only viable option is shading. It's not the end of the world, but I think it should be possible for the user to install non-mod jars, either in the mods directory or somewhere else. Jar-in-jar could also be an option but that's a bit further away.

Let's Talk: Java Modules

This issue is intended for discussion related to the Java module system that was implemented in Java 9 as part of project Jigsaw.

If implemented in a way that would allow this functionality in Java 9 or greater while maintaining backwards compatibility with Java 8 should be fairly easy, but it will create issues if a backwards compatibility system of sorts is not put in place. If a developer creates a mod and only tests against Java 8 and does not specify any module metadata for Java 9+ it would have to be put into the "Unamed module".

For what I would consider a proper "backwards compatibility" needs the following:

  • A way to read the module metadata (module-info.java) in mods
  • A basic implementation to prevent package collisions, that violates the module system
  • You don't need to prevent reflective access across "modules", but would be nice (Modules can allow reflective access with the opens keyword)
  • Limited access across modules might not be possible, would be nice

Then the actual module loader implementation would require the following:

  • A way to merge the conflicting Minecraft libraries, primarily the logger and Paule's Audio
  • A module definition for all of the included libraries plus Minecraft itself, my toy implementations based this off of the module name in the library manifest

If this is implemented properly it would allow more flexibility with libraries and prevent reflection hacking from occurring. This prevents many issues with code accessing things that they should not, and can entirely hide internals of libraries if the need arises. It also makes it so all public fields, methods and types have to be explicitly exported. Makes it a little harder to leak info from implementations.

Let's talk configuration format

Fabric-loader should probably distribute a library for one, at least.

There are many options which were brought up: YAML, HOCON, IC2-style .ini files, something entirely custom, etc.; and many libraries which implement them. What would you, as a modder, want to use?

Prod server with cotton-energy crashes with `java.net.MalformedURLException: unknown protocol: jimfs`

Running a fabric server with Cotton-Energy (jar in a zip
attached below) will cause the server to crash with this output: https://gist.github.com/Blayyke/3f50792b1f4a905956c3832abe2db6c0.

Running the server without cotton-energy in the mods folder does not crash, and using some other combination of mods also does not crash. However, I think cotton-energy is the only mod that uses nested jars, so this may be a loader 0.4.0 issue. This crash does not occur on a prod client, dev environment client, or dev environment server.

CottonEnergy.zip

Major inconsistency in mappings resolver API

All map methods in FabricMappingResolver require class names in dot format, but nothing is actually done to convert dot format to internal anywhere. As such, no mapping can ever get resolved.

Only one mod's initializer gets loaded

I tried to make a simple mod using fabric-language-kotlin, and noticed my mod's initializer wan't being called. Initially I thought it was the fault of fabric-language-kotlin, so I tried again in Java. The problem persisted.

I did some debugging, and found that the initializers stored in InstanceStorage weren't being mapped from classes properly. Specifically, Fabric API's initializer and my initializer are being mapped from two different "instances" of the ModInitializer interface class. Here's a screenshot to show what I mean:

Capture

You can see there are two ModInitializer keys, one Class@6421, and one Class@6419, each with only one mod initializer in its set of values. So, when the loader tries to get all mods' ModInitializer, it only gets it from Fabric API, not my mod, and misses running my initialize.

Did some more digging to find how this MultiMap get populated, and found that you call getInterfaces on my initializer class.

Capture2
Capture3

This is where the two different Class<ModInitializer> instances are coming from; see Class@6430 and Class@6428. I recommend you use the class's canonical name String as the key of the MultiMap inside the InstanceStorage, not the class itself since it seems two different Class<Foo> are not guaranteed to always be equal.

My versions are as follows:

minecraft_version=19w13a
yarn_mappings=19w13a.2
loader_version=0.3.7.109
fabric_version=0.2.6.116

Missing api.FabricLoader.getConfigDirectory

Deprecation in FabricLoader.INSTANCE tells me to report stuff I'm missing, so: there's no getConfigDirectory in api.FabricLoader. Unless there's some other way to get the config directory now?

Cannot mixin into package private inner class

Since the latest release (0.4.1) the following mixin into a private inner class doesn't work anymore:

@Mixin(targets = "net/minecraft/recipe/RecipeFinder$MatchableRecipe")

It fails with the following message:

Error loading class: Lnet/minecraft/recipe/RecipeFinder$MatchableRecipe; (java.lang.ClassNotFoundException: Lnet/minecraft/recipe/RecipeFinder$MatchableRecipe;)

It works fine with Loader 0.4.0.

I don't know whether this is related to changes of the loader or changes of the mixin library. So feel free to close this here and open it there ;)

Conditionally apply mixins

Currently, to conditionally do something, you have virtually 2 choices.
1.

if (disabledFeature) return;
  1. IMixinConfigPlugin. This is powerful, although slightly harder to use.

I think the mixin JSON could be extended to allow a condition to be passed next to the applicable mixin.

codestyle

Do we use 4 spaces or tabs? I think 4 spaces may be better.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.