#Registries Overhaul
1 messages · Page 3 of 1
then don't write bad code idk™️
I suspect there's some more complicated reason about it making type solving exponentially harder or something
Anything with super like this can be rewritten with extends, generally, so it's no big deal
ok so I left and came back, what was decided
dual generic is good idea but bin breaking
so we can either put the whole thing off, or leave it single generic and add the second later
how did you confirm it was binary breaking
Another option is to add the second generic to both RO and DH
Which would be source but not binary breaking
Which may be alright
bc to keep RO's single-generic signature I had to do this #1131130393144336385 message
type erases to DH which is bin break
sure I could fix it with a coremod but ew™️
yes
that one is source breaking™️
idk if we care though
@shrewd garden @outer pulsar do we have a policy on source-breaking changes 
as long as it's damn near trivial to fix
well
you have to add the base type for every single registry object
which depends on what registry objects you have
so I wouldn't call it trivial
Since you can't do a mass find replace
it would be RegistryObject<T> -> RegistryObject<R, T>
it's context dependent
or we can just keep single generic with a Holder<X> downcast method for now
I guess its an upcast
not a downcast™️
It's trivial with multi-line selection since all the field declarations should be identical in length up until the generic
So yeah, either double generic on both or I add this to DH for now
/**
* As a workaround, this method is provided to allow upcasting the generic type of this DeferredHolder to properly use the Holder methods.
* @param <X> The target type. Should be a supertype of the current type.
* @return this
* @apiNote This method will be removed in a future release, and DeferredHolder will have two generic types.
*/
@SuppressWarnings({"unchecked"})
public <X> Holder<X> cast()
{
return (Holder<X>) this;
}
Could the vanilla methods that do not support subtypes be patched to support subtypes, if I am understanding this correctly?
some but not all of them
i.e. is(X) can be
unwrap(), unwrapKey(), canSerializeIn and tags() cannot be
That basically means that we have to extend Holder<Block> otherwise we have issues
in the interest of compat (which was the goal, anyway) I'll just add cast()
like
this is kinda ugly™️
if(BLOCK.<Block>cast().is(BlockTags.STAIRS))
granted the vanilla alternative is not much better
this.getBlock().builtInRegistryHolder().is(pTag);
oh well
its a stopgap anyway
no but you can alt click drag, you're going to have a bunch of RegistryObject< in a line that you can replace with DeferredHolder<Item, in ModItems, etc
That's what I said, yes 
it's the first thing on the line so it'll all be at the indent
ah sorry i was scrolled
one thing i don't like is how disjoint the todos are, how are we supposed to ever find them again?
we should standardize a tag for todos for the next breaking change period or something
or at least standardize them across this pr so they can easily be found with a project wide search
sure
as long as its consistent exactly like that
I just found a really crufty old coremod
"getLanguage_bouncer": addBouncer("net.minecraft.network.play.client.CClientSettingsPacket", "func_149524_c", "getLanguage", "()Ljava/lang/String;"),
it's still setup to be loaded!
but this class and method obviously don't exist anymore
Iirc 1.14 to 1.15 timeframe
should we automatically create a world backup if a user loads and entries are deleted?
If there's some system to delete it later automatically if there were no issues - then sure
But otherwise, saves can take up a lot of space in general
we used to show a warning before joining the world
just bring that back
(it got turned into a log message at some point)
Warning with a button to make a backup maybe?
the thing is there's already substantial data loss in the current system
because it only keeps blockstate data
honestly from my experience the user expectation is that removing a mod, loading the world, and then adding it back should nuke all of its content
extra work
at that point I'll just leave a TODO and maybe add it once I guess the rest actually working
yeah honestly dw about it
warning screen is nice to have but i think people are aware of the consequences
It went away because mojang made world loading a lot more complicated and no one has had the time/could figure out where to put such a hook back. So thank you for volunteering
The bigger issue imo is when game errors but you still somehow make it to the main menu, and you don’t realize it errored so you open the world without the mods present/loaded
Consumer<Map<ResourceLocation, Map<ResourceLocation, IdMappingEvent.IdRemapping>>> remapsConsumer
I feel yucky
...what
is that a map in a map 
at what point is it better to switch to a Guava Table 
this is legacy code jank that I don't want to entirely overhaul rn
ah
what is it? I might be able to de-spaghettify it
if this is for the fuckin missing mappings event just delete it
aliasing solves that problem
this code is haunted Map<ResourceLocation, Map<ResourceLocation, Integer[]>> remaps
honestly what even is this for, who needs this event
might be something @keen thicket should waifu
iirc some of the missing mappings code is half broken but in a way that doesn't matter because it was meant for the era when it was important to persist the numeric id map
whereas these days that isn't important at all
[well, mostly - i think chisels & bits might still rely on it]
yeah waifu data is good
@hoary umbra you can make your own queries btw
I might later
for now I will just comment out missingmappingsevent it's garbage and too spaghetti to revive

I feel a little evil
Iterable<? extends Registry<?>> registries = registryAccess == null
? BuiltInRegistries.REGISTRY
: () -> registryAccess.registries().<Registry<?>>map(RegistryAccess.RegistryEntry::value).iterator()

I had to do a little bit of thingies to get biome ids to save
I went through and looked at every single vanilla registry manually to find the ones that need syncing of int ids / saving int ids to disk
I found that only mob effects and biomes need saving to disk
Registries.MOB_EFFECT, // Required for MobEffectInstance serialization
Registries.BIOME // Required for chunk Biome paletted containers```
and I documented each one
ForgeRegistriesSetup
what I forgot to do is make it so I can serialize non-builtin registries
so I had to fix that
now ModifyRegistryEvent fires for all builtin & datapack registries :)
my code needs a lot of testing
@hoary umbra is the mods button being back new?? is that from upstream?
nice, saving the biomes worked https://i.imgur.com/InPKPHy.png
I hate it
yes I hate it too
What's the problem with it?
muscle memory
would it be possible to have a DR register method that doesn't return a supplier, but rather directly constructs the object?
working on the capability rework, I have the following code in a test mod:
private static final Supplier<AttachmentType<ItemStackHandler>> HANDLER = ATTACHMENT_TYPES.register(
"handler", () -> AttachmentType.serializable(() -> new ItemStackHandler(1)).build());
the supplier is kinda unnecessary here because constructing the objects is safe
it might be cool to have something like
private static final AttachmentType<ItemStackHandler> HANDLER = ATTACHMENT_TYPES.registerEager(
"handler", AttachmentType.serializable(() -> new ItemStackHandler(1)).build());
just an idea
not worth it imo
there's no way to reliably tell people to not do that for intrusive objects
which is like, most of what people register anyways
yeah I guess it would cause hard-to-diagnose crashes
you can already do this for your mods and it's called hold the instance in a static field but we wouldn't officially recommend it to anyone cuz it could theoretically break at any moment in a new update
private static final AttachmentType<ItemStackHandler> HANDLER = AttachmentType.serializable(() -> new ItemStackHandler(1)).build();
static { ATTACHMENT_TYPES.register("handler", HANDLER); }
yeah that works
@hoary umbra well my registry overhaul works and runs
and at the very least doesn't crash with forgedev and saves the ids properly to disk and syncs the ids thru network
Create uses this. There should be a way to remap ResourceLocations of registered objects.
the registries will have the ability to register aliases of old names -> new names
yeah that's already done
it also allows chains of A -> B then B -> C but I'm not even sure that's useful
oh actually it is because we save aliases to disk
this behavior sounds very concerning, I do not know how it would be triggered
Honestly may just userdev and some bug that makes it rarely not include the mods or something
could we make intrusive holders thread-safe and unfreeze registries at mod construction time?
that might be beneficial for performance because instantiating blocks is quite expensive due to their shape calculations
that would also allow us to get rid of suppliers 🤔
I should put all my voxelshapes in my main mod class
I already put everything else there
What benefit would you get?!?
Do you mean that would allow you to construct blocks outside of registration time?
I don't think that is a great idea
Cause it will cause several hard to trace issues
if you have so many voxelshapes that classloading your block classes on the main thread is causing problems, then you can init the shapes on mod construction instead of in block class static init
Yeah, that seems like a Bad Idea™️. The unfreezing early or whatnot. The making intrusive holders threadsafe option would eventually be necessary for parallel construction during registration events if that's eventually a thing
Yeah
For parallel registration
Which we will eventually look into
We need the combination of Suppliers
And ThreadSafe Holders
which issues will it cause?
I am thinking about parallel registration in the context of an eventbus rework, and it is a big PITA
Things that take a Block and aren't the method registering it generally expect it to be already registered
So violating that assumption by passing around unregistered Blocks or Items is a bad idea
If you were to allow stuff like that... Why would you even need the registration event? Like, it would be better to do fabric style registration than to keep the registration events but throw out suppliers
And that's got a slew of issues, as we all know, even if it comes with some advantages too
The suppliers are the advantage of registry events. They let you avoid classloading order issues, dependency issues where you can't make items till blocks exist, etc., in a way where the assumptions vanilla makes still hold true
I mean, the end goal is to make mod loading faster - I spent some time thinking about parallel registration, but it stops working the moment someone tries to read a registry from the registration event
Not necessarily
Which is a nono anyway
Eh, you can read the registry during that event just fine
Possibly
But realistically if your mod is doing that
And there's reasons you would have to
right now you can; but that might change indeed
Then you are already on the wrong path
As in, I have a mod that does and there's no alternative
Cause what in gods name would be a good reason to do that?
he's registering copies of some blocks, kind of
You can do that with a single block
Blocks whose stuff depends on other blocks, and you're locked into needing it at block construction due to vanilla constraints
That you always register.....
See C&B
You don't need to know what is in the registry
Hmm? I don't see how that's related
Or going to be in the registry
I am not aware of such blocks existing in vanilla
And if taht is the case
yes but that is a very complex approach - in theory we could provide a "tail register" event that would allow you to register extra registry entries that depend on previous entries; and that event could run on the main thread
Then we need to patch it out of vanilla and into a supplier
No
Hah! Good luck doing that with blockstate properties...
Cause that is a viscous cycle
?!? Example
But all of this aside, we don't really need parallel registration. We just need parallel construction
how so? we need to make the common use case parallel, but I think that registering block "copies" is a valid use case
The blockstate properties of a block must be known at construction. Period. In my case, those properties are dependent on the properties of other blocks
But 95% of all blockstate properties can be also overriden in the implementing block
So I am not sure what the issue is
You can pass in a default for the properties
And then override the method in the block
And pass it along for handling there
Basically, the slow part we want parallel isn't actually registering stuff. It's constructing it. I'm thinking, basically, each registry event would get split into two phases - a parallel construction phase and a non-parallel registration phase
The fuck? No they can't. I'm talking about FACING, LIT, those ones. Not BlockBehaviour.Properties
Due to intrinsic holders: That is not possible at the moment
assuming we fix intrinsic holders, I suppose DR could instantiate the objects in parallel in the backend and then register them serially
WTF
Those properties must be provided at block construction
Why would you need some other blocks STATICALLY DEFINED properties in your own?!?
All properties are statically defined in vanilla
I don't copy them. I add or don't add various properties based on what certain target blocks have
And I don't know what those blocks will be at compile time so...
Are you mimicking or what?
Cause C&B does all of that....
Granted not stuff like Facing
Sorta. This is for Excavated Variants. A, for example, nether gold ore embedded in basalt ought to have the same facing-ness as basalt has, but one in sandstone shouldn't
I can't work with a single block; I have to register separate blocks for everything
well luke's stuff would work if we made DRs register instantiate in parallel in the background
Also keep in mind that if someone is doing something where they need to access the same register that they're registering to during construction, and expect to get stuff, they're probably working with some sort of registry callbacks anyways
Like, I'm doing this with registry callbacks, mostly, except for stuff based only on vanilla blocks
(I mixin my own registry callbacks at present cause forge doesn't have them)
I think your idea could indeed work much better with something like a registry callback
couldn't you instead iterate the block registry right before it gets frowzen?
I could but then it becomes a game of who wants to do that last, and someone always wants to get the last say, and then my stuff breaks Contenttweaker or something like that
Yeah, that's what I do. I think we shouldn't worry about block ctors looking for non-vanilla entries in the block registry, so long as registry callbacks are an option. So basically, parallel construction, then register everything, would be a great option in my opinion if we make intrusive holders threadsafe. But that's probably a longer term sort of change
realistically if we do registry callbacks
Then we don't need to care for the seperation of registration and instantiation
Cause it means that they should be considered to run in parallel anyway....
Or am I seeing this wrong?
My thought was that ctors would be parallel, and registration serial, and within registration callbacks would be parallel, to offset the potential performance issues of callbacks
If the callback is already in parallel
I see no reason for the actual registration to not be in parallel as well
Also, keep in mind that people will still want to read from the register during construction. And that's fine, as long as they only read vanilla entries
Well, it's basically just writing to a map. Meaning that you've got to sync it every operation, basically, anyways
So, no huge benefit
But the big issue with parallel registration is that the contents of the register, during construction, will not stay the same run to run; and, you can't (and shouldn't !) stop people from reading the registry during registration, which would cause risks of modification while reading causing issues
Hence, why I'd separate out the ctor stuff
Again, I don't think that reading the registry is a good idea.....
I think poeple should use callbacks to do it
If you're looking for vanilla entries it should be fine, no?
No
Orion: I can't use callbacks to listen for vanilla stuff to be registered
Cause it's already registered
Due to registry replacements, when you are running in parallel, there is no guarantee
Well there were some reasons to use them, for example with cauldrons
But fundamentally, callbacks won't work for stuff where I need to inspect vanilla registry entries
No, there is nowhere to use them where a mixin wouldn't suffice and be more compatible
I mean we could offer a snapshot of the state from before the registration process......
And that way you can at least see the vanilla blocks
registry replacement is gone for good, forge regs existed 60% just to half-ass the buggy registry replacement and it's a lot of patches
Okey
Then yeah luke
What do you think
A snapshot of the registry before the registration process starts
Basically, my current flow, for Excavated Variants, is:
- during the normal registration event, register anything where the dependent blocks are already registered
- register everything else in callbacks
I have to do this cause - during the normal registration event, lots of stuff I need isn't registered yet in all likelihood
- callbacks will never catch vanilla entries
Plus callbacks
What benefit does that bring over just separating out the ctors?
Not sure
Just discussion different approaches
I think a dual stage process, is a bit meh
It's also... Workable but painful for multiloader mods
Cause now I have to handle grabbing that snapshot and whatnot which could be painful, but eh, doable
Also, I really want to move away from neoforge providing its own version of registries - ditching the forge registries is a good idea. I worry that a snapshot would be, once again, basically just recreating a vanilla system that already exists
No i mean it should be trivial to get you a nice Map<ResourceLocation, T>
Trivially enough
It would not be a full registry
Just a copy of the contents
Cause that is what you are interesting in, is it not?
I mean, yes, but we already have that. It's a registry. But I see your point
The other option is to make registries backed by a concurrent map of some sort. That way this all sorta becomes a moot point
But I also don't know how intrusive that would be
My point is that during parallel processing of the event, the view of the registry might not be consistent from run to run, or even have problems with Concurrent modifications during iterations.
Or what sort of performance issues might arise from it later on
that sounds slow
synchronizing maps with thousands of put calls isn't exactly efficient
Yeah, exactly. That's why the snapshot approach or the separate out ctors approach would be better
You can make the map concurrent during registration and then readonly / immutable during freeze
Eh, seems iffy to me
We have the relevant methods already:
freeze and unfreeze
Simply change the backing map
You're poking around in vanilla registries too much
We want to try not intrusively modifying the system beyond recognition
So yeah, I'd say a snapshot could work, though I don't see a benefit over separating out ctors
(also yeah, concurrent backing map has issues of non-deterministic-ness)
Separate ctors leads to less possibilities for weirdness in my mind - it's impossible to screw up and access the registry in a block ctor when you shouldn't - something which would lead to difficult to reproduce bugs
And neither would really have many user facing changes
indeed we should run some tests but separate ctors sound promising to me
You cannot but you could use a bake callback at the end or have some sort of Boolean in your add callback that only runs the first time but I think the one modded entry would be added after this.
Currently my registry rework has registry snapshots for vanilla and when the game is frozen but these are up to be deleted.
And also in internal classes
why not go through the vanilla entries before the register event and run the callbacks manually
well that is a possibility I guess
Yeah but to do that I need to grab vanilla stuff from the registry somehow, which involves either enqueueing work (I can probably do this, actually) or running it at the beginning to avoid parallel stuff. Though yeah, I feel like maybe the solution here is to do all registry stuff as enqueued work. So, like, in the registry event - you construct the block normally, then enqueue registration
Because I can't register stuff before the register event
Oh wait, you mean forge doing that?
Hmm, maybe. That could be interesting
Though honestly I don't think it's too necessary
the parallel registration is entirely outside of my current rework, although it could be added with proper discussion
I just feel that's a separate topic altogether
Yeah, I think it's a future issue
I brought it up because I am working on EventBus
don't most vanilla shape calculations happen at classloading time? could we do something to parallel preload all vanilla block classes?
you mean before mod construction?
idk exactly when would be good
I had an idea™️
what if instead of the dual generic that we need to fix holder, we reduce to one generic for the base DeferredHolder class, and introduce a DeferredHolder.Strict subclass with two generics
then DeferredHolder instances may be referred to with less verbosity, and those who need stronger typing can use the subclass
why? even vanilla does the hacky casting
Vanilla does no such casting, vanilla holders only match the registry type
that's why were in this mess in the first place
I saw a helper method for exactly that casting somewhere in vanilla/forge code before tho
I assume you just mean why in general?
I don't want to manually downcast all my things
I have a feeling youre talking about that Class<T> cast back when forge regs had a class type
vanilla doesn't have this problem because vanilla has statically typed fields [of the correct type rather than the registry type] that are used 99% of the time
so maaaayybe the solution is to rework ObjectHolder
how exactly does object holder help in any way
because then mods can use correctly typed static fields too
you don't need a Holder<MyBlock> if you can have a MyBlock
or... go back to static fields 😄
We are not going back to static fields
At least I would not......
We are planning on doing parallel instantiation
With delayed serial registration still right?
So....
Holder<X> is way better
why exactly does the registration need to be serial?
the registries and some constructors aren't threadsafe
you'd have to patch all of that to be threadsafe
or you can just have registration happen in the main thread
Even though it does sound like trouble
It is an excellent target for parallelised optimisation
Its definitely worth at least thinking about
I think synchronizing the register method would be the easiest (a single patch)
construction would benefit more than registration probably
Disagree
that defeats the purpose of parallel
IO will be a bottleneck on its own
i've had the thought of a parallelized registry event holding the registered objects in itself, and then later they all get registered serially
Registration is all in memory objects created from all in memory objects, isolated from each other
Its about as perfect a target as we could ask for
technically possible right now with changing the impl for RegistryHelper
The only exception is mods that dep on other mods, but that’s workable
that would also be super inefficient
definitely, yes
class loading would benefit most
Construction is inherently limited by io, which for many small objects is often a bottleneck of its own
Disk io
yeah, state definitions..
?!?
registry stuff doesn't do any IO
And shapes
They are loaded later are they not?
This?
those are static tho
Like yeah it creates the default values on construct
But the actuall parsing of the file happens way later
But classloaded at registration
Well, construction
Given that all jars are loaded into memory by NIO
The IO is not really such a problem for classloading.....
we'd need to control when the class loading happens tho
Nah
I thought they meant data loading
Like asset loading
state definitions? no, they're done in the block ctor
Nope.....
I am going to check
But I am like 90% sure that they are loaded later
Then how does default state definition work lol
That is defined in code.....
There is no file needed for that
Additionally the blockstate files only exist on the client
pretty sure it's the block ctor
What..?
blockstate jsons aren't state definitions?..
The state definition is not loaded from disk
It is done in code
There is no IO involved here
I dropped io ages ago and explained it was a misinterpretation
See
They are fully in code
The Client then loads the blockstate file later during resource reloading
so, that part should be parallel
yes.. and that's why we've said block construction is slow. not sure where you're trying to get at 
we all agree that block construction needs to happen in parallel
^
yes but the question is if and when we need the synchronisation point
createIntrusiveHolder needs to be made parallel capable
yea that is step 1
lets get rid of intrusive holders
There i no real performance degradation
you basically need to make the holder map concurrent. which is a problem since it slows reads
Becaue the map only holds unregistered holders
And as the blocks are registered they are drained
And then not used at runtime anymore
concurrent maps have fast reads
and yes what Orion said is correct
Nope, again that map only holds them temporarily untill they are registered
use a synchronized wrapper for intrusive
So the performance on them really does not matter
Not needed, also way to expensive
A concurrentmap is much cheaper
That is the line that needs to be patched
And done
is there a concurrent variant of the identity hash map?
sadly no
i believe the IdentityHashMap in unique in that regard
What is the key property of that hashmap?
it replaces equals by ==
as long as Blocks etc don't override equals or hashCode we'll be fine]
he meant the key type
IdentityHashMap goes counter to the Map interface by comparing with == (identity) rather than with Object#equals
ah, map key 
see, this is why English is trash
T
which is why it should probably still be a identity map
disable writes entirely and just give it a holder later
otherwise a concurrenthashmap is a further perf degradation
let's use german instead
I asked for the key property that makes it special
so collections.synchronizedmap?
The answer for the reference comparison is the right one 😄
we're NOT gonna use synchronized it is way too slow
and we'll have high contention
both of them are right answers 
what we can do is patch a final equals and hashCode that call super in intrusive holder types (Item, Block, etc) to ensure that mods don't override them
the duplicate checks of a normal hash map are going to bite us back
this sounds like a lot of overengineering when you can just turn off intrusive holders and add them back during registration or someplace later when it's serial
it's a single patch + optionally some safeguards for mods not to get things wrong... I personally think we'll be fine with ConcurrentHashMap
that makes them completely useless
time for a record ValueHackery<T>(T data) { @Override public boolean equals(Object other) { return other instanceof ValueHackery<T> otherVal && otherVal.data == this.data; } }
cursed solutions for cursed problems
(fun thing is I did write that kind of code in ParchmentJAM; don't remember if I kept it)
I mean they should be really. they shouldn't exist. but it not existing before registration time doesn't really matter as it's just as useless until then when it's bound
and if you don't create them on construction they're completely useless since you can't check it the object was registered
the only uses of the intrusive holder is to check that the object ended up being registered, that you don't construct them when you shouldn't, and that there's always a holder available in the deprecated builtin holder methods
Somebody check this
I think the types that currently use intrusive holders
how is that first one true
Are reference equals anyway....
yeah that's what we need to check
So it does not matter whether it is an identity map or not
well, I guess yes the registry validates all intrusive holders get registered but that's not very useful
the registry can't be frozen with unregistered intrusive holders
that makes them useful since you at least know somewhere you did something wrong
and with debugging you can figure out where
intrusive holders should be on their way out anyways
if the perf hit for the concurrent maps is significant (see: benchmarking) then it should be turned off
the features that intrusive holders provide are simply insignificant
I disagree with that but w/e
Personally, I think parallel registration is entirely out of scope until event bus changes are finalized, and the core rework is finalized. That includes moving everything to vanilla registries and reworking all the registry sync/save code to work off vanilla registries aswell.
The more I think about it the more it seems that eventbus won't change a lot
It should only change to support new features imo, to limit breakage
Which generic would you be keeping in DeferredHolder? If you're keeping the less specific Block there then yeah, makes sense to me - I think in the long run, we want it to be a Holder<Block> and easily provide a Supplier<MyBlock>
Yeah, DeferredHolder<Block> with something like DeferredHolder.Strict<Block, MyBlock>
I don't like Strict
It's still a Holder<Block>, right?
The only difference is that the get method has a more specific return type
But yeah, sounds good to me I guess, especially as DeferredHolder won't be final or anything
So the register function returns an instance of the specific one, but that doesn't have to be kept around
this will break stuff because for my datagen I use Supplier<? extends X> where X is not necessarily the reg type but that is not possible since Holder is a Supplier (it can not be a Supplier<Thing> and a Holder<RegType>)
and no I won't do ::get since the whole point of the suppliers is to be able to just use the RO
I think we're talking breaking changes anyways here
Since for now there's no alternative to doing unsafe casts somewhere until breaking changes are made
Because yeah, at the end of the day it must be a Holder<Block> to avoid tons of vanilla issues, and that means letting people use ::get if they really need a more specific type
yes but that would make any DeferredHolder for blocks a supplier for Block and not the more specific type which is a problem for things that require Supplier of a more specific type (one example is FlowerPot i think)
Hence why you use ::get, as necessary, to get the more specific supplier
then it no longer makes sense to even implement Supplier in the first place if you have to do ::get anyway
well yes
It's still a Supplier<Block> though, and it allows vanilla holders to be passed as suppliers which is useful
but many (if not most) things need suppliers of the more specific type not the base type
that is not vanilla but modded stuff
Sure, and for those cases ::get would exist
::value exists too for vanilla Holder
Oh, hmm, then yeah, I say we drop the supplier from holders altogether
hmmm, assuming we make intrusive holders threadsafe, instantiating them directly seems to end up being the simplest solution
does the registry rework get rid of the ModLoadingContext.get().getActiveNamespace(); calls?
@hoary umbra and @quaint pagoda? 
wait, I didn't need to pong, since they'll read this anyway 
I'd suspect that it does get rid of it, since if I remember right that silly namespace check was done in the forge registry stuff
Need links
these two:
- https://github.com/neoforged/NeoForge/blob/70b5d784b7f1eefbaaef4a48fd6b67593ece27a5/src/main/java/net/minecraftforge/registries/GameData.java#L787
- https://github.com/neoforged/NeoForge/blob/70b5d784b7f1eefbaaef4a48fd6b67593ece27a5/src/main/java/net/minecraftforge/registries/ForgeRegistry.java#L393
there is also this one that I will deprecate in a Neo PR in a few hours: - https://github.com/neoforged/NeoForge/blob/70b5d784b7f1eefbaaef4a48fd6b67593ece27a5/src/main/java/net/minecraftforge/registries/RegisterEvent.java#L128
the 3rd one is so unneedlessly expensive..
thread local lookup for every registered object
well that's just a map lookup
the registration does some of these already, so it isn't a big difference
also fun fact: the third one is not even used??? 
nothing is using that RegisterHelper, right?
(well, unless some mods decided to)
Is there any reason that the new registry system actually needs the mod context stuff at all?
that's the point
the first two are gone the third one easily can be aswell
at least in my version of it
is this feature going away or what?
maybe, I don't know
it forces any event using it to be dispatched on the mod event bus
Oh, true
Some people think that the current system (that being, registries locked until register event, and unlocked forever after) is a better system than unlocking until the register event, and locking after.
We should discuss this before we go any further with an overhaul (further than the current non-breaking stage, that is)
It's worth noting that mods' static initializers run in dependency order, so mod registry ordering is still preserved with unlocked-for-static-init.
Surely you inversed locked and unlocked?
registries are not thread safe you cannot open up registration at random points during mod loading
Yes I did
This is what I get for wanting people to discuss stuff while on holiday
There's a very clear disparity between how I remember these discussions going and how they actually seem to have gone
So clarity is great
The first step would be to investigate making intrusive holders threadsafe
Once that is done, I think static init as an option would be fine
static init yes, you can do it for any registry w/o an intrusive holder (which in 1.20.2 is basically none lol), Registry.register on the other hand
I think we'd want to keep registries locked, but allow intrusive holder creation
That would remove a lot of the current supplier-based patches
Maybe I confused static register and static init
How it works right now is vanilla registers everything and locks itself before mods can do anything. Then, register event unlocks every registry, runs all the register events, then locks every registry.
It makes the most sense to me to keep it like this
To me too
me too, I also see no point in changing except being more like fabric
A big advantage is to remove the supplier-based patches for many things
And we also get parallel object construction for free that way
The suppliers only existed to support registry replacement in the first place, then it turned out they accidentally helped with the 1.18.2 holder changes; neither of these things is a serious limitation: RR is going away and we need to patch intrusive holders to make them threadsafe already
that would technically make DR and RO obsolete which I don't think I like
DR will still be useful since registration will remain locked to the registry event
so DR.register would return the object instead of RO
RO doesn't need to be mandatory, maybe do two overrides for DR so that people who want RO can keep it
Idk if mojang plans on replacing Blocks and Items fields by holders
wasn't there a discussion about making entry construction parallel
Indeed - but enabling static construction makes some things nicer and kinda comes for free once you have the patches for parallel entry construction
but it removes the parallel aspect since it would happen in dep order
The benefits of static init are, in my mind, twofold: first, it's possible to make construction parallel at some point in the future, though that's honestly not a huge worry for me. The biggest advantage is that it guarantees that stuff is constructed and registered in the same order as vanilla does
Which can help avoid all sorts of weirdness
And also makes using stuff from other mods easier as a weak dependency
Like, if stuff is allowed to be statically registered - and I want to register an item that needs someone else's block, which currently I can do just fine - then there's no guarantee that that is registered when my mod loads. Heck, there's no guarantee that they do it in static init instead of instance init. And it requires a hard dependency, not just getting the block by location
As for static construction - maybe
The risk there is that Mojang makes a lot of assumptions that things being passed around will be registered
And heck, mods do too
Static construction was fine before 1.18.2
No? Mod ctors are not ordered in a specific way I think
Well in any case I don't care much - you can fake static init using the block register event if you want 😛
The suppliers only existed to support registry replacement in the first place
It was also needed for e.g. spawn eggs before we fixed the registration order to do entities before items which I think wasn't until 1.18
also I don't know if registration order is reliable for things like stairs [blocks that depend on other blocks] - something to think about if we're considering removing them
this idea sounds a bit concerning to me and that mods could stumble upon static init loops that break all assumptions and cause NPEs
it would only take two mods for things to go wrong.
Mod A could have a block which depends on a block from Mod B.
Mod B could also have a second block which depends on a second block from Mod A.
Depending on the ordering of the fields and assuming only one class for block init per mod, this could just cause NPEs and people would be forced to move to suppliers anyways.
and this probably could get more complicated across registries where mods add new registry ordering which does not exist in vanilla
say, requiring an Item definition in a Block constructor, for example
you could run into territories where it is impossible to linearly sort registry order
i mean the most common case is going to be that a mod has a block that depends on another block from itself, i.e. i register a solid block and a stair
but i suppose an addon to a mod could register a stair of a solid block from that mod
we could have a guarantee that mods are called in dependency order [within each registry, i.e. mod A blocks mod B blocks mod A items mod B items], and that blocks in each DeferredRegister are called in the order they are added
and possibly also that multiple DeferredRegisters belonging to the same mod are called in the order that their event listener was registered, if that's not too difficult
I don't believe listeners are ordered in registration order, at least I was having issues with that
possibly the event would have to be replaced (or augmented) with a system that provides more ordering guarantees
like I don't think it would be impossible to get rid of the supplier patches, but it'd need to be done carefully
like i am not saying we should try to make all kinds of dependencies possible, just that we should make it predictable which ones work or not. People who need items to construct blocks can use suppliers, the important thing is that nothing in vanilla does it
that seems like a slippery slope
how are modders supposed to know what they can and can't use without a supplier?
you would have to effectively memorize the order of registries + how classloading of static fields work and check that with other mods you depend on
i am not advocating for static init
just saying that removing the supplier patches from vanilla classes is worth looking at independent of that
Entirely fair, actually
That won't happen in practice - it's not happening on Fabric
That's fine... assuming all the objects are constructed it doesn't matter in which order you register them
uh..
it does though?
the vast majority of blocks need to be registered before items
entitytypes are also an oddity cus you tend to end up registering egg items for them too
except it does because I use my DRs registry order to construct my creative tab
I think tech has spent a bit too much time in fabric land
because "it's not happening on Fabric" is not a valid argument in forge sence we have so vastly different systems
yep
Well, the underlying vanilla classes are the same
Ah hmmm is that the case? I forgot
The only meaningful difference is that Forge mods are constructed in parallel... Everything else can be changed
Does this imply the removal of deferred registries? I think not having to worry about classloading when registering or having a step when each category registers things is really handy
that is the general implication for eventually™️
I would definitely not like not having DR because I use those to fill my creative tab
I am talking about the underlying system... I would hope that the high-level API (i.e. DR) isn't changed too much to make migration easy
I checked this and it's not true
the mapping (in Block#asItem) is lazily computed
the only thing is that you should not call Block#asItem before both the block and the item are registered
well if the EntityType were constructed beforehand you could just pass it to the item 😄
This might be an unpopular take, but I significantly prefer the fully deferred nature of the current implementation, especially when registration helpers come into play to make registration of large amounts of blocks not even more of a chore. One of the cases where the lack of deferral is especially annoying is registration of items referring to multiple blocks (i.e. signs and torches) because it causes field order to matter when it shouldn't IMO. The amount of BS it took to basically replicate deferred and ordered registration and then jam that into things Forge patches to take suppliers was absurd when I dabbled a bit with ML.
Yeah same. I hate having to balance classloading order since many registry entries need other ones to be registered beforehand and so on. I like just registering stuff and forgetting about it. Plus having dedicated steps at which you are sure all blocks have been registered allows for nice things too. Definitely in favor of not doing things the vanilla way of it means keeping deferred registries
So I'm strongly advocating to keep the supplier based stuff also because migrating would be a pain otherwise
Intact what would even be the benefit of not having that? Seems like just removing something useful just for the sake of removing it
not having it would significantly reduce the patch surface
is it just a matter of personal preference? assuming you split your objects in files by type (i.e. all items in one file, all blocks in one file, etc...) static creation should just work™️
I like having all on one class or generally not having to worry about it. Tbh one of the main things I like about forge
actually, having all in one class will also work with static definitions because javac will yell at you if you try to refer to a field that hasn't been initialized yet 😛
so in that sense it is more robust than just doing supplier.get() in the block constructor and hoping that the supplier is already ready
I don't think anyone was suggesting static registration
registration isn't construction
I don't do that separation because in my case blockitems are just a necessary evil and not something I explicitly store in fields. Instead, my block registration helper also handles item registration, which is where the issue I mentioned occurs when deferral is not a thing.
That's why the patched constructors don't immediately resolve the supplier 😉
well these patches don't currently exist for everything - and they sound annoying to maintain
I can't think of any relevant case where such a patch does not exist
I don't really understand why that would happen - if the block is registered sufficiently late shouldn't it be fine to construct the item at the same time?
it doesn't seem to exist for StandingAndWallBlockItem
No, because the issue is the construction of the item. Blockitems are the singular case that is not patched because the surface would indeed be far too large. In my case the block implementation controls which item is used, mainly because it significantly simplifies the implementation, which creates a dependence on the construction order when it's not deferred
but if you had static construction javac would enforce the correct dependency order for you
It would not because it can't see through the indirection of the item construction
class MyBlock extends Block {
public MyBlock(..., Block dep1, Block dep2) {
this.myItem = new MyBlockItem(..., dep1, dep2);
}
}
or am I missing something?
That's not what I'm doing, my implementation is closer to this:
registerBlock(BlockFactory factory) {
Block b = register(factory.get());
register(b.makeItem());
}
ah yes I see
could be refactored to not use a supplier with static init 🙂
in any case I don't think the existing supplier patches should be removed for now; but maybe the recommended pattern is worth reconsidering
Not without either relying on field order or separating block init from item init
I am saying that you would rely on field order, and javac would check it for you
worst case you get a startup crash if you get it wrong
Javac cannot see through the indirection of the item factory method, making it blow up on startup instead. Field order should not be relevant for this
"Field order should not be relevant for this" I disagree, this is just your opinion
it sounds completely arbitrary to me... maybe it's not the cleanest but it is also really easy to live with / work around
It is just my opinion, yes, but it has benefits in terms of simplicity and safety
I kind of agree with XFact here.
I like the lazy init / deferred init with suppliers
I like it even so much that I implemented it Fabric
hmmm, I have the impression that it doesn't change much on the user side overall, it's just a small matter of convenience - if you're already working with suppliers it's nicer to be able to pass a supplier directly, and if you're working with direct object references it's nicer to pass the reference
the thing is that the former requires a number of patches to add supplier support, whereas the latter matches vanilla and thus doesn't require patches
I did have many issues when I tried this stuff on fabric. Sure nothing you can't get by without but I think the matter of convenience is nice. Plus stuff feels less fool proof as you don't risk ending up with null stuff in your object if you clasload wrong
for me personally (as in for my mods) I don't care as long as we fix intrusive holder creation to be thread-safe... then I can do the static init which I prefer and everyone else can keep doing whatever
I think it's more a matter of having designed your mod around Forge's supplier paradigm and then encountering unexpected difficulties on Fabric which encourages the other paradigm... but if the mod had been written on Fabric from the start you would probably have paid more attention to the issue and it wouldn't have happened as much (my case - I don't remember having issues with this)
Having holders instead of registry object sounds like an awesome change tho
For me personally I use deferred stuff on fabric too with a crappy system I wrote
I actually had no unexpected difficulties
Didn't you have to make your own supplier-based versions of some block/item classes?
Where needed yeah
ANY time a registrable needs a reference to a registrable of the same registry, it should use a supplier/holder to reference it (and we can patch vanilla stuff where necessary)
though, how many places does vanilla still have that have blocks having direct references to blocks (or whichever registry)
The one remaining case that comes to my mind without an IDE is the stairs block
In my opinion, the use of static fields for construction/registration - either deferred registers or otherwise - is not the best practice in general because it relies on classloading to have side effects. At least with deferred registers you have to add the register to the mod bus so it's not quite as egregious, since the side effects are then dependent on registering the register, but that's a particular practice that I think vanilla has accustomed to even though it's not too great.
But that's also a very opinionated view and I recognize that writing stuff that way is not always convenient
forge patches it in 1.19.2 at least
Yes, I was referring to it in general, not in the sense of being an unpatched case
Isn't static init impossible to ever run in parallel for a single mod?
It's limited to parallelizing several mods
that sounds fine 😄
Macaw's mods would like a word with you
I create my defregs during the constructor instead of static init because I use a one-liner function to both create the defreg and register it to the mod bus, and I don't like touching the mod bus in static init
for the record, mod bus in static init is completely safe to do as long as you don't go through the thread local container
which one's the thread-local container 
is FMLJavaModLoadingContext.get().getModEventBus() safe or unsafe
unsafe
How do you get at the mod bus without going through the container?
what's the safe way to get it
get the container from ModList
That makes sense
do you call container.extension<FMLJavaModLoadingContext>().getModBus() then?
The paradigm I've been trying to switch my stuff over to is:
- set up deferred registration on instance init, in instance fields if I want to reference them other places
- store the mod instance in a nullable static field on the class after construction
cast the container to FMLModContainer
and that exists before my main mod class static inits?
no because the container static inits your class
okay I can't use that then
why
because you said no?
I interpreted that as "I can't get the container during my main class's static init"
wait nvm the container doesn't use the forName overload that inits the clas
can I get the mod bus via the container in static init main or not 
right but I wanted to know whether it was likely to work or not before I refactored everything :p
the items take the block as a constructor parameter. Yes technically this means the blocks need to be constructed before the items, not registered, but it is still an ordering requirement.
oh, that's been covered
anyway i don't think "jvm enforces it through class loading order" is a valid solution, too easy to fuck up, especially between mods
at least if we have a deterministic order then when you fuck it up it crashes in the same place every time and it's clear who is wrong
(on the other hand, if we did do static init, most cycles could be safely broken by separating problematic constructs off into separate classes instead of having one singular ModItems etc class - but that relies on modders being able to correctly identify what is problematic)
my approach is to still use static fields but have a Things.register(bus) method which does the classload; the fact that registration is done in clinit is an implementation detail though
This isn't too bad because the actual side effects - registering the DR - is done in the method
But if construction or registration were done in clinit, that's where you start seeing the paradigm that I think isn't the best approach
Yeah, definitely
The main reason I use clinit is because I hate mutability and there's no sane way to make an immutable field and stuff without it 😜
I mean, if you do it on instance init and pass around your mod instance to whatever needs it that'd be immutable
I just pushed a new commit to https://github.com/SizableShrimp/NeoForge/tree/1.20.x-overhaul-registries-cherrypicked which entirely removes registry serialization to disk.
It was actually rather easy because I coded the system in such a way that it is mostly modular and only requires a few hook points to delete, being unified with other codepaths in a sane way.
Basically: fuck the legacy registry system!!
I removed this feature cuz of 23w32a which officially makes vanilla no longer serialize registry int ids to disk. Mods should follow suit.
Since this overhaul is targeting 1.20.2 or later, it obviously makes sense to just get rid of now.
Basically the only thing left for me personally is fixing up MissingMappingsEvent / RegistryRemapHandler to actually do something.
However, MissingMappingsEvent is kinda awful. I would much rather just delete the system now and readd it later if someone needs it with a properly designed API.
Using IForgeRegistry#addAlias is just such a better system and should be preferred in all cases. I don't really see how people would need more than that, but it's likely someone out there does /shrug
so basically: review time maybe?
I guess another important thing is waiting for Shadow's PR to be merged with DeferredHolder, and rebasing on that, as that will probably fuck up a good bit of my work
are the numeric ids not used in save data at all? Everything is paletted?
well not in 1.20.1
but in 23w32a, yup!
even confirmed by mojang staff
And this has also been verified by me personally
same same
i know there's a difference
but mcp name is entrenched deep in my mind
Potion/PotionType™️
vs moj MobEffect/Potion
I believe missingmappings can be axed entirely
I agree, for now
I wouldn't be surprised if some modders still need it for whatever reason
But I'm almost sure that the scope of it does not need to be as large as it currently is
and the implementation is just fucking awful
i mean it just lets you do the same thing that aliasing does, unless you have some other need to react to the presence of a saved registry entry being lost
but, you aren't saving the registries, so you can't know that anyway
Do aliases allow a renamed entry to be mapped from its old key to the new key on world load?
aliasing allows you to place an entry in the registry such that, for a resource key A, all Registry#get(A) calls will return value B, which can have any name
That seems to cover the use case I described then
Do aliases already exist or are they something that will be added as part of the registry refactors?
I cannot find them
until a less-forever-ago PR by me they have been useless
and even now they are not api-accessible
See ForgeRegistry#addAlias
Are mods not supposed to use them?
lex wanted it to be "properly" exposed and thus wouldn't let me just make it available via IForgeRegistry
I would say its probably stable api
actually what the hell let me just go expose it
I don't think I want to deal with making it available in defreg because bleh
don't want to introduce merge conflicts with the DH PR
I assume after the registry refactor it will be accessible from all places that regular registration is accessible
my rework does this already /shrug
if you remove saving to disk how can you prompt the "missing registry entries, do you want a backup" screen?
hrm
MissingMappings should be reimplemented in a way that makes sense with the new system eventually but isn't necessarily the most important for now
Unless aliases would work for that use
aliases cover everything the mappings event does except notifying you about which entries are missing
but you are the mod whose entries are missing, so you know which entries are missing
I think Fabric is adding back a very stripped down form of saving to disk (just saving a stable list of names) to get around that
indeed
yeah the fabric pr is what reminded me lol
oh also
can we hook into the datafix for effecttype int -> RL and use the data that's saved to disk by forge if exists
otherwise you wreck any world updating
unless we're registering a built-in biome registry for reasons?..
slap a deprecated on it and delete in in 1.20.2
slap a deprecated on all vanilla "redirects" in that class right now
and delete in 1.20.2
it's fully binary breaking and the vanilla API doesn't match the old forge one anyways
also make sure to test simple forge client/server w/ vanilla server/client
also shouldn't this be 257
nvm it's 0 indexed
shouldn't it be 255
shouldn't the whole ForgeRegistries class be deprecated?
ah no, not Forge's own registries
I forgot you guys can do that lol
Yeah, lol
Being able to modify the game code directly through patches is neat for this kinda stuff
that shouldn't be much worse with a mixin tho
depends whether it uses a lambda
nope, it's a map lol
tho aagh, at the same time you need a world context
that's not going to be fun
forge's serverlifecyclehooks thing might help with that?
so I guess I do need to fuck with this more, ugh
all the registries in ForgeRegistries were just a holdover so I didn't have to fix as many compile errors at that moment in time
they can be removed whenever
we can either mark them for removal or delete them
imo same field different type will just cause more confusion
mhm
I did it purely for the writing process
IForgeRegistry means something completely different now
I wonder if the data fix runs before or after we load registry snapshots from disk
we should probably nane it IRegistryExtension
it should be named the same as other extension interfaces imo
the name is pretty pointless for mods
given that Registry extends it
that's all anyone will use
yes, and the new stanard is Extension
it can be put to a vote by maintainers /shrug
renaming it would maybe be less confusing for modders porting
So I might just sway to that
feels a bit pointless renaming the extension interfaces to me idk
probably doesn't affect source code though
Are the I prefixes staying? I really dislike them.
yes
I like them 
psure yes, the new stanard is already in practice
it was put to a vote by the team and the vote said yes
Can't we do a renaming pass on 1.20.2 for all of them? Once we determine which naming scheme to use, obviously
With how my rework is currently designed, IForgeRegistry should almost never be touched by mods and everyone should just pass around a Registry instance
probably best to rename then yeah
therefore it probably makes sense to rename and explicitly inform modders to use Registry in the documentation of the new class
the more pressing matter is the mob effect datafix thing
Assuming the datafix runs after the level.dat is loaded (I think it would have to in order to know if datafix needs to run), THEN it would not be too hard to revive that data and inject it into the running copy of registries
rather than patching the datafixer to hell
And it is also apparent to me that I need to rework RegistrySnapshot to store the list of entry names for every single registry to store to disk.... that's a lot of fucking data
uhm that's not how the datafix works, it's a hard-coded int to string map
lol
technically that map could be replaced each time a world is loaded?
Generally mods don't support version upgrading anyway
plus if the registry is no longer on-disk we can't fix the ID->name mapping
because that ID->name mapping is level.dat dependent
so let me get this straight, are we using some form of suppliers now as a registry output or are we just going vanilla style and registering like Fabric/vanilla?
no, DR gives out holders is the main thing, so it works like vanilla registries
but the actual registration is handled as before using the registry events
The goal is to move to more vanilla-style registering, but not fully vanilla-style
you get a holder which is a super-supplier
makes sense, reading through all of this was kinda confusing
yeah, the forge way of doing registration still exists, it's just more compatible with vanilla now
Yeah I should probably pin this here, it's easier to digest
Primary github issue for registry overhaul: https://github.com/neoforged/NeoForge/issues/37
So is the end goal of the registry overhaul to do away with the forge registries entirely and register things the normal way they're done in vanilla?
i.e.
public interface ModItems {
Item MY_CUSTOM_ITEM = register("my_custom_Item", new Item(new Item.Settings()));
private static <T extends Item> T register(String name, T item) {
return Registry.register(Registries.ITEM, new Identifier("modid", name), item);
}
public static void bootstrap() {} // called somewhere else
}
``` (how I normally do it)
nah, the deferredregister apis will still exist
It would basically be that pattern but defreg, you might be able to use static init like that in some cases
but objects which depend on other objects existing gets tricky when using static init
But will we still be forced to use defreg, or will that end up being a helper?
and some objects do crash if constructed at the wrong time due to mojang (so defreg helps there anyway)
So long as I'm able to have static consts containing my registered objects I think it should be fine.
I agree registration order is a concern, though I've rarely every had issues with it.
I do have some places where a bootstrap() method calls bootstrap method for other things, but in general it all just sits in the initializer, and since in fabric mods are initialised after the rest of the game, so long as I keep my stuff straight in terms of which references the other, it's okay.
As far as vanilla is concerned, I have seen them doing some deferred registration and it largely involves using RegistryKey<?> in place of the public constant values. Particularly anything that's loaded dynamically from datapacks is done that way.
(wouldn't be surprised if we see more registries move in that direction in the future, tbh)
So if forge's defreg stuff made use of RegistryKey as well, I think that would be something I'd accept
no it will not work like fabric, you still need to use the registry event and DR is not going anywhere, not even getting overloads that take the object instead of the supplier ... this has been discussed further up in the conversation already
The forge registries and the deferred register system are two entirely different things
Importantly you will be a lot more free with how DR works because RO is non-final
well yes but also no
if the mod is solely about mob effects or whatever then it won't have its data nuked ahem BEs
something that we need to test is if the datafix runs with server context
because if so we can definitely provide old ids
we won't be officially supporting static registration but there also isn't anything we can do to stop you from unfreezing the registries yourself to do this so /shrug
some numbers on why registry object creation should be parallel
for DR usage (see bottom of the trace) we have 7.5s to construct the objects and 0.4s to register them
note that it's not a single mod being extremely slow, but just a bunch of mods doing a lot of work each... no good solution other than parallel creation
You mean the actual instantiation of the registered things?
yes
Hm so what's the catch? Or is it just a free load time decrease?
same catch as parallel setup events: be careful if you access game state otherwise it's free yes
Well so doing stuff like accessing some other mod object to say copy its properties won't work anymore. Previously it was possible since mod load order is a thing
Guess not much of a big deal. Would parallelization still be done in steps? Say first register all blocks in parallel and then items and so on
probably yes
well we don't really know what the requirements are
are mods actually relying on that?
Copying properties? It's nice in some cases for addons and such but we can live without
I suppose
Performance improvement on load times sounds exciting
in principle we can respect mod dependencies while still doing stuff in parallel
independent mods could still construct their objects in parallel
So like grouping mod dependencies in blocks and running those sync?
more like building a graph out of completable futures
you might run on a different thread than your dependency but you will run after it is complete so it will still be ok
details TBD
Maybe would be better to just enforce the new requirements for the sake of performance
depends what is necessary
I'll probably look into it around the 1.20.2 release (assuming we decide that we start BCs there)
do note that parallel events do follow mod dependencies
no, right?
I have heard that mentioned but I have also heard the opposite
last time I checked parallel events were just conceptually foreachmod(post).joinAll()
but that was a few years ago
i remember putting a lot of work into making it work
so i sure as hell hope it does lol
👌
Should still be possible with registry callbacks once those exist
Or yeah, mod dependency ordering which would work too
oh true that is also nice
is this far enough to just need a rebase once 1.20.2 lands?
it should really probably be ready before 1.20.2 drops
since it is a huge change that we don't want modders to have to switch to mid version
Same situation for the capability rework
Can't we announce the first 1.20.2 builds as "very unstable" or similar? Maybe not even a public release on the maven... To give us some time to land a first set of overhauls
Maybe publish to maven but not public for players to install / use? To allow modders to start porting / updating to 1.20.2 before it publicly releases?
my idea is you doing the rebase on rc1 next week so it can be merged as a batch (with the update and caps) before anything is released
You can't publish mdk and stuff without the installer?
But I understand if it can't be done, just thought I would ask, "don't know the answer if you don't ask"
You can but that's awful
in my personal opinion
this'll have the same outcome as the 1.19 refactor
part of testing a mod is putting it in prod/modpacks. Also players would probably be mad if modders can play but they can't and they see modders preparing builds and shit
I just don't think it's a good idea to stagger releases between userbases
Which is what?
if we were to do anything like this, my opinion is that it would have to be the one where we keep it only on GitHub/discord while we refactor things
Merging on the public branch would be a good idea I guess yeah. but also it will be an awful rebase and it's not feature complete and I don't think I have the time or energy
the overhaul can wait or if someone wants to take it over
the refactors shouldn't come squashed with the port anyways
not squashed but the build doesn't run for every merge so if we merge it in right after the update the first build will be a .1 and include the registry overhaul
Can't we disable publish for a few days when 1.20.2 is released?
A bunch of 1.19/1.19.1 mods got released before the refactor, and then broke until they were updated
this update definitely has some tooling that needs to be done
Like turning off SRG and maybe writing a compat layer like maty said
And making sure NG6 supports NeoForm
And fixing S2S
like all that should be done before release
And then they fixed themselves. It wasn't that big of a deal honestly
The same reasoning can be applied to making breaking changes at any point, so I don't agree
Nah, because people expect it early on in a release and neoforge doesn't make any guarantees of "this won't break" early on
Like, if your stuff has been stable for 3 months and forge decides to break something - that's different
shadows does your 1.20.1 PR make any sense to have in that version?
(probably no)
does it make sense to review it anyway?
(probably yes)
It is fully workable for 1.20.1
Technically it would be nice for something I have in placebo, as currently I'm reflecting into RO to accomplish something that would just work™️ with DH
am I correct in saying the following:
there are 3 types of holders:
- direct holders, used by dynamic registries
- intrusive reference holders, used by the 5 intrusive object types
- stand-alone reference holders, used by the 5 other static registries
the intrusive holder count is probably bigger in 1.20.2
Oh god did they start using those for more stuff?
yes
"intrusive reference holders" are just ref holders that are a field on the registered object
(and we have to make them thread safe if we ever want to be able to construct blocks in parallel)
So there are only two types, direct and ref
and direct ones are worthless because they return false to all the key checks or smth
wtf
I still have no idea what direct is useful for
like, it can only be used to pass into something which ONLY cares about value()
Yeah, idk
holders seem rushed
DH is what I would think holders should be
which is a ResourceKey that is bound if present
yeah it's quite confusing
Direct is used in datapack registries for places where you have something holding something that isn't actually registered
Which is possible, because of wacky codecs and inline definitions
Seems contrary to the point of the holder
And registry-or-direct codecs
Which give you a holder, that's either a reference to the registry or a direct
And can encode the same way. Yes it's weird. I don't try to think about it too much
Technically you could probably inline features in a biome modifier or something silly like that if you tried
datapack regs are fairly cursed
RegistryObject<MyItem> vs DeferredHolder<MyItem, Item>
like if you deserialize an object with dependencies and discard it, it has side effects that will require the dependencies be resolved even if the object is not retained
Did I keep the two types?
it's a TODO
Eh. I'm not too worried. If they really want it's possible to use one generic too - just assign to a Holder<Item>
I thought I just went with cast <X>
I'd definitely keep two types
the cast is gross yeah
right two types is binbreaking
If you're going to erase one, it should be the more specific one in my opinion, leaving the generic Item
Good news is that that no longer matters
(and this is currently possible with a two type class by assigning to the normal old Holder type)
having the exact type is quite important
it doesnt matter in most cases
Usually the specifics of an item type are just overrides
or other registry type
the generic type of BlockEntityType is quite useful for example
would it be sensible to cast the Holder<T> to Holder<R> where R super T?
although, having a Holder<MyItem> doesn't make sense anyway
what types are holders useful for again?
Yes, and in that case you could use ::get at the end to capture a supplier
If you need the specific type only -> grab a supplier
If you need the generic type only -> grab a holder
If you need both -> grab the normal old deferred holder
well there should only be one recommended one way to say declare items or blocks or BETs
The "one recommended way" that works for every use case is the third - grab both
The others are for the people who don't want two generics because they think it's too much to type or whatever
nooo wtf why? like what??
Yeah that's weird
the existing intrusives are all deprecated
and from discussions with Boq I thought the intention was to remove them entirely
so if we're keeping two parameters, can we please have the most-derived-type on the right? I think DeferredHolder<RegistryType, RegisteredType> personally is easier to reason about than DeferredHolder<RegisteredType, RegistryType>
though this might be a reason to keep RegistryObject, so we can have RegistryObject<RegistryType, RegisteredType> implement DeferredHolder<RegistryType> to make the holder API cleaner, while still exposing the registered type for people who need it
i'm sorely tempted to say that we should have e.g. BlockDeferredHolder<B extends Block> extends DeferredHolder<Block, B> implements ItemLike
that's not really possible without providing an overloaded BlockDeferredRegister and etc. in every part of the API that would return DeferredHolder
which is pretty yucky
Eh, we could maintain something like this for common / simple types https://github.com/Shadows-of-Fire/Placebo/blob/1.20/src/main/java/dev/shadowsoffire/placebo/registry/DeferredHelper.java
This would give us such capability
that just looks like overload spaghetti to me
Why not use a DR per registry type though