Skip to content

Instantly share code, notes, and snippets.

@gigaherz
Created October 21, 2018 20:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gigaherz/ea82a4cd3daa0df8a45e9fc9ccf428ef to your computer and use it in GitHub Desktop.
Save gigaherz/ea82a4cd3daa0df8a45e9fc9ccf428ef to your computer and use it in GitHub Desktop.

An attempt at explaining the purpose and design of capabilities

Capabilitites are a concrete solution to an abstract problem.

The problem

In order to implement an api like RF, you have to implement interfaces in your tileentities or items or such. If you are implementing 20 different APIs for a tech mod they pile up into the same TE-

class MyTileEntity extends TileEntity implements EnergyInterface1,  EnergyInterface2, EnergyInterface3, FluidsInterface1, FluidsInterface2, FluidsInterface3, ItemsApi1, ItemsApi2, ItemsApi3, ComputerApi1, ComputerApi2, ...

On top of that, if you want to add any of those APIs into a 3rdparty object, you gotta ASM the shit out of it. Which is an ugly thing to do, SPECIALLY to other mods.

Enter capabilities

Why not, instead of having all those interfaces implemented through inheritance, use composition instead? All you need is a replacement for the existing needs of the interface: instanceof and type casting.

You need a function to query if the capability provider is implementing an interface, and a function to get the reference to that interface.

And what do you know, look at ICapabilityProvider: hasCapability -> serves the same function as instanceof, and getCapability -> serves the same function as type casting (except it's "safe" in the sense it won't crash if you didn't check hasCapability first).

Now, in order to identify which capability you want to request, you need some object that tells you the capability.

x instanceof Something -> x.getCapability(<Something?>)

They could have used the class, but that has certain annoyances, mostly that it's a hard dependency on Something.class, for both the provider and the consumer.

x.getCapability(Something.class)
if (capability == Something.class) return y;

So to work around that, the capability system has a registry for proxy objects Capability<T>. These objects work in place of the actual type of the capability, and since the generic parameter is erased at compile-time, it forms a soft dependency, not a hard one.

This is why the api works through the annotation:

@CapabilityInject(Something.class)
public static Capability<Something> SMTH;

Now the weird part.

The capability system was originally designed with things like pipes and cables in mind. Blocks are in a 3D grid, where they have beighbours, and even the player acts on a block from one of their sides, so apabilitites were designed to take a EnumFacing as a parameter, to remove that load off the APIs, and put it in the capability system.

This is weird because Forge chose to use the same interfaces on every capability-containing object, even those that aren't so explicitly "sided". And it deemed a feature, that you could still be able to specify a side in other contexts, such as for entitites, the side could be used to differentiate equipment slots.

This does mean that you have a seemingly useless in ItemStacks, Chunks, Worlds, etc. but it's something you'll have to jsut get used to.

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