From 693d3623efd66e551e86aa238c881dab4e032162 Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 4 Jul 2024 23:16:03 +0300 Subject: [PATCH] trying add crafting and water handling inside blockentity --- .../block/custom/BrewingKegBlock.java | 1 + .../block/entity/BrewingKegBlockEntity.java | 123 +++++++++++++++--- .../networking/ModMessages.java | 15 +++ .../networking/packet/FluidSyncS2CPacket.java | 30 +++++ .../recipe/BrewingRecipe.java | 118 +++++++++++++++++ .../recipe/BrewingRecipeSerializer.java | 41 ++++++ .../screen/BrewingKegScreenHandler.java | 10 ++ .../ukrainian_dlight/util/FluidStack.java | 37 ++++++ 8 files changed, 354 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/megatrex4/ukrainian_dlight/networking/ModMessages.java create mode 100644 src/main/java/com/megatrex4/ukrainian_dlight/networking/packet/FluidSyncS2CPacket.java create mode 100644 src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipe.java create mode 100644 src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipeSerializer.java create mode 100644 src/main/java/com/megatrex4/ukrainian_dlight/util/FluidStack.java diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/block/custom/BrewingKegBlock.java b/src/main/java/com/megatrex4/ukrainian_dlight/block/custom/BrewingKegBlock.java index 071345e..3b3a148 100644 --- a/src/main/java/com/megatrex4/ukrainian_dlight/block/custom/BrewingKegBlock.java +++ b/src/main/java/com/megatrex4/ukrainian_dlight/block/custom/BrewingKegBlock.java @@ -1,5 +1,6 @@ package com.megatrex4.ukrainian_dlight.block.custom; + import com.megatrex4.ukrainian_dlight.block.entity.BrewingKegBlockEntity; import com.megatrex4.ukrainian_dlight.block.entity.ModBlockEntities; import net.minecraft.block.*; diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/block/entity/BrewingKegBlockEntity.java b/src/main/java/com/megatrex4/ukrainian_dlight/block/entity/BrewingKegBlockEntity.java index bc73aa6..e1038fb 100644 --- a/src/main/java/com/megatrex4/ukrainian_dlight/block/entity/BrewingKegBlockEntity.java +++ b/src/main/java/com/megatrex4/ukrainian_dlight/block/entity/BrewingKegBlockEntity.java @@ -1,31 +1,45 @@ package com.megatrex4.ukrainian_dlight.block.entity; import com.megatrex4.ukrainian_dlight.block.DrinkBottleBlock; -import com.megatrex4.ukrainian_dlight.block.ModBlocks; -import com.megatrex4.ukrainian_dlight.item.DrinkBlockItem; -import com.megatrex4.ukrainian_dlight.item.ModItems; +import com.megatrex4.ukrainian_dlight.networking.ModMessages; +import com.megatrex4.ukrainian_dlight.recipe.BrewingRecipe; import com.megatrex4.ukrainian_dlight.screen.BrewingKegScreenHandler; +import com.megatrex4.ukrainian_dlight.util.FluidStack; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PlayerLookup; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.fluid.Fluids; import net.minecraft.inventory.Inventories; import net.minecraft.inventory.Inventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; import net.minecraft.screen.PropertyDelegate; import net.minecraft.screen.ScreenHandler; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; +import java.util.Optional; + public class BrewingKegBlockEntity extends BlockEntity implements ExtendedScreenHandlerFactory, ImplementedInventory { private final DefaultedList inventory = DefaultedList.ofSize(10, ItemStack.EMPTY); @@ -39,21 +53,41 @@ public class BrewingKegBlockEntity extends BlockEntity implements ExtendedScreen //adds container(like bottle of vial) input public static final int CONTAINER_SLOT = 6; //adds 1 input slot for water - public static final int INPUT_SLOT = 7; + public static final int WATER_SLOT = 7; //adds 1 output slot and display slot public static final int DRINKS_DISPLAY_SLOT = 8; public static final int OUTPUT_SLOT = 9; protected final PropertyDelegate propertyDelegate; private int progress; - private int maxProgress = 72; + private int maxProgress = 200; // Adjusted to match the brewing time in the JSON + + public final SingleVariantStorage fluidStorage = new SingleVariantStorage() { + @Override + protected FluidVariant getBlankVariant() { + return FluidVariant.blank(); + } + + @Override + protected long getCapacity(FluidVariant variant) { + return FluidStack.convertDropletsToMb(FluidConstants.BUCKET) * 20; // 20k mB + } + + @Override + protected void onFinalCommit() { + markDirty(); + if(!world.isClient()) { + sendFluidPacket(); + } + } + }; - public BrewingKegBlockEntity(BlockPos pos, BlockState state){ + public BrewingKegBlockEntity(BlockPos pos, BlockState state) { super(ModBlockEntities.BREWING_KEG_BLOCK_ENTITY, pos, state); this.propertyDelegate = new PropertyDelegate() { @Override public int get(int index) { - return switch (index){ + return switch (index) { case 0 -> BrewingKegBlockEntity.this.progress; case 1 -> BrewingKegBlockEntity.this.maxProgress; default -> 0; @@ -62,7 +96,7 @@ public int get(int index) { @Override public void set(int index, int value) { - switch (index){ + switch (index) { case 0 -> BrewingKegBlockEntity.this.progress = value; case 1 -> BrewingKegBlockEntity.this.maxProgress = value; } @@ -85,6 +119,8 @@ protected void writeNbt(NbtCompound nbt) { super.writeNbt(nbt); Inventories.writeNbt(nbt, inventory); nbt.putInt("brewing_keg.progress", progress); + nbt.put("brewing_keg.fluid_variant", fluidStorage.variant.toNbt()); + nbt.putLong("brewing_keg.fluid_amount", fluidStorage.amount); } @Override @@ -92,6 +128,14 @@ public void readNbt(NbtCompound nbt) { super.readNbt(nbt); Inventories.readNbt(nbt, inventory); progress = nbt.getInt("brewing_keg.progress"); + fluidStorage.variant = FluidVariant.fromNbt(nbt.getCompound("brewing_keg.fluid_variant")); + fluidStorage.amount = nbt.getLong("brewing_keg.fluid_amount"); + } + + + private void debugFluidLevel() { + System.out.println("Fluid: " + fluidStorage.variant.getFluid().toString()); + System.out.println("Amount: " + fluidStorage.amount + " mB"); } @Override @@ -107,21 +151,23 @@ public Text getDisplayName() { @Nullable @Override public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) { + sendFluidPacket(); return new BrewingKegScreenHandler(syncId, playerInventory, this, propertyDelegate); } public void tick(World world, BlockPos pos, BlockState state) { - if(world.isClient){ + if (world.isClient) { return; } - if(isOutoutSlotEmptyOrReceivable()){ - if(this.hasRecipe()){ + if (isOutputSlotEmptyOrReceivable()) { + if (this.hasRecipe() && hasEnoughFluid()) { this.increaseCraftProgress(); markDirty(world, pos, state); - if(hasCraftingFinished()){ + if (hasCraftingFinished()) { this.craftItem(); + extractFluid(); this.resetProgress(); } } else { @@ -133,6 +179,43 @@ public void tick(World world, BlockPos pos, BlockState state) { } } + private void extractFluid() { + try (Transaction transaction = Transaction.openOuter()) { + this.fluidStorage.extract(FluidVariant.of(Fluids.WATER), 500, transaction); + transaction.commit(); + } + } + + private static void transferFluidToFluidStorage(BrewingKegBlockEntity entity) { + try(Transaction transaction = Transaction.openOuter()) { + entity.fluidStorage.insert(FluidVariant.of(Fluids.WATER), + FluidStack.convertDropletsToMb(FluidConstants.BUCKET), transaction); + transaction.commit(); + entity.setStack(0, new ItemStack(Items.BUCKET)); + } + } + + + private boolean hasEnoughFluid() { + return this.fluidStorage.amount >= 500; // mB amount! + } + + private void sendFluidPacket() { + PacketByteBuf data = PacketByteBufs.create(); + fluidStorage.variant.toPacket(data); + data.writeLong(fluidStorage.amount); + data.writeBlockPos(getPos()); + + for (ServerPlayerEntity player : PlayerLookup.tracking((ServerWorld) world, getPos())) { + ServerPlayNetworking.send(player, ModMessages.FLUID_SYNC, data); + } + } + + public void setFluidLevel(FluidVariant fluidVariant, long fluidLevel) { + this.fluidStorage.variant = fluidVariant; + this.fluidStorage.amount = fluidLevel; + } + private void resetProgress() { this.progress = 0; } @@ -152,21 +235,19 @@ private void increaseCraftProgress() { } private boolean hasRecipe() { - ItemStack result = new ItemStack(DrinkBottleBlock.WINE_BOTTLE); - boolean hasInput = getStack(INGREDIENT_SLOT_1).getItem() == ModItems.APPLE_SLICE; - - return hasInput && canInsertAmoutIntoOutputSlot(result) && canInstertItemIntoOutputSlot(result.getItem()); + Optional recipe = world.getRecipeManager().getFirstMatch(BrewingRecipe.Type.INSTANCE, this, world); + return recipe.isPresent() && canInsertAmountIntoOutputSlot(recipe.get().getResult()) && canInsertItemIntoOutputSlot(recipe.get().getResult().getItem()); } - private boolean canInstertItemIntoOutputSlot(Item item) { - return this.getStack(OUTPUT_SLOT).getItem() == item || this.getStack(OUTPUT_SLOT).isEmpty(); + private boolean canInsertItemIntoOutputSlot(Item item) { + return this.getStack(OUTPUT_SLOT).getItem() == item || this.getStack(OUTPUT_SLOT).isEmpty(); } - private boolean canInsertAmoutIntoOutputSlot(ItemStack result) { - return this.getStack(OUTPUT_SLOT).getCount() + result.getCount() <= getStack(OUTPUT_SLOT).getMaxCount(); + private boolean canInsertAmountIntoOutputSlot(ItemStack result) { + return this.getStack(OUTPUT_SLOT).getCount() + result.getCount() <= this.getStack(OUTPUT_SLOT).getMaxCount(); } - private boolean isOutoutSlotEmptyOrReceivable() { + private boolean isOutputSlotEmptyOrReceivable() { return this.getStack(OUTPUT_SLOT).isEmpty() || this.getStack(OUTPUT_SLOT).getCount() < this.getStack(OUTPUT_SLOT).getMaxCount(); } } diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/networking/ModMessages.java b/src/main/java/com/megatrex4/ukrainian_dlight/networking/ModMessages.java new file mode 100644 index 0000000..5e95e86 --- /dev/null +++ b/src/main/java/com/megatrex4/ukrainian_dlight/networking/ModMessages.java @@ -0,0 +1,15 @@ +package com.megatrex4.ukrainian_dlight.networking; + +import com.megatrex4.ukrainian_dlight.UkrainianDelight; +import com.megatrex4.ukrainian_dlight.networking.packet.FluidSyncS2CPacket; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.util.Identifier; + +public class ModMessages { + public static final Identifier FLUID_SYNC = new Identifier(UkrainianDelight.MOD_ID, "fluid_sync"); + + public static void registerS2CPackets() { + ClientPlayNetworking.registerGlobalReceiver(FLUID_SYNC, FluidSyncS2CPacket::receive); + } +} diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/networking/packet/FluidSyncS2CPacket.java b/src/main/java/com/megatrex4/ukrainian_dlight/networking/packet/FluidSyncS2CPacket.java new file mode 100644 index 0000000..9e9e215 --- /dev/null +++ b/src/main/java/com/megatrex4/ukrainian_dlight/networking/packet/FluidSyncS2CPacket.java @@ -0,0 +1,30 @@ +package com.megatrex4.ukrainian_dlight.networking.packet; + +import com.megatrex4.ukrainian_dlight.block.entity.BrewingKegBlockEntity; +import com.megatrex4.ukrainian_dlight.screen.BrewingKegScreenHandler; +import com.megatrex4.ukrainian_dlight.util.FluidStack; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.math.BlockPos; + +public class FluidSyncS2CPacket { + public static void receive(MinecraftClient client, ClientPlayNetworkHandler handler, + PacketByteBuf buf, PacketSender responseSender) { + FluidVariant variant = FluidVariant.fromPacket(buf); + long fluidLevel = buf.readLong(); + BlockPos position = buf.readBlockPos(); + + if(client.world.getBlockEntity(position) instanceof BrewingKegBlockEntity blockEntity) { + blockEntity.setFluidLevel(variant, fluidLevel); + + if(client.player.currentScreenHandler instanceof BrewingKegScreenHandler screenHandler && + screenHandler.blockEntity.getPos().equals(position)) { + blockEntity.setFluidLevel(variant, fluidLevel); + screenHandler.setFluid(new FluidStack(variant, fluidLevel)); + } + } + } +} diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipe.java b/src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipe.java new file mode 100644 index 0000000..0209239 --- /dev/null +++ b/src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipe.java @@ -0,0 +1,118 @@ +package com.megatrex4.ukrainian_dlight.recipe; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.*; +import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.world.World; +import net.minecraft.registry.DynamicRegistryManager; + +public class BrewingRecipe implements Recipe { + private final Identifier id; + private final DefaultedList ingredients; + private final ItemStack result; + private final int brewingTime; + private final float experience; + // private final int waterAmount; + private final ItemStack container; + + public BrewingRecipe(Identifier id, DefaultedList ingredients, ItemStack result, int brewingTime, float experience, + //int waterAmount, + ItemStack container) { + this.id = id; + this.ingredients = ingredients; + this.result = result; + this.brewingTime = brewingTime; + this.experience = experience; + // this.waterAmount = waterAmount; + this.container = container; + } + + public static BrewingRecipe fromJson(Identifier id, JsonObject json) { + JsonArray ingredientsArray = JsonHelper.getArray(json, "ingredients"); + DefaultedList ingredients = DefaultedList.ofSize(ingredientsArray.size(), Ingredient.EMPTY); + for (int i = 0; i < ingredientsArray.size(); i++) { + ingredients.set(i, Ingredient.fromJson(ingredientsArray.get(i))); + } + + ItemStack result = ShapedRecipe.outputFromJson(JsonHelper.getObject(json, "result")); + int brewingTime = JsonHelper.getInt(json, "brewingtime"); + float experience = JsonHelper.getFloat(json, "experience"); + //int waterAmount = JsonHelper.getInt(json, "water"); + ItemStack container = ShapedRecipe.outputFromJson(JsonHelper.getObject(json, "container")); + + return new BrewingRecipe(id, ingredients, result, brewingTime, experience, + //waterAmount, + container); + } + + public DefaultedList getIngredients() { + return ingredients; + } + + public ItemStack getResult() { + return result; + } + + public int getBrewingTime() { + return brewingTime; + } + + public float getExperience() { + return experience; + } + +// public int getWaterAmount() { +// return waterAmount; +// } + + public ItemStack getContainer() { + return container; + } + + @Override + public boolean matches(Inventory inv, World world) { + // Implement matching logic + return false; + } + + @Override + public ItemStack craft(Inventory inv, DynamicRegistryManager registryManager) { + return getResult().copy(); + } + + @Override + public boolean fits(int width, int height) { + return true; + } + + @Override + public ItemStack getOutput(DynamicRegistryManager registryManager) { + return getResult(); + } + + @Override + public Identifier getId() { + return id; + } + + @Override + public RecipeSerializer getSerializer() { + return BrewingRecipeSerializer.INSTANCE; + } + + @Override + public RecipeType getType() { + return Type.INSTANCE; + } + + public static class Type implements RecipeType { + private Type() {} + public static final Type INSTANCE = new Type(); + public static final String ID = "brewing"; + } +} diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipeSerializer.java b/src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipeSerializer.java new file mode 100644 index 0000000..c906234 --- /dev/null +++ b/src/main/java/com/megatrex4/ukrainian_dlight/recipe/BrewingRecipeSerializer.java @@ -0,0 +1,41 @@ +package com.megatrex4.ukrainian_dlight.recipe; + +import com.google.gson.JsonObject; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.recipe.RecipeSerializer; +import net.minecraft.recipe.RecipeType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +public class BrewingRecipeSerializer implements RecipeSerializer { + + @Override + public BrewingRecipe read(Identifier id, JsonObject json) { + return BrewingRecipe.fromJson(id, json); + } + + @Override + public BrewingRecipe read(Identifier id, PacketByteBuf buf) { + // Implement packet reading logic if necessary + return null; + } + + @Override + public void write(PacketByteBuf buf, BrewingRecipe recipe) { + // Implement packet writing logic if necessary + } + + public static final RecipeSerializer INSTANCE = new BrewingRecipeSerializer(); + public static final Identifier ID = new Identifier("ukrainian_dlight", "brewing"); + + public static void register() { + Registry.register(Registries.RECIPE_SERIALIZER, ID, INSTANCE); + Registry.register(Registries.RECIPE_TYPE, ID, new RecipeType() { + @Override + public String toString() { + return ID.toString(); + } + }); + } +} diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/screen/BrewingKegScreenHandler.java b/src/main/java/com/megatrex4/ukrainian_dlight/screen/BrewingKegScreenHandler.java index 8800b5b..31f8800 100644 --- a/src/main/java/com/megatrex4/ukrainian_dlight/screen/BrewingKegScreenHandler.java +++ b/src/main/java/com/megatrex4/ukrainian_dlight/screen/BrewingKegScreenHandler.java @@ -1,6 +1,7 @@ package com.megatrex4.ukrainian_dlight.screen; import com.megatrex4.ukrainian_dlight.block.entity.BrewingKegBlockEntity; +import com.megatrex4.ukrainian_dlight.util.FluidStack; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; @@ -15,6 +16,7 @@ public class BrewingKegScreenHandler extends ScreenHandler { + public FluidStack fluidStack; private final Inventory inventory; private final PropertyDelegate propertyDelegate; public final BrewingKegBlockEntity blockEntity; @@ -32,6 +34,7 @@ public BrewingKegScreenHandler(int syncId, PlayerInventory playerInventory, inventory.onOpen(playerInventory.player); this.propertyDelegate = arrayPropertyDelegate; this.blockEntity = ((BrewingKegBlockEntity) blockEntity); + this.fluidStack = new FluidStack(((BrewingKegBlockEntity) blockEntity).fluidStorage.variant, ((BrewingKegBlockEntity) blockEntity).fluidStorage.amount); //add 6 ingredients slots int inputStartX = 53; @@ -75,6 +78,13 @@ public boolean canInsert(ItemStack stack) { addProperties(arrayPropertyDelegate); } + + public void setFluid(FluidStack stack) { + fluidStack = stack; + } + + + public boolean isCrafting() { return propertyDelegate.get(0) > 0; } diff --git a/src/main/java/com/megatrex4/ukrainian_dlight/util/FluidStack.java b/src/main/java/com/megatrex4/ukrainian_dlight/util/FluidStack.java new file mode 100644 index 0000000..d1acee0 --- /dev/null +++ b/src/main/java/com/megatrex4/ukrainian_dlight/util/FluidStack.java @@ -0,0 +1,37 @@ +package com.megatrex4.ukrainian_dlight.util; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; + +public class FluidStack { + public FluidVariant fluidVariant; + public long amount; + + public FluidStack(FluidVariant variant, long amount) { + this.fluidVariant = variant; + this.amount = amount; + } + + public FluidVariant getFluidVariant() { + return fluidVariant; + } + + public void setFluidVariant(FluidVariant fluidVariant) { + this.fluidVariant = fluidVariant; + } + + public long getAmount() { + return amount; + } + + public void setAmount(long amount) { + this.amount = amount; + } + + public static long convertDropletsToMb(long droplets) { + return (droplets / 81); + } + + public static long convertMbToDroplets(long mb) { + return mb * 81; + } +}