Skip to content

Commit

Permalink
Added GT Recipe info exporter for EU/t, duration, fluids... Work rema…
Browse files Browse the repository at this point in the history
…ins to a) run everything *not* blocking on the graphics thread and b) find other info (other mods, etc) I might want to export
  • Loading branch information
Hermanoid committed Jan 12, 2024
1 parent 1893671 commit 271be98
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 10 deletions.
5 changes: 4 additions & 1 deletion src/main/java/com/hermanoid/nerd/NEI_NERD_Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import codechicken.nei.api.API;
import codechicken.nei.api.IConfigureNEI;
import com.hermanoid.nerd.info_extractors.GTDefaultRecipeInfoExtractor;

public class NEI_NERD_Config implements IConfigureNEI {
@Override
public void loadConfig() {
API.addOption(new RecipeDumper("tools.dump.recipes"));
RecipeDumper recipeDumper = new RecipeDumper("tools.dump.recipes");
recipeDumper.registerRecipeInfoExtractor(new GTDefaultRecipeInfoExtractor());
API.addOption(recipeDumper);
}

@Override
Expand Down
61 changes: 52 additions & 9 deletions src/main/java/com/hermanoid/nerd/RecipeDumper.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
import codechicken.nei.recipe.GuiCraftingRecipe;
import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.util.NBTJson;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonWriter;
import com.hermanoid.nerd.info_extractors.IRecipeInfoExtractor;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
Expand All @@ -37,6 +40,18 @@ public RecipeDumper(String name) {

public String version = "1.0";

public int totalQueries = -1;
public int dumpedQueries = -1;

private Multimap<String, IRecipeInfoExtractor> recipeInfoExtractors = HashMultimap.create();

public void registerRecipeInfoExtractor(IRecipeInfoExtractor extractor){
for(String id : extractor.getCompatibleHandlers())
recipeInfoExtractors.put(id, extractor);
}



@Override
public String[] header() {
return new String[] { "Name", "ID", "NBT", "Handler Name", "Handler Recipe Index", "Ingredients", "Other Items",
Expand Down Expand Up @@ -68,22 +83,34 @@ private JsonArray stacksToJsonArray(List<PositionedStack> stacks) {
return arr;
}

public JsonObject extractJsonRecipeData(ItemStack targetStack){
private static class QueryResult{
public ItemStack targetStack;
public List<ICraftingHandler> handlers;
}

private QueryResult performQuery(ItemStack targetStack){
QueryResult result = new QueryResult();
result.targetStack = targetStack;
result.handlers = GuiCraftingRecipe.getCraftingHandlers("item", targetStack);
return result;
}

private JsonObject extractJsonRecipeData(QueryResult queryResult){
// Gather item details (don't grab everything... you can dump items if you want more details)
// These columns will be repeated many times in the output, so don't write more than needed.

JsonObject queryDump = new JsonObject();
queryDump.add("query_item", stackToDetailedJson(targetStack));
queryDump.add("query_item", stackToDetailedJson(queryResult.targetStack));

JsonArray handlerDumpArr = new JsonArray();
// Perform the Query
List<ICraftingHandler> handlers = GuiCraftingRecipe.getCraftingHandlers("item", targetStack);
List<ICraftingHandler> handlers = queryResult.handlers;
for (ICraftingHandler handler : handlers) {

JsonObject handlerDump = new JsonObject();

// Gather crafting handler details (again, just a minimum, cross-reference a handler dump if you want)
handlerDump.addProperty("id", handler.getHandlerId());
String handlerId = handler.getHandlerId();
handlerDump.addProperty("id", handlerId);
handlerDump.addProperty("name", handler.getRecipeName());
handlerDump.addProperty("tab_name", handler.getRecipeTabName());

Expand All @@ -100,6 +127,11 @@ public JsonObject extractJsonRecipeData(ItemStack targetStack){
if (handler.getResultStack(recipeIndex) != null) {
recipeDump.add("out_item", stackToDetailedJson(handler.getResultStack(recipeIndex).item));
}
if(recipeInfoExtractors.containsKey(handlerId)){
for(IRecipeInfoExtractor extractor : recipeInfoExtractors.get(handlerId)){
recipeDump.add(extractor.getSlug(), extractor.extractInfo(handler, recipeIndex));
}
}
recipeDumpArr.add(recipeDump);
}
handlerDump.add("recipes", recipeDumpArr);
Expand All @@ -109,9 +141,14 @@ public JsonObject extractJsonRecipeData(ItemStack targetStack){
return queryDump;
}

public Stream<JsonObject> getQueryDumps() {
return ItemList.items.parallelStream()
.limit(4000)
public Stream<JsonObject> getQueryDumps(List<ItemStack> items) {
// TODO: Experiment with parallelization here
// Since the bulk of work here is the query, which is already parallel,
// I'm not sure how much performance gain (if any) this would cause.
return items.stream()
.limit(500)
.map(this::performQuery)
// .parallel()
.map(this::extractJsonRecipeData);
}

Expand Down Expand Up @@ -160,6 +197,9 @@ public void dumpJson(File file) throws IOException {
final FileWriter writer;
final JsonWriter jsonWriter;
final Gson gson = new Gson();
List<ItemStack> items = ItemList.items;
totalQueries = items.size();
dumpedQueries = 0;

try {
writer = new FileWriter(file);
Expand All @@ -174,10 +214,11 @@ public void dumpJson(File file) throws IOException {

// AtomicReference<IOException> error = new AtomicReference<>(null);

getQueryDumps().forEach(obj ->
getQueryDumps(items).forEach(obj ->
{
synchronized (lock){
gson.toJson(obj, jsonWriter);
dumpedQueries++;
}
});

Expand All @@ -194,6 +235,8 @@ public void dumpJson(File file) throws IOException {
} catch (IOException e) {
NEIClientConfig.logger.error("Filed to save dump recipe list to file {}", file, e);
}
totalQueries = -1;
dumpedQueries = -1;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.hermanoid.nerd.info_extractors;

import codechicken.nei.recipe.ICraftingHandler;
import codechicken.nei.util.NBTJson;
import com.google.gson.*;
import gregtech.api.util.GT_Recipe;
import gregtech.nei.GT_NEI_DefaultHandler;
import net.minecraftforge.fluids.FluidStack;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class GTDefaultRecipeInfoExtractor implements IRecipeInfoExtractor {
private static class GTRecipeExclusionStrategy implements ExclusionStrategy {
private final static Set<String> badFields = new HashSet<String>(Arrays.asList(
// Unnecessary/bulky info
"recipeCategory",
"stackTraces",
"owners",
// Rely on other dumping logic for inputs and outputs;
// auto-dumping Minecraft ItemStacks causes recursion into some nasty places
// I could make an adapter, but the generic NEI inputs/outputs logic covers these items no problemo
"mInputs",
"mOutputs",
// FluidStack things
// Icons are very large, not wise to have stored every time we dump an item
"stillIconResourceLocation",
"flowingIconResourceLocation",
"stillIcon",
"flowingIcon",
// There's a recursive fluid definition here
"registeredFluid",
// The automatic serializer doesn't like the rarity enum, I dunno why
"rarity",
// I don't think a fluid's corresponding block is useful, and it causes breaky recursion
"block"

));
@Override
public boolean shouldSkipField(FieldAttributes f) {

return badFields.contains(f.getName());
}

@Override
public boolean shouldSkipClass(Class<?> clazz) {
return clazz.equals(GT_NEI_DefaultHandler.class); // Block recursion
}
}

private class FluidStackSerializer implements JsonSerializer<FluidStack>{
@Override
public JsonElement serialize(FluidStack src, Type typeOfSrc, JsonSerializationContext context) {
// Fluids have some goofy unserializable things, similar to ItemStacks
JsonObject root = new JsonObject();
root.addProperty("amount", src.amount);
if(src.tag != null){
root.add("tag", NBTJson.toJsonObject(src.tag));
}
JsonObject fluid = (JsonObject) gson.toJsonTree(src.getFluid());
// Manually serialize rarity bc wierdness
fluid.addProperty("rarity", src.getFluid().getRarity().toString());
// Slap on some info that's only available via method calls
fluid.addProperty("id", src.getFluidID());
root.add("fluid", fluid);
return root;
}
}

private Gson gson;
public GTDefaultRecipeInfoExtractor(){
gson = new GsonBuilder()
// We might be only doing serializations, but GSON will still create
// a type adapter and get stuck in nasty recursion/type access land
// if it thinks it might need to do deserialization.
.addSerializationExclusionStrategy(new GTRecipeExclusionStrategy())
.addDeserializationExclusionStrategy(new GTRecipeExclusionStrategy())
.registerTypeAdapter(FluidStack.class, new FluidStackSerializer())
.create();
}

@Override
public JsonElement extractInfo(ICraftingHandler handler, int recipeIndex) {
GT_NEI_DefaultHandler gthandler = (GT_NEI_DefaultHandler) handler;
GT_Recipe recipe = gthandler.getCache().get(recipeIndex).mRecipe;
return gson.toJsonTree(recipe);
}

@Override
public String[] getCompatibleHandlers() {
return new String[]{ "gregtech.nei.GT_NEI_DefaultHandler" };
}

@Override
public String getSlug() {
return "greg_data";
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.hermanoid.nerd.info_extractors;

import codechicken.nei.recipe.ICraftingHandler;
import com.google.gson.JsonElement;

public interface IRecipeInfoExtractor {
public JsonElement extractInfo(ICraftingHandler handler, int recipeIndex);

public String[] getCompatibleHandlers();

public String getSlug();
}

0 comments on commit 271be98

Please sign in to comment.