pipes (not done for fabric) and tank (maybe for fabric?)

This commit is contained in:
2026-02-03 14:47:35 -05:00
parent 9deea24e7b
commit a30721d9b8
39 changed files with 2201 additions and 100 deletions
@@ -7,6 +7,7 @@ import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.entity.ModEntities;
import net.cmr.jurassicrevived.item.ModCreativeTabs;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.networking.ModPackets;
import net.cmr.jurassicrevived.platform.Services;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.ModMenuTypes;
@@ -68,5 +69,6 @@ public class CommonClass
ModEvents.init();
ModSounds.register();
ModPackets.register();
}
}
@@ -5,6 +5,7 @@ import net.cmr.jurassicrevived.block.entity.custom.PipeBlockEntity;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.platform.Services;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
@@ -198,6 +199,15 @@ public class PipeBlock extends Block implements EntityBlock, SimpleWaterloggedBl
};
}
if (level instanceof Level lvl) {
boolean platformHasHandler = switch (this.transport) {
case ITEMS -> Services.TRANSFER.getItemHandler(lvl, neighborPos, dir.getOpposite()).isPresent();
case FLUIDS -> Services.TRANSFER.getFluidHandler(lvl, neighborPos, dir.getOpposite()).isPresent();
case ENERGY -> Services.TRANSFER.getEnergyHandler(lvl, neighborPos, dir.getOpposite()).isPresent();
};
hasCommonConnection = hasCommonConnection || platformHasHandler;
}
if (hasCommonConnection) {
if (existing == ConnectionType.CONNECTOR_PULL) {
return ConnectionType.CONNECTOR_PULL;
@@ -352,14 +362,14 @@ public class PipeBlock extends Block implements EntityBlock, SimpleWaterloggedBl
}
public int getMaxItemsPerTick() {
return Math.max(0, JRConfigManager.get().itemsPerSecond / 20);
return Math.max(1, JRConfigManager.get().itemsPerSecond / 20);
}
public int getMaxFluidPerTick() {
return Math.max(0, JRConfigManager.get().milliBucketsPerSecond / 20);
return Math.max(1, JRConfigManager.get().milliBucketsPerSecond / 20);
}
public int getMaxEnergyPerTick() {
return Math.max(0, JRConfigManager.get().fePerSecond / 20);
return Math.max(1, JRConfigManager.get().fePerSecond / 20);
}
}
@@ -1,11 +1,16 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.block.custom.PipeBlock;
import net.cmr.jurassicrevived.block.custom.PipeBlock.ConnectionType;
import net.cmr.jurassicrevived.block.custom.PipeBlock.Transport;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.platform.Services;
import net.cmr.jurassicrevived.platform.transfer.PlatformEnergyHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformFluidHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformItemHandler;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.Container;
@@ -16,7 +21,8 @@ import net.minecraft.world.level.block.state.BlockState;
import java.util.*;
public class PipeBlockEntity extends BlockEntity {
public class PipeBlockEntity extends BlockEntity
{
private final Transport transport;
@@ -39,9 +45,8 @@ public class PipeBlockEntity extends BlockEntity {
PipeBlock block = (PipeBlock) state.getBlock();
int itemCap = block.getMaxItemsPerTick();
// Fallback caps if Config is not ready
int fluidCap = 1000;
int energyCap = 1000;
int fluidCap = block.getMaxFluidPerTick();
int energyCap = block.getMaxEnergyPerTick();
switch (be.transport) {
case ITEMS -> transferItems(level, pos, state, itemCap);
@@ -52,9 +57,12 @@ public class PipeBlockEntity extends BlockEntity {
// ===== Network discovery =====
private record PipeEndpoint(BlockPos pipePos, Direction side) {}
private record PipeEndpoint(BlockPos pipePos, Direction side)
{
}
private static class Network {
private static class Network
{
final List<PipeEndpoint> sources = new ArrayList<>();
final List<PipeEndpoint> sinks = new ArrayList<>();
}
@@ -99,73 +107,188 @@ public class PipeBlockEntity extends BlockEntity {
private static void transferItems(Level level, BlockPos pos, BlockState state, int perTickLimit) {
Network net = discoverNetwork(level, pos, Transport.ITEMS);
List<Container> outputs = new ArrayList<>();
for (PipeEndpoint ep : net.sinks) {
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (be instanceof Container c) outputs.add(c);
}
if (outputs.isEmpty()) return;
if (net.sources.isEmpty() || net.sinks.isEmpty()) return;
Map<BlockPos, List<PipeEndpoint>> sinksByPipe = indexSinksByPipe(net.sinks);
int remaining = perTickLimit;
for (PipeEndpoint ep : net.sources) {
for (PipeEndpoint srcEp : net.sources) {
if (remaining <= 0) break;
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (!(be instanceof Container src)) continue;
for (int i = 0; i < src.getContainerSize() && remaining > 0; i++) {
ItemStack stack = src.getItem(i);
if (stack.isEmpty()) continue;
BlockPos srcPos = srcEp.pipePos.relative(srcEp.side);
Direction srcSide = srcEp.side.getOpposite();
ItemStack toMove = stack.copy();
toMove.setCount(Math.min(stack.getCount(), remaining));
PlatformItemHandler src = Services.TRANSFER
.getItemHandler(level, srcPos, srcSide)
.orElse(null);
if (src == null) continue;
for (Container out : outputs) {
// Logic to insert into vanilla container (simplified)
// You might want a helper for this
}
}
PipeEndpoint sinkEp = findNearestSink(level, srcEp.pipePos, sinksByPipe, Transport.ITEMS);
if (sinkEp == null) continue;
BlockPos dstPos = sinkEp.pipePos.relative(sinkEp.side);
Direction dstSide = sinkEp.side.getOpposite();
PlatformItemHandler dst = Services.TRANSFER
.getItemHandler(level, dstPos, dstSide)
.orElse(null);
if (dst == null) continue;
remaining = moveFromSourceToSingleTarget(src, dst, remaining);
}
}
// ===== Energy Transfer (Using Custom Energy System) =====
private static PipeEndpoint findNearestSink(
Level level,
BlockPos startPipe,
Map<BlockPos, List<PipeEndpoint>> sinksByPipe,
Transport transport
) {
ArrayDeque<BlockPos> q = new ArrayDeque<>();
HashSet<BlockPos> seen = new HashSet<>();
q.add(startPipe);
seen.add(startPipe);
while (!q.isEmpty()) {
BlockPos p = q.removeFirst();
List<PipeEndpoint> sinksHere = sinksByPipe.get(p);
if (sinksHere != null && !sinksHere.isEmpty()) {
return sinksHere.get(0);
}
BlockState st = level.getBlockState(p);
if (!(st.getBlock() instanceof PipeBlock pb) || pb.getTransport() != transport) continue;
for (Direction d : Direction.values()) {
if (st.getValue(PipeBlock.getProp(d)) == ConnectionType.PIPE) {
BlockPos np = p.relative(d);
if (seen.add(np)) q.add(np);
}
}
}
return null;
}
private static void transferEnergy(Level level, BlockPos pos, BlockState state, int perTickLimit) {
Network net = discoverNetwork(level, pos, Transport.ENERGY);
List<ModEnergyStorage> outputs = new ArrayList<>();
for (PipeEndpoint ep : net.sinks) {
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage storage = provider.getEnergyStorage(ep.side.getOpposite());
if (storage != null && storage.canReceive()) outputs.add(storage);
}
}
if (outputs.isEmpty()) return;
if (net.sources.isEmpty() || net.sinks.isEmpty()) return;
Map<BlockPos, List<PipeEndpoint>> sinksByPipe = indexSinksByPipe(net.sinks);
int remaining = perTickLimit;
for (PipeEndpoint ep : net.sources) {
for (PipeEndpoint srcEp : net.sources) {
if (remaining <= 0) break;
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage src = provider.getEnergyStorage(ep.side.getOpposite());
if (src == null || !src.canExtract()) continue;
int canExtract = src.extractEnergy(remaining, true);
if (canExtract <= 0) continue;
BlockPos srcPos = srcEp.pipePos.relative(srcEp.side);
Direction srcSide = srcEp.side.getOpposite();
for (ModEnergyStorage out : outputs) {
int accepted = out.receiveEnergy(canExtract, true);
if (accepted > 0) {
int actuallyExtracted = src.extractEnergy(accepted, false);
out.receiveEnergy(actuallyExtracted, false);
remaining -= actuallyExtracted;
break;
}
}
}
PlatformEnergyHandler src = Services.TRANSFER
.getEnergyHandler(level, srcPos, srcSide)
.orElse(null);
if (src == null) continue;
PipeEndpoint sinkEp = findNearestSink(level, srcEp.pipePos, sinksByPipe, Transport.ENERGY);
if (sinkEp == null) continue;
BlockPos dstPos = sinkEp.pipePos.relative(sinkEp.side);
Direction dstSide = sinkEp.side.getOpposite();
PlatformEnergyHandler dst = Services.TRANSFER
.getEnergyHandler(level, dstPos, dstSide)
.orElse(null);
if (dst == null) continue;
int available = src.extract(remaining, true);
if (available <= 0) continue;
int accepted = dst.insert(available, true);
if (accepted <= 0) continue;
int extracted = src.extract(accepted, false);
int inserted = dst.insert(extracted, false);
remaining -= inserted;
}
}
private static void transferFluids(Level level, BlockPos pos, BlockState state, int perTickLimit) {
// Implementation would use Architectury FluidStack similarly to energy
Network net = discoverNetwork(level, pos, Transport.FLUIDS);
if (net.sources.isEmpty() || net.sinks.isEmpty()) return;
Map<BlockPos, List<PipeEndpoint>> sinksByPipe = indexSinksByPipe(net.sinks);
long remaining = perTickLimit;
for (PipeEndpoint srcEp : net.sources) {
if (remaining <= 0) break;
BlockPos srcPos = srcEp.pipePos.relative(srcEp.side);
Direction srcSide = srcEp.side.getOpposite();
PlatformFluidHandler src = Services.TRANSFER
.getFluidHandler(level, srcPos, srcSide)
.orElse(null);
if (src == null) continue;
PipeEndpoint sinkEp = findNearestSink(level, srcEp.pipePos, sinksByPipe, Transport.FLUIDS);
if (sinkEp == null) continue;
BlockPos dstPos = sinkEp.pipePos.relative(sinkEp.side);
Direction dstSide = sinkEp.side.getOpposite();
PlatformFluidHandler dst = Services.TRANSFER
.getFluidHandler(level, dstPos, dstSide)
.orElse(null);
if (dst == null) continue;
for (FluidStack candidate : src.getExtractableFluids()) {
if (candidate.isEmpty()) continue;
long available = src.extract(candidate, remaining, true);
if (available <= 0) continue;
long accepted = dst.insert(candidate, available, true);
if (accepted <= 0) continue;
FluidStack toMove = candidate.copy();
toMove.setAmount(accepted);
long extracted = src.extract(toMove, accepted, false);
long inserted = dst.insert(toMove, extracted, false);
remaining -= inserted;
if (remaining <= 0) break;
}
}
}
private static Map<BlockPos, List<PipeEndpoint>> indexSinksByPipe(List<PipeEndpoint> sinks) {
Map<BlockPos, List<PipeEndpoint>> map = new HashMap<>();
for (PipeEndpoint ep : sinks) {
map.computeIfAbsent(ep.pipePos, k -> new ArrayList<>()).add(ep);
}
return map;
}
private static int moveFromSourceToSingleTarget(PlatformItemHandler src, PlatformItemHandler dst, int limit) {
int remaining = limit;
for (ItemStack candidate : src.getExtractableStacks()) {
if (candidate.isEmpty()) continue;
int available = src.extract(candidate, remaining, true);
if (available <= 0) continue;
int accepted = dst.insert(candidate, available, true);
if (accepted <= 0) continue;
int extracted = src.extract(candidate, accepted, false);
if (extracted <= 0) continue;
int inserted = dst.insert(candidate, extracted, false);
remaining -= inserted;
if (remaining <= 0) break;
}
return remaining;
}
}
@@ -3,19 +3,29 @@ package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.fluid.FluidStack;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.platform.transfer.InternalFluidHandler;
import net.cmr.jurassicrevived.platform.transfer.InternalFluidProvider;
import net.cmr.jurassicrevived.screen.custom.TankMenu;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.platform.Services;
import net.cmr.jurassicrevived.platform.services.IItemFluidHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Containers;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
@@ -25,7 +35,7 @@ import org.jetbrains.annotations.Nullable;
/*import net.minecraft.core.HolderLookup;
*///?}
public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider {
public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider, InternalFluidProvider {
public final SimpleContainer itemHandler = new SimpleContainer(2) {
@Override
public void setChanged() {
@@ -46,6 +56,66 @@ public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider
private FluidStack fluidStack = FluidStack.empty();
private static final long CAPACITY = 64000;
public TankFluidHandler getTank(@Nullable Direction side) {
return tank;
}
private final TankFluidHandler tank = new TankFluidHandler();
@Override
public InternalFluidHandler getFluidHandler(@Nullable Direction side) {
return tank;
}
public class TankFluidHandler implements InternalFluidHandler {
@Override
public FluidStack getFluid() {
return fluidStack;
}
@Override
public long getCapacity() {
return CAPACITY;
}
public long fill(FluidStack stack, boolean simulate) {
if (stack.isEmpty()) return 0;
if (!fluidStack.isEmpty() && fluidStack.getFluid() != stack.getFluid()) return 0;
long space = CAPACITY - fluidStack.getAmount();
if (space <= 0) return 0;
long toFill = Math.min(space, stack.getAmount());
if (!simulate) {
fluidStack = FluidStack.create(stack.getFluid(), fluidStack.getAmount() + toFill);
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
return toFill;
}
public FluidStack drain(long amount, boolean simulate) {
if (fluidStack.isEmpty() || amount <= 0) return FluidStack.empty();
long drained = Math.min(amount, fluidStack.getAmount());
FluidStack out = FluidStack.create(fluidStack.getFluid(), drained);
if (!simulate) {
long remaining = fluidStack.getAmount() - drained;
fluidStack = remaining > 0
? FluidStack.create(fluidStack.getFluid(), remaining)
: FluidStack.empty();
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
return out;
}
}
public TankBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.TANK_BE.get(), pos, blockState);
}
@@ -83,6 +153,112 @@ public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider
}
public void tick(Level level, BlockPos blockPos, BlockState blockState) {
if (level.isClientSide) return;
ItemStack input = itemHandler.getItem(0);
if (input.isEmpty()) return;
ItemStack output = itemHandler.getItem(1);
long rate = Math.max(1, JRConfigManager.get().milliBucketsPerSecond / 20);
IItemFluidHelper helper = Services.ITEM_FLUID;
ItemStack inputOne = input.copy();
inputOne.setCount(1);
var containedOpt = helper.getContainedFluid(inputOne);
if (containedOpt.isPresent() && !containedOpt.get().isEmpty()) {
FluidStack contained = containedOpt.get();
long canFill = tank.fill(contained, true);
long toDrain = Math.min(rate, canFill);
if (toDrain <= 0) return;
IItemFluidHelper.TransferResult drained = helper.drain(inputOne, toDrain, false);
// If partial drain failed (amount 0), and we requested less than full capacity, try requesting full capacity
if (drained.amount() == 0 && toDrain < canFill) {
drained = helper.drain(inputOne, canFill, false);
}
if (drained.amount() > 0) {
ItemStack resultStack = drained.stack();
boolean emptyNow = helper.getContainedFluid(resultStack)
.map(FluidStack::isEmpty)
.orElse(true);
if (emptyNow) {
if (canMoveToOutput(resultStack, output)) {
tank.fill(FluidStack.create(contained.getFluid(), drained.amount()), false);
input.shrink(1);
if (input.isEmpty()) {
itemHandler.setItem(0, ItemStack.EMPTY);
} else {
itemHandler.setChanged();
}
addToOutput(resultStack, output);
}
} else {
if (input.getCount() == 1) {
tank.fill(FluidStack.create(contained.getFluid(), drained.amount()), false);
itemHandler.setItem(0, resultStack);
}
}
}
} else {
FluidStack tankFluid = tank.getFluid();
if (tankFluid.isEmpty()) return;
long toFill = Math.min(rate, tankFluid.getAmount());
IItemFluidHelper.TransferResult filled = helper.fill(inputOne, tankFluid, toFill, false);
// If partial fill failed, and we requested less than available fluid, try requesting more
if (filled.amount() == 0 && toFill < tankFluid.getAmount()) {
filled = helper.fill(inputOne, tankFluid, tankFluid.getAmount(), false);
}
if (filled.amount() > 0) {
ItemStack resultStack = filled.stack();
boolean fullNow = helper.fill(resultStack, tankFluid, 1, true).amount() == 0;
boolean tankWillBeEmpty = tankFluid.getAmount() - filled.amount() <= 0;
if (fullNow || tankWillBeEmpty) {
if (canMoveToOutput(resultStack, output)) {
tank.drain(filled.amount(), false);
input.shrink(1);
if (input.isEmpty()) {
itemHandler.setItem(0, ItemStack.EMPTY);
} else {
itemHandler.setChanged();
}
addToOutput(resultStack, output);
}
} else {
if (input.getCount() == 1) {
tank.drain(filled.amount(), false);
itemHandler.setItem(0, resultStack);
}
}
}
}
}
private boolean canMoveToOutput(ItemStack result, ItemStack output) {
if (output.isEmpty()) return true;
//? if >1.20.1 {
/*return ItemStack.isSameItemSameComponents(result, output) && output.getCount() + result.getCount() <= output.getMaxStackSize();
*///?} else {
return ItemStack.isSameItemSameTags(result, output) && output.getCount() + result.getCount() <= output.getMaxStackSize();
//?}
}
private void addToOutput(ItemStack result, ItemStack output) {
if (output.isEmpty()) {
itemHandler.setItem(1, result);
} else {
output.grow(result.getCount());
itemHandler.setChanged();
}
}
public boolean isEmptyForDrop() {
@@ -94,17 +270,42 @@ public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider
protected void saveAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.saveAdditional(pTag, pRegistries);
pTag.put("Inventory", itemHandler.createTag(pRegistries));
if (!fluidStack.isEmpty()) {
// Manually save fluid stack to avoid issues with Architectury's default serialization
CompoundTag fluidTag = new CompoundTag();
fluidStack.write(pRegistries, fluidTag);
fluidTag.putString("id", BuiltInRegistries.FLUID.getKey(fluidStack.getFluid()).toString());
fluidTag.putLong("amount", fluidStack.getAmount());
if (fluidStack.getPatch() != null && !fluidStack.getPatch().isEmpty()) {
// Fallback to standard save if components are present
pTag.put("Fluid", FluidStack.CODEC.encodeStart(NbtOps.INSTANCE, fluidStack).getOrThrow());
} else {
pTag.put("Fluid", fluidTag);
}
}
}
@Override
protected void loadAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.loadAdditional(pTag, pRegistries);
itemHandler.fromTag(pTag.getList("Inventory", 10), pRegistries);
if (pTag.contains("Fluid")) {
this.fluidStack = FluidStack.read(pRegistries, pTag.getCompound("Fluid")).orElse(FluidStack.empty());
CompoundTag fluidTag = pTag.getCompound("Fluid");
if (fluidTag.contains("id") && fluidTag.contains("amount")) {
// Manual load
try {
// Use ResourceLocation.parse for 1.21.1
net.minecraft.world.level.material.Fluid fluid = BuiltInRegistries.FLUID.get(ResourceLocation.parse(fluidTag.getString("id")));
long amount = fluidTag.getLong("amount");
this.fluidStack = FluidStack.create(fluid, amount);
} catch (Exception e) {
this.fluidStack = FluidStack.empty();
}
} else {
// Standard load
this.fluidStack = FluidStack.CODEC.parse(NbtOps.INSTANCE, fluidTag).result().orElse(FluidStack.empty());
}
} else {
this.fluidStack = FluidStack.empty();
}
}
*///?} else {
@@ -112,10 +313,12 @@ public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider
protected void saveAdditional(CompoundTag pTag) {
super.saveAdditional(pTag);
pTag.put("Inventory", itemHandler.createTag());
if (!fluidStack.isEmpty()) {
CompoundTag fluidTag = new CompoundTag();
fluidStack.write(fluidTag);
pTag.put("Fluid", fluidTag);
}
}
@Override
public void load(CompoundTag pTag) {
@@ -123,6 +326,8 @@ public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider
itemHandler.fromTag(pTag.getList("Inventory", 10));
if (pTag.contains("Fluid")) {
this.fluidStack = FluidStack.read(pTag.getCompound("Fluid"));
} else {
this.fluidStack = FluidStack.empty();
}
}
//?}
@@ -6,7 +6,7 @@ public final class JRConfig {
public int spawnWeight = 10;
public boolean requirePower = true;
public int fePerSecond = 1000;
public int itemsPerSecond = 10;
public int itemsPerSecond = 100;
public int milliBucketsPerSecond = 1000;
public JRConfig() {
@@ -0,0 +1,121 @@
package net.cmr.jurassicrevived.networking;
import dev.architectury.fluid.FluidStack;
import dev.architectury.networking.NetworkManager;
import io.netty.buffer.Unpooled;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.screen.custom.TankMenu;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
//? if >1.20.1 {
/*import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
*///?}
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
public class ModPackets {
public static final ResourceLocation TANK_SYNC = Constants.rl("tank_sync");
public static void register() {
//? if >1.20.1 {
/*NetworkManager.registerReceiver(NetworkManager.Side.S2C, TANK_SYNC_TYPE, TANK_SYNC_STREAM_CODEC, (payload, context) -> {
FluidStack fluidStack = payload.fluidStack();
context.queue(() -> {
Player player = context.getPlayer();
if (player != null) {
AbstractContainerMenu menu = player.containerMenu;
if (menu instanceof TankMenu tankMenu) {
tankMenu.setFluid(fluidStack);
}
}
});
});
*///?} else {
NetworkManager.registerReceiver(NetworkManager.Side.S2C, TANK_SYNC, (buf, context) -> {
FluidStack fluidStack = FluidStack.read(buf);
context.queue(() -> {
Player player = context.getPlayer();
if (player != null) {
AbstractContainerMenu menu = player.containerMenu;
if (menu instanceof TankMenu tankMenu) {
tankMenu.setFluid(fluidStack);
}
}
});
});
//?}
}
public static void sendTankSync(ServerPlayer player, FluidStack fluidStack) {
//? if >1.20.1 {
/*NetworkManager.sendToPlayer(player, new TankSyncPayload(fluidStack));
*///?} else {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
fluidStack.write(buf);
NetworkManager.sendToPlayer(player, TANK_SYNC, buf);
//?}
}
//? if >1.20.1 {
/*public static final CustomPacketPayload.Type<TankSyncPayload> TANK_SYNC_TYPE = new CustomPacketPayload.Type<>(TANK_SYNC);
public static final StreamCodec<RegistryFriendlyByteBuf, TankSyncPayload> TANK_SYNC_STREAM_CODEC = StreamCodec.of((buf, payload) -> payload.write(buf), TankSyncPayload::new);
public record TankSyncPayload(FluidStack fluidStack) implements CustomPacketPayload {
public TankSyncPayload(RegistryFriendlyByteBuf buf) {
this(readFluid(buf));
}
public void write(RegistryFriendlyByteBuf buf) {
if (fluidStack.isEmpty()) {
buf.writeBoolean(false);
} else {
buf.writeBoolean(true);
buf.writeResourceLocation(BuiltInRegistries.FLUID.getKey(fluidStack.getFluid()));
buf.writeLong(fluidStack.getAmount());
//? if >1.20.1 {
/^DataComponentPatch.STREAM_CODEC.encode(buf, fluidStack.getPatch());
^///?} else {
buf.writeNbt(fluidStack.getTag());
//?}
}
}
private static FluidStack readFluid(RegistryFriendlyByteBuf buf) {
if (!buf.readBoolean()) {
return FluidStack.empty();
}
ResourceLocation fluidId = buf.readResourceLocation();
long amount = buf.readLong();
//? if >1.20.1 {
/^DataComponentPatch patch = DataComponentPatch.STREAM_CODEC.decode(buf);
^///?} else {
CompoundTag tag = buf.readNbt();
//?}
Fluid fluid = BuiltInRegistries.FLUID.get(fluidId);
if (fluid == Fluids.EMPTY) {
return FluidStack.empty();
}
//? if >1.20.1 {
/^return FluidStack.create(fluid, amount, patch);
^///?} else {
FluidStack stack = FluidStack.create(fluid, amount);
stack.setTag(tag);
return stack;
//?}
}
@Override
public Type<? extends CustomPacketPayload> type() {
return TANK_SYNC_TYPE;
}
}
*///?}
}
@@ -3,6 +3,8 @@ package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.platform.services.IPlatformHelper;
import net.cmr.jurassicrevived.platform.services.ITransferHelper;
import net.cmr.jurassicrevived.platform.services.IItemFluidHelper;
import java.util.ServiceLoader;
@@ -16,6 +18,8 @@ public class Services
// For example this can be used to check if the code is running on Forge vs Fabric, or to ask the modloader if another
// mod is loaded.
public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class);
public static final ITransferHelper TRANSFER = load(ITransferHelper.class);
public static final IItemFluidHelper ITEM_FLUID = load(IItemFluidHelper.class);
// This code is used to load a service for the current environment. Your implementation of the service must be defined
// manually by including a text file in META-INF/services named with the fully qualified class name of the service.
@@ -0,0 +1,15 @@
package net.cmr.jurassicrevived.platform.services;
import dev.architectury.fluid.FluidStack;
import net.minecraft.world.item.ItemStack;
import java.util.Optional;
public interface IItemFluidHelper {
record TransferResult(long amount, ItemStack stack) {}
Optional<FluidStack> getContainedFluid(ItemStack stack);
TransferResult drain(ItemStack stack, long amount, boolean simulate);
TransferResult fill(ItemStack stack, FluidStack fluid, long amount, boolean simulate);
boolean isFluidHandler(ItemStack stack);
}
@@ -0,0 +1,16 @@
package net.cmr.jurassicrevived.platform.services;
import net.cmr.jurassicrevived.platform.transfer.PlatformItemHandler;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.cmr.jurassicrevived.platform.transfer.PlatformEnergyHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformFluidHandler;
import java.util.Optional;
public interface ITransferHelper {
Optional<PlatformItemHandler> getItemHandler(Level level, BlockPos pos, Direction side);
Optional<PlatformFluidHandler> getFluidHandler(Level level, BlockPos pos, Direction side);
Optional<PlatformEnergyHandler> getEnergyHandler(Level level, BlockPos pos, Direction side);
}
@@ -0,0 +1,10 @@
package net.cmr.jurassicrevived.platform.transfer;
import dev.architectury.fluid.FluidStack;
public interface InternalFluidHandler {
FluidStack getFluid();
long getCapacity();
long fill(FluidStack stack, boolean simulate);
FluidStack drain(long amount, boolean simulate);
}
@@ -0,0 +1,33 @@
package net.cmr.jurassicrevived.platform.transfer;
import dev.architectury.fluid.FluidStack;
import java.util.List;
public class InternalFluidHandlerAdapter implements PlatformFluidHandler {
private final InternalFluidHandler handler;
public InternalFluidHandlerAdapter(InternalFluidHandler handler) {
this.handler = handler;
}
@Override
public Iterable<FluidStack> getExtractableFluids() {
FluidStack fluid = handler.getFluid();
return fluid.isEmpty() ? List.of() : List.of(fluid);
}
@Override
public long extract(FluidStack stack, long amount, boolean simulate) {
if (stack.isEmpty()) return 0;
return handler.drain(amount, simulate).getAmount();
}
@Override
public long insert(FluidStack stack, long amount, boolean simulate) {
if (stack.isEmpty()) return 0;
FluidStack toFill = stack.copy();
toFill.setAmount(amount);
return handler.fill(toFill, simulate);
}
}
@@ -0,0 +1,8 @@
package net.cmr.jurassicrevived.platform.transfer;
import net.minecraft.core.Direction;
import org.jetbrains.annotations.Nullable;
public interface InternalFluidProvider {
InternalFluidHandler getFluidHandler(@Nullable Direction side);
}
@@ -0,0 +1,6 @@
package net.cmr.jurassicrevived.platform.transfer;
public interface PlatformEnergyHandler {
int extract(int amount, boolean simulate);
int insert(int amount, boolean simulate);
}
@@ -0,0 +1,9 @@
package net.cmr.jurassicrevived.platform.transfer;
import dev.architectury.fluid.FluidStack;
public interface PlatformFluidHandler {
Iterable<FluidStack> getExtractableFluids();
long extract(FluidStack stack, long amount, boolean simulate);
long insert(FluidStack stack, long amount, boolean simulate);
}
@@ -0,0 +1,9 @@
package net.cmr.jurassicrevived.platform.transfer;
import net.minecraft.world.item.ItemStack;
public interface PlatformItemHandler {
Iterable<ItemStack> getExtractableStacks();
int extract(ItemStack stack, int amount, boolean simulate);
int insert(ItemStack stack, int amount, boolean simulate);
}
@@ -1,13 +1,18 @@
package net.cmr.jurassicrevived.screen.custom;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.block.entity.custom.TankBlockEntity;
import net.cmr.jurassicrevived.networking.ModPackets;
import net.cmr.jurassicrevived.platform.Services;
import net.cmr.jurassicrevived.screen.ModMenuTypes;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.DataSlot;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
@@ -16,6 +21,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class TankMenu extends AbstractContainerMenu {
public final TankBlockEntity blockEntity;
private final Level level;
private FluidStack fluidStack = FluidStack.empty();
public TankMenu(int pContainerId, Inventory inv, FriendlyByteBuf extraData) {
this(pContainerId, inv, inv.player.level().getBlockEntity(extraData.readBlockPos()));
@@ -26,19 +32,80 @@ public class TankMenu extends AbstractContainerMenu {
this.blockEntity = ((TankBlockEntity) blockEntity);
this.level = inv.player.level();
// Initialize fluidStack with the current state of the block entity
// This ensures the client sees the correct fluid immediately if the BE is already synced
if (this.blockEntity != null) {
this.fluidStack = this.blockEntity.getFluid().copy();
}
addPlayerInventory(inv);
addPlayerHotbar(inv);
// Input Slot (0)
this.addSlot(new Slot(this.blockEntity.itemHandler, 0, 44, 34));
// Output Slot (1)
this.addSlot(new Slot(this.blockEntity.itemHandler, 1, 116, 34) {
this.addSlot(new Slot(this.blockEntity.itemHandler, 0, 44, 34) {
@Override
public int getMaxStackSize() {
return 1;
public boolean mayPlace(ItemStack stack) {
return Services.ITEM_FLUID.isFluidHandler(stack);
}
});
// Output Slot (1)
this.addSlot(new Slot(this.blockEntity.itemHandler, 1, 116, 34){
@Override
public boolean mayPlace(ItemStack stack) {
return false;
}
});
addDataSlot(new DataSlot() {
@Override
public int get() {
return 0;
}
@Override
public void set(int pValue) {
}
});
}
public void setFluid(FluidStack stack) {
this.fluidStack = stack;
}
public FluidStack getFluid() {
return this.fluidStack;
}
public void syncFluidToPlayers() {
if (this.level != null && !this.level.isClientSide()) {
for (Player player : this.level.players()) {
if (player.containerMenu == this && player instanceof ServerPlayer serverPlayer) {
ModPackets.sendTankSync(serverPlayer, this.fluidStack);
}
}
}
}
@Override
public void sendAllDataToRemote() {
super.sendAllDataToRemote();
// Ensure we have the latest fluid state from the block entity before syncing
if (this.blockEntity != null) {
this.fluidStack = this.blockEntity.getFluid().copy();
}
syncFluidToPlayers();
}
@Override
public void broadcastChanges() {
super.broadcastChanges();
if (this.blockEntity != null) {
FluidStack currentFluid = this.blockEntity.getFluid();
if (!currentFluid.equals(this.fluidStack)) {
this.fluidStack = currentFluid.copy();
syncFluidToPlayers();
}
}
}
private static final int HOTBAR_SLOT_COUNT = 9;
@@ -51,7 +51,7 @@ public class TankScreen extends AbstractContainerScreen<TankMenu> {
int x = (width - imageWidth) / 2;
int y = (height - imageHeight) / 2;
renderFluidTooltipArea(guiGraphics, pMouseX, pMouseY, x, y, menu.blockEntity.getFluid(), 80, 8, fluidRenderer);
renderFluidTooltipArea(guiGraphics, pMouseX, pMouseY, x, y, menu.getFluid(), 80, 8, fluidRenderer);
}
@Override
@@ -64,7 +64,7 @@ public class TankScreen extends AbstractContainerScreen<TankMenu> {
guiGraphics.blit(GUI_TEXTURE, x, y, 0, 0, imageWidth, imageHeight);
fluidRenderer.render(guiGraphics, x + 80, y + 8, menu.blockEntity.getFluid());
fluidRenderer.render(guiGraphics, x + 80, y + 8, menu.getFluid());
}
@Override
@@ -0,0 +1,208 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"ambientocclusion": false,
"render_type": "cutout",
"texture_size": [64, 64],
"textures": {
"0": "jurassicrevived:block/generator",
"particle": "jurassicrevived:block/generator"
},
"elements": [
{
"from": [2, 0, 2],
"to": [14, 8, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 1]},
"faces": {
"north": {"uv": [3, 0, 6, 2], "texture": "#0"},
"east": {"uv": [3, 2, 6, 4], "texture": "#0"},
"south": {"uv": [3, 4, 6, 6], "texture": "#0"},
"west": {"uv": [0, 6, 3, 8], "texture": "#0"},
"up": {"uv": [3, 3, 0, 0], "texture": "#0"},
"down": {"uv": [3, 3, 0, 6], "texture": "#0"}
}
},
{
"from": [-1.425, 9.7, 3.5],
"to": [6.575, 17.7, 12.5],
"rotation": {"angle": -45, "axis": "z", "origin": [6.575, 7.7, -0.5]},
"faces": {
"north": {"uv": [5, 6.25, 7, 8.25], "texture": "#0"},
"east": {"uv": [6, 0, 8.25, 2], "texture": "#0"},
"south": {"uv": [7, 6.25, 9, 8.25], "texture": "#0"},
"west": {"uv": [6, 2, 8.25, 4], "texture": "#0"},
"up": {"uv": [5, 8.25, 3, 6], "texture": "#0"},
"down": {"uv": [8, 4, 6, 6.25], "texture": "#0"}
}
},
{
"from": [1, 0, 1],
"to": [3, 9, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, 0]},
"faces": {
"north": {"uv": [4, 8.75, 4.5, 11], "texture": "#0"},
"east": {"uv": [4.5, 8.75, 5, 11], "texture": "#0"},
"south": {"uv": [5, 8.75, 5.5, 11], "texture": "#0"},
"west": {"uv": [5.5, 8.75, 6, 11], "texture": "#0"},
"up": {"uv": [9.25, 2.5, 8.75, 2], "texture": "#0"},
"down": {"uv": [9.75, 2, 9.25, 2.5], "texture": "#0"}
}
},
{
"from": [1, 0, 13],
"to": [3, 9, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, 12]},
"faces": {
"north": {"uv": [6, 8.75, 6.5, 11], "texture": "#0"},
"east": {"uv": [6.5, 8.75, 7, 11], "texture": "#0"},
"south": {"uv": [7, 8.75, 7.5, 11], "texture": "#0"},
"west": {"uv": [7.5, 8.75, 8, 11], "texture": "#0"},
"up": {"uv": [10.25, 2.5, 9.75, 2], "texture": "#0"},
"down": {"uv": [10.5, 6, 10, 6.5], "texture": "#0"}
}
},
{
"from": [13, 0, 13],
"to": [15, 9, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 0, 12]},
"faces": {
"north": {"uv": [8, 8.75, 8.5, 11], "texture": "#0"},
"east": {"uv": [8.5, 8.75, 9, 11], "texture": "#0"},
"south": {"uv": [0, 9, 0.5, 11.25], "texture": "#0"},
"west": {"uv": [0.5, 9, 1, 11.25], "texture": "#0"},
"up": {"uv": [10.5, 7, 10, 6.5], "texture": "#0"},
"down": {"uv": [10.5, 7, 10, 7.5], "texture": "#0"}
}
},
{
"from": [13, 7, 3],
"to": [15, 9, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 8, 12]},
"faces": {
"north": {"uv": [10, 7.5, 10.5, 8], "texture": "#0"},
"east": {"uv": [0, 8, 2.5, 8.5], "texture": "#0"},
"south": {"uv": [10, 8, 10.5, 8.5], "texture": "#0"},
"west": {"uv": [8, 4, 10.5, 4.5], "texture": "#0"},
"up": {"uv": [3, 10.5, 2.5, 8], "texture": "#0"},
"down": {"uv": [8.75, 0, 8.25, 2.5], "texture": "#0"}
}
},
{
"from": [1, 7, 3],
"to": [3, 9, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 8, 12]},
"faces": {
"north": {"uv": [10, 8.5, 10.5, 9], "texture": "#0"},
"east": {"uv": [8, 4.5, 10.5, 5], "texture": "#0"},
"south": {"uv": [10, 9, 10.5, 9.5], "texture": "#0"},
"west": {"uv": [8, 5, 10.5, 5.5], "texture": "#0"},
"up": {"uv": [3.5, 10.75, 3, 8.25], "texture": "#0"},
"down": {"uv": [4, 8.25, 3.5, 10.75], "texture": "#0"}
}
},
{
"from": [4, 8, 9],
"to": [6, 14, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 9, 14]},
"faces": {
"north": {"uv": [8.75, 0.5, 9.25, 2], "texture": "#0"},
"east": {"uv": [9, 8.25, 9.5, 9.75], "texture": "#0"},
"south": {"uv": [9.25, 0.5, 9.75, 2], "texture": "#0"},
"west": {"uv": [9.5, 6, 10, 7.5], "texture": "#0"},
"up": {"uv": [10.5, 10, 10, 9.5], "texture": "#0"},
"down": {"uv": [10.5, 10, 10, 10.5], "texture": "#0"}
}
},
{
"from": [10, 8, 5],
"to": [12, 14, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 9, 10]},
"faces": {
"north": {"uv": [9.5, 7.5, 10, 9], "texture": "#0"},
"east": {"uv": [9.5, 9, 10, 10.5], "texture": "#0"},
"south": {"uv": [9.75, 0.5, 10.25, 2], "texture": "#0"},
"west": {"uv": [9, 9.75, 9.5, 11.25], "texture": "#0"},
"up": {"uv": [10.75, 1, 10.25, 0.5], "texture": "#0"},
"down": {"uv": [10.75, 1, 10.25, 1.5], "texture": "#0"}
}
},
{
"from": [13, 0, 1],
"to": [15, 9, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 0, 0]},
"faces": {
"north": {"uv": [1, 9, 1.5, 11.25], "texture": "#0"},
"east": {"uv": [1.5, 9, 2, 11.25], "texture": "#0"},
"south": {"uv": [2, 9, 2.5, 11.25], "texture": "#0"},
"west": {"uv": [9, 6, 9.5, 8.25], "texture": "#0"},
"up": {"uv": [10.75, 2, 10.25, 1.5], "texture": "#0"},
"down": {"uv": [10.75, 2, 10.25, 2.5], "texture": "#0"}
}
},
{
"from": [3, 7, 1],
"to": [13, 9, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 0, 0]},
"faces": {
"north": {"uv": [8, 5.5, 10.5, 6], "texture": "#0"},
"east": {"uv": [2.5, 10.5, 3, 11], "texture": "#0"},
"south": {"uv": [8.25, 2.5, 10.75, 3], "texture": "#0"},
"west": {"uv": [10.5, 4, 11, 4.5], "texture": "#0"},
"up": {"uv": [10.75, 3.5, 8.25, 3], "texture": "#0"},
"down": {"uv": [10.75, 3.5, 8.25, 4], "texture": "#0"}
}
},
{
"from": [3, 7, 13],
"to": [13, 9, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 0, 12]},
"faces": {
"north": {"uv": [4, 8.25, 6.5, 8.75], "texture": "#0"},
"east": {"uv": [10.5, 4.5, 11, 5], "texture": "#0"},
"south": {"uv": [6.5, 8.25, 9, 8.75], "texture": "#0"},
"west": {"uv": [10.5, 5, 11, 5.5], "texture": "#0"},
"up": {"uv": [2.5, 9, 0, 8.5], "texture": "#0"},
"down": {"uv": [11.25, 0, 8.75, 0.5], "texture": "#0"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, 45, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"thirdperson_lefthand": {
"rotation": [75, 45, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, 45, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, -135, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, -135, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"scale": [0.5, 0.5, 0.5]
}
},
"groups": [
{
"name": "Root",
"origin": [8, 8, 8],
"color": 0,
"children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
}
]
}
@@ -0,0 +1,208 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"ambientocclusion": false,
"render_type": "cutout",
"texture_size": [64, 64],
"textures": {
"0": "jurassicrevived:block/white_generator",
"particle": "jurassicrevived:block/white_generator"
},
"elements": [
{
"from": [2, 0, 2],
"to": [14, 8, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 1]},
"faces": {
"north": {"uv": [3, 0, 6, 2], "texture": "#0"},
"east": {"uv": [3, 2, 6, 4], "texture": "#0"},
"south": {"uv": [3, 4, 6, 6], "texture": "#0"},
"west": {"uv": [0, 6, 3, 8], "texture": "#0"},
"up": {"uv": [3, 3, 0, 0], "texture": "#0"},
"down": {"uv": [3, 3, 0, 6], "texture": "#0"}
}
},
{
"from": [-1.425, 9.7, 3.5],
"to": [6.575, 17.7, 12.5],
"rotation": {"angle": -45, "axis": "z", "origin": [6.575, 7.7, -0.5]},
"faces": {
"north": {"uv": [5, 6.25, 7, 8.25], "texture": "#0"},
"east": {"uv": [6, 0, 8.25, 2], "texture": "#0"},
"south": {"uv": [7, 6.25, 9, 8.25], "texture": "#0"},
"west": {"uv": [6, 2, 8.25, 4], "texture": "#0"},
"up": {"uv": [5, 8.25, 3, 6], "texture": "#0"},
"down": {"uv": [8, 4, 6, 6.25], "texture": "#0"}
}
},
{
"from": [1, 0, 1],
"to": [3, 9, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, 0]},
"faces": {
"north": {"uv": [4, 8.75, 4.5, 11], "texture": "#0"},
"east": {"uv": [4.5, 8.75, 5, 11], "texture": "#0"},
"south": {"uv": [5, 8.75, 5.5, 11], "texture": "#0"},
"west": {"uv": [5.5, 8.75, 6, 11], "texture": "#0"},
"up": {"uv": [9.25, 2.5, 8.75, 2], "texture": "#0"},
"down": {"uv": [9.75, 2, 9.25, 2.5], "texture": "#0"}
}
},
{
"from": [1, 0, 13],
"to": [3, 9, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 0, 12]},
"faces": {
"north": {"uv": [6, 8.75, 6.5, 11], "texture": "#0"},
"east": {"uv": [6.5, 8.75, 7, 11], "texture": "#0"},
"south": {"uv": [7, 8.75, 7.5, 11], "texture": "#0"},
"west": {"uv": [7.5, 8.75, 8, 11], "texture": "#0"},
"up": {"uv": [10.25, 2.5, 9.75, 2], "texture": "#0"},
"down": {"uv": [10.5, 6, 10, 6.5], "texture": "#0"}
}
},
{
"from": [13, 0, 13],
"to": [15, 9, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 0, 12]},
"faces": {
"north": {"uv": [8, 8.75, 8.5, 11], "texture": "#0"},
"east": {"uv": [8.5, 8.75, 9, 11], "texture": "#0"},
"south": {"uv": [0, 9, 0.5, 11.25], "texture": "#0"},
"west": {"uv": [0.5, 9, 1, 11.25], "texture": "#0"},
"up": {"uv": [10.5, 7, 10, 6.5], "texture": "#0"},
"down": {"uv": [10.5, 7, 10, 7.5], "texture": "#0"}
}
},
{
"from": [13, 7, 3],
"to": [15, 9, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 8, 12]},
"faces": {
"north": {"uv": [10, 7.5, 10.5, 8], "texture": "#0"},
"east": {"uv": [0, 8, 2.5, 8.5], "texture": "#0"},
"south": {"uv": [10, 8, 10.5, 8.5], "texture": "#0"},
"west": {"uv": [8, 4, 10.5, 4.5], "texture": "#0"},
"up": {"uv": [3, 10.5, 2.5, 8], "texture": "#0"},
"down": {"uv": [8.75, 0, 8.25, 2.5], "texture": "#0"}
}
},
{
"from": [1, 7, 3],
"to": [3, 9, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 8, 12]},
"faces": {
"north": {"uv": [10, 8.5, 10.5, 9], "texture": "#0"},
"east": {"uv": [8, 4.5, 10.5, 5], "texture": "#0"},
"south": {"uv": [10, 9, 10.5, 9.5], "texture": "#0"},
"west": {"uv": [8, 5, 10.5, 5.5], "texture": "#0"},
"up": {"uv": [3.5, 10.75, 3, 8.25], "texture": "#0"},
"down": {"uv": [4, 8.25, 3.5, 10.75], "texture": "#0"}
}
},
{
"from": [4, 8, 9],
"to": [6, 14, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 9, 14]},
"faces": {
"north": {"uv": [8.75, 0.5, 9.25, 2], "texture": "#0"},
"east": {"uv": [9, 8.25, 9.5, 9.75], "texture": "#0"},
"south": {"uv": [9.25, 0.5, 9.75, 2], "texture": "#0"},
"west": {"uv": [9.5, 6, 10, 7.5], "texture": "#0"},
"up": {"uv": [10.5, 10, 10, 9.5], "texture": "#0"},
"down": {"uv": [10.5, 10, 10, 10.5], "texture": "#0"}
}
},
{
"from": [10, 8, 5],
"to": [12, 14, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 9, 10]},
"faces": {
"north": {"uv": [9.5, 7.5, 10, 9], "texture": "#0"},
"east": {"uv": [9.5, 9, 10, 10.5], "texture": "#0"},
"south": {"uv": [9.75, 0.5, 10.25, 2], "texture": "#0"},
"west": {"uv": [9, 9.75, 9.5, 11.25], "texture": "#0"},
"up": {"uv": [10.75, 1, 10.25, 0.5], "texture": "#0"},
"down": {"uv": [10.75, 1, 10.25, 1.5], "texture": "#0"}
}
},
{
"from": [13, 0, 1],
"to": [15, 9, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 0, 0]},
"faces": {
"north": {"uv": [1, 9, 1.5, 11.25], "texture": "#0"},
"east": {"uv": [1.5, 9, 2, 11.25], "texture": "#0"},
"south": {"uv": [2, 9, 2.5, 11.25], "texture": "#0"},
"west": {"uv": [9, 6, 9.5, 8.25], "texture": "#0"},
"up": {"uv": [10.75, 2, 10.25, 1.5], "texture": "#0"},
"down": {"uv": [10.75, 2, 10.25, 2.5], "texture": "#0"}
}
},
{
"from": [3, 7, 1],
"to": [13, 9, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 0, 0]},
"faces": {
"north": {"uv": [8, 5.5, 10.5, 6], "texture": "#0"},
"east": {"uv": [2.5, 10.5, 3, 11], "texture": "#0"},
"south": {"uv": [8.25, 2.5, 10.75, 3], "texture": "#0"},
"west": {"uv": [10.5, 4, 11, 4.5], "texture": "#0"},
"up": {"uv": [10.75, 3.5, 8.25, 3], "texture": "#0"},
"down": {"uv": [10.75, 3.5, 8.25, 4], "texture": "#0"}
}
},
{
"from": [3, 7, 13],
"to": [13, 9, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 0, 12]},
"faces": {
"north": {"uv": [4, 8.25, 6.5, 8.75], "texture": "#0"},
"east": {"uv": [10.5, 4.5, 11, 5], "texture": "#0"},
"south": {"uv": [6.5, 8.25, 9, 8.75], "texture": "#0"},
"west": {"uv": [10.5, 5, 11, 5.5], "texture": "#0"},
"up": {"uv": [2.5, 9, 0, 8.5], "texture": "#0"},
"down": {"uv": [11.25, 0, 8.75, 0.5], "texture": "#0"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, 45, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"thirdperson_lefthand": {
"rotation": [75, 45, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, 45, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, -135, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, -135, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"scale": [0.5, 0.5, 0.5]
}
},
"groups": [
{
"name": "Root",
"origin": [8, 8, 8],
"color": 0,
"children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
}
]
}
+8
View File
@@ -61,6 +61,14 @@ dependencies {
}
})
val energyVersion = if (is120) {
commonMod.prop("teamreborn_energy_1_20_1")
} else {
commonMod.prop("teamreborn_energy_1_21_1")
}
modImplementation("teamreborn:energy:$energyVersion")
modImplementation("net.fabricmc:fabric-loader:${commonMod.prop("fabric_loader_version")}")
modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.prop("fabric_api_version")}")
@@ -7,6 +7,8 @@ import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage;
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.cmr.jurassicrevived.platform.FabricEnergyWrapper;
import team.reborn.energy.api.EnergyStorage;
public class JRMod implements ModInitializer
{
@@ -22,15 +24,54 @@ public class JRMod implements ModInitializer
Constants.LOG.info("Hello Fabric world!");
CommonClass.init();
/*
Items (Fabric Transfer API)
ItemStorage.SIDED.registerForBlockEntities((be, side) -> (Storage<ItemVariant>) ((CrateBlockEntity)be).itemHandler, ModBlockEntities.CRATE_BE.get());
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((PowerCellBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.POWER_CELL_BE.get()
);
Energy (TeamReborn Energy API is standard for Fabric)
EnergyStorage.SIDED.registerForBlockEntities((be, side) -> ((PowerCellBlockEntity)be).getEnergyStorage(side), ModBlockEntities.POWER_CELL_BE.get());
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((GeneratorBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.GENERATOR_BE.get()
);
Fluids (Fabric Transfer API)
FluidStorage.SIDED.registerForBlockEntities((be, side) -> ((TankBlockEntity)be).getTank(side), ModBlockEntities.TANK_BE.get());
*/
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((DNAExtractorBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.DNA_EXTRACTOR_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((DNAAnalyzerBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.DNA_ANALYZER_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((DNAHybridizerBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.DNA_HYBRIDIZER_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((FossilCleanerBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.FOSSIL_CLEANER_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((FossilGrinderBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.FOSSIL_GRINDER_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((EmbryonicMachineBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.EMBRYONIC_MACHINE_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((EmbryoCalcificationMachineBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.EMBRYO_CALCIFICATION_MACHINE_BE.get()
);
EnergyStorage.SIDED.registerForBlockEntities((be, side) ->
new FabricEnergyWrapper(((IncubatorBlockEntity) be).getEnergyStorage(side)),
ModBlockEntities.INCUBATOR_BE.get()
);
}
}
@@ -0,0 +1,33 @@
package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
import team.reborn.energy.api.EnergyStorage;
public class FabricEnergyWrapper implements EnergyStorage {
private final ModEnergyStorage storage;
public FabricEnergyWrapper(ModEnergyStorage storage) {
this.storage = storage;
}
@Override
public long insert(long maxAmount, TransactionContext transaction) {
return storage.receiveEnergy((int) maxAmount, true);
}
@Override
public long extract(long maxAmount, TransactionContext transaction) {
return storage.extractEnergy((int) maxAmount, true);
}
@Override
public long getAmount() {
return storage.getEnergyStored();
}
@Override
public long getCapacity() {
return storage.getMaxEnergyStored();
}
}
@@ -0,0 +1,103 @@
package net.cmr.jurassicrevived.platform;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.platform.services.IItemFluidHelper;
import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
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.item.ItemVariant;
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.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.material.Fluids;
import java.util.Optional;
public class FabricItemFluidHelper implements IItemFluidHelper {
@Override
public Optional<FluidStack> getContainedFluid(ItemStack stack) {
ContainerItemContext ctx = ContainerItemContext.withConstant(stack.copy());
Storage<FluidVariant> storage = ctx.find(FluidStorage.ITEM);
if (storage == null) {
if (stack.is(Items.WATER_BUCKET)) return Optional.of(FluidStack.create(Fluids.WATER, 1000));
if (stack.is(Items.LAVA_BUCKET)) return Optional.of(FluidStack.create(Fluids.LAVA, 1000));
return Optional.empty();
}
for (StorageView<FluidVariant> view : storage) {
if (!view.isResourceBlank() && view.getAmount() > 0) {
return Optional.of(FluidStack.create(view.getResource().getFluid(), view.getAmount()));
}
}
return Optional.of(FluidStack.empty());
}
@Override
public TransferResult drain(ItemStack stack, long amount, boolean simulate) {
ContainerItemContext ctx = ContainerItemContext.withConstant(stack.copy());
Storage<FluidVariant> storage = ctx.find(FluidStorage.ITEM);
if (storage == null) {
if (stack.is(Items.WATER_BUCKET) && amount >= 1000) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.BUCKET));
}
if (stack.is(Items.LAVA_BUCKET) && amount >= 1000) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.BUCKET));
}
return new TransferResult(0, stack);
}
try (Transaction tx = Transaction.openOuter()) {
long extracted = 0;
for (StorageView<FluidVariant> view : storage) {
if (!view.isResourceBlank()) {
extracted = storage.extract(view.getResource(), amount, tx);
if (extracted > 0) break;
}
}
if (extracted > 0) {
if (!simulate) tx.commit();
ItemStack resultStack = ctx.getItemVariant().toStack((int) ctx.getAmount());
return new TransferResult(extracted, resultStack);
}
}
return new TransferResult(0, stack);
}
@Override
public TransferResult fill(ItemStack stack, FluidStack fluid, long amount, boolean simulate) {
ContainerItemContext ctx = ContainerItemContext.withConstant(stack.copy());
Storage<FluidVariant> storage = ctx.find(FluidStorage.ITEM);
if (storage == null) {
if (stack.is(Items.BUCKET) && amount >= 1000) {
if (fluid.getFluid().isSame(Fluids.WATER)) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.WATER_BUCKET));
}
if (fluid.getFluid().isSame(Fluids.LAVA)) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.LAVA_BUCKET));
}
}
return new TransferResult(0, stack);
}
try (Transaction tx = Transaction.openOuter()) {
long inserted = storage.insert(FluidVariant.of(fluid.getFluid()), amount, tx);
if (inserted > 0) {
if (!simulate) tx.commit();
ItemStack resultStack = ctx.getItemVariant().toStack((int) ctx.getAmount());
return new TransferResult(inserted, resultStack);
}
}
return new TransferResult(0, stack);
}
@Override
public boolean isFluidHandler(ItemStack stack) {
ContainerItemContext ctx = ContainerItemContext.withConstant(stack);
Storage<FluidVariant> storage = ctx.find(FluidStorage.ITEM);
return storage != null || stack.is(Items.BUCKET) || stack.is(Items.WATER_BUCKET) || stack.is(Items.LAVA_BUCKET);
}
}
@@ -0,0 +1,146 @@
package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.platform.services.ITransferHelper;
import net.cmr.jurassicrevived.platform.transfer.PlatformItemHandler;
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
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.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import dev.architectury.fluid.FluidStack;
import team.reborn.energy.api.EnergyStorage;
import net.cmr.jurassicrevived.platform.transfer.PlatformEnergyHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformFluidHandler;
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.StorageView;
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
//import net.fabricmc.fabric.api.transfer.v1.energy.EnergyStorage;
//import team.reborn.energy.api.EnergyStorage;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class FabricTransferHelper implements ITransferHelper {
@Override
public Optional<PlatformItemHandler> getItemHandler(Level level, BlockPos pos, Direction side) {
Storage<ItemVariant> storage = ItemStorage.SIDED.find(level, pos, side);
if (storage == null) return Optional.empty();
return Optional.of(new FabricItemHandler(storage));
}
@Override
public Optional<PlatformFluidHandler> getFluidHandler(Level level, BlockPos pos, Direction side) {
var storage = FluidStorage.SIDED.find(level, pos, side);
if (storage == null) return Optional.empty();
return Optional.of(new FabricFluidHandler(storage));
}
@Override
public Optional<PlatformEnergyHandler> getEnergyHandler(Level level, BlockPos pos, Direction side) {
EnergyStorage storage = EnergyStorage.SIDED.find(level, pos, side);
return Optional.empty();
}
private static class FabricFluidHandler implements PlatformFluidHandler {
private final net.fabricmc.fabric.api.transfer.v1.storage.Storage<FluidVariant> storage;
private FabricFluidHandler(net.fabricmc.fabric.api.transfer.v1.storage.Storage<FluidVariant> storage) {
this.storage = storage;
}
@Override
public Iterable<FluidStack> getExtractableFluids() {
List<FluidStack> stacks = new ArrayList<>();
for (StorageView<FluidVariant> view : storage) {
if (view.isResourceBlank()) continue;
FluidVariant v = view.getResource();
long amt = view.getAmount();
if (amt > 0) {
FluidStack stack = FluidStack.create(v.getFluid(), amt);
stacks.add(stack);
}
}
return stacks;
}
@Override
public long extract(FluidStack stack, long amount, boolean simulate) {
try (Transaction tx = Transaction.openOuter()) {
long extracted = storage.extract(FluidVariant.of(stack.getFluid()), amount, tx);
if (!simulate) tx.commit();
return extracted;
}
}
@Override
public long insert(FluidStack stack, long amount, boolean simulate) {
try (Transaction tx = Transaction.openOuter()) {
long inserted = storage.insert(FluidVariant.of(stack.getFluid()), amount, tx);
if (!simulate) tx.commit();
return inserted;
}
}
}
private static class FabricEnergyHandler implements PlatformEnergyHandler {
private FabricEnergyHandler() {
}
@Override
public int extract(int amount, boolean simulate) {
return 0;
}
@Override
public int insert(int amount, boolean simulate) {
return 0;
}
}
private static class FabricItemHandler implements PlatformItemHandler {
private final Storage<ItemVariant> storage;
private FabricItemHandler(Storage<ItemVariant> storage) {
this.storage = storage;
}
@Override
public Iterable<ItemStack> getExtractableStacks() {
List<ItemStack> stacks = new ArrayList<>();
for (StorageView<ItemVariant> view : storage) {
if (view.isResourceBlank()) continue;
ItemVariant v = view.getResource();
long amt = view.getAmount();
if (amt > 0) stacks.add(v.toStack((int) Math.min(amt, Integer.MAX_VALUE)));
}
return stacks;
}
@Override
public int extract(ItemStack stack, int amount, boolean simulate) {
try (Transaction tx = Transaction.openOuter()) {
long extracted = storage.extract(ItemVariant.of(stack), amount, tx);
if (!simulate) tx.commit();
return (int) extracted;
}
}
@Override
public int insert(ItemStack stack, int amount, boolean simulate) {
try (Transaction tx = Transaction.openOuter()) {
long inserted = storage.insert(ItemVariant.of(stack), amount, tx);
if (!simulate) tx.commit();
return (int) inserted;
}
}
}
}
@@ -0,0 +1 @@
net.cmr.jurassicrevived.platform.FabricItemFluidHelper
@@ -0,0 +1 @@
net.cmr.jurassicrevived.platform.FabricTransferHelper
+3
View File
@@ -42,3 +42,6 @@ cloth_config_version_1_21_1=15.0.140
jade_version=14.3.4
jade_version_1_20_1=11.13.1
jade_version_1_21_1=15.10.4
teamreborn_energy_1_20_1=3.0.0
teamreborn_energy_1_21_1=4.1.0
@@ -0,0 +1,106 @@
// ... existing code ...
package net.cmr.jurassicrevived.event;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.entity.custom.*;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.platform.ForgeEnergyStorage;
import net.cmr.jurassicrevived.platform.ForgeTankFluidAdapter;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.common.util.NonNullSupplier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Mod.EventBusSubscriber(modid = Constants.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class ForgeEvents {
@SubscribeEvent
public static void attachCapabilities(AttachCapabilitiesEvent<BlockEntity> event) {
BlockEntity be = event.getObject();
if (be instanceof PowerCellBlockEntity pc) {
event.addCapability(Constants.rl("energy_power_cell"),
new EnergyProvider(() -> new ForgeEnergyStorage(pc.getEnergyStorage(null))));
}
if (be instanceof GeneratorBlockEntity gen) {
event.addCapability(Constants.rl("energy_generator"),
new EnergyProvider(() -> new ForgeEnergyStorage(gen.getEnergyStorage(null))));
}
if (be instanceof TankBlockEntity tank) {
event.addCapability(Constants.rl("fluid_tank"),
new FluidProvider(() -> new ForgeTankFluidAdapter(tank.getTank(null))));
}
if (JRConfigManager.get().requirePower) {
if (be instanceof DNAExtractorBlockEntity e) {
event.addCapability(Constants.rl("energy_dna_extractor"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof DNAAnalyzerBlockEntity e) {
event.addCapability(Constants.rl("energy_dna_analyzer"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof FossilCleanerBlockEntity e) {
event.addCapability(Constants.rl("energy_fossil_cleaner"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof FossilGrinderBlockEntity e) {
event.addCapability(Constants.rl("energy_fossil_grinder"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof DNAHybridizerBlockEntity e) {
event.addCapability(Constants.rl("energy_dna_hybridizer"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof EmbryonicMachineBlockEntity e) {
event.addCapability(Constants.rl("energy_embryonic_machine"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof EmbryoCalcificationMachineBlockEntity e) {
event.addCapability(Constants.rl("energy_embryo_calcification"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
if (be instanceof IncubatorBlockEntity e) {
event.addCapability(Constants.rl("energy_incubator"),
new EnergyProvider(() -> new ForgeEnergyStorage(e.getEnergyStorage(null))));
}
}
}
private static final class EnergyProvider implements ICapabilityProvider {
private final LazyOptional<?> lazy;
private EnergyProvider(NonNullSupplier<?> supplier) {
this.lazy = LazyOptional.of(supplier);
}
@Override
public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
return cap == ForgeCapabilities.ENERGY ? lazy.cast() : LazyOptional.empty();
}
}
private static final class FluidProvider implements ICapabilityProvider {
private final LazyOptional<?> lazy;
private FluidProvider(NonNullSupplier<?> supplier) {
this.lazy = LazyOptional.of(supplier);
}
@Override
public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
return cap == ForgeCapabilities.FLUID_HANDLER ? lazy.cast() : LazyOptional.empty();
}
}
}
@@ -0,0 +1,36 @@
package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.minecraftforge.energy.IEnergyStorage;
public record ForgeEnergyStorage(ModEnergyStorage storage) implements IEnergyStorage {
@Override
public int receiveEnergy(int maxReceive, boolean simulate) {
return storage.receiveEnergy(maxReceive, simulate);
}
@Override
public int extractEnergy(int maxExtract, boolean simulate) {
return storage.extractEnergy(maxExtract, simulate);
}
@Override
public int getEnergyStored() {
return storage.getEnergyStored();
}
@Override
public int getMaxEnergyStored() {
return storage.getMaxEnergyStored();
}
@Override
public boolean canExtract() {
return storage.canExtract();
}
@Override
public boolean canReceive() {
return storage.canReceive();
}
}
@@ -0,0 +1,79 @@
package net.cmr.jurassicrevived.platform;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.platform.services.IItemFluidHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.material.Fluids;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import java.util.Optional;
public class ForgeItemFluidHelper implements IItemFluidHelper {
@Override
public Optional<FluidStack> getContainedFluid(ItemStack stack) {
Optional<FluidStack> handlerFluid = FluidUtil.getFluidHandler(stack.copy())
.map(handler -> {
if (handler.getTanks() <= 0) return FluidStack.empty();
var fs = handler.getFluidInTank(0);
if (fs.isEmpty()) return FluidStack.empty();
return FluidStack.create(fs.getFluid(), fs.getAmount(), fs.getTag());
});
if (handlerFluid.isPresent() && !handlerFluid.get().isEmpty()) return handlerFluid;
if (stack.is(Items.WATER_BUCKET)) {
return Optional.of(FluidStack.create(Fluids.WATER, 1000));
}
if (stack.is(Items.LAVA_BUCKET)) {
return Optional.of(FluidStack.create(Fluids.LAVA, 1000));
}
return Optional.of(FluidStack.empty());
}
@Override
public TransferResult drain(ItemStack stack, long amount, boolean simulate) {
Optional<TransferResult> handlerResult = FluidUtil.getFluidHandler(stack.copy())
.map(handler -> {
var drained = handler.drain((int) amount,
simulate ? IFluidHandlerItem.FluidAction.SIMULATE : IFluidHandlerItem.FluidAction.EXECUTE);
return new TransferResult(drained.getAmount(), handler.getContainer());
});
if (handlerResult.isPresent()) return handlerResult.get();
if (stack.is(Items.WATER_BUCKET) && amount >= 1000) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.BUCKET));
}
if (stack.is(Items.LAVA_BUCKET) && amount >= 1000) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.BUCKET));
}
return new TransferResult(0, stack);
}
@Override
public TransferResult fill(ItemStack stack, FluidStack fluid, long amount, boolean simulate) {
Optional<TransferResult> handlerResult = FluidUtil.getFluidHandler(stack.copy())
.map(handler -> {
var toFill = new net.minecraftforge.fluids.FluidStack(fluid.getFluid(), (int) amount, fluid.getTag());
int filled = handler.fill(toFill,
simulate ? IFluidHandlerItem.FluidAction.SIMULATE : IFluidHandlerItem.FluidAction.EXECUTE);
return new TransferResult(filled, handler.getContainer());
});
if (handlerResult.isPresent()) return handlerResult.get();
if (stack.is(Items.BUCKET) && amount >= 1000) {
if (fluid.getFluid().isSame(Fluids.WATER)) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.WATER_BUCKET));
}
if (fluid.getFluid().isSame(Fluids.LAVA)) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.LAVA_BUCKET));
}
}
return new TransferResult(0, stack);
}
}
@@ -0,0 +1,49 @@
package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.block.entity.custom.TankBlockEntity;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
public record ForgeTankFluidAdapter(TankBlockEntity.TankFluidHandler tank) implements IFluidHandler {
@Override
public int getTanks() {
return 1;
}
@Override
public FluidStack getFluidInTank(int tankIndex) {
if (tankIndex != 0) return FluidStack.EMPTY;
return new FluidStack(tank.getFluid().getFluid(), (int) tank.getFluid().getAmount());
}
@Override
public int getTankCapacity(int tankIndex) {
return tankIndex == 0 ? (int) tank.getCapacity() : 0;
}
@Override
public boolean isFluidValid(int tankIndex, FluidStack stack) {
return tankIndex == 0;
}
@Override
public int fill(FluidStack resource, FluidAction action) {
if (resource.isEmpty()) return 0;
long filled = tank.fill(dev.architectury.fluid.FluidStack.create(resource.getFluid(), resource.getAmount()), action.simulate());
return (int) filled;
}
@Override
public FluidStack drain(FluidStack resource, FluidAction action) {
if (resource.isEmpty()) return FluidStack.EMPTY;
var drained = tank.drain(resource.getAmount(), action.simulate());
return new FluidStack(drained.getFluid(), (int) drained.getAmount());
}
@Override
public FluidStack drain(int maxDrain, FluidAction action) {
var drained = tank.drain(maxDrain, action.simulate());
return new FluidStack(drained.getFluid(), (int) drained.getAmount());
}
}
@@ -0,0 +1,148 @@
package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.platform.services.ITransferHelper;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.platform.transfer.PlatformItemHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformEnergyHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformFluidHandler;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class ForgeTransferHelper implements ITransferHelper {
@Override
public Optional<PlatformItemHandler> getItemHandler(Level level, BlockPos pos, Direction side) {
BlockEntity be = level.getBlockEntity(pos);
if (be == null) return Optional.empty();
return be.getCapability(ForgeCapabilities.ITEM_HANDLER, side)
.resolve()
.map(ForgeItemHandler::new);
}
@Override
public Optional<PlatformFluidHandler> getFluidHandler(Level level, BlockPos pos, Direction side) {
BlockEntity be = level.getBlockEntity(pos);
if (be == null) return Optional.empty();
return be.getCapability(ForgeCapabilities.FLUID_HANDLER, side)
.resolve()
.map(ForgeFluidHandler::new);
}
@Override
public Optional<PlatformEnergyHandler> getEnergyHandler(Level level, BlockPos pos, Direction side) {
BlockEntity be = level.getBlockEntity(pos);
if (be == null) return Optional.empty();
return be.getCapability(ForgeCapabilities.ENERGY, side)
.resolve()
.map(ForgeEnergyHandler::new);
}
private static class ForgeItemHandler implements PlatformItemHandler {
private final IItemHandler handler;
private ForgeItemHandler(IItemHandler handler) {
this.handler = handler;
}
@Override
public Iterable<ItemStack> getExtractableStacks() {
List<ItemStack> stacks = new ArrayList<>();
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack stack = handler.getStackInSlot(i);
if (!stack.isEmpty()) stacks.add(stack.copy());
}
return stacks;
}
@Override
public int extract(ItemStack stack, int amount, boolean simulate) {
int remaining = amount;
for (int i = 0; i < handler.getSlots() && remaining > 0; i++) {
ItemStack slot = handler.getStackInSlot(i);
if (!ItemHandlerHelper.canItemStacksStack(slot, stack)) continue;
ItemStack extracted = handler.extractItem(i, remaining, simulate);
remaining -= extracted.getCount();
}
return amount - remaining;
}
@Override
public int insert(ItemStack stack, int amount, boolean simulate) {
ItemStack toInsert = stack.copy();
toInsert.setCount(amount);
ItemStack remainder = toInsert;
for (int i = 0; i < handler.getSlots() && !remainder.isEmpty(); i++) {
remainder = handler.insertItem(i, remainder, simulate);
}
return amount - remainder.getCount();
}
}
private static class ForgeFluidHandler implements PlatformFluidHandler {
private final IFluidHandler handler;
private ForgeFluidHandler(IFluidHandler handler) {
this.handler = handler;
}
@Override
public Iterable<FluidStack> getExtractableFluids() {
List<FluidStack> stacks = new ArrayList<>();
for (int i = 0; i < handler.getTanks(); i++) {
net.minecraftforge.fluids.FluidStack fs = handler.getFluidInTank(i);
if (!fs.isEmpty()) {
stacks.add(FluidStack.create(fs.getFluid(), fs.getAmount(), fs.getTag()));
}
}
return stacks;
}
@Override
public long extract(FluidStack stack, long amount, boolean simulate) {
net.minecraftforge.fluids.FluidStack req =
new net.minecraftforge.fluids.FluidStack(stack.getFluid(), (int) amount, stack.getTag());
net.minecraftforge.fluids.FluidStack drained = handler.drain(req, simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE);
return drained.getAmount();
}
@Override
public long insert(FluidStack stack, long amount, boolean simulate) {
net.minecraftforge.fluids.FluidStack toFill =
new net.minecraftforge.fluids.FluidStack(stack.getFluid(), (int) amount, stack.getTag());
return handler.fill(toFill, simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE);
}
}
private static class ForgeEnergyHandler implements PlatformEnergyHandler {
private final IEnergyStorage storage;
private ForgeEnergyHandler(IEnergyStorage storage) {
this.storage = storage;
}
@Override
public int extract(int amount, boolean simulate) {
return storage.extractEnergy(amount, simulate);
}
@Override
public int insert(int amount, boolean simulate) {
return storage.receiveEnergy(amount, simulate);
}
}
}
@@ -0,0 +1 @@
net.cmr.jurassicrevived.platform.ForgeItemFluidHelper
@@ -0,0 +1 @@
net.cmr.jurassicrevived.platform.ForgeTransferHelper
@@ -2,11 +2,18 @@ package net.cmr.jurassicrevived.event;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.FossilCleanerBlockEntity;
import net.cmr.jurassicrevived.block.entity.custom.GeneratorBlockEntity;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.block.entity.custom.PowerCellBlockEntity;
import net.cmr.jurassicrevived.block.entity.custom.TankBlockEntity;
import net.cmr.jurassicrevived.neoforge.capabilities.NeoForgeEnergyStorage;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
@EventBusSubscriber(modid = Constants.MOD_ID)
public class NeoForgeEvents
@@ -24,27 +31,64 @@ public class NeoForgeEvents
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.EMBRYO_CALCIFICATION_MACHINE_BE.get(), (be, side) -> be.getItemHandler(side));
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.CRATE_BE.get(), (be, side) -> be.getItemHandler(side));
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.INCUBATOR_BE.get(), (be, side) -> be.getItemHandler(side));
*/
Energy
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.POWER_CELL_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.GENERATOR_BE.get(), (be, side) -> be.getEnergyStorage(side));
// Energy
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.POWER_CELL_BE.get(),
(be, side) -> new NeoForgeEnergyStorage(((PowerCellBlockEntity) be).getEnergyStorage(side)));
Fluids
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, ModBlockEntities.TANK_BE.get(), (be, side) -> be.getTank(side));
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, ModBlockEntities.FOSSIL_CLEANER_BE.get(), (be, side) -> be.getTank(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.GENERATOR_BE.get(),
(be, side) -> new NeoForgeEnergyStorage(((GeneratorBlockEntity) be).getEnergyStorage(side)));
Machine Logic (if config allows)
if (JRConfigManager.get().requirePower) {
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.DNA_EXTRACTOR_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.DNA_ANALYZER_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.FOSSIL_CLEANER_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.FOSSIL_GRINDER_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.DNA_HYBRIDIZER_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.EMBRYONIC_MACHINE_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.EMBRYO_CALCIFICATION_MACHINE_BE.get(), (be, side) -> be.getEnergyStorage(side));
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.INCUBATOR_BE.get(), (be, side) -> be.getEnergyStorage(side));
// Fluids
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, ModBlockEntities.TANK_BE.get(),
(be, side) -> new TankFluidAdapter(((TankBlockEntity) be).getTank(side)));
//event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, ModBlockEntities.FOSSIL_CLEANER_BE.get(),
// (be, side) -> new TankFluidAdapter(((FossilCleanerBlockEntity) be).getTank(side)));
}
*/
private record TankFluidAdapter(TankBlockEntity.TankFluidHandler tank) implements IFluidHandler {
@Override
public int getTanks() {
return 1;
}
@Override
public FluidStack getFluidInTank(int tankIndex) {
if (tankIndex != 0) return FluidStack.EMPTY;
return new FluidStack(tank.getFluid().getFluid(), (int) tank.getFluid().getAmount());
}
@Override
public int getTankCapacity(int tankIndex) {
return tankIndex == 0 ? (int) tank.getCapacity() : 0;
}
@Override
public boolean isFluidValid(int tankIndex, FluidStack stack) {
return tankIndex == 0;
}
@Override
public int fill(FluidStack resource, FluidAction action) {
if (resource.isEmpty()) return 0;
long filled = tank.fill(dev.architectury.fluid.FluidStack.create(resource.getFluid(), resource.getAmount()), action.simulate());
return (int) filled;
}
@Override
public FluidStack drain(FluidStack resource, FluidAction action) {
if (resource.isEmpty()) return FluidStack.EMPTY;
var drained = tank.drain(resource.getAmount(), action.simulate());
return new FluidStack(drained.getFluid(), (int) drained.getAmount());
}
@Override
public FluidStack drain(int maxDrain, FluidAction action) {
var drained = tank.drain(maxDrain, action.simulate());
return new FluidStack(drained.getFluid(), (int) drained.getAmount());
}
}
}
@@ -0,0 +1,84 @@
package net.cmr.jurassicrevived.platform;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.platform.services.IItemFluidHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.material.Fluids;
import net.neoforged.neoforge.fluids.FluidUtil;
import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
import java.util.Optional;
public class NeoForgeItemFluidHelper implements IItemFluidHelper {
@Override
public Optional<FluidStack> getContainedFluid(ItemStack stack) {
Optional<FluidStack> handlerFluid = FluidUtil.getFluidHandler(stack.copy())
.map(handler -> {
if (handler.getTanks() <= 0) return FluidStack.empty();
var fs = handler.getFluidInTank(0);
if (fs.isEmpty()) return FluidStack.empty();
return FluidStack.create(fs.getFluid(), fs.getAmount());
});
if (handlerFluid.isPresent() && !handlerFluid.get().isEmpty()) return handlerFluid;
if (stack.is(Items.WATER_BUCKET)) {
return Optional.of(FluidStack.create(Fluids.WATER, 1000));
}
if (stack.is(Items.LAVA_BUCKET)) {
return Optional.of(FluidStack.create(Fluids.LAVA, 1000));
}
return Optional.of(FluidStack.empty());
}
@Override
public TransferResult drain(ItemStack stack, long amount, boolean simulate) {
Optional<TransferResult> handlerResult = FluidUtil.getFluidHandler(stack.copy())
.map(handler -> {
var drained = handler.drain((int) amount,
simulate ? IFluidHandlerItem.FluidAction.SIMULATE : IFluidHandlerItem.FluidAction.EXECUTE);
return new TransferResult(drained.getAmount(), handler.getContainer());
});
if (handlerResult.isPresent()) return handlerResult.get();
if (stack.is(Items.WATER_BUCKET) && amount >= 1000) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.BUCKET));
}
if (stack.is(Items.LAVA_BUCKET) && amount >= 1000) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.BUCKET));
}
return new TransferResult(0, stack);
}
@Override
public TransferResult fill(ItemStack stack, FluidStack fluid, long amount, boolean simulate) {
Optional<TransferResult> handlerResult = FluidUtil.getFluidHandler(stack.copy())
.map(handler -> {
var toFill = new net.neoforged.neoforge.fluids.FluidStack(fluid.getFluid(), (int) amount);
int filled = handler.fill(toFill,
simulate ? IFluidHandlerItem.FluidAction.SIMULATE : IFluidHandlerItem.FluidAction.EXECUTE);
return new TransferResult(filled, handler.getContainer());
});
if (handlerResult.isPresent()) return handlerResult.get();
if (stack.is(Items.BUCKET) && amount >= 1000) {
if (fluid.getFluid().isSame(Fluids.WATER)) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.WATER_BUCKET));
}
if (fluid.getFluid().isSame(Fluids.LAVA)) {
return new TransferResult(1000, simulate ? stack : new ItemStack(Items.LAVA_BUCKET));
}
}
return new TransferResult(0, stack);
}
@Override
public boolean isFluidHandler(ItemStack stack) {
return FluidUtil.getFluidHandler(stack).isPresent() || stack.is(Items.BUCKET) || stack.is(Items.WATER_BUCKET) || stack.is(Items.LAVA_BUCKET);
}
}
@@ -0,0 +1,161 @@
package net.cmr.jurassicrevived.platform;
import net.cmr.jurassicrevived.platform.services.ITransferHelper;
import net.cmr.jurassicrevived.platform.transfer.PlatformItemHandler;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.neoforged.neoforge.capabilities.Capabilities;
import dev.architectury.fluid.FluidStack;
import net.cmr.jurassicrevived.platform.transfer.InternalFluidHandlerAdapter;
import net.cmr.jurassicrevived.platform.transfer.InternalFluidProvider;
import net.cmr.jurassicrevived.platform.transfer.PlatformEnergyHandler;
import net.cmr.jurassicrevived.platform.transfer.PlatformFluidHandler;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.energy.IEnergyStorage;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import net.neoforged.neoforge.items.IItemHandler;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class NeoForgeTransferHelper implements ITransferHelper {
@Override
public Optional<PlatformItemHandler> getItemHandler(Level level, BlockPos pos, Direction side) {
IItemHandler handler = level.getCapability(Capabilities.ItemHandler.BLOCK, pos, side);
return Optional.ofNullable(handler).map(NeoForgeItemHandler::new);
}
@Override
public Optional<PlatformFluidHandler> getFluidHandler(Level level, BlockPos pos, Direction side) {
IFluidHandler handler = level.getCapability(Capabilities.FluidHandler.BLOCK, pos, side);
if (handler != null) return Optional.of(new NeoForgeFluidHandler(handler));
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof InternalFluidProvider provider) {
return Optional.of(new InternalFluidHandlerAdapter(provider.getFluidHandler(side)));
}
return Optional.empty();
}
@Override
public Optional<PlatformEnergyHandler> getEnergyHandler(Level level, BlockPos pos, Direction side) {
IEnergyStorage storage = level.getCapability(Capabilities.EnergyStorage.BLOCK, pos, side);
return Optional.ofNullable(storage).map(NeoForgeEnergyHandler::new);
}
private static class NeoForgeFluidHandler implements PlatformFluidHandler {
private final IFluidHandler handler;
private NeoForgeFluidHandler(IFluidHandler handler) {
this.handler = handler;
}
@Override
public Iterable<FluidStack> getExtractableFluids() {
List<FluidStack> stacks = new ArrayList<>();
for (int i = 0; i < handler.getTanks(); i++) {
net.neoforged.neoforge.fluids.FluidStack fs = handler.getFluidInTank(i);
if (!fs.isEmpty()) {
stacks.add(FluidStack.create(fs.getFluid(), fs.getAmount()));
}
}
return stacks;
}
@Override
public long extract(FluidStack stack, long amount, boolean simulate) {
net.neoforged.neoforge.fluids.FluidStack req =
new net.neoforged.neoforge.fluids.FluidStack(stack.getFluid(), (int) amount);
net.neoforged.neoforge.fluids.FluidStack drained = handler.drain(
req,
simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE
);
return drained.getAmount();
}
@Override
public long insert(FluidStack stack, long amount, boolean simulate) {
net.neoforged.neoforge.fluids.FluidStack toFill =
new net.neoforged.neoforge.fluids.FluidStack(stack.getFluid(), (int) amount);
return handler.fill(
toFill,
simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE
);
}
}
private static class NeoForgeEnergyHandler implements PlatformEnergyHandler {
private final IEnergyStorage storage;
private NeoForgeEnergyHandler(IEnergyStorage storage) {
this.storage = storage;
}
@Override
public int extract(int amount, boolean simulate) {
return storage.extractEnergy(amount, simulate);
}
@Override
public int insert(int amount, boolean simulate) {
return storage.receiveEnergy(amount, simulate);
}
}
private static class NeoForgeItemHandler implements PlatformItemHandler {
private final IItemHandler handler;
private NeoForgeItemHandler(IItemHandler handler) {
this.handler = handler;
}
@Override
public Iterable<ItemStack> getExtractableStacks() {
List<ItemStack> stacks = new ArrayList<>();
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack stack = handler.getStackInSlot(i);
if (!stack.isEmpty()) stacks.add(stack.copy());
}
return stacks;
}
@Override
public int extract(ItemStack stack, int amount, boolean simulate) {
int remaining = amount;
for (int i = 0; i < handler.getSlots() && remaining > 0; i++) {
ItemStack slot = handler.getStackInSlot(i);
if (!itemsMatch(slot, stack)) continue;
ItemStack extracted = handler.extractItem(i, remaining, simulate);
remaining -= extracted.getCount();
}
return amount - remaining;
}
private static boolean itemsMatch(ItemStack a, ItemStack b) {
//? if >1.20.1 {
return ItemStack.isSameItemSameComponents(a, b);
//?} else {
/*return ItemStack.isSameItemSameTags(a, b);*/
//?}
}
@Override
public int insert(ItemStack stack, int amount, boolean simulate) {
ItemStack toInsert = stack.copy();
toInsert.setCount(amount);
ItemStack remainder = toInsert;
for (int i = 0; i < handler.getSlots() && !remainder.isEmpty(); i++) {
remainder = handler.insertItem(i, remainder, simulate);
}
return amount - remainder.getCount();
}
}
}
@@ -0,0 +1 @@
net.cmr.jurassicrevived.platform.NeoForgeItemFluidHelper
@@ -0,0 +1 @@
net.cmr.jurassicrevived.platform.NeoForgeTransferHelper