Skip to content

Commit

Permalink
Data attachment api FABRIC!
Browse files Browse the repository at this point in the history
  • Loading branch information
CodexAdrian committed Mar 27, 2024
1 parent 225bae3 commit c931a4a
Show file tree
Hide file tree
Showing 17 changed files with 382 additions and 47 deletions.
4 changes: 2 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# 3.2.2
# 3.2.3

- Fix class loading error (again)
- Implement Attachments on ItemStacks
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
public interface DataManager<T> {
T getData(Object dataHolder);

T getDataOrThrow(Object dataHolder);

T getDataOrInit(Object dataHolder);

T getDataOrInit(Object dataHolder, T data);

T setData(Object dataHolder, T data);

boolean hasData(Object dataHolder);
Expand Down
12 changes: 12 additions & 0 deletions common/src/test/java/testmod/ManaContainer.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package testmod;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import earth.terrarium.botarium.common.generic.utils.AmountBasedContainer;
import earth.terrarium.botarium.util.Serializable;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;

public class ManaContainer implements AmountBasedContainer, Serializable {
public static final Codec<ManaContainer> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.LONG.fieldOf("Mana").forGetter(ManaContainer::getStoredAmount)
).apply(instance, ManaContainer::new));

long storedAmount = 0;

public ManaContainer() {}

public ManaContainer(long amt) {
storedAmount = amt;
}

@Override
public long getStoredAmount() {
return storedAmount;
Expand Down
6 changes: 6 additions & 0 deletions common/src/test/java/testmod/TestItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List<Compone
long manaCapacity = manaContainer.getCapacity();
tooltip.add(Component.literal("Mana: " + mana + " / " + manaCapacity).setStyle(Style.EMPTY.withColor(ChatFormatting.GRAY)));
}

if (TestMod.MANA_DATA.hasData(stack)) {
ManaContainer dataMana = TestMod.MANA_DATA.getData(stack);
tooltip.add(Component.literal("Mana DATA: " + dataMana.storedAmount + " / " + dataMana.getCapacity()).setStyle(Style.EMPTY.withColor(ChatFormatting.GRAY)));
}
}

