Introduction
There are many mods that want to create a method of generating, distributing and using "energy" or fluids through a network of custom blocks. Redstone is the built-in power system.
Generally, each power system has three types of blocks: source, conductor, and sink. The source produces the power, the conductor carries the power over some distance and the sink uses the power to do something. For example, in an electric power system a generator would be the source, a wire would be the conductor and a light would be the sink.
One thing that can differ between power systems is whether there is different strengths of power. For example, for Redstone, it doesn't use strength because a sink will activate the same way if multiple sources are connected to it and will operate the same way if the sink is farther from the source. In contrast an accurate electrical power system would provide twice as much power if two generators were connected.
When implementing a power system, generally you have custom blocks (often with TileEntities but can also just use metadata block states) that have a field representing whether it has power. In a system where the strength of the power matters then you would have an int type field with the power level, and in a system where the strength did not matter then you would have a boolean type field that just indicated power or no power.
Algorithm For Distribution Of Power
The trickiest part of a power system is detecting connections between the source and the sink. This generally requires using a programming method called "recursion" to follow all the possible paths between the source and the sink to check that there is at least on conducting path. The problem with recursion algorithms is that performance gets worse quickly as the range of the possible paths increases. That is why Redstone (and most other power system APIs) limit the range of the conductors. If you're making your own power system pathfinding you need to consider limiting the range, or putting other restriction (like maybe only allowing connections in straight lines) to simplify. Also, you may not want to run the updates every tick but rather occasionally to spread out the calculation.
Here is a description of how you could make a power distribution algorithm that finds connections:
- Imagine you have a generator block, a wire block, and a light-bulb block. We'll use a system where the strength does not matter (so lightbulb will glow the same with more generators attached).
- The generator block could have a block state for on / off or maybe some fuel level (this might need a tile entity if you have a complex fuel system).
- The wires will look at all surrounding blocks and check first for any generators. If there are generators that are on neighboring the wire block it will take on a "distance from generator" of 15 (max value for metadata of a block). If the wire block does not find any generators on around, it will look for neighboring wire blocks and will take on a "distance from generator" value of one less (to minimum value of 0) than the minimum value of any of the surrounding wire blocks.
- A light-bulb block will look at all surrounding blocks for generators and if there is at least one generator on neighboring then the light-bulb will be on. If the light-bulb doesn't find generators it will look for neighboring wires that have a "distance from generator" of greater than 0, in which case the light-bulb will turn on.
If you think about the above, it will automatically figure out if there is a connection over a few ticks of processing. Imagine blocks placed with a generator, then three wire blocks, then a light-bulb and consider how the energy flows per tick:
- generator(on) -- wire1(0) -- wire2(0) -- wire3(0) -- lightbulb(off)
- generator(on) -- wire1(16) -- wire2(0) -- wire3(0) -- lightbulb(off)
- generator(on) -- wire1(16) -- wire2(15) -- wire3(0) -- lightbulb(off)
- generator(on) -- wire1(16) -- wire2(15) -- wire3(14) -- lightbulb(off)
- generator(on) -- wire1(16) -- wire2(15) -- wire3(14) -- lightbulb(on)
Redstone -- Minecraft's Built-In Power System
TheGreyGhost provides some explanation and examples in his tutorial here.
In addition to power source and sink, the redstone system includes logic elements such as comparators to help create complex energy dynamics.
You can certainly create your own custom blocks that interact with redstone however it is likely that if you're reading this you really want a system that handles a different type of energy (like electricity, mana, etc.)
Redstone Flux ("RF") API
The Team CoFH created an API for a commonly used power system. It is documented here. This was a very good system but isn't always guaranteed to be up to date with latest versions of Forge.
EnergyHandler Capability -- Forge's Built-In Power System
In recent versions (in builds after Sept 12 2016 for version 1.10.2) of Forge, they have added a built-in power system for CapabilityEnergy and IEnergyStorage.
According to the Forge source code: this system is "derived from the Redstone Flux power system designed by King Lemming and originally utilized in Thermal Expansion and related mods. Created with consent and permission of King Lemming and Team CoFH. Released with permission under LGPL 2.1 when bundled with Forge."
Key Point: It is recommended that modders move to this system instead of the RF API.
Warning: This system is intentionally very "lightweight" meaning it essentially simply allows you to indicate whether something "has energy" and make it "give energy". So you would still have to do a lot of work to figure out the custom mechanics of your system. However, the benefit is that you can use this interface to mix energy sources from different mods -- like any other mod that provides energy could be used to power your system (if you want it to).
Here is LexManos' explanation of how you might discriminate between which types of energy can power your system:
- IF modders want to be discriminating and create sub-energy systems then they can. It's as simple as 'public interface ThaumcraftMana extends IEnergyStorage{}' Caps.register(ThamcraftMana.class...)
- There you go the code works exactly the same. The point of this particular implementation is for things that don't care. And to serve as a base for other systems on how to do it cleanly. Its also up to modders to do their own energy networks, pipe systems, generators, anything that is actually end user facing content.
- Ideally, and just off the cuff, what I imagine people would do is for consumers like a furnace, they just implement this capability and don't care whatever it is providing. But specific non-generic things like say a Thaumcraft {I keep using this as its a cool mod and the first that pops to mind with a specific energy system} which have a Magical Furnace that they only want powered by mana, they discriminate in their getCapability() method. But their providers, like the RF fluxducts, would do something like
- IEnergyStorage sink = te.getCap(REDSTONE_FLUX, TOP);
- if (sink == null) //Its not specifically RF, does it not care?
- sink = te.getCap(ENERGY, TOP)
- if (sink != null)
- doStuff!
- The above example would allow the energy, code wise to be treated exactly the same for the 'generic' and for the RF, or Mana, or EU. But it ALSO allows things to say which specific type the want IF they care about the specific type.
Implementing Energy Storage In An Item
Thanks to Choonster for this tip.
Capabilities can work with items as well as blocks. To store energy in an item you need to:
- Create an implementation of ICapabilityProvider to provide the IEnergyStorage instance.
- The ICapabilityProvider also needs to implement INBTSerializable to save the IEnergyStorage to NBT. Because you often need to implement these interfaces together, Forge provides the ICapabilitySerializable interface which is simply a combination of the two.
- Once you have the ICapabiltiyProvider implementation, @Overrride theItem#initCapabilities to create and return an instance of it.
Note: This isn't specific to the energy capability, it applies to any capability you want an item to provide.
Conclusion
It is still viable to create fully custom power systems, although it is recommended to start using the IEnergyStorage interface to aid compatibility with other systems. Many of the popular systems in use have source code that is public so you should review those systems to get a better understanding of how they work.
No comments:
Post a Comment