#Transfer rework (IItemHandler, IFluidHandler, IEnergyStorage)
1 messages Β· Page 8 of 1
Surprising to you? Probably. Undefined? Definitely not
It's a bit surprising but ultimately the most sane alternative
If it wasn't defined, we probably wouldn't even be talking about it
Since that's excactly what the old IItemHandler wrapper did
didn't talk about it, but ultimately do the same thing.
For years, I might add.
Just because something was bad in the past, does not mean we don't need to improve it.
Your improvement is crashing in peoples faces
Or what do you suggest the constructor does when the flag you propose is true?
I definitely don't want a crash if I am trying to use this helper on a handler I just received from a capability query
I am not really asking for the crash, I am fine with the crash we do it all over the place in the Utils when stuff goes outside of the defined bounds, but for a way to know that we have a dynamically sized resource handler.
that is a different feature request, and is currently out of scope as discussed earlier
The crashes in utils are about you doing something against the spec
Except that we clearly defined that later slices are not allowed to alter already accepted slices
We did? where?
not at all? just not make arbitrary changes to previous slices
but if a reasonable patch needs to be made (e.g. like I already did to allow for nullable snapshots), it can ideally be sent as a separate mini-slice
that makes no sense given this code doesn't become immutable once it is merged into main
given it will hit in a BC window
you mean merged in feature/transfer-rework?
we can mutate it both in feature/transfer-rework before it hits main, and we can also mutate it again in main
yeah ofc
and we'll likely be adding helpers for a while
Fine, if we can at least have a discussion this before this gets merged into "main", I am willing to lift the reject of this slice.
But I don't want to have this in main without having someway for people that pull an arbitrary resource handler from a capability to check for this.
I don't need any of the other characteristics really, except this one, as it alters the behaviour of my code and how I interact with them.
anyhow, back to the topic of undefined.... I was trying to make this point:
when you use utils out of spec, you get an exception for your own code
but if you combine external handlers, what are you going to do, realistically?
if you combine your own handlers, you already know anyway
You have never been able to use that information with IItemHandler so I don't see why we'd put this a requirement on getting the transfer rework in. But yes I want the reject to be lifted since it will otherwise block further progress on the rework.
eh, we can discuss it, i think there are reasons it could only ever be a hint too
given there are proxying handlers that can change while they exist
Is that a yes, to an honest discussion on this?
or rather, their target can change
It only needs to be a hint for me.
Nothing more
Something that indicates to me to be more carefull
sure but then you cant use it to reject construction of a combined wrapper, realistically
if it's a hint you'll not be able to trust it, and should always be more careful
you can use it in your own code to not construct the wrapper to begin with
as a hint
No that is fine by me
but: i think you are underestimating the robustness of how the combined wrapper is defined, IMHO
That is what I was planning on yeah
or rather, in combination with the definition of indexing behavior for dynamically sized wrappers handlers
I mean, even if I wanted to I couldn't stop you from discussing things
I can come up with nearly 10 different ways where it is behavior is not wanted in my opinion when dynamically sized inventories are involved. But it is robust, under the consideration that the "lenient" approach is taken, in that it does not crash
Well it is robust in the sense that:
it will not corrupt the state
it will not dupe or void items
and it'll not crash
it will not offer a full view of added slots
and show non-interactive read-only slots if it shrinks
Sure but for me it also means: Work as intended. Which is simply does not in my opinion.
but i find that the acceptable middle of the road approach
But that is simply my opinion
keep in mind that having a fully dynamically sized wrapper is prohibitively expensive to do
you need to essentially rebuild your index map on every operation
Do you?, it would be a call to size() on each of the handlers in the for loop on line 52. Which yes is more expensive then the array lookup right now. But not prohibitively in my opinion.
Because it would turn into:
protected int getHandlerIndex(int index) {
if (index < 0)
throw new IndexOutOfBoundsException("Index " + index + " is out-of-bounds for combined handler with size " + sizeCache);
for (int handlerIndex = 0; handlerIndex < baseIndex.length - 1; handlerIndex++) {
var size = handlers[i].size()
if (index < size)
return handlerIndex;
index -= size;
}
//Index bigger then combined inventory
throw new IndexOutOfBoundsException("Index " + index + " is out-of-bounds for combined handler with size " + sizeCache);
}
probably not if you care about performance
you'd kind of want to do a "stable hashmap"
Which in my opinion is not that much more expensive
actually you'd literally want a hashmap
The only extra cost is the call to size()
in the sense that you can leave out slots that become empty to avoid "moving" existing resources in later slots, and then compact if the "load factor" becomes too low π
Which is mostly constant as either a return of a constant in memory or a lookup in a variable in my opinion
I doubt you would even need that tech.
For the amount of combined handlers we are talkin about the solution above will likely perform in the same realm as the array lookup
Sure it is probably a bit slower
But not prohibitively
I mean if you have a truly dynamic handler like an AE2 disk but still want to expose int indices
Sure, I see no reason why you could not do that
Even with the solution above
Where the combined handler querries your size() to determine the relevant handler index
Instead of looking it up in the array
well, different story IMO
From the combined handlers perspective?
How?
Your AE2 Disk would be its own implementation with its own size, no?
I assume so at least
Or are we talking about something else?
I was describing the internals of the disk with the map
the bigger issue with allowing resizes is that we need to be lenient in our handling, which means providing an empty resource to no-op getResource
That would IMHO be implementation dependent
But yeah
If you do a sparse to condensed hashmap approach
Then yeah
You can implement it as such
And condense and change size later on
I mean if we made the combined RH itself re-query the sizes, it would need to be given an empty resource in the ctor as well, which is quite annoying
I'd find it very surprising that the indices would no longer be contiguous though
In what way?
The combined RH would just store an array of handlers
Done
There is no reason why it would need an empty resource anywhere
you also need to adhere to the leniency of getResource/getAmount
which means if your overall handler srhinks
to no-op getResource in case you go out of bounds you need to know an empty resource
you need to return empty for the indices that are no longer valid
but where do you get the empty from..
It literally will lead to abundant crashes in prod if you are not lenient as a dynamically sized handler
Then that should be considered a possible side effect of commiting a transaction
that just makes the helper less useful; I'd have to copy-paste the current version in a bunch of mods
the point isn't that we can't, it's that it's inconvenient to use for the sake of the absolute niche use case π
You need someway to allow for people to ask at least up to the max of all sizes ever
I fail to see a case where the user of this class, does not know what the empty variant is of the resource....
For one we only support one type of resource for all the handlers
the point is that it's annoying to pass an extra parameter, when there is an alternative that doesn't require it
And I don't see a situation where a modder combines a bunch of IResourceHandler<?>
I can see that, but then we should document that properly.
The single <strong> on line 17 does not reflect the required impact on dynamically sized handlers this has. It should properly describe what happens in those cases, especially when inserting (aka that the dynamically sized inventory is then not fully accessible anymore, and the combined inventory handler works more like a RangedResourceHandler)
I will sleep on this overnight. But right now I feel like we should add some documentation on this, to clarify to users what the concequences are.
Let me see
Current text is:
* The range of indices handled by each wrapped handler is assigned when the combined handler is created.
* <strong>As a result, later changes to a wrapped handler's size will not be reflected.</strong>
That could probably be reworded to:
<strong>The size of the wrapper will not react to changes of wrapped handlers that have a dynamic size after its creation.</strong>
This means indices added to such handlers will not be visible, and indices that are removed will appear empty and read-only.
To adjust to size changes of the wrapped handlers, you need to recreate the wrapper.
I would actually be very happy with that change
I'll run it through DeepL to maybe clean it up some more
Be my guest yeah
I really want it to be clear that this is not a supported scenario, what the concequences are when somebody does it anyway and how to deal with it. Which that documentation line above takes care of
Revised:
<strong>The size of the wrapper will not change in response to changes in the size of wrapped handlers.</strong>
This means that indices added to such handlers will not be visible and indices removed will appear empty and read-only.
To adjust to size changes of the wrapped handlers, the wrapper must be recreated.
Good enough for me
Can you at the same to RangedResourceHandler
Which has the same behaviour in my opinion
well, in that case you are already giving an explicit start and end so it's logical that the size won't change, no?
Sure but given that we have no idea how a dynamic inventory grows or changes when stuff gets added or removed, the ranged area can not behave clearly when a dynamic inventory is used.
what is not clear about the range continuing to check indices between start and end?
I don't really see what else you'd expect to happen
You maybe right
I had a case earlier
Where I thought it did not work
I will think about it again over night
But it is likely not needed there
I still feel meh about the stored amount being able to be greater than the capacity. Making it clear it might be an overestimate is fine for sure though
I have a handler that will have to deal with this; what tech is proposing is sensible enough imo within a transaction (this was discussed earlier for my use case at least).
I know being lenient is enough to solve it, to a certain degree
I think it is just not really a great solution, cause the concept of being lenient is not really well defined
As long as it is within a transaction it is probably fine-ish
Within a transaction is absolutely fine
And you should have no guarantees outside of a transaction anyways
Also, requiring such containers to be lenient within a transaction is necessary to avoid footguns like ```java
var size = thing.size()
for (int i = 0; i < size; i++) {
// try doing something in slot i
}
should we add it to: https://github.com/neoforged/NeoForge/issues/2570 then?
It's just a representation of reality though
Just from experience, you'll encounter this scenario in the wild a lot
This was all discussed to death ages ago; requiring some degree of leniency (aka, requiring dynamic containers to no-op on slots above the current cap but higher than a past cap) is about the only functional solution given what everyone needs from this (and I say this as someone with a dynamically sized container who was making a bunch of noise about this)
Don't, like, thermal expansion machines explicitly work this way with how secondary outputs work or some such?
Like, they'll keep outputting items if the primary slot has space and if the secondary slot is full, they just... keep filling it past its cap or whatever? I can't quite remember
well currently, it only is lenient in one direction to be fair. If you happen to merge them when one has a size 10, but later it only has 5 indices... then it will still die, it only prevents against it having issues when increasing the number of indices
Wouldn't it only die if the underlying handler dies?
meh, depends on the "how lenient" the dynamic handler is
Which leads back to what luke alluded to. A dynamically sized handler that isn't somewhat lenient w.r.t. access to now invalid indices will run into crashes in prod anyway
true
Hmm? That's the leniency in question, that'll be fine -- the underlying provider, when it shrinks to 5 indices, needs to no-op on indices 6-10
I think it's actually good we specify what a dynamically sized handler should do to avoid those crashes
Basically -- the highest slot a handler can handle should not decrease during a transaction, that's the leniency in question
In the past, people had to figure that out the hard way (or just crash modpacks a lot and dont care, lol)
Do I remember correctly that there is a way for a handler to get notified when the last transaction closes to addres side effects, yeah?
(Obviously it might become a no-op or something, that's the sensible way of handling this dynamically)
I believe so, yeah
yes, I want to kill the scenario 
and it isn't really "a lot"
just certain edge cases
Okey then it should be trivial to implement this leniently
As you can trivially recompute the public size when the last transaction shrinks
Yes as I have been saying this level of leniency is fine to expect from dynamically sized providers, and it avoids footguns for people who don't consider that case. Such providers growing may still act weird in some cases, but shouldn't be too bad in general.
Wait wait -- Is the leniency that the provider has to handle such slots outside its max size within the transaction, or that it has to report it's max size as being higher than it is within the transaction?
Oh yeah it's just the former, isn't it
Well it'll happen anytime you have a container with "upgrades", the upgrades are removed, and the author doesn't want to immediately drop the content on the ground, for example. right?
Yeah @hidden geode you don't even need that, this is trivial to support -- you just update the size whenever you normally would, within a transaction or whatever, and just no-op on slots higher than your size in general. That's likely what I'll do at least
imo they shouldn't allow removing the upgrade then until the contents have been lowered
or it is definitely abusable by players to get the upgrade's effects for free
actually, MI also has a case where a slot might store an amount larger than its capacity
yes, I am writing a response to one of your comments on github that I think will semi clarify my stance
as I understand the case of amount larger than capacity
this happens if the player reduced the capacity of a slot, but an ongoing recipe still needed to put its output somewhere, so it just ignores the player-limited capacity
I mean it'll be an edge-case, I just think people have to account for insert not allowing them to insert in any case
this is just one of those
Hm, or rather, this is stored amount>capacity, I don't even know what effect that really has on callers
that's not what I am arguing about
especially now
if (amount >= capacity) then insert -> return false
at least if capacity is not say max long
that can be special cased
as I do see the reasons why the amount might be higher than the capacity. I might not like it, but I understand there are use cases or situations
I really don't like the idea that getCapacity is 100% useless as a method
because at that point, like why have it
simple example: the comparator output
or checking if a handler looks full (e.g. for a BC gate)
i.e. prioritize based on free capacity remaining, which a hint is fine for
yes, which is why I want the stronger heuristic that people can assume that it won't accept more if it is at or above the capacity of the index
so that the heuristic is much more useful and consistently useful
I think even for your pipes it wouldn't be less efficient to just call insert directly, since the insert will usually redo the same capacity check internally
for the new system, potentially. And I am definitely going to rewrite it to make use of transactions and stuff. But my argument still stands, why not make the heuristic much more useful
what is the problem? Just overshoot or return max long if you really don't want to calculate it or if it is super expensive, how is that different for the implementer than potentially undershooting it
my general opinion is that heuristics to avoid calling insert are suspicious, and we shouldn't encourage people to use those
The use case for getCapacity isn't "can I call insert"; the way of checking if you can insert is by trying to insert and seeing if it works. The use case of getCapacity is this sort of stuff
Half the point of transactions is that you don't have the whole "check if I can do this; try to do this; do this" cycle; instead you just do the interaction you want to and see from the result whether it was a sensible thing to do. And if you just want to check if you can -- well, transactions let you simulate that
I am not saying we should encourage people to use it vs just calling insert. I am saying why it would be useful to have a more strictly specified heuristic bound
It's a bit weird to link amount >= capacity to insert precisely because you should never do such a check before calling insert, imo
And it's a bit weird to have heuristics for when to call insert because the result of insert is that heuristic
How does a handler report that you should/shouldn't insert something? By letting you insert/not insert something
hehe, I am doing something a bit different in MD that doesn't involve checking the capacity, but it could fail in different ways so it's not necessarily better: https://github.com/Technici4n/Modern-Dynamics/blob/e8e40f02966406acca712fbd36ee847f70bd528e/src/main/java/dev/technici4n/moderndynamics/network/item/SimulatedInsertionTarget.java#L138
I mean yes, this likely is less of a problem with the indexless inserts, as then you really aren't using the capacity or not. But if the capacity doesn't represent anything except that "hey this might be the capacity, but I don't know so I just pulled a random number out of a hat that may be lower or higher than the actual capacity", then it doesn't really let you do any sort of logic with said result. Heuristic or otherwise.
Like with what you are saying and arguing for, I could have a handler that just always returns 10 for the capacity, as that is what it will be in most cases, and who cares about representing the case where it may be 100, I don't feel like it so I will just say it is 10 as a single return statement is easier for me. Then that gives you no information towards being able to calculate said comparator signal, because the number is meaningless. You need to call insert regardless to see how much it could accept to see what the "full" state is and then compare current against that
The capacity is not something you should be using on an insert, indexed or otherwise
that's not my point
that's just an example based on when I currently do (where transactions likely would mostly if not entirely solve it)
The capacity is meant for stuff to report "how full is this compared to how full it could be". Like a comparator or, or some tool for telling how full your storage is
It's basically just a metric of "this is what 100% full should be"
That doesn't mean it can't be more full than that; it just means that generally you wouldn't expect it to be
yet you guys are arguing that it might be more or less than that
if this causes the redstone comparator output to be wrong then users will complain and the modder will fix it; just because it's a hint doesn't mean you should return a random value every time
yes, but not in the majority of cases
it is fine to use it when you ask yourself what the capacity of a handler is; but there is no relationship to whether you can insert or not
I'm not. That's all I'm arguing it is: a reference value. What I am arguing is that it being such a reference value doesn't mean slots can't be "more full than they should be", as it were.
you still have yet to give me an example against it, as to why you would want to return a lower capacity than can fit
yes I agree that you probably shouldn't especially with the new system be checking if it will be able to insert based on the amount and the capacity
Tech has provided several examples, but in general: because the capacity you can fit shrank, but you've got some items you have to put somewhere so they don't just get deleted
which at no point says you can keep adding more after the fact
or that it would support increasing the amount further
The capacity tells you nothing about whether or not you can insert items, right
we can add the contract that amount >= capacity means that insert will return false, but what will this buy you, except doing a pre-check for insert which you should not?
if we add this to the javadoc, some users might be tempted to add an amount < capacity check before the insert which they should not
we can recommend against doing it and that implementers of insert should do the check themselves to fail fast
Might be that it's above the capacity but you can't insert stuff. Might be you can. Why should it report anything related to that, when nobody should be checking it to see if you can insert stuff?
Basically -- it's an extra restriction on this method that imo isn't worth adding, when nobody should be relying on such a guarantee to begin with.
the point is in the past there have been cases where implementations definitely don't do so and are performance bottlenecks, and if that is an issue then I want to be able to have a heuristic that is valid by contract rather than being valid in general like we have now
My point is basically -- if you make "amount >= capacity means insert will fail" part of the API of getCapacity, it becomes a guarantee people can rely on. But it's one nobody should be relying on, which means you need a justification for including it in the API of that method!
In generaly I think the requirements on a method like this should be left broad unless there's a reason to add the restriction -- and in this case I've yet to see a reason someone would want that guarantee
back to tech's comparator example then. amount >= capacity means that it is as full as possible, if it still accepts things then it wasn't really full and shouldn't be indicitive of such
We can't predict every use case, so we don't know if someone has a sensible case where breaking that guarantee would make sense. But we can know that there is no reason to rely on that guarantee in general
oh yes I kinda lost the thread
I think an implNote that the capacity should not be less than the amount that can theoretically fit is fine
but keep in mind that insert can arbitrarily fail even if the capacity indicates there is capacity
just the inverse can be part of the implnote I guess
yes I am aware of this
this is all I have been asking for
In that if the amount and capacity indicate that an index is full, an insert into that index should fail.
with the exception that you can have max long if it literally has no way of knowing
I wouldn't link the capacity to whether insertions will succeed or not
Right but my question remains: why should the API offer that as a guarantee, when it's one that nobody ought to be relying on?
True, see, already forgot about that π
hence an impl note
But I fail to see why a mod would implement it any other way anyway?
OK that makes it even uglier than not specifying anything tbh
I fail to see why we need to add a contract that nobody should ever rely on
that was based on gimp's suggestion earlier
My point about it still remains. You should not ever check capacity if you're trying to insert. Why restrict what mods can do to provide a guarantee nobody should be using?
I don't really care if that exception is in there or not
well no wait, dont say that when you agree with me π
yes I agree people likely won't implement it any other way, which is why I want it to be more of part of the contract, so that logically people can better understand what it means. (And is also why I don't see why you all seem to be so against having it be part of the contract if you don't know why any impl would actually be different). As truth be told from all the arguments I am hearing is that one should literally never use getCapacity and just attempt to insert max int and see how much it can accept to get a better sense of what the actual capacity is. Which given some things have fake backing slots is not ideal as then it is having to insert until it gives out.
Like that's the whole reason I had proposed that getCapacity takes a resource like my current impls in mek do. It is so that it can give you a better more narrow capacity if it knows it, and otherwise it can fallback to the broader general capacity of what it might be able to accept.
Having something in the contract, means that people might, reasonably, rely on that. This is a thing people should not ever be relying on. Hence, why I'm against having it in the contract
getCapacity is independent of the current state of the handler unlike insert; this is why it exists, similarly to isValid
sure
if you care about the current state of the handler, or mutating it -> use insert
if you don't -> use getCapacity
these are super different contexts IMO; for a mod like mek there is a good chance you will never use the capacity of a handler you got from a capability
I don't think MI or even AE2 ever checks the capacity of an arbitrary handler, for example
Create probably does since it has a state observation block I think? idk how much functionality it has for the capacity though
what about a slot with a zero capacity. Yes there is the argument the modder shouldn't be exposing it in general, but that is similar to the type of thing of isValid and cases where people may want to optimize out further checks. Though speaking of which do we currently have it in the contract that the capacity shouldn't be changing like is valid shouldn't be? Because if that is the only real case for if you don't care about the current state... shouldn't it make sure that it isn't actually caring?
I mean, I'm going to have slots with 0 capacity exposed. The extra slots in a shrunk dynamic handler are that, for instance
But once again, what check does that let you avoid?
None
You could just do the insert, see that it fails, and it would be just as cheap
we have
* Returns the capacity of the handler at the given index and for the given resource,
* irrespective of the current amount or resource currently at that index, as a {@code long}.
the formulation is quite similar to isValid
it is true though that we currently specify If this method returns {@code false}, the insertion of the resource is never valid. for isValid
I lifted this from IItemHandler but I am not even sure it is useful
you don't know for how long this guarantee holds - maybe it changes the moment you interact with the handler for some other reason
isValid has a "never" there so I would assume it can't ever change
Or at least not till a capability invalidation
which is not even true tbh
If getCapacity acted that way that would be... obnoxious
I don't want to tie any of these methods to cap invalidation
I meant more that anything is allowed to change at that point
imagine I change the filter of a slot for some reason - am I allowed to change the result of isValid or not?
My general answer is that something like isValid should provide a guarantee within a single transaction
forget about guaranteeing stuff inside a transaction IMO
Or rather, it shouldn't provide any guarantee outside that
why not? there is no point in wrapping a billion things inside one big transaction
the whole point is it helps things for future checks against a cached handler. GRANTED with the indexless methods I don't know how "useful" that concept becomes (given I don't think we have an indexless isValid). But that's my entire point with the getCapacity. That while the use may not really turn up anymore... we might as well have the methods make more logical sense in terms of their contracts as it makes it easier to reason about how things function
isValid was added so that railcraft wouldn't wait forever trying to fill minecarts that won't accept the item
Because you need a time when isValid or the like can be changed -- say, because you've changed the filter for a slot or whatever
that is literally it
Whatever guarantees you ask a handler to provide, you need to leave times when handlers are allowed to change those underlying things. Outside of a transaction was the one that popped to mind for me
it's a tempting answer but it seems very artificial to me
especially since these methods are otherwise not related to transactions at all, and don't mention the concept of transactions in the javadocs
π€·ββοΈ I mean, as long as you make it clear to me when I should be allowed to change this stuff
Cause I have an inventory with varying size, and because of that varying slot capacity, that also is going to have changes quite often in what stuff is allowed in what slots.
the problem with invalidations is that we don't want to be performing them too often either
(a secondary problem is that invalidations only exist for block capabilities)
Yeah, I agree that cap invalidation is not a good time for this. That's why I would say that if you want to provide these guarantees, then either:
- they only tell you something immediately; aka, any interaction with the handler could change this
or - they tell you something for the duration of a transaction
Because anything longer than that is inately problematic (you're not going to be able to change them at all, really)
technically a furnace would have to invalidate on a resource reload if we tied isValid to cap invalidations 
Yeah, I agree that invalidation is not a good time. But I would say let's be explicit on any of these sort of method with when they're allowed to change, if it's not just "whenever the handler wants"
I don't see an issue with the capacity changing at arbitrary times. Most reasonable use cases for the capacity are not burdened by this?
isValid is a bit trickier
believe it or not Fabric does not have an equivalent for it
most base implementation will have such a method, but do we even need it in the core interface? debatable
if we remove it, people migrating from IItemHandler might not be happy though π
just had a look at Create for example and there is not a single isItemValid use that would require it being in the core interface - but it is generally nice to access the override in the base implementations
OK, I pushed a change from the insertion of the resource is never valid to the insertion of the resource is not currently valid. I think that makes the most sense. This method won't be super useful for an arbitrary handler, but I think it's ok to still leave it in
β¦ how is not currently valid any different at all to attempting to insert and it returning zero
I would say
- the fact that it does not depend on the handler's state; and
- potentially the two might be decoupled, e.g. technically something might be valid but still not insertable for reasonsβ’
the only change here was never -> not currently since I don't want to tie this to cap invalidation (we could but then we have to make furnaces invalidate when tags are updated, could get quite messy imo; additionally this would not provide clear semantics for entities or stacks which don't have a concept of invalidation) nor transactions (potentially too short to be useful in that case)
and technically the resource might be invalid but still contained in the handler - in that case you probably can't insert more of it, but it is still there and can be extracted
and with this we are really done with this slice as far as I am concerned
@mental raven we shouldn't get the sizes on demand in CombinedRH because it will force us to pass an empty resource in the ctor which is annoying. (We'd have to noop getResource for OOB accesses which requires an empty resource)
Hmm, I'm not sure how useful it'd be without it being dynamic (endpoints of a tesseract, or drawer controller, etc)
We can't throw on OOB?
Well if it happens that one of the sub handlers is dynamic, we become dynamic and thus need to be lenient with OOB slot access
Dynamic handlers shouldn't as people like to cache the size when iterating
Hmm, I'm not sure the best path forward here then
I am quite sure that we should just keep what's in tve PR
If we keep it, we need a way for dynamic handlers to be excluded from it
Just read the code of CombinedInvWrapper
Oh, this is literally just CombinedInvWrapper for resources
thats fine then, send it
The behavior is reasonable for combined handlers as well, for example if you want to temporarily concatenate multiple handlers it's fine that you won't see updates to the size of the handlers
Yes, exactly. And it already generated hours of discussions even though we already have it and nobody complained for years π
Yes, but I more mean, what is the use case of knowing something is currently valid if it canβt be inserted? Like sure I can see the use for a protected method on our provided impls so people can more easily override it. But from an API perspective? If it doesnβt let you short circuit future checks (by being always), I donβt see what benefit having the method has
(I am arguing either we shouldnβt change it and just have people return true if it might be in the future valid for the handler, or potentially just remove the method from the API)
I donβt actually make use of it for caching, I think @silent root might? So I donβt fully have a stake in us keeping it, but the current change to remove the βeverβ restriction makes the method seem totally pointless
I don't think I changed it compared to its current effective contract
For 90% of handlers that might return false, the accepted items are dependent on some state that might change (player reconfiguration, resource reload, config option change)
To make the method useful for caching you need to define a time bound for isValid, which either a) makes it apply to almost no handler, b) ties it to cap invalidations which is not great, or c) ties it to transactions which are likely too short to provide meaningful caching benefits
So my personal opinion @cunning mulch is to just keep the current semantics and move on with the rest of the rework
then list those types of things as examples to valid times it can change, but even so while I understand the implementation cases where you might want to... I don't understand what use cases there would be for calling the method after the change to "not currently valid"
I don't have one except for convenient access to the impl-specific implementation of the method, which you can't otherwise get in a general way
My main reason to keep it would be for people migrating from the old API to the new one
convenient access sure, but what I am saying is I don't think there is a need to access it with the change vs just not providing it given it is more likely to confuse consumers at that point
I mean
given the change from never valid to not currently valid
consumers mainly would be using it for caching if something is worth sending
so we want that to be a compile break so they don't accidentally prevent things
and implementers could just delete it (or if we have it in our base handler impls as a protected method, just keep overriding it in those cases)
You can't do that in the current system either tbh
Because it literally does not work for furnaces
Unless someone started invalidating the caps of all furnaces on resource reloads, but I am not aware of that
tbh sounds like a bug for furnaces more so. But, I say just move it to a protected method in our templates so people can override easier, but not have it be part of the API. Unless someone can give me a use case of why you would want to be calling the method (especially if it is just currently valid and not ever valid)
Supposedly for some railcraft use case the method makes sense
But personally I am not buying it
I am concerned about not providing a simple migration path from IItemHandler though
Callers
and what I am saying is that I am fairly sure that 90%+ of callers likely use it for caching if things are going to be valid, at which point the change being made to it being "currently valid" and not "ever valid" would introduce bugs that wouldn't be obvious in porting
The bug was already there though
I would rather have it so they realize that rather than just have a method to call that "seems right" but doesn't work for their use case
for furnaces sure, but how many times do people actually do a /reload in modded mc
it isn't necessarily one that is noticable to players
I think you're vastly overstating the extent of the problem. If modders made a bad assumption they'll eventually get a bug report and fix their stuff
Sure, that's relatively rare and arguably not relevant for most players. But changing a filter in the UI for example would also trigger this bug
if the mod that has a filter in their UI doesn't implement the item handler spec properly
Imagine, "I changed the MI Interface filter from A to B and now pipes still won't insert A in it"
Almost nobody implements the supposed spec correctly
I just want an example of what case the method has a use if it isn't for the purpose of caching stuff
like why not just call insert
what good is it to know that it could accept a melon if there are only apples in it now
and by the time there aren't it may no longer accept melons
I don't know. Arguably a pipe might skip scheduling melons to arrive there later. But that's a bit of a stretch
which is the caching that I am talking about
But it seems to me that you're making the same argument as for the capacity getter, which is that "if it's not useful when inserting then it's not useful", which I think is a bit reductive
for isValid, it is more strongly tied to inserting given the docs literally say:
- If this method returns {@code false}, the insertion of the resource is not currently valid.
Personally I dislike the idea that it has anything to do with insertion, and I argued with soaryn about that, that I think it should be whether it is valid in the index, regardless of if you can insert it or extract it
providing for if it is valid in general and fully disconnecting it from insertion (other than maybe being used in our templates as an implementation detail of how they implement insert to make overriding be easier) I am fine with having it, I still don't know if it has a use, but with it being so strongly tied to insertion, as it is currently it is the same antipattern you guys claim exist for what I was wanting with getCapacity
Ok I just found a stretch use case: item pipes with very lenient oversending enabled. You will always send the items towards the destination, unless isValid is false, and only try to insert when the items actually arrive.
personally I still think it has more use for "ever valid" and then just return true, if there is user based filtering that it can't know ahead of time. But that's another matter and one I don't feel as strongly on (in terms of trying to enforce/get changed if other people don't think that is worth it)
I'll make the wording change in 10 mins or so
I still don't like the fact that it seems you guys are against getAmount() >= getCapacity() not meaning that the slot can be considered full though
like sure I understand my wording with insertion was confusion
but it was more how I was trying to describe the main "side effect" of it being full
IMO: it can be considered full (for example by ResourceHandlerUtil.isFull), just not making the connection to insert
I don't care if the connection is stated explicitly, it is an obvious connection that still covers 99% of cases if not 100%. I just don't think the current wording is explicit about the fact of being able to consider it full
also better example that I just remembered I do use in places (at least internally)
needed = getCapacity() - getAmount()
So only try to transfer needed, as then you aren't say having to try to extract from yourself as much as possible, you can just extract up to that to then insert what you could extract into the other destination. Granted idk how much that will be something used without the simulation step of things, but still
IMO that's fine for your own internal handlers, I would just be careful when doing that for an "unknown" one
what does "consider it full" even mean? that you can check for fullness and ignore the inventory entirely, e.g. as a short-circuit in StorageUtil#move?
yes, basically if you can ignore it. Which is why I previously was describing it as being "tied" to insert, given if you are trying to extract, usually it being full means it may be more likely you can actually get something from it
in practice it's probably ok to make the assumption and then run with it
I pushed the change for isValid
I think we are starting to get diminishing returns at this stage, and I am eager to instead move on to the next slices
I think it is fine for mods to make some extra assumptions than what is strictly guaranteed in the docs, as long as they tell us if/when the assumption is broken -> then we can re-evaluate the spec
meh, with gimp's suggested changes to get capacity I suppose it is close enough, I still would prefer if it mentioned that it can be considered full if the amount is >= than the capacity (without necessarily saying what being full means). Especially given we already have ResourceHandlerUtil#isFull so it isn't like it is a new concept, it is just mentioning that same concept in the handler docs itself instead of being hidden in a util method
you willing to add some sort of reference of that sort?
something like this?
* This function serves as a hint on the maximum {@linkplain #getAmountAsLong(int) amount} the resource handler might contain,
* for example the handler can be considered full if {@code amount >= capacity}.
* Note that the returned capacity may overestimate the actual allowed amount, and it might be smaller than the current amount.
* The only way to know if a handler will accept a resource, is to try to {@link #insert insert} it.
exactly
pushed
for the int variant it is slightly less true for purposes of if it is max int, but I don't think it matters enough and would make it potentially too wordy to try and explain
yeah I also had that thought
but it seems a bit too technical, and it's fairly intuitive that if the amount is >= max int you might report fullness too early
amazing, thanks for the approval!
@hidden geode are you around to give your approval as well?
so, the next two slices are the templates and the vanilla wrappers
currently at 800 and 1800 lines respectively
nonetheless, I am tempted to merge them back into a single slice as this might enable simplifications (e.g. using some of the templates' logic for the vanilla wrappers)
I don't really have any preference
Will need to do IItemContext first really
I don't think so? the slice as it is should not include the itemstack-based vanilla wrappers
Bucket?
Ah from that list then no, but we probably should go ahead and do IItemContext sooner than later before doing the "implementations" as that is still back end structuring
the contexts are dependent on the player inventory wrapper
Sure, if including the implementations
The templates/examples though I'd recommend holding off on doing those until the "end" as those will be incredibly subjective of what they should and should not provide. I am more or less indifferent to the example implementations (just as long as we have a generic handler) as I have my own solutions I can use.
@hidden geode poke poke poke https://github.com/neoforged/NeoForge/pull/2571
I wanna see the next slice π
Will do it later today
there, you can "see" it
still a mess though π
i like how latest commit is Make it compile again and CI fails to build 
-# i mean its cause of missing license headers but still thats a little funny
(they technically aren't even missing, there is just a TODO above them)
There were no tests associated with the vanilla wrappers? Or did you just not copy those
They're all in the last slice, I need to bring some of them over
I am getting better at PR-to-PR π
just point me to the actual last slice and I can help
Found it
Uh have we even registered the capabilities yet? Which slice does that? Otherwise doing tests doesnt work
are you gonna PR-to-PR something? what part exactly?
getting the hopper wrapper right is very tricky
It was working fine in the original. As well as test validated
I don't think so. It should check if the full hopper was empty, not a specific slot, and at no other time than right before the insertion
It was working. I did say it could be improved, but that was slated for later. It very much validated
Checking if the hopper is empty is doing a per index search anyway
I found these two bugs in the slice I took over, so idk. I'll look at the test though
Well do remember you also nuked or mutated the utils
I can load up my client later tonight. Busy now
It's fine to make mistakes. It's possible that the check wasn't testing all edge cases and it's possible that I misread some code
tests
can we please revise that?
i think reviewing these wrappers is pointless without the gametests
Tests should be per area being tested imo. If there are more generic tests they can be in a final slice that is more code coverage wise
Especially when it comes to wrappers as gimp is saying
And yeah I forget, I think the hopper stuff might be fine, but definitely was missing a bunch of test cases that I annoyed Soaryn about at some point
yes of course; I am writing a hopper test rn anyway
your test is still working fine for my code, yet it's clearly different π
Were there any tests for the combined / ranged wrappers ? didnt see any unit tests
no
they're simple enough IMO but if you want to write a few tests I suppose it doesn't hurt
Tests: [22Aug2025 23:05:27] [TestFramework neotests:tests]: Test 'testHopperCooldown' has been enabled.
[23:05:27] [Server thread/ERROR] [minecraft/LogTestReporter]: neotests:tests/testhoppercooldown failed at -10597065, -59, -2976792! Encountered exception running method-based gametest test: public static void net.neoforged.neoforge.debug.transfer.VanillaHandlersTests.testHopperCooldown(net.neoforged.testframework.gametest.ExtendedGameTestHelper)
[23:05:27] [Server thread/INFO] [minecraft/GameTestRunner]: Running test environment 'neotests:tests/level.entity.player.event' batch 0 (10 tests)...
Tests: [22Aug2025 23:05:27] [TestFramework neotests:tests]: Status of test 'testHopperCooldown' has had status changed to [result=FAILED,message=GameTest fail: Encountered exception running method-based gametest test: public static void net.neoforged.neoforge.debug.transfer.VanillaHandlersTests.testHopperCooldown(net.neoforged.testframework.gametest.ExtendedGameTestHelper)].
Tests: [22Aug2025 23:05:27] [TestFramework neotests:tests]: Test 'testHopperCooldown' has been disabled.
@copper fjord why is the key information (exception + stacktrace) not here? 
how would you like "yes" for an answer
it should be though, those are the messages in the middle, no?
weird hmm
thanks mojang
did they break it in 1.21.6?
maybe?
I could've sworn I've seen it log something at some point
Seems reasonable to patch or replace
nope it's been like that for 2 years
I guess I was just hallucinating
CI will however tell you the line it fails
and by that I mean there will be an annotation in the changed files view
so there's that
OK, I did a first pass on the Container wrapper classes but I am still not statisfied. The next thing is likely to look into the LivingEntity equipment wrapper -- that will also likely interact with the equipment slots of the player wrapper
it looks like at some point between 1.21.1 and 1.21.8 the entity equipment storage was refactored, with a clean split between storage and side-effects -- that's exactly what we need for transaction support
yeah the LivingEntity wrappers are a bit involved
I think I will trust the existing one (EntityEquipmentInvWrapper) to do the right thing
OK, I did something that looks quite reasonable to me in the end
still need to add a check for stack.canEquip, which was not there in EntityEquipmentInvWrapper but I think makes more sense overall (you don't want to be inserting random items into the entity's slots)
Alright, at least I found a bug with the redstone calc π
Or maybe a point that should be discussed
If you have a slot filled two twice its capacity
That should count as 100% filled for the purposes of redstone level, not 200%, right?
Well right now the return value exceeds the max redstone level
that definitely cant be correct
Ah yes that is wrong
Since it averages the fill-level (percentage-wise) of all slots
I'd say an overfilled slot should count as 1
I would argue that if the slot is considered full (amount >= capacity as we discussed) it should give the max contribution for that slot, not more
Yeah that's what I mean
Yeah same
This is also odd
So if you have a container with 10 slots, 9 are full, 1 has capacity 0, the entire container is considered to have signal 0?
that seems odd
it should just skip the slot
Yeah I did it because something had to be done but I'm not convinced
Yeah agreed
and if all slots were skipped, then return 0 at the end
@fickle forge @neat matrix do you allow me to use your Fabric Transfer API tests here? the testShulkerNoInsert and testJukeboxState tests respectively
Yep, sure.
thanks!
hmm, the patch to make AbstractFurnaceBlockEntity transactional was forgotten
the trick here is that the furnace will reset its recipe when the input is emptied or swapped out
so we have two possible implementation strategies:
- at the end of the transaction, check if the item changed or got emptied; if yes, reset the delay
- make recipe timer transactional. immediately after the item is emptied, change the recipe timer; in case of a rollback it will get reset to its previous state
what's the difference? Well, with 1, if the input is removed from the furnace and then added back to the furnace in the same transaction the timer does not get reset. With 2 the timer will get reset.
I think that 2 is preferable. Fabric and the experiments PR implement 1, but I think that it's slightly incorrect
ya
Alright here go the first batch of tests: https://github.com/neoforged/NeoForge/pull/2593
Couldn't pull really anything from the final slice for these classes, most were not tested.
hmmm, did you know that chests don't have a comparator output if they are obstructed? π
Wait what?
nope, didn't know that!
That's the first time ever hearing about that
I wonder if it does that with shulker boxes
yeah it's weird; one of my tests was failing because there was a barrier block above the (full) chest, which caused the comparator output to be 0 instead of 15 π
the furnace patch is kinda shit - if (insideTransaction && net.neoforged.neoforge.transfer.transaction.Transaction.getLifecycle().isOpen()) cookingTimesJournal.updateSnapshots(net.neoforged.neoforge.transfer.transaction.Transaction.getCurrentOpenedTransaction()); but at least it works π
ah well, the slice is now at +3100 lines π
not a huge fan of this
I have to try an alternative, I don't like this one all that much π€
hm
if you know you're inside transaction by passing the boolean would it help to instead pass a nullable TX context?
can't see the rest of the patch though, so uh no idea
I considered it, but there is a case where insideTransaction is true but we are actually in a close callback so transaction operations are invalid
so I went for this instead π
Hm wait the entire thing (reset cooking time) is a neo change?
no but we have to make it transactional
these vanilla wrappers are quite complex and there's few people who understand them... thankfully they're all kinda hidden π
yes, I was going off of the comment, if it is just to make it transactional it should probably state that
ah yes
I'm late but go ahead
interesting change with the agreed-upon isValid semantics: the bottom handler of a composter cannot accept bone meal from insertion, but isValid should return true
hmmm it seems that vanilla will let you extract the bone meal from a composter from the null side
will leave it as a todo for now
same problem again for the entity equipment wrapper
but this time I will go for the simpler solution of only checking at the end of the transaction, and leave a TODO in the code in case we want to revisit it later
@copper fjord another thing with the current testframework. The summary it prints at the end just hides the much more focused game test summary. This is what I want to see not 200 lines of "Test whatever passed" π
[13:08:16] [Server thread/INFO] [minecraft/GameTestServer]: ========= 149 GAME TESTS COMPLETE IN 1.585 min ======================
[13:08:16] [Server thread/INFO] [minecraft/GameTestServer]: 1 required tests failed :(
[13:08:16] [Server thread/INFO] [minecraft/GameTestServer]: - neotests:tests/testhorsearmorwrapper: Expected equip event count to be 1, was 0 on tick 0
[13:08:16] [Server thread/INFO] [minecraft/GameTestServer]: ====================================================
(the real crime is not using snake case in that test name)/s
Yeah it is extremely annoying to find the test that actually failed
the function name is testHorseArmorWrapper
I made a ton of those comments on the original PR
must be hidden in those 1650 comments π
I didn't find one on ComposterWrapper at least
Lots are probably hidden from βoutdatedβ given GitHub is so so at recognizing if the line or area the comment is about can still apply
ah right
well in any case this slice is slowly approaching a stage where it can be reviewed π
+3200 already
Just let me know when 
Slices definitely make it nicer that we were able to decide on isValid instead of me having to leave a ton of comments saying I donβt like something, and can just shorten them to pointing out when impl doesnβt match
yup agreed
we should probably combine isValid and getCapacity in the default implementations, no?
given that isValid will in most cases be equivalent to getCapacity == 0
well capacity can change, isvalid is not supposed to?
it can change as well, that was changed relatively late in the spec
precisely because if you say that it can never change it becomes relatively useless
I'm not even sure why you'd want it, just trying the insert anyway is not good enough? What's its intended purpose?
I suppose it only tests the filter and might be faster if it doesnt have to record a snapshot? Idk
for insert purposes it doesn't matter where you put your check since both get checked (currently)
then where would i use it?
Well, it depends on what you are talking about. In the case of the general ResourceHandler interface, capacity and isValid are more metadata. You should just try to insert if that's what you're going for. But here I am talking about the provided base implementations
Here's an example for the vanilla Container wrapper:
@Override
protected boolean isValid(ItemResource resource) {
return container.canPlaceItem(index, resource.toStack());
}
/**
* Special cases because vanilla checks the current stack in the following functions (which it shouldn't):
* <ul>
* <li>{@link AbstractFurnaceBlockEntity#canPlaceItem(int, ItemStack)}.</li>
* <li>{@link BrewingStandBlockEntity#canPlaceItem(int, ItemStack)}.</li>
* </ul>
*/
@Override
protected int getCapacity(ItemResource resource) {
// Special case to limit buckets to 1 in furnace fuel inputs.
if (index == 1 && resource.is(Items.BUCKET) && container instanceof AbstractFurnaceBlockEntity) {
return 1;
}
// Special case to limit brewing stand "bottle inputs" to 1.
if (index < 3 && container instanceof BrewingStandBlockEntity) {
return 1;
}
return container.getMaxStackSize(resource.toStack());
}
we could totally change this to
/**
* Special cases because vanilla checks the current stack in the following functions (which it shouldn't):
* <ul>
* <li>{@link AbstractFurnaceBlockEntity#canPlaceItem(int, ItemStack)}.</li>
* <li>{@link BrewingStandBlockEntity#canPlaceItem(int, ItemStack)}.</li>
* </ul>
*/
@Override
protected int getCapacity(ItemResource resource) {
if (!container.canPlaceItem(index, resource.toStack())) {
return 0;
}
// Special case to limit buckets to 1 in furnace fuel inputs.
if (index == 1 && resource.is(Items.BUCKET) && container instanceof AbstractFurnaceBlockEntity) {
return 1;
}
// Special case to limit brewing stand "bottle inputs" to 1.
if (index < 3 && container instanceof BrewingStandBlockEntity) {
return 1;
}
return container.getMaxStackSize(resource.toStack());
}
Right, they are analogues to canPlaceItem and getMaxStackSize, carry on
I think the key "problem" for me is that canPlaceItem == false implies that getMaxStackSize == 0 for simple handlers
yet I don't want to write the same check in two places
doesn't canPlaceItem check if the item could be put there? not if it currently can exist there?
yeah, it's independent of the current state of the inventory
so is the max stack size
another way to see it is that if the capacity is 0, it means that the item cannot fit and thus cannot be placed there
I didnt think maxStackSize was, i thought that was always 64
mm, yes, just looking at that
they totally could, and i see what you mean
i think you could implement maxStackSize as canPlaceItem(item) ? min(item.maxStackSize(), maxStackSize()) : 0, but not the other way around
however, slotted
yeah that's what I did in one of the base implementations:
@Override
public long getCapacityAsLong(int index, ItemResource resource) {
Objects.checkIndex(index, size());
return resource.isEmpty() || isValid(resource) ? getCapacity(resource) : 0;
}
I suppose that if you're gonna return a capacity of 0 then isValid is the better place to put it
whereas if you're gonna limit the capacity to something > 0 you should put it in getCapacity
that makes sense to me at least
That does
OK, finally got around to reactivating the provided implementations based on StackListHandler
still missing some javadocs but that means the PR is ready for review!
I will try and go over the PR again in the coming days
not a fan of the ItemStackListHandler name
it doesn't make it clear that it's a resource handler, no?
an alternative name would be ItemStacksResourceHandler but that's not amazing either
(I am happy to keep ItemStackListHandler but maybe someone has a better suggestion)
i prefer ItemStacksResourceHandler as well
i like how the abstract resource handler ended up being implemented. i was worried for a second youd just eliminate the idea of a generic handler for resources altogether, this is good tho
ItemStacksResourceHandler sounds good imho
thanks; I also like how it is now, feels quite simple and intuitive to me
currently writing some javadoc for it
ok will also rename
StackListHandler -> StackListResourceHandler
{Fluid,Item,Resource}StackListHandler -> {Fluid,Item,Resource}StacksResourceHandler
maybe ResourceStacksResourceHandler is a bit verbose, but at least it is quite clear
Generic maybe?
StackListHandler -> StackListResourceHandler
StacksResourceHandler? hua hua hua
I mean that also works but StackList is a bit clearer (arguably most people probably won't be using it directly anyway)
I was just commenting on the symmetry with ItemStacksResourceHandler
the most generic one is the base one (StackListRH or whatever it's called)
so you like this one? π
oops forgot to rename it lol
SuperStacksResourceHandler :>
nope too late, you already approved
ship it tech

I think we should do a list of class and package names and their description to better see which need renaming and then do all of those at once
will this be the last slice?
sure, we'll do some extra relocations later but there's no reason not to do these renames now
wouldn't you rather review 20 class names rather than 100 at once? π
I just want to give a thank you to everyone that's put so much time into getting this rework done! It looks amazing and I'm excited to use it when it all comes together (and you all've given me the motivation to work on a mod I haven't touched in versions, with this now available...)
meanwhile I'm dreading the day it's merged because the changes provide very little to no benefit to any of my mods (maybe except the inventory thingy in ender-rift which I'm not even sure I want to keep because it's a mess to maintain)
(I'm not saying the changes are negative or anything just that no benefit + changes = work that feels like a chore)
pretend its mc version update
specially thanks to adrian and soaryn for all their hard work
Adrian, soaryn, tech, and all the other folks who've put time into writing this and getting it through the review process and all β₯οΈ thank you!
Oh there's still quite some work. But at least this slice is almost done I hope, and future ones will not be as difficult. I hope we'll get this done for MC 1.21.9. You can always speedup the process by reviewing the slices as they are submitted π
how many left approx.?
π you assume I have the time (I'm in the middle of moving across the country)
3 additional ones probably
(At least)
we probably ought to pin the current PR/branch/slice/etc. in here
also, @loud stirrup thoughts on adding the current slice-in-progress to your transfer rework preview PR? (so those who look at it know what's coming)
I have half a mind to edit the descs of all the slices so far to link to the prev. and next slices in series
why?
the PR itself has publishing already
just so there's convenient links between each of the slices
Oh wait, what do you mean by "add"?
ah, I should've clarified: you know how your transfer rework preview PR lists all the slices that've been merged so far into the branch?
I was thinking of adding the current slice-in-process to the list as well, so people who look at the preview PR also see the one being worked on and up for inclusion next
Why not
yea
wanted to clear it before I did it myself 
also,
do we have a list of remaining slices that can go in the preview PR's list as well?
or is it more of a "look at what's remaining" kind of deal
ah yes, ofcourse, i thought you meant include it in the published code
which is kinda moot since it is not merged into the source branch yet
item contexts, energy, and migration from the previous API are the 3 next big slices
but linking it is totally fine
it'd be neat to at least an overview of what's remaining
Which also means that the current one should not be held up for too long, since we have 3 more to go and we don't know when the first pre will drop
ideally, this'd be in an issue regarding the Transfer Rework, but I'm settling for the PR at least since people are likely to look at it more
I'll add the three to the list alongside the current slice then
I need people to review the PRs not the overview of what work I will do in the future π
Sure
I am waiting for you to tell me when it is more or less ready so that I can review it (last I checked a day or two ago it was still a draft)
with that, worth turning the list into checkboxes? and tick off merged slices?
so that github shows N tasks completed and a progress (circle) bar on the prs screen
sounds good to me
it is done
am I right that the intention is to rebase-merge all the separate slice-commits to the main branch?
It's been ready since Monday, I just forgot to undraft it heh
We'll probably squash
I will try to start looking at it later today or tomorrow
i wouldnt squash, tbh.
Hm Tech, is AbstractFurnaceBlockEntity.this.cookingTotalTime observable?
If it is, it'd be incorrect to defer resetting it until the end of the transaction, I think
Rebase merge sounds reasonable
I asked because I assumed the not-to-be-merged preview PR implies that the transfer rework won't be merged in as a single PR squashed commit
There is surprisingly little in terms of patches in this slice
Alright, submitted my review. I think the most important bit here is the testing of the vanilla wrappers and some of the API design.
Not from transfer code
Sure
There's already a bit of testing code, I don't think it's a blocker. I would probably add regression tests if issues arise in the future
Hmmm how did that go? π
Pssst @mental raven you still have a request for changes as well
what is this abomination that vanilla is doing here π
it's so unnecessary, this could just have been return p_255922_.is(ItemTags.BOOKSHELF_BOOKS)
is this proof that even mojang devs don't understand how to implement Container correctly? π
@loud stirrup I addressed your comments except for 2 (for which I gave a comment on GitHub), can you check them?
Approved
in the meanwhile I'm already going to start the item context slice
I did some of it yesterday, will try to do more today, maybe even finish. As of yet no comments
I like no comments π
To be fair a lot of my comments on the stuff in this slice got addressed pre split
makes sense yeah
I don't think that ItemContext is a good interface name
ItemHandle reads very Win32
ITEMHANDLE
LPITEMHANDLE
/**
* Provides access to the location where an item is stored,
* such that the current item resource and amount can be read,
* and the stored item can be changed.
*
* <p>This interface is primarily used as the context type {@code C} for {@linkplain ItemCapability item capabilities}.
* This allows the returned capability instance to modify the current item or even swap out the item entirely,
* for example to replace an empty bucket by a filled bucket.
* Use the {@link #getCapability(ItemCapability)} method to query a capability for this location.
*/
public interface ItemLocation {
I think this works
hmmmmm
I actually refined it a little:
/**
* References an item storage location, like a slot in an inventory or a player's hand,
* such that the current item resource and amount can be read,
* and the stored item can be changed.
*
* <p>This interface is primarily used as the context type {@code C} for {@linkplain ItemCapability item capabilities}.
* This allows the returned capability instance to modify the current item or even swap out the item entirely,
* for example to replace an empty bucket by a filled bucket.
* Use the {@link #getCapability(ItemCapability)} method to query a capability for this location.
*/
public interface ItemLocation {
But its not a reference to the location, it provides access to the location. you cant know where the item is by having an instance of this class
ItemAccess?
a reference provides access π
right, but a ResourceLocation doesn't provide access to the resource, it just gives you an address for where to find it. ItemLocation has nothing in it that someone could use to navigate to where the item is, just a way to access the item
ItemHandle was not such a bad idea
I don't think that RL is necessarily representative of what a location can be, but the confusion potential between ItemLocation and RL is not great
Told ya ItemHandle was not suhc a bad idea π
I actually considered ItemHandle but the confusion potential with ItemHandler is worrisome
I revive my idea: InItemStorageContext π
what's a Storage mr?
throwing that right back at you for the very first sentence in the doc comment
πποΈ
an item storage location is a location where an item can be stored π
I actually like that
you like StorageMr?
an ItemStorageLocation, so to speak?
ItemSetterGetterSwapper has the same vibe of Outgredient
waaaaay too long
So an InItemStorageContext is the context for storing things in an item!!!!

ItemHandle, ItemGateway
Hey, speaking of gateway
ItemStargate
No, but seriously, I feel like ItemHandle is in fact the better choice
I am kinda whatever about it, ItemHandle works for me
It's kinda in the ballpark for a handle at least
the fun thing is that Fluidity uses handles instead of slots or indices... full circle π
(nobody here knows what Fluidity even is)
ItemAccess or ItemHandle works for me
Both are fine for me
I like ItemAccess more
If we wanted to align with vanilla ig ItemAccess aligns better with SlotAccess
Or what did they call theirs that was specific to menu slots?
it's called SlotAccess yes
ItemHandle is also too close to what the old item handler was called, and people will be confused and try to use it for their new stuff
ItemAccess sounds better imo as well
I would do ItemAccess or ItemHandle
but ultimately both work
It is technically more an ItemHandle then an ItemAccess based on its usage, but the confusion potential on the XYZHandle in particular related to ItemHandle and ItemHandler is too large for me personally
Then we're in agreement! We scrap the whole thing and keep the existing system
LOL
Sure lets just throw the entire change in the bin
Lock this thread
And all go grab a nice beer in a beergarden
Ill drink to that!
IMHO Sounds better then deciding the name anyway
ItemAccess it is
I go eat pasta
Guten Appetit
@flat root left some comments for you, and that is all I have patience for right now, I might try to do a bit more later today, and if not I will finish it tomorrow, down to 9 files left
Thanks
Those last comments are the only things i really have left. Approval for me after :)
Ok thanks I'll have a look tonight
One thing: there is no MapMaker with identity semantics π
But we could maybe detect equals overrides via reflection in VanillaContainerWrapper.of and throw?
(We have similar machinery for data component types so it shouldn't be too hard to do)
We could use a wrapper key object but that would allocate on every lookup
hmmm
i hate rolling collections, but might be required here
not sure how MapMaker would be doing weak keys without allocating
Rolling collections?
I have to check. The java MapMaker file is super complex so I would prefer not trying to implement our own map π
The todo is inherited from Fabric and has existed for 4y so I am inclined to "do nothing" (or at least add a check) if we really can't find a better map implementation
We need a concurrent weak key weak value identity hashmap π
Rolling here meaning creating
Well, we can probably ignore it then, or do a warning if hashCode/equals is implemented
Annoying that MapMaker doesn't support it
Seems like a common thing
Even the JDK doesn't have a weak identity hashmap
I faked one for capabilities but it's quite messy
yikes
i believe googles map builder lets you make one
i can never remember the proper class name tho
MapMaker?
Ohhh
If you specify weakKeys it seems to use identity comparisons for keys!!
@mental raven that means I can just remove the todo lol
Should we move caps to that? (Different pr of course)
did a bit more, and will try to finish off the remaining few files later today, but posted what I did in case I don't get to it until after you no longer have time to look at the reviews for the day
Maybe yes
posted my remaining pending comments. I still need to review VanillaContainerWrapper and the two test classes. But for now I am going to go and get something to eat because I have a headache. I will also try to respond to your replies, though I am not sure if I will do that today or not
OK, thanks a lot! I will push what I have already and then work through the new set of comments
Don't look at the tests too closely π
How else am I supposed to review them if I donβt look at them closely 
well, you squint, look for obvious mistakes, and ignore the rest π
at least that's what I usually do when reviewing tests, as "fixing" them is usually not very relevant compared to other tasks one could be doing
should we report 64 or 99 as the capacity for "empty" stacks?
Imo 99, unless the handler has an explicit max size set and it happens to be 64
Having upper bound is more useful than having a potentially lower value
For what it's worth, ItemStack#getMaxStackSize() returns 1 if the MAX_STACK_SIZE data component is absent which it is for the empty stack
notably, this means that any implementation of getCapacity needs to special case it since air should return 64?
hmm
let me test this in a debugger
WTF
thanks @noble latch π
does this warrant a disclaimer on ItemResource#getMaxStackSize?
So yes we need to special case the empty resource, and imo we should do it at max theoretical, as it is more useful imo than a limited version
sure, if we're gonna be special casing we might as well return 99
Agreed
related question for cauldrons queried with an empty resource: return 1000 or return the largest amount ever registered?
I feel like largest makes more sense, but with the ability to query per type, I am not sure it matters. As long as a non discrete cauldron that only supports two buckets can actually be inserted into, and nothing caps it at one bucket from empty and then have insertion fail
stupid return resource.isEmpty() ? Item.ABSOLUTE_MAX_STACK_SIZE : Math.min(resource.getMaxStackSize(), Item.ABSOLUTE_MAX_STACK_SIZE); π
it's silly yeah
Why no oversized stacks?
it doesn't use the default components items should
i'm 99% sure it's a oversight but it's hard to explain a issue like this on the mojira without it getting closed because "there's no observable issue in vanilla"
Ehh probably to avoid accidental bugs that try to insert so it doesnβt end up stacking empty in the same slot
yeah it used to return 64; either 99 or 1 would make more sense
the item stack constructor that takes void just doesn't set default components for empty
well yeah, there is no item
had a annoying time fixing the issues caused in create by the change
it's also that if the stack is empty the component map is DataComponentMap.EMPTY
the alternative would be using another default for the component map, which would be quite unexpected tbh
(or defaulting to e.g. 99 instead of 1 in stack.getOrDefault(DataComponents.MAX_STACK_SIZE, 1))
OK, another weird thing in recent MC versions is the number of slots in a player Inventory
there are these two weird slots
what should we do about them? just expose them, and let the canEquip check hopefully disallow insertion in them?
Is there no parameter-less capacity getter? If there is, then it should probably use that instead of the absolute max stack size
not in this case, but that is what I am doing for the vanilla Container wrapper:
return resource.isEmpty() ? container.getMaxStackSize() : container.getMaxStackSize(resource.toStack());
I will finish up what I have been needing to do with the open slice today, been somewhat sick so havenβt gotten to it
Okay, thanks. Can you also check my replies to the previous comments?
Especially on the GH threads that are still open
currently doing that
okay added more comments and responded to your replies. I still need to review VanillaHandlersTests, but I am going to go do some stuff irl first
also one comment on your:
// TODO: I would like to bring this test over, but we need snbt test structure support first
I say either document that somewhere else, or uncomment the method, but just have the annotation commented out with that TODO rather than having a large block of code there that isn't using Neo's terms or stuff and that uses yarn names in it
I will make sure to review the rest of that file sometime today though, but given it is tests odds are there isn't that much to say on it, so it shouldn't be that hard
Yeah just commenting out the annotation makes a lot of sense
The actual test is very nice
Yeah thatβs the solution I would go with
That way we actually make sure the test at least compiles
Potential idea for a future PR or something, maybe similar to how our extended game test helper has the startSequence that then makes them parameterized, we might want to provide a startTransaction so that when doing more functional styled tests that is chaining calls, then it can handle the try/close of a transaction automatically and nicely
okay, in theory I have reviewed all the files for the template and vanilla wrapper slice. Granted I suspect you may have some more replies to some of my comments
@mental raven can you approve the PR? or can I dismiss your request for changes?
(still working through pup's comments but still)
Feel free to dismiss
thanks
Sorry, am currently slightly drunk, otherwise i'd have re-reviewed. But i think all my comments were addressed/discussed
I'll raise a glass of sake. Kanpai!
Just stay safe!
okay tech responded to your replies/marked things as resolved. Only added one comment in regard to the changes you made so far. But we are getting there
love it π
Yes, just some games with friends, for batchelor party ish thing
Never had sake, I usually just do Johnnie walker red label, its not the best but it goes down well
I was at a Japanese restaurant. It seemed fitting.
Heh, I have heard good reviews
ok I'm back, let's try to get through these last 14 comments
@cunning mulch your mayPlace logic seems quite complicated to me
(in InventoryContainerSlot)
mayhaps
mainly given mayPlace I guess needs to handle when it has things in it
so
I guess is valid makes sense
it's really weird that you check if you can extract 
hmmm yes probably actually, e.g. when swapping between the mouse and the slot
just add a comment that is like "We just use a rough estimate of if it can be inserted by checking if it is valid as we don't care about the current contents and want to allow swapping them"
which is why my impl has a impl specific handling of bypassing the current contents but still checking the insert predicate itself 
so yeah just add a comment for why we are just using is valid
and that should be good enough and we can let people override if they want it more accurate
we could play some tricks with transactions (e.g. extract all current contents and then try to insert) but it does get quite complicated
yeah, which isn't worth it for now imo
(your teswt double chest comparator thing has the awssertions mention "item variant"
instead of item resource
// Use isValid as a reasonable estimate.
// We can't try to insert as we don't want to check to current contents to allow swapping.
// This method is left for mods to override if this is not sufficient.
sounds good
regarding the cauldron capacity maybe I just add a todo
then at least if someone has a case like that they can know that we are unsure if it comes up and just report it or PR the fix
instead of worrying or just hacking around it
regarding the game events for the cauldron, I have no clue
maybe shulkers are the only thing that would care about the game event being emitted or not
you mean sculk shriekers and sensors?
ermm yes
and might lead to some interesting builds with sensors
for redstone but comparators also would work

ok then I don't change anything π
I'll add a quick note actually
this one I feel can be resolved?
up to you, I still think for the 1 value it might be worth it, because the canPlace doesn't mean that it will only accept one bottle if you try to insert two at once
for the if statement part I agree it isn't worth not just doing < 3
ok then I will resolve it as I don't like referencing the menu (typically menu slots have their own custom I/O restriction logic)
for the player wrapper I'm just gonna limit it to main + armor + offhand
for this comment, this needs to be handled at the PlayerInventoryWrapper level
looks like we just need to call player.containerMenu.broadcastChanges() on the server side from the player wrapper onRootCommit
on the client side, we might also want to call setPopTime(POP_TIME_DURATION) on the created item stacks, but maybe I'll just leave that as a TODO for now
for players we can bounce for armor and hands and throw for the rest
sounds good for me
just make sure to update the javadocs for which ones don't support players
ok, pushed
now you can make the item context PR actually reviewable after you rebase it rather than having it expand upon a slice that isn't merged yet!
ok I force-pushed the item access PR but it's still quite a mess
If you get it at least semi reviewable/cleaned up I can try to give it at least a quick pass this evening/tomorrow
Afterthought, do we want a living entity extension method for what slot types an entity supports? Then we can default it for player that way (filter out more programmatically in the player wrapper), and then do similar for normal entities?
Definitely not something necessary for initial release
Just wondering if it might make sense
also given you are repackaging that, I would rather access.item to itemaccess as the package. That way if we need to add other access types than item, it is as simple as just adding a different subpackage
Hmmm in that case we'd just do fluidaccess, no? I really doubt it would be needed. We can also just generalize ItemAccess to arbitrary resources T if we need to in the future
I don't like deep nesting, the current handlers.wrappers.resources or whatever is crazy
fair, I forgot we could just generalize it
how about just access
then
I thought about fluid access but yeah, I donβt see how useful thatβd be
That works
Right now only the actual ItemAccess interface and the other classes in the same package are reviewable
(except for the Stack one iirc)
Hmmm idk. It's kinda supported but I suppose that canEquip should just always return false.
fair enough
(I haven't tested this)
And these extra equipment slots are actual player Inventory slots as well (which is weird)
Okay, the access implementations look reasonable now
next up I suppose I'll have a look at the bucket wrapper
ok the goal is to provide a very general base implementation similar to StacksResourceHandler and then implement various specializations
so wait, we're actually having like, completely generic Access stuff? and the item based capabilities will be like, Capability<ResourceHandler<ItemResource>, Access<ItemResource>>
More so that I suggested a broader package name so that it is easier to expand in the future if it comes up, without requiring people to do as much refactoring
TLDR I try and make sure to future proof package names and stuff
#1378294669053792337 message
-# easy link to snapshot pin but yes its happening 
[Reference to](#1378294669053792337 message) #1378294669053792337 [β€ ](#1378294669053792337 message)# 1.21.9 - Pre Release 1
- Primer: https://github.com/ChampionAsh5357/neoforged-github/blob/update/219-or-22/primers/README.md
- Article: https://www.minecraft.net/en-us/article/minecraft-1-21-9-pre-release-1
- Changelog: https://misode.github.io/versions/?id=1.21.9-pre1
- Notion: https://apexmodder.notion.site/2524f070f88880648482e3cbdcba566a?v=2524f070f88880b29f60000c52785ecb&p=2704f070f888808abae6c8586904736a&pm=c
- SnowMan: https://github.com/neoforged/Snowman/commit/d2a70ff1cc5b80b0e6c94a702212661610694757
SlicedLimes Videos:
- Main: N/A
- Pack: N/A
Unfortunately yeah
This weekend I will be back and be able to start reviewing the item access PR if it is ready
Hmm I need to get back into it
Writing the item access equivalent of StacksResourceHandler isn't fun though and I am running out of motivation
So maybe pup I'll ask you to directly send a PR-to-PR for your more mechanical comments and maybe also for some discussion items, if you have the time for it
Because I won't have a lot of time and we really need this out
I think you mean cosmetic comments π
Perhaps, regardless I will start with a review, and then if I need to address some of my comments myself I probably(?) will be able to
OK, I just pushed a first set of base RH implementations backed by ItemAccess
I think they're a bit lacking in functionality, but won't be too difficult to extend
porting over a test from Fabric and I already found a bug π
I will try to review the item access pr sometime this week. My original plans of this weekend are likely not going to happen given it seems I picked up a cold during my return trip so just taking it easy 
Ah shit
We're getting tight on time
Maybe someone else can already review it and we fix your later comments in a follow-up PR?
I went over it and left a few minor comments. I think it'll do the job. If there's any convenience ones we've missed we can add those on demand.
I think the complexity is not super high overall of this part of the system
Nothing poped out at me with a quick glance over
Ok thank you both
I will try to give it a quick skim today
yeah I definitely have some comments, all relatively simple (at least so far). I am for the most part not going to bother evaluating the javadocs on things, and I likely won't bother reviewing the test class. Should hopefully be done in like 20-30 min tops
Got distracted and semi reviewed other stuff that I said I wasn't (I didn't necessarily review javadocs of individual misc impls, but decided to at least give them a brief skim for ItemAccess and ItemAccessResourceHandler. Down to just having to review the ItemAccessResourceHandler class and then you can have your change request 
There you go @flat root have your ~25 review comments
I think all of mine should be pretty simple and likely not need a back and forth, but if there are any clarifications or stuff you want feel free to just ping me so that we can try and get this slice merged today
Okay thanks a lot!! I'll have a look a bit later
ok @cunning mulch @loud stirrup please re-check the PR
pup I think the only outstanding comment is the mismatch between a supplier and no supplier between the fluid and item base implementations
do you have a preference either way? (2 suppliers or 0 suppliers?)
I would lean to no suppliers I think
okay, other than potentially making them not be suppliers @flat root it all looks fine. Once they are consistent (whether it is supplier or not) I will approve the PR
ok done
thank you both!
okay here is an easy one: https://github.com/neoforged/NeoForge/pull/2651
because NNN.transfer.handlers.templates.fluids is crazy π
I'll follow-up with energy tomorrow
and legacy migration should come towards the end of the week π
@cunning mulch any opinion here?
I will take a peak after I eat (so maybe in like 45-60 min)
Actually from PR description looks fine
Have an approval @flat root on the assumption the code changes are accurately reflected by the description
How many slices left?
@flat root I did push some changes a while back to the energy slice that matched some of the changes in the Resource Handler changes so should help speed that one up a bit
Didn't you do slotted energy or was that something else?
That was removed in pre slices
Another easy PR: https://github.com/neoforged/NeoForge/pull/2652
the energy slice is coming along well
I do kinda want to rename EnergyBuffer though as I don't think it's a great name (it's the "simple" base implementation)
maybe SimpleEnergyHandler works
in the meanwhile I'll bring over LimitingEnergyHandler
I donβt like conflict when it comes to auto importing, so I wonβt approve on the PR, but I also wonβt block it
Autocomplete becomes a menace when making methods and such
I'm just gonna say this before anyone rushes the full PR of this. I am strictly against doing this quickly just to have it in right after the update, I prefer to have a breaking change a week later rather than rushing this very important and big refactor.
I wouldnt personally want a week (that will be rather long), but having a stepping stone version before it would be helpful
I say this also transparently as I am on the 21.6 version with the old PR build of this system, so Im in limbo until we can get it in
a week is just a ballpark number, I just don't want this rushed in just for the purpose of being early (same reason why I don't like rushing for same day releases)
FYI it's not particularly rushed
isn't there a whole slice of moving things around left?

