#FML Clean-up
1 messages Β· Page 4 of 1
@glacial crag What about https://github.com/neoforged/FancyModLoader/pull/83/files ?
Obviously we're on fabric mixin now
But .... you made some other changes here
As long as we stay on fabric mixin we don't need that PR
Given current upstream progress we'll probably be on it for a long time
Then I'd suggest you close it π
Indeed I did
@chrome mortar ugh https://github.com/neoforged/FancyModLoader/pull/112/checks#step:7:108
How do we handle test reporting in GH actions?
FMLLoaderTest > WithoutMods > testNeoForgeDevClientDiscovery() FAILED
java.lang.AssertionError at FMLLoaderTest.java:130
And no attached artifact with the report doesn't help us
Unless I am missing something
well not at all right now 
rerun with debug might help?
isn't that just debug gh actions, not gradle --debug?
We might just wanna add artifacts **/build/reports/tests if we can
the gradle action might be setting gradle flags if it detects action debug
not sure
so test it
Hm it didnt allow me to choose the debug flag
I thought there was a popup that asked for this
there should have been one
Hm, it's failing only the NeoForgeDev cases π€
Eh I can "fake" it by adding --info to the gradle tasks
well no it didnt run with the debug option or did you just do it again?
OH also, shouldn't it be running check?
It's currently running test
FMLLoaderTest > WithoutMods > testNeoForgeDevServerDiscovery() FAILED
java.lang.AssertionError:
Expecting ArrayList:
["net/minecraft/client/Minecraft.class",
"net/minecraft/server/MinecraftServer.class"]
to contain only:
["assets/.mcassetsroot",
"data/.mcassetsroot",
"net/minecraft/client/Minecraft.class",
"net/minecraft/server/MinecraftServer.class"]
but could not find the following element(s):
["assets/.mcassetsroot", "data/.mcassetsroot"]

