From 4b59528b31612b6c32429c62933ba98a5e039a48 Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Wed, 11 Sep 2024 00:23:14 +0100 Subject: [PATCH] Fix race in Contraption simplifiedEntityColliders update This issue is most obvious in elevators with multiple doors. When the collider gets invalidated multiple times in quick succession, multiple futures are created. If a future which was started before all door state updates were finalized completed after all other futures, the final simplifiedEntityColliders would be outdated and invalid (containing ghost doors, or in the case of an elevator in motion, missing door collisions). This commit addresses this issue in two ways: First by cancelling any existing future to ensure that any future which completes was started after the most recent invalidation. Second by removing the null assignment of simplifiedEntityColliderProvider from the future. This prevents the future from becoming null after a null check and before a cancellation (the only time where the null value matters). Cancelling a future twice is not an issue so there's no need to track if the future is null other than to avoid a null dereference. --- .../com/simibubi/create/content/contraptions/Contraption.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java index f38e1c78ee..d004d9ae23 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java @@ -1386,6 +1386,9 @@ public void invalidateColliders() { private void gatherBBsOffThread() { getContraptionWorld(); + if (simplifiedEntityColliderProvider != null) { + simplifiedEntityColliderProvider.cancel(false); + } simplifiedEntityColliderProvider = CompletableFuture.supplyAsync(() -> { VoxelShape combinedShape = Shapes.empty(); for (Entry entry : blocks.entrySet()) { @@ -1402,7 +1405,6 @@ private void gatherBBsOffThread() { }) .thenAccept(r -> { simplifiedEntityColliders = Optional.of(r); - simplifiedEntityColliderProvider = null; }); }