Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Universal Autocrafting #439

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

phantamanta44
Copy link

@phantamanta44 phantamanta44 commented May 22, 2024

This PR adds the infrastructure needed for autocrafting with any item type that can be represented by an IStorageChannel.

Implementation Notes

The key idea is to just replace all the IAEItemStacks used to represent resources in the autocrafting system with a new type IExAEStack, which can be thought of as an IAEStack<T extends IAEStack<T>> with its type parameter T existentially quantified out. This turns out to be fairly involved, as the autocrafting infrastructure touches many parts of the AE2 codebase, and so a fairly significant portion of the API surface has changed. In particular, there are many new interfaces in the storage API describing type-agnostic collections of AE stacks, as well as supporting structures for manipulating and iterating over them. To get a good idea of how this all works, I would suggest checking out the IExAEStack and IUnivStackIterable interfaces and their implementations.

There is a question of whether or not the IExAEStack type is even necessary, since after erasure, it's isomorphic to IAEStack. In principle, it should be possible to simply use either a wildcarded IAEStack<?> or a raw IAEStack for the same purpose, and to then perform unsafe casts at any point where the type parameter needs to be projected out. I decided for myself that type safety, and therefore code quality, was of greater concern, but it could be argued that it's not worth the performance/memory losses of having to go back and forth between AE stacks and this wrapper type.

Issues

The most obvious issue is that of backwards-compatibility, which manifests in two ways:

  • API compatibility---i.e. will addons still work?
  • Save data compatibility---i.e. will worlds created on older versions still be playable?

For the former, I suspect that enough of the API surface has changed that all AE2 addons doing something nontrivial with ME network features will no longer be compatible. In particular:

  • The changes to IStorageChannel will impact all mods adding custom item types.
  • The changes to IPriorityHost will impact all mods adding custom ME devices with priority.
  • The changes to autocrafting will impact all mods adding autocrafting providers and requesters.

These criteria will include mods such as AE2FC(R), Lazy AE2, Extra Cells, Thaumic Energistics, and so on. Moreover, the solution is not so easy as patching those mods; since this breaks API compatibility with the original AE2, these mods will either have to maintain disparate versions targeting both AE2 and AE2-UEL, or they will have to choose one and forego compatibility with the other. It's not immediately clear to me how this issue ought to be resolved.

For the latter, I've designed the NBT (de)serialization routines so that the old save format should be tolerated. I would not just take my word for this; I would recommend testing vigorously before any production release. The packet serialization format differs considerably, but this is a non-issue, since the client and server should always be running the same version of AE2-UEL anyways.

Future Work

Listed below are some future features that might be interesting:

  • Currently, JEI is required for encoding non-item recipes, either using ghost items or by transferring recipes. Maybe it would be good to have a non-JEI-dependent means of encoding recipes?
    • But on the other hand, who plays without JEI these days?
  • Currently, blocking mode on ME interfaces only checks for items in the target inventory. It may be useful to have more flexible blocking behaviour, but I'm not sure what this would look like in the face of arbitrarily many item types.
    • One possibility is to have a big array of configurable blocking flags with one flag for each item type.
  • Most fluid devices don't yet support autocrafting behaviours in the way item devices currently do.
    • Fluid interfaces should be able to host patterns as crafting providers.
    • Fluid interfaces should accept crafting cards to function as crafting requesters.
    • Fluid export busses should accept crafting cards to function as crafting requesters (implemented).
    • Fluid level emitters should accept crafting cards to emit redstone power for crafting the same way the item level emitter does (implemented).
  • Since fluids are now first-class autocrafting ingredients, it might be good to have either a built-in bucket-filling machine or a variant of the molecular assembler that accepts fluids.
  • Currently, the item interface is able to dispatch any item type for autocrafting. If fluid interfaces are eventually given crafting provider abilities, it might make sense to restrict each interface type to only handle autocrafting with ingredients of that type.
    • For heterogeneous recipes, some other device would be necessary; probably either some kind of combined interface or a dedicated autocrafting dispatcher device

Listed below are some possible future changes to implementation details:

  • Currently, there is no way to go from an "underlying" stack type (e.g. ItemStack) to the corresponding AE stack type (e.g. IAEItemStack) without knowing the correct storage channel beforehand. This results in nasty loops over the channel list, as in GuiPatternTerm::stackFromIngredient. It may be useful to store these relationships in a lookup table somewhere.
  • There are various bits of behaviour that are either awkwardly separated or straight-up duplicated between code handling items and code handling fluids. For example, there is considerable code dupliation between StackSizeRenderer and FluidStackSizeRenderer, and there is an awkward separation of slot handling behaviour between AEBaseGui and GuiFluidTerminal. It would be good to generalize and/or factor out the common code in these situations.
  • Since the AE2 codebase plays rather fast and loose with nullability, I've also been a bit lax on keeping track of nulls. Probably, it would be good to refit the entire codebase with proper nullability tracking, but this would be a pretty big undertaking.

@phantamanta44
Copy link
Author

that should deal with backwards-compat for the outwards-facing API, at least

@NotMyWing
Copy link
Member

Hey, Phanta! Thanks for the PR, and welcome back.

Unfortunately, the main issue is indeed the loss of compatibility with every single addon in existence. The better way I see it done is to instead deprecate old methods and introduce new ones that would allow arbitrary stacks. This would simultaneously keep current addons functional, and allow future ones to leverage the newer methods.


Currently, JEI is required for encoding non-item recipes, either using ghost items or by transferring recipes. Maybe it would be good to have a non-JEI-dependent means of encoding recipes?

Unusual stuff can always be specified with containers, and then adjusted as needed. If necessary, an additional UI could be in place to granularly edit values instead of scroll-wheeling until it's right. In case you need to specify the actual container as-is, why not make, say, right-click functional?

Currently, blocking mode on ME interfaces only checks for items in the target inventory. It may be useful to have more flexible blocking behaviour, but I'm not sure what this would look like in the face of arbitrarily many item types.

A storage-channel-to-capability driver system can be introduced, mapping capability implementations to common methods like getSlots, getStackInSlot and etc., so AE2 can address containers by using "translation layers". This would make containers report IAEStack to the network regardless whether it's gas, item or fluid, with AE2 being completely capability-agnostic.

Most fluid devices don't yet support autocrafting behaviours in the way item devices currently do

Ditto. Making a driver system would allow ME Interfaces to both push and expose any IAEStack via the config slots. This could deprecate ME Fluid Interfaces altogether.

Since fluids are now first-class autocrafting ingredients, it might be good to have either a built-in bucket-filling machine or a variant of the molecular assembler that accepts fluids.

I personally hate recipes that use buckets of fluid in the crafting grid. Making existing Molecular Assemblers accept fluids directly would be more sensible.

Currently, the item interface is able to dispatch any item type for autocrafting.
Ditto. In my opinion, it should be this way.


That being said, it's a good start, but it's going to require a lot of work to get it right and ultimately transition to capability-agnostic behavior if we want to treat all stacks equally.

@phantamanta44
Copy link
Author

Unfortunately, the main issue is indeed the loss of compatibility with every single addon in existence. The better way I see it done is to instead deprecate old methods and introduce new ones that would allow arbitrary stacks. This would simultaneously keep current addons functional, and allow future ones to leverage the newer methods.

this is addressed by 8c2cb0d... at least for the outwards-facing API. internal API structure is still different, so mods that mess with internals like ae2fc might still break, but i'm not so sure that should be a concern. the one API shape that this doesn't apply to is IStorageChannel, since i require additional functionality (mentioned below) that wasn't previously in the storage channel API. this means mods that add storage channels (e.g. the ec2 gas channel or the thaumic energistics essentia channel) will probably still break

Unusual stuff can always be specified with containers, and then adjusted as needed. If necessary, an additional UI could be in place to granularly edit values instead of scroll-wheeling until it's right. In case you need to specify the actual container as-is, why not make, say, right-click functional?

i thought about some kind of modal text box that could pop up on middle-clicking an ingredient slot, but i figured that was probably a bit out of scope for this PR

A storage-channel-to-capability driver system can be introduced, mapping capability implementations to common methods like getSlots, getStackInSlot and etc., so AE2 can address containers by using "translation layers". This would make containers report IAEStack to the network regardless whether it's gas, item or fluid, with AE2 being completely capability-agnostic.

this is given by my new extension IStorageChannel#adaptInventory as well as the InventoryAdaptationCache type, which i'm using to push ingredients out of ME interfaces

Ditto. Making a driver system would allow ME Interfaces to both push and expose any IAEStack via the config slots. This could deprecate ME Fluid Interfaces altogether.

export bus and level emitter autocrafting are addressed in c004036. the interface merge thing seems risky, since this would break save compatibility... at any rate, it's definitely out of scope for this PR

I personally hate recipes that use buckets of fluid in the crafting grid. Making existing Molecular Assemblers accept fluids directly would be more sensible.

in that case, i guess it would make sense to add a toggleable buckets- vs fluids-mode config option for molecular assemblers, although it would be a bit of a pain to have to configure every assembler you ever place if the default isn't to your liking. maybe it would be useful to let players hold memory cards in their off-hand to configure placed blocks, like the keys from storage drawers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants