Skip to content

Commit

Permalink
Fix flaky gametests (#8103)
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte authored Aug 3, 2024
1 parent ee6e8d7 commit 73f4f6e
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/main/java/appeng/block/misc/LightDetectorBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {

@Override
public int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction side) {
if (level instanceof Level && this.getBlockEntity(level, pos).isReady()) {
if (level instanceof Level && this.getBlockEntity(level, pos).isExposedToLight()) {
// FIXME: This is ... uhm... fishy
return ((Level) level).getMaxLocalRawBrightness(pos) - 6;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public LightDetectorBlockEntity(BlockEntityType<?> blockEntityType, BlockPos pos
super(blockEntityType, pos, blockState);
}

public boolean isReady() {
public boolean isExposedToLight() {
return this.lastLight > 0;
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/appeng/me/service/P2PService.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public static P2PService get(IGrid grid) {
}

private final IGrid myGrid;
private final HashMap<Short, P2PTunnelPart> inputs = new HashMap<>();
private final Multimap<Short, P2PTunnelPart> outputs = LinkedHashMultimap.create();
private final HashMap<Short, P2PTunnelPart<?>> inputs = new HashMap<>();
private final Multimap<Short, P2PTunnelPart<?>> outputs = LinkedHashMultimap.create();
private final Random frequencyGenerator;

public P2PService(IGrid g) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public static void create(PlotBuilder plot) {
plot.cable("4 0 [6,9]", AEParts.SMART_DENSE_CABLE);

// Add post-processing action once the grid is up and running
plot.afterGridInitAt("4 0 4", (grid, gridNode) -> {
plot.afterGridInitAt(new BlockPos(4, 0, 4), (grid, gridNode) -> {
var level = gridNode.getLevel();
var patterns = new ArrayList<ItemStack>();
// Crafting pattern table with substitutions enabled and some items that are NOT in storage
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/appeng/server/testplots/P2PPlotHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@

import appeng.api.networking.IGrid;
import appeng.api.parts.PartHelper;
import appeng.blockentity.networking.CableBusBlockEntity;
import appeng.core.definitions.ItemDefinition;
import appeng.items.parts.PartItem;
import appeng.me.service.P2PService;
import appeng.parts.AEBasePart;
import appeng.parts.p2p.P2PTunnelPart;
import appeng.server.testworld.PlotBuilder;
import appeng.util.SettingsFrom;
Expand All @@ -28,9 +28,9 @@ public static <T extends P2PTunnelPart<?>> void placeTunnel(PlotBuilder plot, It
plot.cable(origin);
plot.cable(origin.west()).part(Direction.WEST, tunnel);
plot.cable(origin.east()).part(Direction.EAST, tunnel);
plot.afterGridInitAt(origin, (grid, gridNode) -> {
BlockPos absOrigin = ((AEBasePart) gridNode.getOwner()).getBlockEntity().getBlockPos();

plot.addPostInitAction((level, player, absOrigin) -> {
var be = (CableBusBlockEntity) level.getBlockEntity(absOrigin);
var grid = be.getCableBus().getPart(null).getGridNode().getGrid();
linkTunnels(grid, tunnel.get().getPartClass(), absOrigin.west(), absOrigin.east());
});
}
Expand Down
28 changes: 16 additions & 12 deletions src/main/java/appeng/server/testplots/SpatialTestPlots.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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.ButtonBlock;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;

Expand Down Expand Up @@ -121,33 +122,36 @@ public static void storeAndRetrieveEntities(PlotBuilder plot) {

// Woosh!
plot.test(helper -> {
// Remove all entities
// Spawn cow + chicken
helper.startSequence()
.thenExecute(() -> {
// Remove all entities
// Spawn cow + chicken
helper.killAllEntities();
helper.spawn(EntityType.CHICKEN, chickenPos.above());
helper.spawnItem(Items.OBSIDIAN, chickenPos.getX() + .5f, chickenPos.getY() + .5f,
chickenPos.getZ() + .5f);
helper.spawn(EntityType.CHICKEN, Vec3.atBottomCenterOf(chickenPos.above()));
helper.spawnItem(Items.OBSIDIAN, Vec3.atCenterOf(chickenPos));
})
.thenWaitUntil(() -> {
helper.assertItemEntityCountIs(Items.OBSIDIAN, chickenPos, 1, 1);
helper.assertEntitiesPresent(EntityType.CHICKEN, chickenPos, 1, 1);
})
.thenIdle(5)
// Wait for the grid to become available
.thenWaitUntil(helper::checkAllInitialized)
.thenExecute(() -> helper.pressButton(buttonPos))
.thenIdle(5)
.thenExecute(() -> {
.thenWaitUntil(() -> {
// Validate, that the chicken and obsidian are gone
helper.assertItemEntityCountIs(Items.OBSIDIAN, chickenPos, 1, 0);
helper.assertEntitiesPresent(EntityType.CHICKEN, chickenPos, 0, 1);

})
.thenExecute(() -> {
// Swap the cell back to the input slot and trigger a transition
var cell = getCellFromSpatialIoPortOutput(helper, ioPortPos);
insertCell(helper, ioPortPos, cell);
})
// Wait for button to reset
.thenIdle(25)
.thenWaitUntil(() -> helper.assertBlockProperty(buttonPos, ButtonBlock.POWERED, false))
// Transition back
.thenExecute(() -> helper.pressButton(buttonPos))
.thenIdle(5)
.thenExecute(() -> {
.thenWaitUntil(() -> {
// Validate that the chicken and obsidian are back
helper.assertItemEntityCountIs(Items.OBSIDIAN, chickenPos, 1, 1);
helper.assertEntitiesPresent(EntityType.CHICKEN, chickenPos, 1, 1);
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/appeng/server/testplots/TestPlots.java
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public static void allTerminals(PlotBuilder plot) {
plot.part("0 [0,8] 0", Direction.WEST, AEParts.CABLE_ANCHOR);
plot.block("[-1,0] 5 0", AEBlocks.CONTROLLER);
plot.storageDrive(new BlockPos(0, 5, 1));
plot.afterGridInitAt("0 5 1", (grid, gridNode) -> {
plot.afterGridInitAt(new BlockPos(0, 5, 1), (grid, gridNode) -> {
var enchantedPickaxe = createEnchantedPickaxe(gridNode.getLevel());
var storage = grid.getStorageService().getInventory();
var src = new BaseActionSource();
Expand Down Expand Up @@ -657,7 +657,7 @@ public static void maxChannelsAdHocTest(PlotBuilder plot) {
.part(Direction.EAST, AEParts.PATTERN_PROVIDER)
.part(Direction.WEST, AEParts.PATTERN_PROVIDER);

plot.afterGridExistsAt("0 0 0", (grid, node) -> {
plot.afterGridExistsAt(BlockPos.ZERO, (grid, node) -> {
// This has so many nodes it needs infinite mode
((PathingService) grid.getPathingService()).setForcedChannelMode(ChannelMode.INFINITE);

Expand Down
89 changes: 75 additions & 14 deletions src/main/java/appeng/server/testworld/GridInitHelper.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package appeng.server.testworld;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;

import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntity;

import appeng.api.networking.IGrid;
import appeng.api.networking.IGridNode;
Expand All @@ -13,41 +17,98 @@

final class GridInitHelper {

static void doAfterGridInit(ServerLevel level, BlockPos pos, boolean waitForActive,
static void doAfterGridInit(ServerLevel level, List<BlockPos> positions, boolean waitForActive,
BiConsumer<IGrid, IGridNode> consumer) {
Runnable delayedAction = new Runnable() {
private int attempts = 120;

@Override
public void run() {
// Check if there's a grid node there
var be = level.getBlockEntity(pos);
IGridNode gridNode = null;
if (be instanceof IGridConnectedBlockEntity host) {
gridNode = host.getMainNode().getNode();
} else if (be instanceof CableBusBlockEntity cableBus) {
var centerPart = cableBus.getCableBus().getPart(null);
if (centerPart != null) {
gridNode = centerPart.getGridNode();
List<IGridNode> gridNodes = new ArrayList<>();

for (var position : positions) {
var be = level.getBlockEntity(position);
if (be instanceof IGridConnectedBlockEntity host) {
gridNodes.add(host.getMainNode().getNode());
} else if (be instanceof CableBusBlockEntity cableBus) {
var centerPart = cableBus.getCableBus().getPart(null);
if (centerPart != null) {
gridNodes.add(centerPart.getGridNode());
} else {
return; // Stop -> not eligible
}
} else {
return; // Stop -> not eligible
}
} else {
return; // Stop -> not eligible
}

if (gridNode == null || waitForActive && !gridNode.isActive()) {
if (gridNodes.stream().anyMatch(Objects::isNull)
|| waitForActive && !gridNodes.stream().allMatch(IGridNode::isActive)) {
if (--attempts > 0) {
TickHandler.instance().addCallable(level, this);
} else {
throw new IllegalStateException("Couldn't access grid node @ " + pos);
throw new IllegalStateException("Couldn't access grid nodes @ " + positions);
}
} else {
consumer.accept(gridNode.getGrid(), gridNode);
consumer.accept(gridNodes.getFirst().getGrid(), gridNodes.getFirst());
}
}
};
TickHandler.instance().addCallable(level, delayedAction);
}

static void doAfterGridInit(ServerLevel level, List<BlockEntity> blockEntities, boolean waitForActive,
Runnable callback) {
Runnable delayedAction = new Runnable() {
private int attempts = 120;

@Override
public void run() {
var notInitialized = new ArrayList<BlockEntity>();
var notActive = new ArrayList<BlockEntity>();

for (var be : blockEntities) {
if (be.isRemoved()) {
return;
}

if (be instanceof IGridConnectedBlockEntity host) {
var mainNode = host.getMainNode();
if (mainNode == null) {
notInitialized.add(be);
notActive.add(be);
break;
} else if (!mainNode.isActive()) {
notActive.add(be);
}
} else if (be instanceof CableBusBlockEntity cableBus) {
var centerPart = cableBus.getCableBus().getPart(null);
if (centerPart != null) {
var mainNode = centerPart.getGridNode();
if (mainNode == null) {
notInitialized.add(be);
notActive.add(be);
break;
} else if (!mainNode.isActive()) {
notActive.add(be);
}
}
}
}

if (!notInitialized.isEmpty() || waitForActive && !notActive.isEmpty()) {
if (--attempts > 0) {
TickHandler.instance().addCallable(level, this);
} else {
throw new IllegalStateException("Couldn't wait for grid to initialize. Not initialized: "
+ notInitialized + ". Not active: " + notActive);
}
} else {
callback.run();
}
}
};
TickHandler.instance().addCallable(level, delayedAction);
}
}
22 changes: 22 additions & 0 deletions src/main/java/appeng/server/testworld/Plot.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
Expand All @@ -20,6 +21,7 @@ public class Plot implements PlotBuilder {

private final List<BuildAction> buildActions = new ArrayList<>();
private final List<PostBuildAction> postBuildActions = new ArrayList<>();
private final List<PostBuildAction> postInitActions = new ArrayList<>();

private Test test;

Expand Down Expand Up @@ -52,6 +54,11 @@ public void addPostBuildAction(PostBuildAction action) {
postBuildActions.add(action);
}

@Override
public void addPostInitAction(PostBuildAction action) {
postInitActions.add(action);
}

@Override
public PlotBuilder transform(Function<BoundingBox, BoundingBox> transform) {
return new TransformingPlotBuilder(this, transform);
Expand Down Expand Up @@ -95,6 +102,21 @@ public void build(ServerLevel level, Player player, BlockPos origin, List<Entity
for (var action : postBuildActions) {
action.postBuild(level, player, origin);
}

// Gather all block entities in the built area
if (!postInitActions.isEmpty()) {
var blockEntities = BlockPos
.betweenClosedStream(getBounds().moved(origin.getX(), origin.getY(), origin.getZ()))
.map(level::getBlockEntity)
.filter(Objects::nonNull)
.toList();

GridInitHelper.doAfterGridInit(level, blockEntities, false, () -> {
for (var action : postInitActions) {
action.postBuild(level, player, origin);
}
});
}
}

public Test getTest() {
Expand Down
17 changes: 10 additions & 7 deletions src/main/java/appeng/server/testworld/PlotBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
Expand Down Expand Up @@ -45,14 +46,16 @@
public interface PlotBuilder {

@FunctionalInterface
public interface PostBuildAction {
interface PostBuildAction {
void postBuild(ServerLevel level, Player player, BlockPos origin);
}

void addBuildAction(BuildAction action);

void addPostBuildAction(PostBuildAction action);

void addPostInitAction(PostBuildAction action);

BoundingBox bb(String def);

static String posToBb(BlockPos pos) {
Expand Down Expand Up @@ -296,23 +299,23 @@ default PlotBuilder offset(BlockPos pos) {
/**
* Runs a given callback once the grid has been initialized at all viable nodes in the given bounding box.
*/
default void afterGridInitAt(String bb, BiConsumer<IGrid, IGridNode> consumer) {
addBuildAction(new PostGridInitAction(bb(bb), consumer, true));
default void afterGridInitAt(List<BlockPos> positions, BiConsumer<IGrid, IGridNode> consumer) {
addBuildAction(new PostGridInitAction(positions, consumer, true));
}

default void afterGridInitAt(BlockPos pos, BiConsumer<IGrid, IGridNode> consumer) {
afterGridInitAt(posToBb(pos), consumer);
afterGridInitAt(List.of(pos), consumer);
}

/**
* Runs a given callback once the grid is available at all viable nodes in the given bounding box.
*/
default void afterGridExistsAt(String bb, BiConsumer<IGrid, IGridNode> consumer) {
addBuildAction(new PostGridInitAction(bb(bb), consumer, false));
default void afterGridExistsAt(List<BlockPos> positions, BiConsumer<IGrid, IGridNode> consumer) {
addBuildAction(new PostGridInitAction(positions, consumer, false));
}

default void afterGridExistsAt(BlockPos pos, BiConsumer<IGrid, IGridNode> consumer) {
afterGridExistsAt(posToBb(pos), consumer);
afterGridExistsAt(List.of(pos), consumer);
}

/**
Expand Down
Loading

0 comments on commit 73f4f6e

Please sign in to comment.