#FML Clean-up
1 messages ยท Page 3 of 1
Yeah unsure myself. Patch might actually work ๐ค We could also have the installer generate it explicitly I suppose
Neoform adding it is probably a bad idea since it's NeoForge specific
Although the credits that are currently in the generated file refer directly to MCP
To which NeoForm would be the successor
I would have NeoGradle generate it as a Platform task
Probably
The question becomes neodev runs
How do you get it there
And how do you get the right MC version
This is what we currently generate in code:
final var conf = Config.inMemory();
conf.set("modLoader", "minecraft");
conf.set("loaderVersion", "1");
conf.set("license", "Mojang Studios, All Rights Reserved");
final var mods = Config.inMemory();
mods.set("modId", "minecraft");
mods.set("version", FMLLoader.versionInfo().mcVersion());
mods.set("displayName", "Minecraft");
mods.set("logoFile", "mcplogo.png");
mods.set("credits", "Mojang, deobfuscated by MCP");
mods.set("authors", "MCP: Searge,ProfMobius,IngisKahn,Fesh0r,ZeuX,R4wk,LexManos,Bspkrs");
mods.set("description", "Minecraft, decompiled and deobfuscated with MCP technology");
conf.set("mods", List.of(mods));
I mean NeoForm already injects that ancient MCP entrypoint. It could also just inject this?
I always wondered about this thing. Does this even do anything? https://github.com/neoforged/NeoForm/blob/1.20.5-dev/versions/pre/1.20/1.20.5-pre1/inject/mcp/client/Start.java
It is normally needed for neoform runs only
But since NG7 we don't have those anymore
Yeah in NG7 we could just start the original entrypoint
We also don't ship that logo anymore: mcplogo.png
No wait. We do ๐
In NeoForge itself
I know, we ship the logo still in NF Lol
We should just use this asset as the logo ๐
Your thoughts: NeoforgeXyz or NeoForgeXyz in class-names?
Judging from NFs own code, It'd be NeoForge, right?
ye, NeoForge
Nf
We don't really have much of a use for the Common intermediary classes without FmlOnly:
For production use at least
@chrome mortar
Keep in 1.20.5?
// TODO - 1.21: remove the fallback to the mandatory field
remove
its not feasible to keep it since locators and providers are different and you can't know if it's from modclasses
Yeah heh, that's why I stumbled upon this
and besides people will have to change the mods.toml
I am triyng to split up the responsibilities of MinecraftLocator
The alternative would be to check if the primary path of the mod-file is a directory
Oh right, if you're changing locators and launch handlers I have some cursed code which hooks into neogradle for cross-project stuff which would be nice to include
...?
You'll need to be more specific
A key thing I am currently trying is to "invert" control and have launch handlers only return additional locators/providers
https://github.com/FiniteReality/ng7-locator-demo/ was the version I showed to orion, who said they liked the idea in principle
instead of the MinecraftLocator reaching back into the launch handler
So the production launch handlers will return a provider that knows how to get a IModFile for minecraft in a production environment
Or rather, one for server, one for client (since they differ)
yeah, makes sense; I just wanted to make sure that it'd work well with mod sources being passed via different means in userdev
since IIRC that's also done by the minecraftlocator stuff
Maty already had split locators into two distinct concepts:
- Locators -> ModFileCandidateLocator: Only responsible for finding potential (they return JarContents)
- ModFileReader -> Turns JarContents into IModFile
- ModFileProvider -> Creates IModFiles out of "thin" air (meaning they dont postprocess the result of a locator)
Awesome
Yes but I am not at the point of MOD_CLASSES yet, but I want to split that off from the responsibility of whatever is providing the minecraft sources
The annoying part is that our platform provides the MC classes via MOD_CLASSES too
but that's actually not great
in NF dev
Sorry for getting in your way then ๐
I'd probably favor detecting the MC classes via finding one of the MC classes in system classpath instead
But. Not there yet
I'll keep an eye out for the changes though so I can update my gradle plugin and then maybe get it upstreamed into NG
Well what exactly is it doing?
I could look at the source, but can you summarize? ๐
It's passing mod classes via a gradle artifact, so an outgoing configuration variant
what is the interface towards the actual mc entrypoint
So instead of using MOD_CLASSES it instead passes the classes via, currently, a command line parameter
so same thing
err, a system property but yeah, same thing
You could potentially acually use an arg-file and have a task write that
yeah
If this is for dev-only
that's pretty much how it works
That way you would get away with IDE sync not having to resolve this
I can actually get away with it anyway, since there were things about the Gradle API I weren't familiar with when I first implemented the plugin
We do some real wild shit in our launch handlers, jesus
client-1.20.4-20231207.154220-srg.jar
The srg classifier was just a renamed client? I Forget
It doesn't have the MCP start file so I'd assume it's completely unpatched
neoforge-20.4.214-client.jar is then just the jar with actually patched Minecraft client classes
Alright I can run this from a unit test, that makes things a lot easier
@Test
void testDiscoverMods() throws Exception {
environment.computePropertyIfAbsent(IEnvironment.Keys.LAUNCHTARGET.get(), ignored -> "forgeclient");
FMLPaths.loadAbsolutePaths(gameDir);
FMLLoader.onInitialLoad(environment, Set.of());
FMLPaths.setup(environment);
FMLConfig.load();
FMLLoader.setupLaunchHandler(environment, versionInfo);
FMLEnvironment.setupInteropEnvironment(environment);
Environment.build(environment);
var launchContext = new TestLaunchContext(environment);
FMLLoader.beginModScan(launchContext);
}
Well. Once. java.lang.RuntimeException: Environment is a singleton
Our code is really really not testable
reflect that field to null first?
there's a test setup step right? or a "before each test" thing?
Yeah sure, I am thinking about just lifting that restriction ๐
This is FMLs environment, so ... I can just change the build method
Module org.apache.httpcomponents.httpclient reads more than one module named cpw.mods.securejarhandler
lovely ๐
why does this keep happening
I suppose that it gets loaded from the LCP for some reason
ah yes of course
it's the fucking ignoreList
-DignoreList=securejarhandler-2.1.24.jar,asm-9.5.jar,asm-commons-9.5.jar,asm-tree-9.5.jar,asm-util-9.5.jar,asm-analysis-9.5.jar,bootstraplauncher-1.1.2.jar,JarJarFileSystems-0.4.0.jar,client-extra,neoforge-
if there is any other version of SJH than 2.1.24 on the LCP then it gets added to the MC-BOOTSTRAP layer
can we somehow get rid of the ignore list
that's part of the longer term goal
In theory if the loading was more integrated
but unlikely for 1.20.5
one thing that might be done is have it go by modulename
I think it reads the jar descriptors anyway
Oh and wait, it can check that the module is already loaded
Oh god, Spotless can't handle J21 code ๐
Yup
Chokes on this:
return switch (providerResult) {
case null -> Optional.empty();
case LoadResult.Success<IModFile>(var modFile) -> Optional.of(modFile);
// Rethrow provider errors immediately, to be handled below
case LoadResult.Error<IModFile>(var error) -> throw new EarlyLoadingException("JIJ ERROR", null, List.of(error));
};
I have no idea how
it's applied by GradleUtils
This seems to be in a transitive dependency:
Caused by: com.google.googlejavaformat.java.FormatterException: 76:45: error: ')' expected
at com.google.googlejavaformat.java.FormatterException.fromJavacDiagnostics(FormatterException.java:51)
maybe we can manually apply a higher version?
Well, the FML project also depends directly on the spotless gradle plugin, and bumping that didn't help
hmmm why does it use google-java-format; imports?
Ah, yes RemoveUnusedImports
pffff
Thanks Google!
I suppose this also blocks us from using record patterns in switch in neoforge until we find a workaround
I am pushing my work in progress state. Lots of TODOs left
no but we can always yeet it
that's a possible workaround but it's annoying ๐
Talking testcase, I'd run through FMLLoader init, beginScan/completeScan and then check essentially three things:
The layer contents returned by beginScan and completeScan, as well as the LoadingModList errors+warnings+mods
From my understanding, currently we do not cancel loading if a locator fails, it just gets recorded in the loadingmodlist and shown later, right?
could we disable the remove imports rule
i don't want a code formatter to prevent us from using new language features, that's silly 
Especially with no real horizon on google fixing it
We'll have to add error-display capabilities to early loading later
No...
Right now there's no real way to handle the error condition that arises when minecraft or neoforge is missing
Yeah we have to, Orion
There's no way to handle certain errors
And we can't fall back to AWT/Swing since early display already hijacked the main thread for GL ๐
Well they can be if your install is corrupted and I'd rather show a nice error message
Currently we just crash the process with an exception that swallows the original error
Well I'll just try to barf out the currently queued errors & warnings from early loading and barf out the exception, not much else we can currently do
(barf out -> to the log)
probably yeah - can we make it opt-out in GU?
I have to wrap my head around system mods again ๐
it's just a little hack to load mc and neoforge even if there are loading errors
"just a little hack" โค๏ธ
RE: to multiple comments on https://github.com/neoforged/NeoForge/pull/774/files#diff-f6dcf88ce2e4c294e2bb0002de41bb587303e6dd66b75b4b2d76ee1e4af2e3c6
Big +1 to moving stuff to static methods on ModLoader instead of having stuff like this
It doesn't really matter
For unit testing purposes, it might be beneficial to have an instance hidden behind get() that can be reset
Eh idk
this makes all the methods in ModLoader static
we should probably separate out the actual loading methods and the methods to dispatch mod bus events?
not this time though I think - I want this refactor finished at this point
check my comment on GH
those static fields introduce potential classloading footguns
I saw it and fixed it
the whole thing was already static before - call any of the methods too early and you'd have the same problem
the footguns have always been there ๐
It still requires a call to the method. Now it would just require loading the class ๐
and calling one of its methods
classloading is not enough to run the static initializer ๐
Well, any, though ๐ Eh IDK, it's just not great trying to trace bugs like these later
Hmmmm, can I even test how a SecureJar was built?
I.e. for testing FML launch, I should check that it built the minecraft jar from the right components with the right filter
I suppose I could try querying files in it to check that it doesnt return neoforge classes
yeah I guess
Could Bindings also be made fully static? It has an instance field but it's private and all public methods are static anyway.
that needs more cleanup anyway
and it's not really relevant to complexity - it's just a very minor cleanup
Alright
Hm I should also make sure I only set librariesDirectory in tests for launch targets that actually would have it set, right? ๐
As far as I can tell that would only be production client & production server
I feel like I am close to recreating DataResult light 
it's bedtime grandpa
It's the goddamn exception handling I need to do for JarContents.of
Hey everyone - I've been away from the scene for quite a while (last I worked on minecraft, js coremods were not a thing). Back then, I benchmarked loading and came to the conclusion that most time during loading was spend reading the bytecode into asm trees and back. I think I even proposed having coremods just work on trees directly, but since the JS stuff was already underway, this wasn't really feasible.
I'm curious what the state of coremods is currently, and what the bottlenecks of loading are
Feel free to benchmark again
But as far as I'm aware, ideas to avoid full reading are already being considered
If that's a hot point anyway
Yeah, I came here from the new year's blog post, but it links a thread that I don't believe exists anymore
yeah, I managed to found the issue I made in 2018 but I didn't include the benchmarks there
oh well
Ok, just found this pr https://github.com/neoforged/FancyModLoader/pull/79/files and it looks pretty good! ๐
A lot of the slowdown comes from initializing the coremod engine, hence the move to Java
Another big slowdown is parsing every single class with ASM - we also had plans to address that
the targetting methods in that pr are related to that no?
i have not looked at this code in 6 years, but it looks better than what I remember
Kind of
The problem is that we have other asm transforms that target every class
and those get passed the raw bytes?
No but they cause the ClassNode to be parsed for every class
Ah, they get passed a node - when I did my local modification way back what I did was simply add another interface that takes a tree instead
if two of that interface came in a row in the transformers, you could skip converting out of the tree
What tree??
Sorry - I confused the node hiearchy and reader
by tree I meant node
and I assumed they were passed classreader/classwriters
as in the visitor api
i really should just profile it again
I see the problem - not all classes need to be transformed, so you wouldn't need to create a tree for all of them
there is currently no api for specifying "I only care about x class" right?
Well, targets
there is, but most (all?) of the transformers in Neo all need to decide based on things like the presence of an annotation
and since we don't have an API for filtering a class based on the constant pool, the transformers currently have to say "yes" to every class
yup, we'd need "target class with annotation X", "target method with annotation Y", and as embeddedt said, THEN we could pre-filter by constant pool
But when you load into retail
Ye ye no worries
no, nodes
Then won't most classes need to be turned into a tree anyways?
Or am I assuming that most classes need ats when that is not case
most don't need ATs as far as I know
Got it
Can we just close this? https://github.com/neoforged/FancyModLoader/pull/116
I do not see why they would need this. The supposedly hardcoded bits are in a class that's already loaded via serviceloader
yes
Hm it would actually be nive to have a .neoforgeassetsroot to mark the folder/jar that contains our stuff
that'd allow us to get rid of MOD_CLASSES for neoforge dev at least ๐ค
I could search for /neoforge_logo.png I guess lol
it's also used to run our tests, beware
To add them into the minecraft mod jar?
just to load them
yeah okay I am strictly talking about auto-detecting paths to MC/NF themselves
I have minecraft%%C:\Users\bruno\java\Kits\projects\neoforge\build\resources\main;minecraft%%C:\Users\bruno\java\Kits\projects\neoforge\build\classes\java\main;testframework%%C:\Users\bruno\java\Kits\testframework\build\resources\main;testframework%%C:\Users\bruno\java\Kits\testframework\build\classes\java\main;tests%%C:\Users\bruno\java\Kits\tests\build\resources\main;tests%%C:\Users\bruno\java\Kits\tests\build\classes\java\main on my machine ๐
All of the insane filtering is done since the build output directory of projects/neoforge will contain classes from both MC and NF, right?
while build/resources will contain only the NF resources, and the MC client resources from client-extra.jar
yeah
Ooof.
I was trying to figure out why my neoforge module showed up as main
Then I remembered
We're currently creating a SecureJar based on the classes/resources in Neoforge dev
Then we return the root path of that as a locator location, and then it builds ANOTHER securejar based on that ๐
Hrmpf, there is currently not a single class that's unique to the dedicated server?
Been that way for a while
yeah, aroudn 1.16 or so?
they stopped stripping dedicated server main from the client jar
OnlyIn(dedicated_server) hastn' been a thing since then
Makes testing the FML startup slightly harder but oh well
I can still test for the absence of client classes
Nice ^^
If a jar without a MANIFEST and without a neoforge.mods.toml is in mods/, we don't load it at all?
that's correct, we're meant to show a warning
it should go into unclaimed and then the other loader checks can propose an error
okay that warning needs a test, currently we don't ๐
currently as in current FML?
No, the locator branch
ah well i never tested so yes do test and fix
I am writing unit tests for this stuff onw
looking at the PR, if a jij mod doesn't have a FMLModType manifest it seems like it will be ignored https://github.com/neoforged/FancyModLoader/pull/112/files#diff-bfd0b9684b6434f8854cc6aef63a70450237cdb08fa712b541ab9f7ba9367c9eR73
oh nvm i found NestedLibraryModProvider
that needs a bit of commenting about its existence in the jij code ๐
mhh
@chrome mortar
So in case we do not identify a "jar problem", do we just load it as a library? ๐
or, if we do not, what do we do instead? like, what is the message we warn with
you don't do anything
should probably be a light warning
"i have no idea what to do with this jar"
not sure if it deserves a loading warn
If you do nothing with it, it should be a crash, if you add it as a lib, it should be a warning (opinion)
If we add it as a lib, it should definitely not be a warning
If we don't do anything with it... hm
these are root
The nested library reader will take jars that have a parent mod-file and turn them into libraries
but if they do not have a parent mod-file it ignores them
I think the reasoning there was: if it's a plain jar that was jar-in-jar included in a mod, it likely has a reason to be there
while a loose file found in mods/ might just be an "accident" by the player
Yeah. Hmm. Do we have a reason not to just load it as a library? Especially if we've found no "issue" with it?
I'm not entirely sure here what makes more sense
I'd personally just load it, but I also have no real-life examples of someone doing this
I can't think of scenarios where a mod with nothing would be in the root folder and you'd want to load it, so maybe error/warn loudly?
true it could be a file from a modloader we just didn't account for
oooor it could just be someone throwing in log4j.jar for some reason ๐
In that case it'll have a mod type I'd assume, or something else to get it detected
What?
If it's a modloader we didn't account for, it'll just not be a file we detect
Or what do you mean?
We try to detect a few cases:
@chrome mortar Why didn't we add our old mods.toml to this thing, btw?
Wait, we did
ignore me ๐
I just read OLDFORGE and stopped reading
Oh, misread that
In which case yeah you'd probably want to error in either of those cases
But wait, wasn't it META-INF/mods.toml?
Yep. quilt's quilt.mod.json file is missing too
oh yeah
remember to add translations to neo when bumping fml
I have to do a pass on the localized error messages
I don't like how we handle those at all atm
(translation keys splattered across the codebase)
but anyway, so I have to change that to META-INF/mods.toml, don't i? Otherwise we'll silently ignore mods that forget to rename theirs mods.toml
shartte you have like 5 days most left
I know my dude, the timing sucks ๐
but moving the translation keys to an enum should be doable
Quick
Someone gimme a quilt mod
Is their quilt.mod.json in the file root
or under META-INF?
Awesome thank you, will add that
@chrome mortar why do we still have EarlyLoadingException vs. ModLoadingWarning/ModLoadingException?
the way I understand it, ModLoadingException is a single exception whereas LoadingFailedException is an aggregate of them
no idea why there's other types ๐
EarlyLoading is an aggregate
but ModLoadingWarning is just an aggregate too
Or rather, it is being aggregated, that way around
my god
What is more annoying is the different representations for basically the same thing
(i18n translation key, translation args, mod-file info)
that can probably be cleaned up
This message is not terribly easy to assert against so I gotta find an alternative for the paths: ["[fml.modloading.brokenfile[C:\Users\SEBAST~1\AppData\Local\Temp\gameDir16649063246816979246\mods\plainmod.jar]]"]
the entire thing is just quite annoying to match against
You don't like shortened paths?
How about we try to center reporting around this?
public record ModLoadingIssue(
Severity severity,
String translationKey,
List<Object> translationArgs,
@Nullable
Path affectedPath,
@Nullable
IModFile affectedModFile,
@Nullable
IModInfo affectedMod
) {
public static ModLoadingIssue warning(String translationKey, Object... args) {
return new ModLoadingIssue(Severity.WARNING, translationKey, List.of(args), null, null, null);
}
public ModLoadingIssue withAffectedPath(Path affectedPath) {
return new ModLoadingIssue(severity, translationKey, translationArgs, affectedPath, null, null);
}
public ModLoadingIssue withAffectedModFile(IModFile affectedModFile) {
var affectedPath = affectedModFile.getFilePath();
return new ModLoadingIssue(severity, translationKey, translationArgs, affectedPath, affectedModFile, null);
}
public ModLoadingIssue withAffectedMod(IModInfo affectedMod) {
var affectedModFile = affectedMod.getOwningFile().getFile();
var affectedPath = affectedModFile.getFilePath();
return new ModLoadingIssue(severity, translationKey, translationArgs, affectedPath, affectedModFile, affectedMod);
}
public enum Severity {
WARNING,
ERROR
}
}
Dunno if we should opt to encapsulate the "context" in a typed map or similar to make it extensible
Or if we should abstract ModLoadingIssue, ModLoadingError extends ModLoadingIssue, etc. to require an error or a warning via the type system
feel free to change it, I didn't feel like it
I am trying to figure something out for it right now. In tests I'd likely wanna check that a certain translation code + specific args are emitted as warnings hm
the early loading screen is still opened before locators run after this revamp, right
unfortunate but not really avoidable without introducing the exact problem it tried to solve
I really don't like my top-level jar having to be a SERVICE in order to access things like GraphicsBootstrapper
but a locator rewrite that allows locating JiJed services still wouldn't solve the problem because the locators aren't called upon that early
JiJ'ing service jars just doesn't make sense IMO
at least not right now
- it wouldn't work with SPL and other locators
yeah but like, can I even do compileOnly on a mod with that proposed jar layout (SERVICE + inner JiJed mod)
no, it would likely need different maven artifacts
and this is the other problem, GraphicsBootstrapper is unusable if I'm being loaded through SPL
I'll look at that once this stuff is done for 1.20.5
i dont think the startup flow makes a ton of sense
that is just unsolvable
but we'll get to it
sadly
lets me control OpenGL stuff before the first window is opened
And it's not completely unsolvable, the issue is more that SPL will likely want to ship with earlydisply if we do neoforge-as-a-mod
if it doesn't it wouldn't have any progress indication
why would SPL need to ship with earlydisplay? It's in FML not Neo
We would not want to install FML into the launcher profile since it'd mean we cannot update it during that MC versions lifetime
I should clarify. FML with everything it includes now*
I suppose the argument could be made that earlydisplay doesn't change often
but I'd still be somewhat afraid ๐
Why? People can update Fabric Loader (even if it's often not required).
I think the idea is to try and never get into that situation of having to explain that
Also fabric loader doesn't have anything like earlydisplay, I think
well no - the problem is that SPL wants to show its progress in a window
Hm?
Well yes and if the platform doesnt have it, it could ship earlydisplay itself ๐
obviously this means that you can't run SPL-downloaded/managed code before the window is opened
as in JIJ ship
well that doesn't matter
the problem is that embeddium needs to tweak some things before the window is opened
ok yes we agree on that
The biggest problem was mac not allowing to re-create the gl context or something?
Personal opinion: installing fml with the profile is fine.
We rarely update it
Especially not during an mc cycle
well we don't want to be in the situation where we need to tell users to update NeoForge and FML
Why? Is it really worth all that extra Engineering and complexity
that too
To have discovery in two places
It's not that much engineering, really
We already have two ways of discovery
with the early-transformer discovery FML exposes to mL
I think it's too early for specifics anyway
I have an alternate solution already, it's just not ideal since it requires the user to reconfigure the early window provider manually in each new instance
but it's better than nothing
What are you reconfiguring?
I disable the early window and then Embeddium starts it manually later
this approach works through SPL too
but obviously introduces the flaw of the screen showing up later than desired if SPL is really slow
Yeah that is the same as SPL not having an early display and just not showing progress
I mean ideally you could show an OS window and then replace it later with GL
but as I understand it, we cannot show any OS-level windows in the same thread we later show the GL window in?
we can't show AWT/Swing windows, and I don't think Java provides an alternative?
Or am I mixing that up with AWT trying to process window messages in another thread than thread 1?
Well if you can open&destroy a window in the same thread even on mac
you could just destroy/reopen the early display window
You should be able to create a new window but I don't know if the GL context will work properly
Yeah IDK, we might test it at some point, it would not be the greatest UX I suppose but might work
Uuuuh. we error if a mod has no license set?
yes, since the license line was introduced
it has never been valid to not include it
I don't know the code for this
Yeah ok, something to look at
I just know that cpw really wanted it to be a required value
that piece of code is stupid
It should be in the validator, right?
it should be where everything else is
in general, our mods.toml validation is all over the place
I suppose you validate this later since you want it to cover modsfiles that potentially use a different metadata format than mods toml
but ModValidator#validateFiles seems to be the right place for it
Hm no, not true, it doesnt have IModFileInfo at that point
Oh no, ofcourse it does. Nvm
Any objections to just always generating this error even if the file has other errors too?
If the mod declares an apiElements dependency on the inner one, then yes
not usable for my use case because as far as I know the change has to be done before the first GL window is opened
tbh it's very unlikely anyone else needs such specialized timing
well i was kinda aiming at what change you are talking about ๐
btw do we want to do https://github.com/neoforged/FancyModLoader/issues/101 as well?
it's very easy to write
the hacks sodium applies to shut off nvidia's buggy driver optimizations
just a little bit breaking ๐
You should be able to tear it down and reinit, but with macos i take nothing for granted anymore
Thought about it, and yes. But make it a separate P R:D
of course
Do NVidia drivers even apply on MacOS?
no
problem solved ๐
but yeah it's likely per-proecss and that complicates things
It just wont work with SPL then
SPL is already a big headache for what i had in mind to protoype
due to early display ๐
did you know? the "Y" in SPL stands for safetY
๐
downloading a neoforge jar through SPL would be quite cursed
well yes, but it is convenient for FC at least
yeah for sure
wait, that's brilliant
hehehe
Of it being complicated
I just hadn't thought of that potential use case - but it is something that would be pretty cool
Since initially I'd just have included earlydisplay in the NF Jar
and had a very very small boot-loader in front of it
but loading up SPL will be a long-running thing ๐
and what previously was a simple "search for boot jars" now becomes an annoying iterative process
since SPL would have to contribute the contents of the neoforge jar after the fact
hmmm yes you are right
What is in 5 days?
1.20.5
Oh oops. Emitting a warning for every "plain" jar file also applies to all of the jars on the classpath
I don't think so
most validation is in the mod info ctors
That's why I suggested having a way to mark some jars as "not required"
Potentially just at the locator level
OR we ignore jars that are in the legacy CP
} else if (candidate.getPrimaryPath().startsWith(FMLLoader.getGamePath())) {
I'll just do it for stuff in the gamedir
that's good enough I think
maybe just mention that this is a workaround for the lack of a better solution in some comment ๐
already did
I still think that these warnings produced by our config system are misleading: [11:03:10] [main/WARN] [ne.ne.fm.lo.FMLConfig/CORE]: Configuration file C:\Users\SEBAST~1\AppData\Local\Temp\gameDir16569562345923357093\config\fml.toml is not correct. Correcting
This is when it does not exist, I believe
Oh that just means our default config file is missing keys
we might want to rebase-merge this one
do you want me to enable the rebase-merge option in anticipation of that
oh wait, it's already enabled
usually it's just the merge-merge option that's disabled ๐
actually I can change that setting too 

nope, not on FML
do you want to use some of my work
also should i set up JCC on FML 
JCC?
jarcompatibilitychecker
What does that do?
checks for (source/binary) compatibility between two JARs
Ah interesting, sure why not
Right now that is not reasonable
But I think that we want to move to something like an API or implementation jar
https://github.com/neoforged/JarCompatibilityChecker for reference
And then I think JCC on something like that would really make sense
var plainModJar = installation.getModsFolder().resolve("plainmod.jar");
assertThat(result.issues()).containsOnly(ModLoadingIssue.warning(
"fml.modloading.brokenfile", plainModJar
).withAffectedPath(plainModJar));
That is a bearable way to assert mod loading warnings / errors imho
is that a hamcrest matcher
No, AssertJ
it's less about knowing if the chags re breaking, that's not a concern right now and for a while
dammit, that was my first thought
it's more about documenting the breaks
In reality, I would like to make it clear: FML rarely changed in the past, and if it did it was breaking in 99% of the time.....
And I think once these changes have gone through
We will just drop back into that pattern
Look maty, if you want to spend time on it, be my guest
But it won't really get you any new information
If it's "free" uh why not. but I wouldn't invest time into it right now either
Since we already know we're reaaaaaaaaaaly breaking API right now
@glacial crag That stuff in the Bindings static initializer
Uuhgh
what?
That is kind of my point sharte
Like this is an opensource project
He is a maintainer
final var providers = ServiceLoaderUtils.streamServiceLoader(() -> ServiceLoader.load(FMLLoader.getGameLayer(), IBindingsProvider.class), sce -> {}).toList();
dude it was already creating the instance in the static initializer ๐
Yes I know
I am not talking about your change, I am talking about the Gesamtsituation.
all infra was set up a couple of weeks ago ๐ it's a matter of generating the actions
"Gesamtsituation" what
It's exactly what it sounds like, sci ๐
i read "caesar situation"
Possibly ๐
๐
previously all of that code was in the same source tree as neoforge
The Binding stuff is to jump classloaders
@glacial crag My issue with it is:
If I remember properly
Yeah I know Orion
it's just to be able to reference neoforge from fml
But not sure if it is still needed
Yeah
no it's after MC is loaded ๐
Well, yes but statically you can't compile against it, obviously
Should just be english
since you don't have it at compiletime
yes that's the point hehe
We should have the translations in FML, but currently we probably use standard MC rendering in earlydisplay to render the error screen
which is why the FML translations are in NF
Which is a whole mess of its own
Anyway, that is not a 1.20.5 change
But: What would be nice is to be able to know if Bindings are available or not
Currently it'll just blow up in your face unrecoverably if you try to call any of it before the game layer is built
Yeah
which is sort of correct?
You cannot do:
if (Bindings.areAvailable()) {
print nice translated message using Bindings
} else {
print translation key + args
}
well the thing is, we never want to translate when printing to the log IMO
huh, it was a month ago 
yes I know
But don't take this as a comment on your PR in any case
We'll not fix that now. I hope we'll be able to get the locator stuff done though, the unified modelling of "mod loading issues" should help us build a better error reporting system E2E
yes hopefully
What I do not quite get: If we show translated messages ONLY using Minecraft rendering, why do we access translations at all in FML. I have to look into that.
that's the only use of stripControlCodes and of course it's called by neoforge ๐
the overengineer strikes again /jk
NeoForge implements a ServiceLoader interface that FML looks up. And the only transitive use is by NeoForge itself through these methods?
for that method yes
common pattern
Legacy
We needed that in the past
Well, leave it. We'll need to be able to show translated messages without MC or NF if we go ahead with NF-as-a-mod
Because we could not directly do the rendering
We have different error screen tech now
Nah Orion, what I mean is: FML could just have exposed the translation key + args to NF
And I am not sure it is still needed
interestingly parseMessage is used in the exception message
also by FML in getMessage
Wait what
I thought this entire dance was to use a translation component oO
If we just retrieve strings, I really don't get why we have the translations in NF
it's legacy code that's older than components
but it does call into MC's locale functions
Yeah okay okay I'll stop ๐
look at I18nExtension in NF if you want to get a heart attack
ServiceLoaderUtils.streamServiceLoader does some error checks you have stripped with your code tech ๐
But you can do that Tech, I have an FML-local SLU in the PR
yes and I don't care about them ๐
I want the exception to be propagated, not silently caught
Yeah in one-provider-is-allowed cases that's fine I suppose
even then, it's not an error that's supposed to happen IMO
The SLU util in FML just prints the summary or which implements are registered for a service which is nice if you can have 3rd party implementations
silently catching it is really bad practice
And it tries to identify the jar the service came from
ok that actually sounds useful
I think one of the existing impls did that too, no idea which one
Okay, how do I set up a minimal JIJ test case ๐
I need to produce the JIJ metadata in my test i suppose
There is supposed to be a test jar somewhere
Either in FML
Or in JiJ
If I Remember correctly
Or in NF
Siude question: I am trying to force Javadoc to error if something is missing documentation
Does anybody know how to do this?
Oof. I think I only ever had third party tools do that check
Ah there is
-Werror ๐
Great
Now to figure out how to tell gradle that
XD
Bingo ๐
Needs a gradle hack but works instnatly
Ehm, was not needed XD
I whipped up JIJ against lexes whishes mostly, because I wanted a proper system that was able to deal with mixins in inner jars etc
So I tailored it originally to what I needed, and what was needed for the impl
I always said, if we need more, then add to it
I mean this works ๐
byte[] content;
try (var in = MetadataIOHandler.toInputStream(metadata)) {
content = in.readAllBytes();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
switch with exceptions 
now i can't wait for it 
This is just a test anyway and it's nicely wrapped up ๐
Project Amber is doing some good stuff for java, really
public IdentifiableContent createJijMetadata(ContainedJarMetadata... containedJars) {
Metadata metadata = new Metadata(Arrays.asList(containedJars));
byte[] content;
try (var in = MetadataIOHandler.toInputStream(metadata)) {
content = in.readAllBytes();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return new IdentifiableContent("JIJ_METADATA", Constants.CONTAINED_JARS_METADATA_PATH, content);
}
is project amber the switchification of the language
They do all those nice QoL things under project amber
We probably just ignore isObfuscated on the JIJ metadata, right?
Uuuuuh Caused by: java.nio.file.ProviderNotFoundException: Provider "jij" not found
heh
it likely loads via SJH's url provider wrapper
You likely loaded the FileSystems to early
FileSystemProviders are loaded once by the system
see ModularURLHandler
So if you trigger that too early you can get into trouble
If I remember correctly
There was a way to reset that though, somewhere
Yeah I'll have to debug that, IDK what too early means ๐ I probably forgot to call something
From my test
there is no jij filesystem anymore right?
Is FML missing a dep on that
I will check
You don't have to, I am already in the project anyway
FYI neoforge has it as a moduleOnly dep
I think that is fine
yeah
it needs to be in the boot module layer
It was always loaded via ServiceLocators
And yeah it has to be on the boot layer
Because as I said FileSystemProviders are discovered once by the JVM
And then never again
In particularly: FileSystemProvider#loadInstalledProviders()
intellij doesn't see the dependency on it... ๐
I think you specifically do not have to use the JIJ filesystem with JIJ
No you do....
you can just pass a different classloader
Well I had a prototype that copied the file out in the sourceProducer instead ๐
That looks like new
I think at least
Unsure
I don't think so
Ah no
That does not work with URI
If you have a URI
That is turned into a Path
Via toPath
Then the classloader will be either null
the problem is that the jdk will often call it without passing a specific classloader
Or the one for the bootlayer
Which is what Mixin and others do very frequently
Hence this needing to be on the bootlayer
Yeah when turning URI->Path you can't pass a CL
(and neither would you want to as a downstream consumer)
It'd be nice if it tried the context CL, I suppose
Thsi whole FS thing is needed for URI support
If CL and Mixin worked with Path
It would not even be needed
And we could just use vanilla ZipFS
But since both use URIs
It still needs to be there
Sadly
Anyhow, I'll just add a JIJ Filesystem dep for testRuntimeOnly
NIO is just always so awkward once you start doing advanced stuff with it ๐
Well we'll likely be tied into URI for all eternity
Yeah URIs won't go aways in Classloaders
[12:30:11] [main/DEBUG] [ne.ne.fm.lo.mo.ModDiscoverer/SCAN]: Successfully Loaded 3 mods. Attempting to load dependencies...
[12:30:11] [main/DEBUG] [ne.ne.fm.lo.mo.ModDiscoverer/SCAN]: Trying locator net.neoforged.fml.loading.moddiscovery.locators.JarInJarDependencyLocator@38eb0f4d
[12:30:11] [main/INFO] [ne.ne.fm.lo.mo.lo.JarInJarDependencyLocator/]: Found 1 dependencies adding them to mods collection
Okay, it's "alive" ๐
very nice
the locator toString is still weird. We call toString on locators a bunch,but still have an additional name method

That wasn't me ๐
i blame tech
Hm
I'd have to check how JIJ behaves in the error case
[12:46:09] [main/ERROR] [ne.ne.fm.lo.mo.ModInfo/FATAL]: Invalid modId found in file - embedded-mod does not match the standard: ^[a-z][a-z0-9_]{1,63}$
[12:46:09] [main/ERROR] [ne.ne.fm.lo.mo.lo.JarInJarDependencyLocator/]: Failed to load mod file META-INF\jarjar\embedded-mod-1.0.jar from jijmod.jar
๐
Learning about JIJ more and more ๐
FML has different regexes for the different mod metadata:
- Mod ID:
[a-z][a-z0-9_]{1,63}(-is not a valid character as the mod ID determines the Java module name, and-isn't a valid character in module names) - Namespace:
[a-z][a-z0-9_.-]{1,63} - Mod Version:
\d+.*(versions must start with a digit due to Java module limitations)
Yeah I didnt think it would validate the mod id
Oh
Yeah that's what I was afraid of the, error handling leads to a hard crash in that spot ๐
JIJ Requests mod info data about all candidates
Even if it does not decide to load it in the end after all
I need to check if I can pass it a list<T> and get back a list <U>
Then I can have the source provider return a data result like type
I'm still annoyed by the generics in JarJarSelector
I like that it's agnostic to what it builds
Yeah that is the point
Which allows us flexibility down the line
Because it was still at one point under consideration that FG6 would flatten the zips
And then it needed to be able to run the selector on its own
well, only to the extent where we don't have to refactor the generic class...
is indeed the correct reaction
I gotta check if we can have it continue processing on errors
why would one flatten the zips

Tja tech
I thought you would have expected that
You have been around this modding block once or twice already XD
well not all bad ideas are from Lex ๐
this doesn't sound bad, it just sounds meh
But it is also not very great
yeah
The idea was that 1) we could shrink the jars, and 2) we would not need recursive JarJar
But 1) really does not matter these days
And 2) Did not reduce the complexity
I noticed you can specify a folder in the jar as relative path and it does not complain hehe
No it does not ๐
You can package them literally anywhere
And reference them in the metadata
Only the metadat has a fixed location
And the rest is convention
Which is by design
Well I mean not even a file in the jar heh
Yeah
JarJar is really in that regard a dump piece of tech
Its intelligence lies in two thigns:
- Its FS system
- Its negotiator
The rest is just a pure dump locator
that just tells FML to fucking load shit
@glacial crag https://github.com/neoforged/FancyModLoader/pull/118/files#diff-654401a66f316514958691d6ea49201b940d748d6f460e2b24022c11c3bfd444R11 i would start annotating things with @Internal 
@glacial crag maybe it's also worth adding a code snipped in the docs of EventBusSubscriber so that it's clear that people need to use static methods w/ SubscribeEvent
Which cases for JiJ should I account for
I have:
- embedded mod (
"META-INF/jarjar/embedded_mod-1.0.jar", SimulatedInstallation.createModsToml("embeddedmod", "1.0")) - embedded service (
"META-INF/jarjar/embedded_service-1.0.jar", SimulatedInstallation.createManifest("SERVICE_MANIFEST", Map.of("FMLModType", "LIBRARY")) - embedded game library (
"META-INF/jarjar/embedded_gamelib-1.0.jar", SimulatedInstallation.createManifest("GAMELIB_MANIFEST", Map.of("FMLModType", "GAMELIBRARY"))) - embedded plain jar with no manifest (
"META-INF/jarjar/embedded_lib-1.0.jar")
Those are the 4 I know of
In the future I really also want embedded ML plugin
But that requires a major refactor anyway
So......
Would be funny:
The assertions essentially are:
assertThat(result.gameLayerModules()).containsOnlyKeys("minecraft", "embeddedmod", "embedded.gamelib", "jijmod", "neoforge");
assertThat(result.pluginLayerModules()).containsOnlyKeys("embedded.lib", "embedded.service");
assertThat(result.loadedMods()).containsOnlyKeys("minecraft", "neoforge", "embeddedmod", "jijmod");
That's at least my idea but a half baked idea without a prototype is not worth much
So I am trying to get this out the door so I can iron out some prototype code ๐
That only works if you move all the locator code to one place
And that means FML needs to be installed
I am thinking along the lines of a boot locator that replaces BSL
Instead of loaded from the NF jar
and is a very lightweight loader that does know about mods/ but that's it
That is what ML does right now
Yes but it does more than that
My current idea would not have any transform code in the boot loader
no asm, nothing like that
But that still means that you can not distribute NF via SPL for example, or Mixin via the NF jar or any of the stuff like that
It will always need to do that
Unless you put it one layer up
You could, since all that thing needs to do is scan mods/ for boot layer content and build a boot layer to continue
Yeah, but that is not really practical.......
Since we do not need to actually apply any transforms until the very end of FMLs startup code
Move most parts of it into NF
leave the locators and the connection to ML + the instantiation with language loaders in it
And then just install it with ML on the boot path
And go home
The amount of changes to that logic will be so tiny
And thanks to service loaders we could do locators recursively
And load them one at a time
but here's the thing: have a boot loader that knows absolutly nothing about transforms, which is only responsible for essentially starting the iterative class-loading process of grabbing bootup code from our "blessed" mod jar
yeah we need an iterative locator process to support SPL also supplying the NF jar
that aspect makes it hella complicated, but to fully go that route, we need it
You get into problems with mods wanting to modify the ELS
Like embeddium
embeddium could be gated behind SPL
Yes, we had that discussion yeah. Embeddium already said he asks people to disable EL via config so he can hook it later
Yeah I had the same thought
So I have a design in my head
There are some aspects we can only iron out through prototyping, really
That works
FFS spotless destroys snippets...
spotless is just /sigh
It would make ML so tiny
it replaces @ by @

why is there nothing better in javaland
You could seriously just drop ML and rename it NFBL
And FML really modular, with basically just a tiny API that Locators and Language Loaders hook
NeoForgeBootLoader ๐
And that is it
Nah
ML will need to contain the logic to build a transformable multi layered system
No need to refactor that
I am envisioning a really tiny lib we install into launcher profiles that is already customized to us in that it knows of mods/
The libraries ML uses should not be touched by anybody anyway
Yeah but that idea does not work with SPL
Or at least an NF distro via SPL
But I really need to pour it into code
Yeah we should eventually hop into a call together
And maybe just draw it out on paper
I think we are close to having the same idea
Just with some small tweaks
And yeah, feature-wise the goal should obviously be:
- SPL should be able to supply the NF jar (which requires iterative locator / layer building)
- SPL needs to be able to show progress while it downloads mods
Yeah
With that
You could make amazing launchers
Which have like fully dynamic pack mangement build in into them
Since it's such a security nightmare if you let the general population use it
That does not install anything at all
Just has that one locator
You start the game
In general the "installer" becomes a joke anyway
And it sets up the entire instance
since it just copies a version JSON over ๐
our current installer code essentially moves as an embed into the NF jar
Yeah
we neeeeeeeeed some optimizations along the road for that to get the size down to a humane level, but I have some thoughts on that
we currently ship too many duplicate patch files
but we can trivially fix that
one more for the TODO board:
And variant publishing....
rethink if we even need a joined distribution in neoform
what have I done
if all we do is have onlyin's on classes
Your ideas on configurations and variant publishing
They are not bad, but still very rough
But I think we can even push them
We don't, really. We only need a JOINED
Server and everything else can be yeeted for all I care
We distro the entire client anyway to the server
And stuff like that
So lets just have one type
Yeah
This is from the installers data directory
the EASIEST way of actually compressing that down is actually more stupid ๐
Since both these files
are just lzma compressed jar files
And they have significant overlap in their content
Uh @glad cobalt I think something is fucked about our server patchset. Or I am being stupid here.
Why does the server patch set contain client-side renderer patches in official names?
No that is by design
It patches back in all client classes
Including their onlyin annotations
oO
And then the transformer sees them and throws the user-friendly exception
shartte that's why onlyin exists lol
It is the only way to ๐ฏ% know if the class is actually missing or somebody fucked up on the server
I thought OnlyIn exists to be able to run a dedicated server without client classes in dev
where you are forced to have a joined distribution
Nah
Because I'd personally just have removed OnlyIn in production to get some speed ๐
We have a joined distro literally everywhere
The patches are generated from the same target
So it's by our choice
jesus
in some cases it makes sense for us to ship a vanilla client-only class on the server too
Hrmpf Orion, I think the JIJ filesystem Path implementation doesn't have a nice tostring, or I am not sure how else to solve this
The root ("") being used as the SJH primary path leads to this semi-helpful ModFile#tostring:
Nested Mod File in Mod File: C:\Users\SEBAST~1\AppData\Local\Temp\gameDir13459412453034527518\mods\jijmod.jar
I added the nested part, which helps a little bit at least
Oh no this is a union filesystem wrapping a JIJ filesystem wrapping a union filesystem wrapping a windows path?
FWIW, there are many times when I wished for something like SPL when distributing custom modpacks to friends years ago, so I think it would have value if it were documented better and/or easier to setup
#builds message 502? 
[Jump to referenced message](#builds message) in #builds
3.0.17
main
Move EventBusSubscriber to top-level and cleanup Bindings suppliers (#118)
-
Move EventBusSubscriber to top-level and cleanup Bindings suppliers
-
Inline I18nParser into IBindingsProvider
-
Rename FORGE bus to GAME bus
-
Add EBS snippet in javadoc
The problem is hammering it into the users of the client-side that the server can send you whatever executable they want ๐
Is that any worse than people distributing ZIPs to each other?
I suppose SPL makes problematic software easier to mass-distribute
but at an individual level it is no worse than the server owner giving you the files manually
It's just that it's intransparent
And that they can change what they distribute without you knowing
I was eventually planning on adding like an update screen to it
That you can / need to click to update
But for now ELS is not that flexible
Oh, picking up the license validation again: It should only do that for mods, right?
Since it just complained about a game library
So... I changed it to this instead, is that correct?
if (modFile.getType() == IModFile.Type.MOD && Strings.isNullOrEmpty(modFile.getModFileInfo().getLicense())) {
issues.add(ModLoadingIssue.error("fml.modloading.missinglicense", modFile).withAffectedModFile(modFile));
}
why won't you just... move that check where the other checks are in the mod info ctor 

