Repository: FabricMC/fabric

Issue #1049: Hooks to allow registration of custom tracked data fields on entities.
Created: 2020-08-27T05:00:05Z
Note half the stuff here may be gibberish, this is just a concept
Some context
In vanilla Minecraft, tracked data is typically used to sync data values of an entity to the client.
For example this is used for tracking the amount of stingers or arrows a living entity has stuck in itself.
The problem
However there are two fundamental issues with this system that make it harder for mods to use this system for adding custom data to track on existing entities:
Tracked data values are keyed via network id, between 0 and 254. So each entity is only allowed to have 254 registered tracked data values at once. The id of each tracked data value is assigned is based on registration order at .
Mods CANNOT register their tracked data values before vanilla or they risk having the client crash. Typically the solution to this has been to create the tracked data fields in a mixin so load order isn't affected. And then injecting at TAIL of initDataTracker to start tracking those values.
So a slightly different load order can cause a tracked data value to be incorrectly tracked and cause the client to crash due to de-serializing tracked data of the wrong type. This makes the built-in tracked data system fragile if mods directly touch entity code for these matters.
Also custom entities with their own tracked data is another important aspect to consider if we wish to use a system to work around vanilla's system.
What about working with vanilla's system
In my opinion, vanilla's system could work but we would need to do the following:

Abstract the entire process of registering mod added tracked data onto an entity.
In the case of mismatches, remap any mismatched custom tracked data keys (whether we do this on client or server is a different question).

Issues

Mods which currently mixin to add custom tracked data are effectively in the wild west till all those mods adopt this system.
Vanilla system has a limit of 255 entries per entity. In some absurdly large modpacks this could cause issues
Clients joining other servers with more mods than server could has tracked data id mismatches which are very hard to track.

Summary of vanilla
The above explanation leads me to believe salvaging vanilla's system is inadequate due to size limit and reliance on int ids.
A new subsystem
So I shall propose a concept for a separate subsystem that replicates the functionality of tracked data while being much more flexible and compatible.
Setting course
In order for this new system to be resilient to the flaws that the vanilla data tracker system has, we should avoid use of int value keys for syncing. Unless we have a specific sync packet that will ensure we have the proper client-server mapping
Using registry(ies)?
Possibly handle these via registries of custom tracked data values? This would allow us to use more efficient int ids for synchronizing tracked data since registry sync will handle this. However registry sync mismatches like the block id mismatch could be fatal in this case due to serialization of data.
Thinking simply
To keep things simple, I recommend that custom tracked data should be registered via an identifier key with a corresponding (de)serializer. The tracked data serializer would be similar to the following:
interface TrackedDataSerializer<T> {
    void serialize(PacketByteBuf buf, T value, DynamicRegistryManager registryManager);

    T deserialize(PacketByteBuf buf, DynamicRegistryManager registryManager) throws DataDeserializationException;
}
The dynamic registry manager is present in case implementors of api wish to track values in the dynamic registries.
The exception is not final in name or even existence, the idea there is to throw an exception if the value could not be de-serialized for any reason.
We need to determine how to react to this kind of error (please comment below with ideas).
Serializers would be registered via a simple static method:
public static <T> TrackedDataType<T> registerTrackedDataType(Identifier id, TrackedDataSerializer<T> serializer) {
    [...]
}
Duplicate entries are not allowed in the method.
At login (this would depend on networking v1), the client will validate it supports all the registered tracked data types the server notifies the client about. If there are any missing tracked data types, then disconnect the client with and make disconnect message the list of missing tracked data types.
Registering tracked data onto the entity
For use with entities, there are multiple ways to approach this. Fabric's own tracked data should respect inheritance like in vanilla.
This needs further discussion but registration using entity classes may be a solution.
Using this on entities
Since the value has been registered, you would use the TrackedDataType to obtain the value from the entity. To do this, get the TrackedEntityData from the entity. With this interface, you can query multiple aspects about an entity's tracked data.
interface TrackedEntityData {
    boolean supports(TrackedDataType<T> dataType);

    void setValue(TrackedDataType<T> dataType, T value);

    void getValue(TrackedDataType<T> dataType);
}
When getting/setting a value, if the tracked data type is not supported, an exception should be thrown.
An example of this in use:
class MyEntity extends LivingEntity {
    private Foo foo = DefaultFoo.FOO;
    [...]

    void doThing() {
        if (!world.isClient()) {
            // Send the current foo value to the client
            TrackedEntityData.get(this).setValue(MyTrackedDataTypes.FOO, this.foo)
        }
    }
}
Networking internals
Since Fabric API will manage the creation and handling of packets for these values, the contents of the packet will be mostly internal. My proposal uses the fabric:private/tracked_data as the channel name. This is in the private section since implementors of the api are not intended to send the packet.
The packet is defined using the following data structure:
fabric:private/tracked_data {
    Int // Refers to network id used to identify entity on client
    Identifier // Id of tracked data
    PacketByteBuf // Serialized value of tracked data
}

If the entity referred to in the packet via network id is not present, the implementation should release the payload and fail silently.
If the data cannot be de-serialized, log the error and ignore the packet.

Duplicate issue #3482: Syncing for TrackedDataHandlerRegistry
Created: 2023-12-25T18:57:44Z
Currently, TrackedDataHandlerRegistry does not use a registry, which means that when TrackedDataHandlers are registered, their numeric ID is simply incremented. As such, if the mod loading order differs between client and server, or if their mod lists don't match exactly, there is a very real possibility that these IDs will mismatch, corrupting any EntityTrackerUpdateS2CPacket that involve custom TrackedDataHandlers.
This issue proposes that some system be added to sync these IDs. This could be resolved by either migrating the class to a registry, or by setting up some kind of sync to synchronize these IDs on join. I personally believe using a registry would be the ideal approach here since registry syncing already exists.

Closing comment (2024-01-07T04:10:36Z): Duplicate of #1049