diff --git a/examples/postInit/immersiveengineering.groovy b/examples/postInit/immersiveengineering.groovy index 18ede4d13..11e67ff60 100644 --- a/examples/postInit/immersiveengineering.groovy +++ b/examples/postInit/immersiveengineering.groovy @@ -79,6 +79,8 @@ mods.immersiveengineering.blueprint_crafting.recipeBuilder() .register() +mods.immersiveengineering.blueprint_crafting.streamRecipesByCategory('molds') + // Bottling Machine: // Converts an input itemstack and fluidstack into an output itemstack. diff --git a/examples/postInit/minecraft.groovy b/examples/postInit/minecraft.groovy index b61c1e19d..8cc3b8495 100644 --- a/examples/postInit/minecraft.groovy +++ b/examples/postInit/minecraft.groovy @@ -195,6 +195,11 @@ mods.minecraft.ore_dict.remove('netherStar', item('minecraft:nether_star')) mods.minecraft.ore_dict.add('ingotGold', item('minecraft:nether_star')) mods.minecraft.ore_dict.add('netherStar', item('minecraft:gold_ingot')) +mods.minecraft.ore_dict.getOres(~/.*/) +mods.minecraft.ore_dict.getOres(~/.*Gold/) +mods.minecraft.ore_dict.getOres(~/.*or.*/) +mods.minecraft.ore_dict.getOres('ingot*') + // Starting Inventory: // Sets the starting inventory of the player, including armor slots and offhand. diff --git a/src/main/java/com/cleanroommc/groovyscript/api/IOreDicts.java b/src/main/java/com/cleanroommc/groovyscript/api/IOreDicts.java index 2ff243962..eaf8f262c 100644 --- a/src/main/java/com/cleanroommc/groovyscript/api/IOreDicts.java +++ b/src/main/java/com/cleanroommc/groovyscript/api/IOreDicts.java @@ -1,11 +1,13 @@ package com.cleanroommc.groovyscript.api; -import java.util.List; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; /** - * Indicates that the IIngredient represents one or more oredicts. + * Indicates something that represents one or more oredicts, typically an {@link IIngredient}. */ -public interface IOreDicts extends IIngredient { +public interface IOreDicts { // TODO // There are a large number of places currently in the GroovyScript codebase // that check if something is "instanceof OreDictIngredient". @@ -13,7 +15,8 @@ public interface IOreDicts extends IIngredient { // and surrounding code changed appropriately. /** - * @return a list of oredict strings + * @return a collection of oredict strings */ - List getOreDicts(); + @UnmodifiableView + Collection getOreDicts(); } diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/OreDict.java b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/OreDict.java index 189f5cbea..ab3aceb0e 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/OreDict.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/OreDict.java @@ -2,12 +2,15 @@ import com.cleanroommc.groovyscript.api.GroovyBlacklist; import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.api.documentation.annotations.Example; import com.cleanroommc.groovyscript.api.documentation.annotations.MethodDescription; import com.cleanroommc.groovyscript.api.documentation.annotations.RegistryDescription; import com.cleanroommc.groovyscript.core.mixin.OreDictionaryAccessor; import com.cleanroommc.groovyscript.helper.Alias; +import com.cleanroommc.groovyscript.helper.SimpleObjectStream; import com.cleanroommc.groovyscript.helper.ingredient.IngredientHelper; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictMatcherIngredient; import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; import net.minecraft.block.Block; import net.minecraft.item.Item; @@ -18,6 +21,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; @RegistryDescription(category = RegistryDescription.Category.ENTRIES) public class OreDict extends VirtualizedRegistry { @@ -150,4 +154,23 @@ public void removeAll() { } } } + + @MethodDescription(type = MethodDescription.Type.QUERY) + public SimpleObjectStream streamOreNames() { + return new SimpleObjectStream<>(OreDictionaryAccessor.getIdToName()).setRemover(this::removeAll); + } + + @MethodDescription(type = MethodDescription.Type.QUERY, example = @Example("'ingot*'")) + public IIngredient getOres(String pattern) { + return new OreDictMatcherIngredient(pattern); + } + + @MethodDescription(type = MethodDescription.Type.QUERY, example = { + @Example("~/.*Gold/"), + @Example("~/.*or.*/"), + @Example("~/.*/"), + }) + public IIngredient getOres(Pattern pattern) { + return new OreDictMatcherIngredient(pattern); + } } diff --git a/src/main/java/com/cleanroommc/groovyscript/documentation/Registry.java b/src/main/java/com/cleanroommc/groovyscript/documentation/Registry.java index a5c96587b..51955ab55 100644 --- a/src/main/java/com/cleanroommc/groovyscript/documentation/Registry.java +++ b/src/main/java/com/cleanroommc/groovyscript/documentation/Registry.java @@ -134,6 +134,7 @@ public String exampleBlock() { } out.append(documentMethodDescriptionType(MethodDescription.Type.ADDITION)); out.append(documentMethodDescriptionType(MethodDescription.Type.VALUE)); + out.append(documentMethodDescriptionType(MethodDescription.Type.QUERY)); return out.toString(); } diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java index 20984463b..980b992b8 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/IngredientHelper.java @@ -98,7 +98,7 @@ public static IIngredient toIIngredient(FluidStack fluidStack) { public static @NotNull List> cartesianProductOres(@NotNull List inputs) { List> entries = new ArrayList<>(); for (var input : inputs) { - if (input instanceof IOreDicts ore) entries.add(ore.getOreDicts()); + if (input instanceof IOreDicts ore) entries.add(new ArrayList<>(ore.getOreDicts())); else entries.add(Arrays.asList(input.getMatchingStacks())); } return Lists.cartesianProduct(entries); diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java index 25180b59e..e79b63676 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java @@ -10,7 +10,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.List; public class ItemsIngredient extends IngredientBase implements Iterable { @@ -85,11 +84,6 @@ public boolean matches(ItemStack itemStack) { return false; } - // protected since modifying un-copied stack directly can result in unexpected results - protected List getItemStacks() { - return Collections.unmodifiableList(this.itemStacks); - } - @Override public @NotNull Iterator iterator() { return new AbstractIterator<>() { diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java index d1bcc72af..11ef055d6 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java @@ -2,25 +2,32 @@ import com.cleanroommc.groovyscript.api.IOreDicts; import com.cleanroommc.groovyscript.compat.vanilla.VanillaModule; +import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableList; import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; import net.minecraftforge.oredict.OreDictionary; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; +import java.util.Collection; +import java.util.Iterator; import java.util.List; -public class OreDictIngredient extends ItemsIngredient implements Iterable, IOreDicts { +public class OreDictIngredient extends IngredientBase implements Iterable, IOreDicts { private final String oreDict; + private int amount = 1; public OreDictIngredient(String oreDict) { - super(OreDictionary.getOres(oreDict)); this.oreDict = oreDict; } - // fast copy - private OreDictIngredient(String oreDict, List itemStacks) { - super(itemStacks); - this.oreDict = oreDict; + private static ItemStack selectItemStack(List stacks, int index, int amount) { + if (amount == 0 || stacks.isEmpty() || stacks.size() < index) return ItemStack.EMPTY; + ItemStack stack = stacks.get(index).copy(); + stack.setCount(amount); + return stack; } public String getOreDict() { @@ -28,19 +35,78 @@ public String getOreDict() { } @Override - public List getOreDicts() { + public @UnmodifiableView Collection getOreDicts() { return ImmutableList.of(getOreDict()); } @Override public OreDictIngredient exactCopy() { - OreDictIngredient oreDictIngredient = new OreDictIngredient(this.oreDict, getItemStacks()); - oreDictIngredient.setAmount(getAmount()); + OreDictIngredient oreDictIngredient = new OreDictIngredient(this.oreDict); + oreDictIngredient.amount = amount; oreDictIngredient.transformer = transformer; oreDictIngredient.matchCondition = matchCondition; return oreDictIngredient; } + @Override + public Ingredient toMcIngredient() { + return Ingredient.fromStacks(getMatchingStacks()); + } + + @Override + public ItemStack[] getMatchingStacks() { + var stacks = getItemStacks(); + ItemStack[] output = new ItemStack[stacks.size()]; + for (int i = 0; i < output.length; i++) { + output[i] = selectItemStack(stacks, i, amount); + } + return output; + } + + @Override + public ItemStack getAt(int index) { + return selectItemStack(getItemStacks(), index, amount); + } + + @Override + public int getAmount() { + return getItemStacks().isEmpty() ? 0 : amount; + } + + @Override + public void setAmount(int amount) { + this.amount = Math.max(0, amount); + } + + @Override + public boolean matches(ItemStack itemStack) { + for (ItemStack itemStack1 : getItemStacks()) { + if (OreDictionary.itemMatches(itemStack1, itemStack, false)) { + return true; + } + } + return false; + } + + private List getItemStacks() { + return OreDictionary.getOres(oreDict); + } + + @Override + public @NotNull Iterator iterator() { + return new AbstractIterator<>() { + + private int index = 0; + + @Override + protected ItemStack computeNext() { + var stacks = getItemStacks(); + if (index >= stacks.size()) return endOfData(); + return selectItemStack(stacks, index++, amount); + } + }; + } + @Override public String toString() { return "OreDictIngredient{ " + oreDict + " } * " + getAmount(); diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictMatcherIngredient.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictMatcherIngredient.java new file mode 100644 index 000000000..504553304 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictMatcherIngredient.java @@ -0,0 +1,110 @@ +package com.cleanroommc.groovyscript.helper.ingredient; + +import com.cleanroommc.groovyscript.api.IIngredient; +import com.cleanroommc.groovyscript.api.IOreDicts; +import com.cleanroommc.groovyscript.core.mixin.OreDictionaryAccessor; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import net.minecraftforge.oredict.OreDictionary; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; +import java.util.regex.Pattern; + +public class OreDictMatcherIngredient extends IngredientBase implements IOreDicts { + + private static final Pattern WILDCARD = Pattern.compile("\\*"); + + private final Collection oreDicts; + private final ItemStackList itemStacks; + private final Pattern pattern; + private int amount = 1; + + public OreDictMatcherIngredient(String pattern) { + this(Pattern.compile(WILDCARD.matcher(pattern).replaceAll(".*"))); + } + + public OreDictMatcherIngredient(Pattern pattern) { + this.pattern = pattern; + this.oreDicts = new ObjectOpenHashSet<>(); + this.itemStacks = new ItemStackList(); + generate(); + } + + private OreDictMatcherIngredient(Pattern pattern, Collection oreDicts, ItemStackList itemStacks) { + this.pattern = pattern; + this.oreDicts = new ObjectOpenHashSet<>(oreDicts); + this.itemStacks = new ItemStackList(itemStacks); + } + + private void generate() { + for (var ore : OreDictionaryAccessor.getIdToName()) { + if (pattern.matcher(ore).matches() && oreDicts.add(ore)) { + itemStacks.addAll(OreDictionary.getOres(ore)); + } + } + } + + @Override + public IIngredient exactCopy() { + var ingredient = new OreDictMatcherIngredient(pattern, oreDicts, itemStacks); + ingredient.amount = amount; + ingredient.transformer = transformer; + ingredient.matchCondition = matchCondition; + return ingredient; + } + + @Override + public Ingredient toMcIngredient() { + return Ingredient.fromStacks(getMatchingStacks()); + } + + @Override + public ItemStack[] getMatchingStacks() { + ItemStack[] stacks = new ItemStack[itemStacks.size()]; + for (int i = 0; i < stacks.length; i++) { + stacks[i] = getAt(i); + } + return stacks; + } + + @Override + public ItemStack getAt(int index) { + ItemStack stack = this.itemStacks.get(index).copy(); + stack.setCount(getAmount()); + return stack; + } + + @Override + public int getAmount() { + return itemStacks.isEmpty() ? 0 : amount; + } + + @Override + public void setAmount(int amount) { + this.amount = amount; + } + + @Override + public boolean matches(ItemStack itemStack) { + for (ItemStack itemStack1 : itemStacks) { + if (OreDictionary.itemMatches(itemStack1, itemStack, false)) { + return true; + } + } + return false; + } + + @Override + public @UnmodifiableView Collection getOreDicts() { + return oreDicts; + } + + public OreDictMatcherIngredient regenerate() { + oreDicts.clear(); + itemStacks.clear(); + generate(); + return this; + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictWildcardIngredient.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictWildcardIngredient.java deleted file mode 100644 index d77882a9d..000000000 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictWildcardIngredient.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.cleanroommc.groovyscript.helper.ingredient; - -import com.cleanroommc.groovyscript.api.IIngredient; -import com.cleanroommc.groovyscript.api.IOreDicts; -import com.cleanroommc.groovyscript.core.mixin.OreDictionaryAccessor; -import net.minecraft.item.ItemStack; -import net.minecraftforge.oredict.OreDictionary; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class OreDictWildcardIngredient extends ItemsIngredient implements IOreDicts { - - private final String oreDict; - private final List matchingOreDictionaries = new ArrayList<>(); - public final List ores = Collections.unmodifiableList(this.matchingOreDictionaries); - - private OreDictWildcardIngredient(String oreDict, List matchingOreDictionaries, List itemStacks) { - super(itemStacks); - this.oreDict = oreDict; - this.matchingOreDictionaries.addAll(matchingOreDictionaries); - } - - public OreDictWildcardIngredient(String oreDict) { - this.oreDict = oreDict; - List matchingOreDictionaries = new ArrayList<>(); - List stacks = new ArrayList<>(); - Pattern pattern = Pattern.compile(oreDict.replace("*", ".*")); - - for (String ore : OreDictionaryAccessor.getIdToName()) { - if (pattern.matcher(ore).matches()) { - matchingOreDictionaries.add(ore); - for (ItemStack stack : OreDictionary.getOres(ore)) { - stacks.add(stack.copy()); - } - } - } - this.matchingOreDictionaries.addAll(matchingOreDictionaries); - setItemStacks(stacks); - } - - public String getOreDict() { - return oreDict; - } - - @Override - public List getOreDicts() { - return getMatchingOreDictionaries(); - } - - public List getMatchingOreDictionaries() { - return ores; - } - - public List getOres() { - return this.ores.stream().map(OreDictIngredient::new).collect(Collectors.toList()); - } - - @Override - public IIngredient exactCopy() { - OreDictWildcardIngredient odwi = new OreDictWildcardIngredient(this.oreDict, this.matchingOreDictionaries, getItemStacks()); - odwi.setAmount(getAmount()); - odwi.transformer = transformer; - odwi.matchCondition = matchCondition; - return odwi; - } -} diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java index c5d71cd2e..1d69f5aec 100644 --- a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java +++ b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java @@ -2,14 +2,11 @@ import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.api.IObjectParser; -import com.cleanroommc.groovyscript.api.Result; import com.cleanroommc.groovyscript.compat.mods.GroovyContainer; import com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer; import com.cleanroommc.groovyscript.core.mixin.CreativeTabsAccessor; import com.cleanroommc.groovyscript.core.mixin.OreDictionaryAccessor; import com.cleanroommc.groovyscript.core.mixin.VillagerProfessionAccessor; -import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; -import com.cleanroommc.groovyscript.helper.ingredient.OreDictWildcardIngredient; import com.cleanroommc.groovyscript.sandbox.expand.ExpansionHelper; import com.cleanroommc.groovyscript.server.CompletionParams; import com.cleanroommc.groovyscript.server.Completions; @@ -85,7 +82,7 @@ public static void init() { .docOfType("resource location") .register(); ObjectMapper.builder("ore", IIngredient.class) - .parser((s, args) -> s.contains(WILDCARD) ? Result.some(new OreDictWildcardIngredient(s)) : Result.some(new OreDictIngredient(s))) + .parser(ObjectMappers::parseOreDict) .completerOfNames(OreDictionaryAccessor::getIdToName) .docOfType("ore dict entry") .textureBinder(TextureBinder.ofArray(IIngredient::getMatchingStacks, TextureBinder.ofItem())) diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMappers.java b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMappers.java index 6869cc3b1..c64205624 100644 --- a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMappers.java +++ b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMappers.java @@ -1,9 +1,12 @@ package com.cleanroommc.groovyscript.mapper; import com.cleanroommc.groovyscript.GroovyScript; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.api.Result; import com.cleanroommc.groovyscript.core.mixin.CreativeTabsAccessor; import com.cleanroommc.groovyscript.core.mixin.VillagerProfessionAccessor; +import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; import com.google.common.base.Optional; import com.google.common.collect.Iterators; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -28,7 +31,10 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; import static com.cleanroommc.groovyscript.mapper.ObjectMapperManager.SPLITTER; import static com.cleanroommc.groovyscript.mapper.ObjectMapperManager.WILDCARD; @@ -59,6 +65,23 @@ public class ObjectMappers { return Result.some(new ResourceLocation(GroovyScript.getRunConfig().getPackId(), mainArg)); } + public static @NotNull Result parseOreDict(String mainArg, Object... args) { + if (args.length > 0) { + return Result.error("Arguments not valid for object mapper. Use 'ore(String)'"); + } + if ("Unknown".equals(mainArg)) { + return Result.error("Unknown cannot be an OreDict"); + } + // TODO: remove this warning in a later update + if (mainArg.contains("*")) { + GroovyLog.msg("ore Object Mapper '{}' contained '*'", mainArg) + .add("if this is supposed to be an OreDictWildcardIngredient, use 'oredict.getOres(name)' instead") + .warn() + .post(); + } + return Result.some(new OreDictIngredient(mainArg)); + } + public static @NotNull Result parseItemStack(String mainArg, Object... args) { if (args.length > 1 || (args.length == 1 && !(args[0] instanceof Integer))) { return Result.error("Arguments not valid for bracket handler. Use 'item(String)' or 'item(String, int meta)'"); diff --git a/src/main/resources/assets/groovyscript/lang/en_us.lang b/src/main/resources/assets/groovyscript/lang/en_us.lang index a26101856..7cfad3f12 100644 --- a/src/main/resources/assets/groovyscript/lang/en_us.lang +++ b/src/main/resources/assets/groovyscript/lang/en_us.lang @@ -160,6 +160,8 @@ groovyscript.wiki.minecraft.ore_dict.remove=Removes the given itemstack from the groovyscript.wiki.minecraft.ore_dict.clear=Removes all itemstacks from the given oredict groovyscript.wiki.minecraft.ore_dict.exists=Returns true if the given oredict exists, although this does not check if the oredict contains entries groovyscript.wiki.minecraft.ore_dict.getItems=Returns a list of all itemstacks in the given oredict +groovyscript.wiki.minecraft.ore_dict.getOres=Returns an IIngredient containing all items for all matching oredicts at the time it is called +groovyscript.wiki.minecraft.ore_dict.streamOreNames=Iterates through every registered oredict, with the ability to clear all items from each one groovyscript.wiki.minecraft.ore_dict.removeAll=Removes all itemstacks from all oredicts groovyscript.wiki.minecraft.player.title=Starting Inventory