diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java index 0c5e1b43..fee645b8 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java @@ -7,15 +7,11 @@ import java.util.function.Predicate; import java.util.function.Supplier; import com.google.common.base.Suppliers; -import com.google.gson.JsonParseException; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.item.EnchantableItem; +import com.minelittlepony.unicopia.util.CodecUtils; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.Decoder; -import com.mojang.serialization.DynamicOps; -import com.mojang.serialization.Encoder; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketByteBuf; import net.minecraft.recipe.Ingredient; @@ -25,23 +21,11 @@ public class IngredientWithSpell implements Predicate { private static final IngredientWithSpell EMPTY = new IngredientWithSpell(Optional.empty(), Optional.empty()); private static final Predicate INGREDIENT_IS_PRESENT = ((Predicate)(Ingredient::isEmpty)).negate(); - public static final Codec CODEC = Codec.of(new Encoder() { - @Override - public DataResult encode(IngredientWithSpell input, DynamicOps ops, T prefix) { - throw new JsonParseException("cannot serialize this type"); - } - }, new Decoder() { - @Override - public DataResult> decode(DynamicOps ops, T input) { - // TODO: Doing codecs properly is an exercise left to the readers - return DataResult.success(new Pair<>( - new IngredientWithSpell( - Ingredient.ALLOW_EMPTY_CODEC.decode(ops, input).map(Pair::getFirst).result(), - ops.getMap(input).flatMap(maplike -> SpellType.REGISTRY.getCodec().parse(ops, maplike.get("spell"))).result() - ), input) - ); - } - }); + public static final Codec CODEC = CodecUtils.extend(Ingredient.ALLOW_EMPTY_CODEC, SpellType.REGISTRY.getCodec().fieldOf("spell")).xmap( + pair -> new IngredientWithSpell(pair.getFirst(), pair.getSecond()), + ingredient -> new Pair<>(ingredient.stack, ingredient.spell) + ); + public static final Codec> LIST_CODEC = CODEC.listOf().xmap( list -> DefaultedList.copyOf(EMPTY, list.toArray(IngredientWithSpell[]::new)), Function.identity() diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java index cfac436c..a2a24ef1 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java @@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.container.inventory.SpellbookInventory; import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.item.URecipes; +import com.minelittlepony.unicopia.util.CodecUtils; import com.minelittlepony.unicopia.util.InventoryUtil; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; @@ -45,9 +46,9 @@ public class SpellCraftingRecipe implements SpellbookRecipe { /** * The resulting item */ - final ItemStackWithSpell output; + final ItemStack output; - private SpellCraftingRecipe(IngredientWithSpell material, TraitIngredient requiredTraits, List requiredItems, ItemStackWithSpell output) { + private SpellCraftingRecipe(IngredientWithSpell material, TraitIngredient requiredTraits, List requiredItems, ItemStack output) { this.material = material; this.requiredTraits = requiredTraits; this.requiredItems = requiredItems; @@ -63,7 +64,7 @@ public class SpellCraftingRecipe implements SpellbookRecipe { requiredTraits.min().ifPresent(min -> { min.forEach(e -> builder.input(e.getKey(), e.getValue())); }); - builder.result(output.toItemStack()); + builder.result(output); } @Override @@ -115,7 +116,7 @@ public class SpellCraftingRecipe implements SpellbookRecipe { @Override public ItemStack getResult(DynamicRegistryManager registries) { - return output.toItemStack(); + return output; } @Override @@ -123,29 +124,17 @@ public class SpellCraftingRecipe implements SpellbookRecipe { return URecipes.TRAIT_REQUIREMENT; } - record ItemStackWithSpell(ItemStack stack, Optional> spell) { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - RecipeCodecs.CRAFTING_RESULT.fieldOf("stack").forGetter(ItemStackWithSpell::stack), - SpellType.REGISTRY.getCodec().optionalFieldOf("spell").forGetter(ItemStackWithSpell::spell) - ).apply(instance, ItemStackWithSpell::new)); - - public ItemStack toItemStack() { - return spell.filter(s -> s != SpellType.EMPTY_KEY).map(s -> { - return EnchantableItem.enchant(stack.copy(), s); - }).orElse(stack); - } - - public ItemStackWithSpell(ItemStack stack) { - this(EnchantableItem.unenchant(stack), Optional.of(EnchantableItem.getSpellKey(stack))); - } - } - public static class Serializer implements RecipeSerializer { + private static final Codec RESULT_CODEC = CodecUtils.extend(RecipeCodecs.CRAFTING_RESULT, SpellType.REGISTRY.getCodec().fieldOf("spell")).xmap( + pair -> pair.getSecond().map(spell -> EnchantableItem.enchant(pair.getFirst().orElse(ItemStack.EMPTY), spell)).orElse(pair.getFirst().orElse(ItemStack.EMPTY)), + stack -> Pair.of(Optional.of(stack), EnchantableItem.getSpellKeyOrEmpty(stack)) + ); + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( IngredientWithSpell.CODEC.fieldOf("material").forGetter(recipe -> recipe.material), TraitIngredient.CODEC.fieldOf("traits").forGetter(recipe -> recipe.requiredTraits), IngredientWithSpell.CODEC.listOf().fieldOf("ingredients").forGetter(recipe -> recipe.requiredItems), - ItemStackWithSpell.CODEC.fieldOf("result").forGetter(recipe -> recipe.output) + RESULT_CODEC.fieldOf("result").forGetter(recipe -> recipe.output) ).apply(instance, SpellCraftingRecipe::new)); @Override @@ -159,7 +148,7 @@ public class SpellCraftingRecipe implements SpellbookRecipe { IngredientWithSpell.fromPacket(buf), TraitIngredient.fromPacket(buf), buf.readCollection(DefaultedList::ofSize, IngredientWithSpell::fromPacket), - new ItemStackWithSpell(buf.readItemStack()) + buf.readItemStack() ); } @@ -168,7 +157,7 @@ public class SpellCraftingRecipe implements SpellbookRecipe { recipe.material.write(buf); recipe.requiredTraits.write(buf); buf.writeCollection(recipe.requiredItems, (b, i) -> i.write(b)); - buf.writeItemStack(recipe.output.toItemStack()); + buf.writeItemStack(recipe.output); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java index 72bba977..e43b001a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java @@ -163,7 +163,7 @@ public enum Trait implements CommandArgumentEnum { @Deprecated public static Optional fromId(Identifier id) { - return Optional.ofNullable(ID_CODEC.byId(id.toString())); + return Optional.ofNullable(IDS.get(id)); } @Deprecated diff --git a/src/main/java/com/minelittlepony/unicopia/advancement/UCriteria.java b/src/main/java/com/minelittlepony/unicopia/advancement/UCriteria.java index a0e9a69b..c2f92a12 100644 --- a/src/main/java/com/minelittlepony/unicopia/advancement/UCriteria.java +++ b/src/main/java/com/minelittlepony/unicopia/advancement/UCriteria.java @@ -3,9 +3,9 @@ package com.minelittlepony.unicopia.advancement; import net.minecraft.advancement.criterion.Criteria; public interface UCriteria { - CustomEventCriterion CUSTOM_EVENT = Criteria.register("unicopia:send_dragon_breath", new CustomEventCriterion()); + CustomEventCriterion CUSTOM_EVENT = Criteria.register("unicopia:custom", new CustomEventCriterion()); RaceChangeCriterion PLAYER_CHANGE_RACE = Criteria.register("unicopia:player_change_race", new RaceChangeCriterion()); - SendViaDragonBreathScrollCriterion SEND_DRAGON_BREATH = Criteria.register("unicopia:custom", new SendViaDragonBreathScrollCriterion()); + SendViaDragonBreathScrollCriterion SEND_DRAGON_BREATH = Criteria.register("unicopia:send_dragon_breath", new SendViaDragonBreathScrollCriterion()); CustomEventCriterion.Trigger LOOK_INTO_SUN = CUSTOM_EVENT.createTrigger("look_into_sun"); CustomEventCriterion.Trigger WEAR_SHADES = CUSTOM_EVENT.createTrigger("wear_shades"); diff --git a/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java b/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java index 39d8598d..97927b1b 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.item; +import java.util.Optional; import java.util.function.Predicate; import org.jetbrains.annotations.Nullable; @@ -83,6 +84,10 @@ public interface EnchantableItem extends ItemConvertible { return stack; } + static Optional> getSpellKeyOrEmpty(ItemStack stack) { + return isEnchanted(stack) ? SpellType.REGISTRY.getOrEmpty(new Identifier(stack.getNbt().getString("spell"))) : Optional.empty(); + } + static SpellType getSpellKey(ItemStack stack) { return SpellType.getKey(isEnchanted(stack) ? new Identifier(stack.getNbt().getString("spell")) : SpellType.EMPTY_ID); } diff --git a/src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java b/src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java index fce561d0..c4eb921a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java @@ -20,12 +20,11 @@ public class ZapAppleRecipe extends ShapelessRecipe { public static class Serializer implements RecipeSerializer { private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.STRING.optionalFieldOf("group", "").forGetter(ZapAppleRecipe::getGroup), - CraftingRecipeCategory.CODEC.fieldOf("category").forGetter(ZapAppleRecipe::getCategory), - Registries.ITEM.getCodec().xmap(item -> { - return UItems.ZAP_APPLE.setAppearance(UItems.ZAP_APPLE.getDefaultStack(), item.getDefaultStack()); - }, stack -> { - return UItems.ZAP_APPLE.getAppearance(stack); - }).fieldOf("appearance").forGetter(recipe -> recipe.getResult(null)), + CraftingRecipeCategory.CODEC.optionalFieldOf("category", CraftingRecipeCategory.MISC).forGetter(ZapAppleRecipe::getCategory), + Registries.ITEM.getCodec().xmap( + item -> UItems.ZAP_APPLE.setAppearance(UItems.ZAP_APPLE.getDefaultStack(), item.getDefaultStack()), + stack -> UItems.ZAP_APPLE.getAppearance(stack) + ).fieldOf("appearance").forGetter(recipe -> recipe.getResult(null)), URecipes.SHAPELESS_RECIPE_INGREDIENTS_CODEC.fieldOf("ingredients").forGetter(ZapAppleRecipe::getIngredients) ).apply(instance, ZapAppleRecipe::new)); diff --git a/src/main/java/com/minelittlepony/unicopia/util/CodecUtils.java b/src/main/java/com/minelittlepony/unicopia/util/CodecUtils.java new file mode 100644 index 00000000..6ee9a0f3 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/util/CodecUtils.java @@ -0,0 +1,61 @@ +package com.minelittlepony.unicopia.util; + +import java.util.Optional; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapCodec; + +public interface CodecUtils { + /** + * Combines the result of two unrelated codecs into a single object. + *

+ * The first codec serves as the "base" whilst the second codec serves as an additional field to merge into + * that object when serializing. Deserializing produces a pair with the parent value and the extra value. + *

+ * Recommended usage: + * + * Codec CODEC = CodecUtils.extend(SOME_CODEC, MY_FIELD_CODEC.fieldOf("my_extra_field")).xmap( + * pair -> new MyObject(pair.getLeft(), pair.getRight()), + * myObject -> Pair.of(myObject.parent(), myObject.myExtraField()); + * + *

+ * Json: + * + * { + * "something": "something", + * "something_else": 1, + * + * "my_extra_field": "HAH EAT THAT CODECS" + * } + * + * @param The base type + * @param Type of the field to append + * @param baseCodec Codec for the base type + * @param fieldCodec Codec for the appended field + * @return A codec for serializing objects of the base with the extra field inserted + */ + static Codec, Optional>> extend(Codec baseCodec, MapCodec fieldCodec) { + return Codec.of(new Encoder, Optional>>() { + @Override + public DataResult encode(Pair, Optional> input, DynamicOps ops, T prefix) { + return baseCodec.encode(input.getFirst().get(), ops, prefix) + .flatMap(leftResult -> input.getSecond() + .map(r -> fieldCodec.encode(r, ops, ops.mapBuilder()).build(prefix)) + .orElse(DataResult.success(leftResult))); + } + }, new Decoder, Optional>>() { + @Override + public DataResult, Optional>, T>> decode(DynamicOps ops, T input) { + return DataResult.success(new Pair<>(new Pair<>( + baseCodec.decode(ops, input).map(Pair::getFirst).result(), + fieldCodec.decode(ops, ops.getMap(input).result().get()).result() + ), input)); + } + }); + } +} diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json b/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json index 263cdd20..21a0eb14 100644 --- a/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json +++ b/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json @@ -16,7 +16,7 @@ "block": "unicopia:apple_pie", "condition": "minecraft:block_state_property", "properties": { - "stomped": true + "stomped": "true" } }, {