Skip to content

Commit

Permalink
Improved recipe error handling, added category to shaped, shapeless…
Browse files Browse the repository at this point in the history
… and cooking recipes
  • Loading branch information
LatvianModder committed Aug 11, 2024
1 parent 39d8733 commit 85a63f0
Show file tree
Hide file tree
Showing 31 changed files with 294 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
import dev.latvian.mods.kubejs.plugin.KubeJSPlugins;
import dev.latvian.mods.kubejs.recipe.component.BlockComponent;
import dev.latvian.mods.kubejs.recipe.component.BlockStateComponent;
import dev.latvian.mods.kubejs.recipe.component.BookCategoryComponent;
import dev.latvian.mods.kubejs.recipe.component.BooleanComponent;
import dev.latvian.mods.kubejs.recipe.component.CharacterComponent;
import dev.latvian.mods.kubejs.recipe.component.EitherRecipeComponent;
Expand Down Expand Up @@ -666,6 +667,9 @@ public void registerRecipeComponents(RecipeComponentFactoryRegistry registry) {
registry.register(TagKeyComponent.BIOME);
registry.register(NestedRecipeComponent.RECIPE);

registry.register(BookCategoryComponent.CRAFTING_BOOK_CATEGORY);
registry.register(BookCategoryComponent.COOKING_BOOK_CATEGORY);

registry.register("tag", TagKeyComponent.FACTORY);
registry.register("registry_element", RegistryComponent.FACTORY);
registry.register("enum", EnumComponent.FACTORY);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.latvian.mods.kubejs.error;

import dev.latvian.mods.kubejs.recipe.component.RecipeComponent;

public class EmptyRecipeComponentException extends KubeRuntimeException {
public final RecipeComponent<?> component;

public EmptyRecipeComponentException(RecipeComponent<?> component) {
super("Component '" + component + "' is not allowed to be empty!");
this.component = component;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.latvian.mods.kubejs.error;

import dev.latvian.mods.kubejs.recipe.component.RecipeComponent;

public class EmptyRecipeComponentValueException extends KubeRuntimeException {
public final RecipeComponent<?> component;

public EmptyRecipeComponentValueException(RecipeComponent<?> component) {
super("Component '" + component + "' is not allowed to contain empty values!");
this.component = component;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.latvian.mods.kubejs.error;

import dev.latvian.mods.kubejs.recipe.component.RecipeComponentValue;

public class InvalidRecipeComponentException extends KubeRuntimeException {
public final RecipeComponentValue<?> componentValueHolder;

public InvalidRecipeComponentException(RecipeComponentValue<?> h, Throwable cause) {
super("Invalid component '" + h.key.name + "' (" + h.key.component + ")", cause);
this.componentValueHolder = h;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,24 @@ public KubeRuntimeException(String m, Throwable cause) {

@Override
public String toString() {
String message = getLocalizedMessage();
return message != null && !message.isEmpty() ? message : getClass().getName();
var sb = new StringBuilder();

var message = getLocalizedMessage();

if (message != null) {
sb.append(message);
} else {
sb.append(getClass().getName());
}

var c = getCause();

while (c != null) {
sb.append(" - ").append(c);
c = c.getCause();
}

return sb.toString();
}

public KubeRuntimeException source(SourceLine sourceLine) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.latvian.mods.kubejs.error;

public class MissingRequiredValueException extends KubeRuntimeException {
public MissingRequiredValueException() {
super("Missing required value");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ public void accept(CreativeModeTab.ItemDisplayParameters itemDisplayParameters,
}

if (items.isEmpty()) {
output.accept((ItemStack) Items.PAPER.getDefaultInstance().kjs$setCustomName(Component.literal("Use .content(showRestrictedItems => ['kubejs:example']) to add more items!")));
var is = Items.PAPER.getDefaultInstance();
is.kjs$setCustomName(Component.literal("Use .content(showRestrictedItems => ['kubejs:example']) to add more items!"));
output.accept(is);
} else {
for (var item : items) {
output.accept(item);
Expand Down

This file was deleted.

13 changes: 5 additions & 8 deletions src/main/java/dev/latvian/mods/kubejs/recipe/KubeRecipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class KubeRecipe implements RecipeLikeKJS, CustomJavaToJsWrapper {
private MutableObject<Recipe<?>> originalRecipe = null;
public JsonObject json = null;
public boolean changed = false;
public boolean creationError = false;

protected List<IngredientActionHolder> recipeIngredientActions;

Expand All @@ -72,7 +73,7 @@ public void deserialize(boolean merge) {
try {
v.key.component.readFromJson(this, Cast.to(v), json);
} catch (Exception ex) {
ConsoleJS.SERVER.error("", new KubeRuntimeException("Failed to read " + v.key + " from recipe " + this, ex).source(sourceLine), RecipesKubeEvent.POST_SKIP_ERROR);
ConsoleJS.SERVER.error("Failed to read " + v.key + " from recipe " + this, sourceLine, ex, RecipesKubeEvent.POST_SKIP_ERROR);
}

if (v.value != null) {
Expand Down Expand Up @@ -186,11 +187,7 @@ public RecipeComponentValue<?>[] getRecipeComponentValues() {
*/
public void afterLoaded() {
for (var v : valueMap.holders) {
var e = v.checkEmpty();

if (!e.isEmpty()) {
throw new KubeRuntimeException(e).source(sourceLine);
}
v.validate(sourceLine);
}
}

Expand Down Expand Up @@ -485,7 +482,7 @@ public RecipeHolder<?> createRecipe() {
try {
json.add(KubeJSCraftingRecipe.INGREDIENT_ACTIONS_KEY, IngredientActionHolder.LIST_CODEC.encodeStart(type.event.registries.json(), recipeIngredientActions).getOrThrow());
} catch (Throwable ex) {
ConsoleJS.SERVER.error("", new KubeRuntimeException("Failed to encode " + KubeJSCraftingRecipe.INGREDIENT_ACTIONS_KEY, ex).source(sourceLine), RecipesKubeEvent.CREATE_RECIPE_SKIP_ERROR);
ConsoleJS.SERVER.error("Failed to encode " + KubeJSCraftingRecipe.INGREDIENT_ACTIONS_KEY, sourceLine, ex, RecipesKubeEvent.CREATE_RECIPE_SKIP_ERROR);
}
}

Expand All @@ -504,7 +501,7 @@ public RecipeHolder<?> createRecipe() {
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("", new KubeRuntimeException("Failed to decode " + id + " from json " + o, ex).source(sourceLine), RecipesKubeEvent.CREATE_RECIPE_SKIP_ERROR);
ConsoleJS.SERVER.error("Failed to decode " + id + " from json " + o, sourceLine, ex, RecipesKubeEvent.CREATE_RECIPE_SKIP_ERROR);
}
}
} else if (originalRecipe != null && originalRecipe.getValue() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ public RecipeFunction(Context cx, Scriptable scope, TypeInfo staticType, KubeRec

@Override
public Object get(Context cx, String name, Scriptable start) {
if (recipe instanceof ErroredKubeRecipe errored) {
return errored.dummyFunction;
}

var s = super.get(cx, name, start);

if (s == Scriptable.NOT_FOUND) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import dev.latvian.mods.kubejs.error.KubeRuntimeException;
import dev.latvian.mods.kubejs.recipe.component.ComponentValueMap;
import dev.latvian.mods.kubejs.recipe.schema.RecipeSchemaType;
import dev.latvian.mods.kubejs.script.ConsoleJS;
import dev.latvian.mods.kubejs.script.SourceLine;
import dev.latvian.mods.kubejs.util.MapJS;
import dev.latvian.mods.kubejs.util.WrappedJS;
Expand Down Expand Up @@ -35,18 +36,23 @@ public RecipeTypeFunction(RecipesKubeEvent event, RecipeSchemaType schemaType) {

@Override
public KubeRecipe call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args0) {
var sourceLine = SourceLine.of(cx);

try {
return createRecipe(cx, args0);
} catch (KubeRuntimeException rex) {
var recipe = new ErroredKubeRecipe(event, "Failed to create recipe for type '%s'".formatted(idString), rex, SKIP_ERROR);
recipe.sourceLine = rex.sourceLine;
return recipe;
return createRecipe(cx, sourceLine, args0);
} catch (Throwable cause) {
var r = schemaType.schema.recipeFactory.create(this, sourceLine);
r.creationError = true;
event.failedCount.incrementAndGet();
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);
r.newRecipe = true;
return r;
}
}

public KubeRecipe createRecipe(Context cx, Object[] args) {
var sourceLine = SourceLine.of(cx);

public KubeRecipe createRecipe(Context cx, SourceLine sourceLine, Object[] args) {
try {
for (int i = 0; i < args.length; i++) {
args[i] = Wrapper.unwrapped(args[i]);
Expand Down
44 changes: 23 additions & 21 deletions src/main/java/dev/latvian/mods/kubejs/recipe/RecipesKubeEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import dev.latvian.mods.kubejs.DevProperties;
import dev.latvian.mods.kubejs.bindings.event.ServerEvents;
import dev.latvian.mods.kubejs.core.RecipeManagerKJS;
import dev.latvian.mods.kubejs.error.InvalidRecipeComponentException;
import dev.latvian.mods.kubejs.error.KubeRuntimeException;
import dev.latvian.mods.kubejs.error.UnknownRecipeTypeException;
import dev.latvian.mods.kubejs.event.EventExceptionHandler;
Expand All @@ -31,6 +32,7 @@
import dev.latvian.mods.kubejs.server.ServerScriptManager;
import dev.latvian.mods.kubejs.util.ID;
import dev.latvian.mods.kubejs.util.JsonIO;
import dev.latvian.mods.kubejs.util.JsonUtils;
import dev.latvian.mods.kubejs.util.RegistryAccessContainer;
import dev.latvian.mods.kubejs.util.TimeJS;
import dev.latvian.mods.kubejs.util.UtilsJS;
Expand Down Expand Up @@ -278,6 +280,7 @@ public void post(RecipeManagerKJS recipeManager, Map<ResourceLocation, JsonEleme
ConsoleJS.SERVER.debug("Loaded recipe " + recipeIdAndType + ": " + recipe.getFromToString());
}
}
} catch (InvalidRecipeComponentException ignore) {
} catch (Throwable ex) {
if (DevProperties.get().logErroringRecipes) {
ConsoleJS.SERVER.warn("Failed to parse recipe '" + recipeIdAndType + "'! Falling back to vanilla", ex, POST_SKIP_ERROR);
Expand Down Expand Up @@ -381,7 +384,6 @@ public void post(RecipeManagerKJS recipeManager, Map<ResourceLocation, JsonEleme
private RecipeHolder<?> createRecipe(KubeRecipe r) {
try {
var rec = r.createRecipe();

var path = r.kjs$getMod() + "/" + r.getPath();

if (!r.removed && DataExport.export != null) {
Expand Down Expand Up @@ -415,11 +417,6 @@ public Map<String, Object> getRecipes() {
}

public KubeRecipe addRecipe(KubeRecipe r, boolean json) {
if (r instanceof ErroredKubeRecipe) {
ConsoleJS.SERVER.warn("Tried to add errored recipe %s!".formatted(r));
return r;
}

addedRecipes.add(r);

if (DevProperties.get().logAddedRecipes) {
Expand Down Expand Up @@ -539,24 +536,29 @@ public RecipeTypeFunction getRecipeFunction(@Nullable String id) {
}

public KubeRecipe custom(Context cx, JsonObject json) {
try {
if (json == null || !json.has("type")) {
throw new KubeRuntimeException("JSON must contain 'type'!");
}
if (json == null || !json.has("type")) {
throw new KubeRuntimeException("JSON must contain 'type'!");
}

var type = getRecipeFunction(json.get("type").getAsString());
var type = getRecipeFunction(json.get("type").getAsString());

if (type == null) {
throw new UnknownRecipeTypeException(json.get("type").getAsString());
}
if (type == null) {
throw new UnknownRecipeTypeException(json.get("type").getAsString());
}

var recipe = type.schemaType.schema.deserialize(SourceLine.of(cx), type, null, json);
recipe.afterLoaded();
return addRecipe(recipe, true);
} catch (KubeRuntimeException rex) {
var recipe = new ErroredKubeRecipe(this, "Failed to create custom JSON recipe from '%s'".formatted(json), rex, POST_SKIP_ERROR);
recipe.sourceLine = rex.sourceLine;
return recipe;
var sourceLine = SourceLine.of(cx);

try {
var r = type.schemaType.schema.deserialize(sourceLine, type, null, json);
r.afterLoaded();
return addRecipe(r, true);
} catch (Throwable cause) {
var r = type.schemaType.schema.recipeFactory.create(type, sourceLine);
r.creationError = true;
ConsoleJS.SERVER.error("Failed to create custom recipe from json " + JsonUtils.toString(json), sourceLine, cause, POST_SKIP_ERROR);
r.json = json;
r.newRecipe = true;
return r;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dev.latvian.mods.kubejs.recipe.component;

import net.minecraft.world.item.crafting.CookingBookCategory;
import net.minecraft.world.item.crafting.CraftingBookCategory;

public class BookCategoryComponent {
public static final EnumComponent<CraftingBookCategory> CRAFTING_BOOK_CATEGORY = EnumComponent.of("crafting_book_category", CraftingBookCategory.class, CraftingBookCategory.CODEC);
public static final EnumComponent<CookingBookCategory> COOKING_BOOK_CATEGORY = EnumComponent.of("cooking_book_category", CookingBookCategory.class, CookingBookCategory.CODEC);
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ public void buildUniqueId(UniqueIdBuilder builder, Either<H, L> value) {
}
}

@Override
public void validate(Either<H, L> value) {
var left = value.left();

if (left.isEmpty()) {
high.validate(left.get());
} else {
low.validate(value.right().get());
}
}

@Override
public boolean isEmpty(Either<H, L> value) {
return value.map(high::isEmpty, low::isEmpty);
Expand Down
Loading

0 comments on commit 85a63f0

Please sign in to comment.