Skip to content

Commit

Permalink
Better erroring and skipped recipe logging
Browse files Browse the repository at this point in the history
LatvianModder committed Sep 7, 2024
1 parent 1b36c00 commit 11e827a
Showing 12 changed files with 125 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/main/java/dev/latvian/mods/kubejs/DevProperties.java
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ protected void load() {
logAddedRecipes = get("log_added_recipes", false);
logRemovedRecipes = get("log_removed_recipes", false);
logModifiedRecipes = get("log_modified_recipes", false);
logSkippedRecipes = get("log_skipped_recipes", false);
logSkippedRecipes = get("log_skipped_recipes", true);
logRecipeDebug = get("log_recipe_debug", false);
logSkippedTags = get("log_skipped_tags", false);
logErroringRecipes = get("log_erroring_recipes", true);
Original file line number Diff line number Diff line change
@@ -22,6 +22,8 @@ public interface DirectionWrapper {
Direction WEST = Direction.WEST;
Direction EAST = Direction.EAST;
Direction[] VALUES = Direction.values();
Direction[] NONE = new Direction[0];
Map<String, Direction> ALL = Map.copyOf(Arrays.stream(VALUES).collect(Collectors.toMap(Direction::getSerializedName, Function.identity())));
EnumSet<Direction> ALL_SET = EnumSet.allOf(Direction.class);
EnumSet<Direction> EMPTY_SET = EnumSet.noneOf(Direction.class);
}
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ public BlockEntityAttachment create(BlockEntityAttachmentInfo info, KubeBlockEnt
int rx = Math.max(0, maxReceive.orElse(0));
int tx = Math.max(0, maxExtract.orElse(0));
int auto = Math.max(0, autoOutput.orElse(0));
return new EnergyStorageAttachment(entity, capacity, rx, tx, auto, (auto > 0 ? info.directions() : DirectionWrapper.EMPTY_SET).toArray(new Direction[0]));
return new EnergyStorageAttachment(entity, capacity, rx, tx, auto, auto > 0 ? info.directions().isEmpty() ? DirectionWrapper.VALUES : info.directions().toArray(new Direction[0]) : DirectionWrapper.NONE);
}

@Override
@@ -49,14 +49,38 @@ public void setEnergyStored(int energy) {
}

public int addEnergy(int add, boolean simulate) {
int energyReceived = Mth.clamp(this.capacity - this.energy, 0, add);
int i = Mth.clamp(this.capacity - this.energy, 0, add);

if (!simulate && energyReceived > 0) {
energy += energyReceived;
if (!simulate && i > 0) {
energy += i;
attachment.entity.save();
}

return energyReceived;
return i;
}

public int removeEnergy(int remove, boolean simulate) {
int i = Math.max(energy, remove);

if (!simulate && i > 0) {
energy -= i;
attachment.entity.save();
}

return i;
}

public boolean useEnergy(int use, boolean simulate) {
if (energy >= use) {
if (!simulate) {
energy -= use;
attachment.entity.save();
}

return true;
}

return false;
}

@Override
Original file line number Diff line number Diff line change
@@ -266,8 +266,20 @@ public boolean mouseClicked(double d, double e, int i) {
}
}

private String fixSource(@Nullable String source) {
if (source != null && !source.isEmpty()) {
int c = source.indexOf(':');

if (c >= 0) {
return source.substring(c + 1);
}
}

return source;
}

public void open() {
var path = line.externalFile == null ? (line.sourceLines.isEmpty() || line.sourceLines.iterator().next().source().isEmpty() ? null : line.console.scriptType.path.resolve(line.sourceLines.iterator().next().source())) : line.externalFile;
var path = line.externalFile == null ? (line.sourceLines.isEmpty() || line.sourceLines.iterator().next().source().isEmpty() ? null : line.console.scriptType.path.resolve(fixSource(line.sourceLines.iterator().next().source()))) : line.externalFile;

if (path != null && Files.exists(path)) {
try {
35 changes: 11 additions & 24 deletions src/main/java/dev/latvian/mods/kubejs/recipe/RecipeHelper.java
Original file line number Diff line number Diff line change
@@ -2,15 +2,12 @@

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import dev.latvian.mods.kubejs.script.ConsoleJS;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.neoforged.neoforge.common.conditions.ConditionalOps;
import org.jetbrains.annotations.Nullable;

public interface RecipeHelper {
@@ -21,6 +18,8 @@ static RecipeHolder<?> fromJson(DynamicOps<JsonElement> ops, RecipeSerializer<?>
if (codec == null) {
if (errors) {
ConsoleJS.SERVER.error("Error parsing recipe " + id + ": Codec not found in " + serializer.getClass().getName());
} else {
RecipeManager.LOGGER.error("Error parsing recipe " + id + ": Codec not found in " + serializer.getClass().getName());
}

return null;
@@ -31,6 +30,8 @@ static RecipeHolder<?> fromJson(DynamicOps<JsonElement> ops, RecipeSerializer<?>
if (map.isEmpty()) {
if (errors) {
ConsoleJS.SERVER.error("Error parsing recipe " + id + ": Couldn't convert " + json + " to a map");
} else {
RecipeManager.LOGGER.error("Error parsing recipe " + id + ": Couldn't convert " + json + " to a map");
}

return null;
@@ -45,38 +46,24 @@ static RecipeHolder<?> fromJson(DynamicOps<JsonElement> ops, RecipeSerializer<?>
} else if (recipe.error().isPresent()) {
if (errors) {
ConsoleJS.SERVER.error("Error parsing recipe " + id + ": " + recipe.error().get().message());
} else {
RecipeManager.LOGGER.error("Error parsing recipe " + id + ": " + recipe.error().get().message());
}
} else {
if (errors) {
ConsoleJS.SERVER.error("Error parsing recipe " + id + ": Unknown");
} else {
RecipeManager.LOGGER.error("Error parsing recipe " + id + ": Unknown");
}
}
} catch (Exception e) {
if (errors) {
ConsoleJS.SERVER.error("Error parsing recipe " + id + " from " + map.get(), e, RecipesKubeEvent.CREATE_RECIPE_SKIP_ERROR);
} else {
RecipeManager.LOGGER.error("Error parsing recipe " + id + " from " + map.get(), e);
}
}

return null;
}

static DataResult<JsonObject> validate(DynamicOps<JsonElement> ops, JsonElement jsonElement) {
if (!jsonElement.isJsonObject()) {
return DataResult.error(() -> "not a json object: " + jsonElement);
}

var json = GsonHelper.convertToJsonObject(jsonElement, "top element");

if (!json.has("type")) {
return DataResult.error(() -> "missing type");
}

var codec = ConditionalOps.createConditionalCodec(Codec.unit(json));

return codec.parse(ops, json)
.mapError(str -> "error while parsing conditions: " + str)
.flatMap(optional -> optional
.map(DataResult::success)
.orElseGet(() -> DataResult.error(() -> "conditions not met")));
}
}
61 changes: 54 additions & 7 deletions src/main/java/dev/latvian/mods/kubejs/recipe/RecipesKubeEvent.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.mojang.serialization.Codec;
import dev.latvian.mods.kubejs.CommonProperties;
import dev.latvian.mods.kubejs.DevProperties;
import dev.latvian.mods.kubejs.bindings.event.ServerEvents;
@@ -47,6 +48,7 @@
import net.minecraft.world.item.ItemStack;
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 net.neoforged.neoforge.common.conditions.ConditionalOps;
import org.jetbrains.annotations.Nullable;
@@ -240,30 +242,75 @@ public void post(RecipeManagerKJS recipeManager, Map<ResourceLocation, JsonEleme
for (var entry : datapackRecipeMap.entrySet()) {
var recipeId = entry.getKey();

//Forge: filter anything beginning with "_" as it's used for metadata.
if (recipeId == null || recipeId.getPath().startsWith("_")) {
continue; //Forge: filter anything beginning with "_" as it's used for metadata.
if (DevProperties.get().logSkippedRecipes) {
ConsoleJS.SERVER.info("Skipping recipe %s, filename starts with _".formatted(recipeId));
}

continue;
}

var jsonResult = RecipeHelper.validate(jsonOps, entry.getValue());
var originalJsonElement = entry.getValue();

if (!originalJsonElement.isJsonObject()) {
if (DevProperties.get().logSkippedRecipes) {
ConsoleJS.SERVER.warn("Skipping recipe %s, not a json object".formatted(recipeId));
} else {
RecipeManager.LOGGER.warn("Skipping recipe %s, not a json object".formatted(recipeId));
}

continue;
}

var originalJson = originalJsonElement.getAsJsonObject();

if (!originalJson.has("type")) {
if (DevProperties.get().logSkippedRecipes) {
ConsoleJS.SERVER.warn("Skipping recipe %s, not a json object".formatted(recipeId));
} else {
RecipeManager.LOGGER.warn("Skipping recipe %s, not a json object".formatted(recipeId));
}

continue;
}

var codec = ConditionalOps.createConditionalCodec(Codec.unit(originalJson));

var jsonResult = codec.parse(jsonOps, originalJson);

if (jsonResult.isError()) {
if (DevProperties.get().logSkippedRecipes) {
ConsoleJS.SERVER.error("Skipping recipe %s, error parsing conditions: %s".formatted(recipeId, jsonResult.error().get().message()));
} else {
RecipeManager.LOGGER.error("Skipping recipe %s, error parsing conditions: %s".formatted(recipeId, jsonResult.error().get().message()));
}

continue;
}

if (jsonResult.error().isPresent()) {
var error = jsonResult.error().get();
var result = jsonResult.result().get();

if (result.isEmpty()) {
if (DevProperties.get().logSkippedRecipes) {
ConsoleJS.SERVER.info("Skipping recipe %s, %s".formatted(recipeId, error.message()));
ConsoleJS.SERVER.info("Skipping recipe %s, conditions not met".formatted(recipeId));
} else {
RecipeManager.LOGGER.info("Skipping recipe %s, conditions not met".formatted(recipeId));
}

continue;
}

var json = jsonResult.getOrThrow(JsonParseException::new);
var json = result.get();
var typeStr = GsonHelper.getAsString(json, "type");
var recipeIdAndType = recipeId + "[" + typeStr + "]";
var type = getRecipeFunction(typeStr);

if (type == null) {
if (DevProperties.get().logSkippedRecipes) {
ConsoleJS.SERVER.info("Skipping recipe " + recipeId + ", unknown type: " + typeStr);
ConsoleJS.SERVER.warn("Skipping recipe " + recipeId + ", unknown type: " + typeStr);
} else {
RecipeManager.LOGGER.warn("Skipping recipe " + recipeId + ", unknown type: " + typeStr);
}

continue;
6 changes: 2 additions & 4 deletions src/main/java/dev/latvian/mods/kubejs/script/ConsoleJS.java
Original file line number Diff line number Diff line change
@@ -155,6 +155,7 @@ private synchronized void setCapturingErrors(boolean enabled) {
public synchronized void resetFile() {
errors.clear();
warnings.clear();
KubeJSWeb.broadcastEvent(wsBroadcaster, "clear", "", null);
scriptType.executor.execute(() -> {
try {
Files.write(logFile, List.of());
@@ -246,10 +247,7 @@ private ConsoleLine log(LogType type, SourceLine sourceLine, @Nullable Throwable

if (writeToFile) {
writeToFile(type, line.timestamp, line.getText());

if (wsBroadcaster != null) {
KubeJSWeb.broadcastEvent(wsBroadcaster, type.id, "", line);
}
KubeJSWeb.broadcastEvent(wsBroadcaster, type.id, "", line);
}

return line;
4 changes: 2 additions & 2 deletions src/main/java/dev/latvian/mods/kubejs/script/ConsoleLine.java
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@ public ConsoleLine withSourceLine(String source, int line) {
line = 0;
}

return source.isEmpty() && line == 0 ? this : withSourceLine(new SourceLine(source, line));
return source.isEmpty() && line == 0 ? this : withSourceLine(SourceLine.of(source, line));
}

public ConsoleLine withSourceLine(SourceLine sourceLine) {
@@ -119,7 +119,7 @@ public ConsoleLine withSourceLine(SourceLine sourceLine) {

public ConsoleLine withExternalFile(Path path) {
externalFile = path;
sourceLines = Set.of(new SourceLine(path.getFileName().toString(), 0));
sourceLines = Set.of(SourceLine.of(path.getFileName().toString(), 0));
return this;
}

Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import net.minecraft.tags.TagKey;

import java.lang.reflect.AccessibleObject;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@@ -230,4 +231,8 @@ public Object classOf(Object from) {
return loadJavaClass(String.valueOf(from), true).getClassObject();
}
}

public Map<String, Either<NativeJavaClass, Boolean>> getJavaClassCache() {
return javaClassCache == null ? Map.of() : Collections.unmodifiableMap(javaClassCache);
}
}
4 changes: 2 additions & 2 deletions src/main/java/dev/latvian/mods/kubejs/script/SourceLine.java
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ public static SourceLine of(@Nullable String source, int line) {
if ((source == null || source.isEmpty()) && line <= 0) {
return UNKNOWN;
} else {
return new SourceLine(source == null ? "" : source, Math.max(line, 0));
return new SourceLine(source == null || source.isEmpty() ? "" : source, Math.max(line, 0));
}
}

@@ -26,7 +26,7 @@ public static SourceLine of(@Nullable Context cx) {

int[] lineP = {0};
var source = Context.getSourcePositionFromStack(cx, lineP);
return new SourceLine(source == null ? "" : source, lineP[0]);
return SourceLine.of(source, lineP[0]);
}

public boolean isUnknown() {
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
import org.jetbrains.annotations.Nullable;

import java.nio.file.Files;
import java.nio.file.Path;
@@ -43,8 +44,8 @@ public class KubeJSWeb {
"logs", FMLPaths.GAMEDIR.get().resolve("logs")
);

public static int broadcastEvent(WSHandler<?, ?> handler, String event, String requiredTag, Supplier<JsonElement> payload) {
if (handler.sessions().isEmpty()) {
public static int broadcastEvent(@Nullable WSHandler<?, ?> handler, String event, String requiredTag, @Nullable Supplier<JsonElement> payload) {
if (handler == null || handler.sessions().isEmpty()) {
return 0;
}

1 change: 1 addition & 0 deletions src/main/resources/META-INF/accesstransformer.cfg
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ public net.minecraft.tags.TagEntry id # Lnet.minecraft.resources.ResourceLocatio
public net.minecraft.tags.TagEntry required # Z
public net.minecraft.tags.TagEntry tag # Z

public net.minecraft.world.item.crafting.RecipeManager LOGGER # Lorg.slf4j.Logger;
public net.minecraft.world.item.crafting.ShapelessRecipe$Serializer CODEC # Lcom.mojang.serialization.MapCodec;
# public net.minecraft.world.item.crafting.ShapedRecipe$Serializer CODEC Lcom.mojang.serialization.MapCodec;
# public net.minecraft.world.item.crafting.ShapedRecipePattern symmetrical # Z

0 comments on commit 11e827a

Please sign in to comment.