#Transfer rework (IItemHandler, IFluidHandler, IEnergyStorage)

1 messages · Page 2 of 1

stone dragon
#

Transactions could be trivially wrapped in a simple API for simple extract/insert with remainder operations

cursive geyser
#

yes and if that's the case I'm good

stone dragon
#

Plenty of inventories cannot sensibly implement this

#

As in, if I had to implement that it'd lead to wacky undefined behavior

wind steppe
#

setting anything is not great api design. does not give the author enough control over how the interaction occurs. which sucks because this is how all the existing heat apis work ;-;

stone dragon
#

I already have to do enough weirdness just to pretend to have normal "slots"

cursive geyser
#

if the default is "only one stack, any transaction that tries to insert more than one thing at a time receives a 'not valid' result" that is fine

#

that's what I mean with not giving modders additional work

stone dragon
wind steppe
#

transactions could be simplified on the dev side using someone elses storage, but they are more complicated to implement on a storage than traditional handlers

#

because it forces you to defer all unrollbackable actions to some final commit method

cursive geyser
stone dragon
#

Also, transactions aren't "rolling back" an inventories state...

#

The whole point is that you don't have to do that

cursive geyser
#

let me say it this way: if I have to implement transactions into Ender-Rift I'm not porting the mod

#

I have no energy for it

#

nor the use case

wind steppe
#

well no, itd be easy to have like, TransferUtils.simulateInsert() for those who need it. But there is no real way to simplify how storage is implemented with transactions

#

thats the big problem

#

and im not sure it can be avoided

cursive geyser
#

but ender-rift is a powered inventory -- insert/extract operations cost power

stone dragon
cursive geyser
#

so you can run into the situation where there's enough power to simulate one insertion positively

#

but once you commit to the first insertion, there's no power for the second

stone dragon
#

Right, and handling that situation sensibly is exactly what transactions are good for...

cursive geyser
#

yes but I don't want to be the person that has to implement them

#

that's precisely the kind of situation that I am against: the "fuck you implement transactions if you have your own inventory implementation"

#

no thanks.

wind steppe
#

yeaah i get that sentiment. i sorta feel the same way. transactions definitely complicate how one has to handle their storage

stone dragon
#

I mean the alternative is to disallow complex operations entirely -- which now forces the issue onto the people querying your thing instead. They have to figure out how to handle otherwise unrecoverable states, when they need to implement A only if they can also implement B, etc.

wind steppe
#

yeah thats just going to have to be what forge deals with im afraid

cursive geyser
#

the alternative is to make it opt-in. if someone's requirement is "I must be able to insert these 2 stacks, or nothing at all"

serene igloo
#

in my pipe mod I don't immediately insert items into chests after I simulate them

I simulate insertions into connected chests, then if I find sufficient room for the inserted items, I queue up the items and let them run through the tubes along the path to that chest

then however many ticks later, when the items actually reach the chests, I physically insert them

if there's no room for items at that point then I either try to reinsert items back into the tubes or eject them into the level (so if you get extra backwash from the chest filling up then they can get rerouted to other inventories in the network)

cursive geyser
#

the answer for Ender-Rift will be "nothing at all" every time.

wind steppe
#

there is no such thing as opt in transactions. if transactions are part of the system, everything has to use it or your storage wont interact with other things properly

cursive geyser
#

not true, the default transaction provider can be a fallback that disallows complex operations

wind steppe
#

and what does 'disallowing complex operations' look like

serene igloo
#

if transactions are part of the system then you WILL end up with an entirely separate ecosystem of mods who don't want to support transactions and use a different itemhandler system

wind steppe
#

a new standard popping up is not going to happen, thats stupid

serene igloo
#

well, we already have at least two mods that would use the transactionless standard

stone dragon
#

That would actually be pretty easy to make

cursive geyser
#

I picture a transactional API looking somewhat like this: ```java
transaction = inventory.beginTransaction();
transaction.insert(....);
transaction.insert(...);
if (transaction.isValid()) // This will be false if the request cannot be satisfied, which will be the case always for the fallback
transaction.commit();
else {
transaction.cancel();
}

wind steppe
#

this is just the builder system i described, and has the same issues tech noted

cursive geyser
#

I don't care what the API looks like, just the concept

#

a transaction is a stateful object

#

that lets you declare your intentions

#

and then optionally choose to execute all of the actions at once

#

at some point in the API, there's a way to query if the transaction can be performed in whole or not

stone dragon
#

Giga: you could make a wrapper to fabric's transaction stuff that does what you want here

wind steppe
#

if transactions are going to be an issue causing people like commoble to just ignore them, then itd be better not to have them. i dont want to personally have to deal with mods being incompatible with mine because they were too stubborn to implement the standard api

stone dragon
#

All you'd need is an implementation that allows only one operation and then just returns a full remainder after that. It would be completely valid normal transaction stuff too

analog sapphire
#

this reminds me of AE2 refusing to invalidate caps

#

we should avoid creating the same problem a second time

cursive geyser
analog sapphire
#

wouldn't that just result in any mods that do complex operations simply not functioning with the fallback?

stone dragon
cursive geyser
#

there's no way in which you can expect the average modder to implement complex transactions correctly for their custom chest variant

stone dragon
#

And we're right back where we started

stone dragon
#

They just use the premade class for a fixed size inventory or whatever

wind steppe
#

because the system you're describing still requires devs to rewrite how their mods work to defer un-rollback-able changes to the commit method. at that point you might as well just implement transactions

cursive geyser
#

but that's the only way to do transactions safely?

wind steppe
#

yes, the only way to do transactions safely is to make sure nothing physically changes in the world until commit is called

cursive geyser
#

yes, so the only way to have a fallback, is one that disallows complex operations, because that's the only thing you can do safely via simulateInsertion/simulateExtraction

#

otherwise, every single use case that cannot use the stock ItemHandler implementation is fucked

stone dragon
cursive geyser
#

no?

stone dragon
#

So you're right back in the current nightmare

stone dragon
# cursive geyser no?

If most modders don't implement them... then you can't rely on being able to do them

#

If it's the default, most modders won't implement them

#

I would much prefer to have complex operations normally, just provide default implementations that handle them for you

cursive geyser
#

the default ItemHandler can support complex operations

stone dragon
#

So, for your example, the folks just adding a custom chest would never have to touch it, it would be implemented for them

cursive geyser
#

yes

#

I used a bad example

#

a simple chest can use the default ItemHandler, so it can receive the stock "complex operations supported" handler

stone dragon
#

But if you have a fully custom wacky inventory (like mine... acts as a FIFO buffer), you'd have to implement it yourself

#

Or you'd have to specifically not support it -- which, I point out, you can still do pretty easily with a transaction system...

#

Neo just wouldn't provide that as a default/fallback

cursive geyser
#

let's rephrase

#

I'm okay with having no default in IItemHandler (or whatever replaces it)

serene igloo
#

nothing's replacing iitemhandler

cursive geyser
#

I'm okay with ItemStackHandler having a complex-operations-allowed default transaction handler

#

although I suspect it will cause a large number of mods to be buggy

#

because they implement processing logic on insert/extract by overriding the methods from the reference implementation

stone dragon
#

And sure, you could wrap it in a nice API so it looks similar to the current stuff

cursive geyser
#

IMO, the average modder shouldn't have to be aware that transactions exist

#

they should be something you choose to use if you are ok with raising the usage complexity

stone dragon
#

The average modder isn't doing that sort of custom logic...

stone dragon
cursive geyser
#

there's plenty of mods that have ```
new ItemStackHandler(9) {
@Override insert
}

wind steppe
serene igloo
#

ew, why

wind steppe
#

because you shouldnt use it

serene igloo
#

why not

stone dragon
cursive geyser
#

the vast majority, are implementing insert/extract conditions

wind steppe
#

because its worse, devs should use the immutable resource storage system. idc what you say about transactions cuz i understand the drawbacks there, but itemstacks and fluidstacks are both very flawed as units for transfer. you just dont care about why that is

serene igloo
#

okay iitemhandler isn't deprecated

stone dragon
serene igloo
#

it's deprecated in your PR

wind steppe
#

yes

serene igloo
#

your PR is non-canon unless and until it's merged

wind steppe
#

any storage rework pr will deprecate item handler as part of it

serene igloo
#

why are you assuming any storage rework PR will ever be merged

analog sapphire
#

maintaining a deprecated iitemhandler sounds like more work than just deleting it outright in some future version

cursive geyser
#

well that's the point of this thread

#

the discussion here is about redesigning the APIs

serene igloo
#

the APIs don't need to be redesigned

wind steppe
#

yes. everyone but you is okay with this change

cursive geyser
#

so we are talking as if the design would be happening

#

I just happen to oppose making the API transaction-first, and forcing people to implement transactions when all they needed was insert+extract of 1 stack at a time

stone dragon
#

The replacement of iitemhandler with the newer resource API is, imo, separate from the discussion of transactions

wind steppe
#

^

serene igloo
#

itemhandlers are simple, why do we need to make them convoluted?

#

itemstacks go in, itemstacks come out

cursive geyser
#

because there's people with use cases that are convoluted :p

wind steppe
#

you havent even used it and you're deeming it more convoluted

cursive geyser
#

I remember a PR some years ago

stone dragon
cursive geyser
#

may be one of you

stone dragon
#

The new API looks a lot like IItemHandler

cursive geyser
#

someone had a minecart / train thing

stone dragon
#

It just solves that mutation issue

#

That's... basically it

cursive geyser
#

and they wanted the train to stop ONLY if the train would be able to insert every single stack at once

stone dragon
#

Transactions or similar stuff is an entirely separate issue

wind steppe
#

yeah the storage class looks very similar to iitemhandler now

#

just using immutable resources. thats really all ive changed about it so far

cursive geyser
#

solves many annoyances I have had in the past

#

anyone has a link to the PR(s)?

wind steppe
#

ive tried convincing commoble before, the idea of using anything other than itemstacks in and of itself is convoluted to him :P

