-
Notifications
You must be signed in to change notification settings - Fork 270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fake mod containers #192
base: master
Are you sure you want to change the base?
Fake mod containers #192
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few comments with a few questions.
One addition I would like to see is a way to get a list of fake/provided mods, possibly a:
Collection<ProvidedModContainer> getModsProvidedFrom(ModContainer);
One contract of that above method would be that, the ModContainer used to get the providers MUST be a real mod.
I think we should add a isFake
method to ModContainer.
For an extended ModContainer type for the fake containers I think we should have a ProvidedModContainer where there is a getProvider
method so we can figure out which mod provided the fake container.
@@ -30,6 +31,7 @@ public static void start(File runDir, Object gameInstance) { | |||
|
|||
FabricLoader.INSTANCE.prepareModInit(runDir, gameInstance); | |||
EntrypointUtils.invoke("main", ModInitializer.class, ModInitializer::onInitialize); | |||
FabricLoader.INSTANCE.getEntrypoints("fake_mods", FakeModProvider.class).forEach(provider -> FabricLoader.INSTANCE.appendFakeMods(provider.getFakeMods())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question I have is, is this the right spot for the FakeModProviders to run?
I personally think that we should load fake mods after the GameProvider is set but before onInitialize after each of the ModInitializers fires their constructors.
One key reason for doing this before Client/Common/Server entrypoints is a concern about mod loading order, where if you do this after the Common Initializer has gone off you don't exactly have much context in what all mods that exist at that point along with breaking the expectation of the ModInitalizers having a final state for mods being loaded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main worry there is that then the fake mods likely haven't been loaded. You'd almost definitely want to do that in onInitialize
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is your reason for loading fake mods after the mod initializer (thereby the client/server would be loaded). Sponge's events cover some states near what on Initialize would cover.
* Should run <i>after</i> {@link ModInitializer#onInitialize()} | ||
*/ | ||
public interface FakeModProvider { | ||
Collection<ModContainer> getFakeMods(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be like below instead?
void registerFakeMods(Consumer<ProvidedModContainer> acceptor);
That would allow for a few things:
No need to worry about the whole collection being passed as null.
Anyone using the method would do the following for example:
public void registerFakeMods(Consumer<ProvidedModContainer> acceptor) {
ProvidedModContainer container1 = ...
ProvidedModContainer container2 = ...
acceptor.accept(container1);
acceptor.accept(container2);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that might be a good idea. I'll think about it.
@Override | ||
public boolean isModLoaded(String id) { | ||
return modMap.containsKey(id); | ||
return modMap.containsKey(id) || fakeModMap.containsKey(id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My request for this would require a contract to be established that a IllegalArgumentException
would be thrown if two ModContainers of the same modid were to be registered during the registration process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh that's a good point.
@@ -0,0 +1,13 @@ | |||
package net.fabricmc.api; | |||
|
|||
import net.fabricmc.loader.api.ModContainer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is currently not way to distinguish between a normal and fake mod container.
First I think we should add a isFake/isProvided method.
I think we should have return a ProvidedModContainer
which extends ModContainer
and a ModContainer getProvider();
so we could track which mod made the fake mod. Of course this would require a way to get what ModContainer provides an entrypoint (so we have a source of the FakeModProvider).
@@ -59,6 +59,12 @@ static FabricLoader getInstance() { | |||
*/ | |||
Collection<ModContainer> getAllMods(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should getAllMods()
return every single mod, regardless of weather it's fake or real and add a getRealMods()
method.
Or should we make this exclusively real mods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like it'd be better to have near-total separation between fake and real mods. I might even want to have isModLoaded
have a counterpart isFakeModLoaded
instead of the ||
behavior it has in the current state of the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree with boundary here, I think mods should be all equal by default. If someone provides a "fake" mod with the same id as a real mod, there should be a good reason, and they should be treated the same.
I’m not sure fake is the right word here. As it could well be something that resembles a real mod (to the user anyway). We use builtin for the game. Provided mod? Proxy mod? I’m not really too sure. |
I've been referring to these ProvidedModContainers. Proxy mod makes it sound like a real mod hides behind it. |
I think we shouldn't limit this API to fake mods. There are several use cases in which provided mods should have full capabilities, actual fake mods are merely a special case. In fact, couldn't fake mods simply be regular mods with no source ? |
Yeah, that is a good point. Maybe it'd be better for the provider to just let mods add new candidate finders to the ModResolver. |
The only issue there is, of course, load problems. You shouldn't exactly have mods be able to add mod resolvers at a time when mods aren't even loaded yet. It seems like a big nightmare mess. Maybe we could use Java-native |
I think we would have to load mods in several passes, using native services is begging for classloading issues. |
yeah, and loading mods in several passes seems like a wide open door for issues, along with kinda destroying one of the main points of fabric loader. |
Loading mods in several passes is not a big deal, as long as the passes are entirely data-driven and not decided by mod code - that is, they can still happen before classpath injection or any other part of loading mods. |
But then that removes quite a bit of flexibility for those mod providers (eg. no conditional mod loading) |
One issue we had when adding mc as a builtin mod, was api was trying to load it as a resource pack when it shouldnt have done. There will need to be a way for api to know if it should load the resources, and ideally done in a way without breaking existing api versions? |
EDIT: I am going to write up a doc explaining my concept in much more detail. So we all seem to be asking for different specs. If we want full capability mod providers we have to load them before the game provider starts, which would thereby start mixin. A data driven system does not go far enough for some contexts such as plugin loading (with sponge for example). I would suggest an entrypoint which allow you to define a mod with a container, and optionally the source of the mod within class path. This entrypoint will be loaded on a special classloader which will crash if MC is referred to and has no access to api outside of fabric loader and some basic deps. Addition 1: I think the instant before preLaunch is invoked we should lock the mod loading process, no more providing mods. Addition 2: To use this system we should require a value in the fabric.mod.json be set (not just a custom value) to use the system, as a way to require an opt in instead of people just throwing their provide_mods entrypoint in and calling it a day. With all mods which are provided by this system, their mod container should be an instanceof a ProvidedModContainer (As a way to distinguish between provided and regular mods). A ProvidedModBuilder should be in the api to allow provided mods to be built by code, this will return a ProvidedModContainer with capability of specifying metadata down to even mixins and entrypoints. To keep with the contractual detail of older versions, all ModContainers, once built should be immutable. Also pyrofab's mention of this in an above comment with possibly merging mod containers and metadata with same versions, I think we should crash and burn in that case since with jar mods, we don't support multiple mods with same ID. |
My comment was regarding a provided mod that has the id of a real mod, but with the latter not being present. In that case, there is a decent chance the provided mod is supposed to work exactly like the missing real mod. |
Is this covered by the "provides" functionality? |
As far as I can tell, no; |
Mainly intended as a conversation starter, absolutely not merge-ready
Adds a system for adding "fake" mods which exist primarily as metadata storage and do not have any entrypoints called or mixins applied. Intended for systems which want to add "mods" as metadta for systems like Mod Menu to access. Possibly not helpful in the first place.
Currently runs the
fake_mods
entrypoint right aftermain
is run. No system is yet provided for removing fake mods or calling after mods are loaded.