From 7d63797d4dc5fdc7287b9b0e6a49c0f6b1a52024 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 31 Mar 2024 22:34:31 +0100 Subject: [PATCH] Jars can now hold fluids --- .../unicopia/block/ItemJarBlock.java | 295 +++--------------- .../unicopia/block/jar/EntityJarContents.java | 80 +++++ .../block/jar/FakeFluidJarContents.java | 79 +++++ .../block/jar/FluidOnlyJarContents.java | 55 ++++ .../unicopia/block/jar/ItemsJarContents.java | 208 ++++++++++++ .../unicopia/client/render/RenderUtil.java | 76 +---- .../entity/ItemJarBlockEntityRenderer.java | 99 ++++-- .../client/render/model/CubeModel.java | 77 +++++ .../loot/UBlockLootTableProvider.java | 29 ++ .../unicopia/util/FluidHelper.java | 69 ++++ .../unicopia/util/PsyFluidHelper.java | 51 +++ 11 files changed, 772 insertions(+), 346 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java create mode 100644 src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java create mode 100644 src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java diff --git a/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java index 1c726165..ef84849d 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java @@ -1,42 +1,34 @@ package com.minelittlepony.unicopia.block; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem; -import com.minelittlepony.unicopia.util.NbtSerialisable; +import com.minelittlepony.unicopia.block.jar.EntityJarContents; +import com.minelittlepony.unicopia.block.jar.FluidOnlyJarContents; +import com.minelittlepony.unicopia.block.jar.ItemsJarContents; +import com.minelittlepony.unicopia.block.jar.FakeFluidJarContents; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; import net.minecraft.block.InventoryProvider; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.Bucketable; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.SidedInventory; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; +import net.minecraft.item.ItemUsage; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; -import net.minecraft.registry.Registries; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; -import net.minecraft.util.Identifier; import net.minecraft.util.TypedActionResult; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; import net.minecraft.world.World; import net.minecraft.world.WorldAccess; @@ -79,7 +71,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven public int getComparatorOutput(BlockState state, World world, BlockPos pos) { return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR) .map(TileData::getItems) - .map(data -> Math.min(16, data.getStacks().size())) + .map(data -> Math.min(16, data.stacks().size())) .orElse(0); } @@ -96,7 +88,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven return new TileData(pos, state); } - + @Nullable @Override public SidedInventory getInventory(BlockState state, WorldAccess world, BlockPos pos) { return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(TileData::getItems).orElse(null); @@ -130,6 +122,16 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven return getContents() instanceof EntityJarContents c ? c : null; } + @Nullable + public FluidJarContents getFluid() { + return getContents() instanceof FluidJarContents c ? c : null; + } + + @Nullable + public FakeFluidJarContents getFakeFluid() { + return getContents() instanceof FakeFluidJarContents c ? c : null; + } + @Override public Packet toUpdatePacket() { return BlockEntityUpdateS2CPacket.create(this); @@ -151,11 +153,13 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven @Override public void readNbt(NbtCompound nbt) { if (nbt.contains("items", NbtElement.COMPOUND_TYPE)) { - contents = new ItemsJarContents(this); - contents.fromNBT(nbt.getCompound("items")); + contents = new ItemsJarContents(this, nbt.getCompound("items")); } else if (nbt.contains("entity", NbtElement.COMPOUND_TYPE)) { - contents = new EntityJarContents(this); - contents.fromNBT(nbt.getCompound("entity")); + contents = new EntityJarContents(this, nbt.getCompound("entity")); + } else if (nbt.contains("fluid", NbtElement.COMPOUND_TYPE)) { + contents = new FluidOnlyJarContents(this, nbt.getCompound("fluid")); + } else if (nbt.contains("fakeFluid", NbtElement.COMPOUND_TYPE)) { + contents = new FakeFluidJarContents(this, nbt.getCompound("fakeFluid")); } } @@ -163,249 +167,34 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven protected void writeNbt(NbtCompound nbt) { var items = getItems(); if (items != null) { - nbt.put("items", items.toNBT()); + nbt.put("items", items.toNBT(new NbtCompound())); } else if (getEntity() != null) { - nbt.put("entity", getEntity().toNBT()); + nbt.put("entity", getEntity().toNBT(new NbtCompound())); + } else if (getFluid() != null) { + nbt.put("fluid", getFluid().toNBT(new NbtCompound())); + } else if (getFakeFluid() != null) { + nbt.put("fakeFluid", getFakeFluid().toNBT(new NbtCompound())); } } } - - public interface JarContents extends NbtSerialisable { + public interface JarContents { TypedActionResult interact(PlayerEntity player, Hand hand); void onDestroyed(); + + NbtCompound toNBT(NbtCompound compound); + + default void consumeAndSwap(PlayerEntity player, Hand hand, ItemStack output) { + player.setStackInHand(hand, ItemUsage.exchangeStack(player.getStackInHand(hand), player, output.copy())); + } } - public static class EntityJarContents implements JarContents { - @Nullable - private EntityType entityType; - @Nullable - private Entity renderEntity; + public interface FluidJarContents extends JarContents { + FluidVariant fluid(); - private final TileData tile; - - public EntityJarContents(TileData tile) { - this(tile, null); - } - - public EntityJarContents(TileData tile, EntityType entityType) { - this.tile = tile; - this.entityType = entityType; - } - - @Nullable - public Entity getOrCreateEntity() { - if (entityType == null && tile.getWorld() != null) { - return null; - } - - if (renderEntity == null || renderEntity.getType() != entityType) { - renderEntity = entityType.create(tile.getWorld()); - } - return renderEntity; - } - - @Override - public TypedActionResult interact(PlayerEntity player, Hand hand) { - ItemStack stack = player.getStackInHand(hand); - if (stack.isOf(Items.BUCKET)) { - if (getOrCreateEntity() instanceof Bucketable bucketable) { - if (!player.isCreative()) { - stack.decrement(1); - if (stack.isEmpty()) { - player.setStackInHand(hand, bucketable.getBucketItem()); - } else { - player.giveItemStack(bucketable.getBucketItem()); - } - } - player.playSound(bucketable.getBucketFillSound(), 1, 1); - } - tile.markDirty(); - return TypedActionResult.success(new ItemsJarContents(tile)); - } - return TypedActionResult.pass(this); - } - - @Override - public void onDestroyed() { - tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState()); - Entity entity = getOrCreateEntity(); - if (entity != null) { - entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos()); - tile.getWorld().spawnEntity(entity); - } - } - - @Override - public void toNBT(NbtCompound compound) { - compound.putString("entity", EntityType.getId(entityType).toString()); - } - - @Override - public void fromNBT(NbtCompound compound) { - entityType = Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null); - } - - } - - public static class ItemsJarContents implements JarContents, SidedInventory { - private static final int[] SLOTS = IntStream.range(0, 16).toArray(); - - private final TileData tile; - private List stacks = new ArrayList<>(); - - public ItemsJarContents(TileData tile) { - this.tile = tile; - } - - @Override - public TypedActionResult interact(PlayerEntity player, Hand hand) { - ItemStack handStack = player.getStackInHand(hand); - - if (handStack.isEmpty()) { - if (stacks.isEmpty()) { - return TypedActionResult.fail(this); - } - dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0)); - markDirty(); - return TypedActionResult.success(this); - } - - if (stacks.isEmpty()) { - if (handStack.getItem() instanceof MixinEntityBucketItem bucket) { - if (!player.isCreative()) { - handStack.decrement(1); - if (handStack.isEmpty()) { - player.setStackInHand(hand, Items.BUCKET.getDefaultStack()); - } else { - player.giveItemStack(Items.BUCKET.getDefaultStack()); - } - } - - player.playSound(bucket.getEmptyingSound(), 1, 1); - markDirty(); - return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType())); - } - } - - if (stacks.size() >= size()) { - return TypedActionResult.fail(this); - } - stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1)); - markDirty(); - - return TypedActionResult.success(this); - } - - @Override - public void onDestroyed() { - stacks.forEach(stack -> { - dropStack(tile.getWorld(), tile.getPos(), stack); - }); - } - - public List getStacks() { - return stacks; - } - - @Override - public int size() { - return 15; - } - - @Override - public boolean isEmpty() { - return stacks.isEmpty(); - } - - @Override - public ItemStack getStack(int slot) { - return slot < 0 || slot >= stacks.size() ? ItemStack.EMPTY : stacks.get(slot); - } - - @Override - public ItemStack removeStack(int slot, int amount) { - if (slot < 0 || slot >= stacks.size()) { - return ItemStack.EMPTY; - } - - try { - ItemStack stack = stacks.get(slot); - ItemStack removed = stack.split(1); - if (stack.isEmpty()) { - stacks.remove(slot); - } - return removed; - } finally { - markDirty(); - } - } - - @Override - public ItemStack removeStack(int slot) { - if (slot < 0 || slot >= stacks.size()) { - return ItemStack.EMPTY; - } - - try { - return stacks.remove(slot); - } finally { - markDirty(); - } - } - - @Override - public void setStack(int slot, ItemStack stack) { - if (slot >= stacks.size()) { - stacks.add(stack); - } else { - stacks.set(slot, stack); - } - markDirty(); - } - - @Override - public boolean canPlayerUse(PlayerEntity player) { - return false; - } - - @Override - public void clear() { - stacks.clear(); - markDirty(); - } - - @Override - public int[] getAvailableSlots(Direction side) { - return SLOTS; - } - - @Override - public boolean canInsert(int slot, ItemStack stack, Direction dir) { - return slot >= 0 && slot < size() && slot >= stacks.size(); - } - - @Override - public boolean canExtract(int slot, ItemStack stack, Direction dir) { - return slot >= 0 && slot < size() && slot < stacks.size(); - } - - @Override - public void toNBT(NbtCompound compound) { - compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks)); - } - - @Override - public void fromNBT(NbtCompound compound) { - stacks = NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE)) - .limit(size()) - .collect(Collectors.toList()); - } - - @Override - public void markDirty() { - tile.markDirty(); + default long amount() { + return FluidConstants.BUCKET; } } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java new file mode 100644 index 00000000..967807c1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java @@ -0,0 +1,80 @@ +package com.minelittlepony.unicopia.block.jar; + +import java.util.function.Supplier; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.base.Suppliers; +import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents; +import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents; +import com.minelittlepony.unicopia.block.ItemJarBlock.TileData; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Bucketable; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.TypedActionResult; + +public record EntityJarContents ( + TileData tile, + @Nullable EntityType entityType, + Supplier<@Nullable Entity> entity +) implements FluidJarContents { + public EntityJarContents(TileData tile, NbtCompound compound) { + this(tile, Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null)); + } + + public EntityJarContents(TileData tile) { + this(tile, (EntityType)null); + } + + public EntityJarContents(TileData tile, EntityType entityType) { + this(tile, entityType, Suppliers.memoize(() -> { + return entityType == null ? null : entityType.create(tile.getWorld()); + })); + } + + @Override + public TypedActionResult interact(PlayerEntity player, Hand hand) { + ItemStack stack = player.getStackInHand(hand); + if (stack.isOf(Items.BUCKET)) { + if (entity().get() instanceof Bucketable bucketable) { + consumeAndSwap(player, hand, bucketable.getBucketItem()); + player.playSound(bucketable.getBucketFillSound(), 1, 1); + } + tile.markDirty(); + return TypedActionResult.success(new ItemsJarContents(tile)); + } + return TypedActionResult.pass(this); + } + + @Override + public void onDestroyed() { + tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState()); + Entity entity = entity().get(); + if (entity != null) { + entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos()); + tile.getWorld().spawnEntity(entity); + } + } + + @Override + public NbtCompound toNBT(NbtCompound compound) { + compound.putString("entity", EntityType.getId(entityType).toString()); + return compound; + } + + @Override + public FluidVariant fluid() { + return FluidVariant.of(Fluids.WATER); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java new file mode 100644 index 00000000..e49d648c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java @@ -0,0 +1,79 @@ +package com.minelittlepony.unicopia.block.jar; + +import java.util.Optional; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents; +import com.minelittlepony.unicopia.block.ItemJarBlock.TileData; +import com.minelittlepony.unicopia.util.FluidHelper; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluid; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.TypedActionResult; + +public record FakeFluidJarContents ( + TileData tile, + String fluid, + int color, + Item empty, + Item filled +) implements JarContents { + public FakeFluidJarContents(TileData tile, NbtCompound compound) { + this(tile, compound.getString("fluid"), compound.getInt("color"), + Registries.ITEM.get(new Identifier(compound.getString("empty"))), + Registries.ITEM.get(new Identifier(compound.getString("filled")))); + } + + @Override + public TypedActionResult interact(PlayerEntity player, Hand hand) { + ItemStack stack = player.getStackInHand(hand); + + tile.markDirty(); + return getRealFluid().map(FluidVariant::of).>map(fluid -> { + long remainder = FluidHelper.deposit(stack, player, hand, fluid, FluidConstants.BUCKET); + fluid.getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1)); + if (remainder > 0) { + return TypedActionResult.success(new FluidOnlyJarContents(tile, remainder, fluid)); + } + return TypedActionResult.success(new ItemsJarContents(tile)); + }).orElseGet(() -> { + if (!stack.isOf(empty)) { + return TypedActionResult.pass(this); + } + consumeAndSwap(player, hand, filled.getDefaultStack()); + player.playSound("powder_snow".equalsIgnoreCase(fluid) ? USounds.Vanilla.ITEM_BUCKET_FILL_POWDER_SNOW : USounds.Vanilla.ITEM_BUCKET_FILL, 1, 1); + return TypedActionResult.success(new ItemsJarContents(tile)); + }); + } + + @Override + public void onDestroyed() { + getRealFluid().ifPresent(fluid -> { + tile.getWorld().setBlockState(tile.getPos(), FluidHelper.getFullFluidState(FluidVariant.of(fluid)).getBlockState()); + }); + } + + @Override + public NbtCompound toNBT(NbtCompound compound) { + compound.putString("fluid", fluid); + compound.putInt("color", color); + compound.putString("empty", Registries.ITEM.getId(empty).toString()); + compound.putString("filled", Registries.ITEM.getId(filled).toString()); + return compound; + } + + private Optional getRealFluid() { + return Registries.FLUID.getIds().stream() + .filter(id -> id.getPath().equalsIgnoreCase(fluid)) + .findFirst() + .map(Registries.FLUID::get); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java new file mode 100644 index 00000000..0cdcab4b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java @@ -0,0 +1,55 @@ +package com.minelittlepony.unicopia.block.jar; + +import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents; +import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents; +import com.minelittlepony.unicopia.block.ItemJarBlock.TileData; +import com.minelittlepony.unicopia.util.FluidHelper; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; + +public record FluidOnlyJarContents ( + TileData tile, + long amount, + FluidVariant fluid +) implements FluidJarContents { + + public FluidOnlyJarContents(TileData tile, NbtCompound compound) { + this(tile, compound.getLong("amount"), FluidVariant.fromNbt(compound.getCompound("fluid"))); + } + + @Override + public TypedActionResult interact(PlayerEntity player, Hand hand) { + ItemStack stack = player.getStackInHand(hand); + if (stack.isOf(Items.BUCKET)) { + long remainder = FluidHelper.deposit(stack, player, hand, fluid, amount); + tile.markDirty(); + fluid.getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1)); + if (remainder > 0) { + return TypedActionResult.success(new FluidOnlyJarContents(tile, remainder, fluid)); + } + return TypedActionResult.success(new ItemsJarContents(tile)); + } + return TypedActionResult.pass(this); + } + + @Override + public void onDestroyed() { + if (amount >= FluidConstants.BUCKET) { + tile.getWorld().setBlockState(tile.getPos(), FluidHelper.getFullFluidState(fluid).getBlockState()); + } + } + + @Override + public NbtCompound toNBT(NbtCompound compound) { + compound.put("fluid", fluid.toNbt()); + compound.putLong("amount", amount); + return compound; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java new file mode 100644 index 00000000..a406c3dd --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java @@ -0,0 +1,208 @@ +package com.minelittlepony.unicopia.block.jar; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents; +import com.minelittlepony.unicopia.block.ItemJarBlock.TileData; +import com.minelittlepony.unicopia.item.UItems; +import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem; +import com.minelittlepony.unicopia.util.FluidHelper; +import com.minelittlepony.unicopia.util.NbtSerialisable; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.block.Block; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.SidedInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.util.Hand; +import net.minecraft.util.Pair; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.Direction; + +public record ItemsJarContents ( + TileData tile, + List stacks + ) implements JarContents, SidedInventory { + private static final int[] SLOTS = IntStream.range(0, 16).toArray(); + + public ItemsJarContents(TileData tile) { + this(tile, new ArrayList<>()); + } + + public ItemsJarContents(TileData tile, NbtCompound compound) { + this(tile, NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE)) + .limit(15) + .collect(Collectors.toList())); + } + + @Override + public TypedActionResult interact(PlayerEntity player, Hand hand) { + ItemStack handStack = player.getStackInHand(hand); + + if (handStack.isEmpty()) { + if (stacks.isEmpty()) { + return TypedActionResult.fail(this); + } + Block.dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0)); + markDirty(); + return TypedActionResult.success(this); + } + + if (stacks.isEmpty()) { + if (handStack.getItem() instanceof MixinEntityBucketItem bucket) { + consumeAndSwap(player, hand, Items.BUCKET.getDefaultStack()); + player.playSound(bucket.getEmptyingSound(), 1, 1); + markDirty(); + return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType())); + } + + Pair fluid = FluidHelper.extract(handStack, player, hand).orElse(null); + if (fluid != null) { + fluid.getRight().getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1)); + markDirty(); + return TypedActionResult.success(new FluidOnlyJarContents(tile, fluid.getLeft(), fluid.getRight())); + } + + if (handStack.isOf(Items.MILK_BUCKET)) { + consumeAndSwap(player, hand, handStack.getRecipeRemainder()); + player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1); + markDirty(); + return TypedActionResult.success(new FakeFluidJarContents(tile, "milk", 0xFFFFFFFF, Items.BUCKET, Items.MILK_BUCKET)); + } + + if (handStack.isOf(Items.POWDER_SNOW_BUCKET)) { + consumeAndSwap(player, hand, Items.BUCKET.getDefaultStack()); + player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY_POWDER_SNOW, 1, 1); + markDirty(); + return TypedActionResult.success(new FakeFluidJarContents(tile, "powder_snow", 0xFFFFFFFF, Items.BUCKET, Items.POWDER_SNOW_BUCKET)); + } + + if (handStack.isOf(UItems.LOVE_BUCKET)) { + consumeAndSwap(player, hand, handStack.getRecipeRemainder()); + player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1); + markDirty(); + return TypedActionResult.success(new FakeFluidJarContents(tile, "love", 0xFF3030, Items.BUCKET, UItems.LOVE_BUCKET)); + } + + if (handStack.isOf(UItems.JUICE)) { + consumeAndSwap(player, hand, handStack.getRecipeRemainder()); + player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1); + markDirty(); + return TypedActionResult.success(new FakeFluidJarContents(tile, "apple_juice", 0x30FF30, Items.GLASS_BOTTLE, UItems.JUICE)); + } + } + + if (stacks.size() >= size()) { + return TypedActionResult.fail(this); + } + stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1)); + markDirty(); + + return TypedActionResult.success(this); + } + + @Override + public void onDestroyed() { + stacks.forEach(stack -> Block.dropStack(tile.getWorld(), tile.getPos(), stack)); + } + + @Override + public int size() { + return 15; + } + + @Override + public boolean isEmpty() { + return stacks.isEmpty(); + } + + @Override + public ItemStack getStack(int slot) { + return slot < 0 || slot >= stacks.size() ? ItemStack.EMPTY : stacks.get(slot); + } + + @Override + public ItemStack removeStack(int slot, int amount) { + if (slot < 0 || slot >= stacks.size()) { + return ItemStack.EMPTY; + } + + try { + ItemStack stack = stacks.get(slot); + ItemStack removed = stack.split(1); + if (stack.isEmpty()) { + stacks.remove(slot); + } + return removed; + } finally { + markDirty(); + } + } + + @Override + public ItemStack removeStack(int slot) { + if (slot < 0 || slot >= stacks.size()) { + return ItemStack.EMPTY; + } + + try { + return stacks.remove(slot); + } finally { + markDirty(); + } + } + + @Override + public void setStack(int slot, ItemStack stack) { + if (slot >= stacks.size()) { + stacks.add(stack); + } else { + stacks.set(slot, stack); + } + markDirty(); + } + + @Override + public boolean canPlayerUse(PlayerEntity player) { + return false; + } + + @Override + public void clear() { + stacks.clear(); + markDirty(); + } + + @Override + public int[] getAvailableSlots(Direction side) { + return SLOTS; + } + + @Override + public boolean canInsert(int slot, ItemStack stack, Direction dir) { + return slot >= 0 && slot < size() && slot >= stacks.size(); + } + + @Override + public boolean canExtract(int slot, ItemStack stack, Direction dir) { + return slot >= 0 && slot < size() && slot < stacks.size(); + } + + @Override + public NbtCompound toNBT(NbtCompound compound) { + compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks)); + return compound; + } + + @Override + public void markDirty() { + tile.markDirty(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java index c2795b11..9b92256d 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java @@ -5,20 +5,15 @@ import org.joml.Vector3f; import org.joml.Vector4f; import net.minecraft.client.render.BufferBuilder; -import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormats; -import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.ColorHelper; -import net.minecraft.util.math.Direction; public class RenderUtil { public static final Vector4f TEMP_VECTOR = new Vector4f(); - private static final Vector4f TEMP_UV_VECTOR = new Vector4f(); + public static final Vector4f TEMP_UV_VECTOR = new Vector4f(); + public static final Vector3f TEMP_NORMAL_VECTOR = new Vector3f(); public static final Vertex[] UNIT_FACE = new Vertex[] { new Vertex(0, 0, 0, 1, 1), new Vertex(0, 1, 0, 1, 0), @@ -31,72 +26,9 @@ public class RenderUtil { new Vertex(1, 0, 0, 1, 1), new Vertex(0, 0, 0, 0, 1) }; - private static final Vertex[][] CUBE_VERTICES = { - new Vertex[] { // down - new Vertex(0, 0, 0, 0, 0), - new Vertex(1, 0, 0, 1, 0), - new Vertex(1, 0, 1, 1, 1), - new Vertex(0, 0, 1, 0, 1) - }, - new Vertex[] { //up - new Vertex(0, 1, 0, 0, 0), - new Vertex(0, 1, 1, 0, 1), - new Vertex(1, 1, 1, 1, 1), - new Vertex(1, 1, 0, 1, 0) - }, - new Vertex[] { //north - new Vertex(0, 0, 0, 0, 0), - new Vertex(0, 1, 0, 0, 1), - new Vertex(1, 1, 0, 1, 1), - new Vertex(1, 0, 0, 1, 0) - }, - new Vertex[] { //south - new Vertex(0, 0, 1, 0, 0), - new Vertex(1, 0, 1, 1, 0), - new Vertex(1, 1, 1, 1, 1), - new Vertex(0, 1, 1, 0, 1) - }, - new Vertex[] { //west - new Vertex(0, 0, 0, 0, 0), - new Vertex(0, 0, 1, 1, 0), - new Vertex(0, 1, 1, 1, 1), - new Vertex(0, 1, 0, 0, 1) - }, - new Vertex[] { //east - new Vertex(1, 0, 0, 0, 0), - new Vertex(1, 1, 0, 1, 0), - new Vertex(1, 1, 1, 1, 1), - new Vertex(1, 0, 1, 0, 1) - } - }; - public static void renderSpriteCubeFaces(MatrixStack matrices, VertexConsumerProvider provider, Sprite sprite, - float width, float height, float length, - int color, int light, int overlay, - Direction... directions) { - float r = ColorHelper.Abgr.getRed(color), - g = ColorHelper.Abgr.getGreen(color), - b = ColorHelper.Abgr.getBlue(color), - a = ColorHelper.Abgr.getAlpha(color); - float u0 = sprite.getMinU(), uDelta = sprite.getMaxU() - u0; - float v0 = sprite.getMinV(), vDelta = sprite.getMaxV() - v0; - RenderLayer layer = RenderLayer.getEntitySolid(sprite.getAtlasId()); - VertexConsumer buffer = provider.getBuffer(layer); - Matrix4f position = matrices.peek().getPositionMatrix(); - for (Direction direction : directions) { - for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) { - Vector4f pos = position.transform(TEMP_VECTOR.set(vertex.position(), 1).mul(width, height, length, 1)); - buffer.vertex( - pos.x, pos.y, pos.z, - r, g, b, a, - u0 + vertex.texture().x * uDelta, - v0 + vertex.texture().y * vDelta, - overlay, light, - direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ() - ); - } - } - } + + public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) { renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java index e0b4883c..472a8335 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java @@ -1,14 +1,24 @@ package com.minelittlepony.unicopia.client.render.entity; +import java.util.Arrays; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.common.util.Color; import com.minelittlepony.unicopia.block.ItemJarBlock; -import com.minelittlepony.unicopia.block.ItemJarBlock.EntityJarContents; -import com.minelittlepony.unicopia.block.ItemJarBlock.ItemsJarContents; -import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents; +import com.minelittlepony.unicopia.block.jar.ItemsJarContents; +import com.minelittlepony.unicopia.block.jar.FakeFluidJarContents; +import com.minelittlepony.unicopia.block.jar.EntityJarContents; +import com.minelittlepony.unicopia.client.render.model.CubeModel; +import com.minelittlepony.unicopia.util.FluidHelper; import com.minelittlepony.unicopia.util.PosHelper; -import net.minecraft.block.Blocks; +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler; +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.color.world.BiomeColors; +import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.BlockEntityRenderer; import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; @@ -20,8 +30,12 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.Fluid; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.Vec3d; @@ -29,16 +43,13 @@ import net.minecraft.util.math.random.Random; import net.minecraft.world.World; public class ItemJarBlockEntityRenderer implements BlockEntityRenderer { - + private static final Direction[] GLASS_SIDES = Arrays.stream(PosHelper.ALL).filter(i -> i != Direction.UP).toArray(Direction[]::new); private final ItemRenderer itemRenderer; private final EntityRenderDispatcher dispatcher; - private final Sprite waterSprite; - public ItemJarBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) { itemRenderer = ctx.getItemRenderer(); dispatcher = ctx.getEntityRenderDispatcher(); - waterSprite = MinecraftClient.getInstance().getBakedModelManager().getBlockModels().getModel(Blocks.WATER.getDefaultState()).getParticleSprite(); } @Override @@ -53,6 +64,16 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer= 1; + CubeModel.render( matrices, - vertices, - waterSprite, - 0.4F, 0.4F, 0.4F, - BiomeColors.getWaterColor(world, pos), - light, overlay, PosHelper.ALL + vertices.getBuffer(opaque ? RenderLayer.getEntitySolid(topSprite.getAtlasId()) : RenderLayer.getEntityTranslucent(topSprite.getAtlasId())), + topSprite.getMinU(), topSprite.getMinV(), + topSprite.getMaxU(), topSprite.getMaxV(), + 0.28F, 0.01F, 0.28F, + 0.73F, 0.01F + height, 0.73F, + color, + light, overlay, Direction.UP + ); + Sprite sideSprite = sprite[sprite.length - 1]; + CubeModel.render( + matrices, + vertices.getBuffer(opaque ? RenderLayer.getEntitySolid(sideSprite.getAtlasId()) : RenderLayer.getEntityTranslucent(sideSprite.getAtlasId())), + sideSprite.getMinU(), sideSprite.getMinV(), + sideSprite.getMaxU(), sideSprite.getMaxV(), + 0.28F, 0.01F, 0.28F, + 0.73F, 0.01F + height, 0.73F, + color, + light, overlay, GLASS_SIDES ); matrices.pop(); } + + private int getFluidColor(World world, BlockPos pos, FluidState state) { + return getFluidHandler(state.getFluid()).getFluidColor(world, pos, state); + } + + private Sprite[] getFluidSprite(@Nullable World world, BlockPos pos, FluidState state) { + return getFluidHandler(state.getFluid()).getFluidSprites(world, pos, state); + } + + private FluidRenderHandler getFluidHandler(Fluid fluid) { + FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluid); + if (handler == null) { + return FluidRenderHandlerRegistry.INSTANCE.get(Fluids.WATER); + } + return handler; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java new file mode 100644 index 00000000..6aaadb33 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java @@ -0,0 +1,77 @@ +package com.minelittlepony.unicopia.client.render.model; + +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector2f; +import org.joml.Vector3f; +import org.joml.Vector4f; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.client.render.RenderUtil.Vertex; + +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Direction; + +public class CubeModel { + private static final Vector2f TEMP_UV_VECTOR = new Vector2f(); + private static final Vertex[][] CUBE_VERTICES = { + new Vertex[] { + new Vertex(0, 0, 0, 0, 0), + new Vertex(1, 0, 0, 1, 0), + new Vertex(1, 0, 1, 1, 1), + new Vertex(0, 0, 1, 0, 1) + }, + new Vertex[] { + new Vertex(0, 1, 0, 0, 0), + new Vertex(0, 1, 1, 0, 1), + new Vertex(1, 1, 1, 1, 1), + new Vertex(1, 1, 0, 1, 0) + }, + new Vertex[] { + new Vertex(0, 0, 0, 0, 0), + new Vertex(0, 1, 0, 0, 1), + new Vertex(1, 1, 0, 1, 1), + new Vertex(1, 0, 0, 1, 0) + }, + new Vertex[] { + new Vertex(0, 0, 1, 0, 0), + new Vertex(1, 0, 1, 1, 0), + new Vertex(1, 1, 1, 1, 1), + new Vertex(0, 1, 1, 0, 1) + }, + new Vertex[] { + new Vertex(0, 0, 0, 0, 0), + new Vertex(0, 0, 1, 1, 0), + new Vertex(0, 1, 1, 1, 1), + new Vertex(0, 1, 0, 0, 1) + }, + new Vertex[] { + new Vertex(1, 0, 0, 0, 0), + new Vertex(1, 1, 0, 1, 0), + new Vertex(1, 1, 1, 1, 1), + new Vertex(1, 0, 1, 0, 1) + } + }; + + public static void render(MatrixStack matrices, VertexConsumer buffer, + float u0, float v0, float u1, float v1, + float x0, float y0, float z0, float x1, float y1, float z1, + int color, int light, int overlay, + Direction... directions) { + float r = Color.r(color), g = Color.g(color), b = Color.b(color); + float du = u1 - u0, dv = v1 - v0; + float dx = x1 - x0, dy = y1 - y0, dz = z1 - z0; + Matrix4f position = matrices.peek().getPositionMatrix(); + Matrix3f normal = matrices.peek().getNormalMatrix(); + for (Direction direction : directions) { + for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) { + Vector4f pos = position.transform(RenderUtil.TEMP_VECTOR.set(vertex.position(), 1).mul(dx, dy, dz, 1).add(x0, y0, z0, 0)); + Vector2f tex = TEMP_UV_VECTOR.set(vertex.texture().x, vertex.texture().y).mul(du, dv).add(u0, v0); + Vector3f norm = normal.transform(RenderUtil.TEMP_NORMAL_VECTOR.set(direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ())); + buffer.vertex(pos.x, pos.y, pos.z, r, g, b, 1, tex.x, tex.y, overlay, light, norm.x, norm.y, norm.z); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java index 77b0d94e..d7ed00b1 100644 --- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java +++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java @@ -1,6 +1,8 @@ package com.minelittlepony.unicopia.datagen.providers.loot; import java.util.List; + +import com.minelittlepony.unicopia.block.PieBlock; import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.datagen.providers.UModelProvider; import com.minelittlepony.unicopia.item.UItems; @@ -14,14 +16,21 @@ import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.block.enums.BedPart; import net.minecraft.enchantment.Enchantments; +import net.minecraft.item.Item; import net.minecraft.item.Items; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; +import net.minecraft.loot.condition.BlockStatePropertyLootCondition; +import net.minecraft.loot.condition.LootConditionConsumingBuilder; import net.minecraft.loot.condition.TableBonusLootCondition; import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; +import net.minecraft.predicate.StatePredicate; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.state.property.Property; +import net.minecraft.util.StringIdentifiable; public class UBlockLootTableProvider extends FabricBlockLootTableProvider { @@ -116,6 +125,7 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider { addDrop(UBlocks.CLOUD_PILLAR, drops(UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, ConstantLootNumberProvider.create(2))); addDrop(UBlocks.FROSTED_OBSIDIAN, Blocks.OBSIDIAN); + addDrop(UBlocks.APPLE_PIE, pieDrops(UBlocks.APPLE_PIE, UItems.APPLE_PIE, UItems.APPLE_PIE_HOOF)); } private LootTable.Builder fruitLeavesDrops(Block leaves) { @@ -136,4 +146,23 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider { ); } + private LootTable.Builder pieDrops(Block block, Item drop, Item stomped) { + return LootTable.builder().pool(LootPool.builder() + .rolls(ConstantLootNumberProvider.create(1)).conditionally(WITH_SILK_TOUCH) + .with(addStateCondition(block, PieBlock.STOMPED, false, applyExplosionDecay(block, ItemEntry.builder(drop)))) + .with(addStateCondition(block, PieBlock.STOMPED, true, applyExplosionDecay(block, ItemEntry.builder(stomped)))) + ); + } + + public static , P extends Comparable

& StringIdentifiable> T addStateCondition(Block block, + Property

property, P value, + LootConditionConsumingBuilder builder) { + return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value))); + } + + public static > T addStateCondition(Block block, + BooleanProperty property, boolean value, + LootConditionConsumingBuilder builder) { + return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value))); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java b/src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java new file mode 100644 index 00000000..48508815 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java @@ -0,0 +1,69 @@ +package com.minelittlepony.unicopia.util; + +import java.util.Iterator; +import java.util.Optional; +import java.util.function.Function; + +import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; +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.Storage; +import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.Pair; + +/** + * Here be dragons + */ +public interface FluidHelper { + static FluidState getFullFluidState(FluidVariant variant) { + return PsyFluidHelper.getFullFluidState(variant); + } + + static Optional> extract(ItemStack stack, PlayerEntity player, Hand hand) { + return getAsContainer(stack, ContainerItemContext.forPlayerInteraction(player, hand)) + .filter(c -> !c.isResourceBlank()) + .map(container -> applyTransaction(t -> { + FluidVariant type = container.getResource(); + long amountExtracted = container.extract(type, FluidConstants.BUCKET, t); + if (amountExtracted > 0) { + return new Pair<>(amountExtracted, type); + } + return null; + })); + } + + static long deposit(ItemStack stack, PlayerEntity player, Hand hand, FluidVariant variant, long amount) { + return amount - getAsStorage(stack, ContainerItemContext.forPlayerInteraction(player, hand)) + .map(storage -> applyTransaction(t -> storage.insert(variant, amount, t))) + .orElse(0L); + } + + private static Optional> getAsStorage(ItemStack stack, ContainerItemContext context) { + return Optional.ofNullable(FluidStorage.ITEM.find(stack, context)); + } + + private static Optional> getAsContainer(ItemStack stack, ContainerItemContext context) { + return getAsStorage(stack, context).map(storage -> { + Iterator> iter = storage.iterator(); + return iter.hasNext() ? iter.next() : null; + }); + } + + private static T applyTransaction(Function action) { + try (@SuppressWarnings("deprecation") var transaction = Transaction.isOpen() + ? Transaction.getCurrentUnsafe().openNested() + : Transaction.openOuter()) { + try { + return action.apply(transaction); + } finally { + transaction.commit(); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java b/src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java new file mode 100644 index 00000000..f83e44df --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java @@ -0,0 +1,51 @@ +package com.minelittlepony.unicopia.util; + +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.fluid.FluidState; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; + +final class PsyFluidHelper { + private static final Supplier>> SIMPLE_FLUID_CLASS = Suppliers.memoize(() -> { + try { + return Optional.ofNullable(Class.forName("ivorius.psychedelicraft.fluid.SimpleFluid")); + } catch (Throwable t) { + return Optional.empty(); + } + }); + private static final Function FALLBACK_METHOD = fluid -> fluid.getFluid().getDefaultState(); + private static final Supplier> GET_FULL_FLUID_STATE = Suppliers.memoize(() -> SIMPLE_FLUID_CLASS.get().>map(type -> { + try { + final Method method = type.getDeclaredMethod("getFluidState", ItemStack.class); + if (method != null) { + return fluid -> { + try { + ItemStack stack = Items.STONE.getDefaultStack(); + NbtCompound fluidTag = stack.getOrCreateSubNbt("fluid"); + fluidTag.putString("id", Registries.FLUID.getId(fluid.getFluid()).toString()); + fluidTag.put("attributes", fluid.getNbt()); + return FluidState.class.cast(method.invoke(type.cast(fluid), stack)); + } catch (Throwable tt) {} + return FALLBACK_METHOD.apply(fluid); + }; + } + } catch (Throwable t) {} + return FALLBACK_METHOD; + }).orElse(FALLBACK_METHOD)); + + static FluidState getFullFluidState(FluidVariant variant) { + return SIMPLE_FLUID_CLASS.get() + .filter(type -> type.isAssignableFrom(variant.getFluid().getClass())) + .map(type -> GET_FULL_FLUID_STATE.get().apply(variant)) + .orElseGet(() -> variant.getFluid().getDefaultState()); + } +}