cursive geyser
#

nothing is pinned

wind steppe
#

here

serene igloo
wind steppe
#

i have no power to pin things

cursive geyser
#

but I do :p

wind steppe
#

nice

cursive geyser
#

like my only complaint at a glance with the whole thing, is using "stack" for fluids, which has always annoyed me because fluids aren't individual objects you can "stack", and it's an extremely pedantic complaint with very little value :P

#

transactions though, that I have opinions on :P

wind steppe
#

i really understand the concern about it being convoluted. i thought the exact same thing when i wrote botarium, which is a library trying to abstract storage from forge and fabric into a lib. But after implementing some default containers in botarium and really understanding why the change was made in fabric, i have come to believe that immutable resources are a much better alternative. i implore you to try it before you complain about its usability

cursive geyser
#

I can't wait for immutable resources

#

one of the most annoying things right now with Ender-Rift

#

is that I have to trust that people won't mess with the preview stack returned from getStackInSlot

stone dragon
#

Yeah that mutation confusion always caused me issues

cursive geyser
#

I used to have a .copy() in the method

#

but it was horrible in combination with AE2 storage bus

stone dragon
#

Luckily itemstack copying is now a lot cheaper so now I just defensively copy everything... but, well, not everyone is going to think to do that

cursive geyser
#

cheaper only in the case of a complex stack

stone dragon
#

And at that point, might as well use immutable resources and have it all be a lot simpler

cursive geyser
#

when the stack has no components, it's equal or slower

#

