From a19d423d6eb4649fd5871ff7eca78a6bf1b46410 Mon Sep 17 00:00:00 2001
From: screret <68943070+screret@users.noreply.github.com>
Date: Fri, 31 Jul 2026 19:38:25 +0300
Subject: [PATCH 01/19] Fix half of #3981 not being ported to 1.21
somebody-a forgot to merge half of #3981. not having it broke reloading.
---
.../recipe/lookup/RecipeManagerHandler.java | 66 +++++++++++++++++++
.../core/mixins/RecipeManagerLateMixin.java | 52 +++++----------
2 files changed, 83 insertions(+), 35 deletions(-)
create mode 100644 src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java
diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java
new file mode 100644
index 00000000000..f5e5bd08c5d
--- /dev/null
+++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java
@@ -0,0 +1,66 @@
+package com.gregtechceu.gtceu.api.recipe.lookup;
+
+import com.gregtechceu.gtceu.api.recipe.GTRecipe;
+import com.gregtechceu.gtceu.api.recipe.GTRecipeType;
+
+import net.minecraft.world.item.crafting.RecipeHolder;
+import net.minecraft.world.item.crafting.RecipeType;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Internal class handling adding recipes to GT's lookup system.
+ *
+ * Intended for use by {@link com.gregtechceu.gtceu.core.mixins.RecipeManagerLateMixin} and
+ * {@link com.gregtechceu.gtceu.integration.kjs.GregTechKubeJSPlugin}
+ */
+@ApiStatus.Internal
+public final class RecipeManagerHandler {
+
+ /**
+ * Adds proxy recipes to an {@link GTRecipeType}'s {@link RecipeAdditionHandler} and adds them to a list.
+ *
+ * @param recipes the recipes stored by their ID
+ * @param gtRecipeType the recipe type to add the recipes to, which owns the proxy recipes
+ * @param proxyRecipes the list of proxy recipes to populate
+ */
+ public static void addProxyRecipesToLookup(@NotNull Collection> recipes,
+ @NotNull GTRecipeType gtRecipeType, @NotNull RecipeType> proxyType,
+ @NotNull List> proxyRecipes) {
+ var lookup = gtRecipeType.getAdditionHandler();
+ proxyRecipes.clear();
+ recipes.forEach((recipe) -> {
+ if (recipe.value().getType() != proxyType) {
+ // do not add recipes of incompatible type
+ return;
+ }
+ RecipeHolder gtRecipe = gtRecipeType.toGTRecipe(recipe);
+ proxyRecipes.add(gtRecipe);
+ lookup.addStaging(gtRecipe.value());
+ });
+ }
+
+ /**
+ * Adds recipes to an {@link GTRecipeType}'s {@link RecipeAdditionHandler}
+ *
+ * @param recipes the recipes stored by their ID
+ * @param gtRecipeType the recipe type to add recipes to
+ */
+ public static void addRecipesToLookup(@NotNull Collection> recipes,
+ @NotNull GTRecipeType gtRecipeType) {
+ var lookup = gtRecipeType.getAdditionHandler();
+ for (RecipeHolder> r : recipes) {
+ if (r.value().getType() != gtRecipeType) {
+ // do not add recipes of incompatible type
+ continue;
+ }
+ if (r.value() instanceof GTRecipe recipe) {
+ lookup.addStaging(recipe);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
index 563c9523382..03da562da57 100644
--- a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
+++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
@@ -2,7 +2,8 @@
import com.gregtechceu.gtceu.api.recipe.GTRecipe;
import com.gregtechceu.gtceu.api.recipe.GTRecipeType;
-import com.gregtechceu.gtceu.api.recipe.lookup.StagingRecipeDB;
+import com.gregtechceu.gtceu.api.recipe.lookup.MapIngredientPool;
+import com.gregtechceu.gtceu.api.recipe.lookup.RecipeManagerHandler;
import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder;
import net.minecraft.advancements.Advancement;
@@ -31,10 +32,9 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import java.util.Collection;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import java.util.stream.Stream;
@Mixin(value = RecipeManager.class, priority = 1500)
public abstract class RecipeManagerLateMixin {
@@ -76,40 +76,22 @@ public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
gtceu$replaceRecipes(recipesByName);
for (RecipeType> recipeType : BuiltInRegistries.RECIPE_TYPE) {
- if (recipeType instanceof GTRecipeType gtRecipeType) {
- var stagingDB = new StagingRecipeDB();
-
- var proxyRecipes = gtRecipeType.getProxyRecipes();
- for (Map.Entry, List>> entry : proxyRecipes.entrySet()) {
- var type = entry.getKey();
- var recipes = entry.getValue();
- recipes.clear();
- if (this.byType.containsKey(type)) {
- for (var recipe : this.byType.get(type)) {
- recipes.add(gtRecipeType.toGTRecipe(recipe));
- }
- }
- }
-
- if (this.byType.containsKey(gtRecipeType)) {
- Stream.concat(
- this.byType.get(gtRecipeType).stream(),
- proxyRecipes.entrySet().stream().flatMap(entry -> entry.getValue().stream()))
- .filter(holder -> holder != null && holder.value() instanceof GTRecipe)
- .forEach(holder -> {
- GTRecipe recipe = (GTRecipe) holder.value();
- recipe.setId(holder.id());
- stagingDB.add(recipe);
- });
- } else if (!proxyRecipes.isEmpty()) {
- proxyRecipes.values().stream()
- .flatMap(List::stream)
- .forEach(gtRecipe -> stagingDB.add(gtRecipe.value()));
- }
-
- stagingDB.populateDB(gtRecipeType.db());
+ if (!(recipeType instanceof GTRecipeType gtRecipeType)) {
+ continue;
}
+ gtRecipeType.beginStagingRecipes();
+ gtRecipeType.getProxyRecipes().forEach((type, list) -> {
+ Collection> recipes = this.byType.get(type);
+ if (recipes.isEmpty()) {
+ return;
+ }
+ RecipeManagerHandler.addProxyRecipesToLookup(recipes, gtRecipeType, type, list);
+ });
+ Collection> recipesByID = this.byType.get(gtRecipeType);
+ RecipeManagerHandler.addRecipesToLookup(recipesByID, gtRecipeType);
+ gtRecipeType.getAdditionHandler().completeStaging();
}
+ MapIngredientPool.clear();
}
@Unique
From a211f0fb84643ddfef354c84be56ea3f1dde17e2 Mon Sep 17 00:00:00 2001
From: screret <68943070+screret@users.noreply.github.com>
Date: Wed, 5 Aug 2026 12:00:47 +0300
Subject: [PATCH 02/19] Use vanilla's `replaceRecipes` method instead of adding
our own
---
.../core/mixins/RecipeManagerLateMixin.java | 26 ++++++-------------
1 file changed, 8 insertions(+), 18 deletions(-)
diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
index 03da562da57..28770123d8f 100644
--- a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
+++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
@@ -20,20 +20,18 @@
import net.minecraft.world.item.crafting.RecipeType;
import net.neoforged.neoforge.common.conditions.ICondition;
-import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.JsonElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
@Mixin(value = RecipeManager.class, priority = 1500)
@@ -45,11 +43,16 @@ public abstract class RecipeManagerLateMixin {
@Shadow
private Map> byName;
+ @Shadow
+ public abstract void replaceRecipes(Iterable> recipes);
+
@Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
at = @At(value = "TAIL"))
private void gtceu$cloneVanillaRecipes(Map map, ResourceManager resourceManager,
ProfilerFiller profiler, CallbackInfo ci) {
- var recipesByName = new HashMap<>(byName);
+ // use a linked hash map to keep the immutable map's order
+ Map> recipesByName = new LinkedHashMap<>(byName);
+ // regenerate child recipes
byName.values().forEach(holder -> {
if (holder.value() instanceof GTRecipe gtRecipe) {
new GTRecipeBuilder(gtRecipe, gtRecipe.recipeType)
@@ -73,7 +76,7 @@ public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
});
}
});
- gtceu$replaceRecipes(recipesByName);
+ replaceRecipes(recipesByName.values());
for (RecipeType> recipeType : BuiltInRegistries.RECIPE_TYPE) {
if (!(recipeType instanceof GTRecipeType gtRecipeType)) {
@@ -93,17 +96,4 @@ public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
}
MapIngredientPool.clear();
}
-
- @Unique
- public void gtceu$replaceRecipes(Map> map) {
- byName = map;
-
- var recipesByType = ImmutableMultimap., RecipeHolder>>builder();
-
- for (var entry : map.entrySet()) {
- recipesByType.put(entry.getValue().value().getType(), entry.getValue());
- }
-
- byType = recipesByType.build();
- }
}
From 78a41fc4b4006a7811b93d9462b0e03a7349ae17 Mon Sep 17 00:00:00 2001
From: screret <68943070+screret@users.noreply.github.com>
Date: Wed, 5 Aug 2026 21:18:30 +0300
Subject: [PATCH 03/19] Move dynamic recipe generation/loading to a handler
class[^1] and delay it to only happen when recipes are parsed so we have a
more complete registry context
[^1]: from the mixins it used to reside in
---
.../gtceu/api/recipe/GTRecipeType.java | 5 +
.../recipe/lookup/RecipeManagerHandler.java | 4 +-
.../core/mixins/RecipeManagerEarlyMixin.java | 24 ++-
.../core/mixins/RecipeManagerLateMixin.java | 86 ++--------
.../ReloadableServerResourcesMixin.java | 23 +--
.../data/dynamic/DynamicRecipeHandler.java | 155 ++++++++++++++++++
.../gtceu/data/pack/GTDynamicDataPack.java | 25 ++-
7 files changed, 212 insertions(+), 110 deletions(-)
create mode 100644 src/main/java/com/gregtechceu/gtceu/data/dynamic/DynamicRecipeHandler.java
diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeType.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeType.java
index db990d0abbb..ffc03e4ae6b 100644
--- a/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeType.java
+++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/GTRecipeType.java
@@ -324,6 +324,11 @@ public void beginStagingRecipes() {
additionHandler.beginStaging();
}
+ @ApiStatus.Internal
+ public void completeStagingRecipes() {
+ additionHandler.completeStaging();
+ }
+
public interface ICustomRecipeLogic {
/**
diff --git a/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java b/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java
index f5e5bd08c5d..f7ec6345ad4 100644
--- a/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java
+++ b/src/main/java/com/gregtechceu/gtceu/api/recipe/lookup/RecipeManagerHandler.java
@@ -28,7 +28,7 @@ public final class RecipeManagerHandler {
* @param gtRecipeType the recipe type to add the recipes to, which owns the proxy recipes
* @param proxyRecipes the list of proxy recipes to populate
*/
- public static void addProxyRecipesToLookup(@NotNull Collection> recipes,
+ public static void addProxyRecipesToLookup(@NotNull Collection extends RecipeHolder>> recipes,
@NotNull GTRecipeType gtRecipeType, @NotNull RecipeType> proxyType,
@NotNull List> proxyRecipes) {
var lookup = gtRecipeType.getAdditionHandler();
@@ -50,7 +50,7 @@ public static void addProxyRecipesToLookup(@NotNull Collection>
* @param recipes the recipes stored by their ID
* @param gtRecipeType the recipe type to add recipes to
*/
- public static void addRecipesToLookup(@NotNull Collection> recipes,
+ public static void addRecipesToLookup(@NotNull Collection extends RecipeHolder>> recipes,
@NotNull GTRecipeType gtRecipeType) {
var lookup = gtRecipeType.getAdditionHandler();
for (RecipeHolder> r : recipes) {
diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerEarlyMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerEarlyMixin.java
index 2cb9557f2a8..d9cc4e81277 100644
--- a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerEarlyMixin.java
+++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerEarlyMixin.java
@@ -1,14 +1,19 @@
package com.gregtechceu.gtceu.core.mixins;
-import com.gregtechceu.gtceu.common.data.GTRecipes;
+import com.gregtechceu.gtceu.data.dynamic.DynamicRecipeHandler;
+import net.minecraft.core.HolderLookup;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
+import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.crafting.RecipeManager;
+import com.google.gson.Gson;
import com.google.gson.JsonElement;
+import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -16,12 +21,21 @@
import java.util.Map;
@Mixin(value = RecipeManager.class, priority = 500)
-public abstract class RecipeManagerEarlyMixin {
+public abstract class RecipeManagerEarlyMixin extends SimpleJsonResourceReloadListener {
+
+ @Shadow
+ @Final
+ private HolderLookup.Provider registries;
+
+ private RecipeManagerEarlyMixin(Gson gson, String directory) {
+ super(gson, directory);
+ }
@Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
at = @At("HEAD"))
- private void gtceu$removeRecipes(Map map, ResourceManager pResourceManager,
- ProfilerFiller pProfiler, CallbackInfo ci) {
- GTRecipes.recipeRemoval(map::remove);
+ private void gtceu$handleDynamicRecipesEarly(Map map,
+ ResourceManager resourceManager, ProfilerFiller profiler,
+ CallbackInfo ci) {
+ DynamicRecipeHandler.handleRecipesEarly(map, this.registries, this.makeConditionalOps());
}
}
diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
index 28770123d8f..8e97a5239c2 100644
--- a/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
+++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/RecipeManagerLateMixin.java
@@ -1,99 +1,33 @@
package com.gregtechceu.gtceu.core.mixins;
-import com.gregtechceu.gtceu.api.recipe.GTRecipe;
-import com.gregtechceu.gtceu.api.recipe.GTRecipeType;
-import com.gregtechceu.gtceu.api.recipe.lookup.MapIngredientPool;
-import com.gregtechceu.gtceu.api.recipe.lookup.RecipeManagerHandler;
-import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder;
+import com.gregtechceu.gtceu.data.dynamic.DynamicRecipeHandler;
-import net.minecraft.advancements.Advancement;
-import net.minecraft.advancements.AdvancementHolder;
-import net.minecraft.core.registries.BuiltInRegistries;
-import net.minecraft.data.recipes.RecipeBuilder;
-import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
+import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
-import net.minecraft.world.item.crafting.Recipe;
-import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
-import net.minecraft.world.item.crafting.RecipeType;
-import net.neoforged.neoforge.common.conditions.ICondition;
-import com.google.common.collect.Multimap;
+import com.google.gson.Gson;
import com.google.gson.JsonElement;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import java.util.Collection;
-import java.util.LinkedHashMap;
import java.util.Map;
@Mixin(value = RecipeManager.class, priority = 1500)
-public abstract class RecipeManagerLateMixin {
+public abstract class RecipeManagerLateMixin extends SimpleJsonResourceReloadListener {
- @Shadow
- private Multimap, RecipeHolder>> byType;
-
- @Shadow
- private Map> byName;
-
- @Shadow
- public abstract void replaceRecipes(Iterable> recipes);
+ public RecipeManagerLateMixin(Gson gson, String directory) {
+ super(gson, directory);
+ }
@Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
at = @At(value = "TAIL"))
- private void gtceu$cloneVanillaRecipes(Map map, ResourceManager resourceManager,
- ProfilerFiller profiler, CallbackInfo ci) {
- // use a linked hash map to keep the immutable map's order
- Map> recipesByName = new LinkedHashMap<>(byName);
- // regenerate child recipes
- byName.values().forEach(holder -> {
- if (holder.value() instanceof GTRecipe gtRecipe) {
- new GTRecipeBuilder(gtRecipe, gtRecipe.recipeType)
- .id(holder.id().withPath(path -> path.substring(path.indexOf('/') + 1)))
- .onSave(gtRecipe.recipeType.getRecipeBuilder().onSave)
- .save(new RecipeOutput() {
-
- @SuppressWarnings("removal")
- @Override
- public Advancement.@NotNull Builder advancement() {
- return Advancement.Builder.recipeAdvancement()
- .parent(RecipeBuilder.ROOT_RECIPE_ADVANCEMENT);
- }
-
- @Override
- public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
- @Nullable AdvancementHolder advancement,
- ICondition @NotNull... conditions) {
- recipesByName.put(id, new RecipeHolder<>(id, recipe));
- }
- });
- }
- });
- replaceRecipes(recipesByName.values());
-
- for (RecipeType> recipeType : BuiltInRegistries.RECIPE_TYPE) {
- if (!(recipeType instanceof GTRecipeType gtRecipeType)) {
- continue;
- }
- gtRecipeType.beginStagingRecipes();
- gtRecipeType.getProxyRecipes().forEach((type, list) -> {
- Collection> recipes = this.byType.get(type);
- if (recipes.isEmpty()) {
- return;
- }
- RecipeManagerHandler.addProxyRecipesToLookup(recipes, gtRecipeType, type, list);
- });
- Collection> recipesByID = this.byType.get(gtRecipeType);
- RecipeManagerHandler.addRecipesToLookup(recipesByID, gtRecipeType);
- gtRecipeType.getAdditionHandler().completeStaging();
- }
- MapIngredientPool.clear();
+ private void gtceu$handleDynamicRecipesLate(Map map, ResourceManager resourceManager,
+ ProfilerFiller profiler, CallbackInfo ci) {
+ DynamicRecipeHandler.handleRecipesLate((RecipeManager) (Object) this);
}
}
diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ReloadableServerResourcesMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ReloadableServerResourcesMixin.java
index 6df3a51520e..bba30f8e232 100644
--- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ReloadableServerResourcesMixin.java
+++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ReloadableServerResourcesMixin.java
@@ -41,35 +41,18 @@ public abstract class ReloadableServerResourcesMixin {
FeatureFlagSet featureFlags, Commands.CommandSelection commands,
int functionCompilationLevel, Executor backgroundExecutor, Executor gameExecutor,
CallbackInfoReturnable> cir) {
- // load and loot tables recipes *before* other data so that we have the registries loaded
- // before saving recipes to JSON.
+ // load loot tables *before* other data so we have the registries loaded before saving recipes to JSON.
// because it breaks if we don't do that.
// this doesn't have dynamic registries available, by the way.
RegistryAccess.Frozen frozen = access.compositeAccess();
- // Register recipes & unification data again
+ // Register dynamic loot
long startTime = System.currentTimeMillis();
- GTCraftingComponents.init();
- SteamBoilerLogic.clearBoilerRecipeCaches();
- GTRecipes.recipeAddition(new RecipeOutput() {
-
- @Override
- public Advancement.@NotNull Builder advancement() {
- // noinspection removal
- return Advancement.Builder.recipeAdvancement().parent(RecipeBuilder.ROOT_RECIPE_ADVANCEMENT);
- }
-
- @Override
- public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
- @Nullable AdvancementHolder advancement, ICondition @NotNull... conditions) {
- GTDynamicDataPack.addRecipe(id, recipe, advancement, frozen);
- }
- });
MixinHelpers.generateGTDynamicLoot(GTDynamicDataPack::addLootTable, frozen);
// Initialize dungeon loot additions
DungeonLootLoader.init();
- GTCEu.LOGGER.info("GregTech Data loading took {}ms", System.currentTimeMillis() - startTime);
+ GTCEu.LOGGER.info("GregTech Loot table loading took {}ms", System.currentTimeMillis() - startTime);
}
}
diff --git a/src/main/java/com/gregtechceu/gtceu/data/dynamic/DynamicRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/data/dynamic/DynamicRecipeHandler.java
new file mode 100644
index 00000000000..3b598794464
--- /dev/null
+++ b/src/main/java/com/gregtechceu/gtceu/data/dynamic/DynamicRecipeHandler.java
@@ -0,0 +1,155 @@
+package com.gregtechceu.gtceu.data.dynamic;
+
+import com.gregtechceu.gtceu.GTCEu;
+import com.gregtechceu.gtceu.api.machine.trait.customlogic.SteamBoilerLogic;
+import com.gregtechceu.gtceu.api.recipe.GTRecipe;
+import com.gregtechceu.gtceu.api.recipe.GTRecipeType;
+import com.gregtechceu.gtceu.api.recipe.lookup.MapIngredientPool;
+import com.gregtechceu.gtceu.api.recipe.lookup.RecipeManagerHandler;
+import com.gregtechceu.gtceu.common.data.GTRecipes;
+import com.gregtechceu.gtceu.config.ConfigHolder;
+import com.gregtechceu.gtceu.data.pack.GTDynamicDataPack;
+import com.gregtechceu.gtceu.data.recipe.GTCraftingComponents;
+import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder;
+
+import net.minecraft.advancements.Advancement;
+import net.minecraft.advancements.AdvancementHolder;
+import net.minecraft.core.HolderLookup;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.data.recipes.RecipeBuilder;
+import net.minecraft.data.recipes.RecipeOutput;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.*;
+import net.neoforged.neoforge.common.conditions.ConditionalOps;
+import net.neoforged.neoforge.common.conditions.ICondition;
+import net.neoforged.neoforge.common.conditions.WithConditions;
+
+import com.google.gson.JsonElement;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@ApiStatus.Internal
+public final class DynamicRecipeHandler {
+
+ private DynamicRecipeHandler() {}
+
+ // reload time spent in handleRecipesEarly, in milliseconds.
+ private static final AtomicLong earlyLoadElapsed = new AtomicLong();
+
+ public static void handleRecipesEarly(Map map, HolderLookup.Provider registries,
+ final ConditionalOps serializationContext) {
+ long startTime = System.currentTimeMillis();
+
+ // first, remove old recipes & clear caches
+ GTRecipes.recipeRemoval(map::remove);
+ SteamBoilerLogic.clearBoilerRecipeCaches();
+ GTCraftingComponents.init();
+
+ GTRecipes.recipeAddition(new RecipeOutput() {
+
+ @Override
+ public Advancement.@NotNull Builder advancement() {
+ // noinspection removal
+ return Advancement.Builder.recipeAdvancement().parent(RecipeBuilder.ROOT_RECIPE_ADVANCEMENT);
+ }
+
+ @Override
+ public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
+ @Nullable AdvancementHolder advancement, ICondition @NotNull... conditions) {
+ JsonElement recipeJson = Recipe.CONDITIONAL_CODEC
+ .encodeStart(serializationContext, Optional.of(new WithConditions<>(recipe, conditions)))
+ .getOrThrow();
+ map.put(id, recipeJson);
+
+ if (ConfigHolder.INSTANCE.dev.dumpRecipes) {
+ // add the recipe JSON to the generated datapack if data dumping is enabled so it can be dumped
+ // immediately or with a command
+ GTDynamicDataPack.addResource(GTDynamicDataPack.RECIPE_ID_CONVERTER.idToFile(id), recipeJson);
+ }
+
+ if (advancement != null) {
+ GTDynamicDataPack.addAdvancement(advancement, serializationContext);
+ }
+ }
+ });
+
+ earlyLoadElapsed.set(System.currentTimeMillis() - startTime);
+ }
+
+ public static void handleRecipesLate(RecipeManager recipeManager) {
+ long startTime = System.currentTimeMillis();
+
+ cloneVanillaRecipes(recipeManager);
+ addRecipesToLookup(recipeManager);
+
+ long elapsed = (System.currentTimeMillis() - startTime) + earlyLoadElapsed.get();
+ GTCEu.LOGGER.info("GregTech Dynamic Recipe loading took {}ms", elapsed);
+ }
+
+ private static void addRecipesToLookup(RecipeManager recipeManager) {
+ for (RecipeType> t : BuiltInRegistries.RECIPE_TYPE) {
+ if (!(t instanceof GTRecipeType recipeType)) {
+ continue;
+ }
+ recipeType.beginStagingRecipes();
+
+ for (var entry : recipeType.getProxyRecipes().entrySet()) {
+ RecipeType> proxyRecipeType = entry.getKey();
+ Collection extends RecipeHolder>> recipes = recipeManager.getAllRecipesFor(proxyRecipeType);
+ if (recipes.isEmpty()) {
+ continue;
+ }
+ List> proxyRecipes = entry.getValue();
+ RecipeManagerHandler.addProxyRecipesToLookup(recipes, recipeType, proxyRecipeType, proxyRecipes);
+ }
+
+ Collection extends RecipeHolder>> recipesByID = recipeManager.getAllRecipesFor(recipeType);
+ RecipeManagerHandler.addRecipesToLookup(recipesByID, recipeType);
+ recipeType.completeStagingRecipes();
+ }
+ MapIngredientPool.clear();
+ }
+
+ private static void cloneVanillaRecipes(RecipeManager recipeManager) {
+ Collection> originalRecipes = recipeManager.getRecipes();
+
+ // use a linked map to keep the immutable map's order
+ // this is a map so duplicate recipes can replace older ones easily without adding a bunch of useless entries to
+ // a list or set
+ Map> replacementRecipes = originalRecipes.stream()
+ .collect(Collectors.toMap(RecipeHolder::id, Function.identity(),
+ (oldValue, value) -> value, LinkedHashMap::new));
+
+ // regenerate child recipes
+ originalRecipes.forEach(holder -> {
+ if (holder.value() instanceof GTRecipe gtRecipe) {
+ new GTRecipeBuilder(gtRecipe, gtRecipe.recipeType)
+ .id(holder.id().withPath(path -> path.substring(path.indexOf('/') + 1)))
+ .onSave(gtRecipe.recipeType.getRecipeBuilder().onSave)
+ .save(new RecipeOutput() {
+
+ @SuppressWarnings("removal")
+ @Override
+ public Advancement.@NotNull Builder advancement() {
+ return Advancement.Builder.recipeAdvancement()
+ .parent(RecipeBuilder.ROOT_RECIPE_ADVANCEMENT);
+ }
+
+ @Override
+ public void accept(@NotNull ResourceLocation id, @NotNull Recipe> recipe,
+ @Nullable AdvancementHolder advancement,
+ ICondition @NotNull... conditions) {
+ replacementRecipes.put(id, new RecipeHolder<>(id, recipe));
+ }
+ });
+ }
+ });
+ recipeManager.replaceRecipes(replacementRecipes.values());
+ }
+}
diff --git a/src/main/java/com/gregtechceu/gtceu/data/pack/GTDynamicDataPack.java b/src/main/java/com/gregtechceu/gtceu/data/pack/GTDynamicDataPack.java
index 0b7246c2593..fccb873cbea 100644
--- a/src/main/java/com/gregtechceu/gtceu/data/pack/GTDynamicDataPack.java
+++ b/src/main/java/com/gregtechceu/gtceu/data/pack/GTDynamicDataPack.java
@@ -2,12 +2,14 @@
import com.gregtechceu.gtceu.GTCEu;
import com.gregtechceu.gtceu.api.addon.AddonFinder;
+import com.gregtechceu.gtceu.api.addon.IGTAddon;
import com.gregtechceu.gtceu.config.ConfigHolder;
import net.minecraft.SharedConstants;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.core.HolderLookup;
+import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.FileToIdConverter;
import net.minecraft.resources.ResourceLocation;
@@ -22,6 +24,7 @@
import com.google.common.collect.Sets;
import com.google.gson.JsonElement;
+import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
@@ -41,7 +44,7 @@ public class GTDynamicDataPack implements PackResources {
protected static final ObjectSet SERVER_DOMAINS = new ObjectOpenHashSet<>();
protected static final GTDynamicPackContents CONTENTS = new GTDynamicPackContents();
- private static final FileToIdConverter RECIPE_ID_CONVERTER = FileToIdConverter.json("recipe");
+ public static final FileToIdConverter RECIPE_ID_CONVERTER = FileToIdConverter.json("recipe");
private static final FileToIdConverter LOOT_TABLE_ID_CONVERTER = FileToIdConverter.json("loot_table");
private static final FileToIdConverter ADVANCEMENT_ID_CONVERTER = FileToIdConverter.json("advancement");
@@ -80,25 +83,33 @@ public static void addResource(ResourceLocation location, byte[] data) {
CONTENTS.addToData(location, data);
}
+ /**
+ * @deprecated API consumers shouldn't use this method. Instead, they should implement their recipe additions in
+ * {@link IGTAddon#addRecipes(RecipeOutput) IGTAddon.addRecipes}.
+ * @see IGTAddon#addRecipes(RecipeOutput)
+ */
+ @Deprecated(since = "8.0.0", forRemoval = true)
public static void addRecipe(ResourceLocation recipeId, Recipe> recipe, @Nullable AdvancementHolder advancement,
HolderLookup.Provider registries) {
+ DynamicOps serializationContext = registries.createSerializationContext(JsonOps.INSTANCE);
JsonElement recipeJson = Recipe.CODEC
- .encodeStart(registries.createSerializationContext(JsonOps.INSTANCE), recipe)
+ .encodeStart(serializationContext, recipe)
.getOrThrow();
addResource(RECIPE_ID_CONVERTER.idToFile(recipeId), recipeJson);
if (advancement != null) {
- addAdvancement(advancement, registries);
+ addAdvancement(advancement, serializationContext);
}
}
- public static void addAdvancement(AdvancementHolder advancement, HolderLookup.Provider registries) {
- addAdvancement(advancement.id(), advancement.value(), registries);
+ public static void addAdvancement(AdvancementHolder advancement, DynamicOps serializationContext) {
+ addAdvancement(advancement.id(), advancement.value(), serializationContext);
}
- public static void addAdvancement(ResourceLocation loc, Advancement advancement, HolderLookup.Provider registries) {
+ public static void addAdvancement(ResourceLocation loc, Advancement advancement,
+ DynamicOps serializationContext) {
JsonElement advancementJson = Advancement.CODEC
- .encodeStart(registries.createSerializationContext(JsonOps.INSTANCE), advancement)
+ .encodeStart(serializationContext, advancement)
.getOrThrow();
addResource(ADVANCEMENT_ID_CONVERTER.idToFile(loc), advancementJson);
}
From e510d5af59592250607e7fbb4591b0e402efa704 Mon Sep 17 00:00:00 2001
From: screret <68943070+screret@users.noreply.github.com>
Date: Wed, 5 Aug 2026 21:20:39 +0300
Subject: [PATCH 04/19] clean up/optimize ingredient compression code slightly
---
.../recipe/FluidRecipeCapability.java | 47 +++++++++----------
.../recipe/ItemRecipeCapability.java | 42 ++++++-----------
.../api/recipe/lookup/StagingRecipeDB.java | 4 +-
3 files changed, 37 insertions(+), 56 deletions(-)
diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/FluidRecipeCapability.java b/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/FluidRecipeCapability.java
index 29e01660a6b..db5c7e6dbee 100644
--- a/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/FluidRecipeCapability.java
+++ b/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/FluidRecipeCapability.java
@@ -68,42 +68,37 @@ public IntProviderFluidIngredient copyWithModifier(IntProviderFluidIngredient co
@Override
public List