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

Re-implement FluidTankList & OverlayedFluidHandler #1695

Merged
merged 17 commits into from
Jun 1, 2023

Conversation

Tictim
Copy link
Contributor

@Tictim Tictim commented Apr 11, 2023

What

This PR rewrites FluidTankList and OverlayedFluidHandler for a much cleaner implementation with new algorithm fully covered by unit tests.

Implementation Details

Although previous iteration of FluidTankList allowed creation of nested tank lists (FluidTankList inside FluidTankList), it didn't handle it correctly - specifically, differing allowSameFluidFill flag between parent and children. This led to creation of an abomination with a name of NotifiableFluidTankFromList, which was an extremely hacky solution to retain the parent IMultipleTankHandler and handle allowSameFluidFill setting manually during insertion calls. This questionable design decision then propagated to OverlayedFluidHandler which gave birth to per-fluid tank cache of distinct fluids.

In this PR, IMultipleTankHandler received a major redesign to accommodate behavior of nested FluidTankLists, and provide better API for usage. The insertion algorithm of FluidTankList was changed to handle nested tanks.

OverlayedFluidHandler, or rather a simulator for consecutive fill operations on IMultipleTankHandler, also received a major refactor. One notable change in API perspective is that the insertStackedFluidKey no longer receives FluidKeys. Since the algorithm can handle consecutive fill of same fluids now, it is possible to fill FluidStacks without worrying about redundant entry breaking recipe checks. As a result, usage of GTHashMaps#fromFluidCollection got completely phased out.

Since handling of nested tanks is no longer needed, implementation of MetaTileEntityMultiFluidHatch was changed to use standard NotifiableFluidTanks. I didn't test it yet. I just assumed it works flawlessly. 😃

NotifiableFluidTankFromList was marked as deprecated.

Test cases cover all insertion/extraction logic in both FluidTankList and OverlayedFluidHandler.

Additional Information

The distinct insertion logic of FluidTankList and OverlayedFluidHandler has a behavior change:

  • Previously, during the first iteration (inserting fluid to tanks with same type of fluid), only one tank could receive the fluid.
  • In the new algo, multiple tanks can receive the fluid if they have same type of fluid.

Distinct insertion on second iteration is unchanged.

Serialization result of FluidTankLists have a minor change of TankAmount field being removed. As the information was redundant in reading the contents of list tag, I just removed the field.

Potential Compatibility Issues

