It is encouraged for {@link A} to be an immutable data type, such as a primitive type * or an immutable record.
* - *Otherwise, one must be very careful, as attachments must not share any mutable state. + *
Otherwise, it is important to ensure that attachments do not share any mutable state. * As an example, for a (mutable) list/array attachment type, * the initializer should create a new independent instance each time it is called.
* @@ -147,6 +150,15 @@ public final class AttachmentRegistry { */ Builder initializer(Supplier initializer); + /** + * Declares that this attachment type may be automatically synchronized with some clients, as determined by {@code syncPredicate}. + * + * @param packetCodec the codec used to serialize the attachment data over the network + * @param syncPredicate an {@link AttachmentSyncPredicate} determining with which clients to synchronize data + * @return the builder + */ + AttachmentRegistry.Builder syncWith(PacketCodecThe class extends {@link BiPredicate} to allow for custom predicates, outside the ones provided by methods.
+ */ +@ApiStatus.NonExtendable +@FunctionalInterface +public interface AttachmentSyncPredicate extends BiPredicateWhile the API places no restrictions on the types of data that can be attached, it is generally encouraged to use * immutable types. More generally, different attachments must not share mutable state, and it is strongly advised @@ -53,6 +54,9 @@ import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; *
* * @param type of the attached data. It is encouraged for this to be an immutable type. + * @see AttachmentRegistry + * @see AttachmentRegistry.Builder#persistent(Codec) + * @see AttachmentRegistry.Builder#syncWith(PacketCodec, AttachmentSyncPredicate) */ @ApiStatus.NonExtendable @ApiStatus.Experimental @@ -93,6 +97,15 @@ public interface AttachmentType { @Nullable Supplier initializer(); + /** + * Whether this attachment type can be synchronized with clients. This method returning {@code true} does not in any way + * indicate that the attachment type will synchronize data with any given client, only that it is able to, as per its + * {@link AttachmentSyncPredicate}. + * + * @return whether this attachment type is synced + */ + boolean isSynced(); + /** * @return whether the attachments should persist after an entity dies, for example when a player respawns or * when a mob is converted (e.g. zombie → drowned) diff --git a/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentRegistryImpl.java b/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentRegistryImpl.java index f1ce87465..ae6076478 100644 --- a/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentRegistryImpl.java +++ b/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentRegistryImpl.java @@ -16,9 +16,12 @@ package net.fabricmc.fabric.impl.attachment; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Supplier; import com.mojang.serialization.Codec; @@ -26,20 +29,35 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.codec.PacketCodec; import net.minecraft.util.Identifier; import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry; +import net.fabricmc.fabric.api.attachment.v1.AttachmentSyncPredicate; import net.fabricmc.fabric.api.attachment.v1.AttachmentType; +import net.fabricmc.fabric.impl.attachment.sync.AttachmentSync; public final class AttachmentRegistryImpl { private static final Logger LOGGER = LoggerFactory.getLogger("fabric-data-attachment-api-v1"); private static final Map