The main way of interacting with the api, from a value tweaking standpoint, is through datapack entries. The schema of which is as follows:
{
// === Only present in tagged registries (`TaggedPollutionRegistryResolver` vs `UntaggedPollutionRegistryResolver`) ===
"tags": [
// List of tags and the value associated with them
{
// Contains a "tag" key and a "value" key
// The following would cause any vanilla log to produce 10 of this pollution on its destruction
"tag": "minecraft:logs", // Must be a tag applicable to the backing tag registry
"value": 10 // The amount of pollution created by the destruction of something matching the tag
}
],
// === Present in either version ===
"values": {
// Entries of `ResourceLocation` to `long` values
// These represent specific values for specific objects
// e.g.
"minecraft:oak_log": 20 // This would mean that destroying an oak log would create 20 of this pollution
}
}The location of these entries is dependent on which registry they are intended for, and by extension what ResourceLocation was used
for its creation. For example, creating a pollution file for the pollution pollution_mod:heavy_metals for a registry with the
location compressed_pollution:pollutions/item (the location of Compressed Pollution's default item pollution registry) would
require this structure:
<Source directory>
└ resources
└ data
└ pollution_mod ────────────── Pollution's namespace
└ compressed_pollution ─────────┐
└ pollutions ─────────────────┼ Registry's path
└ item ─────────────────────┘
└ heavy_metals.json ── Pollution's name
When resolving an object's pollution values, the resolver's cache is first checked to see if the values have already been recently computed, and uses that if one is present. If there is a cache miss, the registry's entries are iterated over to check what it produces.
For each registry entry the values key is checked first for a match for the ResourceLocation
that the toRL() function produced from the object. If a match is found, then the match's value is used and the next entry
is processed, but if not and the resolver is a TaggedPollutionRegistryResolver
then the tags key is iterated over in order until either isTag() returns true or the end of the list is encountered.
If the former case is encountered first, then that match's value is used, else if the latter is met or the resolver is an
UntaggedPollutionRegistryResolver
then the entry is ignored and no value is produced for it.
Both options allow for 0 to be used as a value, giving the option to "short-circuit" the operation and cause none of that type of pollution for that object, regardless of any later matches that would normally be found.
Both types of resolvers have been brought up thus far, so it begs the question, "How does one even use these?"
Each resolver class provides a static utility method for quickly creating its respective resolver, being
called create() in both classes. This function takes in a DataPackRegistryEvent$NewRegistry event and the necessary
components to create a resolver, and returns an instance using the arguments passed in. Examples of this function being
used can be seen in the BuiltInResolvers
class, although both are using TaggedPollutionRegistryResolver, but the untagged version is extremely similar with a just
a few less parameters.
Both resolver classes provide a few methods to use them, being resolve() and the two fireEvent() methods. The former
is mostly used internally, and goes through the process of resolving the pollution values for the provided object, as
described above.
The latter is what will be used most commonly, as it handles both the firing of the event and the obtaining of the pollution
values for the provided object. The two methods are extremely similar, but with the notable difference being the trans
parameter, which is a function that gets called on the resolved pollution object before it gets sent off for the event.
This is mostly useful for objects that can appear in groups that get destroyed all at once, such as items in stacks, or
fluids being dealt with by the millibucket.
For using these custom registries, view extending.md
Data generation for the registries created by the resolver create() methods are roughly the same as what would be done
for registries created by normal means, providing the registry key and the objects to be added. The registry key for the
resolver can be obtained from the getRegistryKey() method provided by the instance, and the registry entry can be
constructed by the class' Builder subclass. An (old but still mostly accurate) example can be found here.
Do note that the link goes to a version of the file that was long before the simplification of the resolver,
so references to an X_REGISTRY_KEY can be replaced with the getRegistryKey() call mentioned earlier
There is a single event provided by this api, that being PollutionEvent.
This is a generic fired whenever pollution is about to be applied to a Level that can be filtered to when a specific
class "type" causes it, e.g. a stack of items being dropped in lava fires a PollutionEvent for Item.class, a fluid
source block fires for Fluid.class, etc.
For the handlers, there is a fair bit of information to use, being:
- The object causing the pollution via
getObj() - The
Levelthat the pollution is going to be applied to viagetLevel() - Where the pollution is being caused (if applicable) via
getSourcePos()- This function will return null if there's no viable "source" position of the pollution, such as passive global increases
- And the pollution that will be applied via
getPollution()
All of this provides the event handler with a lot of options regarding how the pollution should be handled, be it actively modified, canceled outright, or simply left alone.
There are two main ways to fire off this event. The first was mentioned previously, being the fireEvent() methods on the
resolver classes, which creates the event based on the arguments passed to it as well as the data of the resolver itself.
The second, which fireEvent() is backed by, is CompressedPollution#handlePollution(), which is a more direct way
of causing the event, allowing explicit assignment of each value in the event. This static function passes off
the parameters to the event batcher, which deduplicates and merges pollution objects that would cause the same event to
be fired. The events themselves are dispatched at the end of the server's tick, to be passed onto the handlers to cause
whatever changes or cancellations they end up doing.
There's been a lot of talk pollution thus far, more specifically how to create it. But that does leave the question, how do you interact with this pollution once it's been created?
LevelPollution objects are attached to levels, and are where the pollution from the events gets put after they've
passed through the handlers. Obtaining the object for a level is done via the getFromLevel() static function in the
same class, providing the level you want to get the pollution levels for as the argument. The values of different
pollutants can be queried through getPollutant() and modified via setPollutant() and addPollutant(). Do note
that the functions that modify the pollutant values do not trigger event handlers, they get applied as-is without
allowing for any modification before-hand.