As all of the classes except multi fluid hatches are API, this PR is API breaking change. The actual severity of compat break depends on its usage.

  • Plain usage of FluidTankList, like instantiation or calling IFluidHandler methods, should remain unharmed.
    FluidTankList fluidTanks = new FluidTankList(false, tanks);
    fluidTanks.insert(fluid, true);
  • Instantiation of OverlayedFluidHandler should also remain.
    OverlayedFluidHandler ofh = new OverlayedFluidHandler(fluidTanks);
  • Iterating over FluidTankList#getFluidTanks() still works I think. At least on code it works. Not sure about binary compat.
    for(IFluidTank tank : fluidTanks){
      doSomething(tank);
    }
  • Operations on fluid tanks returned by FluidTankList#getTankAt() should retain their functions; but any instance-specific operations, like casting the object or instance checking with refeq will need updates.
    IFluidTank tank = fluidTanks.getTankAt(0);
    if(tank instanceof FluidTank){ // compat break
  • Storing FluidTankList#getFluidTanks() in a field with type of List<IFluidTank> will produce compilation error on code, due to how generics work. I think it's fine on binary.
    List<IFluidTank> tanks = fluidTanks.getFluidTanks(); // compat break
  • Calling OverlayedFluidHandler#insertStackedFluidKey will break after update.
    ofh.insertStackedFluidKey(fluidKey, 1000); // compat break
  • FluidTankList#validateTankIndex() no longer exists; although I don't think anyone actually uses the method, it is technically an API break.

@Tictim Tictim added the type: refactor Suggestion to refactor a section of code label Apr 11, 2023
@ALongStringOfNumbers ALongStringOfNumbers added this to the 2.7 milestone Apr 30, 2023
@Tictim Tictim requested a review from a team as a code owner May 23, 2023 06:09
@ALongStringOfNumbers
Copy link
Contributor

Getting this JEI integration error when launching the game, https://pastebin.com/VjaGBEf3

@Tictim
Copy link
Contributor Author

Tictim commented May 24, 2023

In addition to the changes in FluidTankList and OverlayedFluidHandler, new changes in API were introduced.

  • A generic filter interface, IFilter, defines match function for a specific type of object, as well as numeric priority to minimize issue of order-dependent behavior changes during insertion. This is a required feature for Allow filtering output hatches #1752.
    The interface is designed to encompass other types of instances as well, not just FluidStacks. Item filter is an obvious target, but the migration hasn't been done yet.
  • All instances of filtered fluid handler implementations in the mod are migrated to IFilter-based impl. Most of them are internal changes, but couple of breaking changes are present as well.
    • ModHandler#isLava and ModHandler#getLava were removed. ModHandler#getFuelValue was also removed, despite the fact that this method has nothing whatsoever to do with any fluid logic. 🇹🇷
    • ModHandler#isWater, ModHandler#isSteam, and ModHandler#getSteam were marked as deprecated.
    • IThermalFluidHandlerItemStack, ThermalFluidHandlerItemStack, and SimpleThermalFluidHandlerItemStack were marked as deprecated. Code referencing these class such as item tooltip may cease to function.
    • FilteredFluidStats#<init>(int, boolean, Function<FluidStack, Boolean>) were marked as deprecated.
    • ThermalFluidStats were marked as deprecated.
    • Other preexisting fluid container implementations such as FilteredFluidHandler, GTFluidHandlerItemStack, and GTSimpleFluidHandlerItemStack received a refactor. FilteredFluidHandler#setFillPredicate was marked as deprecated.

@Tictim
Copy link
Contributor Author

Tictim commented May 24, 2023

JEI integration error should be resolved now.

Copy link
Contributor

@ALongStringOfNumbers ALongStringOfNumbers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In progress review

int getMaxFluidTemperature();

/**
* This is always checked, regardless of the contained fluid being a {@link MaterialFluid} or not
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get a bit more clarification on this and the above javadoc, This is always checked, as it is implemented by vanilla, regardless...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically implemented by forge, since the whole fluid thing is mod-introduced system, but I'll expand the docs a bit.

@Nonnull
public static FluidStack getLava(int amount) {
return new FluidStack(FluidRegistry.LAVA, amount);
return stack == null || CommonFluidFilters.BOILER_FLUID.test(stack);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you have this deprecated, but this is a logic change, in case people were still calling this. Before if the stack was null, this returned false. Now it will return true if the stack is null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another stupid mistake by me 😃😎

* @param fluid fluid
* @return whether the fluid in fluid stack and fluid parameter are equal
*/
public static boolean matchesFluid(@Nonnull FluidStack fluidStack, @Nonnull Fluid fluid) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be a case where we want a FluidStack and FluidStack matches method? Say for example the fluid regulator or fluid voiding cover where you can specify exact amounts of fluid in the filter to be operated upon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FluidStack to FluidStack match is handled by FluidStack#isFluidEqual. Those two CommonFluidFilters#matchesFluid are just methods that simulates FluidStack#isFluidEqual without needing to create new FluidStack.

Also FluidStack#isFluidEqual doesn't check the amount of fluid, so if you want to match the amount of fluid you would need to create custom implementation of IFilter that calls a different match function.

* @param fluidStack Fluid stack to search index
* @return Index corresponding to tank at {@link #getFluidTanks()} with matching
*/
default int getIndexOfFluid(@Nullable FluidStack fluidStack) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would also have been an issue with the old implementation, but if allowSameFluidFill is true, couldn't the passed in fluid be in multiple slots?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method was a part of NotifiableFluidTankFromList jank and can be removed, but I chose to keep it in. Still, this method is never called outside of that specific class. Also the contract of the method is not particularily associated to insertion logic so I don't think it's a problem.

/**
* @return {@code false} if insertion to this fluid handler enforces input to be
* filled in one slot at max. {@code true} if it bypasses the rule.
*/
boolean allowSameFluidFill();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rename this? This is one of the method names from CE that I really don't like, and doesn't really match well with the javadoc of its implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b...but compatbilty 🥺

I guess we could just make a new method and link this method to the new one, but I don't have any strong idea for a new name so feel free to suggest some.

@ALongStringOfNumbers
Copy link
Contributor

Could you add a @since tag in the javadoc for the deprecated classes and methods, so that way it is easier to know when things were deprecated? I want to allow at least 1 major version between deprecation and removal, so knowing when things were deprecated would help a lot for that. It would be nicer if we could use since in the deprecation annotation, but unfortunately that was from java 9

@Tictim
Copy link
Contributor Author

Tictim commented May 30, 2023

The javadoc tag @since is for the version the stuff was added. Using it to communicate deprecated version instead might just lead to confusion.

Tictim added 2 commits May 31, 2023 00:47
# Conflicts:
#	src/main/java/gregtech/api/unification/material/properties/FluidPipeProperties.java
#	src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityDrum.java
@brachy84
Copy link
Contributor

brachy84 commented May 30, 2023

The javadoc tag @since is for the version the stuff was added. Using it to communicate deprecated version instead might just lead to confusion.

Using jetbrains annotations when 😔

@TechLord22 TechLord22 merged commit dc82278 into GregTechCEu:master Jun 1, 2023
serenibyss added a commit that referenced this pull request Jun 25, 2023
@Tictim Tictim deleted the fluid_tank_list branch September 8, 2023 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: refactor Suggestion to refactor a section of code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants