Add NBT support for all recipes + fix tests

This commit is contained in:
thedarkcolour 2024-04-16 12:52:10 -07:00
parent 0a8b54e1a1
commit 1e461faa26
No known key found for this signature in database
GPG Key ID: 86B37B3575FD5976
22 changed files with 229 additions and 105 deletions

View File

@ -162,6 +162,10 @@ dependencies {
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
}
test {
useJUnitPlatform()
}
def replaceProperties = [ mc_version: mc_version, mod_version: version ]
processResources {

View File

@ -274,7 +274,9 @@ public class BarrelBlockEntity extends EBlockEntity {
if (recipe != null && this.tank.getFluidAmount() >= recipe.baseFluidAmount && itemFluid.getAmount() == 1000) {
if (!level.isClientSide) {
this.tank.drain(recipe.baseFluidAmount, IFluidHandler.FluidAction.EXECUTE);
setItem(new ItemStack(recipe.result));
ItemStack result = new ItemStack(recipe.result);
result.setTag(recipe.getResultNbt());
setItem(result);
if (recipe.consumesAdditive) {
itemFluidCap.drain(1000, IFluidHandler.FluidAction.EXECUTE);
@ -378,7 +380,9 @@ public class BarrelBlockEntity extends EBlockEntity {
// Empty barrel
this.tank.drain(recipe.fluidAmount, IFluidHandler.FluidAction.EXECUTE);
// Replace fluid with result
setItem(new ItemStack(recipe.result));
ItemStack result = new ItemStack(recipe.result);
result.setTag(recipe.getResultNbt());
setItem(result);
this.level.playSound(null, this.worldPosition, SoundEvents.AMBIENT_UNDERWATER_EXIT, SoundSource.BLOCKS, 0.8f, 0.8f);
}
// Mixing was successful, so return true

View File

@ -41,10 +41,7 @@ public class InfestedLeavesBlockEntity extends EBlockEntity {
public static final float PROGRESS_INTERVAL = 0.005f;
public static final int SPREAD_INTERVAL = 40;
public static final ModelProperty<BlockState> MIMIC_PROPERTY = new ModelProperty<>();
// A percentage of how much this leaf is infested
// todo change this to a short between 0 and 16000
private float progress;
// A timer that determines when this block should try to spread
private int spreadTimer;
@ -66,24 +63,24 @@ public class InfestedLeavesBlockEntity extends EBlockEntity {
}
// Attempt to convert a leaf block within 1 block radius around this block
private void trySpread() {
private void trySpread(Level level) {
// Get random offset
int x = this.level.random.nextInt(3) - 1;
int y = this.level.random.nextInt(3) - 1;
int z = this.level.random.nextInt(3) - 1;
int x = level.random.nextInt(3) - 1;
int y = level.random.nextInt(3) - 1;
int z = level.random.nextInt(3) - 1;
// Get the block in the world
BlockPos targetPos = getBlockPos().offset(x, y, z);
BlockState state = this.level.getBlockState(targetPos);
BlockState state = level.getBlockState(targetPos);
// DO NOT SPREAD TO ALREADY INFESTED LEAVES
if (state.is(BlockTags.LEAVES) && state.getBlock() != EBlocks.INFESTED_LEAVES.get()) {
// Spread and keep distance/persistent properties
this.level.setBlock(targetPos, EBlocks.INFESTED_LEAVES.get().defaultBlockState()
level.setBlock(targetPos, EBlocks.INFESTED_LEAVES.get().defaultBlockState()
.setValue(LeavesBlock.DISTANCE, state.getValue(LeavesBlock.DISTANCE))
.setValue(LeavesBlock.PERSISTENT, state.getValue(LeavesBlock.PERSISTENT)),
2);
var te = this.level.getBlockEntity(targetPos);
var te = level.getBlockEntity(targetPos);
// Set mimic state of other block
if (te instanceof InfestedLeavesBlockEntity leaves) {
@ -131,11 +128,6 @@ public class InfestedLeavesBlockEntity extends EBlockEntity {
this.mimic = mimic;
}
@Override
public @NotNull ModelData getModelData() {
return ModelData.builder().with(MIMIC_PROPERTY, this.mimic).build();
}
public static class Ticker implements BlockEntityTicker<InfestedLeavesBlockEntity> {
@Override
public void tick(Level level, BlockPos pos, BlockState state, InfestedLeavesBlockEntity leaves) {
@ -154,7 +146,7 @@ public class InfestedLeavesBlockEntity extends EBlockEntity {
// Attempt to spread and reset the timer
if (leaves.spreadTimer >= SPREAD_INTERVAL) {
leaves.trySpread();
leaves.trySpread(level);
leaves.spreadTimer = level.random.nextInt(10);
}
}

View File

@ -43,6 +43,8 @@ import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
import thedarkcolour.exdeorum.registry.EBlockEntities;
import thedarkcolour.exdeorum.tag.EItemTags;
import java.util.Objects;
public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<MechanicalHammerBlockEntity> {
private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_HAMMER_SCREEN_TITLE);
private static final int INPUT_SLOT = 0;
@ -127,7 +129,7 @@ public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<Mech
if (output.isEmpty() || output.getCount() < output.getMaxStackSize()) {
var recipe = RecipeUtil.getHammerRecipe(input.getItem());
if (recipe != null && (output.isEmpty() || matchesStack(recipe.result, output))) {
if (recipe != null && (output.isEmpty() || matchesStack(recipe.result, recipe.getRawResultNbt(), output))) {
return recipe;
}
}
@ -135,8 +137,8 @@ public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<Mech
return null;
}
private static boolean matchesStack(Item item, ItemStack stack) {
return !stack.hasTag() && item == stack.getItem();
private static boolean matchesStack(Item item, @Nullable CompoundTag itemNbt, ItemStack stack) {
return Objects.equals(itemNbt, stack.getTag()) && item == stack.getItem();
}
@Override
@ -156,7 +158,9 @@ public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<Mech
resultCount += HammerLootModifier.calculateFortuneBonus(this.inventory.getStackInSlot(HAMMER_SLOT), ctx.getRandom(), resultCount == 0);
var output = this.inventory.getStackInSlot(OUTPUT_SLOT);
if (output.isEmpty()) {
this.inventory.setStackInSlot(OUTPUT_SLOT, new ItemStack(recipe.result, resultCount));
ItemStack stack = new ItemStack(recipe.result, resultCount);
stack.setTag(recipe.getResultNbt());
this.inventory.setStackInSlot(OUTPUT_SLOT, stack);
} else {
output.setCount(Math.min(output.getMaxStackSize(), resultCount + output.getCount()));
}

View File

@ -99,6 +99,7 @@ public class SieveLogic {
hasDrops = true;
// make a single item copy of recipe result
var result = new ItemStack(recipe.result, 1);
result.setTag(recipe.getResultNbt());
// the size of the stack respecting stack limits (ex. ender pearl limits to 16)
var stackAmount = Math.min(amount, recipe.result.getMaxStackSize(result));
result.setCount(stackAmount);

View File

@ -92,7 +92,9 @@ public record GroupedSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Res
for (var recipe : meshRecipes) {
int resultCount = recipe.resultAmount instanceof ConstantValue constant ? Math.round(constant.value) : 1;
results.add(new Result(new ItemStack(recipe.result, resultCount), recipe.resultAmount, recipe.byHandOnly));
ItemStack result = new ItemStack(recipe.result, resultCount);
result.setTag(recipe.getResultNbt());
results.add(new Result(result, recipe.resultAmount, recipe.byHandOnly));
}
results.sort(resultSorter);

View File

@ -90,7 +90,9 @@ public abstract class BarrelMixingCategory<T> implements IRecipeCategory<T> {
public void setRecipe(IRecipeLayoutBuilder builder, BarrelMixingRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 1, 1).addFluidStack(recipe.fluid, recipe.fluidAmount).setFluidRenderer(1000, false, 16, 16);
builder.addSlot(RecipeIngredientRole.INPUT, 33, 1).addIngredients(recipe.getIngredient());
builder.addSlot(RecipeIngredientRole.OUTPUT, 79, 1).addItemStack(new ItemStack(recipe.result));
ItemStack result = new ItemStack(recipe.result);
result.setTag(recipe.getResultNbt());
builder.addSlot(RecipeIngredientRole.OUTPUT, 79, 1).addItemStack(result);
}
@Override
@ -113,7 +115,9 @@ public abstract class BarrelMixingCategory<T> implements IRecipeCategory<T> {
if (recipe.consumesAdditive) {
additiveSlot.addTooltipCallback((view, tooltip) -> tooltip.add(CONTENTS_ARE_CONSUMED_TOOLTIP));
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 79, 1).addItemStack(new ItemStack(recipe.result));
ItemStack result = new ItemStack(recipe.result);
result.setTag(recipe.getResultNbt());
builder.addSlot(RecipeIngredientRole.OUTPUT, 79, 1).addItemStack(result);
}
@Override

View File

@ -100,7 +100,9 @@ public class CrookCategory implements IRecipeCategory<CrookJeiRecipe> {
@Override
public void setRecipe(IRecipeLayoutBuilder builder, CrookJeiRecipe recipe, IFocusGroup focuses) {
recipe.addIngredients(builder);
builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(new ItemStack(recipe.result)).addTooltipCallback((recipeSlotView, tooltip) -> {
ItemStack result = new ItemStack(recipe.result, 1);
result.setTag(recipe.getResultNbt());
builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(result).addTooltipCallback((recipeSlotView, tooltip) -> {
tooltip.add(ClientJeiUtil.formatChance(recipe.chance));
});
}

View File

@ -24,6 +24,7 @@ import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.recipe.RecipeIngredientRole;
import net.minecraft.ChatFormatting;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
@ -43,11 +44,14 @@ import java.util.List;
public sealed abstract class CrookJeiRecipe {
public final List<BlockState> states;
public Item result;
@Nullable
private final CompoundTag resultNbt;
public float chance;
public CrookJeiRecipe(List<BlockState> states, Item result, float chance) {
public CrookJeiRecipe(List<BlockState> states, Item result, @Nullable CompoundTag resultNbt, float chance) {
this.states = states;
this.result = result;
this.resultNbt = resultNbt;
this.chance = chance;
}
@ -55,30 +59,36 @@ public sealed abstract class CrookJeiRecipe {
static CrookJeiRecipe create(CrookRecipe recipe) {
if (recipe.blockPredicate() instanceof BlockPredicate.BlockStatePredicate state) {
return new StatesRecipe(state, state.possibleStates().filter(blockState -> !blockState.hasProperty(BlockStateProperties.WATERLOGGED) || !blockState.getValue(BlockStateProperties.WATERLOGGED)).toList(), recipe.result(), recipe.chance());
return new StatesRecipe(state, state.possibleStates().filter(blockState -> !blockState.hasProperty(BlockStateProperties.WATERLOGGED) || !blockState.getValue(BlockStateProperties.WATERLOGGED)).toList(), recipe.result(), recipe.resultNbt(), recipe.chance());
} else if (recipe.blockPredicate() instanceof BlockPredicate.SingleBlockPredicate block) {
return new BlockRecipe(block.block(), recipe.result(), recipe.chance());
return new BlockRecipe(block.block(), recipe.result(), recipe.resultNbt(), recipe.chance());
} else if (recipe.blockPredicate() instanceof BlockPredicate.TagPredicate tag) {
var list = new ArrayList<BlockState>();
//noinspection deprecation
for (var holder : BuiltInRegistries.BLOCK.getTagOrEmpty(tag.tag())) {
if (holder.isBound()) {
list.add(holder.value().defaultBlockState());
}
}
return new TagRecipe(tag.tag(), List.copyOf(list), recipe.result(), recipe.chance());
return new TagRecipe(tag.tag(), List.copyOf(list), recipe.result(), recipe.resultNbt(), recipe.chance());
}
throw new IllegalArgumentException("Invalid crook recipe?? -> " + recipe);
}
@Nullable
public CompoundTag getResultNbt() {
return this.resultNbt == null ? null : this.resultNbt.copy();
}
sealed static class StatesRecipe extends CrookJeiRecipe {
private final List<ItemStack> itemIngredients;
public final List<Component> requirements;
StatesRecipe(@Nullable BlockPredicate.BlockStatePredicate predicate, List<BlockState> states, Item result, float chance) {
super(states, result, chance);
StatesRecipe(@Nullable BlockPredicate.BlockStatePredicate predicate, List<BlockState> states, Item result, @Nullable CompoundTag resultNbt, float chance) {
super(states, result, resultNbt, chance);
ImmutableList.Builder<ItemStack> itemIngredients = ImmutableList.builder();
var blocks = new HashSet<Block>();
@ -120,8 +130,8 @@ public sealed abstract class CrookJeiRecipe {
static final class TagRecipe extends StatesRecipe {
public final TagKey<Block> tag;
public TagRecipe(TagKey<Block> tag, List<BlockState> states, Item result, float chance) {
super(null, states, result, chance);
public TagRecipe(TagKey<Block> tag, List<BlockState> states, Item result, @Nullable CompoundTag resultNbt, float chance) {
super(null, states, result, resultNbt, chance);
this.tag = tag;
}
}
@ -129,8 +139,8 @@ public sealed abstract class CrookJeiRecipe {
static final class BlockRecipe extends CrookJeiRecipe {
private final ItemStack itemIngredient;
BlockRecipe(Block block, Item result, float chance) {
super(List.of(block.defaultBlockState()), result, chance);
BlockRecipe(Block block, Item result, @Nullable CompoundTag resultNbt, float chance) {
super(List.of(block.defaultBlockState()), result, resultNbt, chance);
var item = block.asItem();
if (item == Items.AIR) {

View File

@ -26,9 +26,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
import thedarkcolour.exdeorum.registry.EItems;
import java.util.function.Supplier;
@ -53,11 +51,14 @@ class HammerCategory extends OneToOneCategory<HammerRecipe> {
@Override
protected void addOutput(IRecipeSlotBuilder slot, HammerRecipe recipe) {
ItemStack result;
if (recipe.resultAmount instanceof ConstantValue constant) {
slot.addItemStack(new ItemStack(recipe.result, (int) constant.value));
result = new ItemStack(recipe.result, (int) constant.value);
} else {
slot.addItemStack(new ItemStack(recipe.result));
SieveCategory.addTooltips(slot, false, recipe.resultAmount);
result = new ItemStack(recipe.result, 1);
}
SieveCategory.addTooltips(slot, false, recipe.resultAmount);
result.setTag(recipe.getResultNbt());
slot.addItemStack(result);
}
}

View File

@ -33,7 +33,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraftforge.common.loot.IGlobalLootModifier;
import net.minecraftforge.common.loot.LootModifier;
import org.jetbrains.annotations.NotNull;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
@ -47,7 +46,7 @@ public class CrookLootModifier extends LootModifier {
}
@Override
protected @NotNull ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
protected ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
var state = context.getParamOrNull(LootContextParams.BLOCK_STATE);
var stack = context.getParamOrNull(LootContextParams.TOOL);
@ -61,7 +60,9 @@ public class CrookLootModifier extends LootModifier {
for (CrookRecipe recipe : RecipeUtil.getCrookRecipes(state)) {
for (int i = 0; i < rolls; i++) {
if (rand.nextFloat() < recipe.chance()) {
generatedLoot.add(new ItemStack(recipe.result()));
ItemStack result = new ItemStack(recipe.result(), 1);
result.setTag(recipe.getResultNbt());
generatedLoot.add(result);
}
}
}

View File

@ -62,7 +62,9 @@ public class HammerLootModifier extends LootModifier {
}
if (resultAmount > 0) {
newLoot.add(new ItemStack(recipe.result, resultAmount));
ItemStack result = new ItemStack(recipe.result, resultAmount);
result.setTag(recipe.getResultNbt());
newLoot.add(result);
}
return newLoot;
}

View File

@ -19,24 +19,42 @@
package thedarkcolour.exdeorum.recipe;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import org.jetbrains.annotations.Nullable;
public abstract class ProbabilityRecipe extends SingleIngredientRecipe {
public final Item result;
public final NumberProvider resultAmount;
@Nullable
protected final CompoundTag resultNbt;
public ProbabilityRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount) {
public ProbabilityRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt) {
super(id, ingredient);
this.result = result;
this.resultAmount = resultAmount;
this.resultNbt = resultNbt;
}
@Nullable
public CompoundTag getResultNbt() {
return this.resultNbt == null ? null : this.resultNbt.copy();
}
// Do not use in ItemStack, compound tags are mutable and new copies should be made per stack
@Nullable
public CompoundTag getRawResultNbt() {
return this.resultNbt;
}
@Override
public ItemStack getResultItem(RegistryAccess access) {
return new ItemStack(this.result);
ItemStack result = new ItemStack(this.result, 1);
result.setTag(getResultNbt());
return result;
}
}

View File

@ -28,6 +28,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minecraft.commands.arguments.blocks.BlockStateParser;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
@ -501,4 +502,9 @@ public final class RecipeUtil {
public static String writeItemJson(Item result) {
return BuiltInRegistries.ITEM.getKey(result).toString();
}
@Nullable
public static CompoundTag readNbtTag(JsonObject json, String key) {
return json.has(key) ? CraftingHelper.getNBT(json.get(key)) : null;
}
}

View File

@ -20,6 +20,7 @@ package thedarkcolour.exdeorum.recipe.barrel;
import com.google.gson.JsonObject;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
@ -48,14 +49,17 @@ public class BarrelFluidMixingRecipe implements Recipe<Container> {
public final int baseFluidAmount;
public final Fluid additiveFluid;
public final Item result;
@Nullable
protected final CompoundTag resultNbt;
public final boolean consumesAdditive;
public BarrelFluidMixingRecipe(ResourceLocation id, Fluid baseFluid, int baseFluidAmount, Fluid additiveFluid, Item result, boolean consumesAdditive) {
public BarrelFluidMixingRecipe(ResourceLocation id, Fluid baseFluid, int baseFluidAmount, Fluid additiveFluid, Item result, @Nullable CompoundTag resultNbt, boolean consumesAdditive) {
this.id = id;
this.baseFluid = baseFluid;
this.baseFluidAmount = baseFluidAmount;
this.additiveFluid = additiveFluid;
this.result = result;
this.resultNbt = resultNbt;
this.consumesAdditive = consumesAdditive;
}
@ -94,6 +98,11 @@ public class BarrelFluidMixingRecipe implements Recipe<Container> {
return ERecipeTypes.BARREL_FLUID_MIXING.get();
}
@Nullable
public CompoundTag getResultNbt() {
return this.resultNbt == null ? null : this.resultNbt.copy();
}
public static class Serializer implements RecipeSerializer<BarrelFluidMixingRecipe> {
@Override
public BarrelFluidMixingRecipe fromJson(ResourceLocation id, JsonObject json) {
@ -101,9 +110,10 @@ public class BarrelFluidMixingRecipe implements Recipe<Container> {
int baseFluidAmount = GsonHelper.getAsInt(json, "base_fluid_amount");
Fluid additiveFluid = RecipeUtil.readFluid(json, "additive_fluid");
Item result = RecipeUtil.readItem(json, "result");
CompoundTag resultNbt = RecipeUtil.readNbtTag(json, "result_nbt");
boolean consumesAdditive = GsonHelper.getAsBoolean(json, "consumes_additive");
return new BarrelFluidMixingRecipe(id, baseFluid, baseFluidAmount, additiveFluid, result, consumesAdditive);
return new BarrelFluidMixingRecipe(id, baseFluid, baseFluidAmount, additiveFluid, result, resultNbt, consumesAdditive);
}
@Override
@ -112,6 +122,7 @@ public class BarrelFluidMixingRecipe implements Recipe<Container> {
buffer.writeVarInt(recipe.baseFluidAmount);
buffer.writeRegistryId(ForgeRegistries.FLUIDS, recipe.additiveFluid);
buffer.writeRegistryId(ForgeRegistries.ITEMS, recipe.result);
buffer.writeNbt(recipe.resultNbt);
buffer.writeBoolean(recipe.consumesAdditive);
}
@ -121,9 +132,10 @@ public class BarrelFluidMixingRecipe implements Recipe<Container> {
int baseFluidAmount = buffer.readVarInt();
Fluid additiveFluid = buffer.readRegistryId();
Item result = buffer.readRegistryId();
CompoundTag resultNbt = buffer.readNbt();
boolean consumesAdditive = buffer.readBoolean();
return new BarrelFluidMixingRecipe(id, baseFluid, baseFluidAmount, additiveFluid, result, consumesAdditive);
return new BarrelFluidMixingRecipe(id, baseFluid, baseFluidAmount, additiveFluid, result, resultNbt, consumesAdditive);
}
}
}

View File

@ -20,6 +20,7 @@ package thedarkcolour.exdeorum.recipe.barrel;
import com.google.gson.JsonObject;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
@ -43,12 +44,20 @@ public class BarrelMixingRecipe extends SingleIngredientRecipe {
public final Fluid fluid;
public final int fluidAmount;
public final Item result;
@Nullable
protected final CompoundTag resultNbt;
public BarrelMixingRecipe(ResourceLocation id, Ingredient ingredient, Fluid fluid, int fluidAmount, Item result) {
public BarrelMixingRecipe(ResourceLocation id, Ingredient ingredient, Fluid fluid, int fluidAmount, Item result, @Nullable CompoundTag resultNbt) {
super(id, ingredient);
this.fluid = fluid;
this.fluidAmount = fluidAmount;
this.result = result;
this.resultNbt = resultNbt;
}
@Nullable
public CompoundTag getResultNbt() {
return this.resultNbt == null ? null : this.resultNbt.copy();
}
// Do not use
@ -84,8 +93,9 @@ public class BarrelMixingRecipe extends SingleIngredientRecipe {
Fluid fluid = RecipeUtil.readFluid(json, "fluid");
int fluidAmount = GsonHelper.getAsInt(json, "fluid_amount");
Item result = RecipeUtil.readItem(json, "result");
CompoundTag resultNbt = RecipeUtil.readNbtTag(json, "result_nbt");
return new BarrelMixingRecipe(id, ingredient, fluid, fluidAmount, result);
return new BarrelMixingRecipe(id, ingredient, fluid, fluidAmount, result, resultNbt);
}
@Override
@ -94,6 +104,7 @@ public class BarrelMixingRecipe extends SingleIngredientRecipe {
buffer.writeRegistryId(ForgeRegistries.FLUIDS, recipe.fluid);
buffer.writeVarInt(recipe.fluidAmount);
buffer.writeRegistryId(ForgeRegistries.ITEMS, recipe.result);
buffer.writeNbt(recipe.resultNbt);
}
@Override
@ -102,8 +113,9 @@ public class BarrelMixingRecipe extends SingleIngredientRecipe {
Fluid fluid = buffer.readRegistryId();
int fluidAmount = buffer.readVarInt();
Item result = buffer.readRegistryId();
CompoundTag resultNbt = buffer.readNbt();
return new BarrelMixingRecipe(id, ingredient, fluid, fluidAmount, result);
return new BarrelMixingRecipe(id, ingredient, fluid, fluidAmount, result, resultNbt);
}
}
}

View File

@ -21,22 +21,30 @@ package thedarkcolour.exdeorum.recipe.crook;
import com.google.gson.JsonObject;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Container;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.recipe.BlockPredicate;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.ERecipeSerializers;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
public record CrookRecipe(ResourceLocation id, BlockPredicate blockPredicate, Item result, float chance) implements Recipe<Container> {
import java.util.Objects;
public record CrookRecipe(ResourceLocation id, BlockPredicate blockPredicate, Item result, @Nullable CompoundTag resultNbt, float chance) implements Recipe<Container> {
@Nullable
public CompoundTag getResultNbt() {
return this.resultNbt == null ? null : this.resultNbt.copy();
}
@Override
public boolean matches(Container pContainer, Level pLevel) {
return false;
@ -54,7 +62,9 @@ public record CrookRecipe(ResourceLocation id, BlockPredicate blockPredicate, It
@Override
public ItemStack getResultItem(RegistryAccess pRegistryAccess) {
return new ItemStack(this.result);
ItemStack result = new ItemStack(this.result, 1);
result.setTag(getResultNbt());
return result;
}
@Override
@ -72,38 +82,37 @@ public record CrookRecipe(ResourceLocation id, BlockPredicate blockPredicate, It
return ERecipeTypes.CROOK.get();
}
@SuppressWarnings("deprecation")
public static class Serializer implements RecipeSerializer<CrookRecipe> {
@Override
public CrookRecipe fromJson(ResourceLocation id, JsonObject json) {
BlockPredicate blockPredicate = RecipeUtil.readBlockPredicate(id, json, "block_predicate");
if (blockPredicate == null) return null;
BlockPredicate blockPredicate = Objects.requireNonNull(RecipeUtil.readBlockPredicate(id, json, "block_predicate"));
Item result = RecipeUtil.readItem(json, "result");
CompoundTag resultNbt = RecipeUtil.readNbtTag(json, "result_nbt");
float chance = json.get("chance").getAsFloat();
return new CrookRecipe(id, blockPredicate, result, chance);
return new CrookRecipe(id, blockPredicate, result, resultNbt, chance);
}
@Override
@SuppressWarnings("deprecation")
public CrookRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
BlockPredicate blockPredicate = RecipeUtil.readBlockPredicateNetwork(id, buffer);
if (blockPredicate == null) return null;
Item result = buffer.readById(BuiltInRegistries.ITEM);
if (result == null || result == Items.AIR) {
return null;
}
Item result = Objects.requireNonNull(buffer.readById(BuiltInRegistries.ITEM));
CompoundTag resultNbt = buffer.readNbt();
float chance = buffer.readFloat();
return new CrookRecipe(id, blockPredicate, result, chance);
return new CrookRecipe(id, blockPredicate, result, resultNbt, chance);
}
@Override
@SuppressWarnings("deprecation")
public void toNetwork(FriendlyByteBuf buffer, CrookRecipe recipe) {
recipe.blockPredicate.toNetwork(buffer);
buffer.writeId(BuiltInRegistries.ITEM, recipe.result);
buffer.writeNbt(recipe.resultNbt);
buffer.writeFloat(recipe.chance);
}
}

View File

@ -18,18 +18,21 @@
package thedarkcolour.exdeorum.recipe.hammer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.registry.ERecipeSerializers;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
public class CompressedHammerRecipe extends HammerRecipe {
public CompressedHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount) {
super(id, ingredient, result, resultAmount);
public CompressedHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt) {
super(id, ingredient, result, resultAmount, resultNbt);
}
@Override
@ -44,8 +47,8 @@ public class CompressedHammerRecipe extends HammerRecipe {
public static class Serializer extends HammerRecipe.AbstractSerializer<CompressedHammerRecipe> {
@Override
protected CompressedHammerRecipe createHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount) {
return new CompressedHammerRecipe(id, ingredient, result, resultAmount);
protected CompressedHammerRecipe createHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt) {
return new CompressedHammerRecipe(id, ingredient, result, resultAmount, resultNbt);
}
}
}

View File

@ -20,6 +20,7 @@ package thedarkcolour.exdeorum.recipe.hammer;
import com.google.gson.JsonObject;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
@ -27,16 +28,25 @@ import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.recipe.ProbabilityRecipe;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import thedarkcolour.exdeorum.registry.ERecipeSerializers;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
import java.util.Objects;
public class HammerRecipe extends ProbabilityRecipe {
public HammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount) {
super(id, ingredient, result, resultAmount);
public HammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt) {
super(id, ingredient, result, resultAmount, resultNbt);
}
public static boolean areEqual(HammerRecipe a, HammerRecipe b) {
if (a.getClass() != b.getClass()) return false;
return RecipeUtil.areIngredientsEqual(a.ingredient, b.ingredient)
&& Objects.equals(a.result, b.result)
&& Objects.equals(a.resultNbt, b.resultNbt);
}
@Override
@ -50,14 +60,16 @@ public class HammerRecipe extends ProbabilityRecipe {
}
public static abstract class AbstractSerializer<T extends HammerRecipe> implements RecipeSerializer<T> {
protected abstract T createHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount);
protected abstract T createHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt);
@Override
public T fromJson(ResourceLocation name, JsonObject json) {
Ingredient ingredient = RecipeUtil.readIngredient(json, "ingredient");
Item result = RecipeUtil.readItem(json, "result");
NumberProvider resultAmount = RecipeUtil.readNumberProvider(json, "result_amount");
return createHammerRecipe(name, ingredient, result, resultAmount);
CompoundTag resultNbt = RecipeUtil.readNbtTag(json, "result_nbt");
return createHammerRecipe(name, ingredient, result, resultAmount, resultNbt);
}
@Override
@ -66,7 +78,8 @@ public class HammerRecipe extends ProbabilityRecipe {
Ingredient ingredient = Ingredient.fromNetwork(buffer);
Item result = Objects.requireNonNull(buffer.readById(BuiltInRegistries.ITEM));
NumberProvider resultAmount = RecipeUtil.fromNetworkNumberProvider(buffer);
return createHammerRecipe(name, ingredient, result, resultAmount);
CompoundTag resultNbt = buffer.readNbt();
return createHammerRecipe(name, ingredient, result, resultAmount, resultNbt);
}
@Override
@ -75,13 +88,14 @@ public class HammerRecipe extends ProbabilityRecipe {
recipe.getIngredient().toNetwork(buffer);
buffer.writeId(BuiltInRegistries.ITEM, recipe.result);
RecipeUtil.toNetworkNumberProvider(buffer, recipe.resultAmount);
buffer.writeNbt(recipe.resultNbt);
}
}
public static class Serializer extends AbstractSerializer<HammerRecipe> {
@Override
protected HammerRecipe createHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount) {
return new HammerRecipe(id, ingredient, result, resultAmount);
protected HammerRecipe createHammerRecipe(ResourceLocation id, Ingredient ingredient, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt) {
return new HammerRecipe(id, ingredient, result, resultAmount, resultNbt);
}
}
}

View File

@ -18,18 +18,20 @@
package thedarkcolour.exdeorum.recipe.sieve;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.registry.ERecipeSerializers;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
public class CompressedSieveRecipe extends SieveRecipe {
public CompressedSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, boolean byHandOnly) {
super(id, ingredient, mesh, result, resultAmount, byHandOnly);
public CompressedSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt, boolean byHandOnly) {
super(id, ingredient, mesh, result, resultAmount, resultNbt, byHandOnly);
}
@Override
@ -44,8 +46,8 @@ public class CompressedSieveRecipe extends SieveRecipe {
public static class Serializer extends SieveRecipe.AbstractSerializer<CompressedSieveRecipe> {
@Override
protected CompressedSieveRecipe createSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, boolean byHandOnly) {
return new CompressedSieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly);
protected CompressedSieveRecipe createSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt, boolean byHandOnly) {
return new CompressedSieveRecipe(id, ingredient, mesh, result, resultAmount, resultNbt, byHandOnly);
}
}
}

View File

@ -22,6 +22,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
@ -46,8 +47,8 @@ public class SieveRecipe extends ProbabilityRecipe {
public final Item mesh;
public final boolean byHandOnly;
public SieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, boolean byHandOnly) {
super(id, ingredient, result, resultAmount);
public SieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt, boolean byHandOnly) {
super(id, ingredient, result, resultAmount, resultNbt);
this.mesh = mesh;
this.byHandOnly = byHandOnly;
@ -63,8 +64,17 @@ public class SieveRecipe extends ProbabilityRecipe {
return ERecipeTypes.SIEVE.get();
}
public static boolean areEqual(SieveRecipe a, SieveRecipe b) {
if (a.getClass() != b.getClass()) return false;
return a.byHandOnly == b.byHandOnly
&& Objects.equals(a.mesh, b.mesh)
&& RecipeUtil.areIngredientsEqual(a.ingredient, b.ingredient)
&& Objects.equals(a.result, b.result)
&& Objects.equals(a.resultNbt, b.resultNbt);
}
public static abstract class AbstractSerializer<T extends SieveRecipe> implements RecipeSerializer<T> {
protected abstract T createSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, boolean byHandOnly);
protected abstract T createSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt, boolean byHandOnly);
@Override
public T fromJson(ResourceLocation id, JsonObject json) {
@ -87,18 +97,21 @@ public class SieveRecipe extends ProbabilityRecipe {
}
NumberProvider resultAmount = RecipeUtil.readNumberProvider(json, "result_amount");
CompoundTag resultNbt = RecipeUtil.readNbtTag(json, "result_nbt");
boolean byHandOnly = json.has("by_hand_only") && json.get("by_hand_only").getAsBoolean();
return createSieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly);
return createSieveRecipe(id, ingredient, mesh, result, resultAmount, resultNbt, byHandOnly);
}
@SuppressWarnings("deprecation")
@Override
public @Nullable T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
Ingredient ingredient = Ingredient.fromNetwork(buffer);
Item mesh = Objects.requireNonNull(buffer.readById(BuiltInRegistries.ITEM));
Item result = Objects.requireNonNull(buffer.readById(BuiltInRegistries.ITEM));
NumberProvider resultAmount = RecipeUtil.fromNetworkNumberProvider(buffer);
return createSieveRecipe(id, ingredient, mesh, result, resultAmount, buffer.readBoolean());
CompoundTag resultNbt = buffer.readNbt();
boolean byHandOnly = buffer.readBoolean();
return createSieveRecipe(id, ingredient, mesh, result, resultAmount, resultNbt, byHandOnly);
}
@SuppressWarnings("deprecation")
@ -108,14 +121,15 @@ public class SieveRecipe extends ProbabilityRecipe {
buffer.writeId(BuiltInRegistries.ITEM, recipe.mesh);
buffer.writeId(BuiltInRegistries.ITEM, recipe.result);
RecipeUtil.toNetworkNumberProvider(buffer, recipe.resultAmount);
buffer.writeNbt(recipe.resultNbt);
buffer.writeBoolean(recipe.byHandOnly);
}
}
public static class Serializer extends AbstractSerializer<SieveRecipe> {
@Override
protected SieveRecipe createSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, boolean byHandOnly) {
return new SieveRecipe(id, ingredient, mesh, result, resultAmount, byHandOnly);
protected SieveRecipe createSieveRecipe(ResourceLocation id, Ingredient ingredient, Item mesh, Item result, NumberProvider resultAmount, @Nullable CompoundTag resultNbt, boolean byHandOnly) {
return new SieveRecipe(id, ingredient, mesh, result, resultAmount, resultNbt, byHandOnly);
}
}
}

View File

@ -21,14 +21,17 @@ package thedarkcolour.exdeorum.recipe;
import com.google.gson.JsonObject;
import io.netty.buffer.Unpooled;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.Bootstrap;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import thedarkcolour.exdeorum.recipe.barrel.FinishedFluidTransformationRecipe;
@ -36,7 +39,10 @@ import thedarkcolour.exdeorum.recipe.barrel.FluidTransformationRecipe;
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
import thedarkcolour.exdeorum.recipe.crucible.CrucibleHeatRecipe;
import thedarkcolour.exdeorum.recipe.crucible.FinishedCrucibleHeatRecipe;
import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import java.util.function.BiPredicate;
import java.util.function.Function;
import static org.junit.jupiter.api.Assertions.*;
@ -50,41 +56,42 @@ public class RecipeSerializationTests {
}
@Test
void crucibleHeatSourceNetwork() {
void networkRecipes() {
CompoundTag testData = new CompoundTag();
testData.putInt("apple", 12);
testNetwork(new CrucibleHeatRecipe(null, BlockPredicate.blockTag(BlockTags.DIRT), 3), new CrucibleHeatRecipe.Serializer());
testNetwork(new FluidTransformationRecipe(null, Fluids.WATER, Fluids.LAVA, 1, BlockPredicate.blockTag(BlockTags.DIRT), WeightedList.<BlockState>builder().build(), 1000), new FluidTransformationRecipe.Serializer());
testNetwork(new CrookRecipe(null, BlockPredicate.blockTag(BlockTags.DIRT), Items.DIAMOND, null, 0.0025f), new CrookRecipe.Serializer());
testNetwork(new SieveRecipe(null, Ingredient.of(Items.SOUL_SOIL), Items.IRON_AXE, Items.DIAMOND, UniformGenerator.between(1, 3), null, true), new SieveRecipe.Serializer(), SieveRecipe::areEqual);
testNetwork(new SieveRecipe(null, Ingredient.of(Items.SOUL_SOIL), Items.IRON_AXE, Items.DIAMOND, UniformGenerator.between(1, 3), testData, true), new SieveRecipe.Serializer(), SieveRecipe::areEqual);
testNetwork(new HammerRecipe(null, Ingredient.of(Items.SOUL_SOIL), Items.SOUL_SAND, UniformGenerator.between(1, 3), null), new HammerRecipe.Serializer(), HammerRecipe::areEqual);
testNetwork(new HammerRecipe(null, Ingredient.of(Items.SOUL_SOIL), Items.SOUL_SAND, UniformGenerator.between(1, 3), testData), new HammerRecipe.Serializer(), HammerRecipe::areEqual);
}
@Test
void crucibleHeatSourceJson() {
void jsonRecipes() {
testJson(new CrucibleHeatRecipe(null, BlockPredicate.blockTag(BlockTags.DIRT), 3), new CrucibleHeatRecipe.Serializer(), recipe -> {
return new FinishedCrucibleHeatRecipe(recipe.id(), recipe.blockPredicate(), recipe.heatValue());
});
}
@Test
void barrelFluidTransformationNetwork() {
testNetwork(new FluidTransformationRecipe(null, Fluids.WATER, Fluids.LAVA, 1, BlockPredicate.blockTag(BlockTags.DIRT), WeightedList.<BlockState>builder().build(), 1000), new FluidTransformationRecipe.Serializer());
}
@Test
void barrelFluidTransformationJson() {
testJson(new FluidTransformationRecipe(null, Fluids.WATER, Fluids.LAVA, 1, BlockPredicate.blockTag(BlockTags.DIRT), WeightedList.<BlockState>builder().build(), 1000), new FluidTransformationRecipe.Serializer(), recipe -> {
return new FinishedFluidTransformationRecipe(recipe.getId(), recipe.baseFluid, recipe.resultFluid, recipe.resultColor, recipe.catalyst, recipe.byproducts, recipe.duration);
});
}
@Test
void crookNetwork() {
testNetwork(new CrookRecipe(null, BlockPredicate.blockTag(BlockTags.DIRT), Items.DIAMOND, 0.0025f), new CrookRecipe.Serializer());
// Takes a recipe, uses its serializer to write it to a buffer, then reads it
// back and checks if the recipe it got is the same as the one originally written
private static <T extends Recipe<?>> void testNetwork(T recipe, RecipeSerializer<T> serializer) {
testNetwork(recipe, serializer, Recipe::equals);
}
// Takes a recipe, uses its serializer to write it to a buffer, then reads it
// back and checks if the recipe it got is the same as the one originally written
private static <T extends Recipe<?>> void testNetwork(T recipe, RecipeSerializer<T> serializer) {
private static <T extends Recipe<?>> void testNetwork(T recipe, RecipeSerializer<T> serializer, BiPredicate<T, T> equalityCheck) {
var id = recipe.getId();
var buffer = new FriendlyByteBuf(Unpooled.buffer());
serializer.toNetwork(buffer, recipe);
assertEquals(recipe, serializer.fromNetwork(id, buffer));
assertTrue(equalityCheck.test(recipe, serializer.fromNetwork(id, buffer)));
}
private static <T extends Recipe<?>> void testJson(T recipe, RecipeSerializer<T> serializer, Function<T, EFinishedRecipe> toJson) {