Skip to content

Commit

Permalink
Added fluid tank and energy storage attachments to block entities
Browse files Browse the repository at this point in the history
  • Loading branch information
LatvianModder committed Sep 5, 2024
1 parent 4612297 commit 621dd67
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
import dev.latvian.mods.kubejs.block.custom.WallBlockBuilder;
import dev.latvian.mods.kubejs.block.entity.BlockEntityAttachmentRegistry;
import dev.latvian.mods.kubejs.block.entity.CustomCapabilityAttachment;
import dev.latvian.mods.kubejs.block.entity.EnergyStorageAttachment;
import dev.latvian.mods.kubejs.block.entity.FluidTankAttachment;
import dev.latvian.mods.kubejs.block.entity.InventoryAttachment;
import dev.latvian.mods.kubejs.block.state.BlockStatePredicate;
import dev.latvian.mods.kubejs.color.KubeColor;
Expand Down Expand Up @@ -707,6 +709,8 @@ public void registerRecipeComponents(RecipeComponentFactoryRegistry registry) {
public void registerBlockEntityAttachments(BlockEntityAttachmentRegistry registry) {
registry.register(CustomCapabilityAttachment.TYPE);
registry.register(InventoryAttachment.TYPE);
registry.register(FluidTankAttachment.TYPE);
registry.register(EnergyStorageAttachment.TYPE);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package dev.latvian.mods.kubejs.block.entity;

import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.common.util.INBTSerializable;
import org.jetbrains.annotations.Nullable;

public interface BlockEntityAttachment {
default Object getExposedObject() {
default Object getWrappedObject() {
return this;
}

Expand All @@ -15,9 +18,24 @@ default <CAP, SRC> CAP getCapability(BlockCapability<CAP, SRC> capability) {
return null;
}

@Nullable
default Tag serialize(HolderLookup.Provider registries) {
if (getWrappedObject() instanceof INBTSerializable<?> s) {
return s.serializeNBT(registries);
}

return null;
}

default void deserialize(HolderLookup.Provider registries, @Nullable Tag tag) {
if (tag != null && getWrappedObject() instanceof INBTSerializable s) {
s.deserializeNBT(registries, tag);
}
}

default void onRemove(ServerLevel level, KubeBlockEntity blockEntity, BlockState newState) {
}

default void tick() {
default void serverTick() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.util.List;

public interface BlockEntityAttachmentFactory {
BlockEntityAttachment create(KubeBlockEntity entity);
BlockEntityAttachment create(BlockEntityAttachmentInfo info, KubeBlockEntity entity);

default List<BlockCapability<?, ?>> getCapabilities() {
return List.of();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import dev.latvian.mods.rhino.util.HideFromJS;
import net.minecraft.core.Direction;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.fluids.crafting.FluidIngredient;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;
Expand Down Expand Up @@ -41,4 +42,16 @@ default void inventory(String id, Set<Direction> directions, int width, int heig
default void inventory(String id, Set<Direction> directions, int width, int height) {
inventory(id, directions, width, height, null);
}

default void fluidTank(String id, Set<Direction> directions, int capcity, @Nullable FluidIngredient inputFilter) {
attach(id, FluidTankAttachment.TYPE, directions, new FluidTankAttachment.Factory(capcity, Optional.ofNullable(inputFilter)));
}

default void fluidTank(String id, Set<Direction> directions, int capcity) {
fluidTank(id, directions, capcity, null);
}

default void energyStorage(String id, Set<Direction> directions, int capcity, int maxReceive, int maxExtract, int autoOutput) {
attach(id, EnergyStorageAttachment.TYPE, directions, new EnergyStorageAttachment.Factory(capcity, maxReceive <= 0 ? Optional.empty() : Optional.of(maxReceive), maxExtract <= 0 ? Optional.empty() : Optional.of(maxExtract), autoOutput <= 0 ? Optional.empty() : Optional.of(autoOutput)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.fluids.FluidUtil;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;

import java.util.EnumSet;
import java.util.HashMap;
Expand Down Expand Up @@ -97,6 +99,14 @@ public void rightClickOpensInventory(String id) {
};
}

public void rightClickFillsTank(String id) {
blockBuilder.rightClick = e -> {
if (e.getPlayer() instanceof ServerPlayerKJS && e.getBlock().getEntity() instanceof KubeBlockEntity entity && entity.attachments.get(id) instanceof IFluidHandler tank) {
FluidUtil.interactWithFluidHandler(e.getPlayer(), e.getHand(), tank);
}
};
}

@HideFromJS
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new KubeBlockEntity(pos, state, this);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package dev.latvian.mods.kubejs.block.entity;

import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.Tag;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.common.util.INBTSerializable;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.function.Supplier;

public record CustomCapabilityAttachment(BlockCapability<?, ?> capability, Object data) implements BlockEntityAttachment, INBTSerializable<Tag> {
public record CustomCapabilityAttachment(BlockCapability<?, ?> capability, Object data) implements BlockEntityAttachment {
public static final BlockEntityAttachmentType TYPE = new BlockEntityAttachmentType("custom_capability", Factory.class);

public record Factory(BlockCapability<?, ?> type, Supplier<?> dataFactory) implements BlockEntityAttachmentFactory {
@Override
public BlockEntityAttachment create(KubeBlockEntity entity) {
public BlockEntityAttachment create(BlockEntityAttachmentInfo info, KubeBlockEntity entity) {
return new CustomCapabilityAttachment(type, dataFactory.get());
}

Expand All @@ -25,7 +22,7 @@ public BlockEntityAttachment create(KubeBlockEntity entity) {
}

@Override
public Object getExposedObject() {
public Object getWrappedObject() {
return data;
}

Expand All @@ -38,16 +35,4 @@ public <CAP, SRC> CAP getCapability(BlockCapability<CAP, SRC> c) {

return null;
}

@Override
public Tag serializeNBT(HolderLookup.Provider registries) {
return data instanceof INBTSerializable<?> s ? s.serializeNBT(registries) : null;
}

@Override
public void deserializeNBT(HolderLookup.Provider registries, Tag tag) {
if (data instanceof INBTSerializable s) {
s.deserializeNBT(registries, tag);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package dev.latvian.mods.kubejs.block.entity;

import dev.latvian.mods.kubejs.bindings.DirectionWrapper;
import net.minecraft.core.Direction;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.energy.EnergyStorage;
import net.neoforged.neoforge.energy.IEnergyStorage;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class EnergyStorageAttachment implements BlockEntityAttachment {
public static final BlockEntityAttachmentType TYPE = new BlockEntityAttachmentType("energy_storage", Factory.class);

public record Factory(int capacity, Optional<Integer> maxReceive, Optional<Integer> maxExtract, Optional<Integer> autoOutput) implements BlockEntityAttachmentFactory {
@Override
public BlockEntityAttachment create(BlockEntityAttachmentInfo info, KubeBlockEntity entity) {
int rx = Math.max(0, maxReceive.orElse(0));
int tx = Math.max(0, maxExtract.orElse(0));
int auto = Math.max(0, autoOutput.orElse(0));
return new EnergyStorageAttachment(entity, capacity, rx, tx, auto, (auto > 0 ? info.directions() : DirectionWrapper.EMPTY_SET).toArray(new Direction[0]));
}

@Override
public boolean isTicking() {
return autoOutput.isPresent() && autoOutput.get() > 0;
}

@Override
public List<BlockCapability<?, ?>> getCapabilities() {
return List.of(Capabilities.EnergyStorage.BLOCK);
}
}

public static class Wrapped extends EnergyStorage {
private final EnergyStorageAttachment attachment;

public Wrapped(EnergyStorageAttachment attachment, int capacity, int maxReceive, int maxExtract) {
super(capacity, maxReceive, maxExtract);
this.attachment = attachment;
}

@Override
public int extractEnergy(int toExtract, boolean simulate) {
int s = super.extractEnergy(toExtract, simulate);

if (s > 0 && !simulate && !attachment.entity.getLevel().isClientSide()) {
attachment.entity.save();
}

return s;
}

@Override
public int receiveEnergy(int toReceive, boolean simulate) {
int s = super.receiveEnergy(toReceive, simulate);

if (s > 0 && !simulate && !attachment.entity.getLevel().isClientSide()) {
attachment.entity.save();
}

return s;
}
}

private final KubeBlockEntity entity;
public final Wrapped energyStorage;
public final int autoOutput;
public final Direction[] autoOutputDirections;

public EnergyStorageAttachment(KubeBlockEntity entity, int capacity, int maxReceive, int maxExtract, int autoOutput, Direction[] autoOutputDirections) {
this.entity = entity;
this.energyStorage = new Wrapped(this, capacity, maxReceive, maxExtract);
this.autoOutput = autoOutput;
this.autoOutputDirections = autoOutputDirections;
}

@Override
public Object getWrappedObject() {
return energyStorage;
}

@Override
@Nullable
public <CAP, SRC> CAP getCapability(BlockCapability<CAP, SRC> capability) {
if (capability == Capabilities.EnergyStorage.BLOCK) {
return (CAP) energyStorage;
}

return null;
}

@Override
public void serverTick() {
if (autoOutputDirections.length > 0 && autoOutput > 0) {
var list = new ArrayList<IEnergyStorage>(1);

for (var dir : autoOutputDirections) {
var c = Capabilities.EnergyStorage.BLOCK.getCapability(entity.getLevel(), entity.getBlockPos().relative(dir), null, null, dir.getOpposite());

if (c != null) {
list.add(c);
}
}

if (!list.isEmpty()) {
int draw = Math.min(autoOutput, energyStorage.getEnergyStored()) / list.size();

if (draw > 0) {
for (var c : list) {
int e = energyStorage.extractEnergy(draw, true);

if (e > 0) {
energyStorage.extractEnergy(c.receiveEnergy(e, false), false);
} else {
break;
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package dev.latvian.mods.kubejs.block.entity;

import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.Tag;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.fluids.capability.templates.FluidTank;
import net.neoforged.neoforge.fluids.crafting.FluidIngredient;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

public class FluidTankAttachment implements BlockEntityAttachment {
public static final BlockEntityAttachmentType TYPE = new BlockEntityAttachmentType("fluid_tank", Factory.class);

public record Factory(int capacity, Optional<FluidIngredient> inputFilter) implements BlockEntityAttachmentFactory {
private static final Predicate<FluidStack> ALWAYS_TRUE = stack -> true;

@Override
public BlockEntityAttachment create(BlockEntityAttachmentInfo info, KubeBlockEntity entity) {
return new FluidTankAttachment(entity, capacity, inputFilter.isEmpty() ? ALWAYS_TRUE : inputFilter.get());
}

@Override
public List<BlockCapability<?, ?>> getCapabilities() {
return List.of(Capabilities.EnergyStorage.BLOCK);
}
}

public static class Wrapped extends FluidTank {
private final FluidTankAttachment attachment;

public Wrapped(FluidTankAttachment attachment, int capacity, Predicate<FluidStack> inputFilter) {
super(capacity, inputFilter);
this.attachment = attachment;
}

@Override
protected void onContentsChanged() {
attachment.entity.save();
}
}

public final KubeBlockEntity entity;
public final Wrapped fluidTank;

public FluidTankAttachment(KubeBlockEntity entity, int capacity, Predicate<FluidStack> filter) {
this.entity = entity;
this.fluidTank = new Wrapped(this, capacity, filter);
}

@Override
public Object getWrappedObject() {
return fluidTank;
}

@Override
@Nullable
public <CAP, SRC> CAP getCapability(BlockCapability<CAP, SRC> capability) {
if (capability == Capabilities.FluidHandler.BLOCK) {
return (CAP) fluidTank;
}

return null;
}

@Override
@Nullable
public Tag serialize(HolderLookup.Provider registries) {
return fluidTank.getFluid().isEmpty() ? null : fluidTank.getFluid().save(registries);
}

@Override
public void deserialize(HolderLookup.Provider registries, @Nullable Tag tag) {
fluidTank.setFluid(tag == null ? FluidStack.EMPTY : FluidStack.parse(registries, tag).orElse(FluidStack.EMPTY));
}
}
Loading

0 comments on commit 621dd67

Please sign in to comment.