Yeah.... no idea
I've just installed J21 and checked out FML fresh in WSL2 on my machine and it ran successfully BUILD SUCCESSFUL in 54s
Nevermind. Idiot move.
Okay, same failure now. I can work with that
Aaaaahem
Why is SJH on Linux returning a base-path like that?
...... why does it do this ?!
it makes sense for entry, but it makes no sense for basepath
Oh well userdev is completely busted π
hehehe
https://openjdk.org/jeps/8323658 it's coming soonβ’οΈ
(JEP draft: Exception handling in switch (Preview))
Hm. We're scanning the system classloader for potential FML and ML services. So far so good.
Why do we also scan MOD_CLASSES in addition to that?
Shouldn't everything from mod classes already be on the system loader classpath?
No.....
That is the funny thing
They way mod classes works
That is not actually required
And tools like workspace use it
Sorry, but what is "workspace"
and why can't it just be on the CP? π
It would make our lives easier I think
Workspace is a tool by covers that creates an idea multi project environment that contains many mods service layer elements and neo. And runs without requiring the setup process by running on the native neo patched sources and binary class files
As such it does not run at all using the normal logic
And it combines the mods and service layers together into a run
Without them depending on each other
I don't like that we're cementing approaches that are even further away from how it runs in prod π
But blergh, so you're saying mod classes should be able to add to the CP, but we still scan the CP anyway
Feels awfully weird to have your internal operations dictated by a third party tool..
We can trivially support that use case once we look at the startup process as a whole
currently it's messy since a very early call will already try to use MOD_CLASSES
before FML even really starts
Specifically ClasspathTransformerDiscoverer
Wait, does this allow developing a mod and Neo simultaneously?
Yeah
That's insanely cool
Well okay, I did write a test case
But yeah
/**
* Special test-case that checks we can add additional candidates via the modFolders system property,
* even if they are not on the classpath.
*/
@Test
void testUserdevWithModProjectNotOnClasspath() throws Exception {
var additionalClasspath = installation.setupUserdevProject();
var entrypointClass = SimulatedInstallation.generateClass("MOD_ENTRYPOINT", "mod/Entrypoint.class");
var modManifest = SimulatedInstallation.createModsToml("mod", "1.0.0");
var mainModule = installation.setupGradleModule(entrypointClass, modManifest);
// Tell FML that the classes and resources directory belong together
SimulatedInstallation.setModFoldersProperty(Map.of("mod", mainModule));
var result = launchWithAdditionalClasspath("forgeclientuserdev", additionalClasspath);
assertThat(result.pluginLayerModules()).doesNotContainKey("mod");
assertThat(result.gameLayerModules()).containsKey("mod");
installation.assertModContent(result, "mod", List.of(entrypointClass, modManifest));
}
You basically load your mods into a neodev env
hm it stripped the blank lines. it's more readable in IDE π
Key point is that mainmodule (which are the simulated gradle output folders) do not get added to the additional classpath
And same testcase for the ModLauncher level classpath locator /sigh
Yeah
This is really good work shartte
We need these kinds of tests for the ML-FML-NF chain rebuild anyway
What I cannot reliably test is the ML->FML handover yet
Essentially I start at the handover point to FML, but I have found a new way of skipping paths that have already been loaded by ML
I would argue that something like that would need to happen in some e2E test variant somewhere
without having to exchange info between the ML transformer locators and the FML locators via static fields
If you do it E2E it becomes hard to inspect the final state properly, but yeah this is just so we have something in the FML project
currently we have absolutly no idea if the code in FML just crashes if used π
This at least gives us some coverage
BTW maybe you see something immediately wrong with this approach:
I am doing this when we enter the primary FML launch process:
// Index current layers of the module layer manager
for (var layerId : IModuleLayerManager.Layer.values()) {
moduleLayerManager.getLayer(layerId).ifPresent(layer -> {
for (var resolvedModule : layer.configuration().modules()) {
resolvedModule.reference().location().ifPresent(moduleUri -> {
try {
locatedPaths.add(Paths.get(moduleUri));
} catch (Exception ignored) {}
});
}
});
}
LOG.debug(LogMarkers.SCAN, "Located paths when launch context was created: {}", locatedPaths);
The idea is that a FML locator can then check if a jar it finds has already been picked up by ML
Yeah
That should be fine
Allthough I I think you can just srhink that down to BOOT and PLUGIN
Cause the others are never present at that point
But I am not 100%
Because FML is simultaniously a PLUGIN and the Bootstrap handler
So.....
Kind of depends on what ever you exactly mean with the Handover XD
Well yeah, pretty much when we enter FMLServiceProvider
At that point, ML already discovered some jars via ClasspathTransformerDiscoverer and ModDirTransformerDiscoverer, and built PLUGIN using those
So you're right, I could only scan PLUGIN
IMHO that whole PLUGIN just needs to go.....
The concept that both do scanning
And both try to load the same thing
From the same location
Is just problematic
It runs into sooooo many issues
With duplicates alone we have a problem
Because every single locator needs to double and tripple check
But doing that means that at least some part of FML needs be on the boot layer and installed via the installer
Which is something I am totally fine with
Well, I agree with the first part, and we'll find a way around the second one ^^
But anyway, I need to find a way to actually test this at least once manually in all environments /sigh
IMHO If we define a proper API for FML
Then I don't really think installing FML is a big deal
Especially since we always updated FML with MC
And I personally think we should retain that update cycle
p.s.: I also added support for setting MOD_CLASSES from either a system prop and also via a file
we'll see if we can somehow make NG simpler / more robust using that. don't know yet π
I'll get on integrating the PR into NF as well
Very nice
I have it locally
The EventBusSubscriber thing?
Yes
Push it push it push it
I mean I can also fix it again in my PR but it'll just be noise in the diff
fuuuuck
???
@glacial crag This code is by you:
// Minecraft instance isn't available in datagen, so don't call initializeClient if in datagen
if (net.neoforged.fml.loading.FMLEnvironment.dist == net.neoforged.api.distmarker.Dist.CLIENT && !net.neoforged.fml.loading.FMLLoader.getLaunchHandler().isData()) {
In FluidType. Is this really necessary? Reaching back into the FML startup code?
probably from a reformatting commit though
that can be wired up as a DatagenModLoader check I think
oh yes that initalizeClient pattern will be removed eventually
Maybe replace that immediatly with a Version or Constants class somewhere?
Yeah that's what I am also thinking
This is very weird data flow
since it's essentially CLI argument -> FML -> [used to find neoforge jar] -> then read inside of that very neoforge jar π
the neoforge version should be available from FML already?
Yeah as the version of the neoforge mod
why do we even have the spec and the group
As far as I can tell, it's only used to log on startup
In NeoForgeVersion:
LOGGER.debug(Logging.CORE, "Found NeoForge version {}", neoForgeVersion);
LOGGER.debug(Logging.CORE, "Found NeoForge spec {}", neoForgeSpec);
LOGGER.debug(Logging.CORE, "Found NeoForge group {}", neoForgeGroup);
In NeoForgeMod:
CrashReportCallables.registerCrashCallable("NeoForge", () -> NeoForgeVersion.getGroup() + ":" + NeoForgeVersion.getVersion());
I'd just remove the spec and the group then
I'd keep the version, but get rid of the group, yeah
yes - keep version, remove spec, remove group is what I meant
and the version should be accessible via FML in all cases, it shouldn't be necessary to look at the manifest
The group is actually needed I think....
Not there
We used to use those in the past because of things like Sponge
Which build their API ontop of hours
And then modified
They have to add a sponge version info line regardless
But just to be sure, let me find some random sponge crash report to verify
I thought about it, yeah. currently they'd already have to patch our jar in case they really want to replace the group there
Yeah they're not supposed to patch our jar anyway
Uh... what do we even print there π€
I can't even find that log string in my FC logs
Okay that group is always net.neoforged since we don't specify an implementation-title in our manifest anyway
So I think it falls back to the hardcoded string π
... and the FML version is just bogus:
FML: 20.4
NeoForge: net.neoforged:20.4.196
that is computed by NG by trimming the neoforge version π
honestly the FML version is pointless anyway IMO
I mean if it says FML it should at least be the FML version π
I think that NG computes this by trimming the NF version
Ultimately we should merge https://github.com/McModLauncher/securejarhandler/pull/64 which would also make this unfeasible, but dunno if we'll manage for 1.20.5
the spec version var is a bit flawed anyway
It just doesn't exist at all in "vanilla" JPMS
iirc it also means that a neodev client is incompatible with a prod server
Not that - even if it did - it makes a ton of sense to log it
or meant, not sure if we fixed it
The next freakshow class is NeoFormVersion π
oh we're hosting a freakshow?
why did no one tell me, i need to prepare the peanut gallery of interaction events
now that is completely useless as far as I know
(the neoform version in general)
we should not even display it
That is also just logged
Essentially that entire class exists only for this log line:
LOGGER.info(NEOFORGEMOD, "NeoForge mod loading, version {}, for MC {} with MCP {}", NeoForgeVersion.getVersion(), NeoFormVersion.getMCVersion(), NeoFormVersion.getMCPVersion());
And this:
Idk
Keep in mind that errors won't be found as long as CommonModLoader.begin is not called
So it would be wrong to check hasErrors in an early mixin for example
Well mixins should not check against the ModLoader
They should check the LoadingModList
If they get called early that is
Well ModLoader does things quite late
Yes, but the list of errors is available from multiple sources in different states
That is bad
Yeah
Hm. I don't understand. When running the runs in the Tests project, client-extra is not on the class path. (But on the legacy classpath)
This is confusing. In the main neoforge project, it is on both.
I suppose I'll have to restore FML reading the legacyClassPath system property until we can really dig into this π
Great
BSL currently loads the client-extra jar into a module
Hence my code excluded it in FML since it was already claimed/loaded

what's the name of the client-extra jar?
yeah that doesn't work
it doesnt matter, the old code just looked in legacyClassPath for something containing "client-extra"
I'll restore that behavior and just somehow get it to work π
We'll clean this up for 1.21
yeah
do that
1.20.5 will probably release tomorrow
but it's ok if this takes a few extra days
I am just testing it manually now in all envs i can think of
and fixing bugs along the way
I was hoping we'd be able to drop legacyClassPath from FML but the NF environment is just... odd
For Tests: GameTestServer, client-extra is not on the CP
but it's on LCP
which imho should not happen
that is weird yeah
Even weirder it IS on the CP for the GameTestServer in the main project

Alright. Userdev also works
But only showing warnings for unknown files in mods/ sucks, since I lost some time figuring out that my own mod still had a mods.toml π
So we might wanna expand that to candidates that come in via MOD_CLASSES
Yeah I agree
@glacial crag so I am refactoring the locators again to support better control over warnings
Ultimately, I'll remove the return value and instead pass in another "pipeline" argument
π€¨
public interface IDiscoveryPipeline {
Optional<IModFile> addPath(Path path, @Nullable IModFile parent);
Optional<IModFile> addJarContent(JarContents contents, @Nullable IModFile parent);
boolean addModFile(IModFile modFile);
void addIssue(ModLoadingIssue issue);
}
I have yet to add the third arg to control:
REQUIRED
WARN_IF_INCOMPATIBLE
WARN_IF_SKIPPED
NO_WARNING
No idea how to phrase these yet π
what is happening π
What are you talking about π
why are there so many functions now π
I am removing IModFileProvider
That only existed because of the single return type function in the locator did not allow the locator to return mod-files at different levels of the pipeline
(the pipeline being: filesystem location -> jar contents -> mod file)
It also removes the entire reason I had to wrap the actual return type in a "DataResult": Stream<LoadResult<JarContents>>
Since I can just add issues directly via the pipeline now
I see
we probably want a standard helper for "try to add this path/jar content to the pipeline, if failed add an issue"?
Well, that's the other thing
We changed locator to only be able to return JarContent
But this way, a locator can also just add Path
and let the pipeline handle conversion into JarContent
And the pipeline will do what you describe (and auto-create the issue, centralize the exception handling for the Path->JarContent conversion, etc.)
ah ok nice
The only "issue" I still have: We have IModFile#getSource()
and if a mod file is located via JiJ, what is that supposed to return
I like this info output, but need to figure out why it finds mixinextras twice
[00:35:30.691] [main/INFO] [loading.moddiscovery.ModDiscoverer/SCAN]: Found mod file "server-1.20.5-20240423.152201-srg.jar" of type MOD [locator: production server provider +net.neoforged:neoforge:server:20.5.16-beta-pr-834-fml-locator-rewrite, system]
[00:35:30.700] [main/INFO] [loading.moddiscovery.ModDiscoverer/SCAN]: Found mod file "neoforge-20.5.16-beta-pr-834-fml-locator-rewrite-universal.jar" of type MOD [locator: PathBasedLocator[name=neoforge, paths=[libraries\net\neoforged\neoforge\20.5.16-beta-pr-834-fml-locator-rewrite\neoforge-20.5.16-beta-pr-834-fml-locator-rewrite-universal.jar]], reader: mod manifest]
[00:35:30.747] [main/INFO] [loading.moddiscovery.ModDiscoverer/SCAN]: Found mod file "mixinextras-neoforge-0.3.5.jar" of type GAMELIBRARY [parent: neoforge-20.5.16-beta-pr-834-fml-locator-rewrite-universal.jar, locator: jarinjar, reader: mod manifest]
[00:35:30.768] [main/INFO] [loading.moddiscovery.ModDiscoverer/SCAN]: Found mod file "mixinextras-neoforge-0.3.5.jar" of type GAMELIBRARY [parent: neoforge-20.5.16-beta-pr-834-fml-locator-rewrite-universal.jar, locator: jarinjar, reader: mod manifest]
[00:35:30.769] [main/INFO] [loading.moddiscovery.locators.JarInJarDependencyLocator/]: Found 1 dependencies adding them to mods collection
Alright tested in production client and production server.
Now gonna do the translations but we're closing in on this one
Alright, translations added. More tests added too.
This will require more testing and we'll likely uncover some bugs in configurations we did not think about.
Sooo, PRs ready. Now: protoyping time!
Hmm?
I wanted to make the locator and java coremod stuff reviewable before starting to try some stuff for later
I need like a bit of time as well
Alright, let me know when
/sigh everytime I run spotless in FML, it changes ALL files from CRLF to LF π
Hm Orion, on second thought I am too lazy to go and find my mike. Maybe another time?
Sure
Alright, I did a quick prototype: We could use a mini java agent to get access to Instrumentation, and use that to freely add opens/exports to java.base et al after the fact
Allowing us to essentially load our core modules later (as we do now), and add the exports/opens they need
This would - for example - solve our problem with opening up java.base to netty when we modularize it
Mock up:
class BootAgent {
public static void premain(String agentArgs, Instrumentation inst) {
BootAgentInterface.instrumentation = inst;
}
}
---
public final class BootAgentInterface {
static Instrumentation instrumentation;
private BootAgentInterface() {
}
public static void addOpens(Module module, String pn, Module to) {
if (instrumentation == null) {
throw new IllegalStateException("The JVM was started without the BootAgent. Please make sure that the -javaagent parameter was not removed.");
}
// Do a caller check here to only make this callable from our own packages
instrumentation.redefineModule(
module,
Set.of(),
Map.of(),
Map.of(pn, Set.of(to)),
Set.of(),
Map.of()
);
}
}
We could then call that BootAgentInterface from our normal startup code π€
Wtf
I'd just use --add-opens for the module controller and then we will be able to reflectively add exports to any module
No, we can open to only the FML module
I'd actually try not to require the boot loader to be loaded as a module itself
Or to the bootstrapper I mean
It doesn't hurt though?
It's ugly as fuck π
They use a dynamic agent
They will not be able to soon
If we're gonna use a static one might as well add the opens directly
And their dynamic agent uses the exact same principle
The thing about the add-opens is that you still reach into internal methods
Isn't that what the agent would do too?
No?
The mechanism by which the agent adds-exports / add-opens is fully documented & supported
The add-opens/add-exports it adds themselves, THOSE are obviously reaching into internals
but that is not actually our code mostly
i.e. netty reaching into JDK internals π
Ahh there is an API for that, ok
yeah that redefineModule call is actually a public API
In any case, I was just exploring the problem space π
Oh that is a nice API
I let a few comments on the PR
once they are sorted I'll do a bit of testing and then merge the PR
Thank you I'll go through them
p.s.: the trick to not loading transformers again is in LaunchContext#isLocated by scanning the previous layers
@glacial crag So i'll just remove the system mod flag on the mod-file level. Any mod-file can declare system mods via FML-System-Mods then
I also added a priority-system to locators/readers since they could snag the candidates from each other
Otherwise, review comments fixed, I'l re-request
I think that we can just hardcode minecraft and neoforge as system mods
System mods = what we need to load to get to the error screen
I generally prefer text messages so that the people who are not in the call also get to follow the discussion. It's also too easy to say "I was in a call with X other people so now we'll do what we decided among us"
Out and about right now. So I don't know. What do you want to discuss? Is it.about the current PR? And yeah I like asynchronous comms more hehe
Yeah that would.go beyond. We would then do remove the manifest entry.from NF and didn't want to do that right now
I understand that, but I am not a writing person. Especially not when I am initially formulating and transmitting ideas.
I am a fan of writing it down though once my idea is not stupid.
That way we can all work together on it
Well we can keep the manifest entry even if we don't use it anymore
Idk, I'm not convinced by that approach π
It doesn't seem to work that well
I understand that
But that is how some people, like me work
I first want to bounce my ideas of someone with domain knowledege
I mean it could also be you
But shartte was working on it
Ya I understand. It's just that I usually do many things simultaneously and hopping in voice means I have to drop everything and focus on that for potentially hours π
Hence my hesitation
And my ADHD workflow is usually coding it until it works and I like it .
I mean he has the same insights I guess
That would normally be my way as well, but for this kind of piece of infrastructure, a bit of org and idea collection ahead of time, seems reasonable
We often forget some weird use case during theoretical discussion. The more we model in terms of tests or integration tests the easier.that workflow becomes π
Yes for sure
Btw that is one of the reasons I would still be in favour or moving stuff to the NF repo. I have hopes this would enable one click e2e testing of a production like environment
Unless we can find some other way
Right now it's hard enough to check if NF makes use of some FML method hehehe
Sounds good now that I got sidetracked by neoform in a box lol
My basic idea is to make ML a library and move the entry point entirely into FML
So many things to do π©
That is the other thing I need to do
Rip the pipeline stuff from NeoForm
That was my SECRET plan!!!!
π
Our ideas are not that stupid
The concequence is that FML needs to be installable (or some module from FML)
Because yes it's sooo much easier that.l way
My plan was to make FML so small by moving most of it into NF
That updating it only on MC breaks is easy
We can keep a BSL style mini wrapper whose only purpose it is to load NF from the mods folder
It would be version agnostic though
I hate the fact that ML/BSL do mods dir locating for plugins
And that these locators work completely differently from FML locators
And behave completely differently
They don't need to be
That is the point
And right now that is the part that always introduced the most complexity for me
The idea that there are two locator types
Named the same
Scanning the same directory
Could always take the approach quilt loader's loader plugins take -- plugins there are able to locate more plugins. So, in this world, you'd have a system for mod locators to be able to locate more mod locators
And then in theory it could all get moved to one sort of locator. Unless there's something besides locators that has to be discoverred that way -- I'm not familiar enough with all the bits and bobs of that system
Well yes but you need to find the first one
We want to install into the launcher version as little as possible
With the goal of having users only consciously install once per MC version
You'd have to have 1 or 2 built in, yeah
And there's really no way around that
Because something has to start discovery
I thought about a bootloader that scans mods/* jars for a manifest attribute and loads a subfolder in those jars all into a boot layer
No complicated per file selection
Seems like more complexity and duplication
We can not use JIJ as is imho since we otherwise cannot remove libs
If the user still has the old NF mod in their mods folder
As a mod maybe but the boot system should be all or nothing as it is now
Nah, I don't see a need for it to be
I do I don't wanna debug half of the system being loaded from nf-old.jar and the other half from nf-new.jar
Why would you have two nf jars in the directory?
Have you met our userbase
And the answer there is simple -- nf jars would pin the versions of specific things that ought not to be upgraded, so that would be an error case
As it ought to be
It is for mods though? If I put two versions of the same mod in a directory it'll error
Won't it?
I don't think so
Dear god, does that not error at present?
That's... mildly concerning
And that is on purpose
It's user friendly
Does the oldest participate in jarJar selection at all?
It does
Oh wow, that's atrocious. So you get a jar that is not itself loaded, but may have it's components loaded?
Ah well
If you expect mods to work fine with that, you should also expect NF to work fine with that
So I wouldn't see that possibility as a limit on recursive locator discovery
After all, anything NF JiJs, mods could also JiJ newer versions of
So the risk of loading half from one NF jar and half from another is equivalent to loading half from the NF jar and the other half from whatever mod bundles it
The problem I ran into conceptually with recursive locators is that an older version of X might be loaded already
And then a newer one comes along by a locator that was loaded later
Going back to this -- this seems like an issue. If you're picking only the newer mod to load, the older mod shouldn't participate in jarJar version selection. Because, say, two different versions of the mod might pin a bunled dep to different versions. We really only want jars that are actually selected to have any of their children actually selected
We need to construct some test cases and d.efine the desired outcome
There are some.fucked up cases
I'd almost phrase the problem in reverse. We want to take a full tree of jars and subjars, and trim out branches to leave no duplicates, preferring newer versions higher in the tree
Could just be that selection between different versions of a locator is only allowed between locators at the same level -- if a locator was already loaded by something in a level closer to "root", it can't be loaded. Otherwise you end up needing a system to "unapply" loaders and that gets complicated
But I can also see how allowing recursive location of locators could add complexity
I mean mods loaded by locators
Oh, sure, but that would presumably be figured out by the same sort of system that selects the newest version of multiple JiJed mods, right?
I don't think it makes much sense
I should clarify. Not mods. Services π
loading service-layer jars from mod jars just makes no sense
The case I thought of:
server pack locator is the only present file in mods/
it bundles earlydisplay
it then locates neoforge, which includes a newer version of earlydisplay
no dice
We already do that tech
since you need the service layer to exist to locate mods
sure, and it only works in hardcoded locations π
I'm aware π
it's the best use case
Sure. My proposal was basically to have locators run first and be "special" and then you could treat everything else sensibly after that, but, I see your point. Basically -- my thoughts on this are that really, anything except a mod locator itself should definitely make sense to be locatable from jarJar-ed stuff
I should also clarify that we should write down the use cases, and those can become test cases later π
Yeah that also makes a lot of sense
there is a bootstrapping problem because things like GraphicsBootstrapper are not mod locators but run before mod locators
there are some pathological cases that I don't really care about but we should at least be aware of.
i.e. I think we currently determine the "file version" based on the "primary mod" in mods.toml (which just happens to be the first...)
Ugh, that runs before mod locators?
of course it does since it can be used to show progress of the locators
Yeah the whole thing with file versions is just weird
that "ugh" annoys me π
One of the experiments we should do: bring up a window using GLFW, render some shit, tear it down and then see if the Minecraft window opens up successfully... (on Mac)
Locators ought to be fast enough that their progress shouldn't need to be shown, imo
have you ever heard of ServerPackLocator?
I was about to say π
that will break our (Sodium/Embeddium's) use of GraphicsBootstrapper
that's the problem
And even normal locators on spinning disks surely exceed the normal rule of ~150ms without feedback
I think mod locating takes a few seconds on my system, yes
on SPL there is seriously nothing you can do
Anyways, in terms of file versions: If I stick two different LIBRARY files in the mods folder, that provide the same thing but with different versions -- how does FML figure that out? I've tested it and it seems to work, but what's it using to figure out that those're the same thing, and what the versions are, from the actual jar?
There is some hackish code that tries to parse it out of the filename
IIRC in SJH
only a self-extracting service into mods so that it's picked up on the next launch, pretty much
Do you have a link to the hacks handy, embedded? Or would you need to search around for it?
well that's not the worst xD
I feel like mod locating taking a few seconds is... more than a bit concerning. What's the bottleneck on it?
That's a bit
. Does it at least try to make use of the automatic module name and declared specification version or something? Now I have to check
normally in vanilla/Fabric, the call to glfwCreateWindow is intercepted and before it runs, either an environment variable is set, or the process command line is changed (
) to instruct the Nvidia driver not to enable an optimization (the exact mechanism depends on operating system)
on (Neo)Forge, this is currently a no-op because the window was already created at that point
Scanning hundreds of files?
Honestly embedded
That's it? π
Why isn't that just in NeoForge as a feature optin?
because the implementation is horrendous (you're not exactly supposed to change the command line of a process in Windows at runtime, afaik)
But the more important question: does disabling threaded optimizations have any effect when embeddium is not present?
How much scanning is needed to construct the minimal info needed at that point though? In theory you just need to figure out what's in it and the version at that point, right? Or is there other work done then?
It only says should in the Windows API π
The lifetime of the returned value is managed by the system, applications should not free or modify this value.
Parsing the main zip file table of 150-200 files takes time if you have a spinning disk
Not to mention, we do it multiple times.
I mean I know, one of my laptops has a spinning disk
@plain solar What irks me though is: what part of the cmdline is nvidia looking for. Is it that ugly-ass system prop mojang sets?
I don't know if anyone knows for sure
at some point I believe JellySquid had reverse engineered one version of the driver to figure it out
but I also think the detection mechanism may have changed since then
The approach is not suitable for inclusion in Neo as-is, is my point
And is that nvapi that comes up on google not an option?
iirc for Minecraft specifically the driver ignores a number of the reasonable ways of disabling this feature
If I read that API correctly it only looked like it was managing the profiles found in the control panel anyway, which sucks
I think installing a profile was explored at some point and didn't work
Should just try to use ANGLE lol π
I wonder though, is it this setting? "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump"
Hm, probably not. It says Intel drivers
they still ship that in 1.20.6?
for now the next version of Embeddium allows solving it with a manual edit of the FML config file
We need earlydisplay BEFORE location is done, really
ah we have priorities now?
ELS needs to be started before locating
For me the order would be:
- Main()
- Locate platform (optionally if main is not in FML)
- Start ELS
- Start locating
It is a chicken and an egg problem
in the PR I mean
Ah well then I have no idea
@haughty copper I'm happy with the PR; I will do a bit more testing right now, hopefully we can merge it afterwards
one question: priorities?
our priority should be getting it shipped
I'm aware, that's why I am in this channel discussing the PR π
yes π
simple one
I suppose it's mostly useful for mod file readers rather than locators
a fabric mod adds a neoforge.mods.toml just so that people that try to run it on neo would get a "missing connector" error screen
but connector needs to be able to parse that file instead of neo as it's still a fabric mod
agreed
The problem with that order shartte is that ELS becomes fixed
No more modifications by mods to ELS
Yuuuuuup, same thought here
Yes, true. For readers it's really required since they're first come first serve
For locators, there's the thing about ModDiscovery skipping paths that are already located
Difficult situation to be honest
Custom locators are really usefull (at least can be, for packs etc)
The ability to modify the GL context is niche but used by some mods
Soooo
Not sure where to put that
For VERY early error -> AWT Error Dialog and System.exit (So, errors in completing 2))
That is if we cannot even find earlydisplay
ELS would just always be in FML
But yes, the use case that embedded has... We really have no good option
The worst case scenario really is SPL
So there would not even be discovery if main is held in FML
Yeah but I can think of other scenarios where it takes longer to locate
They all involve any kind of async operation
Or slower networking / disk
Then there is runtime patching
That will take time
So....
All of that
@haughty copper not quite there yet?
MCF is missing a lang entry
apparently the fabric one doesn't have the right context?
why is the button placed so horribly like that 
might just be a bad merge on my end
Uh that button is not on me
Okay I gotta double-check the translation context
that entire system is an unmaintinable mess
it really is
just pad with nulls until the file is at position 2 I guess π
Urhgulhrgulh π
Platin probably missed that when they added the quit button
yeah, looking at the PR code, I did miss it
I update the x position for one, but not the other
it's also weird that it says that 1 warning has occurred even though there's 2 of them π
I didn't touch that Β―_(γ)_/Β―
Did you push your update to the branch, Tech?
I added "for Minecraft Forge,"
since, people are just gonna stir up drama if a MCF mod gets deteted as "for an older version of NeoForge"
or did they change their mods.toml?
y'all want me to quickly make a fix PR?
that's good
you should add a comma after NeoForge but that's a very minor nitpick
are you fixing the button placement too?
and if you could also check the displayed warning count... π
no π
then yes
BTW, I looked at the warning count
that was always broken and used the error count instead
and any count == 0 got clamped to 1 lol π
wtf
alright, gimme a min
Well yes, the actual lang-entry hard codes it to 1 for <= 1, which is ok: {0,choice,1#1 warning has|1<{0} warnings have} occurred during loading
just not when it accidentally gets shown for count=0
what is this arcane syntax
Some apache common lang stuff? Maybe?
@glacial crag Okay, done https://github.com/neoforged/NeoForge/pull/834/commits/86251bf6f6f706ae6c2307560d563c76cd041311
I'll do more testing tomorrow then hopefully merge the PR
oh yeah definitely
I just want to do a bit of low effort testing to make sure that we don't ship something too broken π
yup and it's much appreciated
is the dirt background totally gone from vanilla code now? I much preferred it over this red... maybe a shade of brown would work better?
okay i'm back home so what kind of testing do you need @haughty copper
your PR is using the PR artifact build, right?
(neo PR)
yes it is
@haughty copper pls fix merge conflicts and bump the NG version for fixed installersβ’οΈ
What did you do
Both done
going great
I love how most of that message is just the first line, with the extremely long command-line invocation
making it 18 KB in size
Build file 'P:\NeoForged\NeoForge\projects\neoforge\build.gradle' line: 11
A problem occurred evaluating project ':neoforge'.
> Failed to write dummy data for dependency: ng_dummy_ng.net.minecraft:client:1.20.6:client-extra

i want to stab someone
like right now
Is this refactor related?
doesnt look like it
--fml.neoForgeVersion 20.6.3-beta-dmaps-updated π
yeah i forgot to refresh gradle
now it is refactor related

so uhm @haughty copper it's obviously your fault
ok so
i want someone to quickly PR a try catch there
i can debug it but it's not the first time i see the error, and if a modder encounters it too it will be painful to debug
java is also stupid anyway
Not my fault π
and it is indeed empty
anyway i still want that pr kthx
we really should have exception handling in all SecureJar#froms
so actually a try catch is better in another place
They should all throw IOException but alas
checked exceptions are super annoying
No?
They exist so that people check them
Checked exceptions are a very nice feature IMO
UnionFileSystem#openFileSystem needs to log the path when rethrowing the exception
IOException and InterruptedException are the only real "tolerable" ones
i still do not quite understand how best to handle InterruptedException, to this day
that makes two of us π
shartte
Thread.current.interrupt() if you don't rethrow
Maybe you have two gradles
otherwise rethrow
I thought you were commenting on my lack of skill thereof, maty 
can you try running the updated NG too
nice little spring cleaning
uh you mean in userdev?
that too
will try
make sure to pull my commit updating the wrapper and gversion
i feel like for some reason, NG (at least recently) is too keen on recreating ZIP files from scratch, even if they theoretically shouldn't be
it is
neoform... in a box! π³οΈ
i always wanted my setupDecompWorkspace back
Failed to write dummy data for dependency: ng_dummy_ng.net.minecraft:client:1.20.6:client-extra
run the select task manually
then refresh
then try to run client
fail
run the select task manually
observe the file exists
run client
fail
also look what i noticed randomly looking through NG code
oops.
Well it's a bit... we're only doing a joined dist anyway
the client seems to be a bit of a misnomer
So this could actually be correct
Depending on the context
The button overlap is from another PR that got already merged into 1.20.x
(and yet another PR shall fix it, but not this one!)
so far this has been a very long and painful road π
Also, yes same issue for me on neoforgedev, maty
Exception in thread "main" cpw.mods.niofs.union.UnionFileSystem$UncheckedIOException: java.util.zip.ZipException: zip END header not found
Caused by: java.util.zip.ZipException: zip END header not found
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.findEND(ZipFileSystem.java:1320)
orion broke it
What did I do?
broke neodev
MDK works
also @haughty copper want to know why pr publishing installers don't work?
the transitive PR repo is missing
I had to patch it by hand
(the installer json that is)
vs
that's not why
URI#resolve being annoying is why
lol
yeah welcome to me ~6 months ago
I discovered how fucking shite the Java URI class was back then
there are a ton of footguns with resolve especially
private static URI addLeadingSlash(URI uri) {
String asString = uri.toString();
if (asString.endsWith("/")) return uri;
return URI.create(asString + "/");
}
maven urls don't have query strings
I know it's just not a general purpose "resolve this relative path against this url"
prod works
yes but for this use case specifically it works
@haughty copper https://github.com/neoforged/NeoGradle/pull/159
i understand about nothing of this ng code
Waiting for tests to complete then I'll merge it. Why do we not have the auto-merge on that repo?
it needs the branch protection to be setup first
added
Leading I think you mean Trailing ;P
smh maty
oh lol
@chrome mortar wouldn't using the process-test-data job for the status check still mean it will always pass
because the job is set to if: success() || failure()
yes
NG has historically had failing tests

and with it using +... i doubt it won't have in the future
if we always delete and recreate the file not only do we cause needless disk strain but also introduce more random errors with concurrent invocations
which IDEs do do
#builds message
and orion disabled my correct auto merge and instead enabled it with the default commit message
[Jump to referenced message](#builds message) in #builds
7.0.117
NG_7.0
Fix installer URLs without a trailing slash (#159)
- Fix installer URLs without a leading slash
URI#resolve is stupid.
- I meant trailing trust me

so is this usable or is NG still preventing it?
[Reference to](#neogradle-dev message) #neogradle-dev [β€ ](#neogradle-dev message)@glad cobalt why exactly did you change that
@glacial crag you have 3 hours to test the fml pr more if you want 
Successfully scheduled reminder on <t:1714579535:F> (<t:1714579535:R>)!
sounds good 
Is this sufficient, @chrome mortar ? Or did I forget something?
The overall goal of this PR is to make the code structure in the FML mod discovery process easier to follow and to support a better end-to-end error reporting experience for mod loading issues of all kinds.
To this end, the PR rewrites the mod discovery system with the following changes:
- Remove superfluous API surface
- Add test coverage to the mod discovervy process by trying to simulate our common installation environments (Production, NeoForge Developoment, Mod Development)
- Unify the reporting of mod loading errors and warnings on a single type
ModLoadingIssue, and provide a single exception that can report one or more such issues up the call chain (ModLoadingException) - Allow locators to produce output at different levels of the discovery pipeline:
- Unprocessed filesystem locations (i.e. from files in the
modsfolder) - Virtual Jar Files combined from various file-system locations and filtered (i.e. userdev locator that splits the combined Minecraft+Neoforge Jar into multiple separate virtual Jar files)
- Fully built mod files (such as the Minecraft mod-file for which the locator provides the mod metadata on-the-fly)
- Unprocessed filesystem locations (i.e. from files in the
- Allow locators to also supply mod loading issues (warnings and errors) on a per-file basis
- Introduce a priority system for locators and readers, allowing user supplied implementations to go before/after the built-in ones
- Untangle the dependency between locators and launch targets by having launch targets supply additional locators that are specific to the environment they are concerned with (i.e. the production launch targets supply locators for the production client/server)
- Untagle the dependency on the early phase ModLauncher discovery by determining whether a file has already been loaded not by reaching into previous locators statically, but rather by inspecting the ModLauncher ModuleLayers that have already been built when FML starts mod discovery
that looks very good
Should I also approve the FML PR π
lol
@chrome mortar
merge fml pr
gotta wait a second, i'm adding that logging line to sjh
When you say logging line, do you actually mean to say add the failing path to the exception message?
yes
@haughty copper https://github.com/McModLauncher/securejarhandler/pull/71 i'm waiting π
Where is the PR description π
public UncheckedIOException(final String message, final IOException cause) eclipse user detected
or where are the finals coming in
Also. What the fuck is this private static class UncheckedIOException extends java.io.UncheckedIOException {
my hand?
@haughty copper did you never update sjh in the fml pr? 
@glacial crag reapprove 
why strikethrough?
the 3.0 SJH bump broke a test
that and we don't dismiss approvals for FML apparently
did something else change in SJH? 
shartte pr'd something
I need to figure out why it broke first
you mind if I try to fix it? 
What was it?
you turned the bipred<str, str> into a unionfilefilter
btw this is suspicious
hehehe
the tests are running, this should fix it
in the meantime someone should write a quick announcement
"hey your locators are broken"
Ah yes, the PR to SJH was in response to the FML locator
since string copmarisons on the base path are fucking stupid
@glacial crag approve
approved
so exactly how much carnage is this PR going to bring when it's merged
welp, asked too late
guess I'll find out
@plain solar in all honestly, probably not a lot
There might... no there certainly are still bugs. But there aren't that many custom locators out there, and adapting is not that hard.
I know i know
Error: Exception in thread "main" java.lang.module.ResolutionException: Module JarJarMetadata reads more than one module named cpw.mods.securejarhandler
Welcome to dependency hell
Why did you have to go and update that SJH dep, Maty? π
Whyyyyyy π±
Hey everyone!
We have recently merged a PR rewriting the FML locator system, and it has been released as NeoForge version 20.6.14-beta, affecting only 1.20.6.
This update is more technical than developer-facing, but it will break any mods adding custom locators. However, mods for another loader (including mods using an old mods.toml file) will now be correctly detected and warned about, in a way that will not give mods such as Connector headaches.
This is quite a big update, so please do not hesitate to report any bugs you find.
Make sure to announce it as affecting 1.20.6 only
more prominently than just the version
ok apparently you can't even sort mods within a single mods.toml declaration?
the declaration is done at the ModInfo level but the sorting happens with ModFileInfos???
I don't think it's your fault
Especially since we use the first mod from mods.toml as the "primary mod"
the sorting is done across files instead of being done across mods π
yet the dependency declaration is per-mod
uhhh
got this after updating to 20.6.18 in my dev env
the actual cause of the error is unrelated
but why aren't these translated?
There is an error during an event!
Oho! are you doing that as an FML test? π
yeah π
I did think about having a test for this inside of FML, but I didn't want to be mocking everything
@haughty copper re #124, I am very much leaning towards closing as not planned
I'd like to understand why Gradle is not collapsing the dep
and instead decides to include it multiple times hm
We can pull it from the legacy class path again, which is why this was not happening before
The issue is that Gradle has no configuration which adds to runtimeClasspath and compileClasspath WITHOUT being transitive
which means that you need to declare a custom configuration in order to use NeoForge currently
that doesn't make leaking neo deps any better π
and this people is why NGs rule is stupid but I digress
I feel that you and @glad cobalt need to duke this one out to figure out what you actually want to do π
Is it bad?
Because your mod does depend on neoforge
why should it not declare it?
because as it stands currently, you must declare a custom configuration to "correctly" consume NeoForge
which is a lot of work that wasn't previously necessary
Didnt we do all this dep replacement dance so the normal mechanics of gradle would work?
so either NeoGradle should supply a configuration which does this work, or the locator just doesn't throw for duplicates
meaning if you depend on 2 projects which themselves depend on net.neoforged:neoforge:x
it would resolve it so you only have that dep ONCE
Not throwing for duplicate is not an option, it would have to be switched over to using the legacy classpath hack
since if we have MC on the classpath from across multiple projects, it could pick the one with the wrong ATs applied
i seriously did not account for this case happening because it seems like a NG bug
the issue is, to my understanding, that because the artifacts are different, they get all get passed together
(or me misundestanding basic gradle)
the repos are also different, to my understanding
yes but that shouldn't matter? for external deps you dont know the repos they originally used at all
that is, one mod project's ng_dummy_ng repository is different from another mod project's ng_dummy_ng repository
Β―_(γ)_/Β―
imagine if we had a proper configuration for neo stuff that didn't get leaked π
Gradle was a mistakeβ’οΈ
I think neogradle prepends the ng_dummy_ng prefix to the group to force it to lookup from the repo it made
or something to that effect
pipe dream innit
I'm not too sure
Presumably yes, but the dependency in the other subproject has the same prefix
cause right now it's an "implementation" dep until it isn't lol
should it be added to an equivalent of localRuntimeOnly 
I really think we should fix the root cause of this if we actually can π€
The thing is: for subprojects you dont want the dep to leak, for transitive deps on other mods you actually might
isn't leaking the dep always incorrect?
why should a ng_dummy_ng dep leak
They do depend on it
yeah, I theorised another workaround which would be:
configurations {
whyNeoforge { }
compileClasspath.extendsFrom(whyNeoforge)
}
runs.configureEach { run ->
run.dependencies {
runtime confgurations.whyNeoforge
}
}
dependencies {
whyNeoforge "net.neoforged:neoforge:${neo_version}"
}
what??
it leaks the replaced dependencies?
that's not gonna work don't even bother lol
not the original one?
uhm that's going too far
pretty sure yes
just add to runtimeClasspath
blergh that sucks
what part of replacement don't you understand π
all of it
hehe ok; I can say I understand 1% now
tech the question is what part you understand and the answer is "si pronto"
wouldn't only adding to runtimeClasspath mean you can't compile against it?
the top or the bottom 1%? π
idk π
"any" 1%
to both classpaths
so essentially you did an any % speedrun
the whole runtime thing is not needed
And yes blergh that is incredibly bad that it leaks the replaced deps
right, that's good
i wasn't super sure at first so i played it safe
Gradleβ’οΈ
what would be beneficial is if it exposed net.neoforged:neoforge:20.6.100 so gradle picks the highest required version across the set of dependencies
in theory
at the very least, I think we got the "workaround" working for soaryn's stream :p
ng_dummy_ng speaks for itself.. "oh ng, you dummy..." /s
also people need to stop proxying soaryn, just get the guy to ask for support himself in the official servers and repos lol
i don't disagree buuut this also affects me
I don't think that's feasible
and soaryn probably won't ever change because, y'know
that's just how some people be
leaking mc deps has historically been badβ’οΈ although that was mostly because mappings affected the version
localRuntimeOnly? that exists?
in that case it shouldn't leak to other projects
only in the MDK
it doesn't, it's part of the mdk
ah, TIL
ideally ng would have a config for this, but this isn't an ideal world..
we'll have to reconsider that stance because it's quite ridiculous IMO
well, please do let me know when you've decided what the actual solution should be
the solution for now is to use these configurations
you can copy the definition of localRuntimeOnly from the MDK
we will look for a better fix on the NG side whenever we can
yeah
So do we however for the time being make the locator use the legacyClasspath?
please no
we have uncovered broken behavior in NG / whatever people are doing
let's not add a workaround that will stay with us for years
at the very least, documenting this somewhere outside of this thread would be very helpful, which is also part of the reason I opened the github issue
because I imagine it's a good number of people who currently have this "issue" π
I really don't wanna fuck over modders until we get around to fixing NG π
the response needs a minimum of gradle understanding unfortunately
Since from a modders perspective, implementation dep on NF is the correct intent
I do; otherwise NG will never be fixed and FML will keep that crappy hack forever
implementation leaks the dependency to consumers π
you see, I look at this issue as living proof that NGs design concepts are flawed
it's a motivation to get them changed :P
can you when you do replacement, replace the replaced dependencies in another configuration?
TBH we should drop the fancy idea of adding it to implementation altogether I assume, we'll never get this to work right
if dependency replacement cannot add dependencies to other configurations than the one the original dependency is in
the entire point of dep replacement is that it's local to the configuration
Dep replacement should be yeeted
What's the advantage over another system? That you can have multiple neo versions in a project and it's easier for non-main source sets? Cool, we can do that without dep replacement...
We already link stuff to a source set when we do dep replacement (and runs and stuff pull that), so it's not true dep replacement anyways
Might as well just go all out and fully link it to a source set
Can i get a tldr on what is the issue. Cause the GitHub issues on this are unclear. And the suggestions are weird. We already do the split replacement. It is the main reason dep replacement exists in this weird way per configuration. It can't leak to consumers cause if you add it to implementation it is not actually added to that configuration but to complieClasspath and runtime classpath instead. Even in multi project workspaces Gradle does not leak them through.......
Is the ide being stupid here?
Using your own configurations does not help here then....
Because it would still need to end up in runtime classpath and compile classpath for it to work....
I guess it is because they are different paths...
And that causes issues
Because that will create duplicates at runtime in ides
Hmm
Not sure how Gradle handles that internally
That might indeed be a design flaw
But has no bearing on anything you all discussed
Actually it is not, I checked how FG6 handled this.
And there you also get two jars in the CP (well not if you had exactly the same setup, but two different ATs already sufficed, and is not preventable!)
I think we can collapse them trivially if they are actually 100% the same
And then we should
But you will not get around the problem of handling this natively in FML somewhere
The IDEs will always pass in two Minecraft instances
That is literally what it already does.....
So it literally already does what you are wanting it to do
"it is not actually added to implementation but to compileClasspath and runtimeClasspath"
How is that intuitive in any way? The entire argument for dependency replacement was that it's supposed to be more or less obvious
On top of that, the dependency replacement code is one of the worst pieces of code in the entire NeoForged project. It's completely unreadable.
It's not obvious at all what that configuration does in the subsequent Gradle and lambda soup
It is not..... It is the concequence of luke not wanting my stupid pom hack from NG6 and FG6.
Sure it needs cleaning
And that can be done relatively easily
But in this particular case it is a straw man argument, cause cleaning up that code, won't solve your issue with multiple Forges beeing in the classpath.
Which is a problem of the FML rewrite combined with how IDEs process dependencies with the same GAV but in different locations. Gradle will just pass one as far as I can see, while the IDE will pass them all (Aka Gradle differentiates by GAV, while the IDE differentiates by path).
This in practice is not solveable by NG7, because as I already said, you can easily get multiple distinct NeoForge jars by using different ATs (something very common in multi-loader project, or even combined workplaces). This was already the case in FG5+, and the old FML handled this gracefully, which clearly indicates that this is a regression of some kind in FML.
That said, I agree that I need to rewrite thet dep replacement logic, to make it cleaner and better documented.
At that point we might as well just have a DSL to select the NeoForge version and get rid of replacement
Yet there were several people in the community which wanted different NF versions per sourceset
For what ever fucking reason
And that can be done with a DSL too
The dep replacement (outside of the code being attrocious) is not the problem here.
So can we please get back to fixing the problem at hand, instead of drifting of into topics which have nothing to do with it?
I understand that you find it complex, ugly and weird, and your complaint is noted and will be addressed in due time
But we have a bigger problem to solve right now
You all stated you don't think this is an FML issue, however I can clearly show that even with all of your wishes completly full filled, in multi-project layouts it is trivial to get a duplicate NF or minecraft jar
Is that sufficient to call it a regression in FML?
Or do we need to discuss this further?
I'm not sure I understand how the dep is leaking to other projects
So wait. If you have the ability to add deps to other configurations based on dependency replacement, why do we not add a configuration that does not leak outside of the project
It is not