diff --git a/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java b/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java
index bc0b260a4..4e888f7e8 100644
--- a/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java
+++ b/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java
@@ -15,7 +15,7 @@
import com.cleanroommc.groovyscript.event.EventHandler;
import com.cleanroommc.groovyscript.helper.JsonHelper;
import com.cleanroommc.groovyscript.helper.StyleConstant;
-import com.cleanroommc.groovyscript.mapper.ObjectMapper;
+import com.cleanroommc.groovyscript.mapper.AbstractObjectMapper;
import com.cleanroommc.groovyscript.mapper.ObjectMapperManager;
import com.cleanroommc.groovyscript.network.CReload;
import com.cleanroommc.groovyscript.network.NetworkHandler;
@@ -166,7 +166,7 @@ public static void initializeGroovyPreInit() {
ObjectMapperManager.init();
StandardInfoParserRegistry.init();
ModSupport.init();
- for (ObjectMapper> goh : ObjectMapperManager.getObjectMappers()) {
+ for (AbstractObjectMapper> goh : ObjectMapperManager.getObjectMappers()) {
getSandbox().registerBinding(goh);
}
if (FMLLaunchHandler.isDeobfuscatedEnvironment()) Documentation.generate();
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/BetterWithAddons.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/BetterWithAddons.java
index 39fba6f0a..2f2e80b9b 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/BetterWithAddons.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/BetterWithAddons.java
@@ -27,13 +27,19 @@ public BetterWithAddons() {
}
/**
- * Because Better With Addons checks if the ingredient is an instanceof {@link betterwithaddons.util.IHasSize IHasSize}
- * to determine if the Ingredient has an amount, we have to use their custom Ingredient.
- *
- * If this isn't used, then the recipe may appear correctly in JEI but will actually only consume 1 when done in-game.
+ * This is required for grs to not crash when bwm is not loaded for some reason.
*/
- public static Ingredient fromIIngredient(IIngredient ingredient) {
- return new IngredientSized(ingredient.toMcIngredient(), ingredient.getAmount());
+ public static class FromIngredient {
+
+ /**
+ * Because Better With Addons checks if the ingredient is an instanceof {@link betterwithaddons.util.IHasSize IHasSize}
+ * to determine if the Ingredient has an amount, we have to use their custom Ingredient.
+ *
+ * If this isn't used, then the recipe may appear correctly in JEI but will actually only consume 1 when done in-game.
+ */
+ public static Ingredient fromIIngredient(IIngredient ingredient) {
+ return new IngredientSized(ingredient.toMcIngredient(), ingredient.getAmount());
+ }
}
public static boolean isBetterWithEverything() {
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/DryingBox.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/DryingBox.java
index e674627ca..b7e13646e 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/DryingBox.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/DryingBox.java
@@ -77,7 +77,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable CherryBoxRecipe register() {
if (!validate()) return null;
- CherryBoxRecipe recipe = new CherryBoxRecipe(BlockCherryBox.CherryBoxType.DRYING, BetterWithAddons.fromIIngredient(input.get(0)), output.get(0));
+ CherryBoxRecipe recipe = new CherryBoxRecipe(BlockCherryBox.CherryBoxType.DRYING, BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), output.get(0));
ModSupport.BETTER_WITH_ADDONS.get().dryingBox.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/FireNet.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/FireNet.java
index b6fc35518..3787e7297 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/FireNet.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/FireNet.java
@@ -78,7 +78,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable NetRecipe register() {
if (!validate()) return null;
- NetRecipe recipe = new NetRecipe(BlockNettedScreen.SifterType.FIRE, BetterWithAddons.fromIIngredient(input.get(0)), 0, output.toArray(new ItemStack[0]));
+ NetRecipe recipe = new NetRecipe(BlockNettedScreen.SifterType.FIRE, BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), 0, output.toArray(new ItemStack[0]));
ModSupport.BETTER_WITH_ADDONS.get().fireNet.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Packing.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Packing.java
index 0e973d0de..52ba617f9 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Packing.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Packing.java
@@ -85,7 +85,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable PackingRecipe register() {
if (!validate()) return null;
- PackingRecipe recipe = new PackingRecipe(BetterWithAddons.fromIIngredient(input.get(0)), compress);
+ PackingRecipe recipe = new PackingRecipe(BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), compress);
recipe.setJeiOutput(IngredientHelper.isEmpty(jeiOutput) ? IngredientHelper.toItemStack(compress) : jeiOutput);
ModSupport.BETTER_WITH_ADDONS.get().packing.add(recipe);
return recipe;
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SandNet.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SandNet.java
index 5ebc2ea55..ff380da49 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SandNet.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SandNet.java
@@ -91,7 +91,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable NetRecipe register() {
if (!validate()) return null;
- NetRecipe recipe = new NetRecipe(BlockNettedScreen.SifterType.SAND, BetterWithAddons.fromIIngredient(input.get(0)), sand, output.toArray(new ItemStack[0]));
+ NetRecipe recipe = new NetRecipe(BlockNettedScreen.SifterType.SAND, BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), sand, output.toArray(new ItemStack[0]));
ModSupport.BETTER_WITH_ADDONS.get().sandNet.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SoakingBox.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SoakingBox.java
index 7f4bd8930..f8d468d8c 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SoakingBox.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/SoakingBox.java
@@ -77,7 +77,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable CherryBoxRecipe register() {
if (!validate()) return null;
- CherryBoxRecipe recipe = new CherryBoxRecipe(BlockCherryBox.CherryBoxType.SOAKING, BetterWithAddons.fromIIngredient(input.get(0)), output.get(0));
+ CherryBoxRecipe recipe = new CherryBoxRecipe(BlockCherryBox.CherryBoxType.SOAKING, BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), output.get(0));
ModSupport.BETTER_WITH_ADDONS.get().soakingBox.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Spindle.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Spindle.java
index 7ba87f7a3..6c463fc08 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Spindle.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Spindle.java
@@ -87,7 +87,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable SpindleRecipe register() {
if (!validate()) return null;
- SpindleRecipe recipe = new SpindleRecipe(popoff, BetterWithAddons.fromIIngredient(input.get(0)), output.toArray(new ItemStack[0]));
+ SpindleRecipe recipe = new SpindleRecipe(popoff, BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), output.toArray(new ItemStack[0]));
ModSupport.BETTER_WITH_ADDONS.get().spindle.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Tatara.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Tatara.java
index bca79cf67..ffd11f04c 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Tatara.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Tatara.java
@@ -76,7 +76,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable SmeltingRecipe register() {
if (!validate()) return null;
- SmeltingRecipe recipe = new SmeltingRecipe(BetterWithAddons.fromIIngredient(input.get(0)), output.get(0));
+ SmeltingRecipe recipe = new SmeltingRecipe(BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), output.get(0));
ModSupport.BETTER_WITH_ADDONS.get().tatara.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Transmutation.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Transmutation.java
index 85a268a5f..fb3f13575 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Transmutation.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/Transmutation.java
@@ -86,7 +86,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable TransmutationRecipe register() {
if (!validate()) return null;
- TransmutationRecipe recipe = new TransmutationRecipe(BetterWithAddons.fromIIngredient(input.get(0)), spirits, output.get(0));
+ TransmutationRecipe recipe = new TransmutationRecipe(BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), spirits, output.get(0));
ModSupport.BETTER_WITH_ADDONS.get().transmutation.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/WaterNet.java b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/WaterNet.java
index 39f21d325..6d1be3c1b 100644
--- a/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/WaterNet.java
+++ b/src/main/java/com/cleanroommc/groovyscript/compat/mods/betterwithaddons/WaterNet.java
@@ -80,7 +80,7 @@ public void validate(GroovyLog.Msg msg) {
@RecipeBuilderRegistrationMethod
public @Nullable NetRecipe register() {
if (!validate()) return null;
- NetRecipe recipe = new NetRecipe(BlockNettedScreen.SifterType.WATER, BetterWithAddons.fromIIngredient(input.get(0)), 0, output.toArray(new ItemStack[0]));
+ NetRecipe recipe = new NetRecipe(BlockNettedScreen.SifterType.WATER, BetterWithAddons.FromIngredient.fromIIngredient(input.get(0)), 0, output.toArray(new ItemStack[0]));
ModSupport.BETTER_WITH_ADDONS.get().waterNet.add(recipe);
return recipe;
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/AbstractObjectMapper.java b/src/main/java/com/cleanroommc/groovyscript/mapper/AbstractObjectMapper.java
new file mode 100644
index 000000000..ab602f29d
--- /dev/null
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/AbstractObjectMapper.java
@@ -0,0 +1,190 @@
+package com.cleanroommc.groovyscript.mapper;
+
+import com.cleanroommc.groovyscript.api.GroovyLog;
+import com.cleanroommc.groovyscript.api.INamed;
+import com.cleanroommc.groovyscript.api.IObjectParser;
+import com.cleanroommc.groovyscript.api.Result;
+import com.cleanroommc.groovyscript.compat.mods.GroovyContainer;
+import com.cleanroommc.groovyscript.helper.ArrayUtils;
+import com.cleanroommc.groovyscript.sandbox.expand.IDocumented;
+import com.cleanroommc.groovyscript.server.CompletionParams;
+import com.cleanroommc.groovyscript.server.Completions;
+import groovy.lang.Closure;
+import groovy.lang.groovydoc.Groovydoc;
+import groovy.lang.groovydoc.GroovydocHolder;
+import org.apache.commons.lang3.StringUtils;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+public abstract class AbstractObjectMapper extends Closure implements INamed, IDocumented, IObjectParser, TextureBinder {
+
+ private final String name;
+ private final GroovyContainer> mod;
+ private final Class returnType;
+ private final List[]> paramTypes;
+ protected String documentation = StringUtils.EMPTY;
+ private List methodNodes;
+
+ protected AbstractObjectMapper(String name, GroovyContainer> mod, Class returnType) {
+ super(null);
+ this.name = name;
+ this.mod = mod;
+ this.returnType = returnType;
+ this.paramTypes = new ArrayList<>();
+ addSignature(String.class);
+ }
+
+ /**
+ * Call in ctor to configure signatures
+ */
+ protected final void clearSignatures() {
+ this.paramTypes.clear();
+ this.methodNodes = null;
+ }
+
+ /**
+ * Call in ctor to configure signatures.
+ * By default, only `name(String)` exists.
+ */
+ protected final void addSignature(Class>... types) {
+ this.paramTypes.add(types);
+ this.methodNodes = null;
+ }
+
+ public final T doCall(String s, Object... args) {
+ return invokeWithDefault(false, s, args);
+ }
+
+ public final T doCall() {
+ return invokeDefault();
+ }
+
+ @Nullable
+ public final T invoke(boolean silent, String s, Object... args) {
+ Result t = Objects.requireNonNull(parse(s, args), "Object mapper must return a non null result!");
+ if (t.hasError()) {
+ if (!silent) {
+ if (this.mod == null) {
+ GroovyLog.get().error("Can't find {} for name {}!", name, s);
+ } else {
+ GroovyLog.get().error("Can't find {} {} for name {}!", mod, name, s);
+ }
+ if (t.getError() != null && !t.getError().isEmpty()) {
+ GroovyLog.get().error(" - reason: {}", t.getError());
+ }
+ }
+ return null;
+ }
+ return Objects.requireNonNull(t.getValue(), "Object mapper result must contain a non-null value!");
+ }
+
+ public final T invokeWithDefault(boolean silent, String s, Object... args) {
+ T t = invoke(silent, s, args);
+ return t != null ? t : invokeDefault();
+ }
+
+ public final T invokeDefault() {
+ Result t = getDefaultValue();
+ return t == null || t.hasError() ? null : t.getValue();
+ }
+
+ /**
+ * Returns a default value for this mapper. This is called every time the parser returns an errored result.
+ *
+ * @return default value of this mapper. May be null
+ */
+ public abstract Result getDefaultValue();
+
+ /**
+ * Adds all possible values this mapper can have at a param position.
+ * For example the `item()` mapper adds all item registry names when the index is 0.
+ *
+ * @param index the index of the param to complete
+ * @param params the values of all current (constant) params of the mapper
+ * @param items a list of completion items
+ */
+ public void provideCompletion(int index, CompletionParams params, Completions items) {}
+
+ /**
+ * Draws an image representation of the given object. This is used for lsp.
+ * The icon will show up in VSC or other code editors with compat.
+ * If this is implemented, {@link #hasTextureBinder()} must return true. Otherwise, this will not be used.
+ *
+ * @param t object for which a texture should be drawn.
+ */
+ public void bindTexture(T t) {}
+
+ /**
+ * Determines if {@link #bindTexture(Object)} is implemented and should be used.
+ *
+ * @return true if this mapper can bind textures
+ */
+ public abstract boolean hasTextureBinder();
+
+ @NotNull
+ public List getTooltip(T t) {
+ return Collections.emptyList();
+ }
+
+ public List getMethodNodes() {
+ if (methodNodes == null) {
+ this.methodNodes = new ArrayList<>();
+ for (Class>[] paramType : this.paramTypes) {
+ Parameter[] params = ArrayUtils.map(
+ paramType,
+ c -> new Parameter(ClassHelper.makeCached(c), ""),
+ new Parameter[paramType.length]);
+ MethodNode node = new MethodNode(
+ this.name,
+ Modifier.PUBLIC | Modifier.FINAL,
+ ClassHelper.makeCached(this.returnType),
+ params,
+ null,
+ null);
+ node.setDeclaringClass(
+ this.mod != null ? ClassHelper.makeCached(this.mod.get().getClass()) : ClassHelper.makeCached(ObjectMapperManager.class));
+ node.setNodeMetaData(GroovydocHolder.DOC_COMMENT, new Groovydoc(getDocumentation(), node));
+ this.methodNodes.add(node);
+ }
+ }
+ return methodNodes;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Collection getAliases() {
+ return Collections.singleton(this.name);
+ }
+
+ public GroovyContainer> getMod() {
+ return mod;
+ }
+
+ public Class getReturnType() {
+ return returnType;
+ }
+
+ public List[]> getParamTypes() {
+ return paramTypes;
+ }
+
+ @Override
+ public final String getDocumentation() {
+ return documentation;
+ }
+
+ protected final String docOfType(String type) {
+ String mod = this.mod == null ? StringUtils.EMPTY : this.mod.getContainerName() + ' ';
+ return "returns a " + mod + type;
+ }
+}
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/BlockStateMapper.java b/src/main/java/com/cleanroommc/groovyscript/mapper/BlockStateMapper.java
new file mode 100644
index 000000000..9221a5ef6
--- /dev/null
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/BlockStateMapper.java
@@ -0,0 +1,79 @@
+package com.cleanroommc.groovyscript.mapper;
+
+import com.cleanroommc.groovyscript.api.Result;
+import com.cleanroommc.groovyscript.compat.mods.GroovyContainer;
+import com.cleanroommc.groovyscript.server.CompletionParams;
+import com.cleanroommc.groovyscript.server.Completions;
+import net.minecraft.block.Block;
+import net.minecraft.block.properties.IProperty;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fml.common.registry.ForgeRegistries;
+import net.prominic.groovyls.util.CompletionItemFactory;
+import org.eclipse.lsp4j.CompletionItem;
+import org.eclipse.lsp4j.CompletionItemKind;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+public class BlockStateMapper extends AbstractObjectMapper {
+
+ public static final BlockStateMapper INSTANCE = new BlockStateMapper("blockstate", null);
+
+ protected BlockStateMapper(String name, GroovyContainer> mod) {
+ super(name, mod, IBlockState.class);
+ addSignature(String.class, int.class);
+ addSignature(String.class, String[].class);
+ this.documentation = docOfType("block state");
+ }
+
+ @Override
+ public Result getDefaultValue() {
+ return Result.some(Blocks.AIR.getDefaultState());
+ }
+
+ @Override
+ public @NotNull Result parse(String mainArg, Object[] args) {
+ return ObjectMappers.parseBlockState(mainArg, args);
+ }
+
+ @Override
+ public void provideCompletion(int index, CompletionParams params, Completions items) {
+ if (index == 0) items.addAllOfRegistry(ForgeRegistries.BLOCKS);
+ if (index >= 1 && params.isParamType(0, String.class)) {
+ Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(params.getParamAsType(0, String.class)));
+ if (block != null) {
+ // TODO completions for ints doesnt work properly
+ /*items.addAll(block.getBlockState().getValidStates(), state -> {
+ return CompletionItemFactory.createCompletion(CompletionItemKind.Value, String.valueOf(state.getBlock().getMetaFromState(state)));
+ });*/
+ for (IProperty property : block.getBlockState().getProperties()) {
+ items.addAll(property.getAllowedValues(), val -> {
+ CompletionItem item = CompletionItemFactory.createCompletion(CompletionItemKind.Constant, property.getName() + "=" + property.getName((Comparable) val));
+ return item;
+ });
+ }
+ }
+ }
+ }
+
+ @Override
+ public void bindTexture(IBlockState iBlockState) {
+ ItemStack itemStack = new ItemStack(iBlockState.getBlock(), 1, iBlockState.getBlock().getMetaFromState(iBlockState));
+ TextureBinder.ofItem().bindTexture(itemStack);
+ }
+
+ @Override
+ public @NotNull List getTooltip(IBlockState iBlockState) {
+ ItemStack itemStack = new ItemStack(iBlockState.getBlock(), 1, iBlockState.getBlock().getMetaFromState(iBlockState));
+ return Collections.singletonList(itemStack.getDisplayName());
+ }
+
+ @Override
+ public boolean hasTextureBinder() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/ItemStackMapper.java b/src/main/java/com/cleanroommc/groovyscript/mapper/ItemStackMapper.java
new file mode 100644
index 000000000..175a89073
--- /dev/null
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/ItemStackMapper.java
@@ -0,0 +1,66 @@
+package com.cleanroommc.groovyscript.mapper;
+
+import com.cleanroommc.groovyscript.api.Result;
+import com.cleanroommc.groovyscript.compat.mods.GroovyContainer;
+import com.cleanroommc.groovyscript.server.CompletionParams;
+import com.cleanroommc.groovyscript.server.Completions;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.RenderHelper;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fml.common.registry.ForgeRegistries;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ItemStackMapper extends AbstractObjectMapper {
+
+ public static final ItemStackMapper INSTANCE = new ItemStackMapper("item", null);
+
+ protected ItemStackMapper(String name, GroovyContainer> mod) {
+ super(name, mod, ItemStack.class);
+ addSignature(String.class, int.class);
+ this.documentation = docOfType("item stack");
+ }
+
+ @Override
+ public Result getDefaultValue() {
+ return Result.some(ItemStack.EMPTY);
+ }
+
+ @Override
+ public @NotNull Result parse(String mainArg, Object[] args) {
+ return ObjectMappers.parseItemStack(mainArg, args);
+ }
+
+ @Override
+ public void provideCompletion(int index, CompletionParams params, Completions items) {
+ if (index == 0) items.addAllOfRegistry(ForgeRegistries.ITEMS);
+ }
+
+ @Override
+ public void bindTexture(ItemStack item) {
+ GlStateManager.enableDepth();
+ RenderHelper.enableGUIStandardItemLighting();
+ var mc = Minecraft.getMinecraft();
+ var fontRenderer = item.getItem().getFontRenderer(item);
+ if (fontRenderer == null) fontRenderer = mc.fontRenderer;
+ mc.getRenderItem().renderItemAndEffectIntoGUI(null, item, 0, 0);
+ mc.getRenderItem().renderItemOverlayIntoGUI(fontRenderer, item, 0, 0, null);
+ GlStateManager.disableBlend();
+ RenderHelper.disableStandardItemLighting();
+ GlStateManager.enableAlpha();
+ GlStateManager.disableDepth();
+ }
+
+ @Override
+ public @NotNull List getTooltip(ItemStack itemStack) {
+ return Collections.singletonList(itemStack.getDisplayName());
+ }
+
+ @Override
+ public boolean hasTextureBinder() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapper.java b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapper.java
index cba110013..ad8c8f44e 100644
--- a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapper.java
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapper.java
@@ -1,26 +1,21 @@
package com.cleanroommc.groovyscript.mapper;
-import com.cleanroommc.groovyscript.api.*;
+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.ModSupport;
-import com.cleanroommc.groovyscript.helper.ArrayUtils;
import com.cleanroommc.groovyscript.sandbox.expand.IDocumented;
-import groovy.lang.Closure;
-import groovy.lang.groovydoc.Groovydoc;
-import groovy.lang.groovydoc.GroovydocHolder;
+import com.cleanroommc.groovyscript.server.CompletionParams;
+import com.cleanroommc.groovyscript.server.Completions;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.apache.commons.lang3.StringUtils;
-import org.codehaus.groovy.ast.ClassHelper;
-import org.codehaus.groovy.ast.MethodNode;
-import org.codehaus.groovy.ast.Parameter;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
-import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -30,7 +25,7 @@
*
* @param return type of the function
*/
-public class ObjectMapper extends Closure implements INamed, IDocumented {
+public class ObjectMapper extends AbstractObjectMapper {
/**
* Creates an object mapper builder.
@@ -46,124 +41,61 @@ public static Builder builder(String name, Class returnType) {
return new Builder<>(name, returnType);
}
- private final String name;
- private final GroovyContainer> mod;
private final IObjectParser handler;
private final Supplier> defaultValue;
- private final Class returnType;
- private final List[]> paramTypes;
private final Completer completer;
- private final String documentation;
private final TextureBinder textureBinder;
- private List methodNodes;
+ private final Function> tooltip;
- private ObjectMapper(String name, GroovyContainer> mod, IObjectParser handler, Supplier> defaultValue, Class returnType, List[]> paramTypes, Completer completer, String documentation, TextureBinder textureBinder) {
- super(null);
- this.name = name;
- this.mod = mod;
+ private ObjectMapper(String name, GroovyContainer> mod, IObjectParser handler, Supplier> defaultValue, Class returnType, List[]> paramTypes, Completer completer, String documentation, TextureBinder textureBinder, Function> tooltip) {
+ super(name, mod, returnType);
this.handler = handler;
this.defaultValue = defaultValue;
- this.returnType = returnType;
- this.paramTypes = paramTypes;
+ this.tooltip = tooltip;
this.completer = completer;
this.documentation = documentation;
this.textureBinder = textureBinder;
- }
-
- public @Nullable T invoke(boolean silent, String s, Object... args) {
- Result t = Objects.requireNonNull(handler.parse(s, args), "Object mapper must return a non null result!");
- if (t.hasError()) {
- if (!silent) {
- if (this.mod == null) {
- GroovyLog.get().error("Can't find {} for name {}!", name, s);
- } else {
- GroovyLog.get().error("Can't find {} {} for name {}!", mod, name, s);
- }
- if (t.getError() != null && !t.getError().isEmpty()) {
- GroovyLog.get().error(" - reason: {}", t.getError());
- }
- }
- return null;
+ clearSignatures();
+ for (Class>[] signature : paramTypes) {
+ addSignature(signature);
}
- return Objects.requireNonNull(t.getValue(), "Object mapper result must contain a non-null value!");
- }
-
- public T invokeWithDefault(boolean silent, String s, Object... args) {
- T t = invoke(silent, s, args);
- return t != null ? t : invokeDefault();
- }
-
- public T invokeDefault() {
- Result t = this.defaultValue.get();
- return t == null || t.hasError() ? null : t.getValue();
- }
-
- public GroovyContainer> getMod() {
- return mod;
}
@Override
- public Collection getAliases() {
- return Collections.singleton(this.name);
+ public @NotNull Result parse(String mainArg, Object[] args) {
+ return this.handler.parse(mainArg, args);
}
@Override
- public String getName() {
- return name;
+ public Result getDefaultValue() {
+ return defaultValue.get();
}
- public List[]> getParamTypes() {
- return this.paramTypes;
- }
-
- public Class getReturnType() {
- return returnType;
- }
-
- @GroovyBlacklist
- public Completer getCompleter() {
- return completer;
- }
-
- public T doCall(String s, Object... args) {
- return invokeWithDefault(false, s, args);
- }
-
- public T doCall() {
- return invokeDefault();
+ @Override
+ public void provideCompletion(int index, CompletionParams params, Completions items) {
+ if (this.completer != null) {
+ this.completer.complete(index, items);
+ }
}
@Override
- public String getDocumentation() {
- return documentation;
+ public void bindTexture(T t) {
+ if (this.textureBinder != null) {
+ this.textureBinder.bindTexture(t);
+ }
}
- public List getMethodNodes() {
- if (methodNodes == null) {
- this.methodNodes = new ArrayList<>();
- for (Class>[] paramType : this.paramTypes) {
- Parameter[] params = ArrayUtils.map(
- paramType,
- c -> new Parameter(ClassHelper.makeCached(c), ""),
- new Parameter[paramType.length]);
- MethodNode node = new MethodNode(
- this.name,
- Modifier.PUBLIC | Modifier.FINAL,
- ClassHelper.makeCached(this.returnType),
- params,
- null,
- null);
- node.setDeclaringClass(this.mod != null ? ClassHelper.makeCached(this.mod.get().getClass()) : ClassHelper.makeCached(ObjectMapperManager.class));
- node.setNodeMetaData(GroovydocHolder.DOC_COMMENT, new Groovydoc(this.documentation, node));
- this.methodNodes.add(node);
- }
+ @Override
+ public @NotNull List getTooltip(T t) {
+ if (this.tooltip != null) {
+ return this.tooltip.apply(t);
}
- return methodNodes;
+ return Collections.emptyList();
}
- @ApiStatus.Experimental
- public TextureBinder getTextureBinder() {
- return textureBinder;
+ @Override
+ public boolean hasTextureBinder() {
+ return this.textureBinder != null;
}
/**
@@ -182,6 +114,7 @@ public static class Builder {
private Completer completer;
private String documentation;
private TextureBinder textureBinder;
+ private Function> tooltip;
@ApiStatus.Internal
public Builder(String name, Class returnType) {
@@ -350,6 +283,46 @@ public Builder textureBinder(TextureBinder textureBinder) {
return this;
}
+ @ApiStatus.Experimental
+ public Builder textureBinder(Function mapper, TextureBinder binder) {
+ return textureBinder(TextureBinder.of(mapper, binder));
+ }
+
+ @ApiStatus.Experimental
+ public Builder textureBinderOfList(Function> mapper, TextureBinder binder) {
+ return textureBinder(TextureBinder.ofList(mapper, binder));
+ }
+
+ @ApiStatus.Experimental
+ public Builder textureBinderOfArray(Function mapper, TextureBinder binder) {
+ return textureBinder(TextureBinder.ofArray(mapper, binder));
+ }
+
+ public Builder tooltip(Function> tooltip) {
+ this.tooltip = tooltip;
+ return this;
+ }
+
+ public Builder tooltipOfValues(Function> values, Function toString) {
+ return tooltip(t -> {
+ List list = new ArrayList<>();
+ for (V v : values.apply(t)) {
+ list.add(toString.apply(v));
+ }
+ return list;
+ });
+ }
+
+ public Builder tooltipOfArray(Function values, Function toString) {
+ return tooltip(t -> {
+ List list = new ArrayList<>();
+ for (V v : values.apply(t)) {
+ list.add(toString.apply(v));
+ }
+ return list;
+ });
+ }
+
/**
* Registers the mapper.
*
@@ -366,7 +339,7 @@ public void register() {
});
if (this.defaultValue == null) this.defaultValue = () -> null;
this.documentation = IDocumented.toJavaDoc(this.documentation);
- ObjectMapper goh = new ObjectMapper<>(
+ ObjectMapper mapper = new ObjectMapper<>(
this.name,
this.mod,
this.handler,
@@ -375,8 +348,9 @@ public void register() {
this.paramTypes,
this.completer,
this.documentation,
- this.textureBinder);
- ObjectMapperManager.registerObjectMapper(this.mod, goh);
+ this.textureBinder,
+ this.tooltip);
+ ObjectMapperManager.registerObjectMapper(mapper);
}
}
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java
index 65220e86e..c5d71cd2e 100644
--- a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperManager.java
@@ -11,11 +11,11 @@
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;
import groovy.lang.ExpandoMetaClass;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.block.Block;
-import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.init.Blocks;
@@ -41,42 +41,43 @@
public class ObjectMapperManager {
- private static final Map> handlers = new Object2ObjectOpenHashMap<>();
- private static final Map>> handlerConflicts = new Object2ObjectOpenHashMap<>();
- private static final Map, Map>> modHandlers = new Object2ObjectOpenHashMap<>();
+ private static final Map> handlers = new Object2ObjectOpenHashMap<>();
+ private static final Map>> handlerConflicts = new Object2ObjectOpenHashMap<>();
+ private static final Map, Map>> modHandlers = new Object2ObjectOpenHashMap<>();
public static final String EMPTY = "empty";
public static final String WILDCARD = "*";
public static final String SPLITTER = ":";
- static void registerObjectMapper(GroovyContainer> container, ObjectMapper> goh) {
- String key = goh.getName();
- if (goh.getMod() != null) {
- Class> clazz = goh.getMod().get().getClass();
- for (Class>[] paramTypes : goh.getParamTypes()) {
+ public static void registerObjectMapper(AbstractObjectMapper> mapper) {
+ String key = mapper.getName();
+ if (mapper.getMod() != null) {
+ GroovyContainer> mod = mapper.getMod();
+ Class> clazz = mapper.getMod().get().getClass();
+ for (Class>[] paramTypes : mapper.getParamTypes()) {
ExpandoMetaClass emc = ExpansionHelper.getExpandoClass(clazz);
- emc.registerInstanceMethod(new ObjectMapperMetaMethod(goh, paramTypes, clazz));
+ emc.registerInstanceMethod(new ObjectMapperMetaMethod(mapper, paramTypes, clazz));
}
+ GroovyPropertyContainer propertyContainer = mod.get();
+ var map = modHandlers.computeIfAbsent(propertyContainer.getClass(), k -> new Object2ObjectOpenHashMap<>());
+ if (map.containsKey(key)) {
+ throw new IllegalStateException("There already is a ObjectMapper with name '" + key + "' in mod " + mod.getContainerName());
+ }
+ map.put(key, mapper);
}
if (handlerConflicts.containsKey(key)) {
- handlerConflicts.get(key).add(goh);
+ handlerConflicts.get(key).add(mapper);
} else if (handlers.containsKey(key)) {
- List> conflicts = handlerConflicts.computeIfAbsent(key, k -> new ArrayList<>());
+ List> conflicts = handlerConflicts.computeIfAbsent(key, k -> new ArrayList<>());
conflicts.add(handlers.remove(key));
- conflicts.add(goh);
+ conflicts.add(mapper);
} else {
- handlers.put(key, goh);
- }
- if (container != null) {
- GroovyPropertyContainer propertyContainer = container.get();
- var map = modHandlers.computeIfAbsent(propertyContainer.getClass(), k -> new Object2ObjectOpenHashMap<>());
- if (map.containsKey(key)) {
- throw new IllegalStateException("There already is a ObjectMapper with name '" + key + "' in mod " + container.getContainerName());
- }
- map.put(key, goh);
+ handlers.put(key, mapper);
}
}
public static void init() {
+ registerObjectMapper(ItemStackMapper.INSTANCE);
+ registerObjectMapper(BlockStateMapper.INSTANCE);
ObjectMapper.builder("resource", ResourceLocation.class)
.parser(ObjectMappers::parseResourceLocation)
.addSignature(String.class)
@@ -87,9 +88,10 @@ public static void init() {
.parser((s, args) -> s.contains(WILDCARD) ? Result.some(new OreDictWildcardIngredient(s)) : Result.some(new OreDictIngredient(s)))
.completerOfNames(OreDictionaryAccessor::getIdToName)
.docOfType("ore dict entry")
- .textureBinder(TextureBinder.of(i -> Arrays.asList(i.getMatchingStacks()), TextureBinder.ofItem(), i -> String.format("}) %s", i.getItem().getRegistryName(), i.getDisplayName())))
+ .textureBinder(TextureBinder.ofArray(IIngredient::getMatchingStacks, TextureBinder.ofItem()))
+ .tooltipOfArray(IIngredient::getMatchingStacks, i -> String.format("}) %s", i.getItem().getRegistryName(), i.getDisplayName()))
.register();
- ObjectMapper.builder("item", ItemStack.class)
+ /*ObjectMapper.builder("item", ItemStack.class)
.parser(ObjectMappers::parseItemStack)
.addSignature(String.class)
.addSignature(String.class, int.class)
@@ -97,7 +99,7 @@ public static void init() {
.completer(ForgeRegistries.ITEMS)
.docOfType("item stack")
.textureBinder(TextureBinder.ofItem())
- .register();
+ .register();*/
ObjectMapper.builder("liquid", FluidStack.class)
.parser(ObjectMappers::parseFluidStack)
.completerOfNames(FluidRegistry.getRegisteredFluids()::keySet)
@@ -108,6 +110,7 @@ public static void init() {
.parser(ObjectMappers::parseFluidStack)
.completerOfNames(FluidRegistry.getRegisteredFluids()::keySet)
.textureBinder(TextureBinder.ofFluid())
+ .tooltip(f -> Collections.singletonList(f.getLocalizedName()))
.register();
ObjectMapper.builder("block", Block.class)
.parser(IObjectParser.wrapForgeRegistry(ForgeRegistries.BLOCKS))
@@ -116,7 +119,7 @@ public static void init() {
.docOfType("block")
.textureBinder(TextureBinder.of(ItemStack::new, TextureBinder.ofItem()))
.register();
- ObjectMapper.builder("blockstate", IBlockState.class)
+ /*ObjectMapper.builder("blockstate", IBlockState.class)
.parser(ObjectMappers::parseBlockState)
.addSignature(String.class)
.addSignature(String.class, int.class)
@@ -124,7 +127,7 @@ public static void init() {
.completer(ForgeRegistries.BLOCKS)
.defaultValue(() -> Blocks.AIR.getBlockState().getBaseState())
.docOfType("block state")
- .register();
+ .register();*/
ObjectMapper.builder("enchantment", Enchantment.class)
.parser(IObjectParser.wrapForgeRegistry(ForgeRegistries.ENCHANTMENTS))
.completer(ForgeRegistries.ENCHANTMENTS)
@@ -222,8 +225,9 @@ public static void init() {
* @param silent if error messages should be logged
* @return game object or null
*/
+
public static @Nullable Object getGameObject(boolean silent, String name, String mainArg, Object... args) {
- ObjectMapper> objectMapper = handlers.get(name);
+ AbstractObjectMapper> objectMapper = handlers.get(name);
if (objectMapper != null) {
return objectMapper.invokeWithDefault(silent, mainArg, args);
}
@@ -234,32 +238,32 @@ public static boolean hasObjectMapper(String key) {
return handlers.containsKey(key);
}
- public static ObjectMapper> getObjectMapper(String key) {
+ public static AbstractObjectMapper> getObjectMapper(String key) {
return handlers.get(key);
}
- public static List> getConflicts(String key) {
+ public static List> getConflicts(String key) {
return handlerConflicts.get(key);
}
- public static ObjectMapper> getObjectMapper(Class> containerClass, String key) {
+ public static AbstractObjectMapper> getObjectMapper(Class> containerClass, String key) {
if (!GroovyPropertyContainer.class.isAssignableFrom(containerClass)) return null;
var map = modHandlers.get(containerClass);
return map != null ? map.get(key) : null;
}
- public static Collection> getObjectMappers() {
+ public static Collection> getObjectMappers() {
return handlers.values();
}
+ @Deprecated
public static Class> getReturnTypeOf(String name) {
- ObjectMapper> goh = handlers.get(name);
+ AbstractObjectMapper> goh = handlers.get(name);
return goh == null ? null : goh.getReturnType();
}
+ @Deprecated
public static void provideCompletion(String name, int index, Completions items) {
- Completer completer = handlers.get(name).getCompleter();
- if (completer == null) return;
- completer.complete(index, items);
+ handlers.get(name).provideCompletion(index, CompletionParams.EMPTY, items);
}
}
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperMetaMethod.java b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperMetaMethod.java
index 08e155e7a..4c5663a17 100644
--- a/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperMetaMethod.java
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/ObjectMapperMetaMethod.java
@@ -9,10 +9,10 @@
public class ObjectMapperMetaMethod extends MetaMethod implements IDocumented {
- private final ObjectMapper> closure;
+ private final AbstractObjectMapper> closure;
private final Class> owner;
- ObjectMapperMetaMethod(ObjectMapper> closure, Class>[] nativeParamTypes, Class> owner) {
+ ObjectMapperMetaMethod(AbstractObjectMapper> closure, Class>[] nativeParamTypes, Class> owner) {
super(nativeParamTypes);
this.closure = closure;
this.nativeParamTypes = nativeParamTypes;
diff --git a/src/main/java/com/cleanroommc/groovyscript/mapper/TextureBinder.java b/src/main/java/com/cleanroommc/groovyscript/mapper/TextureBinder.java
index 66fe30560..9fa4ad5f1 100644
--- a/src/main/java/com/cleanroommc/groovyscript/mapper/TextureBinder.java
+++ b/src/main/java/com/cleanroommc/groovyscript/mapper/TextureBinder.java
@@ -12,77 +12,70 @@
import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.ApiStatus;
-import java.util.Collections;
import java.util.List;
import java.util.function.Function;
-import java.util.stream.Collectors;
/**
* This interface draws objects, so they can be rendered as icons in an ide.
- *
+ *