/*
Expand All @@ -80,6 +85,7 @@ public InteractionResultHolder<ItemStack> use(Level level, Player player, Intera
if (energyManager != null) {
energyManager.setEnergy(100000);
}
TestMod.MANA_DATA.getDataOrInit(stack).insert(100, false);
}
return InteractionResultHolder.success(player.getMainHandItem());
}
Expand Down
6 changes: 6 additions & 0 deletions common/src/test/java/testmod/TestMod.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package testmod;

import earth.terrarium.botarium.common.data.DataManager;
import earth.terrarium.botarium.common.data.DataManagerRegistry;
import earth.terrarium.botarium.common.energy.EnergyApi;
import earth.terrarium.botarium.common.energy.impl.SimpleEnergyContainer;
import earth.terrarium.botarium.common.energy.impl.UnlimitedEnergyContainer;
Expand Down Expand Up @@ -37,6 +39,7 @@ public class TestMod {
public static final RegistryHolder<Item> ITEMS = new RegistryHolder<>(BuiltInRegistries.ITEM, MOD_ID);

public static final RegistryHolder<Fluid> FLUIDS = new RegistryHolder<>(BuiltInRegistries.FLUID, MOD_ID);
public static final DataManagerRegistry DATA_REG = DataManagerRegistry.create(MOD_ID);
public static final FluidRegistry FLUID_TYPES = new FluidRegistry(MOD_ID);


Expand All @@ -62,6 +65,7 @@ public class TestMod {
public static final BlockContainerLookup<ManaContainer, @Nullable Direction> MANA_LOOKUP_BLOCK = LookupApi.createBlockLookup(new ResourceLocation(MOD_ID, "mana"), ManaContainer.class);
public static final ItemContainerLookup<ManaContainerItem, Void> MANA_ITEM_LOOKUP = LookupApi.createItemLookup(new ResourceLocation(MOD_ID, "mana"), ManaContainerItem.class);

public static final DataManager<ManaContainer> MANA_DATA = DATA_REG.register("mana", ManaContainer::new, ManaContainer.CODEC, true);

@SuppressWarnings("unchecked")
public static void init() {
Expand All @@ -72,6 +76,8 @@ public static void init() {
FLUID_TYPES.initialize();
FLUIDS.initialize();

DATA_REG.initialize();

FluidApi.registerFluidItem(EXAMPLE_ITEM_NO_INTERFACE, stack -> new WrappedItemFluidContainer(stack, new SimpleFluidContainer(FluidConstants.fromMillibuckets(4000), 1, (integer, fluidHolder) -> true)));
FluidApi.registerFluidItem(EXAMPLE_ITEM_UNLIMITED, stack -> new WrappedItemFluidContainer(stack, new UnlimitedFluidContainer(TEST_FLUID_SOURCE.get())));
FluidApi.registerFluidBlock(EXAMPLE_BLOCK_UNLIMITED, (level, blockPos, blockState, blockEntity, direction) -> new UnlimitedFluidContainer(TEST_FLUID_SOURCE.get()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package earth.terrarium.botarium.fabric.data;

import com.mojang.serialization.Codec;
import net.fabricmc.fabric.api.attachment.v1.AttachmentType;
import net.fabricmc.fabric.impl.attachment.AttachmentTargetImpl;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;

@SuppressWarnings({"UnstableApiUsage", "unchecked"})
public class DataUtils {
private static final Logger LOGGER = LoggerFactory.getLogger("botarium-item-data-attachment-impl");

public static <H extends AttachmentTargetImpl> void copyAttachments(H from, H to, Predicate<AttachmentType<?>> filter) {
Map<AttachmentType<?>, ?> attachments = from.fabric_getAttachments();
if (attachments == null) return;
for (var entry : attachments.entrySet()) {
AttachmentType<Object> type = (AttachmentType<Object>) entry.getKey();
var serializer = type.persistenceCodec();
if (serializer != null && filter.test(type)) {
Tag tag = write(type, entry.getValue());
Object data = read(type, tag);
to.setAttached(type, data);
}
}
}

public static <H extends AttachmentTargetImpl> boolean areAttachmentsCompatible(H first, H second) {
Map<AttachmentType<?>, ?> firstAttachments = first.fabric_getAttachments();
Map<AttachmentType<?>, ?> secondAttachments = second.fabric_getAttachments();
if (firstAttachments == null && secondAttachments == null) return true;
if (secondAttachments == null || firstAttachments == null) return false;
for (var entry : firstAttachments.entrySet()) {
AttachmentType<Object> type = (AttachmentType<Object>) entry.getKey();
if (type.persistenceCodec() != null) {
var otherData = secondAttachments.get(type);
Supplier<Object> initializer = type.initializer();
if (otherData == null && initializer != null)
otherData = initializer.get();
if (!Objects.equals(write(type, entry.getValue()), write(type, otherData)))
return false;
}
}
for (var entry : secondAttachments.entrySet()) {
AttachmentType<Object> type = (AttachmentType<Object>) entry.getKey();
if (type.persistenceCodec() != null) {
var data = firstAttachments.get(type);
if (data != null)
continue; // already checked in the first loop
data = type.initializer().get();
if(!Objects.equals(write(type, entry.getValue()), write(type, data))) return false;
}
}
return true;
}

public static <T> Tag write(AttachmentType<T> type, T value) {
AtomicReference<Tag> tag = new AtomicReference<>(new CompoundTag());
Codec<T> codec = type.persistenceCodec();
if (codec == null) return tag.get();
codec.encodeStart(NbtOps.INSTANCE, value)
.get()
.ifRight(partial -> {
LOGGER.warn("Couldn't serialize attachment " + type.identifier() + ", skipping. Error:");
LOGGER.warn(partial.message());
})
.ifLeft(tag::set);
return tag.get();
}

public static <T> T read(AttachmentType<T> type, Tag tag) {
AtomicReference<T> value = new AtomicReference<>(null);
Codec<T> codec = type.persistenceCodec();
if (codec == null) return value.get();
codec.decode(NbtOps.INSTANCE, tag)
.get()
.ifRight(partial -> {
LOGGER.warn("Couldn't deserialize attachment " + type.identifier() + ", skipping. Error:");
LOGGER.warn(partial.message());
})
.ifLeft(parsed -> value.set(parsed.getFirst()));

return value.get();
}

public static <T> T copy(AttachmentType<T> type, T value) {
return read(type, write(type, value));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,33 @@ public T getData(Object dataHolder) {
}
}

@Override
public T getDataOrThrow(Object dataHolder) {
if (dataHolder instanceof AttachmentTarget target) {
return target.getAttachedOrThrow(this.attachmentType);
} else {
throw new IllegalArgumentException(dataHolder + " is not an attachment target");
}
}

@Override
public T getDataOrInit(Object dataHolder) {
if (dataHolder instanceof AttachmentTarget target) {
return target.getAttachedOrCreate(this.attachmentType);
} else {
throw new IllegalArgumentException(dataHolder + " is not an attachment target");
}
}

@Override
public T getDataOrInit(Object dataHolder, T data) {
if (dataHolder instanceof AttachmentTarget target) {
return target.getAttachedOrSet(this.attachmentType, data);
} else {
throw new IllegalArgumentException(dataHolder + " is not an attachment target");
}
}

@Override
public T setData(Object dataHolder, T data) {
if (dataHolder instanceof AttachmentTarget target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import earth.terrarium.botarium.common.data.DataManager;
import earth.terrarium.botarium.common.data.DataManagerBuilder;
import earth.terrarium.botarium.common.data.DataManagerRegistry;
import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry;
import net.fabricmc.fabric.impl.attachment.AttachmentRegistryImpl;
import net.fabricmc.fabric.impl.attachment.AttachmentTypeImpl;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
Expand All @@ -23,6 +25,7 @@ public FabricDataManagerRegistry(String modid) {
public <T> DataManager<T> register(@NotNull String name, @NotNull Supplier<T> factory, @Nullable Codec<T> codec, boolean copyOnDeath) {
ResourceLocation id = new ResourceLocation(modid, name);
var type = new AttachmentTypeImpl<>(id, factory, codec, copyOnDeath);
AttachmentRegistryImpl.register(id, type);
return new FabricDataManager<>(type);
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package earth.terrarium.botarium.mixin;

import com.mojang.serialization.Codec;
import net.fabricmc.fabric.api.attachment.v1.AttachmentType;
import net.fabricmc.fabric.impl.attachment.AttachmentSerializingImpl;
import net.fabricmc.fabric.impl.attachment.AttachmentTargetImpl;
import net.fabricmc.fabric.impl.transfer.item.ItemVariantImpl;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;

import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Objects;

@SuppressWarnings("UnstableApiUsage")
@Mixin({ItemStack.class, ItemVariantImpl.class})
public class ItemAttachmentHolderMixin implements AttachmentTargetImpl {
@Unique
@Nullable
private IdentityHashMap<AttachmentType<?>, Object> fabric_dataAttachments = null;

@SuppressWarnings("unchecked")
@Override
@Nullable
public <T> T getAttached(AttachmentType<T> type) {
return fabric_dataAttachments == null ? null : (T) fabric_dataAttachments.get(type);
}

@SuppressWarnings("unchecked")
@Override
@Nullable
public <T> T setAttached(AttachmentType<T> type, @Nullable T value) {
// Extremely inelegant, but the only alternative is separating out these two mixins and duplicating code
if (value == null) {
if (fabric_dataAttachments == null) {
return null;
}

T removed = (T) fabric_dataAttachments.remove(type);

if (fabric_dataAttachments.isEmpty()) {
fabric_dataAttachments = null;
}

return removed;
} else {
if (fabric_dataAttachments == null) {
fabric_dataAttachments = new IdentityHashMap<>();
}

return (T) fabric_dataAttachments.put(type, value);
}
}

@Override
public boolean hasAttached(AttachmentType<?> type) {
return fabric_dataAttachments != null && fabric_dataAttachments.containsKey(type);
}

@Override
public void fabric_writeAttachmentsToNbt(CompoundTag nbt) {
AttachmentSerializingImpl.serializeAttachmentData(nbt, fabric_dataAttachments);
}

@Override
public void fabric_readAttachmentsFromNbt(CompoundTag nbt) {
fabric_dataAttachments = AttachmentSerializingImpl.deserializeAttachmentData(nbt);
}

@Override
public boolean fabric_hasPersistentAttachments() {
return AttachmentSerializingImpl.hasPersistentAttachments(fabric_dataAttachments);
}

@Override
public Map<AttachmentType<?>, ?> fabric_getAttachments() {
return fabric_dataAttachments;
}

}
Loading

0 comments on commit c931a4a

Please sign in to comment.