Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.cyclops.integratedcrafting.gametest;

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.item.Items;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.neoforged.neoforge.gametest.GameTestHolder;
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
import org.cyclops.commoncapabilities.IngredientComponents;
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;
import org.cyclops.integratedcrafting.Reference;
import org.cyclops.integratedcrafting.api.crafting.CraftingJob;
import org.cyclops.integratedcrafting.api.crafting.CraftingJobDependencyGraph;
import org.cyclops.integratedcrafting.api.crafting.CraftingJobStatus;
import org.cyclops.integratedcrafting.api.network.ICraftingNetwork;
import org.cyclops.integratedcrafting.core.CraftingHelpers;
import org.cyclops.integratedcrafting.part.PartTypeInterfaceCraftingAttuned;
import org.cyclops.integrateddynamics.api.network.INetwork;
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;

import static org.cyclops.integratedcrafting.gametest.GameTestHelpersIntegratedCrafting.createBasicNetwork;

/**
* Tests for attuned crafting interfaces whose target changes while crafting.
*
* @author rubensworks
*/
@GameTestHolder(Reference.MOD_ID)
@PrefixGameTestTemplate(false)
public class GameTestsAttunedTarget {

public static final String TEMPLATE_EMPTY = "empty10";
public static final int TIMEOUT = 1000;
public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2);

/**
* A furnace notifies its neighbours whenever its contents change,
* which must not remove the jobs of its attuned crafting interface from the network.
*/
@GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT)
public void testAttunedTargetUpdatesKeepJobDependencies(GameTestHelper helper) {
GameTestHelpersIntegratedCrafting.INetworkPositions<PartTypeInterfaceCraftingAttuned.State> positions =
createBasicNetwork(helper, POS, true, Blocks.CRAFTING_TABLE, Blocks.FURNACE);
ChestBlockEntity chest = helper.getBlockEntity(POS.east());
chest.setItem(0, new ItemStack(Items.COBBLESTONE, 1));

PartTypeInterfaceCraftingAttuned.State interfaceCraftingTable = positions.interfaceStates().get(0);
PartTypeInterfaceCraftingAttuned.State interfaceFurnace = positions.interfaceStates().get(1);
CraftingJob[] jobs = new CraftingJob[2];

helper.runAfterDelay(5, () -> {
INetwork network = getNetwork(positions);
jobs[0] = CraftingHelpers.calculateAndScheduleCraftingJob(network, IPositionedAddonsNetwork.DEFAULT_CHANNEL,
IngredientComponents.ITEMSTACK, new ItemStack(Items.STONE_BUTTON), ItemMatch.ITEM, true, false,
CraftingHelpers.getGlobalCraftingJobIdentifier(), null);
helper.assertTrue(jobs[0] != null, "Crafting job could not be scheduled");
CraftingJobDependencyGraph graph = CraftingHelpers.getCraftingNetworkChecked(network).getCraftingJobDependencyGraph();
helper.assertValueEqual(graph.getDependencies(jobs[0]).size(), 1, "Dependencies of the button job");
jobs[1] = graph.getDependencies(jobs[0]).iterator().next();
});

helper.onEachTick(() -> {
if (jobs[1] == null) {
return;
}
ICraftingNetwork craftingNetwork = CraftingHelpers.getCraftingNetworkChecked(getNetwork(positions));

// As long as the furnace is smelting, the button job must keep waiting for it
if (interfaceFurnace.getCraftingJobHandler().getAllCraftingJobs().containsKey(jobs[1].getId())) {
helper.assertTrue(craftingNetwork.getCraftingJobDependencyGraph().getCraftingJob(jobs[1].getId()) != null,
"The smelting job left the dependency graph");
helper.assertValueEqual(interfaceCraftingTable.getCraftingJobStatus(craftingNetwork, jobs[0].getChannel(), jobs[0].getId()),
CraftingJobStatus.PENDING_DEPENDENCIES, "Status of the button job");
}

if (chest.countItem(Items.STONE_BUTTON) == 1) {
helper.succeed();
}
});
}

protected static INetwork getNetwork(GameTestHelpersIntegratedCrafting.INetworkPositions<?> positions) {
return NetworkHelpers.getNetworkChecked(positions.writer().getPos().getLevel(true),
positions.writer().getPos().getBlockPos(), positions.writer().getSide());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ public boolean shouldTriggerBlockRenderUpdate(@Nullable State oldPartState, @Nul

@Override
public void onBlockNeighborChange(INetwork network, IPartNetwork partNetwork, PartTarget target, State state, BlockGetter world, Block neighbourBlock, BlockPos neighbourBlockPos) {
boolean isNeighbourTarget = neighbourBlockPos.equals(target.getTarget().getPos().getBlockPos());
// Targets such as furnaces notify their neighbours on every operation, while their recipes remain the same.
// Only re-register if the target really changed, as this temporarily removes our crafting jobs from the network.
boolean isNeighbourTarget = neighbourBlockPos.equals(target.getTarget().getPos().getBlockPos())
&& state.hasTargetChanged();
boolean hadValidTarget = false;
if (isNeighbourTarget) {
hadValidTarget = state.hasValidTarget();
Expand Down Expand Up @@ -282,6 +285,20 @@ public boolean hasValidTarget() {
return this.hasValidTarget;
}

/**
* @return If the target gained or lost its recipe handler, or exposes other recipes than the ones that were last read.
*/
public boolean hasTargetChanged() {
if (getTarget() == null) {
return true;
}
Optional<IRecipeHandler> recipeHandler = getTargetRecipeHandler();
if (recipeHandler.isPresent() != this.hasValidTarget) {
return true;
}
return recipeHandler.isPresent() && !Lists.newArrayList(recipeHandler.get().getRecipes()).equals(this.recipes);
}

@Override
public void writeToNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
super.writeToNBT(valueDeseralizationContext, tag);
Expand Down
Loading