From c0627bf3fb0a54859099590ba64926afb8735578 Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 4 May 2025 19:01:06 -0400 Subject: [PATCH] recursive recipe solver --- docs/src/progress/renderertodo.md | 1 + .../data/item/source/ItemSourcingTree.java | 100 +++++++++++------- .../ai/nodes/plan/SolveSourcingTreeNode.java | 2 +- .../server/character/CharacterService.java | 26 ++++- .../electrosphere/server/macro/MacroData.java | 1 - .../server/macro/MacroDataLoader.java | 16 +++ 6 files changed, 104 insertions(+), 42 deletions(-) diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 050fdee4..f127336b 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1675,6 +1675,7 @@ Terrain items Digging produces terrain item form Terrain items can be placed to place terrain Recipe adjustment + voxel work +Recursive recipe item sourcing solver that keeps searching if a recipe fails to source diff --git a/src/main/java/electrosphere/game/data/item/source/ItemSourcingTree.java b/src/main/java/electrosphere/game/data/item/source/ItemSourcingTree.java index 6ef580cb..ac20d7ad 100644 --- a/src/main/java/electrosphere/game/data/item/source/ItemSourcingTree.java +++ b/src/main/java/electrosphere/game/data/item/source/ItemSourcingTree.java @@ -81,49 +81,77 @@ public class ItemSourcingTree { return this.itemSourceMap.get(this.rootItemId); } String currentRootId = this.rootItemId; - while(currentRootId != null){ - ItemSourcingData sourcingData = this.itemSourceMap.get(currentRootId); - if(sourcingData == null){ - throw new Error("Failed to find sourcing data for " + currentRootId); - } - if(sourcingData.harvestTargets.size() > 0){ - return sourcingData; - } - if(sourcingData.trees.size() > 0){ - //if we don't have an axe in inventory, consider it a dependency - if(!InventoryUtils.serverHasTool(entity, CommonEntityTokens.TOKEN_AXE)){ - currentRootId = ItemIdStrings.ITEM_STONE_AXE; - continue; - } - //we have an axe, return this sourcing data - return sourcingData; - } + return this.getCurrentDependencyRecursive(entity,itemIds,currentRootId); + } - currentRootId = null; - if(sourcingData.recipes.size() > 0){ - boolean foundAllIngredients = true; - RecipeData craftableRecipe = null; - for(RecipeData recipeData : sourcingData.recipes){ - //check if we have all the ingredients to craft this item - foundAllIngredients = true; - for(RecipeIngredientData ingredient : recipeData.getIngredients()){ - if(!itemIds.contains(ingredient.getItemType())){ - //ingredient is not in inventory - foundAllIngredients = false; - currentRootId = ingredient.getItemType(); - break; - } - } - if(foundAllIngredients){ - craftableRecipe = recipeData; + /** + * Gets the sourcing data for the current dependency + * @param entity The entity to check + * @param inventoryIds The contents of the entity's inventory + * @param currentRoot The current root item to search for + * @return The sourcing data for the current dependency + */ + private ItemSourcingData getCurrentDependencyRecursive(Entity entity, List inventoryIds, String currentRoot){ + ItemSourcingData sourcingData = this.itemSourceMap.get(currentRoot); + if(sourcingData == null){ + throw new Error("Failed to find sourcing data for " + currentRoot); + } + if(sourcingData.harvestTargets.size() > 0){ + return sourcingData; + } + if(sourcingData.trees.size() > 0){ + //if we don't have an axe in inventory, consider it a dependency + if(!InventoryUtils.serverHasTool(entity, CommonEntityTokens.TOKEN_AXE)){ + ItemSourcingData axeSource = this.getCurrentDependencyRecursive(entity, inventoryIds, ItemIdStrings.ITEM_STONE_AXE); + return axeSource; + } + //we have an axe, return this sourcing data + return sourcingData; + } + if(sourcingData.recipes.size() > 0){ + //the ingredient to source if we don't have all ingredients + ItemSourcingData ingredientToSource = null; + + //whether we have all ingredients already or not + boolean foundAllIngredients = false; + + //whether the recipe is source-able at all + boolean recipeIsSourceable = true; + + //the recipe + RecipeData craftableRecipe = null; + for(RecipeData recipeData : sourcingData.recipes){ + //check if we have all the ingredients to craft this item + foundAllIngredients = true; + recipeIsSourceable = true; + ingredientToSource = null; + for(RecipeIngredientData ingredient : recipeData.getIngredients()){ + ItemSourcingData sourcingForCurrentReagent = this.getCurrentDependencyRecursive(entity, inventoryIds, ingredient.getItemType()); + if(sourcingForCurrentReagent == null){ + recipeIsSourceable = false; break; } + if(!inventoryIds.contains(ingredient.getItemType())){ + //ingredient is not in inventory + foundAllIngredients = false; + ingredientToSource = sourcingForCurrentReagent; + } } - if(craftableRecipe != null){ - return sourcingData; + if(!recipeIsSourceable){ + continue; + } + if(foundAllIngredients){ + craftableRecipe = recipeData; + break; + } else { + return ingredientToSource; } } + if(craftableRecipe != null){ + return sourcingData; + } } + //unable to source this item return null; } diff --git a/src/main/java/electrosphere/server/ai/nodes/plan/SolveSourcingTreeNode.java b/src/main/java/electrosphere/server/ai/nodes/plan/SolveSourcingTreeNode.java index 3a4237b3..ac8ed4d4 100644 --- a/src/main/java/electrosphere/server/ai/nodes/plan/SolveSourcingTreeNode.java +++ b/src/main/java/electrosphere/server/ai/nodes/plan/SolveSourcingTreeNode.java @@ -39,7 +39,7 @@ public class SolveSourcingTreeNode implements AITreeNode { ItemSourcingTree sourcingTree = SolveSourcingTreeNode.getItemSourcingTree(blackboard); ItemSourcingData sourcingData = sourcingTree.getCurrentDependency(entity); if(sourcingData == null){ - throw new Error("Source data is null!"); + throw new Error("Source data is null! " + itemId); } //set the type to harvest if this is a harvest type if(sourcingData.getHarvestTargets().size() > 0){ diff --git a/src/main/java/electrosphere/server/character/CharacterService.java b/src/main/java/electrosphere/server/character/CharacterService.java index cab15004..1ec15bbf 100644 --- a/src/main/java/electrosphere/server/character/CharacterService.java +++ b/src/main/java/electrosphere/server/character/CharacterService.java @@ -48,10 +48,9 @@ public class CharacterService { public static CreatureTemplate getTemplate(int playerId, int characterId){ DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=? AND id=?;",playerId, characterId); if(result.hasResult()){ - Gson gson = new Gson(); //if we get a valid response from the database, check that it actually matches hashes for(DatabaseResultRow row : result){ - CreatureTemplate template = gson.fromJson(row.getAsString("dataVal"),CreatureTemplate.class); + CreatureTemplate template = SerializationUtils.deserialize(row.getAsString("dataVal"),CreatureTemplate.class); return template; } } @@ -66,13 +65,12 @@ public class CharacterService { */ public static List getCharacters(int playerId){ DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=?;",playerId); - Gson gson = new Gson(); List rVal = new LinkedList(); if(result.hasResult()){ //if we get a valid response from the database, check that it actually matches hashes for(DatabaseResultRow row : result){ CharacterDescription description = new CharacterDescription(); - CreatureTemplate template = gson.fromJson(row.getAsString("dataVal"),CreatureTemplate.class); + CreatureTemplate template = SerializationUtils.deserialize(row.getAsString("dataVal"),CreatureTemplate.class); description.setTemplate(template); description.setId(row.getAsInteger("id") + ""); rVal.add(description); @@ -150,4 +148,24 @@ public class CharacterService { ); } + /** + * Gets all characters + * @return The list of all characters + */ + public static List getAllCharacters(){ + DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData"); + List rVal = new LinkedList(); + if(result.hasResult()){ + //if we get a valid response from the database, check that it actually matches hashes + for(DatabaseResultRow row : result){ + CharacterDescription description = new CharacterDescription(); + CreatureTemplate template = SerializationUtils.deserialize(row.getAsString("dataVal"),CreatureTemplate.class); + description.setTemplate(template); + description.setId(row.getAsInteger("id") + ""); + rVal.add(description); + } + } + return rVal; + } + } diff --git a/src/main/java/electrosphere/server/macro/MacroData.java b/src/main/java/electrosphere/server/macro/MacroData.java index 34ab3d71..103a5624 100644 --- a/src/main/java/electrosphere/server/macro/MacroData.java +++ b/src/main/java/electrosphere/server/macro/MacroData.java @@ -21,7 +21,6 @@ import electrosphere.util.FileUtils; import java.util.Random; -import org.joml.Vector3d; import org.joml.Vector3i; /** diff --git a/src/main/java/electrosphere/server/macro/MacroDataLoader.java b/src/main/java/electrosphere/server/macro/MacroDataLoader.java index 2583abfc..62acef84 100644 --- a/src/main/java/electrosphere/server/macro/MacroDataLoader.java +++ b/src/main/java/electrosphere/server/macro/MacroDataLoader.java @@ -1,8 +1,12 @@ package electrosphere.server.macro; import java.io.File; +import java.util.List; +import electrosphere.client.entity.character.CharacterDescription; import electrosphere.game.data.block.BlockFab; +import electrosphere.server.character.CharacterService; +import electrosphere.server.macro.character.Character; import electrosphere.server.macro.structure.Structure; import electrosphere.util.FileUtils; @@ -33,6 +37,18 @@ public class MacroDataLoader { structure.setFab(fab); } + //inject characters from character service + List characters = CharacterService.getAllCharacters(); + for(CharacterDescription desc : characters){ + int targetId = Integer.parseInt(desc.getId()); + Character macroCharacter = rVal.getCharacter(targetId); + if(macroCharacter == null){ + macroCharacter = new Character(); + macroCharacter.setId(targetId); + rVal.characters.add(macroCharacter); + } + } + return rVal; }