added recipe_data() and crafting_remaining_item() - for convenient crafting scripts.

This commit is contained in:
gnembon committed 2020-06-14 01:17:36 -03:00
1 parent 690c85f3b0
commit d098087e05
14 files changed
+270 -1

No files matched your search

+1
View File
@@ -11,6 +11,7 @@ version = project.minecraft_version+'-'+project.mod_version
group = project.maven_group
minecraft {
accessWidener "src/main/resources/carpet.accesswidener"
}
dependencies {
+46
View File
@@ -2940,6 +2940,50 @@ item_category('ender_pearl') => misc
item_category('stone') => building_blocks
</pre>
### `recipe_data(item, type?)`, `recipe_data(recipe, type?)`
returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items
that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its
direct name can be different.
Recipe type can take one of the following options:
* `'crafting'` - default, crafting table recipe
* `'smelting'` - furnace recipe
* `'blasting'` - blast furnace recipe
* `'smoking'` - smoker recipe
* `'campfire_cooking'` - campfire recipe
* `'stonecutting'` - stonecutter recipe
The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of
an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the
ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left
empty, and recipe specification as another list. Possible recipe specs is:
* `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3.
* `['shapeless']` - shapeless crafting
* `['smelting', duration, xp]` - smelting/cooking recipes
* `['cutting']` - stonecutter recipe
* `['special']` - special crafting recipe, typically not present in the crafting menu
* `['custom']` - other recipe types
Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one
of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it
can accept items of any tags.
Also note that some recipes leave some products in the crafting window, and these can be determined using
`crafting_remaining_item()` function
Examples:
<pre>
recipe_data('iron_ingot_from_nuggets')
recipe_data('iron_ingot')
recipe_data('glass', 'smelting')
</pre>
### `crafting_remaining_item(item)`
returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an
item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles.
### `inventory_size(inventory)`
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
@@ -3055,6 +3099,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
__on_player_interacts_with_entity(player, entity, hand)
__on_player_chooses_recipe(player, recipe, full_stack)
__on_player_switches_slot(player, from, to)
__on_player_attacks_entity(player, entity)
__on_player_takes_damage(player, amount, source, source_entity)
__on_player_deals_damage(player, amount, entity)
+2
View File
@@ -32,6 +32,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
__on_player_interacts_with_entity(player, entity, hand)
__on_player_chooses_recipe(player, recipe, full_stack)
__on_player_switches_slot(player, from, to)
__on_player_attacks_entity(player, entity)
__on_player_takes_damage(player, amount, source, source_entity)
__on_player_deals_damage(player, amount, entity)
@@ -12,6 +12,7 @@ blocks_daylight
blocks_movement
brightness
chunk_tickets
crafting_remaining_item
create_marker
current_dimension
day_time
@@ -67,6 +68,7 @@ power
property
query
random_tick
recipe_data
rect
reload_chunk
remove_all_markers
@@ -6,6 +6,7 @@ __on_player_attacks_entity
__on_player_breaks_block
__on_player_clicks_block
__on_player_changes_dimension
__on_player_chooses_recipe
__on_player_deploys_elytra
__on_player_drops_item
__on_player_drops_stack
@@ -21,6 +22,7 @@ __on_player_starts_sneaking
__on_player_starts_sprinting
__on_player_stops_sneaking
__on_player_stops_sprinting
__on_player_switches_slot
__on_player_uses_item
__on_player_wakes_up
__on_statistic
@@ -62,7 +62,9 @@
player spawn entity_id entity_list entity_area entity_selector query modify entity_event
stack_limit item_category inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
stack_limit item_category recipe_data crafting_remaining_item
inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
scan volume neighbours rect diamond
@@ -87,6 +89,7 @@
__on_player_breaks_block
__on_player_clicks_block
__on_player_changes_dimension
__on_player_chooses_recipe
__on_player_deploys_elytra
__on_player_drops_item
__on_player_drops_stack
@@ -102,6 +105,7 @@
__on_player_starts_sprinting
__on_player_stops_sneaking
__on_player_stops_sprinting
__on_player_switches_slot
__on_player_uses_item
__on_player_wakes_up
__on_statistic
@@ -0,0 +1,11 @@
package carpet.fakes;
import net.minecraft.item.ItemStack;
import java.util.Collection;
import java.util.List;
public interface IngredientInterface
{
List<Collection<ItemStack>> getRecipeStacks();
}
@@ -0,0 +1,12 @@
package carpet.fakes;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.Identifier;
import java.util.List;
public interface RecipeManagerInterface
{
List<Recipe<?>> getAllMatching(RecipeType type, Identifier output);
}
@@ -0,0 +1,26 @@
package carpet.mixins;
import carpet.fakes.IngredientInterface;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Mixin(Ingredient.class)
public class Ingredient_scarpetMixin implements IngredientInterface
{
@Shadow @Final private Ingredient.Entry[] entries;
@Override
public List<Collection<ItemStack>> getRecipeStacks()
{
List<Collection<ItemStack>> res = Arrays.stream(entries).map(Ingredient.Entry::getStacks).collect(Collectors.toList());
return res;
}
}
@@ -0,0 +1,32 @@
package carpet.mixins;
import carpet.fakes.RecipeManagerInterface;
import com.google.common.collect.Lists;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeManager;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Mixin(RecipeManager.class)
public class RecipeManager_scarpetMixin implements RecipeManagerInterface
{
@Shadow private Map<RecipeType<?>, Map<Identifier, Recipe<?>>> recipes;
@Override
public List<Recipe<?>> getAllMatching(RecipeType type, Identifier output)
{
Map<Identifier, Recipe<?>> typeRecipes = recipes.get(type);
if (typeRecipes.containsKey(output)) return Collections.singletonList(typeRecipes.get(output));
return Lists.newArrayList(typeRecipes.values().stream().filter(
r -> Registry.ITEM.getId(r.getOutput().getItem()).equals(output)).collect(Collectors.toList()));
}
}
@@ -2,8 +2,10 @@ package carpet.script;
import carpet.CarpetServer;
import carpet.fakes.ChunkTicketManagerInterface;
import carpet.fakes.IngredientInterface;
import carpet.fakes.MinecraftServerInterface;
import carpet.fakes.BiomeArrayInterface;
import carpet.fakes.RecipeManagerInterface;
import carpet.fakes.ServerChunkManagerInterface;
import carpet.fakes.StatTypeInterface;
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
@@ -80,6 +82,13 @@ import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.recipe.AbstractCookingRecipe;
import net.minecraft.recipe.CuttingRecipe;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeType;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.recipe.ShapelessRecipe;
import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ScoreboardCriterion;
import net.minecraft.scoreboard.ScoreboardObjective;
@@ -131,6 +140,7 @@ import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -1471,6 +1481,121 @@ public class CarpetExpression
return (_c, _t) -> res;
});
this.expr.addLazyFunction("recipe_data", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
if (lv.size() < 1) throw new InternalExpressionException("'recipe_data' requires at least one argument");
String recipeName = lv.get(0).evalValue(c).getString();
RecipeType type = RecipeType.CRAFTING;
if (lv.size() > 1)
{
String recipeType = lv.get(1).evalValue(c).getString();
try
{
type = Registry.RECIPE_TYPE.get(new Identifier(recipeType));
}
catch (InvalidIdentifierException ignored)
{
throw new InternalExpressionException("Unknown crafting category: " + recipeType);
}
}
List<Recipe<?>> recipes;
try
{
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
}
catch (InvalidIdentifierException ignored)
{
return LazyValue.NULL;
}
if (recipes.isEmpty())
return LazyValue.NULL;
List<Value> recipesOutput = new ArrayList<>();
for (Recipe<?> recipe: recipes)
{
ItemStack result = recipe.getOutput();
List<Value> ingredientValue = new ArrayList<>();
CarpetSettings.LOG.error("input size: "+recipe.getPreviewInputs().size());
recipe.getPreviewInputs().forEach(
ingredient ->
{
// I am flattening ingredient lists per slot.
// consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference
// in depths of lists.
List<Collection<ItemStack>> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks();
if (stacks.isEmpty())
{
ingredientValue.add(Value.NULL);
}
else
{
List<Value> alternatives = new ArrayList<>();
stacks.forEach(col -> col.stream().map(ListValue::fromItemStack).forEach(alternatives::add));
ingredientValue.add(ListValue.wrap(alternatives));
}
}
);
Value recipeSpec;
if (recipe instanceof ShapedRecipe)
{
recipeSpec = ListValue.of(
new StringValue("shaped"),
new NumericValue(((ShapedRecipe) recipe).getWidth()),
new NumericValue(((ShapedRecipe) recipe).getHeight())
);
}
else if (recipe instanceof ShapelessRecipe)
{
recipeSpec = ListValue.of(new StringValue("shapeless"));
}
else if (recipe instanceof AbstractCookingRecipe)
{
recipeSpec = ListValue.of(
new StringValue("smelting"),
new NumericValue(((AbstractCookingRecipe) recipe).getCookTime()),
new NumericValue(((AbstractCookingRecipe) recipe).getExperience())
);
}
else if (recipe instanceof CuttingRecipe)
{
recipeSpec = ListValue.of(new StringValue("cutting"));
}
else if (recipe instanceof SpecialCraftingRecipe)
{
recipeSpec = ListValue.of(new StringValue("special"));
}
else
{
recipeSpec = ListValue.of(new StringValue("custom"));
}
recipesOutput.add(ListValue.of(ListValue.fromItemStack(result), ListValue.wrap(ingredientValue), recipeSpec));
}
Value ret = ListValue.wrap(recipesOutput);
return (_c, _t) -> ret;
});
this.expr.addLazyFunction("crafting_remaining_item", 1, (c, t, lv) ->
{
String itemStr = lv.get(0).evalValue(c).getString();
Item item;
try
{
Identifier id = new Identifier(itemStr);
item = Registry.ITEM.get(id);
if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air"))
throw new InvalidIdentifierException("boo");
}
catch (InvalidIdentifierException ignored)
{
throw new InternalExpressionException("Incorrect item: "+itemStr);
}
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
return (_c, _t ) -> ret;
});
this.expr.addLazyFunction("inventory_size", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
+2
View File
@@ -0,0 +1,2 @@
accessWidener v1 named
Accessible class net/minecraft/recipe/Ingredient$Entry
+3
View File
@@ -157,6 +157,9 @@
"ServerWorld_scarpetEventMixin",
"HungerManagerMixin_scarpetEventsMixin",
"RecipeManager_scarpetMixin",
"Ingredient_scarpetMixin",
"PistonBlock_movableTEMixin",
"PistonBlockEntity_movableTEMixin",
"World_movableTEMixin",
+1
View File
@@ -24,6 +24,7 @@
"mixins": [
"carpet.mixins.json"
],
"accessWidener" : "carpet.accesswidener",
"depends": {
"minecraft": "1.15.x",