(the tag would have been null, but I think the component map isn't)

wind steppe
#

well i dont think the component map will ever be empty again lol

#

lots of stuff is part of components now

cursive geyser
#

in a stack of like, dirt?

#

with no custom name or anything else?

#

isn't the count outside components still?

wind steppe
#

isnt the stack size part of that now

#

yeah

#

like, stack limit i think is a component ? tho idk if thats set, it might just be defaulted

cursive geyser
#

yes but it's not omnipresent in every stack

#

the default comes from the Item

#

a basic item/block stack should, in principle, contain zero components

wind steppe
#

well all changing the max stack size does is change the component

#

i think

cursive geyser
#

it's an Integer component

#

setting a different max stack size on one stack, just .put()s a value into the component map

#

let me confirm

wind steppe
#

hmm okay so

#

it really wouldnt be difficult to provide a default implementation of storage snapshots. its actually alot easier with the immutable resources cuz you dont have to copy anything

#

could do like, SimpleItemTransactionParticipant whose snapshot is just the exact same class that forge provides for default item storage anyways

#

idk, im going to continue the PR without transactions for now, but id like to revisit this at some point

stone dragon
#

Considering transaction seems like a separate concern. I'd like to too, but that can be another PR

flat root
#

it's just impossible to default-implement transactions

#

even with a limited subset

wind steppe
#

yeah itd be impossible to have that, but it would be possible to just say 'if your storage is simple, just use our provided storage class that handles snapshots for you'

flat root
#

how do you abstract

try (var tx = Transaction.openOuter()) {
    var extracted = storage.extract(water, 1000, tx); // does a simulation under the hood
    // by now you are guaranteed that extracted is usable
    tx.commit(); // oops, non-simulated extraction actually returned less; resource duplication??
}
#

this is just one simple example in case the simulation is not actually trustworthy (unfortunately this is a real issue) 😛

wind steppe
#

im not sure what you mean here

cursive geyser
#
IItemHandler { beginTransaction() } // no default, you have to choose
abstract ItemHandlerBase implements IItemHandler {} // no default, you have to choose
final SimpleItemHandler extends ItemHandlerBase {} // defaults to a default transaction processor
flat root
#

giga I'm afraid that the transactions you are thinking of are extremely limited and of little practical use

wind steppe
#

what hes thinking about might be enough for just itemcontext? not sure

flat root
#

a transaction is an operation that spans the entire set of storages; it's not just a fancy builder for a series of individual operations

cursive geyser
#

I literally have no use cases for transactions, Tech

#

so I don't know what people's use cases are

#

so my default thinking, is how I have seen database transactions

#

any deviation from that is an implementation detail

wind steppe
#

giga can you link your handler class?

cursive geyser
#

you still have to implement some "transaction provider" for your inventory

#

somehow

loud stirrup
#

I am realistic in that I don't believe the ecosystem is going to accept the need to recode every inventory under the sun

flat root
#

obviously

#

which is why we need interop with IItemHandler for a while

cursive geyser
#

so pretend that my pseudocode works in whatever way is necessary

flat root
#

and that directly excludes transactions

cursive geyser
#

IMO, it is possible to have standard item handling and transactions coexist

#

and IMO, we need to keep it like that

loud stirrup
#

hm?

#

how do you deal with the inevitable dupes

flat root
#

the "implementation details" are where all the problems with transactions come from 😛

wind steppe
#

yes its possible to have someone use storage in the same way they were using it before, but not without changing how the storage class is written. its impossible

loud stirrup
#

every implementor of a transactional transfer interface has to deal with snapshotting their world-state

#

and in many cases, they need to transitively snapshot stuff

cursive geyser
#

oh and

cursive geyser
wind steppe
#

its exactly how fabric works and works fine

cursive geyser
# flat root see this message

I have seen this message. I don't think the transactions you think of are viable without me wanting to quit modding.

#

that's why I really want a fallback that tells transaction users "sorry no can do."

wind steppe
#

but then devs like commoble will get an infinite amount of reports of 'your storage doesnt work properly with x pipe mod, and they say its on you'

cursive geyser
#

it will work correctly

#

so long as the pipe is ok with only inserting one stack at a time

serene igloo
#

the pipes literally only insert one stack at a time

cursive geyser
#

if a pipe requires inserting 2 things at once, it won't work

#

and that's fine.

wind steppe
#

thats not really the problem

flat root
#

this type of "lower common denominator" approach sucks

cursive geyser
#

I'm modding for fun, if transactions are mandatory, it would suck out all the fun of working with inventories.

flat root
#

because every inventory that wants to use proper transactions will also need to support partial transactions

#

so nobody can actually rely on transactions

cursive geyser
#

what do you mean with "partial transactions"?

#

here's my situation: ```
begin transaction
inventory1.insert
inventory1.insert // can't satisfy this. the insert is rejected (inserts zero items)

#

(I mean I could, I jsut don't want to implement the logic that would be required for me to do that)

wind steppe
#

i think theres a world where i could make a builder like system work in a transaction environment

cursive geyser
#

it doesn't matter if it's a builder or not, though

#

that's not my issue

wind steppe
#

whats ur issue then

#

cuz implementing this would also require you to defer unrollbackable changes to a final commit method. it just wouldnt need you to handle your own rollbacks

cursive geyser
#

the fact that implementing transactions requires one of:

  1. the ability to track system state in parallel to the real system state
  2. the ability to roll back changes that have already happened in-world
wind steppe
#

well what if the changes havent happened in world yet

cursive geyser
#

then you have option 1

analog sapphire
#

option 1 seems unavoidable since it's the definition of a transaction

cursive geyser
#

yes, and I'm asking for a "simple single-operation transaction provider" that automatically rejects any insert/extract after the first one

analog sapphire
#

the problem is that any mod that uses that will simply not function with advanced transaction consumers

cursive geyser
#

or an API that makes implementing this take minimal brainpower and time

#

I'm okay with that

wind steppe
#

which i think could happen, but yeah would inherently limit what one could do with your container

cursive geyser
#

if someone MUST do multiple operations on my inventory at once, sorry it wont' be compatible

#

their mod won't work with mine and that's fine with me

#

they are free to blame me

#

I will simply tell people "no time or energy to implement this, sorry"

wind steppe
#

well

#

hang on, read this

cursive geyser
#

because the alternative is that I still have no time or energy

wind steppe
#

is this what you have in mind

cursive geyser
#

so I just stop porting my mod

loud stirrup
#

I am not really suring what you are arguing for or against ghz

#

I think techs and my point is: supporting transaction is a whole-world affair that you can't get around. so let's not do that in NeoForge

hidden geode
#

Agreed

cursive geyser
#

I will repeat my original message

#

if transactions become a thing, please have a fallback that means modders don't have to implement transaction handling in every custom inventory

wind steppe
#

what you're asking for isnt possible. if transactions exist, something must change in the storage

cursive geyser
#

I want to implement insert/extract, and have everything else done for me

#

which, given my inventory makes in-world changes on insert/extract

#

means I won't support multiple operations on my inventory during one transaction

#

and I won't support multiple transactions happening at the same time

loud stirrup
#

What you're talking about is more or less batch operations against a single invenotry as an optional operation

wind steppe
#

the second your storage makes an in world change it cannot be rolled back

#

so you have to change how you implement insert/extract

#

which then leads us back to the issue at hand

loud stirrup
#

well yes, but you can offer an optional batch operation method adjacent to it if you can satisfy it fully internally

#

but it has quite the narrow use case

analog sapphire
#

is there any advantage to optional batch operations besides performance?

loud stirrup
#

Simulating against multi-slot inventories

#

that have internal logic related to mutually exclusive content

cursive geyser
#

for reference, I'm not htinking of batch operations as the use case for transactions

#

the only use case I can imagine for them, is a "crafter" type machine (processor or whatever)
where you insert 1 of A,B,C, and then get D in return, so you check if you can insert A,B,C all at once, or cancel if not

loud stirrup
#

compressing drawers

#

simulate extraction of (10 iron ingots, 90 iron nuggets) in face of a compression drawer containing 10 ingots

#

but the use case is VERY limited

#

since you cannot propagate this upwards

#

other cases are machines with multiple input slots (imagine slot 1, 2, 3) where slots 2 and 3 accept ingredients based on the content of slot 1

wind steppe
#

batch operations work in most cases of itemcontext :P. which is the main thing im having issues with rn

cursive geyser
#

is there some documentation of the transaction system in fabric that I can take a look at, at some point?

#

and if so, does it have a "minimal viable product" implementation to show how much boilerplate is required?

loud stirrup
#

the core concept of fabric transactions is that your impl needs to support taking and restoring snapshots IIRC

wind steppe
#

All transactions require you to do is the following:

you tell the system when a change is about to occur to the container and give it a snapshot of its current state
you tell the system how to restore its state from a snapshot
you tell the system what to do when the changes should be finalized

and then you also have to make sure you do not prematurely cause in world changes

cursive geyser
#

yeah I think it would still require 60%+ of the core feature of my mod to be rewritten

#

which I'd have a hard time setting aside time for

wind steppe
#

understandable

#

itd be a big change for most people, and transactions arent the most straightforward thing to understand

flat root
wind steppe
#

i had it on hand lol

#

i did a bit of research before diving into this xd

flat root
#

I feel like I did a decent job at explaining but it's probably still too technical

wind steppe
#

yeah its very overwhelming understanding it

#

i dont understand something tho, i feel like a builder system would be alot easier to understand for devs, and you could probably fit a transaction system on top of a builder thing

#

you would just have to have something in the builder that allows you to make another builder from the builder's current state

flat root
#

challenge for you: implement CombinedStorage using a builder system

#

(I don't think it can be done)

wind steppe
#

i would overlay the transaction system over it and simply use that 😄

#

the builder system wouldnt be required, itd just be an optional way to make it easier for you to make a transactional storage system

flat root
#

so, the builder would only be used by implementors of storage?

wind steppe
#

pmuch

flat root
#

I doubt that it would be shorter than using SnapshotParticipant at least

copper fjord
#

didn't fabric have a transfer-debating channel or whatever.. at this rate we need one too

flat root
#

this is the channel, maty

wind steppe
copper fjord
#

you don't get the point

#

a dediacted channel instead of a hidden thread smh

flat root
#

no need 🤷

flat root
# wind steppe itd eliminate the first and second step of this. because builder would be made r...

how would you port this function to a builder system?

    @Override
    public long insert(T insertedVariant, long maxAmount, TransactionContext transaction) {
        StoragePreconditions.notBlankNotNegative(insertedVariant, maxAmount);

        if ((insertedVariant.equals(variant) || variant.isBlank()) && canInsert(insertedVariant)) {
            long insertedAmount = Math.min(maxAmount, getCapacity(insertedVariant) - amount);

            if (insertedAmount > 0) {
                updateSnapshots(transaction);

                if (variant.isBlank()) {
                    variant = insertedVariant;
                    amount = insertedAmount;
                } else {
                    amount += insertedAmount;
                }

                return insertedAmount;
            }
        }

        return 0;
    }
#

(this is just the standard SingleVariantStorage#insert implementation)

wind steppe
#

the storage class that gets passed to the capability would be a wrapper around a builder system. The builder itself would have the modify methods but also methods to read its current state (getResource and getAmount). The wrapper would store a builder by created from the builder storage. when updateSnapshot() is called, it stores a new builder built from the stored builder as the snapshot, and when rollback happens it it just overrides the original builder with the snapshot one. basically this method body would just be replaced with updateSnapshot() and return builder.insert()

#

technically speaking this would make the snapshots mutable but uh, making it not mutable would further complicate it and i think the goal is to try and make it easier for devs trying to implement storage

flat root
#

not sure I understand

wind steppe
# flat root not sure I understand
@Override
public long insert(T insertedVariant, long maxAmount, TransactionContext transaction) {
    updateSnapshots(transaction);
    return this.builder.insert(insertedVariant, amount, transaction);
}

public BuilderImpl createSnapshot() {
    return this.builder.builder();
}

public void readSnapshot(BuilderImpl snapshot) {
    this.builder = snapshot;
}

public void onFinalCommit(TransactionContext transaction) {
    this.builder.commit(transaction);
}

Then the builder would have the insert/extract methods, methods to see whats currently in it, and a method to create a new builder from its current state. this should be easy for most devs, they just have to pass around a copy of the list they are likely using for storage. But this basically takes the worrying about what the current state of the storage out of the hands of the dev, and makes it so devs dont have to worry about the current state

flat root
#

hmmm

wind steppe
#

idk, anti transactioners will probably still not like this solution, but i think its easier to manage :P

#

and if you wanna do what giga said about failing after the first insert to avoid having to implement a method to copy the builder, you just make the builder copy method return a no ops builder :P

flat root
#

idk if it's easier to manage

#

in any case we're almost surely not doing transactions for 1.21

#

they can come later if we want to

wind steppe
#

if they come later i dont think they are happening at all

#

thatd be a second breaking change to storage

loud stirrup
copper fjord
#

that's half of my point

wind steppe
#

hey all things considered, this wasnt that bad

#

no idea what went down in fabricord tho

loud stirrup
#

Well, bikeshedding 😄

cursive geyser
#

I don't like the term bikeshedding as it exists now

#

because whiel it does mean something deep down

#

people tend to throw it around to refer to stuff they don't care about

flat root
#

oh you have no idea how bad it was

cursive geyser
#

(note: I'm not saying it wasn't bikeshedding there -- it probably was. I just don't like the term XD)

flat root
wind steppe
#

it is a breaking change for item caps

#

quite a large change for them actually

flat root
#

yes

wind steppe
#

@flat root with this, is there not a risk that the fluidstack that the resource was created from could be modified and therefore modify the resource itself? shouldnt it be copied?

#

ah it is

#

mb

wind steppe
#

Hi o/. Me again. So there has been some debate about how Energy might be modified a bit to adapt to the new handler change. Currently its its own class. This is great for simplicity as energy is a pretty simple concept. But some feel that energy is already treated pretty similarly to fluids and items and that it would be incredibly useful to have a single storage class be universally used for all transfer. So EnergyStorage would be converted to IResourceHandler<UntypedResource> or (more likely) an interface that extends it, ie IUntypedResourceHandler with the resource removed from the extract methods. Some default interfaces can be provided so there is little to no burden on migrating, the methods could all be made to be relatively the same (ie, there would be an ISingleUntypedResourceHandler that basically looks exactly like the existing energy storage).

The major concern though is that while it would be relatively trivial for a dev for a dev to implement this, it does add a bunch of junk that 99% of energy handler implementations wouldnt use.

While technically this does overcomplicate the base class, it does technically simplify transfer over all, by making it so developers can completely handle all transfer of all storage with the same methods (with some even provided in a todo TransferUtil class).

Feedback would be really helpful here. I know that @vernal dust felt strongly about this, itd be super helpful if you could touch upon why you would find this change useful

#

while the suggestion here does suggest using UntypedResource or something like that, using the generic handler class could open up something else entirely even, like perhaps making an EnergyResource class that, by default, just uses the Forge provided one in the default interface but could allow others to implement their own energy storage in a way that is compatible with only their energy while still allowing others to make energy pipes that

#

IResourceHandler<IEnergyResource>, by default everyone uses IForgeEnergyHandler which is just the handler but its isResourceAllowed method only accepts ForgeEnergyResource, but a mod like FTB Contraptions could make it so they only accept ZapsEnergyResource or something. If a mod wants to make it so their handler accepts all energy but stores it as something else, they can convert the energy in the insert method and store it as something else.

#

okay scratch the whole untyped resource thing, i really like this IEnergyResource idea. <- This was pup's idea btw, not tryna take credit for something i didnt come up with

sinful tiger
#

what would be the contents of the interface

wind steppe
#

which interface

#

if you mean the energy one, not sure tbh. What was said so far was a localized name, a component and maybe a color

#

Oh, and a conversion rate from whatever the resource is to FE. That way all mods could universally be converted to FE if there is a mod that creates a way to convert it

cunning mulch
#

(Optional way to convert, should definitely be able to explicitly mark as not supporting conversion)

sinful tiger
#

yeah, consider that in principle the energy interface could be used for, like, ember or botania mana [no idea whether those mods would actually do so]

#

hmmmmm

#

the talk of a color makes me think

#

what about a color and a texture - and a way for a resource pack to define the color and texture for FE for mods that don't otherwise have a preference

#

though

#

hmm

#

there's a tension there between mods that want to have their own name and appearance for things but want to still be compatible with everything, mods that want to keep their stuff in their own ecosystem and at most convert to FE so other mods they don't care about can be powered by it, mods that just want to have basic energy using stuff

#

a resource type does open the door for stuff like pipe networks to carry multiple resource types without converting them

#

like, maybe what the interface needs is, like, an "advisory FE ratio" that's mandatory just so that containers know how to size themselves [i.e. if it's 100, a container that holds 1M FE should only hold 10k of the other unit], and then a set of flags to define whether conversion in either or both direction should be allowed at all

#

oh i've invented fluid units per bucket haven't i

#

i do wonder if there's any scope for an energy system to define stuff like electricity or heat or air pressure that have their own characteristics like voltage, amperage, thermal conductivity, volume, passive transfer to equilibrium with transfer rate depending on the difference between each side's pressure/temperature

#

probably too many variables to get right in a fully parametric way

void thicket
#

Such a system would require treating Energy as a concept (i.e. like an Item or Fluid) instead of a primitive as it is now

#

Conceptually that isn't a horrible design, but it gives the false impression that multiple Energy systems are compatible

hollow maple
wind steppe
#

Not all things that call themselves energy are compatible, which is fine. But it does make it easy for mods that want to use energy but dont necessarily want to use energy produced by other mods to stick to their own flavor of energy while still being able to use the networks

hollow maple
#

And a lava bucket, which smelts 100 items, would have 100000 joules stored in it

#

(this is, of course, assuming standard fuel times)

#

Alternatively, we can go based on the hunger system, but I can't think of a more natural way to equate that to something the player would be familiar with

flat root
#

I am against changing energy because it overcomplicates it for 99% of its users

wind steppe
#

cringe

flat root
#

Scope creep is cringe 😛

wind steppe
# flat root Scope creep is cringe 😛

but energy resources arent cringe. cmon, itd be cool. I dont think energy is so conceptually different from the other storages that it couldnt be the same as the others. Most tanks devs implement are single slot despite the api expecting a multi slot container, we should just do the same here. and then it allows for the cool pipe thingy i talked about!

flat root
#

Idk who it helps

#

I sure know who it annoys (= everyone) 😛

#

What is a simple getAmount() turning into?

wind steppe
#

it helps devs who want to have their own confined energy generation but not be punished severely for not being compatible with others.

flat root
#

???

#

In MI I just reuse IEnergyStorage with a different cap

void thicket
#

Energy is fundamentally not a resource

wind steppe
#

then itd help you

void thicket
#

it doesn't have slots, it doesn't have components, it doesn't share any of the semantics of items/fluids

#

it doesn't even "exist"

#

its just a typeless count

wind steppe
#

energy isnt a resource because we all accept energy as a monolithic system and say everything that is energy-like or a different kind of energy isnt energy. well thats dumb

flat root
wind steppe
#

because your energy has to exclusively be transmitted through your mod

#

a different mod could have methods of transferring and storing generic energy. that energy could still be used exclusively with your mod, but itd still work with other things that are designed to store energy

flat root
#

If I wanted that I would have used the IEnergyStorage cap

wind steppe
#

well you dont want other mods who generate energy making energy that your machines can use

#

that would still be the case

#

MI would only accept MI branded energy

flat root
#

Although... If mods actually supported multiple caps they could support multiple energy storage caps

wind steppe
#

but pipes can transfer all brands of energy

flat root
#

Oh god please don't tell me you want multiple energy resources supported by the same storage

wind steppe
#

nah, itd have to be converted first

flat root
#

So that's literally no change compared to the current system?

wind steppe
#

well i guess technically you could make it accept all types, but you wouldnt be able to extract all types. though i guess you could but thatd be confusing

flat root
#

Every change is actively trying to make the design worse it's painful to see lol

wind steppe
#

shush i like this change

flat root
#

These discussions are exhausting

wind steppe
#

this is the one change that didnt give me a headache

flat root
#

That doesn't mean it's a good one

wind steppe
#

but its cool!

flat root
#

Yes, cool and bad in practice are not mutually exclusive

wind steppe
#

this guys so boring smh

flat root
#

In fact, APIs that get built just because they are "cool" are likely to suck

void thicket
#

energy is not a viable surface for this resource system

flat root
#

<rant>
Yes I'm boring because I already designed a full transfer API and every thing you do differently compared to that is an option that I considered and then rejected already

void thicket
#

It supports non-primitive counted objects with different subtypes

#

Energy is... none of those

flat root
#

And at some point it gets tiring to argue every single aspect of the design again every week

#

Every time I read this thread or FC after a long discussion I have to witness and try to address the damage done to the API

#

It's generally been fine but that's the mindset I have every time...

#

"what was discussed this time and how did it affect the API"

#

</rant>

void thicket
#

(syntax error, no matching <rant> tag found in content body)

flat root
#

Fixed 😛

wind steppe
# flat root <rant> Yes I'm boring because I already designed a full transfer API and every t...

i was mostly poking fun at you, but i get what you're saying. it would have been cool to do this, if onlu just to make energy unified with everything else. the conversation in general started when i brought up the idea of a generic integer storage class. a typeless handler was proposed instead, and then how fluxnetworks stores its stuff with types for energy was something pup brought up that i thought was a cool idea. but yeah maybe wouldnt work here.

flat root
#

I know you were joking. Nonetheless, these discussions do get tiring 😛

cunning mulch
copper fjord
#

no excuses

flat root
#

ah pup; why did you suggest ITransferHandler 😦

#

IStorage is clearly the superior option!

cunning mulch
#

Because storage makes no sense for a handler as it might not actually have storage and just be say throughput or voiding etc

flat root
#

that's a special case which is still fine to represent as an IStorage

#

it is just an empty storage

cunning mulch
#

Doesn’t make as much sense compared to something that handles a given type. Which is much more clear and fits better

flat root
#

Handler is a really vague name. It only makes sense because we are used to IFluidHandler and IItemHandler

#

IResourceHandler<ItemResource> leads to a duplication of the word "resource" it's just annoying

cunning mulch
flat root
#

and IHandler is just bad

flat root
cunning mulch
#

IHandle<Resource> is also reasonable. But either way in your impl you then can name it whatever you want to avoid the duplicate names for when you are referencing things

flat root
#

like... I don't get any update or anything and suddenly the core class of the PR was renamed

#

IHandler and IHandle are horrible names in isolation

cunning mulch
#

Hence the resource part

flat root
#

yes but that is a bad name when compared to a resource 😛

cunning mulch
#

IResourceHandler<?> is fine

flat root
#

from the POV of the caller it doesn't matter that some IStorage may or may not actually have some storage

cunning mulch
#

From an implementer though it makes no sense

flat root
#

even a trash can would typically have at least 1 slot, and many pipes also have a concept of slots

flat root
#

it is still an object that can store resources

#

whether it stores them internally, or delegates storage to something else, or never stores anything... it is still something that stasfies the "IStorage" concept from the outside

cunning mulch
#

Because that isn’t as clear? Also think day item caps. Those are pure handlers the storage is a data component not part of the actual handler

flat root
#

well you get an IStorage and you can get its contents and change them

#

it doesn't matter what the backing implementation is

#

the API gives you access to the storage capability regardless of how the resource actually gets serialized under the hood

cunning mulch
#

The api gives you access to a capability to handle the resource type

flat root
#

handle is just vague

#

it literally means "we couldn't find a better name so we chose this"

#

even IEnergyStorage is not called IEnergyHandler

#

I don't think that anyone ever complained about IEnergyStorage possibly not "storing" the contents?

cunning mulch
#

It should be! I use handler for all my energy stuff and didn’t even remember the base one was called storage till someone brought it up as I only interact with it in two classes

flat root
#

yes because you are used to IFluidHandler and IItemHandler

#

if you were used to IFluidStorage and IItemStorage it would only seem natural 😛

#

I have also been confused by IEnergyStorage after dealing with fluid and item handlers

#

a bad argument would be that we don't want to make too many breaking changes and therefore should keep IEnergyStorage

wind steppe
#

i changed energy storage to energy handler already bro

flat root
#

pfff you really want to make me ragequit this

cunning mulch
wind steppe
#

we already broke all the methods in the energy storage class, breaking the name is not a big deal lol

flat root
#

Every change is actively trying to make the design worse it's painful to see lol
as usual

flat root
#

getting rid of receive is a large improvement

wind steppe
#

i think sara has a point on storage not necessarily being the best term for everything this api encompasses

cunning mulch
#

xlurk there is no h in my name

wind steppe
#

oh what

#

sorry

flat root
#

names with multiple spellings stabolb

wind steppe
#

i coulda sworn there was an h dontfeelsowaaaaaaa

flat root
#

I'm not happy about IHandler and I'm not happy about IResourceHandler

#

can we find something better?

wind steppe
#

Im still a fan of ITransfer :P

flat root
#

that is really bad because it doesn't follow naming conventions 😛

wind steppe
#

yeah speaking of, no one answered my question when i asked

#

what is proper naming convention for I interfaces

flat root
#

hmm?

#

IWhatever

wind steppe
#

cuz the I makes it sound like it should be a sentence. otherwise why include the I at all

flat root
#

the I stands for interface

cunning mulch
flat root
#

noooo

cunning mulch
#

but trying to get something better yet could work if we can think of something

flat root
#

Certainly! Here are some additional possible names for a Java interface that can store and transfer resources:

ResourceManager
ResourceStorage
DataStore
ResourceTransporter
StorageHandler
ResourceDepot
DataHandler
ResourceRepository
StorageManager
ResourceCarrier
DataTransfer
ResourceService
StorageInterface
ResourceBroker
ResourceCache
StorageUnit
ResourceHolder
TransferService
ResourceExchange
StorageFacility
#

so bad 😄

wind steppe
#

i did the same thing

cunning mulch
flat root
#

I think that "handler" should be banned 😛

#

the question is: what are the resources being extracted from? what are they being inserted into?

#

and there you see how I came up with Storage

#

it also makes more sense in high-level operations such as "move resources from one storage to another"

wind steppe
#

Well, what things arent storage that shouldnt be? like, a void or something? Basically anything that doesnt physically have a number of slots to put things in. But those containers are still being made to return content reading methods. So while they arent storage, they effectively present themselves as one?

cunning mulch
#

capabilities define how not what

#

you should know this. You are the one who split caps and attachments

flat root
#

the fact that it's a capability isn't too important

#

this is just the more fundamental concept of an interface / object

#

you are too worried that people will confuse this with data storage

#

we can call it a store maybe (the original fluidity name) but that is obscure english

wind steppe
#

i mean, sticking with the vanilla IContainer or IInventory would fix this lol

#

you cant confuse vanilla behavior

flat root
#

what is the difference between a container and a storage? 😛

#

I think that Container vs IContainer would be quite confusing

wind steppe
#

would it? they are both effectively the same thing so itd be fine imo

flat root
#

hmmm yea

wind steppe
#

IResourceContainer then?

flat root
#

logically IContainer is a generalization of Container

#

no resource! only container! 😛

#

imagine IHolder

wind steppe
#

IBox is perfect! lmfao

flat root
#

they are all quite funny

flat root
#

I would actually say that capabilities define what an object can do rather than how it does it

#

this is just about API (what) vs implementation (how) separation

#

obviously the fact that we only have 3 capabilities and are reworking them doesn't really help us with a precedent

wind steppe
#

we need more capabilities!

#

:)

flat root
#

so let's see what AE2 does... MEStorage, ICraftingMachine, GenericInternalInventory, IInWorldGridNodeHost, ICrankable

wind steppe
#

it does seem odd that storage was the only usecase anyone ever introduced for caps

#

seems so versatile

flat root
#

it's too versatile for most use cases 😛

#

mekanism has a bunch of handlers, IConfigurable, IAlloyInteraction, IConfigCardAccess, IEvaporationSolar, ILaserReceptor, ILaserDissipation, IRadiationShielding, IRadiationEntity

#

I don't like the naming convention that is used here 😛

#

it seems weird that a block entity would be an ILaserDissipation

cunning mulch
#

armor is, blocks are laser receptors

wind steppe
#

what else would it be? ILaserDissipatable or something? lol

flat root
#

maybe ILaserDissipator

#

and for example IRadiationShield

cunning mulch
#

Aidan made that one

#

Blame him

wind steppe
#

classic aidan

flat root
#

lol

#

I'm not blaming someone or saying that you should rename all caps in mekanism

wind steppe
#

speaking of mek, lets add a unified heat api to neo >:)

cunning mulch
#

Oh god no

flat root
#

I'm just saying that these names are not what I would have chosen, based on how I usually name interfaces

wind steppe
#

so @cunning mulch , does IContainer fix the implication of data serialization by making it more in line with how vanilla communicates storage or do you think we should keep looking for a clearer name

cunning mulch
#

Isn’t a vanilla container the thing for menus or was that the mcp name and I am getting confused

wind steppe
#

Container is the name of the item storage class

#

Inventory extends that class

flat root
#

let's find out

#

<@&1128776937410670663> here is a little poll for you 🙂

copper fjord
#

jeeez how many options

random fractal
#

I mean, I chose two of them

flat root
wind steppe
#

oh god transfer handler has 4 votes

random fractal
#

Personally, I don't want the vanilla names, and item handler is a storage and transfer component in one

flat root
#

well the good thing is that we will easily see which options people do not want

random fractal
#

Fair

random fractal
#

Just put it in the acknowledgements

copper fjord
queen wagon
flat root
#

I included every option I could think of 😄

distant merlin
#

just to be clear, when we say "the same role as IItemHandler", that refers to its roles as allowing observation into its contents and maximum capacity (getSlots, getStackInSlot, getSlotLimit, isItemValid), and transferring contents in and out of it (insertItem and extractItem)?

flat root
#

yes

#

I/O, current contents, and metadata (capacity, valid items)

vernal dust
#

Not to add a second unrelated fire, but I did want to discuss IColorApplier/Handler/Provider at some point

#

Which gives you a non-transfer/storage name to argue about

flat root
#

IColor and UGetColored obviously

silent root
#

UGetStuff<ItemTank>

unborn terrace
#

IResource.Handler

wind steppe
#

Hmmm

waxen dawn
silent root
#

IResource.Handler/Provider/Reciever

paper merlin
#

For SFM (SuperFactoryManager) I've abstracted item/fluid/forge energy/mek gas/mek infuse/mek etc
ResourceType.java

SFM is a mod that adds a domain-specific language for moving stuff around

EVERY 20 TICKS DO
    INPUT minecraft:stick FROM chest
    OUTPUT TO furnace BOTTOM SIDE
END

is internally

EVERY 20 TICKS DO
    INPUT sfm:item:minecraft:stick FROM chest
    OUTPUT sfm:item:*:* TO furnace BOTTOM SIDE
END
public abstract class ResourceType<STACK, ITEM, CAP>
and
public class ItemResourceType extends ResourceType<ItemStack, Item, IItemHandler>
public class FluidResourceType extends ResourceType<FluidStack, Fluid, IFluidHandler>
public class ForgeEnergyResourceType extends ResourceType<Integer, Class<Integer>, IEnergyStorage>
public class GasResourceType extends ResourceType<GasStack, Gas, IGasHandler>
...

(this is indeed a little awkward with a single-key resource type of sfm:forge_energy:forge:energy)

GitHub

Contribute to TeamDman/SuperFactoryManager development by creating an account on GitHub.

#

transaction/type stuff with the simulate gets a little weird because poeple want me to add a feature where they only perform an action if the entire action will succeed.

Conside a gear press

INPUT FROM chest
OUTPUT EXACTLY 4 iron_ingot -- "exactly" is a not a real SFM feature yet

currently, this could be done by simulating an insertion of 4 iron_ingot into every slot (simulate) until all ingots have been accounted for, not following through with a non-simulate if any remain

#

however, if someone wants to do

OUTPUT EXACTLY 16000 cobblestone, 4 iron_ingot, 16 diamond, 256 gold_ingot TO super_duper_crafter

then the transaction stuff gets a lot more complicated

#

if there's one free slot, a simulate insert for two different stacks will pass for both

#

barrels are also weird, varying by implementation the stacksize limit adds a lot of complexity

#

a barrel may be implemented by showing a stack of 10,000 when getStackInSlot, only returning 64 when extract-nosimulate

#

if someone wants to pull 100,000 cobblestone out of a barrel but the barrel is throttling by 64 per extract that's not a great feel

#

IResourceHolder<ItemStack> would probably be what would fit with my naming for a generic IItemHandler

silent root
#

So your needing help with an API that isn't forge? I'm confused if the snippets are for a script or ?

flat root
#

interesting structed transfer language

silent root
#

Would be cool if Lua was used, as that I believe would allow for more flexibility

#

And it's easily integrable with Java

paper merlin
#

not that I need help, more just insight into my mod which has some more nuanced interactions with stuff due to being an item movement mod

silent root
#

Ah

wind steppe
paper merlin
#

slot is usually still important, what about a furnace where there's a fuel,input,output slot and a minecraft:oak_log is a valid input and fuel
is there a way to differentiate still?

#

I guess it just depends on what direction you used to get the capability?

wind steppe
#

Storage will have a slot dependent and slot agnostic insert/extract method

#

So you can do both

paper merlin
#

neat

sinful tiger
#

[big reactors has graphite by smelting charcoal]

paper merlin
#

another thing that comes up is trying to enumerate the slots provided by a mod, there's no mapping from insertIntoSlot vs menu slot indices

#

all the slots in the mekanism machines are created such that the slot index is zero

            // draw index on each slot
            for (var slot : menu.slots) {
                int colour;
                if (slot.container instanceof Inventory) {
                    //noinspection DataFlowIssue
                    colour = ChatFormatting.YELLOW.getColor();
                    inventorySlotCount++;
                } else {
                    colour = 0xFFF;
                    containerSlotCount++;
                }
                Minecraft.getInstance().font.draw(
                        poseStack,
                        Component.literal(Integer.toString(slot.getSlotIndex())),
                        acs.getGuiLeft() + slot.x,
                        acs.getGuiTop() + slot.y,
                        colour
                );
            }
sinful tiger
#

might be worth looking at, but not really fundamentally transfer api related

#

for your thing for now i'd just check instanceof SlotItemHandler and access the index field by reflection

#

[there's no guarantee that for any arbitrary gui that'll work at all though]

wind steppe
sinful tiger
#

....why does it have two getters?

#

regardless, this doesn't change what i said, it's not useful on SlotItemHandler

#

also it's super confusing that the Slot.index field [index within the Menu] and the SlotItemHandler.index [index within the IItemHandler] have the same name

wind steppe
sinful tiger
#

is one of them a patch

#

yeah getSlotIndex is a patch

#

could probably be cleaned up

wind steppe
#

Slot item handler is a mess lol. I got rid of the duplicate index thing on the new version of SlotItemHandler and got rid of the very cursed handling for getMaxStackSize

#

Doing this PR there is so much legacy garbage that has been fun to sort through

flat root
#

Can you make a Slot backed by immutable stacks? 🙂

loud stirrup
#

Especially for custom slots

wind steppe
flat root
#

Vanilla Menus can mutate the stack

#

(unless we patch the menu code of course)

wind steppe
#

I didnt notice until now, most of the storage things are still written to use INBTSerializable. Is this how the new templates should be written? So far ive just been using data attachments and data components

flat root
#

INBTSerializable is kinda legacy

vernal dust
#

Just gonna point to 1035 >.>

waxen dawn
flat root
#

Huh?

waxen dawn
#

nvm 😅

wind steppe
wind steppe
#

@flat root So Shadows' PR introduced new component based storage stuff, but it uses MutableDataComponentHolder to mutate the stack directly, which is bad with IItemContext. I had written a template using IItemContext already, but im curious as to what should be done with this new thing. Cuz people will get confused seeing 2 different containers that both apply to items but only 1 should actually be used with it

flat root
#

well yeah I'd yeet shadows' stuff 😛

#

or rather, port it to IItemContext

waxen dawn
wind steppe
#

I dont get it, why is this done this way? the if checks are kinda useless. fluidType.getBucket() works on water too

sinful tiger
#

i think the code is just old

#

"call getBucket" almost certainly used to be "construct universal bucket"

#

which hasn't been a thing since almost certainly 1.12

wind steppe
#

Whats the standard we wanna go in for registries in constructors. Like, Shadows' stuff uses the bare DataComponentType, other stuff uses a supplier of DataComponentType, whats the standard

hollow maple
#

If it needs the ID, take a Holder; otherwise take a Supplier

#

DeferredHolder can satisfy both interfaces so there's no problem with it; the worst case is that people just have to add an additional () -> value

distant merlin
#

I wonder if we should start slowly moving things towards Holders

swift summit
#

frankly, it depends on whether it needs to be lazy

cunning mulch
hollow maple
#

blegh, shift key mispress sadge

#

I used to be "use holders wherever possible" but I've softened a little bit after the registry rework because DeferredHolder is way more useful now

cunning mulch
#

Yeah hence my comment of evaluate if it is better for new APIs and for existing ones only do so if mojang changes stuff we are interfacing with in a way holders make more sense

void thicket
wind steppe
#

Why does forge have this arbitrary slot limit for armor? as far as i can tell vanilla does not implement this kind of restriction

opaque vector
#

64 stack chestplate?

#

: concern :

wind steppe
#

well the stack limit for chestplates is 1, so that wouldnt be a problem

#

but it kinda sets an arbitrary limit that anything in the armor slot has to be not stackable

silent root
#

Beacons on the head is possible

wind steppe
#

oh good idea, let me test with something stackable in game for a sec

#

it might be a restriction they set somewhere else, like at the gui level or something

#

ah yeah, i stand corrected, only 1 allowed

silent root
#

I have a mod idea, Activated Beacon on the head, have a beam shoot out of the players head

silent root
wind steppe
#

i feel like thatd just give you radiation sickness or something lmao

silent root
#

Lmao

cunning mulch
#

nvm just scrolled

sinful tiger
#

yeah iirc vanilla doesn't set the item type limits on the armor slots in the container either, just in the gui

wind steppe
#

interesting that neo accounts for the placement limitation but NOT the removal limitation

#

seems like no one bothered to update the handler when curse of binding was introduced

wind steppe
#

For the slotless extract/insert methods, what do you guys think about just having a default impl for it? like something like this:

default int insert(T resource, int amount, TransferAction action) {
  int inserted = 0;
  for (int index = 0; index < size(); index++) {
      if (TransferUtils.isEmpty(this, index)) continue;
      inserted += insert(index, resource, amount - inserted, action);
      if (inserted >= amount) {
          return inserted;
      }
  }

  for (int index = 0; index < size(); index++) {
      if (!TransferUtils.isEmpty(this, index)) continue;
      inserted += insert(index, resource, amount - inserted, action);
      if (inserted >= amount) {
          return inserted;
      }
  }

  return inserted;
}

default int extract(T resource, int amount, TransferAction action) {
  int extracted = 0;
  for (int index = 0; index < size(); index++) {
      if (!resource.equals(getResource(index))) continue;
      extracted += extract(index, resource, amount - extracted, action);
      if (extracted >= amount) {
          return extracted;
      }
  }
  return extracted;
}

Just so devs dont have to have the burden of implementing 4 transfer methods when most use cases just need this

#

could also just move this to a util method that has this logic

flat root
#

why two loops?

#

also, don't check for emptiness

wind steppe
#

once to only insert into already filled slots, then to go through empty slots

flat root
#

ah

#

you can remove if (!resource.equals(getResource(index))) continue; at least

wind steppe
#

ah thats true, thats redundant

flat root
#

I don't think the base implementation should be doing a stacked insert

#

that is for modders to override if they want to

#

we should have a stacked insert helper anyway

#

this is important to not break vanilla compat

wind steppe
#

wdym vanilla compat?

flat root
#

hopper into vanilla chest

wind steppe
#

im not sure how this is relevant tho. the handler wrapper around the chest can just override the method with different logic

flat root
#

idk, I think the core API should be as unopinionated as possible, and stacking into existing slots is already too much for me

wind steppe
#

thats fair

#

i can move it to a helper then

flat root
#

yes that would be very useful

#

another thing: cache the size(), don't query it for every loop iteration

wind steppe
#

ah true

paper merlin
# flat root I don't think the base implementation should be doing a stacked insert

If the method is slotless and the container has room for all the items, I would want it to deposit all the items.
Having it return some remainder and having to retry inserting until there isn't any is just tedium

while remainder != old_remainder do
  old_remainder = remainder
  remainder = insert remainder

If I care about avoiding default logic for fitting all the items in, then I'd use the slotted method and do it myself

void thicket
#

Yeah it should try to deposit everything

flat root
#

It will do that with a stacked insert too, that's not really relevant

#

Or rather: it shouldn't be if implemented correctly 😛

wind steppe
#

yeah, he was just saying that stacked insert is an opinionated way of doing it

#

the slotless insert method is supposed to be an insert method with distribution left to the mod author. it should try to insert all of the items if it can (which is what the javadocs will reflect when i write them)

#

stacked insert is one way, a mod like refined storage may choose to see if a slot for the item exists for it already, and if it doesnt make a new slot and insert everything into that one (which doesnt work exactly with stacked insert)

wind steppe
#

You know, vanilla for its sided containers has methods for telling whether or not an item can be inserted and extracted from a container. It might be nice to turn isValid(slot, resource) into isInsertable(slot, resource) and isExtractable(slot, resource) and do something similar.

wind steppe
#

I think i finished most of the wrappers for items and fluids, i was wondering, do we want to have the same wrappers for energy available as well?

flat root
#

I don't think that it's valuable to know if something can be inserted or extracted, since you can just try it

void thicket
#

Yeah that's the whole point of simulate

#

isInsertable/isExtractable will just end up running a simulate in the general case, I think

#

isValid is about "could this item ever go into this slot", not about the current state of the slot

wind steppe
flat root
#

It's useful in some base implementations

#

But it's debatable in the core API

wind steppe
#

i dont think it would hurt to add. i think most implementations of storage just return true for isValid, and most would return !resource.isBlank() for isExtractable. Just more info. If its too much of an implementation burden we could default it?

flat root
#

I think that anything that doesn't have a clear use case should not be added to the API

wind steppe
#

Im currently stuck ;-;. I cant run patch gen cuz the current version of neo gradle doesnt work on mac, and the latest version of neo gradle is broken with patch gen. anyone got any ideas?

opaque vector
#

yell at someone to stop releasing broken NG versions

wind steppe
#

who do i yell at

opaque vector
#

#maintenance-talk message

wind steppe
#

ah well it seems like orion will get it sorted soon, ill just work around it best i can till then

#

i have so many javadocs to write 😓

sinful tiger
#

that way our hopper implementation can call the method

wind steppe
wind steppe
#

Why is FluidUtil so... massive

sinful tiger
#

dealing with stuff like stacked empty buckets is hard? idk

#

getFilledBucket can probably be yeeted if it hasn't already, it clearly dates back to the old universal bucket API whereas now fluid types are responsible for providing buckets anyway, most of the rest i think has some usefulness though

wind steppe
#

alot of it is useful, but alot of it is just odd?

sinful tiger
#

i'm a bit skeptical of IFluidBlock and associated stuff [in particular, LiquidBlock doesn't implement it, nor do waterloggable blocks], and i've long said IFluidTank should be removed

wind steppe
#

i killed fluid tank, and IFluidBlock has been removed in a separate PR already

sinful tiger
#

there's probably some scope for cleanup in FluidUtil itself but a lot of it is pretty important

#

ah i'm still on 1.20.6

wind steppe
#

actually i think people were using ifluidtank, i might have to bring that back for the transition period ;-;

#

the documentation seems to imply devs should be using it as like... a representation of a slot? but only as a suggestion? its so weird

sinful tiger
#

FluidTank is very useful, IFluidTank is not so much

wind steppe
#

i have an example impl for a single slot fluid tank, so we're good on that front

#

odd that forge never provided an example for multi slot fluid storage?

sinful tiger
#

the big problem is it's not really reasonable for most purposes to require that the implementation you're interacting with implements IFluidTank, since IFluidHandler already has slotless insert and extract methods

#

and IFluidTank doesn't even subclass IFluidHandler

sinful tiger
# wind steppe odd that forge never provided an example for multi slot fluid storage?

iirc the problem is that multi slot fluid storage isn't that well defined, you've basically got three different use cases:

  • different slots that are for different fluids, and each one has its own validation function
  • second slot is used if the first slot is already full of a different fluid etc
  • slots are created dynamically i.e. tinkers construct smeltery
#

the fact that there's no slot targeting insert/extract functions means the utility is limited, if you want, say, your gui, to let you target a specific slot you have to implement your own handler on top of some other structure that lets you do that

#

maybe we should have an "aggregating fluid handler wrapper" that just takes a list of fluid handlers and concatenates their slots together, and you can provide that a list of single tank fluid handlers if you need them

#

(or maybe we should provide slot functions, idk - we've been fine this long without them though and it really does make things worse for tinkers construct and similar cases [another example is the create basin])

wind steppe
#

well storage is completely generic, there is a generic "aggregating resource handler" that would work with fluids. Since fluid uses the same handler as items, they also have both slotless and slot aware insert/extract functions. there are also slot aware validation functions on the base class, so these things shouldnt limit fluids from being used properly as slotted storage

sinful tiger
#

i'm not convinced that int getTanks and FluidStack getFluidInTank - shouldn't be replaced with a single List<FluidStack> getAllContents. I'm sure Technician will tell me why i'm wrong on this one.

#

ah, well, i meant the current fluid api is mostly slotless

wind steppe
#

it does have a slotted extract iirc

sinful tiger
#

unless that's a very recent addition i am certain it does not

wind steppe
#

oh im wrong

sinful tiger
#

it has a specific fluid extract

wind steppe
#

what?? has this always been this way

#

damn, ive been doing multiplatform for too long

#

anyways, yes the new handler has slot aware and slotless methods, so no more of this weird crap

sinful tiger
#

does it still have the specific fluid case? i remember we went back and forth on whether that was a reasonable thing to want for items

wind steppe
#

wdym

#

like, a specific class for fluid that extends the handler?

sinful tiger
#

like, an extract method that says what fluid the caller wants instead of what slot

wind steppe
#

thatll be a utility method

#

in handler util

#

HandlerUtil.extractFiltered()

sinful tiger
#

hmm i wonder if it'd be better as a defaulted method on the interface

#

[like, sure, iterate the slots and find a matching slot by default]

wind steppe
#

it might help with allowing devs to create more efficient methods for their storage, but im not sure about it tbh

sinful tiger
#

i think we're getting into the same "filter as opaque predicate" vs "filter as something that can be used to structure large containers as to not require a linear search" issue, which is honestly less important for fluids than for items

wind steppe
#

linear search isnt really that bad at the scales most devs implement storage. it just gets bad when buuz makes a mod kek

sinful tiger
#

most fluid containers are only going to have one slot, or a single digit amount at most - things like tinkers construct are extreme outliers

#

whereas item containers with a hundred slots or more are not uncommon

wind steppe
#

well the way i currently implemented it is that itll go through the slots till it finds the first item that matches the filter and then itll call the slotless extract method with that resource

sinful tiger
#

buuz? i don't know what buuz has done, the real insane one here is kroeser, did you know every slot of every attached container is exposed in the item handler of ID item interfaces?

#

presumably also fluid interfaces, but you can easily get thousands or tens of thousands of item slots

wind steppe
#

i think buuz has a container in functional storage that has an infinite amount of slots

#

an infinite amount of single stack items

sinful tiger
#

ah, yeah, the armory cabinet

#

iirc it has weird limitations too though, something about not being able to insert multiple items in a single tick

wind steppe
#

interesting, it has a cooldown?

sinful tiger
#

i have no firsthand information, this is just something i heard from the chat of a twitch streamer who was trying to use it

wind steppe
#

gotcha

sinful tiger
#

i also forgot functional storage was him

#

anyways, fluidutil methods

wind steppe
#

anyways, its an interesting idea. dont think itd necessarily be a bad thing to allow devs to implement the search method in their own way, but maybe thats not the best approach if we're adding it on the interface directly

sinful tiger
#

interact is pretty important, it's the most reliable way to get "click on your tank with a bucket" working.

#

tryfill and tryempty are if nothing else called by interact

wind steppe
#

maybe itd be better to just have devs have a findResource(predicate) method instead that gets defaulted to a linear search

sinful tiger
#

i don't know if we need the one-liner methods that just wrap getCapability

wind steppe
#

thatd be more useful overall imo

sinful tiger
#

pickup and place is... well, what i'm seeing in 1.20.6 provides IFluidBlock support, it might be less necessary when that's gone?

wind steppe
#

getting an optional fluid handler seems so dumb lmao. why not just have a method in capability itself for that?

sinful tiger
#

I mean

#

ultimately all it's doing is Optional.ofNullable

#

if we want to use Optionals we should use them in the capability API itself

#

right now it uses nullable

#

i don't think we should have two methods in the API that use nullable vs optional with no other distinction, we should pick one - and evidently we picked nullable

wind steppe
#

the insane part is that its used inside forge code AND THEY JUST DO .orElse(null)

#

i think alot of this class will just end up being deleted. ill keep and cleanup the interaction stuff

sinful tiger
#

anyway, most of the code in FluidUtil is in support of either 'transfer fluid between containers' [including the interaction stuff but a lot of it is generic enough to not require one of them to be an item] or 'pick up and place a block in the world' - the latter i think was to reduce the patch surface on buckets, but it's probably also used at this point by modded machines that directly place or pick up fluid blocks

wind steppe
meager nexus
#

whats wrong with mB ?

#

if it were to change, I would like to be a highly composite number (12 or 60 looks nice)

dire hazel
#

Millibuckets are annoying for mods like Tinkers' that want a nugget worth of fluid (when a block is a bucket). It's one of the reasons fabric went with 81000 for its fluid parts.

sinful tiger
#

what about 'each fluid defines its own ratio, with a guideline to pick a number reasonably close to 1000' so people don't have to rewrite tanks

#

being divisible by 64 would be nice.

raven nymph
# meager nexus whats wrong with mB ?

it doesn't work for cauldrons (and by extension water bottles)
since water cauldron levels are 1/3 of a bucket
(and water bottles are equivalent to cauldron levels)

meager nexus
#

Millibuckets are annoying for mods like Tinkers' that want a nugget worth of fluid (when a block is a bucket)
imo this assumption should be changed then. 1 bucket = 1 block

#

though I am not sure what it should be changed to 😅

hollow maple
#

I think the real solution is to just accept that fluids are lossy :p

opaque vector
#

Prime number fluid units when

ivory mauve
swift glen
#

I think 1bucket = 324mb would be good, can be divided by 3, 4, 9, 81 which afaik are the most common dividers in minecraft

ivory mauve
#

The problem with this discussion most times we have it is we never get people with the mods actually uusing fluids discussing it

#

its always a bunch of third parties who have an opinion but no mod that cares about the result

swift glen
#

the right way would be imo to find the commonly used dividers and then find the lowest common product

ivory mauve
#

Which is what that poll is about

#

Hence why speciifc suggestions are not valuable

#

Though to be honest, that poll still suffers from that problem where I dont think most of the votes come from people using fluids, I feel a lot of them are just people who have an opinion on the topic without stakes

#

I think tying the unit to a name is probably more valuable. Instead of saying "9", say "9, gems like diamond" or "81, ingots and nuggets"

#

or some people with "9, ingots" who don't care about nuggets

#

Knowing why people wnat the number I think is more useful than just knowing the number they want

#

For isntance, I think a lot of people wanting 10 just want to stick with what they have already, I nearly answered 5 before looking back and seeing nothing I had needed 5, I just used 5 as 1000 divides by 5

meager nexus
meager nexus
#

36 could be such a number, divisible by 2, 3,4, 6, 9, 12, 18

swift glen
#

But knightMiner is right, having multiple units could be better, because my number as oddly specific as it is, forgets division by 5 which is most likely used somewhere

opaque vector
ivory mauve
#

to be fully honest, 3 is my absolute minimal

#

if we are changing it, might as well do 9 and 4. And 81 would be great but not essential

opaque vector
ivory mauve
#

But I can keep using 1000mb just fine

#

3 is for vanilla compatibility

meager nexus
#

do you want to keep 1 bucket as 1 block ?

ivory mauve
#

I couldn't care less about 1 bucket 1 block

opaque vector
#

324 for 3, 4, 9, 81
1620 for 3, 4, 5, 9, 81

ivory mauve
#

I also don't know what you mean by "keep"

#

no one is doing 1 bucket 1 block right now (except maybe fabric mods)

#

at least not as a guarantee, just specific fluids do that

meager nexus
#

I see. A message above mentioned it. I could be wrong

ivory mauve
#

It be nice to have, but its not an essential

#

I'd rather a unit that is easy to work with

swift glen
#

I know mekanism uses 200mb in the ore processing pipeline, so it should be divisible by at least 5 too

ivory mauve
#

e.g. someone earlier proposed 900 per bucket, which I could make work with 90 per ingot as before, 10 ingots to a bucket

#

I do think the most important thing though is an API for fluid unit display

#

Before we go about changing bucket divisions, we need a standard for how you show values in tooltips

meager nexus
ivory mauve
#

That way, no one is stuck displaying 1/81000 or other such

#

I do this in Mantle/Tinkers via JSON, though the JSON is mostly to support other mods

#

if Neo did it, they could easily register it as part of fluid types

#

My logic is basically a list of numeric value/translation key pairs in descending order, and holding shift displays in the smallest increment (right now mb) in case people want to compare capacities

loud stirrup
#

Is anyone seriously suggesting to change millibuckets in NF?

#

That would give us bugs until the end of time.

opaque vector
ivory mauve
#

The logic is "while transfer is breaking might as well consider it"

sinful tiger
#

experience fluid has a somewhat longstanding "50 xp per bucket" convention

#

and i believe the last time this came up the terrafirmacraft guy said they use percentages for alloys, so that's a legitimate-ish case for keeping it divisible at least by 100

ivory mauve
#

I agree its worth considering again, though I doubt we will reach a number that satisfies the community enough to finally rid ourselves of millibuckets

sinful tiger
#

I mean, in part this stalled last time because people were throwing around composite numbers large enough that reasonable size tanks would need the quantity amount to be a long and Lex said no to long

ivory mauve
#

I also think trying to force 81 into this will just lead to awkward numbers

#

I can make my stuff work with just 9 if need be, as long as no one is forcing 1 block = 1 bucket. Though I have heard a few people express desire for that to be a thing

sinful tiger
#

i also don't really understand the objection to making it per-fluid

ivory mauve
#

I think making everyone happy will be hard

ivory mauve
sinful tiger
#

yeah

ivory mauve
#

I understand the sentiment behind it

#

it would make standard pipes hard

meager nexus
#

i couldn't find any good prefix for 1/900 parts. I am just gonna call the unit "neoBucket" or "nB".

ivory mauve
#

no one wants to transfer at bucket rates as a standard

ivory mauve
#

I would not worry about naming the minimal unit

sinful tiger
#

I mean, my idea is more "try to keep it close-ish to 1000 so transfer rates and tank sizes don't have to care but it can be 810 or 1276 or whatever and stuff that actually puts fluids in the world or puts it in actual buckets has to do a lookup"

ivory mauve
#

the names can come later

loud stirrup
#

For this to work well you really need to be able to properly format it for display to users

ivory mauve
#

Hence why I said, tooltip API needs to happen

#

Thats something we'd want regardless

loud stirrup
#

You need this in more than tooltips

#

Just generally

opaque vector
#

Fraction of bucket display lol

loud stirrup
#

That is exactly how that works, yes 😄

sinful tiger
#

jade or something on fabric figures out the most reduced fraction of a bucket iirc

ivory mauve
#

Value to component list essentially

sinful tiger
#

but

ivory mauve
sinful tiger
#

having fluids be able to define their own units would open up the possibility of e.g. tinkers fluids saying how many ingots and nuggets you have

#

potions how many bottles, etc

meager nexus
#

how do you suppose recipes using fluid tags would work?

ivory mauve
#

leaving it up to JSON lets modpack makers standarize them

meager nexus
ivory mauve
#

you only tag things with the same fluid units

sinful tiger
#

[by 'their own units' i mean 'their own set of radices for display', independent of whether that means bucket sizes are per fluid or not]

ivory mauve
#

we could leave the units on the serverside API, I could see some benefit

#

maybe you could manage to convert between two fluids with different units

sinful tiger
#

if you need different quantities of different fluids you need two different recipes [or a composite ingredient type that can associate different quantities with different holdersets anyway]

ivory mauve
#

we'd need something akin to tool actions for that to happen though

#

some way to say "these are both ingots so they should be the same"

sinful tiger
#

and if that means you can't make use of a tag so be it

ivory mauve
#

I have not looked recently to see if Neo's does, or if that even got merged yet

#

Looks like they went with single size

#

Though the root ingredient is sizeless

sinful tiger
#

anyway, other than the possibility of going into long quantities after a few thousand buckets, is there any reason not to just multiply together every number anyone could ever want it to be divisible by? i ended up with something like 648000 the last time i tried this

ivory mauve
#

so you can always make your own wrapper that compounds multiple sizes

ivory mauve
#

and no matter how nicely we display, eventually you will have to parse

#

some usages of fluids will use a partial unit and just scale up the behavior

#

so you may end up with the smallest increment left in a tank eventually

#

not to mention, tank capacities

#

no one wants their tank to list "stores 51 ingots, 45 cheese cubes, or 89 snail heads"

#

the smallest unit is how you convert from one to another for capacities

sinful tiger
#

honestly for most purposes i'm not sure we can't just display the number of buckets [or of the smallest unit of a fluid that defines itself in terms of nuggets or whatever] as a float

ivory mauve
#

I also disbelieve anyone who says they need 1000

#

I can understand 100, but not 1000

sinful tiger
#

0.190519 buckets isn't great but it's more legible than "123456 droplets"

ivory mauve
#

8100 would be annoying, but it would not kill me if that was agreed upon

#

my concern is partly someone is going to have 2 droplets sitting in a tank, and wonder what we can do with it. Someone else is going to make a pipe that transfers 1 ingot, and wonder how they transfer 1 brick

sinful tiger
#

an empty tank can display its capacity in buckets, a partially full tank can display its capacity in terms of whatever fluid it's actually holding... which just leaves, uh, smelteries, as the odd one out which has an odd amount of headspace you can put any fluid you want in

ivory mauve
#

at some point in time, any nice unit we make will be split because someone's pipe uses a different unit

ivory mauve
#

but fluid pipes are the real problem here

sinful tiger
#

the only other one i'm aware of is the create basin, which iirc holds "up to 1 bucket of every fluid you put in it"

ivory mauve
#

I like to transfer 1 ingot at a time, works nicely with how my mod works. Others like to transfer in percentages of a bucket

#

If we let people set arbitrary units, someone's pipe will eventually break that unit, and we are back down to the smallest unit in display

#

This is where the "every fluid has its own smallest unit" idea gains some merit tbh, but that really makes piping awkward

#

We'd end up with every tank listing its capacity in buckets, and transfer would have to decide whether to go by buckets or by the smallest unit available to the passed fluid

sinful tiger
#

tbh "end users break a pipe that hasn't finished transferring and void an odd quantity of fluid, and it's a valuable enough fluid that they care enough about the odd leftover quantity to want to do something with it instead of voiding it too" is to some extent a self-created problem by those users.

ivory mauve
#

I'm not even talking about finishing transfer or not

#

lets say I go from a tank that holds 81 ingots though a pipe transferring bricks into a tank holding 72 cheese cubes

#

there is potential for the destination to become full with a partial unit left untransferred

sinful tiger
#

what

#

oh

ivory mauve
#

I've dealt with this before with just bucket tanks and ingot tanks

meager nexus
#

okay. might be a crazy idea but what would happen if there were 2 units. one bigger and one smaller like how feet-inch works

sinful tiger
#

tbh the solution is arguably "recombine all your odd quantities in a tank large enough to hold them"

ivory mauve
#

you fill an ingot tank with lava, and either have leftover spae or partial buckets

ivory mauve
sinful tiger
#

[or, if the fluid isn't valuable enough to care, just void it]

ivory mauve
#

and displaying the partial multiple

ivory mauve
#

there is no one size fit all solution

sinful tiger
#

like essentially this is the same as the fun little tinkers minigame in 1.12 where i have to figure out what alloying recipes i have to do to use up the odd quarter nugget quantity of something that i got as a result of some sequence of accidental alloying operations

ivory mauve
#

Either we make it flexible and hard for transfer, or we make it easy for transfer but limited

sinful tiger
#

i can either figure it out, or i can void the odd quantities

ivory mauve
#

you had to do that for tinkers because tinkers uses a lot of different fluid values

#

imagine having to do it for every combination of mods that use a fluid

#

right now most usages of fluids can stick to divisions of 4 or 10 if they wish and the math is easy

sinful tiger
#

well, it's mainly because 1.12 did a "3/4 nugget of copper + 1/4 nugget of tin = 1 nugget of bronze" instead of "3 nugget of copper + 1 nuggets of tin = 4 nuggets of bronze" thing so the minimum quantity was effectively determined by which alloying recipes were in play

#

like, sure, i have 4 buckets in my bucket tank and that's an odd number of ingots, but eventually i will use all of those ingots and have one partial ingot in the bucket tank and one partial ingot in whatever tank i filled it from and i can either figure that out and recombine them, or void them.

ivory mauve
#

actually, it did 3mb of copper + 1mb of tin

#

there was no relationship to nuggets there, it was just the absolute smallest sets that matched the ratio

#

I decided to at least make all recipes work with nuggets at minimum in newer versions, most notably salvage recipes tht scale the output by durability now clamp to nuggets

#

that worked in part because I could guarantee nuggets divide evenly into buckets

sinful tiger
#

anyway i thought the point of having a grand unifying bucket quantity would be that even if something transfers stuff in units of ingots, every ninth ingot you have a bucket and that's a quantity that makes sense to everyone's pipes and everyone's tanks

ivory mauve
#

ideally if everyone works with buckets in the end it all unifies

#

but its nice to be able to have a tank that has a capacity in terms of other units

#

maybe a bucket is 10 ingots, but why am I not allowed a tank that holds 64 ingots?

#

if I allow it, now we end up with some fun leftover fluids. Some mods can deal with that just fine, tinkers notably has ways to deal with it. But not every mod that uses fluids should be forced to come up with a solution like that

sinful tiger
#

i guess

#

if i put my fluid that is only useful in 1/3 or 1/4 bucket bottles in your 64 ingot tank, then, sure, i get an odd fraction of a bottle left over, but the other fraction is still in whatever source tank i transferred it from

#

and i can recombine them if i want to use them in my recipes that only work in quantities of bottles

#

like, unless you've already voided part of your leftover fluids, all your partial units are somewhere.

#

and they still add up to the whole original amount you had

wind steppe
ivory mauve
#

My key argument there is using Tinkers to justify Fabric APIs is a little silly

#

Use a mod thats on Fabric that uses ingots

wind steppe
#

With the way the new handler is designed it would technically be possible to stack fluids in the same way that items are stacked, with different stack sizes for each liquid, because the getLimit method accepts an index and a resource

#

if you really wanted to .getLimit(0, water) and getLimit(0, lava) could return different results

ivory mauve
#

but unless Neo standardizes that sort of concept in the fluid API its not happening

#

Some sort of getBucketCapacity() method on fluid type

#

Though not sure I want to try dealing with fluid container items in a world where bucket capacity varies per fluid

#

something as simple as "stores half a bucket" breaks as soon as someone has an odd number for their units

wind steppe
#

yeah, that would be real annoying, the bucket stuff currently all makes the assumption that 1 bucket is always the same amount of liquid

#

just sayin that if you want to do it yourself on a storage level you can

ivory mauve
#

I think I just keep becoming more convinced we won't find something better than millibuckets

#

either we get nice divisions or the APIs working with the fluids are conveient

wind steppe
#

well no, millibuckets suck, i just dont think people will agree on whats better enough to actually change it

ivory mauve
#

or we need to get all modders to agree on a value together...

ivory mauve
#

if nothing else, millibuckets have momentum, we know how to deal with them

wind steppe
#

i mean, latvian just suggested we use 162k kek

ivory mauve
#

ew

#

thats like, 81000 but worse

wind steppe
#

your issues with 81k for capacity reasons go away if the storage was switched to using longs :P

ivory mauve
#

not really no

#

my issues are about parsing mostly, humans reading htem

sinful tiger
#

maybe what we really need is to build out that display system (so we can do 1/1296000 or whatever without people having to actually see 648000/1296000 unless they actually have a quantity that isn't divisible by anything, and to allow fluids to specify that they can display in units like nuggets or whatever)

ivory mauve
#

the bigger it is, the harder it is for a human to think about how two different units relate

#

and that is the only point of a bigger number, different units

#

I'm not even convinced a fluid display system solves this entirely, since transfer will mess with it

sinful tiger
#

frankly i think displaying whatever is the smallest unit [bottle, nugget, etc] as a decimal is probably fine for most purposes

ivory mauve
#

plus, why do these numbers have so many 0s at the end?

#

I never heard a good argument for why 81000 instead of 8100

#

never seen a mod that actually needed mb, we just all are used to it

#

keeping a factor of 1000 is as bad as keeping mb

wind steppe
#

8100 is not divisible by 8

ivory mauve
#

do people use 8?

wind steppe
#

it got 5 votes

sinful tiger
ivory mauve
#

but what is 8 for?

#

the poll is very unhelpful in that regard

wind steppe
#

i think in general being divisible by the factors of 4 is helpful for minecraft stuff

ivory mauve
#

I use a lot of factors just because they exist rather than because I actually need them

#

some of them can easily be moved to a related factor

#

e.g. back in 1.16 I even had divided a nugget into 8 pieces at one point

#

upon porting and discovering that ws the last holdout for 16mb nuggets I just decided it was a stupid choice back then and ditched it

sinful tiger
#

iirc 5 ingots crafts 64 grinding balls in enderio - a grinding ball cast might therefore use 45/64 of a nugget.

ivory mauve
#

6 ingots makes 16 iron bars

#

I just round it to some number of nuggets

#

means recycling is lossy from the crafting table, but you get a discount for casting it

opaque vector
#

if those votes were from random people who dont actually do any fluid work, then their vote is kinda pointless

ivory mauve
#

that is the biggest problem with this poll, its so anonymous we cannot ask "why"

wind steppe
#

yeah. but grunt, mr maintainer over here, can we make a seperate brainstorming channel for this

ivory mauve
#

I feel like to get useful data, we need to ask factors + why. I want 8 because "blah blah" has 8 units