mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-05 19:47:00 -04:00
Support stack aware recipe remainders (#2556)
* Support stack aware recipe remainders
* Fix checkstyle
* Remove all overwrites
* Add FabricItemStack and make RecipeRemainderHandler thread safe
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/impl/item/RecipeRemainderHandler.java
Co-authored-by: Technici4n <13494793+Technici4n@users.noreply.github.com>
* Remove hasRecipeRemainder, Update test mod and remove unneeded mixins
* Update fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java
Co-authored-by: Salvatore Peluso <info@devpelux.xyz>
* Avoid copying the ItemStack
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/AbstractFurnaceBlockEntityMixin.java
Co-authored-by: Salvatore Peluso <info@devpelux.xyz>
* Sneakily change duplicate keybinding to a less used key
* make everything thread safe and improve AbstractFurnaceBlockEntityMixin
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: Technici4n <13494793+Technici4n@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItemStack.java
Co-authored-by: Technici4n <13494793+Technici4n@users.noreply.github.com>
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: Salvatore Peluso <info@devpelux.xyz>
* clear thread local and change field prefix
* forgot the allow
* Update fabric-item-api-v1/src/main/java/net/fabricmc/fabric/api/item/v1/FabricItem.java
Co-authored-by: Salvatore Peluso <info@devpelux.xyz>
* Update fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/CustomDamageTest.java
Co-authored-by: Technici4n <13494793+Technici4n@users.noreply.github.com>
* Add FurnaceGameTest
* Change test keybind back to LShift
* Fix brewing stand remainder and fix nitpicks
* add code example to remainder javadoc
* Fixed and reformatted docs, changed recipe mixin behavior to store the remainder stack instead of the original stack, refactoring.
* Added gametests for brewing stand and recipe mixins, fixed furnace gametest compairing stacks with themselves.
* Use (0,1,0) position for game tests
* Review changes
Co-authored-by: apple502j <33279053+apple502j@users.noreply.github.com>
Co-authored-by: Technici4n <13494793+Technici4n@users.noreply.github.com>
Co-authored-by: Salvatore Peluso <info@devpelux.xyz>
Co-authored-by: modmuss50 <modmuss50@gmail.com>
(cherry picked from commit fa140d5976
)
This commit is contained in:
parent
8d7ffdee44
commit
d8cf4e5a10
18 changed files with 774 additions and 6 deletions
fabric-item-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/item/v1
impl/item
mixin/item
resources
testmod
java/net/fabricmc/fabric/test/item
resources
|
@ -4,3 +4,7 @@ version = getSubprojectVersion(project)
|
|||
moduleDependencies(project, [
|
||||
'fabric-api-base'
|
||||
])
|
||||
|
||||
dependencies {
|
||||
testmodImplementation project(path: ':fabric-content-registries-v0', configuration: 'namedElements')
|
||||
}
|
||||
|
|
|
@ -91,4 +91,36 @@ public interface FabricItem {
|
|||
default boolean isSuitableFor(ItemStack stack, BlockState state) {
|
||||
return ((Item) this).isSuitableFor(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a leftover item stack after {@code stack} is consumed in a recipe.
|
||||
* (This is also known as "recipe remainder".)
|
||||
* For example, using a lava bucket in a furnace as fuel will leave an empty bucket.
|
||||
*
|
||||
* <p>Here is an example for a recipe remainder that increments the item's damage.
|
||||
*
|
||||
* <pre>
|
||||
* if (stack.getDamage() < stack.getMaxDamage() - 1) {
|
||||
* ItemStack moreDamaged = stack.copy();
|
||||
* moreDamaged.setDamage(stack.getDamage() + 1);
|
||||
* return moreDamaged;
|
||||
* }
|
||||
*
|
||||
* return ItemStack.EMPTY;
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>This is a stack-aware version of {@link Item#getRecipeRemainder()}.
|
||||
*
|
||||
* <p>Note that simple item remainders can also be set via {@link Item.Settings#recipeRemainder(Item)}.
|
||||
*
|
||||
* <p>If you want to get a remainder for a stack,
|
||||
* is recommended to use the stack version of this method: {@link FabricItemStack#getRecipeRemainder()}.
|
||||
*
|
||||
* @param stack the consumed {@link ItemStack}
|
||||
* @return the leftover item stack
|
||||
*/
|
||||
default ItemStack getRecipeRemainder(ItemStack stack) {
|
||||
return ((Item) this).hasRecipeRemainder() ? ((Item) this).getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.api.item.v1;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/*
|
||||
* Fabric-provided extensions for {@link ItemStack}.
|
||||
* This interface is automatically implemented on all item stacks via Mixin and interface injection.
|
||||
*/
|
||||
public interface FabricItemStack {
|
||||
/**
|
||||
* Return a leftover item for use in recipes.
|
||||
*
|
||||
* <p>See {@link FabricItem#getRecipeRemainder(ItemStack)} for a more in depth description.
|
||||
*
|
||||
* <p>Stack-aware version of {@link Item#getRecipeRemainder()}.
|
||||
*
|
||||
* @return the leftover item
|
||||
*/
|
||||
default ItemStack getRecipeRemainder() {
|
||||
return ((ItemStack) this).getItem().getRecipeRemainder((ItemStack) this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.impl.item;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class RecipeRemainderHandler {
|
||||
public static final ThreadLocal<ItemStack> REMAINDER_STACK = new ThreadLocal<>();
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.mixin.item;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@Mixin(AbstractFurnaceBlockEntity.class)
|
||||
public abstract class AbstractFurnaceBlockEntityMixin {
|
||||
@Unique
|
||||
private static final ThreadLocal<ItemStack> REMAINDER_STACK = new ThreadLocal<>();
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItem()Lnet/minecraft/item/Item;"), locals = LocalCapture.CAPTURE_FAILHARD, allow = 1)
|
||||
private static void getStackRemainder(World world, BlockPos pos, BlockState state, AbstractFurnaceBlockEntity blockEntity, CallbackInfo ci, boolean bl, boolean bl2, ItemStack itemStack, Recipe recipe, int i) {
|
||||
REMAINDER_STACK.set(itemStack.getRecipeRemainder());
|
||||
}
|
||||
|
||||
@ModifyArg(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;set(ILjava/lang/Object;)Ljava/lang/Object;"), index = 1, allow = 1)
|
||||
private static <E> E setStackRemainder(E element) {
|
||||
E remainder = (E) REMAINDER_STACK.get();
|
||||
REMAINDER_STACK.remove();
|
||||
return remainder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.mixin.item;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@Mixin(BrewingStandBlockEntity.class)
|
||||
public class BrewingStandBlockEntityMixin {
|
||||
@Unique
|
||||
private static final ThreadLocal<ItemStack> REMAINDER_STACK = new ThreadLocal<>();
|
||||
|
||||
@Inject(method = "craft", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;decrement(I)V"), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
private static void captureItemStack(World world, BlockPos pos, DefaultedList<ItemStack> slots, CallbackInfo ci, ItemStack itemStack) {
|
||||
REMAINDER_STACK.set(itemStack.getRecipeRemainder());
|
||||
}
|
||||
|
||||
@Redirect(method = "craft", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;hasRecipeRemainder()Z"))
|
||||
private static boolean hasStackRecipeRemainder(Item instance) {
|
||||
return !REMAINDER_STACK.get().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injected after the {@link Item#getRecipeRemainder} to replace the old remainder with are new one.
|
||||
*/
|
||||
@ModifyVariable(method = "craft", at = @At(value = "STORE"), index = 4)
|
||||
private static ItemStack createStackRecipeRemainder(ItemStack old) {
|
||||
ItemStack remainder = REMAINDER_STACK.get();
|
||||
REMAINDER_STACK.remove();
|
||||
return remainder;
|
||||
}
|
||||
}
|
|
@ -37,12 +37,13 @@ import net.minecraft.entity.attribute.EntityAttributeModifier;
|
|||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemStack;
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.ModifyItemAttributeModifiersCallback;
|
||||
import net.fabricmc.fabric.impl.item.ItemExtensions;
|
||||
|
||||
@Mixin(ItemStack.class)
|
||||
public abstract class ItemStackMixin {
|
||||
public abstract class ItemStackMixin implements FabricItemStack {
|
||||
@Shadow public abstract Item getItem();
|
||||
|
||||
@Unique
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.mixin.item;
|
||||
|
||||
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.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
|
||||
import net.fabricmc.fabric.impl.item.RecipeRemainderHandler;
|
||||
|
||||
@Mixin(Recipe.class)
|
||||
public interface RecipeMixin<C extends Inventory> {
|
||||
@Inject(method = "getRemainder", at = @At(value = "INVOKE", target = "Lnet/minecraft/inventory/Inventory;getStack(I)Lnet/minecraft/item/ItemStack;"), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
default void captureStack(C inventory, CallbackInfoReturnable<DefaultedList<ItemStack>> cir, DefaultedList<ItemStack> defaultedList, int i) {
|
||||
RecipeRemainderHandler.REMAINDER_STACK.set(inventory.getStack(i).getRecipeRemainder());
|
||||
}
|
||||
|
||||
@Redirect(method = "getRemainder", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/Item;hasRecipeRemainder()Z"))
|
||||
private boolean hasStackRemainder(Item instance) {
|
||||
return !RecipeRemainderHandler.REMAINDER_STACK.get().isEmpty();
|
||||
}
|
||||
|
||||
@Redirect(method = "getRemainder", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;set(ILjava/lang/Object;)Ljava/lang/Object;"))
|
||||
private Object getStackRemainder(DefaultedList<ItemStack> inventory, int index, Object element) {
|
||||
Object remainder = inventory.set(index, RecipeRemainderHandler.REMAINDER_STACK.get());
|
||||
RecipeRemainderHandler.REMAINDER_STACK.remove();
|
||||
return remainder;
|
||||
}
|
||||
}
|
|
@ -3,10 +3,13 @@
|
|||
"package": "net.fabricmc.fabric.mixin.item",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"ItemStackMixin",
|
||||
"AbstractFurnaceBlockEntityMixin",
|
||||
"ArmorItemMixin",
|
||||
"BrewingStandBlockEntityMixin",
|
||||
"ItemMixin",
|
||||
"ItemStackMixin",
|
||||
"LivingEntityMixin",
|
||||
"ArmorItemMixin"
|
||||
"RecipeMixin"
|
||||
],
|
||||
"client": [
|
||||
"client.ClientPlayerInteractionManagerMixin",
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_1792": ["net/fabricmc/fabric/api/item/v1/FabricItem"]
|
||||
"net/minecraft/class_1792": ["net/fabricmc/fabric/api/item/v1/FabricItem"],
|
||||
"net/minecraft/class_1799": ["net/fabricmc/fabric/api/item/v1/FabricItemStack"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.test.item;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.PickaxeItem;
|
||||
import net.minecraft.item.ToolMaterials;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.potion.Potions;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
@ -27,11 +29,17 @@ import net.minecraft.util.registry.Registry;
|
|||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||
import net.fabricmc.fabric.api.registry.FuelRegistry;
|
||||
import net.fabricmc.fabric.test.item.mixin.BrewingRecipeRegistryAccessor;
|
||||
|
||||
public class CustomDamageTest implements ModInitializer {
|
||||
public static final Item WEIRD_PICK = new WeirdPick();
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "weird_pickaxe"), new WeirdPick());
|
||||
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "weird_pickaxe"), WEIRD_PICK);
|
||||
FuelRegistry.INSTANCE.add(WEIRD_PICK, 200);
|
||||
BrewingRecipeRegistryAccessor.callRegisterPotionRecipe(Potions.WATER, WEIRD_PICK, Potions.AWKWARD);
|
||||
}
|
||||
|
||||
public static final CustomDamageHandler WEIRD_DAMAGE_HANDLER = (stack, amount, entity, breakCallback) -> {
|
||||
|
@ -55,5 +63,17 @@ public class CustomDamageTest implements ModInitializer {
|
|||
int v = stack.getOrCreateNbt().getInt("weird");
|
||||
return super.getName(stack).shallowCopy().append(" (Weird Value: " + v + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getRecipeRemainder(ItemStack stack) {
|
||||
if (stack.getDamage() < stack.getMaxDamage() - 1) {
|
||||
ItemStack moreDamaged = stack.copy();
|
||||
moreDamaged.setCount(1);
|
||||
moreDamaged.setDamage(stack.getDamage() + 1);
|
||||
return moreDamaged;
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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.item.gametest;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.potion.PotionUtil;
|
||||
import net.minecraft.potion.Potions;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomDamageTest;
|
||||
|
||||
public class BrewingStandGameTest implements FabricGameTest {
|
||||
private static final int BREWING_TIME = 800;
|
||||
private static final BlockPos POS = new BlockPos(0, 1, 0);
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void basicBrewing(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.BREWING_STAND);
|
||||
BrewingStandBlockEntity blockEntity = (BrewingStandBlockEntity) Objects.requireNonNull(context.getBlockEntity(POS));
|
||||
|
||||
loadFuel(blockEntity, context);
|
||||
|
||||
prepareForBrewing(blockEntity, new ItemStack(Items.NETHER_WART, 8),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.WATER));
|
||||
|
||||
brew(blockEntity, context);
|
||||
assertInventory(blockEntity, "Testing vanilla brewing.",
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
new ItemStack(Items.NETHER_WART, 7),
|
||||
ItemStack.EMPTY);
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void vanillaRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.BREWING_STAND);
|
||||
BrewingStandBlockEntity blockEntity = (BrewingStandBlockEntity) Objects.requireNonNull(context.getBlockEntity(POS));
|
||||
|
||||
loadFuel(blockEntity, context);
|
||||
|
||||
prepareForBrewing(blockEntity, new ItemStack(Items.DRAGON_BREATH),
|
||||
PotionUtil.setPotion(new ItemStack(Items.SPLASH_POTION), Potions.AWKWARD));
|
||||
|
||||
brew(blockEntity, context);
|
||||
assertInventory(blockEntity, "Testing vanilla brewing recipe remainder.",
|
||||
PotionUtil.setPotion(new ItemStack(Items.LINGERING_POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.LINGERING_POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.LINGERING_POTION), Potions.AWKWARD),
|
||||
new ItemStack(Items.GLASS_BOTTLE),
|
||||
ItemStack.EMPTY);
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void fabricRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.BREWING_STAND);
|
||||
BrewingStandBlockEntity blockEntity = (BrewingStandBlockEntity) Objects.requireNonNull(context.getBlockEntity(POS));
|
||||
|
||||
loadFuel(blockEntity, context);
|
||||
|
||||
prepareForBrewing(blockEntity, new ItemStack(CustomDamageTest.WEIRD_PICK),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.WATER));
|
||||
|
||||
brew(blockEntity, context);
|
||||
assertInventory(blockEntity, "Testing fabric brewing recipe remainder.",
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
RecipeGameTest.withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 1),
|
||||
ItemStack.EMPTY);
|
||||
|
||||
prepareForBrewing(blockEntity, RecipeGameTest.withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 10),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.WATER));
|
||||
|
||||
brew(blockEntity, context);
|
||||
assertInventory(blockEntity, "Testing fabric brewing recipe remainder.",
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
RecipeGameTest.withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 11),
|
||||
ItemStack.EMPTY);
|
||||
|
||||
prepareForBrewing(blockEntity, RecipeGameTest.withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 31),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.WATER));
|
||||
|
||||
brew(blockEntity, context);
|
||||
assertInventory(blockEntity, "Testing fabric brewing recipe remainder.",
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.AWKWARD),
|
||||
ItemStack.EMPTY,
|
||||
ItemStack.EMPTY);
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
private void prepareForBrewing(BrewingStandBlockEntity blockEntity, ItemStack ingredient, ItemStack potion) {
|
||||
blockEntity.setStack(0, potion.copy());
|
||||
blockEntity.setStack(1, potion.copy());
|
||||
blockEntity.setStack(2, potion.copy());
|
||||
blockEntity.setStack(3, ingredient);
|
||||
}
|
||||
|
||||
private void assertInventory(BrewingStandBlockEntity blockEntity, String extraErrorInfo, ItemStack... stacks) {
|
||||
for (int i = 0; i < stacks.length; i++) {
|
||||
ItemStack currentStack = blockEntity.getStack(i);
|
||||
ItemStack expectedStack = stacks[i];
|
||||
|
||||
RecipeGameTest.assertStacks(currentStack, expectedStack, extraErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFuel(BrewingStandBlockEntity blockEntity, TestContext context) {
|
||||
blockEntity.setStack(4, new ItemStack(Items.BLAZE_POWDER));
|
||||
BrewingStandBlockEntity.tick(context.getWorld(), POS, context.getBlockState(POS), blockEntity);
|
||||
}
|
||||
|
||||
private void brew(BrewingStandBlockEntity blockEntity, TestContext context) {
|
||||
for (int i = 0; i < BREWING_TIME; i++) {
|
||||
BrewingStandBlockEntity.tick(context.getWorld(), POS, context.getBlockState(POS), blockEntity);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.item.gametest;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
|
||||
import net.minecraft.block.entity.FurnaceBlockEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomDamageTest;
|
||||
|
||||
public class FurnaceGameTest implements FabricGameTest {
|
||||
private static final int COOK_TIME = 200;
|
||||
private static final BlockPos POS = new BlockPos(0, 1, 0);
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void basicSmelt(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.FURNACE);
|
||||
FurnaceBlockEntity blockEntity = (FurnaceBlockEntity) Objects.requireNonNull(context.getBlockEntity(POS));
|
||||
|
||||
setInputs(blockEntity, new ItemStack(Blocks.COBBLESTONE, 8), new ItemStack(Items.COAL, 2));
|
||||
|
||||
cook(blockEntity, context, 1);
|
||||
assertInventory(blockEntity, "Testing vanilla smelting.",
|
||||
new ItemStack(Blocks.COBBLESTONE, 7),
|
||||
new ItemStack(Items.COAL, 1),
|
||||
new ItemStack(Blocks.STONE, 1));
|
||||
|
||||
cook(blockEntity, context, 7);
|
||||
assertInventory(blockEntity, "Testing vanilla smelting.",
|
||||
ItemStack.EMPTY,
|
||||
new ItemStack(Items.COAL, 1),
|
||||
new ItemStack(Blocks.STONE, 8));
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void vanillaRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.FURNACE);
|
||||
FurnaceBlockEntity blockEntity = (FurnaceBlockEntity) Objects.requireNonNull(context.getBlockEntity(POS));
|
||||
|
||||
setInputs(blockEntity, new ItemStack(Blocks.COBBLESTONE, 64), new ItemStack(Items.LAVA_BUCKET));
|
||||
|
||||
cook(blockEntity, context, 64);
|
||||
assertInventory(blockEntity, "Testing vanilla smelting recipe remainder.",
|
||||
ItemStack.EMPTY,
|
||||
new ItemStack(Items.BUCKET),
|
||||
new ItemStack(Blocks.STONE, 64));
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void fabricRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.FURNACE);
|
||||
FurnaceBlockEntity blockEntity = (FurnaceBlockEntity) Objects.requireNonNull(context.getBlockEntity(POS));
|
||||
|
||||
setInputs(blockEntity, new ItemStack(Blocks.COBBLESTONE, 32), new ItemStack(CustomDamageTest.WEIRD_PICK));
|
||||
|
||||
cook(blockEntity, context, 1);
|
||||
assertInventory(blockEntity, "Testing fabric smelting recipe remainder.",
|
||||
new ItemStack(Blocks.COBBLESTONE, 31),
|
||||
RecipeGameTest.withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 1),
|
||||
new ItemStack(Blocks.STONE, 1));
|
||||
|
||||
cook(blockEntity, context, 30);
|
||||
assertInventory(blockEntity, "Testing fabric smelting recipe remainder.",
|
||||
new ItemStack(Blocks.COBBLESTONE, 1),
|
||||
RecipeGameTest.withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 31),
|
||||
new ItemStack(Blocks.STONE, 31));
|
||||
|
||||
cook(blockEntity, context, 1);
|
||||
assertInventory(blockEntity, "Testing fabric smelting recipe remainder.",
|
||||
ItemStack.EMPTY,
|
||||
ItemStack.EMPTY,
|
||||
new ItemStack(Blocks.STONE, 32));
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
private void setInputs(FurnaceBlockEntity blockEntity, ItemStack ingredient, ItemStack fuel) {
|
||||
blockEntity.setStack(0, ingredient);
|
||||
blockEntity.setStack(1, fuel);
|
||||
}
|
||||
|
||||
private void assertInventory(FurnaceBlockEntity blockEntity, String extraErrorInfo, ItemStack... stacks) {
|
||||
for (int i = 0; i < stacks.length; i++) {
|
||||
ItemStack currentStack = blockEntity.getStack(i);
|
||||
ItemStack expectedStack = stacks[i];
|
||||
|
||||
RecipeGameTest.assertStacks(currentStack, expectedStack, extraErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void cook(FurnaceBlockEntity blockEntity, TestContext context, int items) {
|
||||
for (int i = 0; i < COOK_TIME * items; i++) {
|
||||
AbstractFurnaceBlockEntity.tick(context.getWorld(), POS, context.getBlockState(POS), blockEntity);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.item.gametest;
|
||||
|
||||
import net.minecraft.inventory.SimpleInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeSerializer;
|
||||
import net.minecraft.recipe.RecipeType;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.GameTestException;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomDamageTest;
|
||||
|
||||
public class RecipeGameTest implements FabricGameTest {
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void vanillaRemainderTest(TestContext context) {
|
||||
Recipe<SimpleInventory> testRecipe = createTestingRecipeInstance();
|
||||
|
||||
SimpleInventory inventory = new SimpleInventory(
|
||||
new ItemStack(Items.WATER_BUCKET),
|
||||
new ItemStack(Items.DIAMOND));
|
||||
|
||||
DefaultedList<ItemStack> remainderList = testRecipe.getRemainder(inventory);
|
||||
|
||||
assertStackList(remainderList, "Testing vanilla recipe remainder.",
|
||||
new ItemStack(Items.BUCKET),
|
||||
ItemStack.EMPTY);
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(structureName = EMPTY_STRUCTURE)
|
||||
public void fabricRemainderTest(TestContext context) {
|
||||
Recipe<SimpleInventory> testRecipe = createTestingRecipeInstance();
|
||||
|
||||
SimpleInventory inventory = new SimpleInventory(
|
||||
new ItemStack(CustomDamageTest.WEIRD_PICK),
|
||||
withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 10),
|
||||
withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 31),
|
||||
new ItemStack(Items.DIAMOND));
|
||||
|
||||
DefaultedList<ItemStack> remainderList = testRecipe.getRemainder(inventory);
|
||||
|
||||
assertStackList(remainderList, "Testing fabric recipe remainder.",
|
||||
withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 1),
|
||||
withDamage(new ItemStack(CustomDamageTest.WEIRD_PICK), 11),
|
||||
ItemStack.EMPTY,
|
||||
ItemStack.EMPTY);
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
private Recipe<SimpleInventory> createTestingRecipeInstance() {
|
||||
return new Recipe<>() {
|
||||
@Override
|
||||
public boolean matches(SimpleInventory inventory, World world) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack craft(SimpleInventory inventory) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fits(int width, int height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOutput() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeSerializer<?> getSerializer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeType<?> getType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void assertStackList(DefaultedList<ItemStack> stackList, String extraErrorInfo, ItemStack... stacks) {
|
||||
for (int i = 0; i < stackList.size(); i++) {
|
||||
ItemStack currentStack = stackList.get(i);
|
||||
ItemStack expectedStack = stacks[i];
|
||||
|
||||
assertStacks(currentStack, expectedStack, extraErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
static void assertStacks(ItemStack currentStack, ItemStack expectedStack, String extraErrorInfo) {
|
||||
if (currentStack.isEmpty() && expectedStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentStack.isItemEqual(expectedStack)) {
|
||||
throw new GameTestException("Item stacks dont match. " + extraErrorInfo);
|
||||
}
|
||||
|
||||
if (currentStack.getCount() != expectedStack.getCount()) {
|
||||
throw new GameTestException("Size doesnt match. " + extraErrorInfo);
|
||||
}
|
||||
|
||||
if (!ItemStack.areNbtEqual(currentStack, expectedStack)) {
|
||||
throw new GameTestException("Nbt doesnt match. " + extraErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
static ItemStack withDamage(ItemStack stack, int damage) {
|
||||
stack.setDamage(damage);
|
||||
return stack;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.item.mixin;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.recipe.BrewingRecipeRegistry;
|
||||
|
||||
@Mixin(BrewingRecipeRegistry.class)
|
||||
public interface BrewingRecipeRegistryAccessor {
|
||||
@Invoker
|
||||
static void callRegisterPotionRecipe(Potion input, Item item, Potion output) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shapeless",
|
||||
"ingredients": [
|
||||
{
|
||||
"item": "minecraft:diamond_ore"
|
||||
},
|
||||
{
|
||||
"item": "fabric-item-api-v1-testmod:weird_pickaxe"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"item": "minecraft:diamond",
|
||||
"count": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.test.item.mixin",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"BrewingRecipeRegistryAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -18,6 +18,14 @@
|
|||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.item.client.TooltipTests"
|
||||
],
|
||||
"fabric-gametest" : [
|
||||
"net.fabricmc.fabric.test.item.gametest.BrewingStandGameTest",
|
||||
"net.fabricmc.fabric.test.item.gametest.FurnaceGameTest",
|
||||
"net.fabricmc.fabric.test.item.gametest.RecipeGameTest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mixins": [
|
||||
"fabric-item-api-tests-v1.mixins.json"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue