mirror of
https://github.com/FabricMC/fabric.git
synced 2025-03-13 16:53:35 -04:00
Reorganize transfer API testmod and rework item api injections (#1652)
* Reorganize the transer API testmod * Rework mixins. Closes #1649.
This commit is contained in:
parent
d19fec74a7
commit
a9bcdbef6a
16 changed files with 257 additions and 195 deletions
|
@ -20,11 +20,15 @@ import java.util.Collections;
|
|||
import java.util.Iterator;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
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.TransactionContext;
|
||||
|
||||
public class TransferApiImpl {
|
||||
public static final Logger LOGGER = LogManager.getLogger("fabric-transfer-api-v1");
|
||||
public static final AtomicLong version = new AtomicLong();
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static final Storage EMPTY_STORAGE = new Storage() {
|
||||
|
|
|
@ -20,13 +20,11 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.block.DispenserBlock;
|
||||
import net.minecraft.block.DropperBlock;
|
||||
import net.minecraft.block.entity.DispenserBlockEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPointerImpl;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
|
@ -35,37 +33,47 @@ 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.StorageUtil;
|
||||
import net.fabricmc.fabric.impl.transfer.TransferApiImpl;
|
||||
|
||||
/**
|
||||
* Allows droppers to insert into ItemVariant storages.
|
||||
*
|
||||
* <p>Maintainer note: it's important that we inject BEFORE the getStack() call,
|
||||
* as the returned stack can be mutated by the StorageUtil.move() call in the injected callback.
|
||||
*/
|
||||
@Mixin(DropperBlock.class)
|
||||
public class DropperBlockMixin {
|
||||
@Inject(
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/block/entity/DispenserBlockEntity;getStack(I)Lnet/minecraft/item/ItemStack;"
|
||||
target = "Lnet/minecraft/block/dispenser/DispenserBehavior;dispense(Lnet/minecraft/util/math/BlockPointer;Lnet/minecraft/item/ItemStack;)Lnet/minecraft/item/ItemStack;"
|
||||
),
|
||||
method = "dispense",
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
cancellable = true,
|
||||
allow = 1
|
||||
)
|
||||
public void hookDispense(ServerWorld world, BlockPos pos, CallbackInfo ci, BlockPointerImpl blockPointerImpl, DispenserBlockEntity dispenser, int slot) {
|
||||
if (dispenser.getStack(slot).isEmpty()) return;
|
||||
public void hookDispense(ServerWorld world, BlockPos pos, CallbackInfo ci) {
|
||||
DispenserBlockEntity dispenser = (DispenserBlockEntity) world.getBlockEntity(pos);
|
||||
Direction direction = dispenser.getCachedState().get(DispenserBlock.FACING);
|
||||
|
||||
Direction direction = world.getBlockState(pos).get(DispenserBlock.FACING);
|
||||
Storage<ItemVariant> target = ItemStorage.SIDED.find(world, pos.offset(direction), direction.getOpposite());
|
||||
|
||||
if (target != null) {
|
||||
Storage<ItemVariant> source = InventoryStorage.of(dispenser, null).getSlots().get(slot);
|
||||
// Always cancel if a storage is available.
|
||||
ci.cancel();
|
||||
|
||||
if (StorageUtil.move(source, target, k -> true, 1, null) == 1) {
|
||||
ci.cancel();
|
||||
// We pick a non empty slot. It's not necessarily the same as the one vanilla picked, but that doesn't matter.
|
||||
int slot = dispenser.chooseNonEmptySlot();
|
||||
|
||||
if (slot == -1) {
|
||||
TransferApiImpl.LOGGER.warn("Skipping dropper transfer because the empty slot is unexpectedly -1.");
|
||||
return;
|
||||
}
|
||||
|
||||
StorageUtil.move(
|
||||
InventoryStorage.of(dispenser, null).getSlots().get(slot),
|
||||
target,
|
||||
k -> true,
|
||||
1,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,14 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.transfer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.HopperBlock;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.Hopper;
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
|
@ -44,49 +43,61 @@ import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil;
|
|||
@Mixin(HopperBlockEntity.class)
|
||||
public class HopperBlockEntityMixin {
|
||||
@Inject(
|
||||
at = @At("HEAD"),
|
||||
at = @At(
|
||||
value = "INVOKE_ASSIGN",
|
||||
target = "Lnet/minecraft/block/entity/HopperBlockEntity;getOutputInventory(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Lnet/minecraft/inventory/Inventory;"
|
||||
),
|
||||
method = "insert(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/inventory/Inventory;)Z",
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
cancellable = true
|
||||
)
|
||||
private static void hookInsert(World world, BlockPos pos, BlockState state, Inventory inventory, CallbackInfoReturnable<Boolean> cir) {
|
||||
private static void hookInsert(World world, BlockPos pos, BlockState state, Inventory inventory, CallbackInfoReturnable<Boolean> cir, Inventory targetInventory) {
|
||||
// Let vanilla handle the transfer if it found an inventory.
|
||||
if (targetInventory != null) return;
|
||||
|
||||
// Otherwise inject our transfer logic.
|
||||
Direction direction = state.get(HopperBlock.FACING);
|
||||
BlockPos targetPos = pos.offset(direction);
|
||||
BlockEntity targetBe = world.getBlockEntity(targetPos);
|
||||
Storage<ItemVariant> target = ItemStorage.SIDED.find(world, targetPos, null, targetBe, direction.getOpposite());
|
||||
Storage<ItemVariant> target = ItemStorage.SIDED.find(world, targetPos, direction.getOpposite());
|
||||
|
||||
if (target != null) {
|
||||
cir.setReturnValue(doTransfer(InventoryStorage.of(inventory, direction), target, inventory, targetBe));
|
||||
long moved = StorageUtil.move(
|
||||
InventoryStorage.of(inventory, direction),
|
||||
target,
|
||||
iv -> true,
|
||||
1,
|
||||
null
|
||||
);
|
||||
cir.setReturnValue(moved == 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
at = @At("HEAD"),
|
||||
at = @At(
|
||||
value = "INVOKE_ASSIGN",
|
||||
target = "Lnet/minecraft/block/entity/HopperBlockEntity;getInputInventory(Lnet/minecraft/world/World;Lnet/minecraft/block/entity/Hopper;)Lnet/minecraft/inventory/Inventory;"
|
||||
),
|
||||
method = "extract(Lnet/minecraft/world/World;Lnet/minecraft/block/entity/Hopper;)Z",
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
cancellable = true
|
||||
)
|
||||
private static void hookExtract(World world, Hopper hopper, CallbackInfoReturnable<Boolean> cir) {
|
||||
private static void hookExtract(World world, Hopper hopper, CallbackInfoReturnable<Boolean> cir, Inventory inputInventory) {
|
||||
// Let vanilla handle the transfer if it found an inventory.
|
||||
if (inputInventory != null) return;
|
||||
|
||||
// Otherwise inject our transfer logic.
|
||||
BlockPos sourcePos = new BlockPos(hopper.getHopperX(), hopper.getHopperY() + 1.0D, hopper.getHopperZ());
|
||||
BlockEntity sourceBe = world.getBlockEntity(sourcePos);
|
||||
Storage<ItemVariant> source = ItemStorage.SIDED.find(world, sourcePos, null, sourceBe, Direction.DOWN);
|
||||
Storage<ItemVariant> source = ItemStorage.SIDED.find(world, sourcePos, Direction.DOWN);
|
||||
|
||||
if (source != null) {
|
||||
cir.setReturnValue(doTransfer(source, InventoryStorage.of(hopper, Direction.UP), sourceBe, hopper));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doTransfer(Storage<ItemVariant> from, Storage<ItemVariant> to, @Nullable Object invFrom, @Nullable Object invTo) {
|
||||
if (invFrom instanceof HopperBlockEntityAccessor hopperFrom && invTo instanceof HopperBlockEntityAccessor hopperTo) {
|
||||
// Hoppers have some special interactions (see HopperBlockEntity#transfer)
|
||||
boolean wasEmpty = hopperTo.isEmpty();
|
||||
boolean moved = StorageUtil.move(from, to, k -> true, 1, null) == 1;
|
||||
|
||||
if (moved && wasEmpty && hopperTo.fabric_getLastTickTime() >= hopperFrom.fabric_getLastTickTime()) {
|
||||
hopperTo.fabric_callSetCooldown(7);
|
||||
}
|
||||
|
||||
return moved;
|
||||
} else {
|
||||
return StorageUtil.move(from, to, k -> true, 1, null) == 1;
|
||||
long moved = StorageUtil.move(
|
||||
source,
|
||||
InventoryStorage.of(hopper, Direction.UP),
|
||||
iv -> true,
|
||||
1,
|
||||
null
|
||||
);
|
||||
cir.setReturnValue(moved == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
"DoubleInventoryAccessor",
|
||||
"DropperBlockMixin",
|
||||
"FluidMixin",
|
||||
"HopperBlockEntityAccessor",
|
||||
"HopperBlockEntityMixin",
|
||||
"ItemMixin"
|
||||
]
|
||||
|
|
|
@ -14,37 +14,38 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
|
||||
import java.util.Iterator;
|
||||
package net.fabricmc.fabric.test.transfer.ingame;
|
||||
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.Items;
|
||||
|
||||
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.TransferVariant;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.ExtractionOnlyStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleViewIterator;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
|
||||
|
||||
public class CreativeFluidStorage implements ExtractionOnlyStorage<FluidVariant>, StorageView<FluidVariant> {
|
||||
public static final CreativeFluidStorage WATER = new CreativeFluidStorage(FluidVariant.of(Fluids.WATER));
|
||||
public static final CreativeFluidStorage LAVA = new CreativeFluidStorage(FluidVariant.of(Fluids.LAVA));
|
||||
public class CreativeStorage<T extends TransferVariant<?>> implements ExtractionOnlyStorage<T>, SingleSlotStorage<T> {
|
||||
public static final CreativeStorage<FluidVariant> WATER = new CreativeStorage<>(FluidVariant.of(Fluids.WATER));
|
||||
public static final CreativeStorage<FluidVariant> LAVA = new CreativeStorage<>(FluidVariant.of(Fluids.LAVA));
|
||||
public static final CreativeStorage<ItemVariant> DIAMONDS = new CreativeStorage<>(ItemVariant.of(Items.DIAMOND));
|
||||
|
||||
private final FluidVariant infiniteFluid;
|
||||
private final T infiniteResource;
|
||||
|
||||
private CreativeFluidStorage(FluidVariant infiniteFluid) {
|
||||
this.infiniteFluid = infiniteFluid;
|
||||
private CreativeStorage(T infiniteResource) {
|
||||
this.infiniteResource = infiniteResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceBlank() {
|
||||
return infiniteFluid.isBlank();
|
||||
return infiniteResource.isBlank();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidVariant getResource() {
|
||||
return infiniteFluid;
|
||||
public T getResource() {
|
||||
return infiniteResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,21 +59,16 @@ public class CreativeFluidStorage implements ExtractionOnlyStorage<FluidVariant>
|
|||
}
|
||||
|
||||
@Override
|
||||
public long extract(FluidVariant resource, long maxAmount, TransactionContext transaction) {
|
||||
public long extract(T resource, long maxAmount, TransactionContext transaction) {
|
||||
StoragePreconditions.notBlankNotNegative(resource, maxAmount);
|
||||
|
||||
if (resource.equals(infiniteFluid)) {
|
||||
if (resource.equals(infiniteResource)) {
|
||||
return maxAmount;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<StorageView<FluidVariant>> iterator(TransactionContext transaction) {
|
||||
return SingleViewIterator.create(this, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVersion() {
|
||||
return 0;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.ingame;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.ingame;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.ingame;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
|
@ -31,7 +31,7 @@ public class FluidChuteBlockEntity extends BlockEntity {
|
|||
private int tickCounter = 0;
|
||||
|
||||
public FluidChuteBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(FluidTransferTest.FLUID_CHUTE_TYPE, pos, state);
|
||||
super(TransferTestInitializer.FLUID_CHUTE_TYPE, pos, state);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.ingame;
|
||||
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
|
||||
|
||||
public class TransferTestInitializer implements ModInitializer {
|
||||
public static final String MOD_ID = "fabric-transfer-api-v1-testmod";
|
||||
|
||||
private static final Block INFINITE_WATER_SOURCE = new Block(AbstractBlock.Settings.of(Material.METAL));
|
||||
private static final Block INFINITE_LAVA_SOURCE = new Block(AbstractBlock.Settings.of(Material.METAL));
|
||||
private static final Block FLUID_CHUTE = new FluidChuteBlock();
|
||||
private static final Item EXTRACT_STICK = new ExtractStickItem();
|
||||
public static BlockEntityType<FluidChuteBlockEntity> FLUID_CHUTE_TYPE;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
registerBlock(INFINITE_WATER_SOURCE, "infinite_water_source");
|
||||
registerBlock(INFINITE_LAVA_SOURCE, "infinite_lava_source");
|
||||
registerBlock(FLUID_CHUTE, "fluid_chute");
|
||||
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "extract_stick"), EXTRACT_STICK);
|
||||
|
||||
FLUID_CHUTE_TYPE = FabricBlockEntityTypeBuilder.create(FluidChuteBlockEntity::new, FLUID_CHUTE).build();
|
||||
Registry.register(Registry.BLOCK_ENTITY_TYPE, new Identifier(MOD_ID, "fluid_chute"), FLUID_CHUTE_TYPE);
|
||||
|
||||
FluidStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> CreativeStorage.WATER, INFINITE_WATER_SOURCE);
|
||||
FluidStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> CreativeStorage.LAVA, INFINITE_LAVA_SOURCE);
|
||||
|
||||
// Obsidian is now a trash can :-P
|
||||
ItemStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> TrashingStorage.ITEM, Blocks.OBSIDIAN);
|
||||
// And diamond ore blocks are an infinite source of diamonds! Yay!
|
||||
ItemStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> CreativeStorage.DIAMONDS, Blocks.DIAMOND_ORE);
|
||||
}
|
||||
|
||||
private static void registerBlock(Block block, String name) {
|
||||
Identifier id = new Identifier(MOD_ID, name);
|
||||
Registry.register(Registry.BLOCK, id, block);
|
||||
Registry.register(Registry.ITEM, id, new BlockItem(block, new Item.Settings().group(ItemGroup.MISC)));
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.ingame;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.unittests;
|
||||
|
||||
import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BOTTLE;
|
||||
import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET;
|
||||
|
@ -43,7 +43,7 @@ import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage;
|
|||
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
|
||||
|
||||
public class FluidItemTests {
|
||||
class FluidItemTests {
|
||||
public static void run() {
|
||||
testFluidItemApi();
|
||||
testWaterPotion();
|
|
@ -14,67 +14,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.unittests;
|
||||
|
||||
import static net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants.BUCKET;
|
||||
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
|
||||
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.storage.base.SingleSlotStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
|
||||
|
||||
public class FluidTransferTest implements ModInitializer {
|
||||
public static final String MOD_ID = "fabric-transfer-api-v1-testmod";
|
||||
|
||||
private static final Block INFINITE_WATER_SOURCE = new Block(AbstractBlock.Settings.of(Material.METAL));
|
||||
private static final Block INFINITE_LAVA_SOURCE = new Block(AbstractBlock.Settings.of(Material.METAL));
|
||||
private static final Block FLUID_CHUTE = new FluidChuteBlock();
|
||||
private static final Item EXTRACT_STICK = new ExtractStickItem();
|
||||
public static BlockEntityType<FluidChuteBlockEntity> FLUID_CHUTE_TYPE;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
registerBlock(INFINITE_WATER_SOURCE, "infinite_water_source");
|
||||
registerBlock(INFINITE_LAVA_SOURCE, "infinite_lava_source");
|
||||
registerBlock(FLUID_CHUTE, "fluid_chute");
|
||||
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "extract_stick"), EXTRACT_STICK);
|
||||
|
||||
FLUID_CHUTE_TYPE = FabricBlockEntityTypeBuilder.create(FluidChuteBlockEntity::new, FLUID_CHUTE).build();
|
||||
Registry.register(Registry.BLOCK_ENTITY_TYPE, new Identifier(MOD_ID, "fluid_chute"), FLUID_CHUTE_TYPE);
|
||||
|
||||
FluidStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> CreativeFluidStorage.WATER, INFINITE_WATER_SOURCE);
|
||||
FluidStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> CreativeFluidStorage.LAVA, INFINITE_LAVA_SOURCE);
|
||||
|
||||
// Obsidian is now a trash can :-P
|
||||
ItemStorage.SIDED.registerForBlocks((world, pos, state, be, direction) -> TrashingStorage.ITEM, Blocks.OBSIDIAN);
|
||||
|
||||
class FluidTests {
|
||||
public static void run() {
|
||||
testFluidStorage();
|
||||
testTransactionExceptions();
|
||||
ItemTests.run();
|
||||
FluidItemTests.run();
|
||||
}
|
||||
|
||||
private static void registerBlock(Block block, String name) {
|
||||
Identifier id = new Identifier(MOD_ID, name);
|
||||
Registry.register(Registry.BLOCK, id, block);
|
||||
Registry.register(Registry.ITEM, id, new BlockItem(block, new Item.Settings().group(ItemGroup.MISC)));
|
||||
}
|
||||
|
||||
private static final FluidVariant TAGGED_WATER, TAGGED_WATER_2, WATER, LAVA;
|
||||
|
@ -195,61 +149,4 @@ public class FluidTransferTest implements ModInitializer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int callbacksInvoked = 0;
|
||||
|
||||
/**
|
||||
* Make sure that transaction global state stays valid in case of exceptions.
|
||||
*/
|
||||
private static void testTransactionExceptions() {
|
||||
// Test exception inside the try.
|
||||
ensureException(() -> {
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
tx.addCloseCallback((t, result) -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Close.");
|
||||
});
|
||||
throw new RuntimeException("Inside try.");
|
||||
}
|
||||
}, "Exception should have propagated through the transaction.");
|
||||
if (callbacksInvoked != 1) throw new AssertionError("Callback should have been invoked.");
|
||||
|
||||
// Test exception inside the close.
|
||||
callbacksInvoked = 0;
|
||||
ensureException(() -> {
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
tx.addCloseCallback((t, result) -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Close 1.");
|
||||
});
|
||||
tx.addCloseCallback((t, result) -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Close 2.");
|
||||
});
|
||||
tx.addOuterCloseCallback(result -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Outer close 1.");
|
||||
});
|
||||
tx.addOuterCloseCallback(result -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Outer close 2.");
|
||||
});
|
||||
}
|
||||
}, "Exceptions in close callbacks should be propagated through the transaction.");
|
||||
if (callbacksInvoked != 4) throw new AssertionError("All 4 callbacks should have been invoked, only so many were: " + callbacksInvoked);
|
||||
|
||||
// Test that transaction state is still OK after these exceptions.
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureException(Runnable runnable, String message) {
|
||||
boolean failed = false;
|
||||
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (!failed) {
|
||||
throw new AssertionError(message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.fluid;
|
||||
package net.fabricmc.fabric.test.transfer.unittests;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
|
@ -38,7 +38,7 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
|
|||
/**
|
||||
* Tests for the item transfer APIs.
|
||||
*/
|
||||
public class ItemTests {
|
||||
class ItemTests {
|
||||
public static void run() {
|
||||
testInventoryWrappers();
|
||||
testLimitedStackCountInventory();
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.transfer.unittests;
|
||||
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
|
||||
|
||||
class TransactionExceptionsTests {
|
||||
public static void run() {
|
||||
testTransactionExceptions();
|
||||
}
|
||||
|
||||
private static int callbacksInvoked = 0;
|
||||
|
||||
/**
|
||||
* Make sure that transaction global state stays valid in case of exceptions.
|
||||
*/
|
||||
private static void testTransactionExceptions() {
|
||||
// Test exception inside the try.
|
||||
ensureException(() -> {
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
tx.addCloseCallback((t, result) -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Close.");
|
||||
});
|
||||
throw new RuntimeException("Inside try.");
|
||||
}
|
||||
}, "Exception should have propagated through the transaction.");
|
||||
if (callbacksInvoked != 1) throw new AssertionError("Callback should have been invoked.");
|
||||
|
||||
// Test exception inside the close.
|
||||
callbacksInvoked = 0;
|
||||
ensureException(() -> {
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
tx.addCloseCallback((t, result) -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Close 1.");
|
||||
});
|
||||
tx.addCloseCallback((t, result) -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Close 2.");
|
||||
});
|
||||
tx.addOuterCloseCallback(result -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Outer close 1.");
|
||||
});
|
||||
tx.addOuterCloseCallback(result -> {
|
||||
callbacksInvoked++; throw new RuntimeException("Outer close 2.");
|
||||
});
|
||||
}
|
||||
}, "Exceptions in close callbacks should be propagated through the transaction.");
|
||||
if (callbacksInvoked != 4) throw new AssertionError("All 4 callbacks should have been invoked, only so many were: " + callbacksInvoked);
|
||||
|
||||
// Test that transaction state is still OK after these exceptions.
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureException(Runnable runnable, String message) {
|
||||
boolean failed = false;
|
||||
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (!failed) {
|
||||
throw new AssertionError(message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,23 +14,19 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.transfer;
|
||||
package net.fabricmc.fabric.test.transfer.unittests;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
/**
|
||||
* Hopper accessors, for use in {@link HopperBlockEntityMixin}.
|
||||
*/
|
||||
@Mixin(HopperBlockEntity.class)
|
||||
public interface HopperBlockEntityAccessor extends Inventory {
|
||||
@Invoker("setCooldown")
|
||||
void fabric_callSetCooldown(int cooldown);
|
||||
|
||||
@Accessor("lastTickTime")
|
||||
long fabric_getLastTickTime();
|
||||
public class UnitTestsInitializer implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
TransactionExceptionsTests.run();
|
||||
FluidTests.run();
|
||||
ItemTests.run();
|
||||
FluidItemTests.run();
|
||||
LogManager.getLogger("fabric-transfer-api-v1 testmod").info("Transfer API unit tests successful.");
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@
|
|||
},
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.transfer.fluid.FluidTransferTest"
|
||||
"net.fabricmc.fabric.test.transfer.ingame.TransferTestInitializer",
|
||||
"net.fabricmc.fabric.test.transfer.unittests.UnitTestsInitializer"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue