From dfd85072e2a07f5f307235c06428d54e24ea928a Mon Sep 17 00:00:00 2001 From: ExoHayvan <18trevor3695@gmail.com> Date: Sun, 19 Jul 2026 11:27:35 -0400 Subject: [PATCH 1/2] fix(neoforge): coordinate recipe load producers --- .../data/RecipeLoadCoordinator.java | 13 ++++++++++ .../event/ForgeEmcValueLoader.java | 5 ++++ .../mixin/RecipeManagerMixin.java | 25 +++++++++---------- .../data/RecipeLoadCoordinatorTest.java | 23 +++++++++++++++++ 4 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java create mode 100644 src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java diff --git a/src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java b/src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java new file mode 100644 index 0000000..03194fa --- /dev/null +++ b/src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java @@ -0,0 +1,13 @@ +package net.exohayvan.dissolver_enhanced.data; + +public final class RecipeLoadCoordinator { + public static final RecipeLoadCoordinator GLOBAL = new RecipeLoadCoordinator(); + + private final Object monitor = new Object(); + + public void runExclusive(Runnable load) { + synchronized (monitor) { + load.run(); + } + } +} diff --git a/src/main/java/net/exohayvan/dissolver_enhanced/event/ForgeEmcValueLoader.java b/src/main/java/net/exohayvan/dissolver_enhanced/event/ForgeEmcValueLoader.java index 06ae128..f95eec9 100644 --- a/src/main/java/net/exohayvan/dissolver_enhanced/event/ForgeEmcValueLoader.java +++ b/src/main/java/net/exohayvan/dissolver_enhanced/event/ForgeEmcValueLoader.java @@ -9,6 +9,7 @@ import net.minecraft.core.Holder; import net.exohayvan.dissolver_enhanced.DissolverEnhanced; import net.exohayvan.dissolver_enhanced.data.EMCValues; +import net.exohayvan.dissolver_enhanced.data.RecipeLoadCoordinator; import net.exohayvan.dissolver_enhanced.helpers.ItemHelper; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -38,6 +39,10 @@ public static void onServerStarted(ServerStartedEvent event) { } private static void loadRecipes(MinecraftServer server) { + RecipeLoadCoordinator.GLOBAL.runExclusive(() -> loadRecipesLocked(server)); + } + + private static void loadRecipesLocked(MinecraftServer server) { RECIPES.clear(); RECIPE_SOURCES.clear(); RECIPE_JSON.clear(); diff --git a/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java b/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java index fe1069c..70ee68e 100644 --- a/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java +++ b/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java @@ -18,6 +18,7 @@ import com.google.gson.JsonParseException; import net.exohayvan.dissolver_enhanced.DissolverEnhanced; import net.exohayvan.dissolver_enhanced.data.EMCValues; +import net.exohayvan.dissolver_enhanced.data.RecipeLoadCoordinator; import net.exohayvan.dissolver_enhanced.helpers.ItemHelper; import net.exohayvan.dissolver_enhanced.helpers.RecipeGenerator; import net.minecraft.core.HolderLookup; @@ -58,22 +59,20 @@ private void applyMixin(Object preparedRecipes, ResourceManager resourceManager, if (map == null) { return; } - - EMCValues.beginStartup(map.size()); - RECIPES.clear(); - RECIPE_SOURCES.clear(); - RECIPE_JSON.clear(); - STONE_CUTTER_LIST.clear(); - // let tag items load before looking through recipes - new Thread(() -> { + Map recipeSnapshot = new HashMap<>(map); + + new Thread(() -> RecipeLoadCoordinator.GLOBAL.runExclusive(() -> { + EMCValues.beginStartup(recipeSnapshot.size()); + RECIPES.clear(); + RECIPE_SOURCES.clear(); + RECIPE_JSON.clear(); + STONE_CUTTER_LIST.clear(); wait(800); - Iterator> recipeIterator = map.entrySet().iterator(); - while (recipeIterator.hasNext()) { - Map.Entry entry = recipeIterator.next(); + for (Map.Entry entry : recipeSnapshot.entrySet()) { try { getRecipe(entry); - }catch (Exception e) { + } catch (Exception e) { if (!getJsonRecipe(entry)) { EMCValues.incrementRecipesNotUnderstood(); } @@ -81,7 +80,7 @@ private void applyMixin(Object preparedRecipes, ResourceManager resourceManager, } EMCValues.recipesLoaded(RECIPES, RECIPE_SOURCES, RECIPE_JSON, STONE_CUTTER_LIST); - }).start(); + })).start(); } @SuppressWarnings("unchecked") diff --git a/src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java b/src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java new file mode 100644 index 0000000..b700a68 --- /dev/null +++ b/src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java @@ -0,0 +1,23 @@ +package net.exohayvan.dissolver_enhanced.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +class RecipeLoadCoordinatorTest { + @Test + void runsOverlappingLoadsExclusively() throws Exception { + RecipeLoadCoordinator coordinator = new RecipeLoadCoordinator(); + CountDownLatch firstStarted = new CountDownLatch(1); + CountDownLatch releaseFirst = new CountDownLatch(1); + CountDownLatch completed = new CountDownLatch(2); + AtomicInteger active = new AtomicInteger(); + AtomicInteger peak = new AtomicInteger(); + Thread first = new Thread(() -> coordinator.runExclusive(() -> { peak.accumulateAndGet(active.incrementAndGet(), Math::max); firstStarted.countDown(); try { releaseFirst.await(5, TimeUnit.SECONDS); } catch (InterruptedException exception) { Thread.currentThread().interrupt(); } active.decrementAndGet(); completed.countDown(); })); + Thread second = new Thread(() -> coordinator.runExclusive(() -> { peak.accumulateAndGet(active.incrementAndGet(), Math::max); active.decrementAndGet(); completed.countDown(); })); + first.start(); assertThat(firstStarted.await(5, TimeUnit.SECONDS)).isTrue(); second.start(); Thread.sleep(100); assertThat(peak.get()).isEqualTo(1); releaseFirst.countDown(); assertThat(completed.await(5, TimeUnit.SECONDS)).isTrue(); assertThat(peak.get()).isEqualTo(1); + } +} From ce77d225e914eea5eeb17967f392b845f322b827 Mon Sep 17 00:00:00 2001 From: ExoHayvan <18trevor3695@gmail.com> Date: Sun, 19 Jul 2026 11:31:49 -0400 Subject: [PATCH 2/2] ci: use common branch for aggregate builds --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b96ec9..f963f70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,14 +10,14 @@ permissions: contents: read env: - COMMON_BRANCH: main + COMMON_BRANCH: common COMMON_DIR: Dissolver Enhanced (Common) TARGET_DIR: Dissolver Enhanced (Target) jobs: build-common: name: Build Common - if: ${{ github.ref_name == 'main' }} + if: ${{ github.ref_name == 'common' }} runs-on: ubuntu-22.04 steps: - name: Checkout Common @@ -59,7 +59,7 @@ jobs: discover-version-branches: name: Discover Version Branches - if: ${{ github.ref_name == 'main' }} + if: ${{ github.ref_name == 'common' }} runs-on: ubuntu-22.04 outputs: branches: ${{ steps.branches.outputs.branches }} @@ -81,7 +81,7 @@ jobs: build-all-version-branches: name: Build ${{ matrix.branch }} - if: ${{ github.ref_name == 'main' && needs.discover-version-branches.outputs.branches != '[]' }} + if: ${{ github.ref_name == 'common' && needs.discover-version-branches.outputs.branches != '[]' }} needs: discover-version-branches runs-on: ubuntu-22.04 strategy: @@ -186,7 +186,7 @@ jobs: package-all-builds: name: Package All Builds - if: ${{ github.ref_name == 'main' && needs.build-common.result == 'success' && needs.build-all-version-branches.result == 'success' }} + if: ${{ github.ref_name == 'common' && needs.build-common.result == 'success' && needs.build-all-version-branches.result == 'success' }} needs: - build-common - build-all-version-branches