From 6a60afdd2e2287bb560fbcdb33193fde9e82cad6 Mon Sep 17 00:00:00 2001
From: Jochen Jacobs Fabric implements this on {@link Entity}, {@link BlockEntity}, {@link ServerWorld} and {@link WorldChunk} via mixin. Fabric implements this on {@link Entity}, {@link BlockEntity}, {@link ServerWorld} and {@link Chunk} via mixin. Note about {@link BlockEntity} and {@link WorldChunk} targets: these objects need to be notified of changes to their
+ * Note about {@link BlockEntity} and {@link Chunk} targets: these objects need to be notified of changes to their
* state (using {@link BlockEntity#markDirty()} and {@link Chunk#setNeedsSaving(boolean)} respectively), otherwise the modifications will not take effect properly.
* The {@link #setAttached(AttachmentType, Object)} method handles this automatically, but this needs to be done manually
* when attached data is mutable, for example:
@@ -55,6 +55,12 @@ import net.minecraft.world.chunk.WorldChunk;
* which takes care of all vanilla types. However, modded block entities may be coded differently, so be wary of this
* when attaching data to modded block entities.
*
+ * Note about {@link Chunk} targets with {@link ChunkStatus#EMPTY}: These chunks are not saved unless the generation
+ * progresses to at least {@link ChunkStatus#STRUCTURE_STARTS}. Therefore, persistent attachments to those chunks may not
+ * be saved. The {@link #setAttached(AttachmentType, Object)} method will log a warning when this is attempted.
+ *
Note on {@link Entity} targets: in several instances, the name needs to copy data from one {@link Entity} to another. - * These are player respawning, mob conversion, return from the End and cross-world entity teleportation. By default, - * attachments are simply copied wholesale, up to {@link #copyOnDeath()}. Since one entity instance is discarded, - * an attachment that keeps a reference to an {@link Entity} instance can and will break unexpectedly. If, - * for whatever reason, keeping to reference to the target entity is absolutely necessary, be sure to use - * {@link ServerPlayerEvents#COPY_FROM}, {@link ServerEntityWorldChangeEvents#AFTER_ENTITY_CHANGE_WORLD} - * and a mixin into {@link MobEntity#convertTo(EntityType, boolean)} to implement custom copying logic.
+ *Note on {@link Entity} and {@link Chunk} targets: in several instances, the game needs to copy data from one instance to another. + * These are player respawning, mob conversion, return from the End, cross-world entity teleportation, and conversion of a {@link ProtoChunk} to + * {@link WorldChunk}. By default, attachments are simply copied wholesale, up to {@link #copyOnDeath()}. Since one instance is discarded, + * an attachment that keeps a reference to an {@link Entity} or {@link ProtoChunk} instance can and will break unexpectedly. If, + * for whatever reason, keeping a reference to the target is absolutely necessary, be sure to implement custom copying logic. + * For {@link Entity} targets, use {@link ServerPlayerEvents#COPY_FROM}, {@link ServerEntityWorldChangeEvents#AFTER_ENTITY_CHANGE_WORLD}, + * and {@link ServerLivingEntityEvents#MOB_CONVERSION}. For {@link Chunk} targets, mixin into + * {@link WorldChunk#WorldChunk(ServerWorld, ProtoChunk, WorldChunk.EntityLoader)}. + *
* * @param type of the attached data. It is encouraged for this to be an immutable type. */ diff --git a/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentEntrypoint.java b/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentEntrypoint.java index 92e848d6a..dc5fa5810 100644 --- a/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentEntrypoint.java +++ b/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentEntrypoint.java @@ -16,23 +16,28 @@ package net.fabricmc.fabric.impl.attachment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents; import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents; import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; public class AttachmentEntrypoint implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("fabric-data-attachment-api-v1"); + @Override public void onInitialize() { ServerPlayerEvents.COPY_FROM.register((oldPlayer, newPlayer, alive) -> - AttachmentTargetImpl.copyOnRespawn(oldPlayer, newPlayer, !alive) + AttachmentTargetImpl.transfer(oldPlayer, newPlayer, !alive) ); ServerEntityWorldChangeEvents.AFTER_ENTITY_CHANGE_WORLD.register(((originalEntity, newEntity, origin, destination) -> - AttachmentTargetImpl.copyOnRespawn(originalEntity, newEntity, false)) + AttachmentTargetImpl.transfer(originalEntity, newEntity, false)) ); // using the corresponding player event is unnecessary as no new instance is created ServerLivingEntityEvents.MOB_CONVERSION.register((previous, converted, keepEquipment) -> - AttachmentTargetImpl.copyOnRespawn(previous, converted, true) + AttachmentTargetImpl.transfer(previous, converted, true) ); } } diff --git a/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentTargetImpl.java b/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentTargetImpl.java index c10d17f33..6cf81b0be 100644 --- a/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentTargetImpl.java +++ b/fabric-data-attachment-api-v1/src/main/java/net/fabricmc/fabric/impl/attachment/AttachmentTargetImpl.java @@ -28,12 +28,13 @@ import net.fabricmc.fabric.api.attachment.v1.AttachmentType; public interface AttachmentTargetImpl extends AttachmentTarget { /** - * Copies entity attachments when it is respawned and a new instance is created. - * Is triggered on player respawn, entity conversion, return from the End or cross-world entity teleportation. + * Copies attachments from the original to the target. This is used when a ProtoChunk is converted to a + * WorldChunk, and when an entity is respawned and a new instance is created. For entity respawns, it is + * triggered on player respawn, entity conversion, return from the End, or cross-world entity teleportation. * In the first two cases, only the attachments with {@link AttachmentType#copyOnDeath()} will be transferred. - */ + */ @SuppressWarnings("unchecked") - static void copyOnRespawn(AttachmentTarget original, AttachmentTarget target, boolean isDeath) { + static void transfer(AttachmentTarget original, AttachmentTarget target, boolean isDeath) { Map