diff --git a/build.gradle b/build.gradle index cfd29573aa..1cf00b3ebd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import net.fabricmc.loom.task.RemapJarTask + plugins { alias libs.plugins.architectury alias libs.plugins.architectury.loom apply false @@ -79,6 +81,18 @@ subprojects { annotationProcessor 'org.projectlombok:lombok:1.18.24' implementation 'com.google.code.findbugs:jsr305:3.0.2' + + // tests + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2' + testImplementation 'io.javalin:javalin:5.4.2' + testImplementation 'org.mockito:mockito-core:5.2.0' + } + + tasks.register('remapSlimJar', RemapJarTask) { + dependsOn(jar) + inputFile.set(jar.archiveFile) + addNestedDependencies = false + archiveClassifier.set("slim") } } diff --git a/common/build.gradle b/common/build.gradle index 67837c968b..000639c6e3 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -90,7 +90,7 @@ dependencies { modImplementation(fabric.ae2) { transitive = false } //AlmostUnified - modImplementation(fabric.almostUnified.common) + modCompileOnly(fabric.almostUnified.common) // KJS modCompileOnly fabric.kubejs diff --git a/common/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java b/common/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java index 716dfa6dc2..549959423c 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java +++ b/common/src/main/java/com/gregtechceu/gtceu/api/machine/trait/RecipeLogic.java @@ -22,6 +22,7 @@ import net.fabricmc.api.Environment; import net.minecraft.network.chat.Component; import net.minecraft.world.item.crafting.RecipeManager; +import org.jetbrains.annotations.VisibleForTesting; import javax.annotation.Nullable; import java.util.ArrayList; @@ -66,6 +67,7 @@ public enum Status { protected int fuelMaxTime; @Getter protected long timeStamp; + @Getter(onMethod_ = @VisibleForTesting) protected boolean recipeDirty; @Persisted @Getter diff --git a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerAccessor.java b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerAccessor.java index ccdf2bc328..bf15d71bc3 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerAccessor.java +++ b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerAccessor.java @@ -4,6 +4,7 @@ import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.RecipeType; +import org.jetbrains.annotations.VisibleForTesting; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @@ -18,4 +19,8 @@ public interface RecipeManagerAccessor { @Accessor("recipes") Map, Map>> getRawRecipes(); + + @Accessor("recipes") + @VisibleForTesting + void setRawRecipes(Map, Map>> recipes); } diff --git a/common/src/main/java/com/gregtechceu/gtceu/test/GTGameTests.java b/common/src/main/java/com/gregtechceu/gtceu/test/GTGameTests.java new file mode 100644 index 0000000000..1710288505 --- /dev/null +++ b/common/src/main/java/com/gregtechceu/gtceu/test/GTGameTests.java @@ -0,0 +1,68 @@ +package com.gregtechceu.gtceu.test; + +import appeng.server.testworld.PlotTestHelper; +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.test.api.machine.trait.ParallelLogicTest; +import com.gregtechceu.gtceu.test.api.machine.trait.RecipeLogicTest; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import com.mojang.datafixers.util.Pair; +import com.simibubi.create.infrastructure.gametest.CreateTestFunction; +import com.simibubi.create.infrastructure.gametest.tests.*; +import io.github.fabricators_of_create.porting_lib.gametest.infrastructure.ExtendedTestFunction; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestGenerator; +import net.minecraft.gametest.framework.GameTestRegistry; +import net.minecraft.gametest.framework.TestFunction; +import net.minecraft.world.level.block.Rotation; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class GTGameTests { + private static final Class[] testHolders = { + RecipeLogicTest.class, + ParallelLogicTest.class + }; + + @GameTestGenerator + public static Collection generateTests() { + return getTestsFrom(testHolders); + } + + public static Collection getTestsFrom(Class... classes) { + return Stream.of(classes) + .map(Class::getDeclaredMethods) + .flatMap(Stream::of) + .filter(method -> !method.isSynthetic() && method.getAnnotation(GameTest.class) != null) + .map(method -> Pair.of(method, method.getAnnotation(GameTest.class))) + .map(method -> new TestFunction( + "gtceu", + GTCEu.MOD_ID + "." + method.getFirst().getDeclaringClass().getSimpleName() + "." + method.getFirst().getName(), + method.getSecond().template(), + Rotation.NONE, + method.getSecond().timeoutTicks(), + method.getSecond().setupTicks(), + method.getSecond().required(), + method.getSecond().requiredSuccesses(), + method.getSecond().attempts(), + gameTestHelper -> { + try { + Object object = null; + if (!Modifier.isStatic(method.getFirst().getModifiers())) { + object = method.getFirst().getDeclaringClass().getConstructor().newInstance(); + } + method.getFirst().invoke(object, gameTestHelper); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + })) + .sorted(Comparator.comparing(TestFunction::getTestName)) + .toList(); + } +} diff --git a/common/src/main/java/com/gregtechceu/gtceu/test/api/machine/trait/ParallelLogicTest.java b/common/src/main/java/com/gregtechceu/gtceu/test/api/machine/trait/ParallelLogicTest.java new file mode 100644 index 0000000000..500a3e95ea --- /dev/null +++ b/common/src/main/java/com/gregtechceu/gtceu/test/api/machine/trait/ParallelLogicTest.java @@ -0,0 +1,127 @@ +package com.gregtechceu.gtceu.test.api.machine.trait; + +import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; +import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.data.GTMaterials; +import com.gregtechceu.gtceu.common.data.GTRecipeModifiers; +import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; +import com.lowdragmc.lowdraglib.side.fluid.IFluidTransfer; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class ParallelLogicTest { + + @GameTest(template = "gtceu:ebf") + public void getMaxRecipeMultiplier_FluidLimitTest(GameTestHelper helper) { + BlockEntity holder = helper.getBlockEntity(new BlockPos(1, 1, 0)); + if (!(holder instanceof MetaMachineBlockEntity atte)) { + helper.fail("wrong block at relative pos [1,1,0]!"); + return; + } + MetaMachine machine = atte.getMetaMachine(); + if (!(machine instanceof IRecipeLogicMachine rlm)) { + helper.fail("wrong machine in MetaMachineBlockEntity!"); + return; + } + + int parallelLimit = 4; + + // Create a simple recipe to be used for testing + GTRecipe recipe = GTRecipeBuilder.ofRaw() + .inputItems(new ItemStack(Blocks.COBBLESTONE)) + .inputFluids(GTMaterials.Acetone.getFluid(4000)) + .outputItems(new ItemStack(Blocks.STONE)) + .blastFurnaceTemp(1000) + .EUt(30).duration(100) + .buildRawRecipe(); + + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.IN, ItemRecipeCapability.CAP)).insertItem(0, new ItemStack(Blocks.COBBLESTONE, 16), false); + ((IFluidTransfer)rlm.getCapabilitiesProxy().get(IO.IN, FluidRecipeCapability.CAP)).fill(GTMaterials.Acetone.getFluid(8000), false); + + var paralleled = GTRecipeModifiers.accurateParallel(machine, recipe, parallelLimit, false); + + helper.assertTrue(paralleled.getB() == 2,"Expected Parallel amount to be 2, is %s.".formatted(paralleled.getB())); + + helper.succeed(); + } + + @GameTest(template = "gtceu:ebf") + public void getMaxRecipeMultiplier_LimitFailureTest(GameTestHelper helper) { + BlockEntity holder = helper.getBlockEntity(new BlockPos(1, 1, 0)); + if (!(holder instanceof MetaMachineBlockEntity atte)) { + helper.fail("wrong block at relative pos [1,1,0]!"); + return; + } + MetaMachine machine = atte.getMetaMachine(); + if (!(machine instanceof IRecipeLogicMachine rlm)) { + helper.fail("wrong machine in MetaMachineBlockEntity!"); + return; + } + + int parallelLimit = 4; + + // Create a simple recipe to be used for testing + GTRecipe recipe = GTRecipeBuilder.ofRaw() + .inputItems(new ItemStack(Blocks.COBBLESTONE)) + .inputFluids(GTMaterials.Acetone.getFluid(1000)) + .outputItems(new ItemStack(Blocks.STONE)) + .blastFurnaceTemp(1000) + .EUt(30).duration(100) + .buildRawRecipe(); + + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.IN, ItemRecipeCapability.CAP)).insertItem(0, new ItemStack(Blocks.COBBLESTONE, 16), false); + ((IFluidTransfer)rlm.getCapabilitiesProxy().get(IO.IN, FluidRecipeCapability.CAP)).fill(GTMaterials.Acetone.getFluid(8000), false); + + var paralleled = GTRecipeModifiers.accurateParallel(machine, recipe, parallelLimit, false); + + helper.assertTrue(paralleled == null || paralleled.getB() == 0, "Parallel is too high, should be 0, is %s.".formatted(paralleled.getB())); + + helper.succeed(); + } + + @GameTest(template = "gtceu:ebf") + public void getMaxRecipeMultiplier_ItemFailureTest(GameTestHelper helper) { + BlockEntity holder = helper.getBlockEntity(new BlockPos(1, 1, 0)); + if (!(holder instanceof MetaMachineBlockEntity atte)) { + helper.fail("wrong block at relative pos [1,1,0]!"); + return; + } + MetaMachine machine = atte.getMetaMachine(); + if (!(machine instanceof IRecipeLogicMachine rlm)) { + helper.fail("wrong machine in MetaMachineBlockEntity!"); + return; + } + + int parallelLimit = 4; + + // Create a simple recipe to be used for testing + GTRecipe recipe = GTRecipeBuilder.ofRaw() + .inputItems(new ItemStack(Blocks.COBBLESTONE)) + .inputFluids(GTMaterials.Acetone.getFluid(100)) + .outputItems(new ItemStack(Blocks.STONE)) + .blastFurnaceTemp(1000) + .EUt(30).duration(100) + .buildRawRecipe(); + + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.IN, ItemRecipeCapability.CAP)).insertItem(0, new ItemStack(Blocks.COBBLESTONE, 16), false); + ((IFluidTransfer)rlm.getCapabilitiesProxy().get(IO.IN, FluidRecipeCapability.CAP)).fill(GTMaterials.Naphtha.getFluid(8000), false); + + var paralleled = GTRecipeModifiers.accurateParallel(machine, recipe, parallelLimit, false); + + helper.assertTrue(paralleled == null || paralleled.getB() == 0, "Parallel is too high, should be 0, is %s.".formatted(paralleled.getB())); + + helper.succeed(); + } + + // TODO add the rest of https://github.com/GregTechCEu/GregTech/blob/master/src/test/java/gregtech/api/recipes/logic/ParallelLogicTest.java. +} diff --git a/common/src/main/java/com/gregtechceu/gtceu/test/api/machine/trait/RecipeLogicTest.java b/common/src/main/java/com/gregtechceu/gtceu/test/api/machine/trait/RecipeLogicTest.java new file mode 100644 index 0000000000..cee4b6c7ff --- /dev/null +++ b/common/src/main/java/com/gregtechceu/gtceu/test/api/machine/trait/RecipeLogicTest.java @@ -0,0 +1,114 @@ +package com.gregtechceu.gtceu.test.api.machine.trait; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.data.GTRecipeTypes; +import com.gregtechceu.gtceu.core.mixins.RecipeManagerAccessor; +import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; +import com.lowdragmc.lowdraglib.side.item.IItemTransfer; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.BeforeBatch; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.HashMap; + +public class RecipeLogicTest { + + private static boolean hasInjectedRecipe = false; + + @BeforeBatch(batch = GTCEu.MOD_ID) + public static void replaceRecipeManagerEntries(ServerLevel level) { + if (hasInjectedRecipe) return; + var recipes = new HashMap<>(((RecipeManagerAccessor) level.getRecipeManager()).getRawRecipes()); + ((RecipeManagerAccessor)level.getRecipeManager()).setRawRecipes(recipes); + recipes.replaceAll((k, v) -> new HashMap<>(v)); + } + + @GameTest(template = "gtceu:recipelogic") + public static void recipeLogicTest(GameTestHelper helper) { + // oops the BeforeBatch isn't registered. + RecipeLogicTest.replaceRecipeManagerEntries(helper.getLevel()); + + BlockEntity holder = helper.getBlockEntity(new BlockPos(0, 2, 0)); + if (!(holder instanceof MetaMachineBlockEntity atte)) { + helper.fail("wrong block at relative pos [0,1,0]!"); + return; + } + MetaMachine machine = atte.getMetaMachine(); + if (!(machine instanceof IRecipeLogicMachine rlm)) { + helper.fail("wrong machine in MetaMachineBlockEntity!"); + return; + } + + GTRecipe recipe = GTRecipeBuilder.ofRaw() + .id(GTCEu.id("test")) + .inputItems(new ItemStack(Blocks.COBBLESTONE)) + .outputItems(new ItemStack(Blocks.STONE)) + .EUt(1).duration(1) + .buildRawRecipe(); + // force insert the recipe into the manager. + + if (!hasInjectedRecipe) { + ((RecipeManagerAccessor) helper.getLevel().getRecipeManager()).getRawRecipes().get(GTRecipeTypes.CHEMICAL_RECIPES).put(GTCEu.id("test"), recipe); + hasInjectedRecipe = true; + } + + RecipeLogic arl = rlm.getRecipeLogic(); + + arl.findAndHandleRecipe(); + + // no recipe found + helper.assertFalse(arl.isActive(), "Recipe logic is active, even when it shouldn't be"); + helper.assertTrue(arl.getLastRecipe() == null, "Recipe logic has somehow found a recipe, when there should be none"); + + // put an item in the inventory that will trigger recipe recheck + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.IN, ItemRecipeCapability.CAP).get(0)).insertItem(0, new ItemStack(Blocks.COBBLESTONE, 16), false); + // Inputs change. did we detect it ? +// helper.assertTrue(arl.isRecipeDirty(), "Recipe is not dirty"); + arl.findAndHandleRecipe(); + helper.assertFalse(arl.getLastRecipe() == null, "Last recipe is empty, even though recipe logic should've found a recipe."); + helper.assertTrue(arl.isActive(), "Recipelogic is inactive, when it should be active."); + int stackCount = ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.IN, ItemRecipeCapability.CAP).get(0)).getStackInSlot(0).getCount(); + helper.assertTrue(stackCount == 15, "Count is wrong (should be 15, when it's %s".formatted(stackCount)); + + // Save a reference to the old recipe so we can make sure it's getting reused + GTRecipe prev = arl.getLastRecipe(); + + // Finish the recipe, the output should generate, and the next iteration should begin + arl.serverTick(); + helper.assertTrue(arl.getLastRecipe() == prev, "lastRecipe is wrong"); + helper.assertTrue(ItemStack.isSameItem(((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.OUT, ItemRecipeCapability.CAP).get(0)).getStackInSlot(0), + new ItemStack(Blocks.STONE, 1)), "wrong output stack."); + helper.assertTrue(arl.isActive(), "RecipeLogic is not active, when it should be."); + + // Complete the second iteration, but the machine stops because its output is now full + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.OUT, ItemRecipeCapability.CAP).get(0)).setStackInSlot(0, new ItemStack(Blocks.STONE, 63)); + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.OUT, ItemRecipeCapability.CAP).get(0)).setStackInSlot(1, new ItemStack(Blocks.STONE, 64)); + arl.serverTick(); + helper.assertFalse(arl.isActive(), "RecipeLogic is active, when it shouldn't be."); + + // Try to process again and get failed out because of full buffer. + arl.serverTick(); + helper.assertFalse(arl.isActive(), "Recipelogic is active, when it shouldn't be."); + + // Some room is freed in the output bus, so we can continue now. + ((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.OUT, ItemRecipeCapability.CAP).get(0)).setStackInSlot(1, ItemStack.EMPTY); + arl.serverTick(); +// helper.assertTrue(arl.isActive(), "Recipelogic is inactive."); + helper.assertTrue(ItemStack.isSameItem(((IItemTransfer)rlm.getCapabilitiesProxy().get(IO.OUT, ItemRecipeCapability.CAP).get(0)).getStackInSlot(0), new ItemStack(Blocks.STONE, 1)), "Wrong stack."); + + // Finish. + helper.succeed(); + } +} diff --git a/common/src/main/resources/data/gtceu/gametest/structures/ebf.snbt b/common/src/main/resources/data/gtceu/gametest/structures/ebf.snbt new file mode 100644 index 0000000000..91d1ae8fa5 --- /dev/null +++ b/common/src/main/resources/data/gtceu/gametest/structures/ebf.snbt @@ -0,0 +1,69 @@ +{ + DataVersion: 3465, + size: [3, 4, 4], + data: [ + {pos: [0, 0, 0], state: "gtceu:heatproof_machine_casing"}, + {pos: [0, 0, 1], state: "gtceu:heatproof_machine_casing"}, + {pos: [0, 0, 2], state: "gtceu:auto_maintenance_hatch{facing:south,server_tick:false}", nbt: {ForgeCaps: {}, cover: {}, id: "gtceu:auto_maintenance_hatch", paintingColor: -1}}, + {pos: [0, 0, 3], state: "minecraft:air"}, + {pos: [1, 0, 0], state: "gtceu:electric_blast_furnace{facing:north,server_tick:false}", nbt: {ForgeCaps: {}, activeRecipeType: 0, cover: {}, id: "gtceu:electric_blast_furnace", isFormed: 1b, isMuffled: 0b, paintingColor: -1, recipeLogic: {duration: 0, fuelMaxTime: 0, fuelTime: 0, progress: 0, status: "IDLE", totalContinuousRunningTime: 0L}}}, + {pos: [1, 0, 1], state: "gtceu:heatproof_machine_casing"}, + {pos: [1, 0, 2], state: "gtceu:heatproof_machine_casing"}, + {pos: [1, 0, 3], state: "minecraft:air"}, + {pos: [2, 0, 0], state: "gtceu:heatproof_machine_casing"}, + {pos: [2, 0, 1], state: "gtceu:heatproof_machine_casing"}, + {pos: [2, 0, 2], state: "gtceu:uv_energy_input_hatch{facing:south,server_tick:false}", nbt: {ForgeCaps: {}, cover: {}, energyContainer: {energyStored: 16777216L, isDistinct: 0b}, id: "gtceu:uv_energy_input_hatch", paintingColor: -1, workingEnabled: 1b}}, + {pos: [2, 0, 3], state: "gtceu:infinite_energy{server_tick:true}", nbt: {ForgeCaps: {}, active: 1b, amps: 2, cover: {}, energyIOPerSec: 0L, id: "gtceu:infinite_energy", paintingColor: -1, setTier: 8, source: 1b, voltage: 524288L}}, + {pos: [0, 1, 0], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [0, 1, 1], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [0, 1, 2], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [0, 1, 3], state: "minecraft:air"}, + {pos: [1, 1, 0], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [1, 1, 1], state: "minecraft:air"}, + {pos: [1, 1, 2], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [1, 1, 3], state: "minecraft:air"}, + {pos: [2, 1, 0], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [2, 1, 1], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [2, 1, 2], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [2, 1, 3], state: "minecraft:air"}, + {pos: [0, 2, 0], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [0, 2, 1], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [0, 2, 2], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [0, 2, 3], state: "minecraft:air"}, + {pos: [1, 2, 0], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [1, 2, 1], state: "minecraft:air"}, + {pos: [1, 2, 2], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [1, 2, 3], state: "minecraft:air"}, + {pos: [2, 2, 0], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [2, 2, 1], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [2, 2, 2], state: "gtceu:cupronickel_coil_block{active:false}"}, + {pos: [2, 2, 3], state: "minecraft:air"}, + {pos: [0, 3, 0], state: "gtceu:heatproof_machine_casing"}, + {pos: [0, 3, 1], state: "gtceu:heatproof_machine_casing"}, + {pos: [0, 3, 2], state: "gtceu:heatproof_machine_casing"}, + {pos: [0, 3, 3], state: "minecraft:air"}, + {pos: [1, 3, 0], state: "gtceu:lv_output_hatch{facing:north,server_tick:false}", nbt: {ForgeCaps: {}, cover: {}, id: "gtceu:lv_output_hatch", paintingColor: -1, tank: {isDistinct: 0b, storages: [{p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}]}, workingEnabled: 1b}}, + {pos: [1, 3, 1], state: "gtceu:iv_muffler_hatch{facing:up,server_tick:false}", nbt: {ForgeCaps: {}, cover: {}, id: "gtceu:iv_muffler_hatch", inventory: {Items: [], Size: 36}, paintingColor: -1}}, + {pos: [1, 3, 2], state: "gtceu:heatproof_machine_casing"}, + {pos: [1, 3, 3], state: "minecraft:air"}, + {pos: [2, 3, 0], state: "gtceu:lv_output_bus{facing:east,server_tick:false}", nbt: {ForgeCaps: {}, circuitInventory: {isDistinct: 0b, storage: {Items: [], Size: 0}}, cover: {}, id: "gtceu:lv_output_bus", inventory: {isDistinct: 0b, storage: {Items: [], Size: 4}}, paintingColor: -1, workingEnabled: 1b}}, + {pos: [2, 3, 1], state: "gtceu:lv_input_hatch{facing:east,server_tick:false}", nbt: {ForgeCaps: {}, cover: {}, id: "gtceu:lv_input_hatch", paintingColor: -1, tank: {isDistinct: 0b, storages: [{p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}]}, workingEnabled: 1b}}, + {pos: [2, 3, 2], state: "gtceu:lv_input_bus{facing:east,server_tick:false}", nbt: {ForgeCaps: {}, circuitInventory: {isDistinct: 0b, storage: {Items: [], Size: 1}}, cover: {}, id: "gtceu:lv_input_bus", inventory: {isDistinct: 0b, storage: {Items: [], Size: 4}}, paintingColor: -1, workingEnabled: 1b}}, + {pos: [2, 3, 3], state: "minecraft:air"} + ], + entities: [], + palette: [ + "gtceu:heatproof_machine_casing", + "gtceu:cupronickel_coil_block{active:false}", + "minecraft:air", + "gtceu:auto_maintenance_hatch{facing:south,server_tick:false}", + "gtceu:electric_blast_furnace{facing:north,server_tick:false}", + "gtceu:uv_energy_input_hatch{facing:south,server_tick:false}", + "gtceu:infinite_energy{server_tick:true}", + "gtceu:lv_output_hatch{facing:north,server_tick:false}", + "gtceu:iv_muffler_hatch{facing:up,server_tick:false}", + "gtceu:lv_output_bus{facing:east,server_tick:false}", + "gtceu:lv_input_hatch{facing:east,server_tick:false}", + "gtceu:lv_input_bus{facing:east,server_tick:false}" + ] +} diff --git a/common/src/main/resources/data/gtceu/gametest/structures/recipelogic.snbt b/common/src/main/resources/data/gtceu/gametest/structures/recipelogic.snbt new file mode 100644 index 0000000000..f0d9de6d45 --- /dev/null +++ b/common/src/main/resources/data/gtceu/gametest/structures/recipelogic.snbt @@ -0,0 +1,13 @@ +{ + DataVersion: 3465, + size: [1, 2, 1], + data: [ + {pos: [0, 0, 0], state: "gtceu:infinite_energy{server_tick:true}", nbt: {ForgeCaps: {}, active: 1b, amps: 1, cover: {}, energyIOPerSec: 0L, id: "gtceu:infinite_energy", paintingColor: -1, setTier: 1, source: 1b, voltage: 32L}}, + {pos: [0, 1, 0], state: "gtceu:lv_chemical_reactor{facing:north,server_tick:false}", nbt: {ForgeCaps: {}, activeRecipeType: 0, allowInputFromOutputSideFluids: 0b, allowInputFromOutputSideItems: 0b, autoOutputFluids: 0b, autoOutputItems: 0b, chargerInventory: {Items: [], Size: 1}, circuitInventory: {isDistinct: 0b, storage: {Items: [], Size: 1}}, cover: {}, energyContainer: {energyStored: 2048L, isDistinct: 0b}, exportFluids: {isDistinct: 0b, storages: [{p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}, {p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}]}, exportItems: {isDistinct: 0b, storage: {Items: [{Count: 32b, Slot: 0, id: "minecraft:stone"}], Size: 2}}, id: "gtceu:lv_chemical_reactor", importFluids: {isDistinct: 0b, storages: [{p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}, {p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}, {p: {Amount: 0L, FluidName: "minecraft:empty"}, t: 11b}]}, importItems: {isDistinct: 0b, storage: {Items: [], Size: 2}}, isMuffled: 0b, outputFacingFluids: "south", outputFacingItems: "south", overclockTier: 1, paintingColor: -1, recipeLogic: {duration: 0, fuelMaxTime: 0, fuelTime: 0, progress: 0, status: "IDLE", totalContinuousRunningTime: 0L}}} + ], + entities: [], + palette: [ + "gtceu:infinite_energy{server_tick:true}", + "gtceu:lv_chemical_reactor{facing:north,server_tick:false}" + ] +} diff --git a/common/src/main/resources/data/gtceu/structures/recipelogictest.recipelogic.nbt_ b/common/src/main/resources/data/gtceu/structures/recipelogictest.recipelogic.nbt_ new file mode 100644 index 0000000000..c8ca803abd Binary files /dev/null and b/common/src/main/resources/data/gtceu/structures/recipelogictest.recipelogic.nbt_ differ diff --git a/fabric/build.gradle b/fabric/build.gradle index df90521ee9..aa338a2700 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -34,6 +34,15 @@ loom { client { programArgs("--username", "Player") } + create("gameTestServer") { + server() + name("Minecraft Game Test") + source("main") + + property("fabric-api.gametest", "true") + property("fabric-api.gametest.command", "true") + } + } } diff --git a/fabric/src/main/java/com/gregtechceu/gtceu/fabric/GTCEuFabric.java b/fabric/src/main/java/com/gregtechceu/gtceu/fabric/GTCEuFabric.java index efb5e3a76e..70b874212e 100644 --- a/fabric/src/main/java/com/gregtechceu/gtceu/fabric/GTCEuFabric.java +++ b/fabric/src/main/java/com/gregtechceu/gtceu/fabric/GTCEuFabric.java @@ -6,14 +6,12 @@ import com.gregtechceu.gtceu.api.capability.fabric.GTCapability; import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; import com.gregtechceu.gtceu.api.recipe.ingredient.fabric.SizedIngredientImpl; -import com.gregtechceu.gtceu.client.TooltipsHandler; import com.gregtechceu.gtceu.common.ServerCommands; import com.gregtechceu.gtceu.common.fabric.CommonProxyImpl; import com.gregtechceu.gtceu.data.loader.fabric.FluidVeinLoaderImpl; import com.gregtechceu.gtceu.data.loader.fabric.OreDataLoaderImpl; import com.gregtechceu.gtceu.utils.TaskHandler; import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; diff --git a/fabric/src/main/java/com/gregtechceu/gtceu/integration/top/fabric/TheOneProbePluginImpl.java b/fabric/src/main/java/com/gregtechceu/gtceu/integration/top/fabric/TheOneProbePluginImpl.java deleted file mode 100644 index 1fbd6e4895..0000000000 --- a/fabric/src/main/java/com/gregtechceu/gtceu/integration/top/fabric/TheOneProbePluginImpl.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gregtechceu.gtceu.integration.top.fabric; - -public class TheOneProbePluginImpl /*implements ITheOneProbePlugin*/ { - -// public void onLoad(ITheOneProbe apiInstance) { -// TheOneProbePlugin.init(apiInstance); -// } - -} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 6f2ec69054..5f24ad6c0d 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -25,7 +25,7 @@ "com.gregtechceu.gtceu.client.fabric.ClientProxyImpl" ], "top_plugin": [ - "com.gregtechceu.gtceu.integration.top.fabric.TheOneProbePluginImpl" + "com.gregtechceu.gtceu.integration.top.TheOneProbePlugin" ], "rei_client": [ "com.gregtechceu.gtceu.integration.rei.GTREIPlugin" @@ -39,6 +39,9 @@ "fabric-datagen": [ "com.gregtechceu.gtceu.data.fabric.GregTechDatagen" ], + "fabric-gametest": [ + "com.gregtechceu.gtceu.test.GTGameTests" + ], "emi": [ "com.gregtechceu.gtceu.integration.emi.GTEMIPlugin" ], diff --git a/forge/build.gradle b/forge/build.gradle index af474e171a..10495179ee 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -52,6 +52,22 @@ loom { } } } + runs { + create("gameTestServer") { + server() + name("Minecraft Game Test") + source("main") + + property("forge.enabledGameTestNamespaces", mod_id) + property("forge.gameTestServer", "true") + + mods { + create(mod_id) { + sourceSet("main") + } + } + } + } } configurations { diff --git a/forge/src/main/java/com/gregtechceu/gtceu/test/forge/GTGameTestsImpl.java b/forge/src/main/java/com/gregtechceu/gtceu/test/forge/GTGameTestsImpl.java new file mode 100644 index 0000000000..af5c541317 --- /dev/null +++ b/forge/src/main/java/com/gregtechceu/gtceu/test/forge/GTGameTestsImpl.java @@ -0,0 +1,16 @@ +package com.gregtechceu.gtceu.test.forge; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.test.GTGameTests; +import net.minecraftforge.event.RegisterGameTestsEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber(modid = GTCEu.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class GTGameTestsImpl { + + @SubscribeEvent + public static void registerGameTests(RegisterGameTestsEvent event) { + event.register(GTGameTests.class); + } +}