From 9703b9f3ab8f726621275ee8a34530d3adb95ff4 Mon Sep 17 00:00:00 2001 From: adolfo carneiro Date: Sat, 16 May 2026 15:54:38 -0300 Subject: [PATCH 1/2] Add NeoForge storage adapters --- .gitignore | 4 + .../NeoForgeEnergyCapabilityLookup.java | 9 +- .../energy/NeoForgeEnergyStorage.java | 155 ++++++++++++ .../neoforge/fluids/NeoForgeFluidKey.java | 60 +++++ .../neoforge/fluids/NeoForgeFluidStorage.java | 207 ++++++++++++++++ .../neoforge/storage/NeoForgeItemKey.java | 44 ++++ .../neoforge/storage/NeoForgeItemStorage.java | 220 ++++++++++++++++++ 7 files changed, 693 insertions(+), 6 deletions(-) create mode 100644 neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java create mode 100644 neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidKey.java create mode 100644 neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidStorage.java create mode 100644 neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemKey.java create mode 100644 neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemStorage.java diff --git a/.gitignore b/.gitignore index 6642a3af..539e2e4b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ build/ out/ classes/ +/common/bin/ +/fabric/bin/ +/neoforge/bin/ +/neoforge/runs/ # Eclipse *.launch diff --git a/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyCapabilityLookup.java b/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyCapabilityLookup.java index 0527a46c..c2f538ae 100644 --- a/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyCapabilityLookup.java +++ b/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyCapabilityLookup.java @@ -2,22 +2,19 @@ import com.logistics.core.lib.energy.EnergyCapabilityLookup; import com.logistics.core.lib.energy.IEnergyStorage; +import net.neoforged.neoforge.capabilities.Capabilities; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; /** - * NeoForge stub implementation of {@link EnergyCapabilityLookup}. - * - *

TODO: implement via NeoForge's {@code Capabilities.EnergyStorage} capability lookup, - * wrapping the found {@code IEnergyStorage} (NeoForge) in an {@link IEnergyStorage} adapter. + * NeoForge implementation of {@link EnergyCapabilityLookup}. */ public final class NeoForgeEnergyCapabilityLookup implements EnergyCapabilityLookup { @Override public @Nullable IEnergyStorage find(Level level, BlockPos pos, Direction side) { - // TODO(neoforge): implement via Capabilities.EnergyStorage.getBlockCapability(level, pos, state, blockEntity, side) - return null; + return NeoForgeEnergyStorage.wrap(level.getCapability(Capabilities.Energy.BLOCK, pos, side)); } } diff --git a/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java b/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java new file mode 100644 index 00000000..1f49312b --- /dev/null +++ b/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java @@ -0,0 +1,155 @@ +package com.logistics.neoforge.energy; + +import com.logistics.core.lib.energy.IEnergyStorage; +import net.neoforged.neoforge.transfer.energy.EnergyHandler; +import net.neoforged.neoforge.transfer.transaction.Transaction; +import net.neoforged.neoforge.transfer.transaction.TransactionContext; +import net.neoforged.neoforge.transfer.transaction.SnapshotJournal; +import org.jetbrains.annotations.Nullable; + +public final class NeoForgeEnergyStorage implements IEnergyStorage { + private final EnergyHandler handler; + + private NeoForgeEnergyStorage(EnergyHandler handler) { + this.handler = handler; + } + + @Nullable + public static IEnergyStorage wrap(@Nullable EnergyHandler handler) { + return handler == null ? null : new NeoForgeEnergyStorage(handler); + } + + @Nullable + public static EnergyHandler asNeoForge(@Nullable IEnergyStorage storage) { + return storage == null ? null : new CommonEnergyHandler(storage); + } + + @Override + public long insert(long maxAmount, boolean simulate) { + try (Transaction tx = openTransaction()) { + int inserted = handler.insert(clampToInt(maxAmount), tx); + if (!simulate) { + tx.commit(); + } + return inserted; + } + } + + @Override + public long extract(long maxAmount, boolean simulate) { + try (Transaction tx = openTransaction()) { + int extracted = handler.extract(clampToInt(maxAmount), tx); + if (!simulate) { + tx.commit(); + } + return extracted; + } + } + + @Override + public long getAmount() { + return handler.getAmountAsLong(); + } + + @Override + public long getCapacity() { + return handler.getCapacityAsLong(); + } + + @Override + public boolean canInsert() { + if (handler instanceof CommonEnergyHandler common) { + return common.canInsert(); + } + try (Transaction tx = openTransaction()) { + return handler.insert(1, tx) > 0; + } + } + + @Override + public boolean canExtract() { + if (handler instanceof CommonEnergyHandler common) { + return common.canExtract(); + } + try (Transaction tx = openTransaction()) { + return handler.extract(1, tx) > 0; + } + } + + private static Transaction openTransaction() { + TransactionContext current = Transaction.getCurrentOpenedTransaction(); + return current == null ? Transaction.openRoot() : Transaction.open(current); + } + + private static int clampToInt(long amount) { + return (int) Math.max(0, Math.min(amount, Integer.MAX_VALUE)); + } + + private static final class CommonEnergyHandler extends SnapshotJournal implements EnergyHandler { + private final IEnergyStorage storage; + private long pendingDelta; + + private CommonEnergyHandler(IEnergyStorage storage) { + this.storage = storage; + } + + @Override + public long getAmountAsLong() { + return Math.max(0, storage.getAmount() + pendingDelta); + } + + @Override + public long getCapacityAsLong() { + return storage.getCapacity(); + } + + private boolean canInsert() { + return storage.canInsert(); + } + + private boolean canExtract() { + return storage.canExtract(); + } + + @Override + public int insert(int amount, net.neoforged.neoforge.transfer.transaction.TransactionContext transaction) { + updateSnapshots(transaction); + long inserted = Math.min(amount, storage.insert(amount, true)); + if (inserted > 0) { + pendingDelta += inserted; + } + return clampToInt(inserted); + } + + @Override + public int extract(int amount, net.neoforged.neoforge.transfer.transaction.TransactionContext transaction) { + updateSnapshots(transaction); + long extracted = Math.min(getAmountAsLong(), storage.extract(amount, true)); + if (extracted > 0) { + pendingDelta -= extracted; + } + return clampToInt(extracted); + } + + @Override + protected Long createSnapshot() { + return pendingDelta; + } + + @Override + protected void revertToSnapshot(Long snapshot) { + pendingDelta = snapshot; + } + + @Override + protected void onRootCommit(Long originalState) { + long delta = pendingDelta - originalState; + if (delta > 0) { + storage.insert(delta, false); + } else if (delta < 0) { + storage.extract(-delta, false); + } + pendingDelta = 0; + } + } +} diff --git a/neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidKey.java b/neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidKey.java new file mode 100644 index 00000000..501b51d7 --- /dev/null +++ b/neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidKey.java @@ -0,0 +1,60 @@ +package com.logistics.neoforge.fluids; + +import com.logistics.core.lib.fluids.IFluidKey; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.transfer.fluid.FluidResource; + +public final class NeoForgeFluidKey implements IFluidKey { + private final FluidResource resource; + + public NeoForgeFluidKey(FluidResource resource) { + if (resource == null) { + throw new NullPointerException("resource must not be null"); + } + this.resource = resource; + } + + public static NeoForgeFluidKey of(FluidStack stack) { + return new NeoForgeFluidKey(FluidResource.of(stack)); + } + + public static NeoForgeFluidKey of(FluidResource resource) { + return new NeoForgeFluidKey(resource); + } + + public FluidResource resource() { + return resource; + } + + @Override + public Fluid getFluid() { + return resource.getFluid(); + } + + @Override + public DataComponentPatch getComponents() { + return resource.getComponentsPatch(); + } + + @Override + public boolean isBlank() { + return resource.isEmpty(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof NeoForgeFluidKey other) return resource.equals(other.resource); + if (o instanceof IFluidKey other) { + return getFluid() == other.getFluid() && getComponents().equals(other.getComponents()); + } + return false; + } + + @Override + public int hashCode() { + return 31 * getFluid().hashCode() + getComponents().hashCode(); + } +} diff --git a/neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidStorage.java b/neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidStorage.java new file mode 100644 index 00000000..521ec434 --- /dev/null +++ b/neoforge/src/main/java/com/logistics/neoforge/fluids/NeoForgeFluidStorage.java @@ -0,0 +1,207 @@ +package com.logistics.neoforge.fluids; + +import com.logistics.core.lib.fluids.IFluidKey; +import com.logistics.core.lib.fluids.IFluidStorage; +import com.logistics.core.lib.fluids.IFluidView; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.neoforged.neoforge.transfer.ResourceHandler; +import net.neoforged.neoforge.transfer.fluid.FluidResource; +import net.neoforged.neoforge.transfer.transaction.SnapshotJournal; +import net.neoforged.neoforge.transfer.transaction.Transaction; +import net.neoforged.neoforge.transfer.transaction.TransactionContext; +import org.jetbrains.annotations.Nullable; + +public final class NeoForgeFluidStorage implements IFluidStorage { + private final ResourceHandler handler; + + private NeoForgeFluidStorage(ResourceHandler handler) { + this.handler = handler; + } + + @Nullable + public static IFluidStorage wrap(@Nullable ResourceHandler handler) { + return handler == null ? null : new NeoForgeFluidStorage(handler); + } + + @Nullable + public static ResourceHandler asNeoForge(@Nullable IFluidStorage storage) { + return storage == null ? null : new CommonFluidHandler(storage); + } + + @Override + public long insert(IFluidKey fluid, long maxAmount, boolean simulate) { + FluidResource resource = toResource(fluid); + if (resource.isEmpty() || maxAmount <= 0) { + return 0; + } + try (Transaction tx = Transaction.openRoot()) { + int inserted = handler.insert(resource, clampToInt(maxAmount), tx); + if (!simulate) { + tx.commit(); + } + return inserted; + } + } + + @Override + public long extract(IFluidKey fluid, long maxAmount, boolean simulate) { + FluidResource resource = toResource(fluid); + if (resource.isEmpty() || maxAmount <= 0) { + return 0; + } + try (Transaction tx = Transaction.openRoot()) { + int extracted = handler.extract(resource, clampToInt(maxAmount), tx); + if (!simulate) { + tx.commit(); + } + return extracted; + } + } + + @Override + public Iterable contents() { + List views = new ArrayList<>(); + for (int i = 0; i < handler.size(); i++) { + FluidResource resource = handler.getResource(i); + long amount = handler.getAmountAsLong(i); + if (resource.isEmpty() || amount <= 0) { + continue; + } + IFluidKey key = NeoForgeFluidKey.of(resource); + views.add(new IFluidView() { + @Override public IFluidKey resource() { return key; } + @Override public long amount() { return amount; } + }); + } + return views; + } + + private static FluidResource toResource(IFluidKey key) { + return key instanceof NeoForgeFluidKey nfKey + ? nfKey.resource() + : FluidResource.of(key.getFluid(), key.getComponents()); + } + + private static int clampToInt(long amount) { + return (int) Math.max(0, Math.min(amount, Integer.MAX_VALUE)); + } + + private static final class CommonFluidHandler extends SnapshotJournal> + implements ResourceHandler { + private final IFluidStorage storage; + private Map pendingDeltas = new HashMap<>(); + + private CommonFluidHandler(IFluidStorage storage) { + this.storage = storage; + } + + @Override + public int size() { + return 1; + } + + @Override + public FluidResource getResource(int index) { + checkIndex(index); + for (IFluidView view : storage.contents()) { + if (view.amount() > 0) { + return toResource(view.resource()); + } + } + return FluidResource.EMPTY; + } + + @Override + public long getAmountAsLong(int index) { + checkIndex(index); + for (IFluidView view : storage.contents()) { + if (view.amount() > 0) { + return view.amount(); + } + } + return 0; + } + + @Override + public long getCapacityAsLong(int index, FluidResource resource) { + checkIndex(index); + if (resource.isEmpty()) { + return 0; + } + return storage.insert(NeoForgeFluidKey.of(resource), Integer.MAX_VALUE, true); + } + + @Override + public boolean isValid(int index, FluidResource resource) { + checkIndex(index); + return !resource.isEmpty() && getCapacityAsLong(index, resource) > 0; + } + + @Override + public int insert(int index, FluidResource resource, int amount, TransactionContext transaction) { + checkIndex(index); + if (resource.isEmpty() || amount <= 0) { + return 0; + } + updateSnapshots(transaction); + IFluidKey key = NeoForgeFluidKey.of(resource); + long pending = pendingDeltas.getOrDefault(key, 0L); + long available = Math.max(0, storage.insert(key, Integer.MAX_VALUE, true) - pending); + long inserted = Math.min(amount, available); + if (inserted > 0) { + pendingDeltas.merge(key, inserted, Long::sum); + } + return clampToInt(inserted); + } + + @Override + public int extract(int index, FluidResource resource, int amount, TransactionContext transaction) { + checkIndex(index); + if (resource.isEmpty() || amount <= 0) { + return 0; + } + updateSnapshots(transaction); + IFluidKey key = NeoForgeFluidKey.of(resource); + long pending = pendingDeltas.getOrDefault(key, 0L); + long available = Math.max(0, storage.extract(key, Integer.MAX_VALUE, true) + pending); + long extracted = Math.min(amount, available); + if (extracted > 0) { + pendingDeltas.merge(key, -extracted, Long::sum); + } + return clampToInt(extracted); + } + + @Override + protected Map createSnapshot() { + return new HashMap<>(pendingDeltas); + } + + @Override + protected void revertToSnapshot(Map snapshot) { + pendingDeltas = snapshot; + } + + @Override + protected void onRootCommit(Map originalState) { + for (var entry : pendingDeltas.entrySet()) { + long original = originalState.getOrDefault(entry.getKey(), 0L); + long delta = entry.getValue() - original; + if (delta > 0) { + storage.insert(entry.getKey(), delta, false); + } else if (delta < 0) { + storage.extract(entry.getKey(), -delta, false); + } + } + pendingDeltas = new HashMap<>(); + } + + private static void checkIndex(int index) { + if (index != 0) { + throw new IndexOutOfBoundsException(index); + } + } + } +} diff --git a/neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemKey.java b/neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemKey.java new file mode 100644 index 00000000..d8899cef --- /dev/null +++ b/neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemKey.java @@ -0,0 +1,44 @@ +package com.logistics.neoforge.storage; + +import com.logistics.core.lib.storage.IItemKey; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.transfer.item.ItemResource; + +public final class NeoForgeItemKey implements IItemKey { + private final ItemResource resource; + + public NeoForgeItemKey(ItemResource resource) { + if (resource == null) { + throw new NullPointerException("resource must not be null"); + } + this.resource = resource; + } + + public static NeoForgeItemKey of(ItemStack stack) { + return new NeoForgeItemKey(ItemResource.of(stack)); + } + + public ItemResource resource() { + return resource; + } + + @Override + public ItemStack toStack(int count) { + return resource.toStack(count); + } + + @Override + public boolean matches(ItemStack stack) { + return resource.matches(stack); + } + + @Override + public boolean equals(Object o) { + return this == o || (o instanceof NeoForgeItemKey other && resource.equals(other.resource)); + } + + @Override + public int hashCode() { + return resource.hashCode(); + } +} diff --git a/neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemStorage.java b/neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemStorage.java new file mode 100644 index 00000000..e4d12a4e --- /dev/null +++ b/neoforge/src/main/java/com/logistics/neoforge/storage/NeoForgeItemStorage.java @@ -0,0 +1,220 @@ +package com.logistics.neoforge.storage; + +import com.logistics.core.lib.storage.IItemKey; +import com.logistics.core.lib.storage.IItemStorage; +import com.logistics.core.lib.storage.IItemView; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.neoforged.neoforge.transfer.ResourceHandler; +import net.neoforged.neoforge.transfer.item.ItemResource; +import net.neoforged.neoforge.transfer.transaction.SnapshotJournal; +import net.neoforged.neoforge.transfer.transaction.Transaction; +import net.neoforged.neoforge.transfer.transaction.TransactionContext; +import org.jetbrains.annotations.Nullable; + +public final class NeoForgeItemStorage implements IItemStorage { + private final ResourceHandler handler; + + private NeoForgeItemStorage(ResourceHandler handler) { + this.handler = handler; + } + + @Nullable + public static IItemStorage wrap(@Nullable ResourceHandler handler) { + return handler == null ? null : new NeoForgeItemStorage(handler); + } + + @Nullable + public static ResourceHandler asNeoForge(@Nullable IItemStorage storage) { + return storage == null ? null : new CommonItemHandler(storage); + } + + @Override + public long insert(IItemKey item, long maxAmount, boolean simulate) { + ItemResource resource = toResource(item); + if (resource.isEmpty() || maxAmount <= 0) { + return 0; + } + try (Transaction tx = Transaction.openRoot()) { + int inserted = handler.insert(resource, clampToInt(maxAmount), tx); + if (!simulate) { + tx.commit(); + } + return inserted; + } + } + + @Override + public long extract(IItemKey item, long maxAmount, boolean simulate) { + ItemResource resource = toResource(item); + if (resource.isEmpty() || maxAmount <= 0) { + return 0; + } + try (Transaction tx = Transaction.openRoot()) { + int extracted = handler.extract(resource, clampToInt(maxAmount), tx); + if (!simulate) { + tx.commit(); + } + return extracted; + } + } + + @Override + public Iterable contents() { + List views = new ArrayList<>(); + for (int i = 0; i < handler.size(); i++) { + ItemResource resource = handler.getResource(i); + long amount = handler.getAmountAsLong(i); + if (resource.isEmpty() || amount <= 0) { + continue; + } + IItemKey key = new NeoForgeItemKey(resource); + views.add(new IItemView() { + @Override public IItemKey resource() { return key; } + @Override public long amount() { return amount; } + }); + } + return views; + } + + private static ItemResource toResource(IItemKey key) { + return key instanceof NeoForgeItemKey nfKey ? nfKey.resource() : ItemResource.of(key.toStack(1)); + } + + private static int clampToInt(long amount) { + return (int) Math.max(0, Math.min(amount, Integer.MAX_VALUE)); + } + + /** + * Adapts a common {@link IItemStorage} to NeoForge's slot-based {@link ResourceHandler}. + * + *

