Skip to content

Commit

Permalink
Port fabric-data-attachment-api-v1
Browse files Browse the repository at this point in the history
  • Loading branch information
Su5eD committed Jun 25, 2024
1 parent 5264072 commit d82d50b
Show file tree
Hide file tree
Showing 24 changed files with 184 additions and 1,075 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
import java.util.Objects;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

import net.fabricmc.fabric.impl.attachment.AttachmentTypeImpl;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.neoforged.neoforge.attachment.IAttachmentHolder;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -74,7 +77,7 @@ public interface AttachmentTarget {
*/
@Nullable
default <A> A getAttached(AttachmentType<A> type) {
throw new UnsupportedOperationException("Implemented via mixin");
return ((IAttachmentHolder) this).getExistingData(((AttachmentTypeImpl<A>) type).internalType()).orElse(null);
}

/**
Expand Down Expand Up @@ -139,13 +142,11 @@ default <A> A getAttachedOrCreate(AttachmentType<A> type, Supplier<A> initialize
* @return the attached data, initialized if originally absent
*/
default <A> A getAttachedOrCreate(AttachmentType<A> type) {
Supplier<A> init = type.initializer();

if (init == null) {
if (type.initializer() == null) {
throw new IllegalArgumentException("Single-argument getAttachedOrCreate is reserved for attachment types with default initializers");
}

return getAttachedOrCreate(type, init);
return ((IAttachmentHolder) this).getData(((AttachmentTypeImpl<A>) type).internalType());
}

/**
Expand All @@ -159,8 +160,7 @@ default <A> A getAttachedOrCreate(AttachmentType<A> type) {
*/
@Contract("_, !null -> !null")
default <A> A getAttachedOrElse(AttachmentType<A> type, @Nullable A defaultValue) {
A attached = getAttached(type);
return attached == null ? defaultValue : attached;
return ((IAttachmentHolder) this).getExistingData(((AttachmentTypeImpl<A>) type).internalType()).orElse(defaultValue);
}

/**
Expand All @@ -176,8 +176,7 @@ default <A> A getAttachedOrElse(AttachmentType<A> type, @Nullable A defaultValue
default <A> A getAttachedOrGet(AttachmentType<A> type, Supplier<A> defaultValue) {
Objects.requireNonNull(defaultValue, "default value supplier cannot be null");

A attached = getAttached(type);
return attached == null ? defaultValue.get() : attached;
return ((IAttachmentHolder) this).getExistingData(((AttachmentTypeImpl<A>) type).internalType()).orElseGet(defaultValue);
}

/**
Expand All @@ -190,7 +189,7 @@ default <A> A getAttachedOrGet(AttachmentType<A> type, Supplier<A> defaultValue)
*/
@Nullable
default <A> A setAttached(AttachmentType<A> type, @Nullable A value) {
throw new UnsupportedOperationException("Implemented via mixin");
return ((IAttachmentHolder) this).setData(((AttachmentTypeImpl<A>) type).internalType(), value);
}

/**
Expand All @@ -201,7 +200,7 @@ default <A> A setAttached(AttachmentType<A> type, @Nullable A value) {
* @return whether there is associated data
*/
default boolean hasAttached(AttachmentType<?> type) {
throw new UnsupportedOperationException("Implemented via mixin");
return ((IAttachmentHolder) this).hasData(((AttachmentTypeImpl<?>) type).internalType());
}

/**
Expand All @@ -214,7 +213,7 @@ default boolean hasAttached(AttachmentType<?> type) {
*/
@Nullable
default <A> A removeAttached(AttachmentType<A> type) {
return setAttached(type, null);
return ((IAttachmentHolder) this).removeData(((AttachmentTypeImpl<A>) type).internalType());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,53 @@

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;
import net.fabricmc.fabric.mixin.attachment.AttachmentHolderAccessor;
import net.fabricmc.fabric.mixin.attachment.AttachmentTypeAccessor;
import net.neoforged.neoforge.attachment.AttachmentType;
import net.neoforged.neoforge.attachment.IAttachmentHolder;

import java.util.Map;

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.transfer(oldPlayer, newPlayer, !alive)
);
ServerEntityWorldChangeEvents.AFTER_ENTITY_CHANGE_WORLD.register(((originalEntity, newEntity, origin, destination) ->
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.transfer(previous, converted, true)
);
}

@Override
public void onInitialize() {
ServerPlayerEvents.COPY_FROM.register((oldPlayer, newPlayer, alive) ->
transfer(oldPlayer, newPlayer, true, !alive)
);
ServerEntityWorldChangeEvents.AFTER_ENTITY_CHANGE_WORLD.register((originalEntity, newEntity, origin, destination) ->
transfer(originalEntity, newEntity, false, false)
);
// using the corresponding player event is unnecessary as no new instance is created
ServerLivingEntityEvents.MOB_CONVERSION.register((previous, converted, keepEquipment) ->
transfer(previous, converted, false, true)
);
}

/**
* 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 net.fabricmc.fabric.api.attachment.v1.AttachmentType#copyOnDeath()} will be transferred.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void transfer(IAttachmentHolder original, IAttachmentHolder target, boolean copyOnlyNonSerializable, boolean isDeath) {
Map<AttachmentType<?>, ?> attachments = ((AttachmentHolderAccessor) original).invokeGetAttachmentMap();

if (attachments == null) {
return;
}

for (Map.Entry<AttachmentType<?>, ?> entry : attachments.entrySet()) {
AttachmentType type = entry.getKey();
net.fabricmc.fabric.api.attachment.v1.AttachmentType<?> fabricType = AttachmentRegistryImpl.getFabricAttachmentType(type);
if (fabricType != null && (!copyOnlyNonSerializable || ((AttachmentTypeAccessor) (Object) type).getSerializer() == null) && (!isDeath || fabricType.copyOnDeath())) {
target.setData(type, entry.getValue());
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,74 +16,85 @@

package net.fabricmc.fabric.impl.attachment;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

import com.mojang.serialization.Codec;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry;
import net.fabricmc.fabric.api.attachment.v1.AttachmentType;
import net.fabricmc.fabric.mixin.attachment.BaseMappedRegistryAccessor;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.neoforge.registries.NeoForgeRegistries;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

public final class AttachmentRegistryImpl {
private static final Logger LOGGER = LoggerFactory.getLogger("fabric-data-attachment-api-v1");
private static final Map<ResourceLocation, AttachmentType<?>> attachmentRegistry = new HashMap<>();
private static final Map<net.neoforged.neoforge.attachment.AttachmentType<?>, AttachmentType<?>> FABRIC_ATTACHMENT_TYPES = new HashMap<>();

public static <A> void register(ResourceLocation id, AttachmentType<A> attachmentType) {
AttachmentType<?> existing = attachmentRegistry.put(id, attachmentType);
public static <A> net.neoforged.neoforge.attachment.AttachmentType<A> register(ResourceLocation id, net.neoforged.neoforge.attachment.AttachmentType<A> attachmentType) {
((BaseMappedRegistryAccessor) NeoForgeRegistries.ATTACHMENT_TYPES).invokeUnfreeze();
Registry.register(NeoForgeRegistries.ATTACHMENT_TYPES, id, attachmentType);
NeoForgeRegistries.ATTACHMENT_TYPES.freeze();
return attachmentType;
}

if (existing != null) {
LOGGER.warn("Encountered duplicate type registration for id " + id);
}
}
public static <A> AttachmentRegistry.Builder<A> builder() {
return new BuilderImpl<>();
}

@Nullable
public static AttachmentType<?> get(ResourceLocation id) {
return attachmentRegistry.get(id);
}
@SuppressWarnings("unchecked")
public static <A> AttachmentType<A> getFabricAttachmentType(net.neoforged.neoforge.attachment.AttachmentType<A> neoType) {
return (AttachmentType<A>) FABRIC_ATTACHMENT_TYPES.get(neoType);
}

public static <A> AttachmentRegistry.Builder<A> builder() {
return new BuilderImpl<>();
}
public static class BuilderImpl<A> implements AttachmentRegistry.Builder<A> {
@Nullable
private Supplier<A> defaultInitializer = null;
@Nullable
private Codec<A> persistenceCodec = null;
private boolean copyOnDeath = false;

public static class BuilderImpl<A> implements AttachmentRegistry.Builder<A> {
@Nullable
private Supplier<A> defaultInitializer = null;
@Nullable
private Codec<A> persistenceCodec = null;
private boolean copyOnDeath = false;
@Override
public AttachmentRegistry.Builder<A> persistent(Codec<A> codec) {
Objects.requireNonNull(codec, "codec cannot be null");

@Override
public AttachmentRegistry.Builder<A> persistent(Codec<A> codec) {
Objects.requireNonNull(codec, "codec cannot be null");
this.persistenceCodec = codec;
return this;
}

this.persistenceCodec = codec;
return this;
}
@Override
public AttachmentRegistry.Builder<A> copyOnDeath() {
this.copyOnDeath = true;
return this;
}

@Override
public AttachmentRegistry.Builder<A> copyOnDeath() {
this.copyOnDeath = true;
return this;
}
@Override
public AttachmentRegistry.Builder<A> initializer(Supplier<A> initializer) {
Objects.requireNonNull(initializer, "initializer cannot be null");

@Override
public AttachmentRegistry.Builder<A> initializer(Supplier<A> initializer) {
Objects.requireNonNull(initializer, "initializer cannot be null");
this.defaultInitializer = initializer;
return this;
}

this.defaultInitializer = initializer;
return this;
}
@Override
public AttachmentType<A> buildAndRegister(ResourceLocation id) {
net.neoforged.neoforge.attachment.AttachmentType<A> neoType = register(id, toNeoForgeAttachmentType());
AttachmentType<A> attachmentType = new AttachmentTypeImpl<>(neoType, id, defaultInitializer, persistenceCodec, copyOnDeath);
FABRIC_ATTACHMENT_TYPES.put(neoType, attachmentType);
return attachmentType;
}

@Override
public AttachmentType<A> buildAndRegister(ResourceLocation id) {
var attachment = new AttachmentTypeImpl<>(id, defaultInitializer, persistenceCodec, copyOnDeath);
register(id, attachment);
return attachment;
}
}
private net.neoforged.neoforge.attachment.AttachmentType<A> toNeoForgeAttachmentType() {
net.neoforged.neoforge.attachment.AttachmentType.Builder<A> builder = net.neoforged.neoforge.attachment.AttachmentType.builder(this.defaultInitializer != null ? this.defaultInitializer : () -> null);
if (this.persistenceCodec != null) {
builder.serialize(this.persistenceCodec);
if (this.copyOnDeath) {
builder.copyOnDeath();
}
}
return builder.build();
}
}
}
Loading

0 comments on commit d82d50b

Please sign in to comment.