#FML Clean-up
1 messages ยท Page 6 of 1
You mean the fact that we have one jar at compile time..... Probably. If we could figure out a good way for neodev, where cross combined compile is not in the way.
I am not even sure it can be in moddev for the recompile
Then the entire stack becomes pretty trivial
you'd have to fake the module-info post recompile
Ah yeah. I mean that in userdev we should be able to split it
But this is only a detail compared to all the work that is required to support modules
the split yes, but you still need to generate module-info not using the standard approach
I can see benefits in the compile-time case
it actually does something for users
runtime is just lots of work for pretty much nothing
I wonder how hard it is to hack it together at compile time
Except that when this was designed the normal CP way really was ugly AF, and people trivially fucked it up.
I am not sure either, it's a completely orthogonal problem
Sorry I don't buy that, really
I have been doing fabric for years
and have NEVER run into the problem of mis-loading classes
I only have second hand knowledge
We can setup proper classloader isolation with a CP environment too
not once
During the same time frame
I can't even count how many times I have run into "module fml_loader reads more than one module fml_blahfasel"
in Forge ๐
Maybe
We are also one step further
I still think we can use a JavaAgent as a dev-time safeguard
if we'd actually wanna go CP
since the JavaAgent can get global callbacks on classload
for ANY classloader
and validate with a nice stacktrace when someone is doing shit like Classloader.getSystemClassLoader().loadClass("net.minecraft")
which borders on malicious
(and would not actually do anything in Prod, mind you)
Since in prod, the deobfuscated Minecraft classes you'd not want to double-load aren't even on the system CP to begin with
So you couldn't double-load them even if you tried
BTW Orion the situation was slightly different when the bus still had to be on the game layer
Because that definitely sucks
But now it's only MC+NF and those aren't loaded via CP in prod
People still break it because mixin plugins load on the game layer and can load an MC class too early
Yep
That's a problem for both Fabric and NeoForge
Unfortunately we can't solve it I think
At least Java coremods are safe here ๐ (who'd have thought them safer than mixin plugins)
safer in that way 100%
Yeah only in that very specific way ^^
Okay but that is not really class-loader related, rather that it circumvents the transformer setup, right?
It prevents targeting that class with a mixin
I think the classic issue we've been talking about is that you load a MC class in dev in a parent CL of the transforming CL
Ideally people should not be able to start loading game classes at all until after the transformers are fully established
That should actually be doable
Although we can probably only do it heuristically for the vanilla packages (net/minecraft, com/mojang)
do mixin plugins actually need access to the game classes?
or could these be loaded disjointed from the TCL?
They shouldn't
But probably to other classes in the mod
However many probably rely on accessing another class from the mod
yeah
I think the community will riot if we require mixin plugins to be in a service jar
I was about to suggest it ๐
we do
you can do some split package magic too
similar to what mixin does
but eh
Tbh this architecturally makes more sense so it might be worth pursuing anyway
but multiloader devs will have pain
probably short-circuiting in the TCL when you try to load net/minecraft before the setup is done
is easier
when is setup done though
mixins are lazily initialized on the first attempt to load a class iirc
yeaaaaaaaaaaah
Why are you making such a good point embedded, can't you leave me in my state of ignorance for a little bit longer
I try not to make bad points
ok so here's a practical concern with the service jar idea
where do you put the mixins
mixins by definition compile against the game classes
That is already the case
it would make more sense to manually load mixin plugins on another CL
The problem is with how mixin operates
Yes, definitely
How much control does the mixin service have over this?
If any
I think we should pull that into our code-base, BTW.
FML already has a hard dep on mixin, that would be the right place for it
Uh, Mixins could freely live in the service JAR
They don't need access to the game after compilation, do they?
They're never loaded
Except for accessor mixins, of course
Exceptions everywhere
Fuck
Yes but then you can't have proper compile-time isolation of the game layer
Mixin already prevents you from loading them so it doesn't matter
Very little I think
Tech where is the event bus started again
I am getting into bootstrap now, but fail at
java.lang.IllegalStateException: Attempted to post event of type RegisterCommandsEvent on a bus that was not started yet!
๐
I wonder what I am forgetting
Oh nevermind, I'll just breakpoint in a normal instance
Oh god I think in FML dev I am pulling in an ancient version of eventbus
Yuuuuuuuuup. 7.x.x
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make static long org.lwjgl.system.MemoryManage$DebugAllocator.untrack(long) accessible: module org.lwjgl does not "opens org.lwjgl.system" to module minecraft
Now we're in the modular weeds
they still use this hack?
No, time to manually record all the things that just work when it's all in the unnamed module and open it using instrumentation manually ๐
I think there are 2-3 things
So this little thing was always hidden by SJH making every module open?
yes
Don't be snarky ๐
But yes, the point needs to be made: now I am writing a custom system that replicates the --add-opens JVM CLI parameters
We're just lucky Mojang didn't rely on --add-opens for their stuff, SJH currently would not be able to handle it
Yay. First time main-menu
You're missing the vanilla resources I think
Yep the client extra jar
That is exactly the same architecture I have in my experimental design
I find that a much better approach
No the assets
I didn't pass the asset index
if the client resources were missing you'd not even have the button textures ๐
Right
The stuff I needed in ML: https://github.com/McModLauncher/modlauncher/pull/128/files (so far)
It's incorrect, tbh. but I haven't hit the inveitable bug it causes yet ๐
I tried to make constructors allow for dep-injection where possible since that mostly prevents having to make things public
it doesnt work in every spot
Architecturally I don't like the .resourceroot approach, but I know that for right now we don't really have an alternative
I saw
This is a first step
My ML clone has like this all majorly refactored
To the point
Where FML even handles its discovery
I hate that ML does its own discovery
But that would require another major refactor of locators
Yes, this is currently trying not to break API if it can avoid it
I wanna try this with ATM10 / Enigmatica 10
Understandable
Otherwise I'd also have refactored some other spots. I.e. in one spot it is leaking Environment as a method return type
I did not set my self that goal XD
where it could have used IEnvironment instead
making me have to use the concrete impl-class instead of just implementing a facade on top of the FML env
Shite
So far at least it seems feasible to get rid of LCP
I mean it's simple to summarize the idea
Okey this is looking pretty good
I understand the idea
๐ I have been following this pretty closely
We whitelist a known set of modules that we know we need to run our boostrap code
That is pretty much it ๐
Yeah
the rest runs through normal discovery hehehe
I have not yet tried out the in-dev mod grouping. My current idea is actually more closely aligned to what NG did initially where it associates mod-dev directories with a mod-id... I'll see when I get to it
I always considered that the better approach personally
If we write that mod-id into a file that we can discover relative to the directories on the CP, we dont need to pass anything to FML to figure out that grouping when it starts, that's why I am considering it
I am still not 100% happy with it though
It'd be "fool proof"
But that is mostly related to the fact that IDEA, Eclipse and Gradle all have a different model
Right now I am considering making a specific model for each IDE
Because that always works regardless of how the directories end up on CP
Instead of trying the "set it up once" and hope for the best appraoch
As an extension to IDEA / Eclipse models for example
We also do not need to know about the directory layout cross-project
It has downsides ofcourse: a common project still needs to write that mod-id to a file location, but that could be done without having to rely on a full-blown moddev plugin
If it is simple enough: Could be a static file like MANIFEST.MF for most cases can be
no because you can't identify the classes dir because of it
I'd currently favor something under build/
and you scan essentially for build/modgrouping.propertiesor whatever
by walking upwards from each classpath directory you found during discovery
the only assumption that needs to hold for that to work
is that output directories must be beneath the project directory
but that is true for all IDEs and Gradle unless you manually chose to break that assumption
Is that true for idea in module specific compile mode?
yeah
it becomes murky when you switch it to non module specific compile ๐
as in inherit output directory from parent project
still need to investigate that, but it's at least still within the gradle project structure
I mean, at some point people can always manually change some project setting to break everything
Is that not the default thoigz
Yup, first i need the game to launch perfectly again though ๐ Gotta still test the Mixin aspects
I was thinking about the whole mixin discussion
Alrighty, took me a biiiiiiit longer than I had hoped but it had a lot of wrinkles
I now have a buildSrc plugin in FML that is able to make standalone MC installations into build/ using the NeoForge installer
And it adds a sourceSet / sets up the classpath so it's possible to run that from IDE
Finally I should be able to test the client prod environment properly from IDE, heh
If anyone is interested, this is a minimal self-attach without bytebuddy or similar, which works for our target VMs: https://gist.github.com/shartte/c1b4e2becebd5e603189b52e2e6aeaad
Aight, adding support for '@'-files directly to my startup main method, that makes passing it the asset location far easier.
Alternatively we might support asset locations via classpath files ๐ค Similar to what coeh did in Neoform
Hm. Why is there no Module-Path Manifest Attribute equivalent to Class-Path?
I suppose we shall use the same hacks for the executable jar as we do for the client
there was a QR code in earlydisplay? 
๐
It leads to https://tinyurl.com/forgetest
ah yes
did it ever work as a QR code?
a lot of it is just boilerplate that already exists in other Gradle plugins
I did scan it
Does it though ๐
As a public API
of course not
They also all do not run the installers
And run configurations are never offered in a generic enough way
yeah I suppose that the prod ones are not worth trying to abstract
how do the dev ones work?
It's just mdg
initially I had those use MDG artifacts from a second subproject
but I did change it to be just MDG now
i added a dependency further down, but yea
you can remove the disambiguation rules, probably
and we might want to move that to MDG's API
where is that?
"further down" -> wrong direction ๐
in the configureEach
Good point that it might actually be borked for moddev, good find lol
we should replace it with a rickroll and re-enable it 
@glacial crag fixed the moddev deps
Yes it is
But as it turns out it also just selects what you have installed locally. I'd thought it really hooks into toolchains, but doesn't
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
rulesMode = RulesMode.FAIL_ON_PROJECT_RULES
why?
because footguns?
What do you expect happens when you have central repo management, and declare project-local repos?
It doesn't append, it fully overrides anything from settings.gradle. Those two settings are there to prevent that from happening accidentally.
Yes, essentially it throws if you try to declare repos in the subprojects
which would kill your centralized mgmt for that repo
hmmm
why is it not picking up the module, again
let's have a look with the debugger
grrr why is gradle not waiting for my debugger
Unknown command-line option '--no-daemon'.
WTF
what is intellij doing???
oh it's your deamon crap @haughty copper
I'll yeet it then
๐
it breaks remote debugging and --no-daemon
MY DAEMON CRAP ?!
And you do not have IJ launch enables, yes? because it does not work yet
no no
Are you trying to run this with your IJ from 1999 again?
Since it's working here, I'd expect that
try running :test:build --no-daemon --no-configuration-cache -Dorg.gradle.debug=true from IntelliJ
why the hell are you running with nodaemon
It hasn'T done anything useful in forever
I seriously have no idea what you are doing, I've been right-click debugging the gradle tasks in that project all the time ๐
I want to debug MDG
I could also use an included build, but in theory remote debugging should work too
-Dorg.gradle.debug=true should be enough for remote debugging
No idea, tbh. I've only done right-click debugs
and yes, includeBuild is way easier
First attempts at loading ATM10 with the experimental startup code ๐
I haven't pulled tags, so my local FML is version 3.0.x ๐
What does this mean?
! [rejected] 4.0 -> 4.0 (would clobber existing tag)
you have a wrong local tag?
classic! ๐
Maybe? I don't remember that, tbh ๐ค
But okay --force it is
MDG computes a module name of FancyModLoader.test.main
it should be FancyModLoader.test
...
that's my fault I think
no?
the project name is test
FancyModLoader.test.main sounds right
Soaryn riding the bleeding edge wave
well yes
I have noticed that too
IJ is going CRAZY
even without the minecraft addon
I guess that test is ambiguous
this is exactly the bug we get, when the minecraft addon is installed
and destroys your runs
but it's not ambiguous....
I just fix them every time they're generated at this point
I gave up trying to diagnose it
But honestly this is the first time I had it without the plugin
FancyModLoader.test could refer to the test subproject, or to the test sourceset of the main project
so I am making the assumption it's definitely some quirk in IJ
Ooooooooooh ๐ค
I bet that it has a check for that exact case, and implements this workaround
๐
And now I have to cash in my debt of not correctly emulating ModLauncher service discovery in FML
wow, you can abuse getInputs to make some properties lazy
that's what you're using it for!
Yep
that's crazy ๐
Without subclassing
And just a static doFirst
Luke said I could have used a lambda and captured the providers instead, but I find it too easy to capture the project or other stuff that will blow up in our faces
ah yeah you have a doFirst like that
I thought it was just overriding one of the properties in the task using magic
not as fancy as I thought
Heheheh okay yes I can see how that would suck
okay, what do we name the subproject
gametest?
I think that it's too easy to confuse with MC gametests
I already switched, so I can do it
I need it on the startup-experiments branch too, so I am eager to do it. I also run into the module problem there
// TODO: Use MDG utility since idea-ext is just wrong
this only matters for projects with a space in their name, so we should be fine with the ModuleRef
Done & pushed
๐
Yeah I kept the TODO we ever would want this to go into MDG
this is resolving a config - bad
I really don't want to until we adopt the new startup code that makes us no longer need module path and LCP
It should only do that in the IJ sync though ๐ค
There's no prepare run task I can attach it to
I'd just leave that since it'll be gone with the startup experiments and still works fine with config cache
all of the idea-ext logic runs even without the sync
Hm true
I remember being surprised by that some time ago heh
No it's right, I shouldn't be. But that's what we had idea.sync.active for in MDG
But the config should be minimal so eh...
yeah it's ok
Also: this doesn't leak to modders and is purely local to FML development
So I am willing to be a bit more lenient there
it's interesting that RunConfigurationSettings is not using the same naming scheme as RunModel for its methods
yes same
we could even reuse RunConfig but it would be hacky
Hmmm, I can correct that I suppose
Yes, I am rather thinking the opposite, we might be reusing the generalized run configuration plugin at some pont
*point
you should also copy the docs over to MDG ๐
if we also make it eclipse capable that is
yeah
we might want to add a comment here just in case we decide to reuse the plugin and somehow still have a module path
the game directory vs. working directory difference is actually deliberate, btw
since the run configs are generic
@glacial crag I changed the prop names to be more MDG like for program and JVM args
Well gameDirectory is also a bit of a shit name
is that also why you added a property for the task group?
because we tie it to the same directory in FML
but it's not actually necessarily true
that the working directory == the gameDir
well that is true
And also it's possible we can use this for non-game runs
so yes yes, YAGNI and all but workingDirectory isn't a bad name ๐
and yes, same for task group
the fuck does YAGNI mean
can't even pronounce that acronym lol
to create the working dirs?
I mean, I feel like you can create it at project eval time lol
remember this won't ever touch "production" code
I'm aware but it's a bad practice nonetheless
it's purely for testing, you can skip some "best practices"
esp if they increase the amount of code ๐
No I think the production installs take too long for that
the working dir along doesnt help you
you need to run the install task
wasn't KISS your favourite acronym after all
ah
I actually think our installer takes long.... and might take a look at it, at some point. But currently, just run the two install production tasks to get it set up
so the run is not even working out of the box?
well, you can blame fart for paprt of it
and bin patches taking ages to decompress for the other part
yes; but hacks are not necessarily simple ๐
I'm very certain that fart logging so much shit is slowing it down considerably
Nope you need to install
Yes, same thought
It's 80% of the logfile
Binpatches might be less of a problem once there's 60% less of them
heh
Caused by: java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.orElseThrow(Optional.java:377)
at MC-BOOTSTRAP/main/net.neoforged.fml.loading.targets.CommonLaunchHandler.overwriteLoggingConfiguration(CommonLaunchHandler.java:68)
at MC-BOOTSTRAP/main/net.neoforged.fml.loading.targets.CommonLaunchHandler.preLaunch(CommonLaunchHandler.java:57)
at MC-BOOTSTRAP/main/net.neoforged.fml.loading.targets.CommonLaunchHandler.launchService(CommonLaunchHandler.java:116)
at MC-BOOTSTRAP/[email protected]/cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30)
... 11 more
when running the prod client ๐
it didn't find the fml_loader module
HUH
ah findModule will also look through parent layers I guess
yeah FML is actually loaded as the main module ๐
yet another problem with modules...
they just don't match the IDE model :/
Hm, okay but you are running through IDE, yes?
As I said, run the gradle tasks for now, that's an aspect that is fixed by my new startup code, even for module mode
So whats your general take Tech?
hmmm okay
but then why do we have the run plugin if the runs don't work ๐
we could add gradle tasks to intellij instead
the run plugin is fine; I haven't looked into the installer
my overall impression is that this dance is just stupid and FML should be moved back into NF ๐
buuuut for now we'll just have this in FML; it's better than nothing
I'll continue looking at the other files tomorrow
Well yes, but you'd still need to run the installer to get a non-neo-dev environment for testing
OTOH you would get a neodev environment for testing, heh. since that I can't easily emulate
Maybe I have to add half of neodev to NFRT to actually do that blergh
well I mean that the dev tasks would at least make more sense if we had FML and NF in the same repo
the prod ones are a different story yes; in principle we could just build the installer then run it
hm yes, but the tasks would largely be the same
and you'd still have to have the run config plugin
so you end up with exactly the same amount of gradle soup
ultimately the only difference would be that you shove the task output of neodev into the installer configuration
yeah that is true
And the IJ run configs should btw work if you run through Gradle
they don't
Gradle won't put the jar on the CP
yeah you are right actually
it only builds with Gradle
but even a Gradle run would usually not run with the full jar, would it?
But yeah, I left them in since I can use them in the startup branch. BSL just doesn't support grouped module folders at all
it would
gradle runs with the actual jars
and it works
yeah, runProductionClient works
I find that suprising actually
hmmm no it makes perfect sense for classpath project(":loader")
ok
I'll have a deeper look at the rest tomorrow; it's sleeping time!
๐
So I got ATM 10 to launch with the new starter but ahem without promoting the CP to modules at all
While still using the layers beneath
Wait
Which one 
Ooooooooooops
That was unintentional, I think I was trying to clean up some branches
But in any case @glad cobalt So what is interesting for productio is this scenario:
- There are no classes on the app cp that shouldn't be loaded there anyway
- setting the parent CL for the first layers modular CP kinda "just works" without any JDK internals used
- if I keep my "modular hoisting" in place, runtime fails because some mods access internals of i.e. gson and are then rejected by the standard JPMS access checks
At least ATM fails to load because kubejs accesses gson.internal, and loading gson into a JDK loader enforces the access check, while it previously did not due to SecureJar making everything open
I would really like those checks
But it is for sure a breaking change
I am very much a proponent of them
Because they are just part of the JPMS system
And they should have been there in the first place
In the same way we should really be generaing a module info for minecraft / forge
We shouldn't stop modders from reflecting into MC
I don't really have an issue with modders reflecting into the internals of gson myself heh
I'm surprised that gson even has a module-info heh
No for sure, our module info should just expose every package
We can trivially generate that at compile time though
That or just make the module automatic
That's what we do at runtime now yes
Yeah, but I am not arguing about now
But it won't work for libraries such as GSON unless we force-open everything
Which we should easily be able to do with instrumentation if we wanted to
idk. Using modules then making everything open sort of defeats the point anyway
Not really, I mean for minecraft it makes sense
But for GSON, which explicitly says: don't access my internals
Or from other mods or libraries
Which explicitly have this
It makes all the sense in the world to me
It might even make sense for us, to have our own internal package
That is not reflectable, or accessible this way
Which helps with the whole compatibility between versions thing IMHO
Why would we let gson dictate our choices when we don't let MC dictate our choices
MC does not have an internal definition of what should be public and what should not
So we can reasonably make up our mind
IMO modders using internals are responsible for keeping that part of their mod up to date
And in our case it makes sense to make everything public no?
MC has private members that we regularly make public
Yes so?
What is the point here?
Again we explicitly make the decision to make this public
It has a definition of what should be public and what should not, in a loose sense
The whole point of JPMS is that as module author, you get some control over the usage of your code
At runtime
By others
Yes, and I am saying that this might not be appropriate for a modding environment
In your opinion
I would argue otherwise, looking at other modding APIs in other games, they all have some form of seperation of what you can touch, and what you can't
Most modding APIs are a lot more limited though
The point is that Minecraft modding is a lot more flexible, and that flexibility comes from being able to change literally anything
Understandable, but there have been many modders, me included, that have long said, that this flexibility is always a pain to deal with. And that we should create some sort of restriction
At least when it comes to our own stuff
Aka, my mod, my code, you not touchy, to summerize
Unfortunately, such a restriction is ultimately limiting the freedom of other modders. I think it's problematic for the platform to encourage that
So you are now limiting modders that do not want that?
What is that for an argument?
No you do, because you limit me in my ability to govern how my code runs
By forcing my accessibility to be explicitly open
By making a change to the bytecode that I published
I don't change your bytecode, but I let others change yours
No you do, you modify the bytecode I exposed in my module info
Because you force it to be open
Or you ignore it
Which ever way you want to look at it
Just like many folks rely on changing Minecraft and sometimes (Neo)Forge's bytecode
I would argue the amount of people that want this limiting factor, to be able to clearly define API, and none API, vs the amount of people that need to hack into other mods, are about equal
There already exist ways to define a clear API (API jar, internal package, etc)
For example, Fabric has been doing it forever
It works very well on Fabric
No it does not
I can trivially access internals
Modify what ever i want
Reflect into it
Mess with its bytecode
Yes, you can
I disagree with that mentality. There is no need to be so controlling over your own code, imo
Lex might have encouraged a lot of that, btw
Accept in the past I needed to be
because people hacked into my code on C&B to "fix" something
Instead they bricked the entire renderer
Or to make it compatible with Optifine
Which then broke all kinds of crap
And I had to spend ours figuring this out
And I am not the only one that has to do this
Hmmm yes ok that really sucks actually
I always forget how stupid people are when it comes to rendering heh
They really really really are
And I am not saying that people like embeddedt don't do it ยดthe right way
But sadly experts like that
Are not the norm
The norm are idiots
Which don't provide the needed support themselves
Which don't have the know how
Or forget that maybe the mod runs on multiple platforms and as such does not behave the same as any forge or fabric mod on its own
And I get to spend hours and hours testing this
And it is not just frustrating me
But also my players
That download a pack
And then have to spend hours fixing it
And sure you could use this method to fix a real bug
But then again, most mods these days are open source
Make a damn PR
And if they are not, then you are an ARR land anyway
So you are not even legally allowed to do this, or are in a grey area at best
On top of that
Out of personal preference
I spend hours making large and very flexible APIs
That allows you to basically modify anything in anyway you like
Is the problem mostly mixins, or is reflection a problem too?
Mostly mixins, but I have most fields defined in such a way that it can be done with both
I have only seen mixins though in practice
The two have vastly different solutions
And most importantly disallowing mixins is unrelated to modules
Yes and no, as a personal opinion, and I said this to shartte before, is that we should only transform what is exposed via module info, and respect the module info
Because the module info defines what is publicly consumeable
And I see no reason why publicly consumeable stuff should not be transformable
By everyone
At the same time
That also limits reflection
Well it might still have non-public internals
Because the JVM restricts that data niocely
If the module is not open you can never reflect by default
I don't think that exports allows deep reflection
I think it does not, but I never tested it either
I think you can reflect a field in a type in a exported package, even if the type is not in an exported package
But I don't think you can reflect then into the fields and methods of the value of said field
Because that would be reflecting into a type that is not exported
You can't call setAccessible on a member that is not open I think
Regardless of the exports
On any non-public member
Regardless of its type
I think you can still find classes that are not exported?
But you can't do anything with them
No access at compile time, and no access at runtime
Seems fine to me
What is the actual resolution here? We are currently not restricting anything whether it is our internals or the internals of libraries on the CP
I see no mandate to introduce any such restrictions at the moment
What do you mean with no mandate?
I mean by default (if a mod does not provide a module info) we can still open it to all
But if a mod or library provides a module info
Then I think it makes clear as to what it considers accessible by others and what not
That is a change in architecture
And no mandate means: we have made no decision to do that and have no consensus to do it, either ๐
fwiw, I have said this in the past, IMO it's wrong to restrict people's ability to modify other mods. it goes against what I believe is the core pillar of modding, which is to give the player the ultimate choice of how the game works, and if we say "this mod doesn't want to be modified, so you can't customize it", this pillar crumbles
I'd tend to agree, yes
Except that this also has a different side: The player wants a modpack to actually "work", and there are now modders, not players, abusing that freedom, to cause problems for players
We have made restrictions as to what modders can do when in the past
To achieve the same balance
Did we? I thought generally we've just added more validation to catch bugs earlier
They still have the trifecta: JNA, Unsafe, ASM ๐
ASM would not work in this case
Not sure how the JVM protects against abuse of JNA and Unsafe, but at least the latter will get removed soon
It doesn't since it cannot
We have multiple ways to access native memory
even if you ignore Unsafe
java does "the best it can" to remain sane when code uses native access
It does
And IMHO we should follow that idea
We should be sane when we allow transformation
And if a modder, explicitly says: "Hey listen I don't think modifications to this area of my code is a great idea" then we should follow that
So anyway, it's pointless to litigate our differences in opinion about allowing mods to close themselves
So, how do we proceed? And what is the scope.
IMHO I would like to see the module info to be respected
If a modder provides it, then follow it
If a modder does not, then open it to all
And I think we should generate an open all for minecraft and neoforge
Can we bring more maintainers into this discussion
Well yes, I understand your opinion, I just don't share it
How do we get to a decision
We talk about it
Since the trade-off is that for a very niche use-case we drag along a massive technical investment
Let's bring it up in #project-talk
This should be in #maintenance-talk IMHO
Just make a brief summary of the pros and cons before
But we can do either one
Yes, it's not really #project-talk
Is it not? It has large implications I would say
Maybe. I think it won't be used by many.
Just by those that are effected by those mixins at least somewhat reasonably often
Wait, opting out of transforms is a different story, and would not require use of module metadata. Actually it is orthogonal.
It's just whether you're open to reflection at runtime that's more or less the question of module metadata
Sure, but as I already said above, I think module metadata describes in a proper JDK defined way what a modder considers to be public code.
As such it is a build in way that allows us to easily define what should be accessible by others, and as such transformable (at least I see no problem in accessible == transformable), versus what is not accessible to the public and as such should also not be transformable.
BTW if you publish such an artifact, it will have no effect at dev-time for consumers of your api
I know that right now it is not used properly, but that is a different step altogether IMHO.
And possibly a parallel discussion to have
And you'd also like to then respect the module-metadata of the libraries?
Meaning KubeJS has to stop using the gson internals?
Yes, maybe, should potentially be discussed that for libraries it can and maybe should default to fully open. Not sure.
I don't use internals of libraries. So I have no idea how far it would reach
IMHO, if it where up to me, then yeah
we cant actually make them open
If a module specifies: this is accessible, and this is not.
Then we should respect that
So yes
If that means GSON says, don't touch this
Then yeah a modder should not be able to do so
so program args can be added as argfiles?
provided that there is no explicit main class given to java I suppose (i.e. it also comes via an argfile) 
I was checking the prod installations
otherwise you could not override that individually
... by simply not including the main class arg-file
I don't understand
you mean the user can just pass something else as the main class to the run configuration settings?
yes
check the startup-experiments branch briefly
just in GH web-view
and check tests/build.gradle there
ah yes
but that's only required because it's using a neoforge that itself uses an old FML/BSL/etc
fine by me but that goes to show how weird the setup is
Yes, that much is true. I think this can still be simplified later but at the moment it is convenient for various experiments
other question: why are we hardcoding the daemon version in a task that is just writing a trivial property?
this updateDaemonJvm task seems incredibly silly
can you even run it if you are running on the wrong jvm version?
yeah we can rip that out
so I've been thinking a bit
we want dev and prod to match as much as possible
yet in prod we have a fixed classpath + a mods folder, while in dev we have a flexible classpath
in dev, we can already know the fixed classpath of the prod instance
so the first thing we should do in dev is split the classpath in two parts:
- the things that will be on the CP in prod (basically this means MC's own libraries and our core startup code)
- everything else
then we could treat "everything else" as if it were a mods folder? i.e. any library there would automatically get loaded into the "PLUGIN layer" by default, etc
i mean that is more or less what my FML startup code does using a hardcoded module whitelist
in the startup-experiments branch
i let everything else pass through mod discovery
and added a low-priority "make the rest a library" locator
i did not add all of vanillas libraries to the whitelist, since that is not necessary, really
And yeah this loads ATM10
couldn't some services want to make use of them?
I'm not surprised; it's the only way to go back to a single classpath I think
Yes, that is more or less a platform we also define as a subset of vanilas platform
correct, it is
well not entirely, if you went with non-modular, you'd invert this
and filter on the TCL
well I was reasoning with a non-modular setup
by applying a filter to load every jar you identified as participating in transformation there
if you're non-modular you do not need to do that
you can just do full discovery
when you identified everything that you want on the game layer
I'd actually build a new classloader that can only access our "whitelist" as the first step of init
you just ensure the TCL loads that locally
in non-modular
you should not do that
because it costs you time needlessly
you just use the app classloader as it is
what is preventing services etc from loading MC classes?
in production, there are no MC classes on the CP
except for the three entrypoints
that is true
in dev, we can validate this using the agent
isn't that slower than scanning the CP once?
in two ways, really. post factum and while it's happening
you're not just scanning the CP once, you're having java reload all the jars twice
the agent can give you a list of ALL loaded classes across ALL loaders
that can be used to just do a sanity check before you launch the game that anything you want on the TCL hasn't already been loaded in the top layer
the other option (we may just hide this behind a debug flag btw)
because URLClassLoader will keep a jar open, you mean?
would be to register a global transformer that makes sure the minecraft classses are not loaded on anything but a TCL
it has to reopen it if you construct a new URLCL
supposedly it is lazy
but if you do a classpath scan
you have obviously made the jars on the app cl nonlazy ๐
well can't you just scan java.class.path
okay you gotta be more precise when you say classpath scan .D
ok well I guess it's not trivial how you determine which jars are whitelisted and which are not
But generally I'd step away from that because other loaders don't need that step and we can have additional safeguards they currently don't use
fabric does exactly that
wait, seriously? ๐ค
let me check
I mean in that scenario you're fucked with the FS providers again
So my general philosophy is: we don't need to take extra care in dev, in my opinion
since there's only three transform candidates on the system CP
Where is it constructing a new CL from that
Yes but that is just used by the transforming CL to filter which classes to let pass through to the parent
at least that is my understanding of what it does
we can do that based on packages if we continue to enforce no package overlaps and scan packages per mod jar, which I'd perefer, honestly
well yes
that's what I mean by scanning the CP
I mean "find the jars that we care about" and "ignore the others"
but I suppose you'd have to scan the entire CP for mod discovery anyway 
scanning for packages isn't free btw
you made it sound like you wanted to construct another URLClassLoader directly beneath the ClassLoader.systemClassLoader
with just the whitelisted URLs of the system classpath
It's trivially cachable
that's exactly what I wanted to do yeah
that's not what fabric is doing, and we don't have to do it either ๐
it just leads to fucking annoying problems
well how else do you think it achieves "classpath isolation"?
by doing child-first for everything except a subset of system libs in its TCL?
I think this is a discussion on how that would even look like
how would it know whether it should do child-first or not for a given class name
in a hypothetical case for us?
I am talking about FLoader
Well they are putting no restriction on packages
I am 100% sure that it builds a CL with a subset of the URLs
well the TCL with the transformable classes
but that is the inverse of the whitelist ๐
Is that not the whole idea of why we use modules?
We can technically assign a given set of modules a given classloader?
eh, well yes it maintains a package-name -> module map internally
And as such a package -> classloader map
It's just one classloader though that matters, really
Ultimately it's "local modules" or up
Yeah the TCL no?
yeah
you have to either decide to load the class with transforms or not, basically
okay if we hypothetically would drop modules
we'd still have to scan every jar
for annotations at the very least
which is why I would not actually drop the discovery of packages per jar file
I think that logical link between jar -> module -> classloader is the thing that it gives us
and still maintain the constraint that packages in jars are not allowed to overlap
The problem is determining in what layer a module goes right
no we do that in the discovery phase anyway based on jar metadata before modules are even loaded
when you look at the output of our discovery it spits out a list of jar files that are supposed to go on the "game layer"
which corresponds to the transforming CL
if we have scanned previousuly which packages these JARs contain
That sounds like reimplementing about Half of jpms
you already have the info you need to filter every loadClass call
Just doing it ourselves
to either remain in the TCL or pass it upwards
it's 5 lines of code in the loader ๐
which by the way
we somewhat alread have since we use a custom MCL
Look I am going to be honest
There is no consensus on removing modules.
Not within the maintainers and not within the community
If you want to research further down this road
Then we should open this up for proper discussion
Instead of doing this in this thread
we are only discussing hypothesis here
Well, my current startup branch doesn't actually remove modules ๐
i commented out the boot layer hoisting to get ATM10 to launch
but the code is there to construct the structure as it is today
layer- and module-wise
the reality is that even in the maintainer team most people have no idea what module support actually entails
Yes and that is fine you can keep on discussing hypothesis, but I think we all know it could work.
Well I am not sure we all know how little it'd change user-facing wise
but how much code we'd just delete
Is that not why we made neoforge
modules or not we need to think about classloaders
So we can all have a say in these decisions
modders will realistically not notice this at all, it's not like the modules are exactly visible to them anyway
That is in our code base (the package -> module lookup)
again, this is a technical discussion on details of the CL setup
that is not relevant for most maintainers (unless they have interest in it, of course)
There's no point in making a case without actually presenting a solution that works
Because functionally
There's not really anything that results from having modules
other than pain, currently
Which yes, we can work around with more work (see startup-experiments :P)
But the realization stands that being able to just delete large swaths of code without loss of functionality for our users
hmm shartte, how exactly does your branch discover mc libraries
I am sure that it also has something that prevents the layers above the TCL from loading a transformable class
I am not sure it does ๐ค
it 100% does, that's the entire point of classloader isolation
You can always go via ClassLoader.getSystemClassloader to get around it fully
it can't have something "in it"
Buuuuuut, we'll just find out
what they do
hmmm it is true that Fabric does not have intermediate class loaders
A side effect of this effort is that the entire game including libraries becomes exposed to Mixin and other bytecode editing.
And we do not actually want that, I hope? ๐
No we do not
This was discussed a bunch
We especially do not want to expose auth libs and several others that are needed in that area
is there any reason besides performance?
I can primarily think of performance reasons
But those are imho quite significant
Since in our case, we parse them all
you can already mess with MC auth anyway since you can transform MC's source code
yes... ๐
in any case I don't think that there is demand for this feature
it's a double-edged sword
on the one hand, maybe one day someone will want to patch a guava bug in an ancient version
but that seems unlikely, and there are probably hacks to pull it off anyway
in practice it seems unlikely that anyone cares about transforming any libraries except maybe DFU
that restriction also applies to libraries (not game libraries) loaded from mod jars, I assume
Yes but that was always the golden rule when it came to core modding and mixin. An unwritten agreement with mojang, that we one do our stinking best to not make it easier, and two if we find people doing it report stuff like that to the community wide.
I'm not buying that since Fabric has been allowing it for a while
there's no reason to transform the auth lib when you can just jump over the call site
precisely
but again, i'd not spend any time on this
that's why auth is server-authoritative ๐
since we gain benefits from not even entertaining the thought
since it also means that in production, everything on the app cp is where it is intended to be with the exception of the three entrypoint classes
I am not sure what to say to that, other then, it was agreed upon before you joined here.....
sounds like it's irrelevant to the present then ๐
It does not seem like it to me
The rule was made for good reasons I am pretty sure, it extended beyond coremodding but more to a general principal
what rule
The rule that we did not make systems that allowed bytecode modifications of mojangs authlib
I am not sure it still holds either
But I don't think it is wrong.
yeah that is very very weird
it's such a pointless rule anyway, who would come up with such nonsense
it kinda is though ๐
The general thought is a good one
not really if you can hook into the game
you can redirect all calls to the authlib to a shaded modified version of it, very easily
the authlib doesnt run on its own
pre-mixin that would have required some actual significant knowledge of bytecode
yes but so would have modifying the authlib
It wa not that simple
probably still does since mixins doesnt do global redirects
you dont have to do a global redirect
you know exactly where the call is in vanilla
this is like old-school securom hacking ๐
My point is, preventing abuse and modifications of the auth systems is at least a worthy cause to a certain degree
when games really had a stupid if(!isValidCdInDrive()) { ERROR PIRATE! }
copy protection
if you can bytecode edit, you rewrite the if
not the isValidCdInDrive function
I understand that with instrumentation you can basically do what ever you want
I will say that with these "unwritten rules" that keep coming out of nowhere... IMO if there is no publicly accessible rule and/or clear reasoning that goes with it the rule does not exist
I understand that, but suppose we had those protections in place
So that people could not easily modify those areas of the code
but we don't ๐
Even then
No it was a hypothetical
as to the fact that protecting the auth chain is a reasonable idea
But anyway my opinion is that this discusison is a waste of time since the more libraries we keep on the app cp the faster it all is
How much does it matter?
And what are the gains if we lift the libraries?
with our current transform chain
we still parse everything
with asm
so add a mountain of classes onto that...
classloading is still ridiculously slow (on NeoForge)
I stil have it in my head that we can at least try CDS at some point
that has massive perf benefits for java desktop apps normally but was so far outside of our reach
at least with parallel init phases we hide some of it
CDS?
Class Data Sharing
They generally improved it betwen 11 and 21 too, at this point is should be possible to have CDS archives for the "Prefix" of your actual classpath
which can still be used even if the classpath after it is appended
well anyway
In general using as much of the "standard stuff" as possible is appealing, in my opinion
but that is farther off and the startup-experiment benefits are more tangible and have nothing to do with actually dropping modules
are you sure that is entirely our fault
when I profiled it last more and more relative time was being spent in java's own stuff
that's where CDS would help btw ๐
I know we burn CPU cycles on the ASM parsing but I'm not sure that removing that will be a silver bullet
Well I will leave you guys to this
the only consideration is if we keep the app cp where it is without modularizing it, which only works due to our lower layers not giving a fuck about require clauses heh
well no, keeping the libs where they are was about not adding more to it
Isn't it, like, way past midnight there? ๐
Yeah
I was watching grey's anatomy with the wife
embedded go and review the startup-experiments ๐
Specifically the stuff in here, which replaces BSL and rebuilds the module-path and makes us not need a legacyClassPath file anymore
@haughty copper Are you using dynamic attach for the new FML?
Or how are you attaching the instrumentation
@timber ingot @glad cobalt the point of that PR is to make testing FML in dev and prod envs easier