Reports {@link #size()}{@code == 1} regardless of how many distinct item types the + * underlying storage holds. {@link #getResource(int)} returns the first non-empty item + * (and {@link #getAmountAsLong(int)} its amount). This is an intentional "unified slot" + * view — the common abstraction has no fixed slot count, so we expose it as a single + * virtual slot. Insert and extract operations route through {@link #insert} and + * {@link #extract} which use the {@link IItemKey} content map and behave correctly + * for storages with multiple item types. + * + *

External code that enumerates slots will only observe the first item type. + * For correct multi-item interaction, callers should use the key-based insert/extract + * methods rather than slot iteration. + */ + private static final class CommonItemHandler extends SnapshotJournal> + implements ResourceHandler { + private final IItemStorage storage; + private Map pendingDeltas = new HashMap<>(); + + private CommonItemHandler(IItemStorage storage) { + this.storage = storage; + } + + @Override + public int size() { + return 1; + } + + @Override + public ItemResource getResource(int index) { + checkIndex(index); + for (IItemView view : storage.contents()) { + if (view.amount() > 0) { + return toResource(view.resource()); + } + } + return ItemResource.EMPTY; + } + + @Override + public long getAmountAsLong(int index) { + checkIndex(index); + for (IItemView view : storage.contents()) { + if (view.amount() > 0) { + return view.amount(); + } + } + return 0; + } + + @Override + public long getCapacityAsLong(int index, ItemResource resource) { + checkIndex(index); + if (resource.isEmpty()) { + return 0; + } + return storage.insert(new NeoForgeItemKey(resource), Integer.MAX_VALUE, true); + } + + @Override + public boolean isValid(int index, ItemResource resource) { + checkIndex(index); + return !resource.isEmpty() && getCapacityAsLong(index, resource) > 0; + } + + @Override + public int insert(int index, ItemResource resource, int amount, TransactionContext transaction) { + checkIndex(index); + if (resource.isEmpty() || amount <= 0) { + return 0; + } + updateSnapshots(transaction); + IItemKey key = new NeoForgeItemKey(resource); + long pending = pendingDeltas.getOrDefault(key, 0L); + long available = Math.max(0, storage.insert(key, Integer.MAX_VALUE, true) - pending); + long inserted = Math.min(amount, available); + if (inserted > 0) { + pendingDeltas.merge(key, inserted, Long::sum); + } + return clampToInt(inserted); + } + + @Override + public int extract(int index, ItemResource resource, int amount, TransactionContext transaction) { + checkIndex(index); + if (resource.isEmpty() || amount <= 0) { + return 0; + } + updateSnapshots(transaction); + IItemKey key = new NeoForgeItemKey(resource); + long pending = pendingDeltas.getOrDefault(key, 0L); + long available = Math.max(0, storage.extract(key, Integer.MAX_VALUE, true) + pending); + long extracted = Math.min(amount, available); + if (extracted > 0) { + pendingDeltas.merge(key, -extracted, Long::sum); + } + return clampToInt(extracted); + } + + @Override + protected Map createSnapshot() { + return new HashMap<>(pendingDeltas); + } + + @Override + protected void revertToSnapshot(Map snapshot) { + pendingDeltas = snapshot; + } + + @Override + protected void onRootCommit(Map originalState) { + for (var entry : pendingDeltas.entrySet()) { + long original = originalState.getOrDefault(entry.getKey(), 0L); + long delta = entry.getValue() - original; + if (delta > 0) { + storage.insert(entry.getKey(), delta, false); + } else if (delta < 0) { + storage.extract(entry.getKey(), -delta, false); + } + } + pendingDeltas = new HashMap<>(); + } + + private static void checkIndex(int index) { + if (index != 0) { + throw new IndexOutOfBoundsException(index); + } + } + } +} From 87d4c400a7970f5b28fa27c643124796c6bb8709 Mon Sep 17 00:00:00 2001 From: adolfo carneiro Date: Sat, 16 May 2026 17:12:56 -0300 Subject: [PATCH 2/2] Fix CommonEnergyHandler.insert ignoring pendingDelta when computing capacity --- .../com/logistics/neoforge/energy/NeoForgeEnergyStorage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java b/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java index 1f49312b..df1f3892 100644 --- a/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java +++ b/neoforge/src/main/java/com/logistics/neoforge/energy/NeoForgeEnergyStorage.java @@ -114,7 +114,8 @@ private boolean canExtract() { @Override public int insert(int amount, net.neoforged.neoforge.transfer.transaction.TransactionContext transaction) { updateSnapshots(transaction); - long inserted = Math.min(amount, storage.insert(amount, true)); + long effectiveFree = Math.max(0, getCapacityAsLong() - getAmountAsLong()); + long inserted = Math.min(amount, effectiveFree); if (inserted > 0) { pendingDelta += inserted; }