diff --git a/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java b/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java
index 904853a9..5de07cae 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java
@@ -1,6 +1,7 @@
package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.api.spell.iota.Iota;
+import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.ChatFormatting;
@@ -23,6 +24,12 @@ import java.util.List;
* and the appropriate cap/CC will be attached.
*/
public interface IotaHolderItem {
+ /**
+ * If this key is set on the item, we ignore the rest of the item and render this as if it were of the
+ * {@link at.petrak.hexcasting.api.spell.iota.IotaType IotaType} given by the resource location.
+ *
+ * This is not useful to the player at all.
+ */
String TAG_OVERRIDE_VISUALLY = "VisualOverride";
@Nullable CompoundTag readIotaTag(ItemStack stack);
@@ -47,6 +54,15 @@ public interface IotaHolderItem {
return null;
}
+ default int getColor(ItemStack stack) {
+ var tag = stack.getTag();
+ if (tag == null) {
+ return HexUtils.ERROR_COLOR;
+ }
+
+ return HexIotaTypes.getColor(tag);
+ }
+
boolean canWrite(ItemStack stack, @Nullable Iota iota);
void writeDatum(ItemStack stack, @Nullable Iota iota);
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt
index d51d53d6..601514ff 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt
@@ -2,9 +2,11 @@
package at.petrak.hexcasting.api.utils
-import at.petrak.hexcasting.api.spell.LegacySpellDatum
import at.petrak.hexcasting.api.spell.SpellList
+import at.petrak.hexcasting.api.spell.iota.Iota
+import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.math.HexCoord
+import at.petrak.hexcasting.common.lib.HexIotaTypes
import net.minecraft.ChatFormatting
import net.minecraft.nbt.*
import net.minecraft.network.chat.*
@@ -85,7 +87,7 @@ fun pxToCoord(px: Vec2, size: Float, offset: Vec2): HexCoord {
else
HexCoord(q, r + (rf + 0.5 * qf).roundToInt())
}
-s
+
fun String.withStyle(op: (Style) -> Style): MutableComponent = asTextComponent.withStyle(op)
fun String.withStyle(style: Style): MutableComponent = asTextComponent.withStyle(style)
fun String.withStyle(formatting: ChatFormatting): MutableComponent = asTextComponent.withStyle(formatting)
@@ -231,13 +233,11 @@ inline operator fun WeakValue.setValue(thisRef: Any?, property: KProperty
/**
* Returns an empty list if it's too complicated.
*/
-fun Iterable>.serializeToNBT(): ListTag {
- val out = LegacySpellDatum.make(SpellList.LList(0, this.toList())).serializeToNBT()
- return if (out.contains(LegacySpellDatum.TAG_WIDGET))
+fun Iterable.serializeToNBT() =
+ if (HexIotaTypes.isTooLargeToSerialize(this))
ListTag()
else
- out.getList(LegacySpellDatum.TAG_LIST, Tag.TAG_COMPOUND)
-}
+ ListIota(SpellList.LList(this.toList())).serialize()
// Copy the impl from forge
fun ItemStack.serializeToNBT(): CompoundTag {
@@ -247,7 +247,7 @@ fun ItemStack.serializeToNBT(): CompoundTag {
}
@Throws(IllegalArgumentException::class)
-fun Tag.downcast(type: TagType) : T {
+fun Tag.downcast(type: TagType): T {
if (this.type == type) {
return this as T
} else {
@@ -256,3 +256,5 @@ fun Tag.downcast(type: TagType) : T {
)
}
}
+
+const val ERROR_COLOR = 0xff_f800f8.toInt()
diff --git a/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java b/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java
index dde3b323..c7bead1b 100644
--- a/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java
+++ b/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java
@@ -3,11 +3,8 @@ package at.petrak.hexcasting.client;
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
-import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.item.MediaHolderItem;
import at.petrak.hexcasting.api.misc.ManaConstants;
-import at.petrak.hexcasting.api.spell.LegacySpellDatum;
-import at.petrak.hexcasting.api.spell.Widget;
import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.client.be.BlockEntityAkashicBookshelfRenderer;
import at.petrak.hexcasting.client.be.BlockEntitySlateRenderer;
@@ -16,10 +13,7 @@ import at.petrak.hexcasting.client.particles.ConjureParticle;
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf;
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicRecord;
import at.petrak.hexcasting.common.entities.HexEntities;
-import at.petrak.hexcasting.common.items.ItemFocus;
-import at.petrak.hexcasting.common.items.ItemScroll;
-import at.petrak.hexcasting.common.items.ItemSlate;
-import at.petrak.hexcasting.common.items.ItemWand;
+import at.petrak.hexcasting.common.items.*;
import at.petrak.hexcasting.common.items.magic.ItemMediaBattery;
import at.petrak.hexcasting.common.items.magic.ItemPackagedHex;
import at.petrak.hexcasting.common.lib.HexBlockEntities;
@@ -29,6 +23,7 @@ import at.petrak.hexcasting.common.lib.HexParticles;
import at.petrak.hexcasting.xplat.IClientXplatAbstractions;
import com.mojang.datafixers.util.Pair;
import net.minecraft.ChatFormatting;
+import net.minecraft.client.color.item.ItemColor;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.Direction;
@@ -49,13 +44,13 @@ import net.minecraft.world.level.material.MaterialColor;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
import java.util.function.UnaryOperator;
public class RegisterClientStuff {
public static void init() {
- registerDataHolderOverrides(HexItems.FOCUS);
- registerDataHolderOverrides(HexItems.SPELLBOOK);
-
registerPackagedSpellOverrides(HexItems.CYPHER);
registerPackagedSpellOverrides(HexItems.TRINKET);
registerPackagedSpellOverrides(HexItems.ARTIFACT);
@@ -107,6 +102,43 @@ public class RegisterClientStuff {
addScryingLensStuff();
}
+ public static void registerColorProviders(BiConsumer colorProviderRegistry) {
+ colorProviderRegistry.accept(makeIotaStorageColorer(
+ stack -> HexItems.FOCUS.readIotaTag(stack) != null,
+ ItemFocus::isSealed,
+ HexItems.FOCUS::getColor),
+ HexItems.FOCUS);
+ colorProviderRegistry.accept(makeIotaStorageColorer(
+ stack -> HexItems.SPELLBOOK.readIotaTag(stack) != null,
+ ItemSpellbook::isSealed,
+ HexItems.SPELLBOOK::getColor),
+ HexItems.SPELLBOOK);
+ }
+
+ /**
+ * Helper function to colorize the layers of an item that stores an iota, in the manner of foci and spellbooks.
+ *
+ * 0 = base; 1 = unsealed overlay; 2 = sealed overlay.
+ */
+ public static ItemColor makeIotaStorageColorer(Predicate hasIota, Predicate isSealed,
+ ToIntFunction getColor) {
+ return (stack, idx) -> {
+ if (idx == 0) {
+ return 0xff_000000;
+ }
+ if (!hasIota.test(stack)) {
+ return 0; // no higher overlays
+ }
+
+ var sealed = isSealed.test(stack);
+ if (!sealed && idx == 1 || sealed && idx == 2) {
+ return getColor.applyAsInt(stack);
+ } else {
+ return 0; // this layer is invisible
+ }
+ };
+ }
+
private static void addScryingLensStuff() {
ScryingLensOverlayRegistry.addPredicateDisplayer(
(state, pos, observer, world, direction, lensHand) -> state.getBlock() instanceof BlockAbstractImpetus,
@@ -253,32 +285,6 @@ public class RegisterClientStuff {
(stack, level, holder, holderID) -> NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID) ? 1f : 0f);
}
- private static void registerDataHolderOverrides(IotaHolderItem item) {
- IClientXplatAbstractions.INSTANCE.registerItemProperty((Item) item, ItemFocus.DATATYPE_PRED,
- (stack, level, holder, holderID) -> {
- var datum = item.readIotaTag(stack);
- String override = NBTHelper.getString(stack, IotaHolderItem.TAG_OVERRIDE_VISUALLY);
- String typename = null;
- if (override != null) {
- typename = override;
- } else if (datum != null) {
- typename = datum.getAllKeys().iterator().next();
- }
-
- return typename == null ? 0f : switch (typename) {
- case LegacySpellDatum.TAG_ENTITY -> 1f;
- case LegacySpellDatum.TAG_DOUBLE -> 2f;
- case LegacySpellDatum.TAG_VEC3 -> 3f;
- case LegacySpellDatum.TAG_WIDGET -> 4f;
- case LegacySpellDatum.TAG_LIST -> 5f;
- case LegacySpellDatum.TAG_PATTERN -> 6f;
- default -> 0f; // uh oh
- };
- });
- IClientXplatAbstractions.INSTANCE.registerItemProperty((Item) item, ItemFocus.SEALED_PRED,
- (stack, level, holder, holderID) -> item.canWrite(stack, LegacySpellDatum.make(Widget.NULL)) ? 0f : 1f);
- }
-
private static void registerPackagedSpellOverrides(ItemPackagedHex item) {
IClientXplatAbstractions.INSTANCE.registerItemProperty(item, ItemPackagedHex.HAS_PATTERNS_PRED,
(stack, level, holder, holderID) ->
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockStoredPlayerImpetus.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockStoredPlayerImpetus.java
index e07c9117..86882450 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockStoredPlayerImpetus.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockStoredPlayerImpetus.java
@@ -1,7 +1,7 @@
package at.petrak.hexcasting.common.blocks.circles.impetuses;
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
-import at.petrak.hexcasting.api.spell.DatumType;
+import at.petrak.hexcasting.api.spell.iota.EntityIota;
import at.petrak.hexcasting.common.blocks.entity.BlockEntityStoredPlayerImpetus;
import at.petrak.hexcasting.common.lib.HexSounds;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
@@ -11,7 +11,6 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
-import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
@@ -53,15 +52,15 @@ public class BlockStoredPlayerImpetus extends BlockAbstractImpetus {
if (datumContainer != null) {
if (pLevel instanceof ServerLevel level) {
var stored = datumContainer.readIota(level);
- if (stored != null && stored.getType() == DatumType.ENTITY) {
- var entity = (Entity) stored.getPayload();
+ if (stored instanceof EntityIota eieio) {
+ var entity = eieio.getEntity();
if (entity instanceof Player player) {
// phew, we got something
tile.setPlayer(player.getGameProfile(), entity.getUUID());
level.sendBlockUpdated(pPos, pState, pState, Block.UPDATE_CLIENTS);
- pLevel.playSound(null, pPos, HexSounds.IMPETUS_STOREDPLAYER_DING, SoundSource.BLOCKS,
- 1f, 1f);
+ pLevel.playSound(pPlayer, pPos, HexSounds.IMPETUS_STOREDPLAYER_DING,
+ SoundSource.BLOCKS, 1f, 1f);
}
}
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java
index a1bc751b..8917c155 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java
@@ -1,9 +1,10 @@
package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.item.IotaHolderItem;
-import at.petrak.hexcasting.api.spell.LegacySpellDatum;
-import at.petrak.hexcasting.api.spell.Widget;
+import at.petrak.hexcasting.api.spell.iota.Iota;
+import at.petrak.hexcasting.api.spell.iota.NullIota;
import at.petrak.hexcasting.api.utils.NBTHelper;
+import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@@ -39,22 +40,22 @@ public class ItemFocus extends Item implements IotaHolderItem {
}
@Override
- public @Nullable LegacySpellDatum> emptyIota(ItemStack stack) {
- return LegacySpellDatum.make(Widget.NULL);
+ public @Nullable Iota emptyIota(ItemStack stack) {
+ return new NullIota();
}
@Override
- public boolean canWrite(ItemStack stack, LegacySpellDatum> datum) {
+ public boolean canWrite(ItemStack stack, Iota datum) {
return datum == null || !NBTHelper.getBoolean(stack, TAG_SEALED);
}
@Override
- public void writeDatum(ItemStack stack, LegacySpellDatum> datum) {
+ public void writeDatum(ItemStack stack, Iota datum) {
if (datum == null) {
stack.removeTagKey(TAG_DATA);
stack.removeTagKey(TAG_SEALED);
- } else if (!NBTHelper.getBoolean(stack, TAG_SEALED)) {
- NBTHelper.put(stack, TAG_DATA, datum.serializeToNBT());
+ } else if (!isSealed(stack)) {
+ NBTHelper.put(stack, TAG_DATA, HexIotaTypes.serialize(datum));
}
}
@@ -63,4 +64,8 @@ public class ItemFocus extends Item implements IotaHolderItem {
TooltipFlag pIsAdvanced) {
IotaHolderItem.appendHoverText(this, pStack, pTooltipComponents, pIsAdvanced);
}
+
+ public static boolean isSealed(ItemStack stack) {
+ return NBTHelper.getBoolean(stack, TAG_SEALED);
+ }
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java
index 8d2e7f8e..ee1cb77b 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java
@@ -1,9 +1,10 @@
package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.item.IotaHolderItem;
-import at.petrak.hexcasting.api.spell.LegacySpellDatum;
-import at.petrak.hexcasting.api.spell.Widget;
+import at.petrak.hexcasting.api.spell.iota.Iota;
+import at.petrak.hexcasting.api.spell.iota.NullIota;
import at.petrak.hexcasting.api.utils.NBTHelper;
+import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@@ -44,11 +45,11 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
@Override
public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltip,
TooltipFlag isAdvanced) {
- boolean sealed = IsSealed(stack);
+ boolean sealed = isSealed(stack);
boolean empty = false;
if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) {
var pageIdx = NBTHelper.getInt(stack, TAG_SELECTED_PAGE);
- int highest = HighestPage(stack);
+ int highest = highestPage(stack);
if (highest != 0) {
if (sealed) {
tooltip.add(new TranslatableComponent("hexcasting.tooltip.spellbook.page.sealed",
@@ -93,7 +94,7 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
@Override
public void inventoryTick(ItemStack stack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
- int index = GetPage(stack, 0);
+ int index = getPage(stack, 0);
NBTHelper.putInt(stack, TAG_SELECTED_PAGE, index);
int shiftedIdx = Math.max(1, index);
@@ -106,14 +107,14 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
}
}
- public static boolean ArePagesEmpty(ItemStack stack) {
+ public static boolean arePagesEmpty(ItemStack stack) {
CompoundTag tag = NBTHelper.getCompound(stack, TAG_PAGES);
return tag == null || tag.isEmpty();
}
@Override
public @Nullable CompoundTag readIotaTag(ItemStack stack) {
- int idx = GetPage(stack, 1);
+ int idx = getPage(stack, 1);
var key = String.valueOf(idx);
var tag = NBTHelper.getCompound(stack, TAG_PAGES);
if (tag != null && tag.contains(key, Tag.TAG_COMPOUND)) {
@@ -124,22 +125,22 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
}
@Override
- public @Nullable LegacySpellDatum> emptyIota(ItemStack stack) {
- return LegacySpellDatum.make(Widget.NULL);
+ public @Nullable Iota emptyIota(ItemStack stack) {
+ return new NullIota();
}
@Override
- public boolean canWrite(ItemStack stack, LegacySpellDatum> datum) {
- return datum == null || !IsSealed(stack);
+ public boolean canWrite(ItemStack stack, Iota datum) {
+ return datum == null || !isSealed(stack);
}
@Override
- public void writeDatum(ItemStack stack, LegacySpellDatum> datum) {
- if (datum != null && IsSealed(stack)) {
+ public void writeDatum(ItemStack stack, Iota datum) {
+ if (datum != null && isSealed(stack)) {
return;
}
- int idx = GetPage(stack, 1);
+ int idx = getPage(stack, 1);
var key = String.valueOf(idx);
CompoundTag pages = NBTHelper.getCompound(stack, TAG_PAGES);
if (pages != null) {
@@ -147,21 +148,21 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
pages.remove(key);
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
} else {
- pages.put(key, datum.serializeToNBT());
+ pages.put(key, HexIotaTypes.serialize(datum));
}
if (pages.isEmpty()) {
NBTHelper.remove(stack, TAG_PAGES);
}
} else if (datum != null) {
- NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, datum.serializeToNBT());
+ NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, HexIotaTypes.serialize(datum));
} else {
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
}
}
- public static int GetPage(ItemStack stack, int ifEmpty) {
- if (ArePagesEmpty(stack)) {
+ public static int getPage(ItemStack stack, int ifEmpty) {
+ if (arePagesEmpty(stack)) {
return ifEmpty;
} else if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) {
int index = NBTHelper.getInt(stack, TAG_SELECTED_PAGE);
@@ -174,8 +175,8 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
}
}
- public static void SetSealed(ItemStack stack, boolean sealed) {
- int index = GetPage(stack, 1);
+ public static void setSealed(ItemStack stack, boolean sealed) {
+ int index = getPage(stack, 1);
String nameKey = String.valueOf(index);
CompoundTag names = NBTHelper.getOrCreateCompound(stack, TAG_SEALED);
@@ -194,15 +195,15 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
}
- public static boolean IsSealed(ItemStack stack) {
- int index = GetPage(stack, 1);
+ public static boolean isSealed(ItemStack stack) {
+ int index = getPage(stack, 1);
String nameKey = String.valueOf(index);
CompoundTag names = NBTHelper.getCompound(stack, TAG_SEALED);
return NBTHelper.getBoolean(names, nameKey);
}
- public static int HighestPage(ItemStack stack) {
+ public static int highestPage(ItemStack stack) {
CompoundTag tag = NBTHelper.getCompound(stack, TAG_PAGES);
if (tag == null) {
return 0;
@@ -216,8 +217,8 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
}).max(Integer::compare).orElse(0);
}
- public static int RotatePageIdx(ItemStack stack, boolean increase) {
- int idx = GetPage(stack, 0);
+ public static int rotatePageIdx(ItemStack stack, boolean increase) {
+ int idx = getPage(stack, 0);
if (idx != 0) {
idx += increase ? 1 : -1;
idx = Math.max(1, idx);
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexIotaTypes.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexIotaTypes.java
index f188e602..4415293c 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexIotaTypes.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexIotaTypes.java
@@ -2,7 +2,9 @@ package at.petrak.hexcasting.common.lib;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.spell.iota.*;
+import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
+import com.mojang.datafixers.util.Pair;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@@ -12,6 +14,8 @@ import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayDeque;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -26,6 +30,8 @@ public class HexIotaTypes {
public static final String
KEY_TYPE = HexAPI.MOD_ID + ":type",
KEY_DATA = HexAPI.MOD_ID + ":data";
+ public static final int MAX_SERIALIZATION_DEPTH = 256;
+ public static final int MAX_SERIALIZATION_TOTAL = 1024;
public static CompoundTag serialize(Iota iota) {
var type = iota.getType();
@@ -35,6 +41,12 @@ public class HexIotaTypes {
"Tried to serialize an unregistered iota type. Iota: " + iota
+ " ; Type" + type.getClass().getTypeName());
}
+
+ // We check if it's too big on serialization; if it is we just return a garbage.
+ if (iota instanceof ListIota listIota && isTooLargeToSerialize(listIota.getList())) {
+ // Garbage will never be too large so we just recurse
+ return serialize(new GarbageIota());
+ }
var dataTag = iota.serialize();
var out = new CompoundTag();
out.putString(KEY_TYPE, typeId.toString());
@@ -42,6 +54,34 @@ public class HexIotaTypes {
return out;
}
+ public static boolean isTooLargeToSerialize(Iterable examinee) {
+ // We don't recurse here, just a work queue (or work stack, if we liked.)
+ // Each element is a found sub-iota, and how deep it is.
+ //
+ // TODO: is it worth trying to cache the depth and size statically on a SpellList.
+ var listsToExamine = new ArrayDeque<>(Collections.singleton(new Pair<>(examinee, 0)));
+ int totalEltsFound = 1; // count the first list
+ while (!listsToExamine.isEmpty()) {
+ var iotaPair = listsToExamine.removeFirst();
+ var sublist = iotaPair.getFirst();
+ int depth = iotaPair.getSecond();
+ for (var iota : sublist) {
+ totalEltsFound++;
+ if (totalEltsFound >= MAX_SERIALIZATION_TOTAL) {
+ return true; // too bad
+ }
+ if (iota instanceof ListIota subsublist) {
+ if (depth + 1 >= MAX_SERIALIZATION_DEPTH) {
+ return true;
+ }
+ listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
+ }
+ }
+ }
+ // we made it!
+ return false;
+ }
+
/**
* This method attempts to find the type from the {@code type} key.
* See {@link HexIotaTypes#getTypeFromTag} for the storage format.
@@ -98,6 +138,14 @@ public class HexIotaTypes {
return type.display(tag);
}
+ public static int getColor(CompoundTag tag) {
+ var type = getTypeFromTag(tag);
+ if (type == null) {
+ return HexUtils.ERROR_COLOR;
+ }
+ return type.color();
+ }
+
@ApiStatus.Internal
public static void registerTypes() {
BiConsumer, ResourceLocation> r = (type, id) -> Registry.register(REGISTRY, id, type);
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java b/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java
index bff44706..75f1cdaa 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java
@@ -60,11 +60,11 @@ public record MsgShiftScrollSyn(InteractionHand hand, double scrollDelta, boolea
}
private void spellbook(ServerPlayer sender, ItemStack stack) {
- var newIdx = ItemSpellbook.RotatePageIdx(stack, this.scrollDelta < 0.0);
+ var newIdx = ItemSpellbook.rotatePageIdx(stack, this.scrollDelta < 0.0);
- var len = ItemSpellbook.HighestPage(stack);
+ var len = ItemSpellbook.highestPage(stack);
- var sealed = ItemSpellbook.IsSealed(stack);
+ var sealed = ItemSpellbook.isSealed(stack);
MutableComponent component;
if (hand == InteractionHand.OFF_HAND && stack.hasCustomHoverName()) {
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/recipe/SealSpellbookRecipe.java b/Common/src/main/java/at/petrak/hexcasting/common/recipe/SealSpellbookRecipe.java
index 3a18cb98..49156f35 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/recipe/SealSpellbookRecipe.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/recipe/SealSpellbookRecipe.java
@@ -22,7 +22,7 @@ public class SealSpellbookRecipe extends ShapelessRecipe {
private static ItemStack getSealedStack() {
ItemStack output = new ItemStack(HexItems.SPELLBOOK);
- ItemSpellbook.SetSealed(output, true);
+ ItemSpellbook.setSealed(output, true);
NBTHelper.putString(output, IotaHolderItem.TAG_OVERRIDE_VISUALLY, "any");
return output;
}
@@ -51,7 +51,7 @@ public class SealSpellbookRecipe extends ShapelessRecipe {
}
if (!out.isEmpty()) {
- ItemSpellbook.SetSealed(out, true);
+ ItemSpellbook.setSealed(out, true);
out.setCount(1);
}
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/empty.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus.png
similarity index 100%
rename from Common/src/main/resources/assets/hexcasting/textures/item/focus/empty.png
rename to Common/src/main/resources/assets/hexcasting/textures/item/focus.png
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/double.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/double.png
deleted file mode 100644
index 8fcfc814..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/double.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/double_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/double_sealed.png
deleted file mode 100644
index 55924d2f..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/double_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/empty_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/empty_sealed.png
deleted file mode 100644
index 67cbfaad..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/empty_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/entity.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/entity.png
deleted file mode 100644
index 7dedb90e..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/entity.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/entity_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/entity_sealed.png
deleted file mode 100644
index ed126ecc..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/entity_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/list.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/list.png
deleted file mode 100644
index a9f1a03f..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/list.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/list_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/list_sealed.png
deleted file mode 100644
index 9701fe2c..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/list_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/pattern.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/pattern.png
deleted file mode 100644
index 0ecb01d3..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/pattern.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/pattern_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/pattern_sealed.png
deleted file mode 100644
index 65e1aa60..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/pattern_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/vec3.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/vec3.png
deleted file mode 100644
index 1435f013..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/vec3.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/vec3_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/vec3_sealed.png
deleted file mode 100644
index 6da7da3b..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/vec3_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/widget.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/widget.png
deleted file mode 100644
index 0910c9b4..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/widget.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus/widget_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus/widget_sealed.png
deleted file mode 100644
index 6c356b7a..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/focus/widget_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus_overlay.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus_overlay.png
new file mode 100644
index 00000000..77083e21
Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/item/focus_overlay.png differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/focus_overlay_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/focus_overlay_sealed.png
new file mode 100644
index 00000000..5f4fb69a
Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/item/focus_overlay_sealed.png differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook.png
new file mode 100644
index 00000000..f6532ab6
Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook.png differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/double.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/double.png
deleted file mode 100644
index 9241b159..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/double.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/double_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/double_sealed.png
deleted file mode 100644
index b66ced29..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/double_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/empty.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/empty.png
deleted file mode 100644
index 0b919d27..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/empty.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/empty_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/empty_sealed.png
deleted file mode 100644
index c5a5c642..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/empty_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/entity.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/entity.png
deleted file mode 100644
index 1332ae6d..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/entity.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/entity_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/entity_sealed.png
deleted file mode 100644
index fec01c29..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/entity_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/list.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/list.png
deleted file mode 100644
index b8872f7a..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/list.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/list_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/list_sealed.png
deleted file mode 100644
index 02553f38..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/list_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/pattern.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/pattern.png
deleted file mode 100644
index 65ca7d05..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/pattern.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/pattern_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/pattern_sealed.png
deleted file mode 100644
index 8c458496..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/pattern_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/vec3.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/vec3.png
deleted file mode 100644
index a4886c82..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/vec3.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/vec3_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/vec3_sealed.png
deleted file mode 100644
index 766704e9..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/vec3_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/widget.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/widget.png
deleted file mode 100644
index 8adec2af..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/widget.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/widget_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/widget_sealed.png
deleted file mode 100644
index 5feb35dd..00000000
Binary files a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook/widget_sealed.png and /dev/null differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook_overlay.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook_overlay.png
new file mode 100644
index 00000000..e6f266d1
Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook_overlay.png differ
diff --git a/Common/src/main/resources/assets/hexcasting/textures/item/spellbook_overlay_sealed.png b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook_overlay_sealed.png
new file mode 100644
index 00000000..08dbe6f0
Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/item/spellbook_overlay_sealed.png differ
diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt
index 36fcecda..244cad32 100644
--- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt
+++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt
@@ -9,6 +9,7 @@ import at.petrak.hexcasting.fabric.network.FabricPacketHandler
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry
+import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
@@ -47,5 +48,8 @@ object FabricHexClientInitializer : ClientModInitializer {
BlockEntityRendererRegistry.register(type, berp)
}
})
+ RegisterClientStuff.registerColorProviders { colorizer, item ->
+ ColorProviderRegistry.ITEM.register(colorizer, item)
+ }
}
}
diff --git a/Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java b/Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java
index 2bf43a09..04d3759d 100644
--- a/Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java
+++ b/Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java
@@ -5,6 +5,7 @@ import at.petrak.hexcasting.client.HexAdditionalRenderers;
import at.petrak.hexcasting.client.RegisterClientStuff;
import at.petrak.hexcasting.client.ShiftScrollListener;
import at.petrak.hexcasting.client.shader.HexShaders;
+import net.minecraft.client.color.item.ItemColors;
import net.minecraftforge.client.event.*;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
@@ -16,9 +17,18 @@ import java.io.IOException;
// This is Java because I can't kotlin-fu some of the consumers
public class ForgeHexClientInitializer {
+ // We copy Fabric's example; it mixes in on the return of the initializer and sticks it in a global variable.
+ // So here's our global.
+ public static ItemColors GLOBAL_ITEM_COLORS;
+
@SubscribeEvent
public static void clientInit(FMLClientSetupEvent evt) {
- evt.enqueueWork(RegisterClientStuff::init);
+ evt.enqueueWork(() -> {
+ RegisterClientStuff.init();
+ RegisterClientStuff.registerColorProviders((colorizer, item) -> {
+ GLOBAL_ITEM_COLORS.register(colorizer, item);
+ });
+ });
var evBus = MinecraftForge.EVENT_BUS;
diff --git a/Forge/src/main/java/at/petrak/hexcasting/forge/mixin/ForgeMixinItemColors.java b/Forge/src/main/java/at/petrak/hexcasting/forge/mixin/ForgeMixinItemColors.java
new file mode 100644
index 00000000..2ef14ac4
--- /dev/null
+++ b/Forge/src/main/java/at/petrak/hexcasting/forge/mixin/ForgeMixinItemColors.java
@@ -0,0 +1,17 @@
+package at.petrak.hexcasting.forge.mixin;
+
+import at.petrak.hexcasting.forge.ForgeHexClientInitializer;
+import net.minecraft.client.color.block.BlockColors;
+import net.minecraft.client.color.item.ItemColors;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(ItemColors.class)
+public class ForgeMixinItemColors {
+ @Inject(method = "createDefault", at = @At("RETURN"))
+ private static void hex$onCreateDefault(BlockColors blockColors, CallbackInfoReturnable info) {
+ ForgeHexClientInitializer.GLOBAL_ITEM_COLORS = info.getReturnValue();
+ }
+}
diff --git a/Forge/src/main/java/at/petrak/hexcasting/forge/recipe/ForgeUnsealedIngredient.java b/Forge/src/main/java/at/petrak/hexcasting/forge/recipe/ForgeUnsealedIngredient.java
index b34ea5b3..dfd28d7d 100644
--- a/Forge/src/main/java/at/petrak/hexcasting/forge/recipe/ForgeUnsealedIngredient.java
+++ b/Forge/src/main/java/at/petrak/hexcasting/forge/recipe/ForgeUnsealedIngredient.java
@@ -2,10 +2,9 @@ package at.petrak.hexcasting.forge.recipe;
import at.petrak.hexcasting.api.addldata.ADIotaHolder;
import at.petrak.hexcasting.api.item.IotaHolderItem;
-import at.petrak.hexcasting.api.spell.DatumType;
-import at.petrak.hexcasting.api.spell.LegacySpellDatum;
-import at.petrak.hexcasting.api.spell.Widget;
+import at.petrak.hexcasting.api.spell.iota.NullIota;
import at.petrak.hexcasting.api.utils.NBTHelper;
+import at.petrak.hexcasting.common.lib.HexIotaTypes;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -19,18 +18,16 @@ import net.minecraftforge.common.crafting.NBTIngredient;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
-import java.util.Arrays;
import java.util.Objects;
public class ForgeUnsealedIngredient extends AbstractIngredient {
private final ItemStack stack;
protected ForgeUnsealedIngredient(ItemStack stack) {
- super(Arrays.stream(DatumType.values())
- .filter((it) -> it != DatumType.EMPTY && it != DatumType.OTHER)
- .map((type) -> {
+ super(HexIotaTypes.REGISTRY.keySet().stream()
+ .map(it -> {
ItemStack newStack = stack.copy();
- NBTHelper.putString(newStack, IotaHolderItem.TAG_OVERRIDE_VISUALLY, LegacySpellDatum.tagForType(type));
+ NBTHelper.putString(newStack, IotaHolderItem.TAG_OVERRIDE_VISUALLY, it.toString());
return new Ingredient.ItemValue(newStack);
}));
this.stack = stack;
@@ -51,7 +48,7 @@ public class ForgeUnsealedIngredient extends AbstractIngredient {
if (this.stack.getItem() == input.getItem() && this.stack.getDamageValue() == input.getDamageValue()) {
ADIotaHolder holder = IXplatAbstractions.INSTANCE.findDataHolder(this.stack);
if (holder != null) {
- return holder.readIotaTag() != null && holder.writeIota(LegacySpellDatum.make(Widget.NULL), true);
+ return holder.readIotaTag() != null && holder.writeIota(new NullIota(), true);
}
}
diff --git a/Forge/src/main/resources/hexcasting_forge.mixins.json b/Forge/src/main/resources/hexcasting_forge.mixins.json
index fcd07e72..769314d2 100644
--- a/Forge/src/main/resources/hexcasting_forge.mixins.json
+++ b/Forge/src/main/resources/hexcasting_forge.mixins.json
@@ -4,5 +4,5 @@
"compatibilityLevel": "JAVA_17",
"refmap": "hexcasting.mixins.refmap.json",
"package": "at.petrak.hexcasting.forge.mixin",
- "mixins": ["ForgeAccessorRegistry", "ForgeMixinCursedRecipeSerializerBase"]
+ "mixins": ["ForgeAccessorRegistry", "ForgeMixinCursedRecipeSerializerBase"], "client": ["ForgeMixinItemColors"]
}