Skip to content

Commit

Permalink
First attempt at reworking recipe event (#909)
Browse files Browse the repository at this point in the history
* First attempt at reworking recipe event

* Code cleanup

* Improve logging of failed recipe

* Bump version

* DataResult cleanup

* Fix handleError for null errors

* Fix server data not being sent if recipe event is null
  • Loading branch information
MaxNeedsSnacks authored Oct 12, 2024
1 parent c0f67bf commit b9195fa
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 349 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ modrinth_id=umyGl7zF

minecraft_version=1.21.1
supported_versions=1.21.1, 1.21
mod_version=2101.7.0
mod_version=2101.7.1

neoforge_version=21.1.42
parchment_mc_version=1.21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.JsonElement;
import com.llamalad7.mixinextras.sugar.Local;
import dev.latvian.mods.kubejs.bindings.event.ServerEvents;
import dev.latvian.mods.kubejs.core.RecipeManagerKJS;
import dev.latvian.mods.kubejs.core.ReloadableServerResourcesKJS;
import dev.latvian.mods.kubejs.net.KubeServerData;
import dev.latvian.mods.kubejs.net.SyncServerDataPayload;
import dev.latvian.mods.kubejs.plugin.KubeJSPlugins;
import dev.latvian.mods.kubejs.recipe.CompostableRecipesKubeEvent;
import dev.latvian.mods.kubejs.recipe.RecipesKubeEvent;
import dev.latvian.mods.kubejs.recipe.special.SpecialRecipeSerializerManager;
import dev.latvian.mods.kubejs.script.ConsoleJS;
import dev.latvian.mods.kubejs.script.ScriptType;
import dev.latvian.mods.kubejs.util.Cast;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.profiling.ProfilerFiller;
Expand All @@ -18,6 +29,7 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.HashMap;
import java.util.Map;

@Mixin(value = RecipeManager.class, priority = 1100)
Expand All @@ -31,11 +43,72 @@ public abstract class RecipeManagerMixin implements RecipeManagerKJS {
@Unique
private ReloadableServerResourcesKJS kjs$resources;

@Inject(method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At("HEAD"), cancellable = true)
@Unique
private RecipesKubeEvent kjs$event;

@Inject(
method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
at = @At("HEAD")
)
private void customRecipesHead(Map<ResourceLocation, JsonElement> map, ResourceManager resourceManager, ProfilerFiller profiler, CallbackInfo ci) {
if (kjs$resources.kjs$getServerScriptManager().recipes(this, resourceManager, map)) {
ci.cancel();
var manager = kjs$resources.kjs$getServerScriptManager();

// FIXME: data maps!
if (ServerEvents.COMPOSTABLE_RECIPES.hasListeners()) {
ServerEvents.COMPOSTABLE_RECIPES.post(ScriptType.SERVER, new CompostableRecipesKubeEvent());
}

for (var entry : manager.getRegistries().cachedRegistryTags.values()) {
if (entry.registry() == null || entry.lookup() == null) {
continue;
}

entry.registry().bindTags(Cast.to(entry.lookup().bindingMap()));
}

manager.recipeSchemaStorage.fireEvents(manager.getRegistries(), resourceManager);

SpecialRecipeSerializerManager.INSTANCE.reset();
ServerEvents.SPECIAL_RECIPES.post(ScriptType.SERVER, SpecialRecipeSerializerManager.INSTANCE);

if (ServerEvents.RECIPES.hasListeners()) {
ConsoleJS.SERVER.info("Processing recipes...");
kjs$event = new RecipesKubeEvent(manager, resourceManager);
kjs$event.post(this, map);
}
}

@Inject(
method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V")
)
private void catchFailingRecipes(CallbackInfo ci, @Local Map.Entry<ResourceLocation, JsonElement> entry, @Local RuntimeException ex) {
if (kjs$event != null) {
kjs$event.handleFailedRecipe(entry.getKey(), entry.getValue(), ex);
}
}

@Inject(
method = "apply(Ljava/util/Map;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V",
at = @At(value = "TAIL")
)
@SuppressWarnings("removal")
private void addServerData(CallbackInfo ci) {
if (kjs$event != null) {
// FIXME: please remove this soon! massive performance implications!
var recipesByName = new HashMap<>(byName);

KubeJSPlugins.forEachPlugin(p -> p.injectRuntimeRecipes(kjs$event, this, recipesByName));

kjs$event.finishEvent();

// make sure byType is also set correctly
kjs$replaceRecipes(recipesByName);
}

kjs$event = null;

kjs$getResources().kjs$getServerScriptManager().serverData = new SyncServerDataPayload(KubeServerData.collect());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ default void beforeRecipeLoading(RecipesKubeEvent event, RecipeManagerKJS manage

/**
* Only use this method if your mod adds runtime recipes and is conflicting with KubeJS recipe manager. Disable your other hook if "kubejs" mod is loaded!
*
* @deprecated This method should no longer be necessary, as KubeJS no longer interferes with dynamic recipes added through RecipeManager.
*/
@Deprecated(forRemoval = true)
default void injectRuntimeRecipes(RecipesKubeEvent event, RecipeManagerKJS manager, Map<ResourceLocation, RecipeHolder<?>> recipesByName) {
}

Expand Down
60 changes: 32 additions & 28 deletions src/main/java/dev/latvian/mods/kubejs/recipe/KubeRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.mojang.serialization.DataResult;
import dev.latvian.mods.kubejs.CommonProperties;
import dev.latvian.mods.kubejs.DevProperties;
import dev.latvian.mods.kubejs.core.RecipeLikeKJS;
Expand Down Expand Up @@ -35,15 +36,18 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
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.RecipeSerializer;
import org.apache.commons.lang3.mutable.MutableObject;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class KubeRecipe implements RecipeLikeKJS, CustomJavaToJsWrapper {
public static final String CHANGED_MARKER = "_kubejs_changed_marker";

public ResourceLocation id;
public RecipeTypeFunction type;
public boolean newRecipe;
Expand Down Expand Up @@ -459,18 +463,13 @@ public KubeRecipe stage(String s) {
}

/**
* Only used by {@link KubeRecipe#getOrCreateId()} and {@link KubeRecipe#createRecipe()} in rare case that a recipe can be another recipe type than itself (e.g. kubejs:shaped -> minecraft:crafting_shaped)
* Only used by {@link KubeRecipe#getOrCreateId()} and {@link KubeRecipe#serializeChanges()} in rare case that a recipe can be another recipe type than itself (e.g. kubejs:shaped -> minecraft:crafting_shaped)
*/
public RecipeTypeFunction getSerializationTypeFunction() {
return type;
}

@Nullable
public RecipeHolder<?> createRecipe() {
if (removed) {
return null;
}

public KubeRecipe serializeChanges() {
if (newRecipe || hasChanged()) {
serialize();

Expand All @@ -490,37 +489,42 @@ public RecipeHolder<?> createRecipe() {
json.addProperty("type", getSerializationTypeFunction().idString);
}

var id = getOrCreateId();

if (type.event.stageSerializer != null && json.has(KubeJSCraftingRecipe.STAGE_KEY) && !type.idString.equals("recipestages:stage")) {
var o = new JsonObject();
o.addProperty("stage", json.get(KubeJSCraftingRecipe.STAGE_KEY).getAsString());
o.add("recipe", json);

try {
var recipe = type.event.registries.decodeJson(type.event.stageSerializer.codec(), o);
return recipe == null ? null : new RecipeHolder<>(id, recipe);
} catch (Throwable ex) {
ConsoleJS.SERVER.error("Failed to decode " + id + " from json " + o, sourceLine, ex, RecipesKubeEvent.CREATE_RECIPE_SKIP_ERROR);
}
var staged = new JsonObject();
staged.addProperty("stage", json.get(KubeJSCraftingRecipe.STAGE_KEY).getAsString());
staged.add("recipe", json);
json = staged;
}
} else if (originalRecipe != null && originalRecipe.getValue() != null) {
return new RecipeHolder<>(getOrCreateId(), originalRecipe.getValue());

json.addProperty(CHANGED_MARKER, true);
}

return RecipeHelper.fromJson(type.event.jsonOps, getSerializationTypeFunction().schemaType.getSerializer(), getOrCreateId(), json, DevProperties.get().logErroringParsedRecipes);
return this;
}

@Nullable
public Recipe<?> getOriginalRecipe() {
if (originalRecipe == null) {
originalRecipe = new MutableObject<>();
try {
var holder = RecipeHelper.fromJson(type.event.jsonOps, type.schemaType.getSerializer(), getOrCreateId(), originalJson, DevProperties.get().logErroringParsedRecipes);

if (holder != null) {
originalRecipe.setValue(holder.value());
}
var serializer = type.schemaType.getSerializer();
var ops = type.event.jsonOps;

// people apparently violate the contract here?!
//noinspection OptionalOfNullableMisuse
Optional.ofNullable(serializer.codec())
.map(DataResult::success)
.orElseGet(() -> DataResult.error(() -> "Codec for " + serializer.getClass().getName() + " is null!"))
.flatMap(codec -> ops.getMap(json).flatMap(map -> codec.decode(ops, map)))
.mapError(err -> "Error parsing recipe " + id + ": " + err)
.ifSuccess(originalRecipe::setValue)
.ifError(err -> {
if (DevProperties.get().logErroringParsedRecipes) {
ConsoleJS.SERVER.error(err.message());
} else {
RecipeManager.LOGGER.error(err.message());
}
});
} catch (Throwable e) {
ConsoleJS.SERVER.error("Could not create recipe from json for " + this, e);
}
Expand Down
69 changes: 0 additions & 69 deletions src/main/java/dev/latvian/mods/kubejs/recipe/RecipeHelper.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public KubeRecipe call(Context cx, Scriptable scope, Scriptable thisObj, Object[
} catch (Throwable cause) {
var r = schemaType.schema.recipeFactory.create(this, sourceLine, true);
r.creationError = true;
event.failedCount.incrementAndGet();
event.failedCount++;
ConsoleJS.SERVER.error("Failed to create a '" + idString + "' recipe from args " + Arrays.toString(args0), sourceLine, cause, SKIP_ERROR);
r.json = new JsonObject();
r.json.addProperty("type", idString);
Expand Down
Loading

0 comments on commit b9195fa

Please sign in to comment.