From 245fc387d2e6564bccd180f22c237efb2b72cf92 Mon Sep 17 00:00:00 2001 From: rearth Date: Mon, 3 Mar 2025 18:00:30 +0100 Subject: [PATCH 1/4] Add check for empty fluid variant insertions (fixes #200) --- .../fabric/transfer_api/compat/FluidStorageFluidHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java index e0945c418..6e26e73b1 100644 --- a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java +++ b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java @@ -49,6 +49,7 @@ public boolean isFluidValid(int tank, @NotNull FluidStack stack) { public int fill(FluidStack resource, FluidAction action) { try (Transaction transaction = Transaction.openOuter()) { FluidVariant variant = NeoCompatUtil.toFluidStorageView(resource); + if (variant.isBlank()) return 0; // because moving blank resources is a thing in neoforge for some reason? (See https://github.com/AztechMC/Modern-Industrialization/issues/1029) int filled = (int) storage.insert(variant, NeoCompatUtil.toFabricBucket(resource.getAmount()), transaction); if (action.execute()) { transaction.commit(); From 6ed319f30dac42d21ee91838c594a9cd4ec9ccb3 Mon Sep 17 00:00:00 2001 From: rearth Date: Mon, 3 Mar 2025 19:49:05 +0100 Subject: [PATCH 2/4] Create new transactions for fluid transfer if one is already open (Fixes #187) --- .../compat/FluidStorageFluidHandler.java | 76 +++++++++++++------ 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java index 6e26e73b1..9048d57d5 100644 --- a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java +++ b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java @@ -11,6 +11,12 @@ import net.neoforged.neoforge.fluids.capability.IFluidHandler; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + public class FluidStorageFluidHandler implements IFluidHandler { private final Storage storage; private final Int2ObjectMap> slots; @@ -44,47 +50,71 @@ public int getTankCapacity(int tank) { public boolean isFluidValid(int tank, @NotNull FluidStack stack) { return StorageUtil.simulateInsert(storage, NeoCompatUtil.toFluidStorageView(stack), NeoCompatUtil.toFabricBucket(stack.getAmount()), null) > 0; } - - @Override - public int fill(FluidStack resource, FluidAction action) { - try (Transaction transaction = Transaction.openOuter()) { - FluidVariant variant = NeoCompatUtil.toFluidStorageView(resource); - if (variant.isBlank()) return 0; // because moving blank resources is a thing in neoforge for some reason? (See https://github.com/AztechMC/Modern-Industrialization/issues/1029) - int filled = (int) storage.insert(variant, NeoCompatUtil.toFabricBucket(resource.getAmount()), transaction); - if (action.execute()) { - transaction.commit(); + + // if a transaction from a fabric context causes a neoforge mod to create a new transaction to a fabric context as part of that first call we need to do so in a new thread + private T executeWithTransactionHandling(Supplier operation, T defaultValue) { + if (Transaction.isOpen()) { + try { + return CompletableFuture.supplyAsync(operation).get(1, TimeUnit.MILLISECONDS); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + return defaultValue; } - return NeoCompatUtil.toForgeBucket(filled); + } else { + return operation.get(); } } @Override - public @NotNull FluidStack drain(FluidStack resource, FluidAction action) { - if (!resource.isEmpty()) { + public int fill(FluidStack resource, @NotNull FluidAction action) { + + // because moving blank/empty fluid resources is a thing in neoforge for some reason? (See https://github.com/AztechMC/Modern-Industrialization/issues/1029) + if (resource.isEmpty()) return 0; + + return executeWithTransactionHandling(() -> { try (Transaction transaction = Transaction.openOuter()) { FluidVariant variant = NeoCompatUtil.toFluidStorageView(resource); - int drained = (int) storage.extract(variant, NeoCompatUtil.toFabricBucket(resource.getAmount()), transaction); + int filled = (int) storage.insert(variant, NeoCompatUtil.toFabricBucket(resource.getAmount()), transaction); if (action.execute()) { transaction.commit(); } - return NeoCompatUtil.toForgeFluidStack(variant, drained); + return NeoCompatUtil.toForgeBucket(filled); } - } - return FluidStack.EMPTY; + }, 0); } @Override - public @NotNull FluidStack drain(int maxDrain, FluidAction action) { - for (StorageView view : storage.nonEmptyViews()) { + public @NotNull FluidStack drain(FluidStack resource, @NotNull FluidAction action) { + + if (resource.isEmpty()) { + return FluidStack.EMPTY; + } + + return executeWithTransactionHandling(() -> { try (Transaction transaction = Transaction.openOuter()) { - FluidVariant resource = view.getResource(); - int drained = (int) storage.extract(resource, NeoCompatUtil.toFabricBucket(maxDrain), transaction); + FluidVariant variant = NeoCompatUtil.toFluidStorageView(resource); + int drained = (int) storage.extract(variant, NeoCompatUtil.toFabricBucket(resource.getAmount()), transaction); if (action.execute()) { transaction.commit(); } - return NeoCompatUtil.toForgeFluidStack(resource, drained); + return NeoCompatUtil.toForgeFluidStack(variant, drained); } - } - return FluidStack.EMPTY; + }, FluidStack.EMPTY); + } + + @Override + public @NotNull FluidStack drain(int maxDrain, @NotNull FluidAction action) { + return executeWithTransactionHandling(() -> { + for (StorageView view : storage.nonEmptyViews()) { + try (Transaction transaction = Transaction.openOuter()) { + FluidVariant resource = view.getResource(); + int drained = (int) storage.extract(resource, NeoCompatUtil.toFabricBucket(maxDrain), transaction); + if (action.execute()) { + transaction.commit(); + } + return NeoCompatUtil.toForgeFluidStack(resource, drained); + } + } + return FluidStack.EMPTY; + }, FluidStack.EMPTY); } } From c73360d8bb3ea2876572d8a55783b164214647ef Mon Sep 17 00:00:00 2001 From: rearth Date: Mon, 3 Mar 2025 19:56:31 +0100 Subject: [PATCH 3/4] Reduce timeouts of completablefutures --- .../fabric/transfer_api/compat/FluidStorageFluidHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java index 9048d57d5..4ae335ea1 100644 --- a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java +++ b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/FluidStorageFluidHandler.java @@ -55,7 +55,7 @@ public boolean isFluidValid(int tank, @NotNull FluidStack stack) { private T executeWithTransactionHandling(Supplier operation, T defaultValue) { if (Transaction.isOpen()) { try { - return CompletableFuture.supplyAsync(operation).get(1, TimeUnit.MILLISECONDS); + return CompletableFuture.supplyAsync(operation).get(10, TimeUnit.MICROSECONDS); } catch (InterruptedException | TimeoutException | ExecutionException e) { return defaultValue; } From 2020720d19e221c768132aebfd9a6bf0d41a1608 Mon Sep 17 00:00:00 2001 From: rearth Date: Tue, 4 Mar 2025 18:42:23 +0100 Subject: [PATCH 4/4] Add blank check to insertItem() method --- .../fabric/transfer_api/compat/ItemStorageItemHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/ItemStorageItemHandler.java b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/ItemStorageItemHandler.java index 0f4e4e3b0..69bf85014 100644 --- a/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/ItemStorageItemHandler.java +++ b/fabric-transfer-api-v1/src/main/java/org/sinytra/fabric/transfer_api/compat/ItemStorageItemHandler.java @@ -44,6 +44,7 @@ public int getSlots() { public @NotNull ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { try (Transaction transaction = Transaction.openOuter()) { ItemVariant resource = ItemVariant.of(stack); + if (resource.isBlank()) return ItemStack.EMPTY; int inserted = (int) storage.insert(resource, stack.getCount(), transaction); if (!simulate) { transaction.commit();