Skip to content

Instantly share code, notes, and snippets.

@Devan-Kerman
Last active November 16, 2020 19:29
Show Gist options
  • Save Devan-Kerman/c4ec5476161732324594df727b2525fb to your computer and use it in GitHub Desktop.
Save Devan-Kerman/c4ec5476161732324594df727b2525fb to your computer and use it in GitHub Desktop.

Quanitity Types

This is how amounts of fluids are represented, there are a few contenders

Fractions

An object that stores a numerator and denominator.

What it does right
Fractions can represent any fraction, so if your mod needs 1/543, then u can do that.

Problems
The problem is that over/underflow is inevitable, it doesn't matter how hard you try, you will always have overflow (in ms paint form) In this case, the fluid will go around and around, continously being divided in half. Eventually, something has to give. The denominator will overflow. Now, you may say, "what if we just set a limit to how much fractions can be divided". Which, unfortunately, creates more problems than it solves. Here, let's imagine our fraction class:

public class Fraction {
  ...
  public Fraction div(int amount) {...}
}

so in our pipe implementation, we would call this divide function, however if we run into said denominator limit how would we know?

Fraction fraction = ...;
// we ration the fraction between our pipes
Fraction newFraction = fraction.div(2);
// and give it to them
pipe1.transfer(newFraction);
pipe2.transfer(newFraction);
this.pipe.remove(fraction);

// however, if fraction.div(2) caused an underflow, then newFraction would be 0, which would mean the current pipe would lose all it's
// fluid, and the other two pipes would get nothing
// sure, we could add a special case for division, but then all operations on fractions will need special casing (including addition)
// also, I find it very unlikely that everyone will special case, which leads us to our second problem

Fractions hide the inevitable: Overflow. When we use floating point numbers, we often don't think or care about the precision loss. However that's not the case with fluid api. It means: we have to use fuzzy comparison (abs(a-b) < .001), are ok with precision loss (1/3 = .33324343), which in the case of fluid api, is a no-go because it leads to duplication, fluid loss, and worst of all: having .98 buckets of water for a recipe that needs 1 bucket of water.

Bad UI: Fractions are a horrific way of displaying quantities, quick, is 15/17 > 47/51? See the problem here? I think most people generally agree on this, fractioneers included. However, I think we should also realize that it is an inevitability that people will display quantities as fractions when given the chance, and we see it in practice too (cough astromine cough).

Misconceptions

Fractions require an object allocation on every operation and on their instantiation, as well as a GCD operation to simplify the fraction. However, We don't know whether or not this will have a noticable impact on performance in practice because we don't have thicc kitchen sink packs.

Fixed denominator

Fixed denominator is a denominator, chosen ahead of time and all fluids are represented as some fraction of that number. Forge's fluid api uses this system, and it's fixed denominator is 1000 (mb). The current contenders are:

  • 1000: established, used by forge
    • not divisible by 3 (breaks cauldrons/bottles)
    • 1 block != 1 bucket (most annoying part of tinkers tbh)
  • 648: works with all vanilla recipes (ingots, nuggets, iron bars, etc.)
    • new By definition a fixed denominator isn't a 'magic number', because it is a unit, and they all have their reasonings.

What it does right
Fixed denominator is the highest performing option available, longs/ints are stack allocated and go brr in java. Fixed denominator makes no such attempt at hiding the inevitable fluid loss, one of the very first things you learn in java is why 1/2 = 0 (integer division), so everyone knows how to ration and divide without loss already. And we see this in practice too (froge)

Problems
The denominator is fixed, so if you want to have 1/839472 or something as your fluid, you can't. (However in practice, you'll design your mod around the api, like how TiCo makes 1 block = 1296mb)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment