diff --git a/.nb-gradle-properties b/.nb-gradle-properties new file mode 100644 index 0000000..b18f6c4 --- /dev/null +++ b/.nb-gradle-properties @@ -0,0 +1,26 @@ + + + + + + debug + no + + runClient + + + + + + run + no + + runClient + + + + -Dfml.coreMods.load=minetweaker.mc172.transformer.MineTweakerTransformer + + + + diff --git a/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java b/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java index f20c64d..ebce36b 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java +++ b/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java @@ -1,6 +1,6 @@ package minetweaker; -import java.lang.annotation.Annotation; +import java.lang.reflect.Field; import minetweaker.runtime.IMineTweaker; import minetweaker.runtime.ILogger; import minetweaker.runtime.Tweaker; @@ -11,6 +11,10 @@ import minetweaker.runtime.GlobalRegistry; import stanhebben.zenscript.annotations.ZenClass; import stanhebben.zenscript.annotations.ZenExpansion; import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolJavaStaticField; +import stanhebben.zenscript.symbols.SymbolJavaStaticGetter; +import stanhebben.zenscript.symbols.SymbolJavaStaticMethod; +import stanhebben.zenscript.type.natives.JavaMethod; /** * Provides access to the MineTweaker API. @@ -67,12 +71,12 @@ public class MineTweakerAPI { * @param annotatedClass */ public static void registerClass(Class annotatedClass) { - for (Annotation annotation : annotatedClass.getAnnotations()) { - if (annotation instanceof ZenExpansion) { - GlobalRegistry.registerExpansion(annotatedClass); - } else if (annotation instanceof ZenClass) { - GlobalRegistry.registerNativeClass(annotatedClass); - } + if (annotatedClass.isAnnotationPresent(ZenExpansion.class)) { + GlobalRegistry.registerExpansion(annotatedClass); + } + + if (annotatedClass.isAnnotationPresent(ZenClass.class)) { + GlobalRegistry.registerNativeClass(annotatedClass); } } @@ -107,4 +111,63 @@ public class MineTweakerAPI { public static void registerBracketHandler(IBracketHandler handler) { GlobalRegistry.registerBracketHandler(handler); } + + /** + * Creates a symbol that refers to a java method. + * + * @param cls class that contains the method + * @param name method name + * @param arguments method argument types + * @return corresponding symbol + */ + public static IZenSymbol getJavaStaticMethodSymbol(Class cls, String name, Class... arguments) { + JavaMethod method = JavaMethod.get(GlobalRegistry.getTypeRegistry(), cls, name, arguments); + return new SymbolJavaStaticMethod(method); + } + + /** + * Creates a symbol that refers to a java getter. The getter must be a method + * with no arguments. The given symbol will act as a variable of which the + * value can be retrieved but not set. + * + * @param cls class that contains the getter method + * @param name name of the method + * @return corresponding symbol + */ + public static IZenSymbol getJavaStaticGetterSymbol(Class cls, String name) { + JavaMethod method = JavaMethod.get(GlobalRegistry.getTypeRegistry(), cls, name); + return new SymbolJavaStaticGetter(method); + } + + /** + * Creates a symbol that refers to a static field. The field must be an + * existing public field in the given class. The field will act as a + * variable that can be retrieved but not set. + * + * @param cls class that contains the field + * @param name field name (must be public) + * @return corresponding symbol + */ + public static IZenSymbol getJavaStaticFieldSymbol(Class cls, String name) { + try { + Field field = cls.getField(name); + return new SymbolJavaStaticField(cls, field, GlobalRegistry.getTypeRegistry()); + } catch (NoSuchFieldException ex) { + return null; + } catch (SecurityException ex) { + return null; + } + } + + /** + * Loads a Java method from an existing class. + * + * @param cls method class + * @param name method name + * @param arguments argument types + * @return java method + */ + public static JavaMethod getJavaMethod(Class cls, String name, Class... arguments) { + return JavaMethod.get(GlobalRegistry.getTypeRegistry(), cls, name, arguments); + } } diff --git a/MineTweaker3-API/src/main/java/minetweaker/annotations/BracketHandler.java b/MineTweaker3-API/src/main/java/minetweaker/annotations/BracketHandler.java new file mode 100644 index 0000000..a11697c --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/annotations/BracketHandler.java @@ -0,0 +1,18 @@ +package minetweaker.annotations; + +/** + * Marks a bracket handler. Bracket handlers are automatically registered. + * The marked class should have an empty constructor. + * + * @author Stan Hebben + */ +public @interface BracketHandler { + /** + * Indicates priority. A lower value means a higher priority. Only change + * if you have issues, default value is 10. Internal minetweaker handlers + * have priorities up to 100. + * + * @return + */ + public int priority() default 10; +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java index 57c1332..5d77593 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java @@ -8,9 +8,11 @@ package minetweaker.minecraft.item; import minetweaker.minecraft.data.IData; import minetweaker.minecraft.liquid.ILiquidStack; +import stanhebben.zenscript.annotations.OperatorType; import stanhebben.zenscript.annotations.ZenClass; import stanhebben.zenscript.annotations.ZenGetter; import stanhebben.zenscript.annotations.ZenMethod; +import stanhebben.zenscript.annotations.ZenOperator; /** * @@ -39,6 +41,10 @@ public interface IItemStack extends IIngredient { @ZenGetter("liquid") public ILiquidStack getLiquid(); + @ZenOperator(OperatorType.MUL) + @ZenMethod + public IItemStack amount(int amount); + @ZenMethod public IIngredient anyDamage(); diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java index dbe5e26..6fb3500 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java @@ -7,13 +7,15 @@ package minetweaker.minecraft.item; import java.util.List; +import minetweaker.minecraft.util.ArrayUtil; /** * * @author Stan */ public class IngredientAny implements IIngredient { - public static IngredientAny INSTANCE = new IngredientAny(); + public static final IngredientAny INSTANCE = new IngredientAny(); + public static Object INTERNAL_ANY = null; // platforms supporting an "any" item should fill it here private IngredientAny() {} @@ -40,36 +42,36 @@ public class IngredientAny implements IIngredient { @Override public IIngredient transform(IItemTransformer transformer) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return new IngredientAnyAdvanced(null, ArrayUtil.EMPTY_CONDITIONS, new IItemTransformer[] { transformer }); } @Override public IIngredient only(IItemCondition condition) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return new IngredientAnyAdvanced(null, new IItemCondition[] { condition }, ArrayUtil.EMPTY_TRANSFORMERS); } @Override public IIngredient marked(String mark) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return new IngredientAnyAdvanced(mark, ArrayUtil.EMPTY_CONDITIONS, ArrayUtil.EMPTY_TRANSFORMERS); } @Override public boolean matches(IItemStack item) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return true; } @Override public boolean contains(IIngredient ingredient) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return true; } @Override public IItemStack applyTransform(IItemStack item) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return item; } @Override public Object getInternal() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return INTERNAL_ANY; } } diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAnyAdvanced.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAnyAdvanced.java new file mode 100644 index 0000000..01fcd85 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAnyAdvanced.java @@ -0,0 +1,96 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import java.util.List; +import minetweaker.minecraft.util.ArrayUtil; + +/** + * + * @author Stan + */ +public class IngredientAnyAdvanced implements IIngredient { + private final String mark; + private final IItemCondition[] conditions; + private final IItemTransformer[] transformers; + + public IngredientAnyAdvanced(String mark, IItemCondition[] conditions, IItemTransformer[] transformers) { + this.mark = mark; + this.conditions = conditions; + this.transformers = transformers; + } + + @Override + public String getMark() { + return mark; + } + + @Override + public int getAmount() { + return 1; + } + + @Override + public List getItems() { + return null; + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(this, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientAnyAdvanced(mark, conditions, ArrayUtil.append(transformers, transformer)); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientAnyAdvanced(mark, ArrayUtil.append(conditions, condition), transformers); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientAnyAdvanced(mark, conditions, transformers); + } + + @Override + public boolean matches(IItemStack item) { + if (!item.matches(item)) return false; + + for (IItemCondition condition : conditions) { + if (!condition.matches(item)) return false; + } + + return true; + } + + @Override + public boolean contains(IIngredient ingredient) { + List iitems = ingredient.getItems(); + for (IItemStack iitem : iitems) { + if (!matches(iitem)) return false; + } + + return true; + } + + @Override + public IItemStack applyTransform(IItemStack item) { + for (IItemTransformer transform : transformers) { + item = transform.transform(item); + } + + return item; + } + + @Override + public Object getInternal() { + return IngredientAny.INTERNAL_ANY; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java index 6b1b3c0..7bcfcf2 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java @@ -99,4 +99,13 @@ public class IngredientItem implements IIngredient { public Object getInternal() { return item.getInternal(); } + + // ############################# + // ### Object implementation ### + // ############################# + + @Override + public String toString() { + return "(Ingredient) " + item.toString(); + } } diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java index dac6496..1ed30f1 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java @@ -21,6 +21,12 @@ public interface ILiquidDefinition { @ZenOperator(OperatorType.MUL) public ILiquidStack asStack(int millibuckets); + @ZenGetter("name") + public String getName(); + + @ZenGetter("displayName") + public String getDisplayName(); + @ZenGetter("luminosity") public int getLuminosity(); diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java index 2aabf47..90d090a 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java @@ -20,6 +20,12 @@ public interface ILiquidStack { @ZenGetter("definition") public ILiquidDefinition getDefinition(); + @ZenGetter("name") + public String getName(); + + @ZenGetter("displayName") + public String getDisplayName(); + @ZenGetter("amount") public int getAmount(); diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/player/IPlayer.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/player/IPlayer.java new file mode 100644 index 0000000..2931462 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/player/IPlayer.java @@ -0,0 +1,28 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.player; + +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.player.IPlayer") +public interface IPlayer { + @ZenGetter("name") + public String getName(); + + @ZenGetter("data") + public IData getData(); + + @ZenMethod + public void update(IData data); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java index 2a973b6..7fa8ed9 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java @@ -26,6 +26,9 @@ public interface ICraftingInventory { @ZenGetter("height") public int getHeight(); + @ZenGetter("stackCount") + public int getStackCount(); + @ZenMethod public IItemStack getStack(int i); diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java index 5fbdd33..39699f9 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java @@ -63,6 +63,12 @@ public class ShapedRecipe { this.width = width1; this.height = height1; this.mirrored = mirrored; + + System.out.println("ShapedRecipe width=" + width + ", height=" + height); + System.out.println("Ingredients: "); + for (int i = 0; i < ingredients.length; i++) { + System.out.println("(" + posx[i] + ", " + posy[i] + "): " + this.ingredients[i]); + } } public int getWidth() { @@ -94,19 +100,12 @@ public class ShapedRecipe { } public boolean matches(ICraftingInventory inventory) { - int numItems = 0; - for (int i = 0; i < inventory.getSize(); i++) { - if (inventory.getStack(i) != null) { - numItems++; - } - } - - if (numItems != ingredients.length) { + if (inventory.getStackCount() != ingredients.length) { return false; } - for (int i = 0; i < inventory.getWidth() - width; i++) { - out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int i = 0; i <= inventory.getWidth() - width; i++) { + out: for (int j = 0; j <= inventory.getHeight() - height; j++) { for (int k = 0; k < ingredients.length; k++) { IItemStack item = inventory.getStack(posx[k] + i, posy[k] + j); if (item == null) continue out; @@ -119,8 +118,8 @@ public class ShapedRecipe { } if (mirrored) { - for (int i = 0; i < inventory.getWidth() - width; i++) { - out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int i = 0; i <= inventory.getWidth() - width; i++) { + out: for (int j = 0; j <= inventory.getHeight() - height; j++) { for (int k = 0; k < ingredients.length; k++) { IItemStack item = inventory.getStack(inventory.getWidth() - (posx[k] + i), posy[k] + j); if (item == null) continue out; @@ -139,8 +138,8 @@ public class ShapedRecipe { public IItemStack getCraftingResult(ICraftingInventory inventory) { IItemStack[] stacks = new IItemStack[ingredients.length]; - for (int i = 0; i < inventory.getWidth() - width; i++) { - out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int i = 0; i <= inventory.getWidth() - width; i++) { + out: for (int j = 0; j <= inventory.getHeight() - height; j++) { for (int k = 0; k < ingredients.length; k++) { IItemStack item = inventory.getStack(posx[k] + i, posy[k] + j); if (item == null) continue out; @@ -154,8 +153,8 @@ public class ShapedRecipe { } if (mirrored) { - for (int i = 0; i < inventory.getWidth() - width; i++) { - out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int i = 0; i <= inventory.getWidth() - width; i++) { + out: for (int j = 0; j <= inventory.getHeight() - height; j++) { for (int k = 0; k < ingredients.length; k++) { IItemStack item = inventory.getStack(inventory.getWidth() - (posx[k] + i), posy[k] + j); if (item == null) continue out; diff --git a/MineTweaker3-API/src/main/java/minetweaker/runtime/GlobalRegistry.java b/MineTweaker3-API/src/main/java/minetweaker/runtime/GlobalRegistry.java index 04bd411..13d8a75 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/runtime/GlobalRegistry.java +++ b/MineTweaker3-API/src/main/java/minetweaker/runtime/GlobalRegistry.java @@ -18,7 +18,21 @@ import java.util.logging.Logger; import minetweaker.IBracketHandler; import minetweaker.IRecipeRemover; import minetweaker.MineTweakerAPI; +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.item.Condition; import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemCondition; +import minetweaker.minecraft.item.IItemDefinition; +import minetweaker.minecraft.item.IItemStack; +import minetweaker.minecraft.item.IItemTransformer; +import minetweaker.minecraft.item.Transform; +import minetweaker.minecraft.liquid.ILiquidDefinition; +import minetweaker.minecraft.liquid.ILiquidStack; +import minetweaker.minecraft.oredict.IOreDict; +import minetweaker.minecraft.oredict.IOreDictEntry; +import minetweaker.minecraft.recipes.ICraftingInventory; +import minetweaker.minecraft.recipes.IFurnaceManager; +import minetweaker.minecraft.recipes.IRecipeFunction; import stanhebben.zenscript.IZenErrorLogger; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.annotations.ZenExpansion; @@ -27,11 +41,12 @@ import stanhebben.zenscript.compiler.IEnvironmentGlobal; import stanhebben.zenscript.compiler.TypeRegistry; import stanhebben.zenscript.expression.partial.IPartialExpression; import stanhebben.zenscript.parser.Token; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.symbols.SymbolJavaStaticField; import stanhebben.zenscript.symbols.SymbolJavaStaticMethod; import stanhebben.zenscript.symbols.SymbolPackage; +import stanhebben.zenscript.symbols.SymbolType; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.type.ZenTypeNative; import stanhebben.zenscript.type.natives.JavaMethod; @@ -46,7 +61,7 @@ public class GlobalRegistry { private static final List removers = new ArrayList(); private static final List bracketHandlers = new ArrayList(); private static final TypeRegistry types = new TypeRegistry(); - private static final SymbolPackage root = new SymbolPackage(); + private static final SymbolPackage root = new SymbolPackage(""); private static final IZenErrorLogger errors = new MyErrorLogger(); private static final IZenCompileEnvironment environment = new MyCompileEnvironment(); private static final Map expansions = new HashMap(); @@ -73,7 +88,7 @@ public class GlobalRegistry { registerExpansion(ExpandShort.class); registerExpansion(ExpandString.class);*/ - /*registerNativeClass(IData.class); + registerNativeClass(IData.class); registerNativeClass(IIngredient.class); registerNativeClass(IItemCondition.class); @@ -91,7 +106,7 @@ public class GlobalRegistry { registerNativeClass(ICraftingInventory.class); registerNativeClass(IFurnaceManager.class); - registerNativeClass(IRecipeFunction.class);*/ + registerNativeClass(IRecipeFunction.class); } private GlobalRegistry() {} @@ -126,7 +141,7 @@ public class GlobalRegistry { ZenTypeNative type = new ZenTypeNative(cls); type.complete(types); - root.put(type.getName(), root, errors); + root.put(type.getName(), new SymbolType(type), errors); } public static TypeRegistry getTypeRegistry() { @@ -285,12 +300,12 @@ public class GlobalRegistry { @Override public void error(ZenPosition position, String message) { - MineTweakerAPI.logger.logError("[" + position.getFile() + ":" + position.getLine() + "] Error: " + message); + MineTweakerAPI.logger.logError(position.toString() + " > " + message); } @Override public void warning(ZenPosition position, String message) { - MineTweakerAPI.logger.logWarning("[" + position.getFile() + ":" + position.getLine() + "] Warning: " + message); + MineTweakerAPI.logger.logWarning(position.toString() + " > " + message); } } } diff --git a/MineTweaker3-API/src/main/java/minetweaker/runtime/ILogger.java b/MineTweaker3-API/src/main/java/minetweaker/runtime/ILogger.java index afa7841..2d5fcda 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/runtime/ILogger.java +++ b/MineTweaker3-API/src/main/java/minetweaker/runtime/ILogger.java @@ -13,6 +13,9 @@ import stanhebben.zenscript.annotations.ZenMethod; * @author Stan */ public interface ILogger { + @ZenMethod + public void logCommand(String message); + @ZenMethod public void logInfo(String message); diff --git a/MineTweaker3-API/src/main/java/minetweaker/runtime/IMineTweaker.java b/MineTweaker3-API/src/main/java/minetweaker/runtime/IMineTweaker.java index c97fefe..65cdf3a 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/runtime/IMineTweaker.java +++ b/MineTweaker3-API/src/main/java/minetweaker/runtime/IMineTweaker.java @@ -45,12 +45,22 @@ public interface IMineTweaker { public List rollback(); /** - * Registers a script provider. + * Sets the script provider. * * @param provider * @return */ - public Closeable registerScriptProvider(IScriptProvider provider); + public void setScriptProvider(IScriptProvider provider); + /** + * Executes all scripts provided by the script provider. + */ public void load(); + + /** + * Retrieves the data from the scripts that were loaded last. + * + * @return scripts data + */ + public byte[] getScriptData(); } diff --git a/MineTweaker3-API/src/main/java/minetweaker/runtime/Tweaker.java b/MineTweaker3-API/src/main/java/minetweaker/runtime/Tweaker.java index 06ac5dc..ebc759a 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/runtime/Tweaker.java +++ b/MineTweaker3-API/src/main/java/minetweaker/runtime/Tweaker.java @@ -7,7 +7,6 @@ package minetweaker.runtime; import java.io.BufferedInputStream; -import java.io.Closeable; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -21,13 +20,14 @@ import java.util.Set; import minetweaker.IUndoableAction; import minetweaker.MineTweakerAPI; import minetweaker.minecraft.item.IIngredient; -import minetweaker.minecraft.util.CloseablePriorityList; +import minetweaker.runtime.providers.ScriptProviderMemory; import stanhebben.zenscript.ZenModule; import static stanhebben.zenscript.ZenModule.compileScripts; import static stanhebben.zenscript.ZenModule.extractClassName; import stanhebben.zenscript.ZenParsedFile; import stanhebben.zenscript.ZenTokener; import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.parser.ParseException; /** * @@ -35,14 +35,17 @@ import stanhebben.zenscript.compiler.IEnvironmentGlobal; * @author Stan Hebben */ public class Tweaker implements IMineTweaker { - private final CloseablePriorityList scriptProviders = new CloseablePriorityList(); private final List actions = new ArrayList(); private final Set wereStuck = new HashSet(); + private IScriptProvider scriptProvider; + private byte[] scriptData; + @Override public void apply(IUndoableAction action) { MineTweakerAPI.logger.logInfo(action.describe()); action.apply(); + actions.add(action); } @Override @@ -68,49 +71,65 @@ public class Tweaker implements IMineTweaker { } @Override - public Closeable registerScriptProvider(IScriptProvider provider) { - return scriptProviders.add(provider); + public void setScriptProvider(IScriptProvider provider) { + scriptProvider = provider; } @Override public void load() { + scriptData = ScriptProviderMemory.collect(scriptProvider); Set executed = new HashSet(); - - for (IScriptProvider provider : scriptProviders) { - Iterator scripts = provider.getScripts(); - while (scripts.hasNext()) { - IScriptIterator script = scripts.next(); - - if (!executed.contains(script.getGroupName())) { - executed.add(script.getGroupName()); - - Map classes = new HashMap(); - IEnvironmentGlobal environmentGlobal = GlobalRegistry.makeGlobalEnvironment(classes); - - List files = new ArrayList(); - - while (script.next()) { - try { - String filename = script.getName(); - - String className = extractClassName(filename); - Reader reader = new InputStreamReader(new BufferedInputStream(script.open())); - ZenTokener parser = new ZenTokener(reader, environmentGlobal.getEnvironment()); - ZenParsedFile pfile = new ZenParsedFile(filename, className, parser, environmentGlobal); - files.add(pfile); - reader.close(); - } catch (IOException ex) { - MineTweakerAPI.logger.logError("Could not load script " + script.getName() + ": " + ex.getMessage()); - } + Iterator scripts = scriptProvider.getScripts(); + while (scripts.hasNext()) { + IScriptIterator script = scripts.next(); + + if (!executed.contains(script.getGroupName())) { + executed.add(script.getGroupName()); + + Map classes = new HashMap(); + IEnvironmentGlobal environmentGlobal = GlobalRegistry.makeGlobalEnvironment(classes); + + List files = new ArrayList(); + + while (script.next()) { + Reader reader = null; + try { + reader = new InputStreamReader(new BufferedInputStream(script.open())); + + String filename = script.getName(); + String className = extractClassName(filename); + + ZenTokener parser = new ZenTokener(reader, environmentGlobal.getEnvironment()); + ZenParsedFile pfile = new ZenParsedFile(filename, className, parser, environmentGlobal); + files.add(pfile); + } catch (IOException ex) { + MineTweakerAPI.logger.logError("Could not load script " + script.getName() + ": " + ex.getMessage()); + } catch (ParseException ex) { + MineTweakerAPI.logger.logError("Error parsing " + ex.getFile() + ":" + ex.getLine() + " -- " + ex.getExplanation()); + } catch (Exception ex) { + MineTweakerAPI.logger.logError("Error loading " + script.getName() + ": " + ex.getMessage()); + ex.printStackTrace(); } + if (reader != null) { + try { + reader.close(); + } catch (IOException ex) {} + } + } + + try { String filename = script.getGroupName(); + System.out.println("MineTweaker: Loading " + filename); compileScripts(filename, files, environmentGlobal); - + // execute scripts - ZenModule module = new ZenModule(classes); + ZenModule module = new ZenModule(classes, MineTweakerAPI.class.getClassLoader()); module.getMain().run(); + } catch (Exception ex) { + MineTweakerAPI.logger.logError("Error executing " + script.getGroupName() + ": " + ex.getMessage()); + ex.printStackTrace(); } } } @@ -122,4 +141,9 @@ public class Tweaker implements IMineTweaker { } } } + + @Override + public byte[] getScriptData() { + return scriptData; + } } diff --git a/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderCascade.java b/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderCascade.java new file mode 100644 index 0000000..4070bbb --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderCascade.java @@ -0,0 +1,74 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.runtime.providers; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import minetweaker.runtime.IScriptIterator; +import minetweaker.runtime.IScriptProvider; + +/** + * + * @author Stan Hebben + */ +public class ScriptProviderCascade implements IScriptProvider { + private final IScriptProvider[] providers; + + public ScriptProviderCascade(IScriptProvider... providers) { + this.providers = providers; + } + + @Override + public Iterator getScripts() { + return new MyScripterator(); + } + + private class MyScripterator implements Iterator { + private final Set executed = new HashSet(); + + private int currentIndex = providers.length - 1; + private Iterator current; + private IScriptIterator currentValue; + + public MyScripterator() { + currentIndex = providers.length - 1; + current = providers[currentIndex].getScripts(); + + advance(); + } + + @Override + public boolean hasNext() { + return currentIndex >= 0; + } + + @Override + public IScriptIterator next() { + IScriptIterator result = currentValue; + executed.add(result.getGroupName()); + + advance(); + + return result; + } + + private void advance() { + do { + while (!current.hasNext()) { + currentIndex--; + if (currentIndex < 0) return; + + current = providers[currentIndex].getScripts(); + } + if (currentIndex < 0) break; + + currentValue = current.next(); + } while (executed.contains(currentValue.getGroupName()) && hasNext()); + } + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderMemory.java b/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderMemory.java index 61dda12..5631211 100644 --- a/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderMemory.java +++ b/MineTweaker3-API/src/main/java/minetweaker/runtime/providers/ScriptProviderMemory.java @@ -31,33 +31,31 @@ import minetweaker.runtime.IScriptProvider; * @author Stan */ public class ScriptProviderMemory implements IScriptProvider { - public static byte[] collect(Iterable providers) { + public static byte[] collect(IScriptProvider provider) { ByteArrayOutputStream output = new ByteArrayOutputStream(); try { DeflaterOutputStream deflater = new DeflaterOutputStream(output); DataOutputStream deflaterData = new DataOutputStream(deflater); Set executed = new HashSet(); - for (IScriptProvider provider : providers) { - Iterator scripts = provider.getScripts(); - while (scripts.hasNext()) { - IScriptIterator script = scripts.next(); - - if (!executed.contains(script.getGroupName())) { - executed.add(script.getGroupName()); - - deflaterData.writeUTF(script.getGroupName()); - - while (script.next()) { - String name = script.getName(); - byte[] data = FileUtil.read(script.open()); - deflaterData.writeUTF(name); - deflaterData.writeInt(data.length); - deflaterData.write(data); - } - - deflaterData.writeUTF(""); + Iterator scripts = provider.getScripts(); + while (scripts.hasNext()) { + IScriptIterator script = scripts.next(); + + if (!executed.contains(script.getGroupName())) { + executed.add(script.getGroupName()); + + deflaterData.writeUTF(script.getGroupName()); + + while (script.next()) { + String name = script.getName(); + byte[] data = FileUtil.read(script.open()); + deflaterData.writeUTF(name); + deflaterData.writeInt(data.length); + deflaterData.write(data); } + + deflaterData.writeUTF(""); } } diff --git a/MineTweaker3-MC17-Main/build.gradle b/MineTweaker3-MC17-Main/build.gradle index e3c60b8..abdc042 100644 --- a/MineTweaker3-MC17-Main/build.gradle +++ b/MineTweaker3-MC17-Main/build.gradle @@ -26,3 +26,9 @@ dependencies { compile project(':MineTweaker3-API') } } + +jar { + manifest { + attributes 'FMLCorePlugin': 'minetweaker.mc172.transformer.MineTweakerTransformer' + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/FMLEventHandler.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/FMLEventHandler.java new file mode 100644 index 0000000..9dacaa5 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/FMLEventHandler.java @@ -0,0 +1,23 @@ +package minetweaker.mc172; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.network.MineTweakerLoadScriptsPacket; +import net.minecraft.entity.player.EntityPlayerMP; + +/** + * + * @author Stan + */ +public class FMLEventHandler { + @SubscribeEvent + public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent ev) { + if (ev.player instanceof EntityPlayerMP) { + EntityPlayerMP player = (EntityPlayerMP) ev.player; + MineTweakerMod.NETWORK.sendTo( + new MineTweakerLoadScriptsPacket(MineTweakerAPI.tweaker.getScriptData()), + player); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/ForgeEventHandler.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/ForgeEventHandler.java new file mode 100644 index 0000000..fbda5ec --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/ForgeEventHandler.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent; + +/** + * + * @author Stan + */ +public class ForgeEventHandler { + @SubscribeEvent + public void onCrafted(PlayerEvent.ItemCraftedEvent ev) { + + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java index ad49214..20b6ccd 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java @@ -2,11 +2,20 @@ package minetweaker.mc172; import java.util.Arrays; import java.util.List; +import java.util.Map; import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.util.ChatComponentText; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.oredict.OreDictionary; import stanhebben.zenscript.util.StringUtil; /** @@ -43,7 +52,21 @@ public class MineTweakerCommand implements ICommand { public String getCommandUsage(ICommandSender icommandsender) { return StringUtil.join(new String[] { "", - "minetweaker name [:]", + "minetweaker reload", + " Reloads all scripts", + "minetweaker names", + " Outputs a list of all item names in the game to the minetweaker log", + "minetweaker liquids", + " Outputs a list of all liquid names in the game to the minetweaker log", + "minetweaker inventory", + " Lists all items in your inventory", + "minetweaker hand", + " Outputs the name of the item in your hand", + "minetweaker oredict", + " Outputs all ore dictionary entries in the game to the minetweaker log", + "minetweaker oredict ", + " Outputs all items in the given ore dictionary entry to the minetweaker log" +/* "minetweaker name [:]", " Prints the unlocalized name of the specified item," + " can be used as item or block name", "minetweaker liquid [:]", @@ -66,7 +89,7 @@ public class MineTweakerCommand implements ICommand { "minetweaker logdamage=off", " Disables damage logging", "minetweaker reload", - " Reloads the server script" + " Reloads the server script"*/ }, "\n"); } @@ -84,6 +107,94 @@ public class MineTweakerCommand implements ICommand { if (arguments[0].equals("reload")) { MineTweakerAPI.tweaker.load(); + + String message = "Scripts have been reloaded"; + MineTweakerAPI.logger.logCommand(message); + sendChatMessage(icommandsender, message); + } else if (arguments[0].equals("names")) { + for (Object okey : Item.itemRegistry.getKeys()) { + String key = (String) okey; + Item item = (Item) Item.itemRegistry.getObject(key); + + String message = "<" + key + "> -- " + new ItemStack(item, 1, 0).getDisplayName(); + MineTweakerAPI.logger.logCommand(message); + } + sendChatMessage(icommandsender, "List generated; see minetweaker.log in your minecraft dir"); + } else if (arguments[0].equals("liquids")) { + for (Map.Entry entry : FluidRegistry.getRegisteredFluids().entrySet()) { + String message = " -- " + entry.getValue().getLocalizedName(); + MineTweakerAPI.logger.logCommand(message); + sendChatMessage(icommandsender, "List generated; see minetweaker.log in your minecraft dir"); + } + } else if (arguments[0].equals("inventory")) { + if (icommandsender instanceof EntityPlayer) { + InventoryPlayer inventory = ((EntityPlayer) icommandsender).inventory; + for(int i = 0; i < inventory.getSizeInventory(); i++) { + ItemStack stack = inventory.getStackInSlot(i); + if (stack != null) { + StringBuilder description = new StringBuilder(); + description.append('<'); + description.append(Item.itemRegistry.getNameForObject(stack.getItem())); + if (stack.getItemDamage() > 0) { + description.append(':').append(stack.getItemDamage()); + } + description.append('>'); + + if (stack.stackSize != 1) { + description.append('*').append(stack.stackSize); + } + + sendChatMessage(icommandsender, new TweakerItemStack(stack).toString()); + } + } + } else { + sendChatMessage(icommandsender, "Inventory command can only be executed by players"); + } + } else if (arguments[0].equals("hand")) { + if (icommandsender instanceof EntityPlayer) { + InventoryPlayer inventory = ((EntityPlayer) icommandsender).inventory; + ItemStack stack = inventory.getCurrentItem(); + if (stack != null) { + StringBuilder description = new StringBuilder(); + description.append('<'); + description.append(Item.itemRegistry.getNameForObject(stack.getItem())); + if (stack.getItemDamage() > 0) { + description.append(':').append(stack.getItemDamage()); + } + description.append('>'); + + if (stack.stackSize != 1) { + description.append('*').append(stack.stackSize); + } + + sendChatMessage(icommandsender, new TweakerItemStack(stack).toString()); + } + } else { + sendChatMessage(icommandsender, "Hand command can only be executed by players"); + } + } else if (arguments[0].equals("oredict")) { + if (arguments.length > 2) { + String entryName = arguments[2]; + List ores = OreDictionary.getOres(entryName); + if (ores.isEmpty()) { + sendChatMessage(icommandsender, "Entry doesn't exist"); + return; + } else { + MineTweakerAPI.logger.logCommand("Ore entries for " + entryName + ":"); + for (ItemStack ore : ores) { + MineTweakerAPI.logger.logCommand(" " + new TweakerItemStack(ore).toString()); + } + } + } else { + for (String entryName : OreDictionary.getOreNames()) { + MineTweakerAPI.logger.logCommand("Ore entries for " + entryName + ":"); + List ores = OreDictionary.getOres(entryName); + for (ItemStack ore : ores) { + MineTweakerAPI.logger.logCommand(" " + new TweakerItemStack(ore).toString()); + } + } + } + sendChatMessage(icommandsender, "List generated; see minetweaker.log in your minecraft dir"); } /*if (arguments[0].equals("name")) { diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerLogger.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerLogger.java new file mode 100644 index 0000000..9df1132 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerLogger.java @@ -0,0 +1,90 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import minetweaker.runtime.ILogger; + +/** + * + * @author Stan + */ +public class MineTweakerLogger implements ILogger { + private Writer writer; + + public MineTweakerLogger() { + File logFile = new File("minetweaker.log"); + if (logFile.exists()) logFile.delete(); + + try { + writer = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(logFile))); + } catch (IOException ex) { + + } + } + + @Override + public void logCommand(String message) { + try { + writer.append("COMMAND: "); + writer.append(message); + writer.append('\n'); + writer.flush(); + } catch (IOException ex) { + + } + + System.out.println("COMMAND: " + message); + } + + @Override + public void logInfo(String message) { + try { + writer.append("INFO: "); + writer.append(message); + writer.append('\n'); + writer.flush(); + } catch (IOException ex) { + + } + + System.out.println("INFO: " + message); + } + + @Override + public void logWarning(String message) { + try { + writer.append("WARNING: "); + writer.append(message); + writer.append('\n'); + writer.flush(); + } catch (IOException ex) { + + } + + System.out.println("WARNING: " + message); + } + + @Override + public void logError(String message) { + try { + writer.append("ERROR: "); + writer.append(message); + writer.append('\n'); + writer.flush(); + } catch (IOException ex) { + + } + + System.out.println("ERROR: " + message); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java index 39f8533..7926dff 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java @@ -6,31 +6,63 @@ package minetweaker.mc172; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.Mod.EventHandler; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.event.FMLServerAboutToStartEvent; +import cpw.mods.fml.common.event.FMLServerStartingEvent; +import cpw.mods.fml.common.network.NetworkRegistry; +import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; +import cpw.mods.fml.relauncher.Side; +import java.io.File; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; import minetweaker.MineTweakerAPI; import minetweaker.annotations.ModOnly; +import minetweaker.mc172.brackets.ItemBracketHandler; +import minetweaker.mc172.brackets.LiquidBracketHandler; +import minetweaker.mc172.network.MineTweakerLoadScriptsHandler; +import minetweaker.mc172.network.MineTweakerLoadScriptsPacket; import minetweaker.mc172.oredict.OreDict; import minetweaker.mc172.recipes.MTRecipeManager; +import minetweaker.mc172.util.MineTweakerHacks; +import minetweaker.runtime.IScriptProvider; +import minetweaker.runtime.providers.ScriptProviderCascade; +import minetweaker.runtime.providers.ScriptProviderDirectory; +import net.minecraftforge.common.MinecraftForge; /** - * - * @author Stanneke + * Main mod class. Performs some general logic, initialization of the API and + * FML event handling. + * + * @author Stan Hebben */ @Mod(modid = MineTweakerMod.MODID, version = MineTweakerMod.MCVERSION + "-3.0.0") public class MineTweakerMod { public static final String MODID = "MineTweaker"; public static final String MCVERSION = "1.7.2"; + public static final SimpleNetworkWrapper NETWORK = NetworkRegistry.INSTANCE.newSimpleChannel(MODID); + + static { + NETWORK.registerMessage(MineTweakerLoadScriptsHandler.class, MineTweakerLoadScriptsPacket.class, 0, Side.CLIENT); + } + @Mod.Instance(MODID) - public static MineTweakerMod instance; + public static MineTweakerMod INSTANCE; + + private final IScriptProvider scriptsGlobal; + + private boolean iAmServer = false; public MineTweakerMod() { MineTweakerAPI.oreDict = new OreDict(); MineTweakerAPI.recipes = new MTRecipeManager(); + MineTweakerAPI.logger = new MineTweakerLogger(); List classes = new ArrayList(); MineTweakerRegistry.getClasses(classes); @@ -47,5 +79,64 @@ public class MineTweakerMod { MineTweakerAPI.registerClass(cls); } + + File globalDir = new File("scripts"); + if (!globalDir.exists()) { + globalDir.mkdirs(); + } + + scriptsGlobal = new ScriptProviderDirectory(globalDir); + MineTweakerAPI.tweaker.setScriptProvider(scriptsGlobal); + } + + public boolean iAmServer() { + return iAmServer; + } + + /** + * Reloads all scripts. + */ + public void reload() { + MineTweakerAPI.tweaker.rollback(); + MineTweakerAPI.tweaker.load(); + } + + // ########################## + // ### FML Event Handlers ### + // ########################## + + @EventHandler + public void onLoad(FMLPreInitializationEvent ev) { + MinecraftForge.EVENT_BUS.register(new FMLEventHandler()); + FMLCommonHandler.instance().bus().register(new FMLEventHandler()); + } + + @EventHandler + public void onPostInit(FMLPostInitializationEvent ev) { + MineTweakerAPI.registerBracketHandler(new ItemBracketHandler()); + MineTweakerAPI.registerBracketHandler(new LiquidBracketHandler()); + } + + @EventHandler + public void onServerAboutToStart(FMLServerAboutToStartEvent ev) { + // starts before loading worlds + // perfect place to start MineTweaker! + + File scriptsDir = new File(MineTweakerHacks.getWorldDirectory(ev.getServer()), "scripts"); + if (!scriptsDir.exists()) { + scriptsDir.mkdir(); + } + + iAmServer = true; + + IScriptProvider scriptsLocal = new ScriptProviderDirectory(scriptsDir); + IScriptProvider cascaded = new ScriptProviderCascade(scriptsGlobal, scriptsLocal); + MineTweakerAPI.tweaker.setScriptProvider(cascaded); + reload(); + } + + @EventHandler + public void onServerStarting(FMLServerStartingEvent ev) { + ev.registerServerCommand(new MineTweakerCommand()); } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/brackets/ItemBracketHandler.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/brackets/ItemBracketHandler.java new file mode 100644 index 0000000..6d8b733 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/brackets/ItemBracketHandler.java @@ -0,0 +1,122 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.brackets; + +import java.util.List; +import minetweaker.IBracketHandler; +import minetweaker.MineTweakerAPI; +import minetweaker.annotations.BracketHandler; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IItemStack; +import minetweaker.minecraft.item.IngredientAny; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; +import stanhebben.zenscript.ZenTokener; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionJavaCallStatic; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +@BracketHandler(priority=100) +public class ItemBracketHandler implements IBracketHandler { + public static IItemStack getItem(String name, int meta) { + Item item = (Item) Item.itemRegistry.getObject(name); + if (item != null) { + return new TweakerItemStack(new ItemStack(item, 1, meta), true); + } else { + return null; + } + } + + private final IZenSymbol symbolAny; + private final JavaMethod method; + + public ItemBracketHandler() { + symbolAny = MineTweakerAPI.getJavaStaticFieldSymbol(IngredientAny.class, "INSTANCE"); + method = MineTweakerAPI.getJavaMethod( + ItemBracketHandler.class, + "getItem", + String.class, int.class); + } + + @Override + public IZenSymbol resolve(List tokens) { + // any symbol + if (tokens.size() == 1 && tokens.get(0).getValue().equals("*")) { + return symbolAny; + } + + // detect special cases: + // item: at the start means item-specific syntax + // :xxx with xxx an integer means sub-item syntax + // :* means any-subitem-syntax + int fromIndex = 0; + int toIndex = tokens.size(); + int meta = 0; + + if (tokens.size() > 2) { + if (tokens.get(0).getValue().equals("item") && tokens.get(1).getValue().equals(":")) { + fromIndex = 2; + } + if (tokens.get(tokens.size() - 1).getType() == ZenTokener.T_INTVALUE + && tokens.get(tokens.size() - 2).getValue().equals(":")) { + toIndex = tokens.size() - 2; + meta = Integer.parseInt(tokens.get(tokens.size() - 1).getValue()); + } else if (tokens.get(tokens.size() - 1).getValue().equals("*") + && tokens.get(tokens.size() - 2).getValue().equals(":")) { + toIndex = tokens.size() - 2; + meta = OreDictionary.WILDCARD_VALUE; + } + } + + return find(tokens, fromIndex, toIndex, meta); + } + + private IZenSymbol find(List tokens, int startIndex, int endIndex, int meta) { + StringBuilder valueBuilder = new StringBuilder(); + for (int i = startIndex; i < endIndex; i++) { + Token token = tokens.get(i); + valueBuilder.append(token.getValue()); + } + + Item item = (Item) Item.itemRegistry.getObject(valueBuilder.toString()); + if (item != null) { + return new ItemReferenceSymbol(valueBuilder.toString(), meta); + } + + return null; + } + + private class ItemReferenceSymbol implements IZenSymbol { + private final String name; + private final int meta; + + public ItemReferenceSymbol(String name, int meta) { + this.name = name; + this.meta = meta; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new ExpressionJavaCallStatic( + position, + method, + new ExpressionString(position, name), + new ExpressionInt(position, meta, ZenType.INT)); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/brackets/LiquidBracketHandler.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/brackets/LiquidBracketHandler.java new file mode 100644 index 0000000..6ab4ad7 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/brackets/LiquidBracketHandler.java @@ -0,0 +1,82 @@ +package minetweaker.mc172.brackets; + +import java.util.List; +import minetweaker.IBracketHandler; +import minetweaker.annotations.BracketHandler; +import minetweaker.mc172.liquid.TweakerLiquidStack; +import minetweaker.minecraft.liquid.ILiquidStack; +import minetweaker.runtime.GlobalRegistry; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import stanhebben.zenscript.expression.ExpressionJavaCallStatic; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +@BracketHandler +public class LiquidBracketHandler implements IBracketHandler { + public static ILiquidStack getLiquid(String name) { + Fluid fluid = FluidRegistry.getFluid(name); + if (fluid != null) { + return new TweakerLiquidStack(new FluidStack(fluid, 1)); + } else { + return null; + } + } + + @Override + public IZenSymbol resolve(List tokens) { + if (tokens.size() > 2) { + if (tokens.get(0).getValue().equals("liquid") && tokens.get(1).getValue().equals(":")) { + return find(tokens, 2, tokens.size()); + } + } + + return null; + } + + private IZenSymbol find(List tokens, int startIndex, int endIndex) { + StringBuilder valueBuilder = new StringBuilder(); + for (int i = startIndex; i < endIndex; i++) { + Token token = tokens.get(i); + valueBuilder.append(token.getValue()); + } + + Fluid fluid = FluidRegistry.getFluid(valueBuilder.toString()); + if (fluid != null) { + return new LiquidReferenceSymbol(valueBuilder.toString()); + } + + return null; + } + + private class LiquidReferenceSymbol implements IZenSymbol { + private final String name; + + public LiquidReferenceSymbol(String name) { + this.name = name; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + JavaMethod method = JavaMethod.get( + GlobalRegistry.getTypeRegistry(), + LiquidBracketHandler.class, + "getLiquid", + String.class); + + return new ExpressionJavaCallStatic( + position, + method, + new ExpressionString(position, name)); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java index 26564ab..57f9cf3 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java @@ -92,7 +92,12 @@ public class NBTConverter implements IDataConverter { } } - private NBTConverter() {} + public static void updateMap(NBTTagCompound nbt, IData data) { + NBTUpdater updater = new NBTUpdater(nbt); + data.convert(updater); + } + + public NBTConverter() {} @Override public NBTBase fromBool(boolean value) { diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTUpdater.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTUpdater.java new file mode 100644 index 0000000..299ebd2 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTUpdater.java @@ -0,0 +1,47 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.data; + +import java.util.Map; +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.data.IDataConverter; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; + +/** + * + * @author Stan + */ +public class NBTUpdater extends NBTConverter implements IDataConverter { + private final NBTTagCompound nbt; + + public NBTUpdater(NBTTagCompound compound) { + this.nbt = compound; + } + + @Override + public NBTBase fromMap(Map map) { + for (Map.Entry entry : map.entrySet()) { + if (nbt.hasKey(entry.getKey())) { + if (entry.getValue() == null) { + nbt.removeTag(entry.getKey()); + } else { + if (nbt.getTag(entry.getKey()).getId() == 10) { + NBTTagCompound tag = (NBTTagCompound) nbt.getTag(entry.getKey()); + nbt.setTag(entry.getKey(), entry.getValue().convert(new NBTUpdater(nbt))); + } else { + nbt.setTag(entry.getKey(), NBTConverter.from(entry.getValue())); + } + } + } else if (entry.getValue() != null) { + nbt.setTag(entry.getKey(), from(entry.getValue())); + } + } + + return nbt; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java index 6fd821b..2a9c402 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java @@ -20,6 +20,7 @@ import minetweaker.minecraft.item.IItemTransformer; import minetweaker.minecraft.item.IngredientItem; import minetweaker.minecraft.liquid.ILiquidStack; import minetweaker.minecraft.util.ArrayUtil; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fluids.FluidContainerRegistry; @@ -34,13 +35,24 @@ public class TweakerItemStack implements IItemStack { private final ItemStack stack; private final List items; private IData tag = null; + private boolean wildcardSize; public TweakerItemStack(ItemStack itemStack) { - stack = itemStack; + if (itemStack == null) throw new IllegalArgumentException("stack cannot be null"); + + stack = itemStack.copy(); items = Collections.singletonList(this); } + public TweakerItemStack(ItemStack itemStack, boolean wildcardSize) { + this(itemStack); + + this.wildcardSize = wildcardSize; + } + private TweakerItemStack(ItemStack itemStack, IData tag) { + if (itemStack == null) throw new IllegalArgumentException("stack cannot be null"); + stack = itemStack; items = Collections.singletonList(this); this.tag = tag; @@ -157,7 +169,7 @@ public class TweakerItemStack implements IItemStack { } @Override - public IIngredient amount(int amount) { + public IItemStack amount(int amount) { return withAmount(amount); } @@ -179,8 +191,11 @@ public class TweakerItemStack implements IItemStack { @Override public boolean matches(IItemStack item) { ItemStack internal = (ItemStack) item.getInternal(); + if (internal == null) { + throw new RuntimeException("Invalid item: " + item); + } return internal.getItem() == stack.getItem() - && internal.stackSize == stack.stackSize + && (wildcardSize || internal.stackSize == stack.stackSize) && (stack.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack.getItemDamage() == internal.getItemDamage()); } @@ -200,4 +215,32 @@ public class TweakerItemStack implements IItemStack { public Object getInternal() { return stack; } + + // ############################# + // ### Object implementation ### + // ############################# + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append('<'); + result.append(Item.itemRegistry.getNameForObject(stack.getItem())); + + if (stack.getItemDamage() > 0) { + result.append(':').append(stack.getItemDamage()); + } + result.append('>'); + + if (stack.getTagCompound() != null) { + result.append(".withData("); + result.append(stack.stackTagCompound.toString()); + result.append(")"); + } + + if (!wildcardSize) { + result.append(" * ").append(stack.stackSize); + } + + return result.toString(); + } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java index b52af13..8ae6050 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java @@ -23,6 +23,16 @@ public class TweakerLiquidDefinition implements ILiquidDefinition { public TweakerLiquidDefinition(Fluid fluid) { this.fluid = fluid; } + + @Override + public String getName() { + return fluid.getName(); + } + + @Override + public String getDisplayName() { + return fluid.getLocalizedName(); + } @Override public ILiquidStack asStack(int millibuckets) { diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java index 0006e9f..4416c4e 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java @@ -34,6 +34,16 @@ public class TweakerLiquidStack implements ILiquidStack { public ILiquidDefinition getDefinition() { return new TweakerLiquidDefinition(stack.getFluid()); } + + @Override + public String getName() { + return stack.getFluid().getName(); + } + + @Override + public String getDisplayName() { + return stack.getFluid().getLocalizedName(); + } @Override public int getAmount() { diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/network/MineTweakerLoadScriptsHandler.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/network/MineTweakerLoadScriptsHandler.java new file mode 100644 index 0000000..5e04969 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/network/MineTweakerLoadScriptsHandler.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.network; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.MineTweakerMod; +import minetweaker.runtime.providers.ScriptProviderMemory; + +/** + * + * @author Stan + */ +public class MineTweakerLoadScriptsHandler implements IMessageHandler { + @Override + public IMessage onMessage(MineTweakerLoadScriptsPacket message, MessageContext ctx) { + if (!MineTweakerMod.INSTANCE.iAmServer()) { + /*IScriptProvider scriptProvider = new ScriptProviderMemory(message.getData()); + Iterator scripts = scriptProvider.getScripts(); + while (scripts.hasNext()) { + IScriptIterator myGroup = scripts.next(); + System.out.println("Scripts: " + myGroup.getGroupName()); + + while (myGroup.next()) { + System.out.println("Script name: " + myGroup.getName()); + } + }*/ + MineTweakerAPI.tweaker.setScriptProvider(new ScriptProviderMemory(message.getData())); + MineTweakerAPI.tweaker.rollback(); + MineTweakerAPI.tweaker.load(); + } + + return null; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/network/MineTweakerLoadScriptsPacket.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/network/MineTweakerLoadScriptsPacket.java new file mode 100644 index 0000000..6a67480 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/network/MineTweakerLoadScriptsPacket.java @@ -0,0 +1,43 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.network; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import io.netty.buffer.ByteBuf; + +/** + * + * @author Stan + */ +public class MineTweakerLoadScriptsPacket implements IMessage { + private byte[] data; + + public MineTweakerLoadScriptsPacket() { + // used for deserialization + } + + public MineTweakerLoadScriptsPacket(byte[] data) { + this.data = data; + } + + public byte[] getData() { + return data; + } + + @Override + public void fromBytes(ByteBuf buf) { + int size = buf.readInt(); + data = new byte[size]; + buf.readBytes(data); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(data.length); + buf.writeBytes(data); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java index b62c60a..46e0201 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java @@ -34,6 +34,10 @@ public class OreDictEntry implements IOreDictEntry { this.id = id; } + public OreDictEntry(String id) { + this.id = OreDictionary.getOreID(id); + } + // #################################### // ### IOreDictEntry implementation ### // #################################### diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/player/TweakerPlayer.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/player/TweakerPlayer.java new file mode 100644 index 0000000..455b030 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/player/TweakerPlayer.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.player; + +import minetweaker.mc172.data.NBTConverter; +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.player.IPlayer; +import net.minecraft.entity.player.EntityPlayer; + +/** + * + * @author Stan + */ +public class TweakerPlayer implements IPlayer { + private final EntityPlayer player; + + public TweakerPlayer(EntityPlayer player) { + this.player = player; + } + + @Override + public String getName() { + return player.getCommandSenderName(); + } + + @Override + public IData getData() { + return NBTConverter.from(player.getEntityData(), true); + } + + @Override + public void update(IData data) { + NBTConverter.updateMap(player.getEntityData(), data); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java index 51e6bb5..367e274 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java @@ -1,9 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - package minetweaker.mc172.recipes; import minetweaker.minecraft.recipes.ShapedRecipe; @@ -13,11 +7,17 @@ import java.util.List; import minetweaker.IUndoableAction; import minetweaker.MineTweakerAPI; import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.mc172.oredict.OreDictEntry; import minetweaker.mc172.util.MineTweakerHacks;import minetweaker.minecraft.item.IIngredient; import minetweaker.minecraft.item.IItemStack; -; import minetweaker.minecraft.recipes.IRecipeFunction; import minetweaker.minecraft.recipes.IRecipeManager; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.ShapedRecipes; import net.minecraft.item.crafting.ShapelessRecipes; @@ -32,7 +32,7 @@ public class MTRecipeManager implements IRecipeManager { private final List recipes; public MTRecipeManager() { - recipes = MineTweakerHacks.getRecipes(); + recipes = (List) CraftingManager.getInstance().getRecipeList(); } @Override @@ -41,13 +41,17 @@ public class MTRecipeManager implements IRecipeManager { List removeIndex = new ArrayList(); for (int i = 0; i < recipes.size(); i++) { IRecipe recipe = recipes.get(i); - - if (output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { - toRemove.add(recipe); - removeIndex.add(i); + + // certain special recipes have no predefined output. ignore those + // since these cannot be removed with MineTweaker scripts + if (recipe.getRecipeOutput() != null) { + if (output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { + toRemove.add(recipe); + removeIndex.add(i); + } } } - + MineTweakerAPI.tweaker.apply(new ActionRemoveRecipes(toRemove, removeIndex)); return toRemove.size(); } @@ -78,21 +82,71 @@ public class MTRecipeManager implements IRecipeManager { @Override public int removeShaped(IIngredient output, IIngredient[][] ingredients) { + int ingredientsWidth = 0; + int ingredientsHeight = 0; + + if (ingredients != null) { + ingredientsHeight = ingredients.length; + + for (int i = 0; i < ingredients.length; i++) { + ingredientsWidth = Math.max(ingredientsWidth, ingredients[i].length); + } + } + List toRemove = new ArrayList(); List removeIndex = new ArrayList(); - for (int i = 0; i < recipes.size(); i++) { + outer: for (int i = 0; i < recipes.size(); i++) { IRecipe recipe = recipes.get(i); if (!output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { continue; } - if (recipe instanceof ShapedRecipes) { - ShapedRecipes srecipe = (ShapedRecipes) recipe; - // TODO: check contents - } else if (recipe instanceof ShapedOreRecipe) { - ShapedOreRecipe srecipe = (ShapedOreRecipe) recipe; - // TODO: check contents + if (ingredients != null) { + if (recipe instanceof ShapedRecipes) { + ShapedRecipes srecipe = (ShapedRecipes) recipe; + if (ingredientsWidth != srecipe.recipeWidth || ingredientsHeight != srecipe.recipeHeight) { + continue; + } + + for (int j = 0; j < ingredientsHeight; j++) { + IIngredient[] row = ingredients[j]; + for (int k = 0; k < ingredientsWidth; k++) { + IIngredient ingredient = k > row.length ? null : row[k]; + ItemStack recipeIngredient = srecipe.recipeItems[j * srecipe.recipeWidth + k]; + + if (!matches(recipeIngredient, ingredient)) { + continue outer; + } + } + } + } else if (recipe instanceof ShapedOreRecipe) { + ShapedOreRecipe srecipe = (ShapedOreRecipe) recipe; + int recipeWidth = MineTweakerHacks.getShapedOreRecipeWidth(srecipe); + int recipeHeight = srecipe.getRecipeSize() / recipeWidth; + if (ingredientsWidth != recipeWidth || ingredientsHeight != recipeHeight) { + continue; + } + + for (int j = 0; j < ingredientsHeight; j++) { + IIngredient[] row = ingredients[j]; + for (int k = 0; k < ingredientsWidth; k++) { + IIngredient ingredient = k > row.length ? null : row[k]; + Object input = srecipe.getInput()[j * recipeWidth + k]; + if (!matches(input, ingredient)) { + continue outer; + } + } + } + } + } else { + if (recipe instanceof ShapedRecipe) { + + } else if (recipe instanceof ShapedOreRecipe) { + + } else { + continue; + } } toRemove.add(recipe); @@ -107,31 +161,60 @@ public class MTRecipeManager implements IRecipeManager { public int removeShapeless(IIngredient output, IIngredient[] ingredients, boolean wildcard) { List toRemove = new ArrayList(); List removeIndex = new ArrayList(); - for (int i = 0; i < recipes.size(); i++) { + outer: for (int i = 0; i < recipes.size(); i++) { IRecipe recipe = recipes.get(i); if (!output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { continue; } - if (recipe instanceof ShapelessRecipes) { - ShapelessRecipes srecipe = (ShapelessRecipes) recipe; - // TODO: check contents - } else if (recipe instanceof ShapelessOreRecipe) { - ShapelessOreRecipe srecipe = (ShapelessOreRecipe) recipe; - ArrayList inputs = srecipe.getInput(); - if (inputs.size() < ingredients.length) { - continue; - } if (!wildcard && inputs.size() > ingredients.length) { - continue; - } - - boolean[] tag = new boolean[ingredients.length]; - for (Object object : inputs) { + if (ingredients != null) { + if (recipe instanceof ShapelessRecipes) { + ShapelessRecipes srecipe = (ShapelessRecipes) recipe; + if (ingredients.length > srecipe.getRecipeSize()) { + continue; + } else if (!wildcard && ingredients.length < srecipe.getRecipeSize()) { + continue; + } + + checkIngredient: for (int j = 0; j < ingredients.length; j++) { + for (int k = 0; k < srecipe.getRecipeSize(); k++) { + if (matches(srecipe.recipeItems.get(k), ingredients[j])) { + continue checkIngredient; + } + } + + continue outer; + } + } else if (recipe instanceof ShapelessOreRecipe) { + ShapelessOreRecipe srecipe = (ShapelessOreRecipe) recipe; + ArrayList inputs = srecipe.getInput(); + + if (inputs.size() < ingredients.length) { + continue; + } if (!wildcard && inputs.size() > ingredients.length) { + continue; + } + + checkIngredient: for (int j = 0; j < ingredients.length; j++) { + for (int k = 0; k < srecipe.getRecipeSize(); k++) { + if (matches(inputs.get(k), ingredients[j])) { + continue checkIngredient; + } + } + + continue outer; + } + } + } else { + if (recipe instanceof ShapelessRecipes) { + + } else if (recipe instanceof ShapelessOreRecipe) { + + } else { + continue; } - - // TODO: check contents } toRemove.add(recipe); @@ -144,7 +227,43 @@ public class MTRecipeManager implements IRecipeManager { @Override public IItemStack craft(IItemStack[][] contents) { - return null; // TODO: implement + Container container = new ContainerVirtual(); + + int width = 0; + int height = contents.length; + for (IItemStack[] row : contents) { + width = Math.max(width, row.length); + } + + ItemStack[] iContents = new ItemStack[width * height]; + for (int i = 0; i < height; i++) { + for (int j = 0; j < contents[i].length; j++) { + if (contents[i][j] != null) { + Object internal = contents[i][j].getInternal(); + if (internal != null && (internal instanceof ItemStack)) { + iContents[i * width + j] = (ItemStack) internal; + } + } + } + } + InventoryCrafting inventory = new InventoryCrafting(container, width, height); + for (int i = 0; i < iContents.length; i++) { + inventory.setInventorySlotContents(i, iContents[i]); + } + + ItemStack result = CraftingManager.getInstance().findMatchingRecipe(inventory, Minecraft.getMinecraft().theWorld); + if (result == null) { + return null; + } else { + return new TweakerItemStack(result); + } + } + + private class ContainerVirtual extends Container { + @Override + public boolean canInteractWith(EntityPlayer var1) { + return false; + } } private class ActionRemoveRecipes implements IUndoableAction { @@ -195,6 +314,7 @@ public class MTRecipeManager implements IRecipeManager { @Override public void apply() { + CraftingManager.getInstance().getRecipeList().add(recipe); recipes.add(recipe); } @@ -218,4 +338,22 @@ public class MTRecipeManager implements IRecipeManager { return "Removing recipe for " + recipe.getRecipeOutput().getDisplayName(); } } + + private static boolean matches(Object input, IIngredient ingredient) { + if ((input == null) != (ingredient == null)) { + return false; + } else if (ingredient != null) { + if (input instanceof ItemStack) { + if (!ingredient.matches(new TweakerItemStack((ItemStack) input))) { + return false; + } + } else if (input instanceof String) { + if (!ingredient.contains(new OreDictEntry((String) input))) { + return false; + } + } + } + + return true; + } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java index d3a5c92..ee934d5 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java @@ -75,6 +75,7 @@ public class RecipeConverter { // construct recipe if (type == TYPE_BASIC) { + System.out.println("Converted to basic recipe"); ItemStack[] basicIngredients = new ItemStack[recipe.getHeight() * recipe.getWidth()]; for (int i = 0; i < ingredients.length; i++) { basicIngredients[posx[i] + posy[i] * recipe.getWidth()] = (ItemStack) ingredients[i].getInternal(); @@ -82,6 +83,7 @@ public class RecipeConverter { return new ShapedRecipeBasic(basicIngredients, recipe); } else if (type == TYPE_ORE) { + System.out.println("Converted to ore recipe"); Object[] converted = new Object[recipe.getHeight() * recipe.getWidth()]; for (int i = 0; i < ingredients.length; i++) { converted[posx[i] + posy[i] * recipe.getWidth()] = ingredients[i].getInternal(); @@ -113,6 +115,7 @@ public class RecipeConverter { return new ShapedRecipeOre(rarguments.toArray(), recipe); } else { + System.out.println("Converted to advanced recipe"); return new ShapedRecipeAdvanced(recipe); } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java index f0042ad..e54b649 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java @@ -31,7 +31,7 @@ public class ShapedRecipeAdvanced implements IRecipe { @Override public ItemStack getCraftingResult(InventoryCrafting inventory) { - return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + return ((ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal()).copy(); } @Override diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java index 6e0aa8e..761f1eb 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java @@ -6,6 +6,7 @@ package minetweaker.mc172.recipes; +import minetweaker.minecraft.item.IItemStack; import minetweaker.minecraft.recipes.ShapedRecipe; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; @@ -32,6 +33,11 @@ public class ShapedRecipeBasic extends ShapedRecipes { @Override public ItemStack getCraftingResult(InventoryCrafting inventory) { - return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + IItemStack result = recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)); + if (result == null) { + return null; + } else { + return ((ItemStack) result.getInternal()).copy(); + } } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java index 8f25bd0..b0d121a 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java @@ -32,6 +32,6 @@ public class ShapedRecipeOre extends ShapedOreRecipe { @Override public ItemStack getCraftingResult(InventoryCrafting inventory) { - return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + return ((ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal()).copy(); } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java index 593516d..fdb25a0 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java @@ -30,7 +30,7 @@ public class ShapelessRecipeAdvanced implements IRecipe { @Override public ItemStack getCraftingResult(InventoryCrafting inventory) { - return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + return ((ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal()).copy(); } @Override diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java index d543640..df2c1b0 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java @@ -33,6 +33,6 @@ public class ShapelessRecipeBasic extends ShapelessRecipes { @Override public ItemStack getCraftingResult(InventoryCrafting inventory) { - return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + return ((ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal()).copy(); } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java index 0aea1cb..3ff3a5a 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java @@ -32,6 +32,6 @@ public class ShapelessRecipeOre extends ShapelessOreRecipe { @Override public ItemStack getCraftingResult(InventoryCrafting inventory) { - return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + return ((ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal()).copy(); } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java index 3d9e3df..1049d91 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java @@ -6,10 +6,16 @@ package minetweaker.mc172.recipes; +import java.util.List; import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.mc172.util.MineTweakerHacks; import minetweaker.minecraft.item.IItemStack; import minetweaker.minecraft.recipes.ICraftingInventory; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.inventory.Slot; +import net.minecraft.inventory.SlotCrafting; import net.minecraft.item.ItemStack; /** @@ -25,24 +31,60 @@ public class TweakerCraftingInventory implements ICraftingInventory { cache.set(result); return result; } else { - return cache.get(); + TweakerCraftingInventory result = cache.get(); + result.update(); + return result; } } - private final int width; - private final int height; + private int width; + private int height; private final InventoryCrafting inventory; - private final IItemStack[] stacks; + private IItemStack[] stacks; + private ItemStack[] original; + private int stackCount; private TweakerCraftingInventory(InventoryCrafting inventory) { this.inventory = inventory; width = height = (int) Math.sqrt(inventory.getSizeInventory()); stacks = new IItemStack[width * height]; + original = new ItemStack[stacks.length]; + stackCount = 0; + update(); + + Container container = MineTweakerHacks.getCraftingContainer(inventory); + List slots = container.inventorySlots; + if (!slots.isEmpty() && slots.get(0) instanceof SlotCrafting) { + SlotCrafting slotCrafting = (SlotCrafting) slots.get(0); + EntityPlayer player = MineTweakerHacks.getCraftingSlotPlayer(slotCrafting); + System.out.println("Crafting slot for " + player.getCommandSenderName()); + } + + System.out.println("Container: " + MineTweakerHacks.getCraftingContainer(inventory)); + } + + private void update() { + if (inventory.getSizeInventory() != original.length) { + width = height = (int) Math.sqrt(inventory.getSizeInventory()); + stacks = new IItemStack[inventory.getSizeInventory()]; + original = new ItemStack[stacks.length]; + stackCount = 0; + } + for (int i = 0; i < inventory.getSizeInventory(); i++) { - if (inventory.getStackInSlot(i) != null) { - stacks[i] = new TweakerItemStack(inventory.getStackInSlot(i)); + if (changed(i)) { + System.out.println("Slot " + i + " changed"); + original[i] = inventory.getStackInSlot(i); + if (inventory.getStackInSlot(i) != null) { + if (stacks[i] == null) stackCount++; + stacks[i] = new TweakerItemStack(original[i]); + } else { + if (stacks[i] != null) stackCount--; + stacks[i] = null; + } } } + //System.out.println("Num stack count: " + stackCount); } @Override @@ -59,6 +101,11 @@ public class TweakerCraftingInventory implements ICraftingInventory { public int getHeight() { return height; } + + @Override + public int getStackCount() { + return stackCount; + } @Override public IItemStack getStack(int i) { @@ -72,13 +119,47 @@ public class TweakerCraftingInventory implements ICraftingInventory { @Override public void setStack(int x, int y, IItemStack stack) { - stacks[y * width + x] = stack; - inventory.setInventorySlotContents(y * width + x, (ItemStack) stack.getInternal()); + System.out.println("SetStack(" + x + ", " + y + ") " + stack); + + int ix = y * width + x; + if (stack != stacks[ix]) { + if (stack == null) { + stackCount--; + inventory.setInventorySlotContents(ix, null); + } else { + inventory.setInventorySlotContents(ix, (ItemStack) stack.getInternal()); + + if (stacks[ix] == null) { + stackCount++; + } + } + stacks[ix] = stack; + } } @Override public void setStack(int i, IItemStack stack) { - stacks[i] = stack; - inventory.setInventorySlotContents(i, (ItemStack) stack.getInternal()); + System.out.println("SetStack(" + i + ") " + stack); + + if (stack != stacks[i]) { + if (stack == null) { + stackCount--; + inventory.setInventorySlotContents(i, null); + } else { + inventory.setInventorySlotContents(i, (ItemStack) stack.getInternal()); + + if (stacks[i] == null) { + stackCount++; + } + } + stacks[i] = stack; + } + } + + private boolean changed(int i) { + if (original[i] != inventory.getStackInSlot(i)) return true; + if (original[i] != null && stacks[i].getAmount() != original[i].stackSize) return true; + + return false; } } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java index d18a710..445772c 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java @@ -18,6 +18,7 @@ import org.objectweb.asm.Opcodes; * @author Stanneke */ public class MineTweakerASM implements IClassTransformer { + public MineTweakerASM() {} @Override public byte[] transform(String name, String newName, byte[] bytecode) { diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java index e1e62b8..47f4ffc 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java @@ -15,8 +15,8 @@ import minetweaker.mc172.MineTweakerMod; * * @author Stanneke */ -@MCVersion(MineTweakerMod.MCVERSION) -@IFMLLoadingPlugin.TransformerExclusions("minetweaker.mc172.transformer") +//@MCVersion(MineTweakerMod.MCVERSION) +//@IFMLLoadingPlugin.TransformerExclusions("minetweaker.mc172.transformer") public class MineTweakerTransformer implements IFMLLoadingPlugin { @Override diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java index 7842c4e..8ac5069 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java @@ -6,17 +6,21 @@ package minetweaker.mc172.util; +import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import minetweaker.MineTweakerAPI; -import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.CraftingManager; -import net.minecraft.item.crafting.IRecipe; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.inventory.SlotCrafting;import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; import net.minecraftforge.oredict.OreDictionary; +import net.minecraftforge.oredict.ShapedOreRecipe; /** * Common class for all runtime hacks (stuff requiring reflection). It is not @@ -28,10 +32,18 @@ import net.minecraftforge.oredict.OreDictionary; public class MineTweakerHacks { private static final Field NBTTAGLIST_TAGLIST; private static final Field OREDICTIONARY_ORESTACKS; + private static final Field MINECRAFTSERVER_ANVILFILE; + private static final Field SHAPEDORERECIPE_WIDTH; + private static final Field INVENTORYCRAFTING_EVENTHANDLER; + private static final Field SLOTCRAFTING_PLAYER; static { NBTTAGLIST_TAGLIST = getField(NBTTagList.class, MineTweakerObfuscation.NBTTAGLIST_TAGLIST); OREDICTIONARY_ORESTACKS = getField(OreDictionary.class, MineTweakerObfuscation.OREDICTIONARY_ORESTACKS); + MINECRAFTSERVER_ANVILFILE = getField(MinecraftServer.class, MineTweakerObfuscation.MINECRAFTSERVER_ANVILFILE); + SHAPEDORERECIPE_WIDTH = getField(ShapedOreRecipe.class, new String[] {"width"}); + INVENTORYCRAFTING_EVENTHANDLER = getField(InventoryCrafting.class, MineTweakerObfuscation.INVENTORYCRAFTING_EVENTHANDLER); + SLOTCRAFTING_PLAYER = getField(SlotCrafting.class, MineTweakerObfuscation.SLOTCRAFTING_PLAYER); } private MineTweakerHacks() {} @@ -49,16 +61,6 @@ public class MineTweakerHacks { } } - public static List getRecipes() { - Field field = getField(CraftingManager.class, MineTweakerObfuscation.CRAFTINGMANAGER_RECIPES); - try { - return (List) field.get(CraftingManager.getInstance()); - } catch (IllegalAccessException ex) { - MineTweakerAPI.logger.logError("EPIC ERROR - could not load recipe list!"); - return null; - } - } - public static HashMap> getOreStacks() { try { return (HashMap>) OREDICTIONARY_ORESTACKS.get(null); @@ -68,10 +70,61 @@ public class MineTweakerHacks { } } + public static File getAnvilFile(MinecraftServer server) { + try { + return (File) MINECRAFTSERVER_ANVILFILE.get(server); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logger.logError("could not load anvil file!"); + return null; + } + } + + public static File getWorldDirectory(MinecraftServer server) { + if (server.isDedicatedServer()) { + return server.getFile("world"); + } else { + File worldsDir = getAnvilFile(server); + if (worldsDir == null) return null; + + return new File(worldsDir, server.getFolderName()); + } + } + + public static int getShapedOreRecipeWidth(ShapedOreRecipe recipe) { + try { + return SHAPEDORERECIPE_WIDTH.getInt(recipe); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logger.logError("could not load anvil file!"); + return 3; + } + } + + public static Container getCraftingContainer(InventoryCrafting inventory) { + try { + return (Container) INVENTORYCRAFTING_EVENTHANDLER.get(inventory); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logger.logError("could not get inventory eventhandler"); + return null; + } + } + + public static EntityPlayer getCraftingSlotPlayer(SlotCrafting slot) { + try { + return (EntityPlayer) SLOTCRAFTING_PLAYER.get(slot); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logger.logError("could not get inventory eventhandler"); + return null; + } + } + + // ####################### + // ### Private Methods ### + // ####################### + private static Field getField(Class cls, String[] names) { for (String name : names) { try { - Field field = cls.getField(name); + Field field = cls.getDeclaredField(name); field.setAccessible(true); return field; } catch (NoSuchFieldException ex) { diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java index 0f35719..2a817d7 100644 --- a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java @@ -16,4 +16,7 @@ public class MineTweakerObfuscation { public static final String[] NBTTAGLIST_TAGLIST = {"tagList", "field_74747_a"}; public static final String[] CRAFTINGMANAGER_RECIPES = {"recipes", "field_77597_b"}; public static final String[] OREDICTIONARY_ORESTACKS = {"oreStacks"}; + public static final String[] MINECRAFTSERVER_ANVILFILE = {"anvilFile", "field_71308_o"}; + public static final String[] INVENTORYCRAFTING_EVENTHANDLER = {"eventHandler", "field_70465_c"}; + public static final String[] SLOTCRAFTING_PLAYER = {"thePlayer", "field_75238_b"}; } diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerUtil.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerUtil.java new file mode 100644 index 0000000..906fb02 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerUtil.java @@ -0,0 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.util; + +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; + +/** + * + * @author Stan + */ +public class MineTweakerUtil { + public static ItemStack[] fromArray(IItemStack[] items) { + ItemStack[] output = new ItemStack[items.length]; + for (int i = 0; i < items.length; i++) { + Object internal = items[i].getInternal(); + if (internal != null && internal instanceof ItemStack) { + output[i] = (ItemStack) internal; + } + } + return output; + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental.jar b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental.jar deleted file mode 100644 index 96e4500..0000000 Binary files a/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental.jar and /dev/null differ diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2BracketHandler.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2BracketHandler.java new file mode 100644 index 0000000..f78e22c --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2BracketHandler.java @@ -0,0 +1,97 @@ +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.item.IC2Items; +import java.util.List; +import minetweaker.IBracketHandler; +import minetweaker.MineTweakerAPI; +import minetweaker.annotations.BracketHandler; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; +import stanhebben.zenscript.ZenTokener; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionJavaCallStatic; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * Makes a ic2-item specific item syntax available in the form <item-ic2:name[:meta]>. + * + * @author Stan Hebben + */ +@BracketHandler +public class IC2BracketHandler implements IBracketHandler { + public static IItemStack getItem(String name, int meta) { + ItemStack stack = IC2Items.getItem(name); + stack.setItemDamage(meta); + return new TweakerItemStack(stack); + } + + private final JavaMethod method; + + public IC2BracketHandler() { + method = MineTweakerAPI.getJavaMethod(IC2BracketHandler.class, "getItem", String.class, int.class); + } + + @Override + public IZenSymbol resolve(List tokens) { + if (tokens.size() >= 5) { + if (tokens.get(2).getValue().equals("ic2") + && tokens.get(0).getValue().equals("item") + && tokens.get(1).getValue().equals("-") + && tokens.get(3).getValue().equals(":")) { + String name = tokens.get(4).getValue(); + int meta = 0; + if (tokens.size() > 6 && tokens.get(5).getValue().equals(":")) { + if (tokens.get(6).getType() == ZenTokener.T_INTVALUE) { + meta = Integer.parseInt(tokens.get(6).getValue()); + } else if (tokens.get(6).getValue().equals("*")) { + meta = OreDictionary.WILDCARD_VALUE; + } else { + MineTweakerAPI.logger.logError("Not a valid meta value: " + tokens.get(6).getValue()); + } + } + ItemStack item = IC2Items.getItem(name); + if (item == null) { + MineTweakerAPI.logger.logError("Not a valid IC2 item: " + name); + return null; + } else { + return new ItemReferenceSymbol(name, meta); + } + } else { + return null; + } + } else { + return null; + } + } + + // ############################# + // ### Private inner classes ### + // ############################# + + private class ItemReferenceSymbol implements IZenSymbol { + private final String name; + private final int meta; + + public ItemReferenceSymbol(String name, int meta) { + this.name = name; + this.meta = meta; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new ExpressionJavaCallStatic( + position, + method, + new ExpressionString(position, name), + new ExpressionInt(position, meta, ZenType.INT)); + } + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2ExplosionWhitelist.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2ExplosionWhitelist.java new file mode 100644 index 0000000..718e32a --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2ExplosionWhitelist.java @@ -0,0 +1,163 @@ +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.tile.ExplosionWhitelist; +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * Manages the explosion whitelist in IC2. Blocks on this whitelist will not resist + * an explosion but won't be destroyed. + * + * The explosion code by default ignores blocks which absorb more than 1000 explosion power to + * prevent abusing personal safes, Trade-O-Mats and other blocks to serve as a cheap and + * invulnerable reactor chambers. Said blocks will not shield the explosion and won't get + * destroyed. + * + * @author Stan Hebben + */ +@ZenClass("mods.ic2.ExplosionWhitelist") +public class IC2ExplosionWhitelist { + /** + * Adds a block to the explosion whitelist. + * + * @param item item to add + */ + @ZenMethod + public static void add(IItemStack item) { + Object internal = item.getInternal(); + if (internal == null) { + return; + } + + ItemStack iStack = (ItemStack) internal; + Block block = Block.getBlockFromItem(iStack.getItem()); + if (block == null) { + MineTweakerAPI.logger.logError("This item is not a block"); + } else { + MineTweakerAPI.tweaker.apply(new AddAction(block)); + } + } + + /** + * Removes a block from the explosion whitelist. + * + * @param item item to remove + */ + @ZenMethod + public static void remove(IItemStack item) { + Object internal = item.getInternal(); + if (internal == null) { + return; + } + + ItemStack iStack = (ItemStack) internal; + Block block = Block.getBlockFromItem(iStack.getItem()); + if (block == null) { + MineTweakerAPI.logger.logError("This item is not a block"); + } else { + if (ExplosionWhitelist.isBlockWhitelisted(block)) { + MineTweakerAPI.logger.logWarning("This block is not in the whitelist"); + } else { + MineTweakerAPI.tweaker.apply(new RemoveAction(block)); + } + } + } + + /** + * Checks if the given item is already whitelisted. + * + * @param item item to check + * @return true if whitelisted + */ + @ZenMethod + public static boolean isWhitelisted(IItemStack item) { + Object internal = item.getInternal(); + if (internal == null) { + return false; + } + + ItemStack iStack = (ItemStack) internal; + Block block = Block.getBlockFromItem(iStack.getItem()); + if (block == null) { + MineTweakerAPI.logger.logWarning("This item is not a block"); + return false; + } else { + return ExplosionWhitelist.isBlockWhitelisted(block); + } + } + + // ###################### + // ### Action classes ### + // ###################### + + private static class AddAction implements IUndoableAction { + private final Block block; + + public AddAction(Block block) { + this.block = block; + } + + @Override + public void apply() { + ExplosionWhitelist.addWhitelistedBlock(block); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + ExplosionWhitelist.removeWhitelistedBlock(block); + } + + @Override + public String describe() { + return "Adding block to IC2 explosion whitelist: " + block.getLocalizedName(); + } + + @Override + public String describeUndo() { + return "Removing block from IC2 explosion whitelist: " + block.getLocalizedName(); + } + } + + private static class RemoveAction implements IUndoableAction { + private final Block block; + + public RemoveAction(Block block) { + this.block = block; + } + + @Override + public void apply() { + ExplosionWhitelist.removeWhitelistedBlock(block); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + ExplosionWhitelist.addWhitelistedBlock(block); + } + + @Override + public String describe() { + return "Removing block from IC2 explosion whitelist: " + block.getLocalizedName(); + } + + @Override + public String describeUndo() { + return "Adding block to IC2 explosion whitelist: " + block.getLocalizedName(); + } + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java index 1e95d21..ed971d1 100644 --- a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java @@ -13,16 +13,16 @@ import net.minecraft.nbt.NBTTagCompound; /** * - * @author Stan + * @author Stan Hebben */ public class MachineAddRecipeAction extends OneWayAction { private final String name; private final IMachineRecipeManager machine; - private final ItemStack output; + private final ItemStack[] output; private final IC2RecipeInput input; private final NBTTagCompound tag; - public MachineAddRecipeAction(String name, IMachineRecipeManager machine, ItemStack output, NBTTagCompound tag, IC2RecipeInput input) { + public MachineAddRecipeAction(String name, IMachineRecipeManager machine, ItemStack[] output, NBTTagCompound tag, IC2RecipeInput input) { this.name = name; this.machine = machine; this.output = output; @@ -37,6 +37,21 @@ public class MachineAddRecipeAction extends OneWayAction { @Override public String describe() { - return "Adding " + name + " recipe for " + output.getDisplayName(); + if (output.length == 1) { + return "Adding " + name + " recipe for " + output[0].getDisplayName(); + } else { + StringBuilder result = new StringBuilder(); + result.append("Adding ").append(name).append(" recipe for "); + result.append("["); + for (int i = 0; i < output.length; i++) { + if (i == 0) { + result.append(", "); + } else { + result.append(output[i].getDisplayName()); + } + } + result.append("]"); + return result.toString(); + } } } diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/expand/ItemExpansion.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/expand/ItemExpansion.java new file mode 100644 index 0000000..90a45a0 --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/expand/ItemExpansion.java @@ -0,0 +1,173 @@ +package stanhebben.minetweaker.mods.ic2.expand; + +import ic2.api.item.ElectricItem; +import ic2.api.item.IElectricItem; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.Optional; +import stanhebben.zenscript.annotations.ZenExpansion; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenExpansion("minecraft.item.IItemStack") +public class ItemExpansion { + /** + * Gets the charge level of this item. + * + * @param stack target + * @return charge level + */ + @ZenGetter("ic2getCharge") + public static int getIC2Charge(IItemStack stack) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return 0; + } + + return ElectricItem.manager.getCharge((ItemStack) internal); + } + + /** + * Gets the tier level of this item. + * + * @param stack target + * @return tier level + */ + @ZenGetter("ic2tier") + public static int getIC2Tier(IItemStack stack) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return 0; + } + + ItemStack iStack = (ItemStack) internal; + if (iStack.getItem() instanceof IElectricItem) { + return ((IElectricItem) iStack.getItem()).getTier(iStack); + } else { + return 0; + } + } + + /** + * Gets the transfer limit of the specified stack. + * + * @param stack target + * @return + */ + @ZenGetter("ic2transferLimit") + public static int getIC2TransferLimit(IItemStack stack) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return 0; + } + + ItemStack iStack = (ItemStack) internal; + if (iStack.getItem() instanceof IElectricItem) { + return ((IElectricItem) iStack.getItem()).getTransferLimit(iStack); + } else { + return 0; + } + } + + /** + * Gets the fully charged IC2 item. + * + * @param stack target + * @return charged item + */ + @ZenGetter("ic2charged") + public static IItemStack getIC2Charged(IItemStack stack) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return null; + } + + ItemStack iStack = (ItemStack) internal; + if (iStack.getItem() instanceof IElectricItem) { + IElectricItem eItem = (IElectricItem) iStack.getItem(); + Item item = eItem.getChargedItem(iStack); + ItemStack stack2 = new ItemStack(item, 1, 0); + ElectricItem.manager.charge(stack2, eItem.getMaxCharge(stack2), eItem.getTier(stack2), true, false); + return new TweakerItemStack(stack2); + } else { + return null; + } + } + + /** + * Gets the fully discharged IC2 item. + * + * @param stack target + * @return discharged item + */ + @ZenGetter("ic2discharged") + public static IItemStack getIC2Discharged(IItemStack stack) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return null; + } + + ItemStack iStack = (ItemStack) internal; + if (iStack.getItem() instanceof IElectricItem) { + IElectricItem eItem = (IElectricItem) iStack.getItem(); + Item item = eItem.getEmptyItem(iStack); + return new TweakerItemStack(new ItemStack(item, 1, 0)); + } else { + return null; + } + } + + /** + * Charges the item with the specified amount and returns the charged item. + * + * @param stack target + * @param amount charge amount + * @param tier charge tier + * @return charged item + */ + @ZenMethod("ic2charge") + public static IItemStack ic2Charge(IItemStack stack, int amount, @Optional int tier) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return stack; + } + + if (tier == 0) { + tier = getIC2Tier(stack); + } + + ItemStack iStack = ((ItemStack) internal).copy(); + ElectricItem.manager.charge(iStack, amount, tier, true, false); + return new TweakerItemStack(iStack); + } + + /** + * Discharges the item with the specified amount and returns the discharged item. + * + * @param stack target + * @param amount discharge amount + * @param tier charge tier + * @return discharged item + */ + @ZenMethod("ic2discharge") + public static IItemStack ic2Discharge(IItemStack stack, int amount, @Optional int tier) { + Object internal = stack.getInternal(); + if (internal == null || !(internal instanceof ItemStack)) { + return stack; + } + + if (tier == 0) { + tier = getIC2Tier(stack); + } + + ItemStack iStack = ((ItemStack) internal).copy(); + ElectricItem.manager.discharge(iStack, amount, tier, true, false); + return new TweakerItemStack(iStack); + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Compressor.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Compressor.java similarity index 88% rename from MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Compressor.java rename to MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Compressor.java index 772b0fb..3fb1397 100644 --- a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Compressor.java +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Compressor.java @@ -1,4 +1,4 @@ -package stanhebben.minetweaker.mods.ic2; +package stanhebben.minetweaker.mods.ic2.machines; import ic2.api.recipe.Recipes; import minetweaker.MineTweakerAPI; @@ -6,6 +6,8 @@ import minetweaker.mc172.item.TweakerItemStack; import minetweaker.minecraft.item.IIngredient; import minetweaker.minecraft.item.IItemStack; import net.minecraft.item.ItemStack; +import stanhebben.minetweaker.mods.ic2.IC2RecipeInput; +import stanhebben.minetweaker.mods.ic2.MachineAddRecipeAction; import stanhebben.zenscript.annotations.NotNull; import stanhebben.zenscript.annotations.ZenClass; import stanhebben.zenscript.annotations.ZenMethod; @@ -37,7 +39,7 @@ public class Compressor { MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( "compressor", Recipes.compressor, - (ItemStack) output.getInternal(), + new ItemStack[] {(ItemStack) output.getInternal()}, null, new IC2RecipeInput(ingredient))); } diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Extractor.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Extractor.java similarity index 88% rename from MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Extractor.java rename to MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Extractor.java index ec054be..96e710e 100644 --- a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Extractor.java +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Extractor.java @@ -4,7 +4,7 @@ * and open the template in the editor. */ -package stanhebben.minetweaker.mods.ic2; +package stanhebben.minetweaker.mods.ic2.machines; import ic2.api.recipe.Recipes; import minetweaker.MineTweakerAPI; @@ -12,6 +12,8 @@ import minetweaker.mc172.item.TweakerItemStack; import minetweaker.minecraft.item.IIngredient; import minetweaker.minecraft.item.IItemStack; import net.minecraft.item.ItemStack; +import stanhebben.minetweaker.mods.ic2.IC2RecipeInput; +import stanhebben.minetweaker.mods.ic2.MachineAddRecipeAction; import stanhebben.zenscript.annotations.NotNull; import stanhebben.zenscript.annotations.ZenClass; import stanhebben.zenscript.annotations.ZenMethod; @@ -42,7 +44,7 @@ public class Extractor { MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( "extractor", Recipes.extractor, - (ItemStack) output.getInternal(), + new ItemStack[] {(ItemStack) output.getInternal()}, null, new IC2RecipeInput(ingredient))); } diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Macerator.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Macerator.java similarity index 88% rename from MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Macerator.java rename to MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Macerator.java index 8cf0b82..293a63a 100644 --- a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Macerator.java +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/Macerator.java @@ -1,4 +1,4 @@ -package stanhebben.minetweaker.mods.ic2; +package stanhebben.minetweaker.mods.ic2.machines; import ic2.api.recipe.Recipes; import minetweaker.MineTweakerAPI; @@ -6,6 +6,8 @@ import minetweaker.mc172.item.TweakerItemStack; import minetweaker.minecraft.item.IIngredient; import minetweaker.minecraft.item.IItemStack; import net.minecraft.item.ItemStack; +import stanhebben.minetweaker.mods.ic2.IC2RecipeInput; +import stanhebben.minetweaker.mods.ic2.MachineAddRecipeAction; import stanhebben.zenscript.annotations.NotNull; import stanhebben.zenscript.annotations.ZenClass; import stanhebben.zenscript.annotations.ZenMethod; @@ -37,7 +39,7 @@ public class Macerator { MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( "macerator", Recipes.macerator, - (ItemStack) output.getInternal(), + new ItemStack[] {(ItemStack) output.getInternal()}, null, new IC2RecipeInput(ingredient))); } diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Centrifuge.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/ThermalCentrifuge.java similarity index 50% rename from MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Centrifuge.java rename to MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/ThermalCentrifuge.java index fa54e49..77e480e 100644 --- a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Centrifuge.java +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/machines/ThermalCentrifuge.java @@ -1,12 +1,15 @@ -package stanhebben.minetweaker.mods.ic2; +package stanhebben.minetweaker.mods.ic2.machines; import ic2.api.recipe.Recipes; import minetweaker.MineTweakerAPI; import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.mc172.util.MineTweakerUtil; import minetweaker.minecraft.item.IIngredient; import minetweaker.minecraft.item.IItemStack; import net.minecraft.item.ItemStack; -import stanhebben.zenscript.annotations.NotNull; +import net.minecraft.nbt.NBTTagCompound; +import stanhebben.minetweaker.mods.ic2.IC2RecipeInput; +import stanhebben.minetweaker.mods.ic2.MachineAddRecipeAction; import stanhebben.zenscript.annotations.ZenClass; import stanhebben.zenscript.annotations.ZenMethod; @@ -16,31 +19,47 @@ import stanhebben.zenscript.annotations.ZenMethod; * * @author Stan Hebben */ -@ZenClass("mods.ic2.Centrifuge") -public class Centrifuge { +@ZenClass("mods.ic2.ThermalCentrifuge") +public class ThermalCentrifuge { /** * Adds a new recipe to the centrifuge. * - * The recipe input can be any pattern, as long as the stack size is determined. + * The recipe input can be any ingredient, as long as the stack size is determined. + * The output is an array of item stacks. + * + * @param output recipe outputs + * @param ingredient recipe input + * @param minHeat minimum centrifuge heat level + */ + @ZenMethod + public static void addRecipe(IItemStack[] output, IIngredient ingredient, int minHeat) { + if (ingredient.getAmount() < 0) { + MineTweakerAPI.logger.logWarning("invalid ingredient: " + ingredient + " - stack size not known"); + } else { + NBTTagCompound tag = new NBTTagCompound(); + tag.setInteger("minHeat", minHeat); + MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( + "compressor", + Recipes.compressor, + MineTweakerUtil.fromArray(output), + null, + new IC2RecipeInput(ingredient))); + } + } + + /** + * Adds a new recipe to the centrifuge. + * + * The recipe input can be any ingredient, as long as the stack size is determined. * The output can be any item stack. * * @param output recipe output * @param ingredient recipe input + * @param minHeat minimum centrifuge heat level */ @ZenMethod - public static void addRecipe( - @NotNull IItemStack output, - @NotNull IIngredient ingredient) { - if (ingredient.getAmount() < 0) { - MineTweakerAPI.logger.logWarning("invalid ingredient: " + ingredient + " - stack size not known"); - } else { - MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( - "compressor", - Recipes.compressor, - (ItemStack) output.getInternal(), - null, - new IC2RecipeInput(ingredient))); - } + public static void addRecipe(IItemStack output, IIngredient ingredient, int minHeat) { + addRecipe(new IItemStack[] { output }, ingredient, minHeat); } /** @@ -51,8 +70,7 @@ public class Centrifuge { * @return recipe output */ @ZenMethod - public static IItemStack getOutput( - @NotNull IItemStack input) { - return new TweakerItemStack(Recipes.compressor.getOutputFor((ItemStack) input.getInternal(), false).items.get(0)); + public static IItemStack getOutput(IItemStack input) { + return new TweakerItemStack(Recipes.centrifuge.getOutputFor((ItemStack) input.getInternal(), false).items.get(0)); } } diff --git a/MineTweaker3-Web/markdown-tutorials/roadmap.txt b/MineTweaker3-Web/markdown-tutorials/roadmap.txt new file mode 100644 index 0000000..cd02eea --- /dev/null +++ b/MineTweaker3-Web/markdown-tutorials/roadmap.txt @@ -0,0 +1,74 @@ +# MineTweaker Roadmap + +## Version 3.0: 1.7.2 and crafting upgrade + +Goal: Make a 1.7.2 version available +ETA: 22 june + +- Support for 1.7.2 and 1.7.3 +- Add and remove shaped and shapeless recipes +- Add and remove furnace recipes +- Allow modification of fuel values +- Allow modification of ore dictionary +- Liquid support +- Allow conditional ingredients, transforming ingredients and improved function recipes +- Mod support for NEI, IC2, Forestry +- Improved scripting engine and better documentation + +## Version 3.1: 1.6.4 upgrade + +Goal: Make a 1.6.4 version available +ETA: 29 june + +- Support for 1.6.4 + +## Version 3.2: Mods upgrade + +Goal: support more mods and port the remaining mods from MineTweaker 2, such that all functionality from MineTweaker 2 is now present in MineTweaker 3 +ETA: 13 july + +- Ability to make scripts adjust themselves to the mods available +- Ability to share functions between modules +- Ability to make functions private inside a specific scripts +- Mod support for BuildCraft, GregTech, MFR +- Mod support for Thaumcraft +- (Hopefully) Mod support for Thermal Expansion + +## Version 3.3: World & Scripting upgrade + +Goal: enable scripted sequences and behavior, make servers more interactive and customized, enable creation of advanced maps and puzzles + +- Event-based actions, scripted sequences and world interaction +- Ability to add worldgen, including ores, plants, trees and other terrain features +- Maybe even entirely new biomes +- Ability to send messages between clients & server +- Can store data with world, dimensions and players +- Can read settings from minetweaker-specific settings files + +## Version 3.4: Assets upgrade + +Goal: make it possible to load images, audio and data from assets inside the zip files (which are then distributed automatically) and enable creation of new items and blocks as well as modifying existing ones. + +- Ability to define new items and blocks with preprogrammed behavior +- Ability to redefine behavior of existing blocks and items, using assets from zipped modules +- Ability to play sounds and music packaged with the zipped modules +- Caching of files so they don't have to be sent again +- (Hopefully) a way to see the progress of downloading new files, as these may grow significantly + +## Version 3.5: Classes upgrade + +Goal: expand the scripting engine's capability (massively) and turn it into a full object-oriented language. Makes it possible to define blocks with custom behavior (more so than in the assets upgrade). + +- Major scripting engine upgrade +- Can define new classes and interfaces +- Can define enumeration values (enums) +- Can implement completely custom blocks and items, same API across all Minecraft versions +- Possible to write entire mods in MineTweaker + +## Version 3.6: Integration upgrade + +Goal: integrate with systems present in existing mods, such as microblocks, cutted blocks, piping systems and energy nets. + +- Support for microblocks and microblock implementations, supporting existing Mineblock libraries +- Integration with IC2, Buildcraft and Thermal Expansion energy nets +- Integration with BuildCraft and Thermal Expansion pipe systems diff --git a/ZenScript/build.gradle b/ZenScript/build.gradle index caef940..fe74478 100644 --- a/ZenScript/build.gradle +++ b/ZenScript/build.gradle @@ -6,5 +6,6 @@ targetCompatibility = '1.6' dependencies { compile group: 'org.ow2.asm', name: 'asm', version:'4.2' compile group: 'org.ow2.asm', name: 'asm-commons', version:'4.2' - compile files("${System.properties['java.home']}/../lib/tools.jar") + //compile files("${System.properties['java.home']}/../lib/tools.jar") + //compile files("libs/tools.jar") } diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenCompileEnvironment.java b/ZenScript/src/main/java/stanhebben/zenscript/IZenCompileEnvironment.java similarity index 90% rename from ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenCompileEnvironment.java rename to ZenScript/src/main/java/stanhebben/zenscript/IZenCompileEnvironment.java index 2b3b1a8..bb6b29d 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenCompileEnvironment.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/IZenCompileEnvironment.java @@ -4,13 +4,14 @@ * and open the template in the editor. */ -package stanhebben.zenscript.symbols; +package stanhebben.zenscript; import java.util.List; import stanhebben.zenscript.IZenErrorLogger; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.compiler.TypeRegistry; import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; /** * diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java index 8ae4221..025a1a3 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java @@ -29,7 +29,6 @@ import stanhebben.zenscript.definitions.ParsedFunctionArgument; import stanhebben.zenscript.expression.partial.IPartialExpression; import stanhebben.zenscript.statements.Statement; import stanhebben.zenscript.statements.StatementReturn; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.symbols.SymbolArgument; import stanhebben.zenscript.symbols.SymbolZenStaticMethod; @@ -79,30 +78,9 @@ public class ZenModule { for (ZenParsedFile script : scripts) { ClassWriter clsScript = new ClassWriter(ClassWriter.COMPUTE_MAXS); clsScript.visitSource(script.getFileName(), null); - EnvironmentClass environmentScript = new EnvironmentClass(clsScript, environmentGlobal); + EnvironmentClass environmentScript = new EnvironmentClass(clsScript, script.getEnvironment()); clsScript.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, script.getClassName(), null, internal(Object.class), new String[] {internal(Runnable.class)}); - for (Import imprt : script.getImports()) { - List name = imprt.getName(); - IPartialExpression value = environmentGlobal.getValue(name.get(0), imprt.getPosition()); - for (int i = 1; i < name.size(); i++) { - IPartialExpression member = value.getMember(imprt.getPosition(), environmentGlobal, name.get(i)); - if (member == null) { - environmentGlobal.error(imprt.getPosition(), StringUtil.join(name, ".") + " not found"); - break; - } - value = member; - } - IZenSymbol symbol = value.toSymbol(); - if (symbol == null) { - environmentGlobal.error(imprt.getPosition(), StringUtil.join(name, ".") + " is not a valid import"); - } else { - environmentScript.putValue( - imprt.getRename() == null - ? name.get(name.size() - 1) - : imprt.getRename(), value.toSymbol()); - } - } for (Map.Entry function : script.getFunctions().entrySet()) { ParsedFunction fn = function.getValue(); @@ -180,7 +158,7 @@ public class ZenModule { * @return compiled module * @throws IOException if the file could not be read */ - public static ZenModule compileScriptFile(File single, IZenCompileEnvironment environment) throws IOException { + public static ZenModule compileScriptFile(File single, IZenCompileEnvironment environment, ClassLoader baseClassLoader) throws IOException { Map classes = new HashMap(); ClassNameGenerator nameGen = new ClassNameGenerator(); EnvironmentGlobal environmentGlobal = new EnvironmentGlobal( @@ -214,7 +192,7 @@ public class ZenModule { output.close(); } - return new ZenModule(classes); + return new ZenModule(classes, baseClassLoader); } /** @@ -227,7 +205,11 @@ public class ZenModule { * @return compiled module * @throws IOException if the file could not be read properly */ - public static ZenModule compileZip(File file, String subdir, IZenCompileEnvironment environment) throws IOException { + public static ZenModule compileZip( + File file, + String subdir, + IZenCompileEnvironment environment, + ClassLoader baseClassLoader) throws IOException { Map classes = new HashMap(); ClassNameGenerator nameGen = new ClassNameGenerator(); EnvironmentGlobal environmentGlobal = new EnvironmentGlobal( @@ -269,7 +251,7 @@ public class ZenModule { output.close(); } - return new ZenModule(classes); + return new ZenModule(classes, baseClassLoader); } private final Map classes; @@ -281,9 +263,9 @@ public class ZenModule { * * @param classes */ - public ZenModule(Map classes) { + public ZenModule(Map classes, ClassLoader baseClassLoader) { this.classes = classes; - classLoader = new MyClassLoader(); + classLoader = new MyClassLoader(baseClassLoader); } /** @@ -341,10 +323,14 @@ public class ZenModule { * Custom class loader. Loads classes from this module. */ private class MyClassLoader extends ClassLoader { + private MyClassLoader(ClassLoader baseClassLoader) { + super(baseClassLoader); + } + @Override public Class findClass(String name) throws ClassNotFoundException { if (classes.containsKey(name)) { - return this.defineClass(name, classes.get(name), 0, classes.get(name).length); + return defineClass(name, classes.get(name), 0, classes.get(name).length); } return super.findClass(name); diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java index e84b667..f5510c8 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java @@ -5,11 +5,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import static stanhebben.zenscript.ZenTokener.*; +import stanhebben.zenscript.compiler.EnvironmentScript; import stanhebben.zenscript.compiler.IEnvironmentGlobal; import stanhebben.zenscript.definitions.Import; import stanhebben.zenscript.definitions.ParsedFunction; +import stanhebben.zenscript.expression.partial.IPartialExpression; import stanhebben.zenscript.parser.Token; import stanhebben.zenscript.statements.Statement; +import stanhebben.zenscript.symbols.SymbolType; +import stanhebben.zenscript.type.ZenType; /** * Contains a parsed file. @@ -32,6 +36,7 @@ public class ZenParsedFile { private final List imports; private final Map functions; private final List statements; + private final IEnvironmentGlobal environmentScript; /** * Constructs and parses a given file. @@ -48,6 +53,7 @@ public class ZenParsedFile { imports = new ArrayList(); functions = new HashMap(); statements = new ArrayList(); + environmentScript = new EnvironmentScript(environment); tokener.setFile(this); @@ -74,20 +80,55 @@ public class ZenParsedFile { imports.add(new Import(start.getPosition(), importName, rename)); } + for (Import imprt : imports) { + List name = imprt.getName(); + IPartialExpression type = null; + + StringBuilder nameSoFar = new StringBuilder(); + + for (String part : name) { + if (type == null) { + nameSoFar.append(part); + type = environment.getValue(part, imprt.getPosition()); + if (type == null) { + environment.error(imprt.getPosition(), "could not find package " + type); + break; + } + } else { + nameSoFar.append('.').append(part); + type = type.getMember(imprt.getPosition(), environment, part); + if (type == null) { + environment.error(imprt.getPosition(), "could not find type or package " + nameSoFar); + break; + } + } + } + + if (type != null) { + environmentScript.putValue(imprt.getRename(), type.toSymbol()); + } else { + environmentScript.putValue(imprt.getRename(), new SymbolType(ZenType.ANY)); + } + } + while (tokener.hasNext()) { Token next = tokener.peek(); if (next.getType() == T_FUNCTION) { - ParsedFunction function = ParsedFunction.parse(tokener, environment); + ParsedFunction function = ParsedFunction.parse(tokener, environmentScript); if (functions.containsKey(function.getName())) { environment.error(function.getPosition(), "function " + function.getName() + " already exists"); } functions.put(function.getName(), function); } else { - statements.add(Statement.read(tokener, environment)); + statements.add(Statement.read(tokener, environmentScript)); } } } + public IEnvironmentGlobal getEnvironment() { + return environmentScript; + } + /** * Gets the output classname for this file. * diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenTokener.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenTokener.java index 0bff770..0a5680d 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/ZenTokener.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenTokener.java @@ -8,7 +8,6 @@ import stanhebben.zenscript.parser.CompiledDFA; import stanhebben.zenscript.parser.NFA; import stanhebben.zenscript.parser.Token; import stanhebben.zenscript.parser.TokenStream; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; /** * A tokener is capable of splitting a single file into tokens. It's intended @@ -96,6 +95,7 @@ public class ZenTokener extends TokenStream { private static final String[] REGEXPS = { "#[^\n]*\n", "//[^\n]*\n", + "/\\*[^\\*]*(\\*^/[^\\*]*)*\\*/", "[ \t\r\n]*", "[a-zA-Z_][a-zA-Z_0-9]*", "\\-?(0|[1-9][0-9]*)\\.[0-9]+([eE][\\+\\-]?[0-9]+)?", @@ -147,6 +147,7 @@ public class ZenTokener extends TokenStream { -1, -1, -1, + -1, T_ID, T_FLOATVALUE, T_INTVALUE, diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java index 900b73f..c1ce5c6 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java @@ -12,7 +12,7 @@ import java.util.Map; import org.objectweb.asm.ClassVisitor; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.expression.partial.IPartialExpression; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.ZenPosition; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java index b6fac0b..879d2a3 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java @@ -12,7 +12,7 @@ import java.util.Map; import stanhebben.zenscript.IZenErrorLogger; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.expression.partial.IPartialExpression; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.ZenPosition; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java index ec0c90d..674b5d4 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java @@ -12,7 +12,7 @@ import java.util.Map; import org.objectweb.asm.ClassVisitor; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.expression.partial.IPartialExpression; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.symbols.SymbolLocal; import stanhebben.zenscript.type.ZenType; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java index c7b5c52..b435c5e 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java @@ -12,7 +12,7 @@ import java.util.Map; import org.objectweb.asm.ClassVisitor; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.expression.partial.IPartialExpression; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.symbols.SymbolLocal; import stanhebben.zenscript.type.ZenType; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScript.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScript.java new file mode 100644 index 0000000..5441eef --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScript.java @@ -0,0 +1,85 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import stanhebben.zenscript.IZenCompileEnvironment; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class EnvironmentScript implements IEnvironmentGlobal { + private final IEnvironmentGlobal parent; + private final Map imports; + + public EnvironmentScript(IEnvironmentGlobal parent) { + this.parent = parent; + imports = new HashMap(); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return parent.getEnvironment(); + } + + @Override + public TypeExpansion getExpansion(String name) { + return parent.getExpansion(name); + } + + @Override + public String makeClassName() { + return parent.makeClassName(); + } + + @Override + public boolean containsClass(String name) { + return parent.containsClass(name); + } + + @Override + public void putClass(String name, byte[] data) { + parent.putClass(name, data); + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (imports.containsKey(name)) { + return imports.get(name).instance(position); + } else { + return parent.getValue(name, position); + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + imports.put(name, value); + } + + @Override + public ZenType getType(Type type) { + return parent.getType(type); + } + + @Override + public void error(ZenPosition position, String message) { + parent.error(position, message); + } + + @Override + public void warning(ZenPosition position, String message) { + parent.warning(position, message); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java index 7a1aac8..e5862a7 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java @@ -9,7 +9,7 @@ package stanhebben.zenscript.compiler; import stanhebben.zenscript.IZenErrorLogger; import stanhebben.zenscript.TypeExpansion; import stanhebben.zenscript.expression.partial.IPartialExpression; -import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.IZenCompileEnvironment; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.util.ZenPosition; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java b/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java index a661c07..75af0e0 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java @@ -33,6 +33,6 @@ public class Import { } public String getRename() { - return rename; + return rename == null ? name.get(name.size() - 1) : rename; } } diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialType.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialType.java new file mode 100644 index 0000000..b076677 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialType.java @@ -0,0 +1,63 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolType; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class PartialType implements IPartialExpression { + private final ZenPosition position; + private final ZenType type; + + public PartialType(ZenPosition position, ZenType type) { + this.position = position; + this.type = type; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + environment.error(position, "cannot use type as expression"); + return new ExpressionInvalid(position, type); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "cannot assign to a type"); + return new ExpressionInvalid(position, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return type.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + environment.error(position, "cannot call a type"); + return new ExpressionInvalid(position, type); + } + + @Override + public IZenSymbol toSymbol() { + return new SymbolType(type); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + return type; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java index 17deef5..9cfd9bd 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java @@ -20,6 +20,8 @@ public class ParsedExpressionValue extends ParsedExpression { public ParsedExpressionValue(ZenPosition position, IPartialExpression value) { super(position); + if (value == null) throw new IllegalArgumentException("value cannot be null"); + this.value = value; } diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java index 44c8345..4b4bd40 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java @@ -6,7 +6,6 @@ import stanhebben.zenscript.expression.Expression; import stanhebben.zenscript.parser.expression.ParsedExpression; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.type.ZenTypeBool; -import stanhebben.zenscript.symbols.IZenNamespace; import stanhebben.zenscript.util.MethodOutput; import stanhebben.zenscript.util.ZenPosition; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenEnvironment.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenEnvironment.java deleted file mode 100644 index d74ee87..0000000 --- a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenEnvironment.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package stanhebben.zenscript.symbols; - -/** - * - * @author Stanneke - */ -public interface IZenEnvironment { - public Object getValue(byte[] key); -} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenNamespace.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenNamespace.java deleted file mode 100644 index ba9ffcc..0000000 --- a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenNamespace.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package stanhebben.zenscript.symbols; - -import stanhebben.zenscript.expression.partial.IPartialExpression; -import stanhebben.zenscript.util.ZenPosition; - -/** - * - * @author Stanneke - */ -public interface IZenNamespace { - public IZenCompileEnvironment getEnvironment(); - - public IPartialExpression getValue(String name, ZenPosition position); - - public void putValue(String name, IZenSymbol value); -} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java index 4baf674..9f0b16d 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java @@ -22,10 +22,17 @@ import stanhebben.zenscript.util.ZenPosition; public class SymbolPackage implements IZenSymbol { private final HashMap members; - public SymbolPackage() { + private final String name; + + public SymbolPackage(String name) { + this.name = name; members = new HashMap(); } + public String getName() { + return name; + } + public Map getPackages() { return members; } @@ -38,7 +45,14 @@ public class SymbolPackage implements IZenSymbol { String[] parts = StringUtil.split(name, '.'); String[] pkgParts = Arrays.copyOf(parts, parts.length - 1); SymbolPackage pkgCurrent = this; + String pkgName = null; for (String part : pkgParts) { + if (pkgName == null) { + pkgName = part; + } else { + pkgName = pkgName + '.' + part; + } + if (pkgCurrent.members.containsKey(part)) { IZenSymbol member = pkgCurrent.members.get(part); if (member instanceof SymbolPackage) { @@ -48,12 +62,13 @@ public class SymbolPackage implements IZenSymbol { return; } } else { - SymbolPackage child = new SymbolPackage(); + SymbolPackage child = new SymbolPackage(pkgName); pkgCurrent.members.put(part, child); pkgCurrent = child; } } + System.out.println("Adding " + parts[parts.length - 1] + " to package " + pkgCurrent.getName() + "(" + symbol + ")"); if (pkgCurrent.members.containsKey(parts[parts.length - 1])) { errors.error(null, parts[parts.length - 1] + " is already defined in that package"); } else { diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolType.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolType.java new file mode 100644 index 0000000..aa1ded6 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolType.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialType; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class SymbolType implements IZenSymbol { + private final ZenType type; + + public SymbolType(ZenType type) { + this.type = type; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialType(position, type); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceLocal.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceLocal.java deleted file mode 100644 index aece804..0000000 --- a/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceLocal.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package stanhebben.zenscript.symbols; - -import stanhebben.zenscript.expression.partial.IPartialExpression; -import java.util.HashMap; -import java.util.Map; -import stanhebben.zenscript.util.ZenPosition; - -/** - * - * @author Stanneke - */ -public class ZenNamespaceLocal implements IZenNamespace { - private final IZenNamespace parent; - private final Map values; - - public ZenNamespaceLocal(IZenNamespace parent) { - this.parent = parent; - this.values = new HashMap(); - } - - @Override - public IZenCompileEnvironment getEnvironment() { - return parent.getEnvironment(); - } - - @Override - public IPartialExpression getValue(String name, ZenPosition position) { - if (values.containsKey(name)) { - return values.get(name).instance(position); - } else { - return parent.getValue(name, position); - } - } - - @Override - public void putValue(String name, IZenSymbol value) { - values.put(name, value); - } -} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceScript.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceScript.java deleted file mode 100644 index 41d94ff..0000000 --- a/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceScript.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package stanhebben.zenscript.symbols; - -import stanhebben.zenscript.expression.partial.IPartialExpression; -import java.util.HashMap; -import java.util.Map; -import stanhebben.zenscript.util.ZenPosition; - -/** - * - * @author Stanneke - */ -public class ZenNamespaceScript implements IZenNamespace { - private final IZenCompileEnvironment environment; - private final Map values; - - public ZenNamespaceScript(IZenCompileEnvironment environment) { - this.environment = environment; - this.values = new HashMap(); - } - - @Override - public IZenCompileEnvironment getEnvironment() { - return environment; - } - - @Override - public IPartialExpression getValue(String name, ZenPosition position) { - if (values.containsKey(name)) { - return values.get(name).instance(position); - } else { - IZenSymbol symbol = environment.getGlobal(name); - if (symbol == null) { - return null; - } else { - return symbol.instance(position); - } - } - } - - @Override - public void putValue(String name, IZenSymbol value) { - values.put(name, value); - } -} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java index 5bc8402..63df8d4 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java @@ -80,12 +80,27 @@ public abstract class ZenType { base = STRING; break; case ZenTokener.T_ID: + base = ANY; + + StringBuilder typeName = new StringBuilder(); + typeName.append(next.getValue()); IPartialExpression partial = environment.getValue(next.getValue(), next.getPosition()); while (parser.optional(ZenTokener.T_DOT) != null) { + typeName.append('.'); + Token member = parser.required(ZenTokener.T_ID, "identifier expected"); + typeName.append(member.getValue()); partial = partial.getMember(member.getPosition(), environment, member.getValue()); + + if (partial == null) { + environment.error(member.getPosition(), "could not find type " + typeName); + break; + } + } + if (partial != null) { + base = partial.toType(environment); + System.out.println("Parsed type " + base.getName()); } - base = partial.toType(environment); break; default: throw new ParseException(next, "Unknown type: " + next.getValue()); diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java index 4af896a..fd21317 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java @@ -140,12 +140,14 @@ public class ZenTypeAny extends ZenType { @Override public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { - return null; + environment.error(position, "any values not yet supported"); + return new ExpressionInvalid(position); } @Override public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { - return null; + environment.error(position, "any values not yet supported"); + return new ExpressionInvalid(position); } @Override @@ -155,7 +157,7 @@ public class ZenTypeAny extends ZenType { @Override public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { - return true; // true or not? + return true; } @Override diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java index 978b81a..d7cdf59 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java @@ -47,7 +47,15 @@ public abstract class ZenTypeArray extends ZenType { @Override public final boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { - return equals(type) || canCastExpansion(environment, type); + if (equals(type) || canCastExpansion(environment, type)) { + return true; + } + + if (type instanceof ZenTypeArray) { + return base.canCastImplicit(((ZenTypeArray) type).base, environment); + } + + return false; } @Override diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeIntRange.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeIntRange.java new file mode 100644 index 0000000..0c33465 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeIntRange.java @@ -0,0 +1,208 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.compiler.ITypeRegistry; +import stanhebben.zenscript.compiler.TypeRegistry; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionJavaCallVirtual; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; +import stanhebben.zenscript.value.IntRange; + +/** + * + * @author Stan + */ +public class ZenTypeIntRange extends ZenType { + public static final ZenTypeIntRange INSTANCE = new ZenTypeIntRange(); + + private final JavaMethod methodFrom; + private final JavaMethod methodTo; + + private ZenTypeIntRange() { + ITypeRegistry dummy = new TypeRegistry(); + methodFrom = JavaMethod.get(dummy, IntRange.class, "getFrom"); + methodTo = JavaMethod.get(dummy, IntRange.class, "getTo"); + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + environment.error(position, "cannot apply unary operators on int ranges"); + return new ExpressionInvalid(position); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + environment.error(position, "cannot apply binary operators on int ranges"); + return new ExpressionInvalid(position); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "cannot apply ternary operators on int ranges"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + environment.error(position, "cannot compare int ranges"); + return new ExpressionInvalid(position, BOOL); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + if (name.equals("from")) { + return new ExpressionJavaCallVirtual(position, methodFrom, value.eval(environment)); + } else if (name.equals("to")) { + return new ExpressionJavaCallVirtual(position, methodTo, value.eval(environment)); + } else { + environment.error(position, "no such member " + name + " in int range"); + return new ExpressionInvalid(position); + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "int ranges don't have static members"); + return new ExpressionInvalid(position); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "int ranges cannot be called"); + return new ExpressionInvalid(position); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type == this) { + return value; + } + + environment.error(position, "cannot cast int ranges to any other type"); + return new ExpressionInvalid(position); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + if (numValues == 1) { + return new IntRangeIterator(methodOutput); + } else { + return null; + } + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this; + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this; + } + + @Override + public Class toJavaClass() { + return IntRange.class; + } + + @Override + public Type toASMType() { + return Type.getType(IntRange.class); + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return signature(IntRange.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String getName() { + return "int.."; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } + + private class IntRangeIterator implements IZenIterator { + private final IEnvironmentMethod method; + + public IntRangeIterator(IEnvironmentMethod methodOutput) { + this.method = methodOutput; + } + + @Override + public void compileStart(int[] locals) { + MethodOutput output = method.getOutput(); + + output.dup(); // copy IntRange reference + output.invoke(methodFrom); + output.storeInt(locals[0]); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + MethodOutput output = method.getOutput(); + + output.dup(); // copy IntRange reference + output.invoke(methodTo); + + output.loadInt(locals[0]); + output.iinc(locals[0]); + output.dup(); + output.storeInt(locals[0]); + + output.ifICmpGT(repeat); + output.goTo(exit); + } + + @Override + public void compileEnd() { + method.getOutput().pop(); // remove IntRange reference + } + + @Override + public ZenType getType(int i) { + return INT; + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java index 56ec70e..877364e 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java @@ -412,7 +412,7 @@ public class ZenTypeNative extends ZenType { @Override public String getSignature() { - return cls.getName(); + return signature(cls); } @Override @@ -429,7 +429,9 @@ public class ZenTypeNative extends ZenType { */ private static boolean canCastImplicit(Class cls, ZenType type) { if (isEqual(cls, type)) return true; - if (canCastImplicit(cls.getSuperclass(), type)) return true; + if (cls.getSuperclass() != null) { + if (canCastImplicit(cls.getSuperclass(), type)) return true; + } for (Class iface : cls.getInterfaces()) { if (canCastImplicit(iface, type)) return true; } @@ -458,7 +460,7 @@ public class ZenTypeNative extends ZenType { } private static String getSignature(Class cls) { - String signature = cls.getName(); + String signature = signature(cls); if (Number.class.isAssignableFrom(cls)) { if (cls == Byte.class) { signature = "B"; diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java b/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java index aa7ddb5..88cd4c1 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java @@ -16,6 +16,7 @@ import stanhebben.zenscript.expression.partial.IPartialExpression; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.type.natives.JavaMethod; +import static stanhebben.zenscript.util.StringUtil.methodMatchingError; import stanhebben.zenscript.util.ZenPosition; /** @@ -87,7 +88,7 @@ public class ZenExpandMember { System.arraycopy(values, 0, newValues, 1, values.length); JavaMethod method = JavaMethod.select(true, methods, environment, newValues); if (method == null) { - environment.error(position, "no matching method found"); + environment.error(position, methodMatchingError(methods, values)); return new ExpressionInvalid(position); } else { return method.callStatic(position, environment, newValues); @@ -133,7 +134,7 @@ public class ZenExpandMember { public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { JavaMethod method = JavaMethod.select(true, methods, environment, values); if (method == null) { - environment.error(position, "no matching method found"); + environment.error(position, methodMatchingError(methods, values)); return new ExpressionInvalid(position); } else { return method.callStatic(position, environment, values); diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java index c32df68..dba109d 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java @@ -38,7 +38,7 @@ public class JavaMethod { Method method = cls.getMethod(name, parameterTypes); return new JavaMethod(cls, method, types); } catch (NoSuchMethodException ex) { - throw new RuntimeException("method not found", ex); + throw new RuntimeException("method " + name + " not found in class " + cls.getName(), ex); } catch (SecurityException ex) { throw new RuntimeException("method retrieval not permitted", ex); } diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java index 01bb9d4..13bd36f 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java @@ -15,6 +15,7 @@ import stanhebben.zenscript.expression.ExpressionInvalid; import stanhebben.zenscript.expression.partial.IPartialExpression; import stanhebben.zenscript.symbols.IZenSymbol; import stanhebben.zenscript.type.ZenType; +import static stanhebben.zenscript.util.StringUtil.methodMatchingError; import stanhebben.zenscript.util.ZenPosition; /** @@ -91,7 +92,7 @@ public class ZenNativeMember { public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { JavaMethod method = JavaMethod.select(false, methods, environment, values); if (method == null) { - environment.error(position, "no matching method found"); + environment.error(position, methodMatchingError(methods, values)); return new ExpressionInvalid(position); } else { return method.callVirtual(position, environment, value.eval(environment), values); @@ -137,7 +138,7 @@ public class ZenNativeMember { public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { JavaMethod method = JavaMethod.select(true, methods, environment, values); if (method == null) { - environment.error(position, "no matching method found"); + environment.error(position, methodMatchingError(methods, values)); return new ExpressionInvalid(position); } else { return method.callStatic(position, environment, values); diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java b/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java index 8b314a4..e29f3ab 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java @@ -26,6 +26,7 @@ public class MethodOutput { private final LocalVariablesSorter visitor; public MethodOutput(ClassVisitor cls, int access, String name, String descriptor, String signature, String[] exceptions) { + System.out.println("Method output: " + name + descriptor); MethodVisitor methodVisitor = cls.visitMethod(access, name, descriptor, signature, exceptions); visitor = new LocalVariablesSorter(access, descriptor, methodVisitor); } diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java b/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java index fae5a8d..37e249f 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java @@ -7,6 +7,9 @@ package stanhebben.zenscript.util; import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.type.natives.JavaMethod; /** * @@ -42,6 +45,39 @@ public class StringUtil { } return result.toString(); } + + /** + * If a set of methods is available and none matches, this method creates + * a suitable message. + * + * @param methods matching methods + * @param arguments calling arguments + * @return return value + */ + public static String methodMatchingError(List methods, Expression... arguments) { + if (methods.isEmpty()) { + return "no method with that name available"; + } else { + StringBuilder message = new StringBuilder(); + if (methods.size() == 1) { + message.append("a method "); + } else { + message.append(methods.size()).append(" methods "); + } + message.append("available but none matches the parameters ("); + boolean first = true; + for (Expression value : arguments) { + if (first) { + first = false; + } else { + message.append(", "); + } + message.append(value.getType().toString()); + } + message.append(")"); + return message.toString(); + } + } /** * Splits a string in parts, given a specified delimiter. diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java b/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java index ef1d08d..05eca23 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java @@ -37,6 +37,6 @@ public class ZenPosition { @Override public String toString() { - return Integer.toString(line) + ":" + offset; + return (file == null ? "?" : file.getFileName()) + " - " + Integer.toString(line) + ":" + offset; } } diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/IntRange.java b/ZenScript/src/main/java/stanhebben/zenscript/value/IntRange.java new file mode 100644 index 0000000..deeaede --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/IntRange.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.value; + +/** + * + * @author Stan + */ +public class IntRange { + private final int from; + private final int to; + + public IntRange(int from, int to) { + this.from = from; + this.to = to; + } + + public int getFrom() { + return from; + } + + public int getTo() { + return to; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java index 04dfe6f..a196536 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java @@ -9,6 +9,7 @@ package stanhebben.zenscript.value.builtin; import java.util.Iterator; import stanhebben.zenscript.ZenRuntimeException; import stanhebben.zenscript.value.IAny; +import stanhebben.zenscript.value.IntRange; /** * @@ -82,8 +83,7 @@ public class AnyByte implements IAny { @Override public IAny range(IAny value) { - //return new AnyIntRange(this.value, value.asInt()); - return null; // TODO + return new AnyIntRange(new IntRange(this.value, value.asInt())); } @Override @@ -117,7 +117,7 @@ public class AnyByte implements IAny { @Override public IAny call(IAny... values) { - throw new ZenRuntimeException("cannot index a byte value"); + throw new ZenRuntimeException("cannot call a byte value"); } @Override diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyInt.java b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyInt.java new file mode 100644 index 0000000..85249c5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyInt.java @@ -0,0 +1,177 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.value.builtin; + +import java.util.Iterator; +import stanhebben.zenscript.ZenRuntimeException; +import stanhebben.zenscript.value.IAny; +import stanhebben.zenscript.value.IntRange; + +/** + * + * @author Stanneke + */ +public class AnyInt implements IAny { + public static AnyInt valueOf(byte value) { + return new AnyInt(value); + } + + private final int value; + + public AnyInt(int value) { + this.value = value; + } + + @Override + public IAny not() { + return new AnyInt(~value); + } + + @Override + public IAny neg() { + return new AnyInt(-value); + } + + @Override + public IAny add(IAny value) { + return new AnyInt(this.value + value.asInt()); + } + + @Override + public IAny sub(IAny value) { + return new AnyInt(this.value - value.asInt()); + } + + @Override + public IAny cat(IAny value) { + return new AnyString(Integer.toString(this.value) + value.asString()); + } + + @Override + public IAny mul(IAny value) { + return new AnyInt(this.value * value.asByte()); + } + + @Override + public IAny div(IAny value) { + return new AnyInt(this.value / value.asByte()); + } + + @Override + public IAny mod(IAny value) { + return new AnyInt(this.value % value.asByte()); + } + + @Override + public IAny and(IAny value) { + return new AnyInt(this.value & value.asByte()); + } + + @Override + public IAny or(IAny value) { + return new AnyInt(this.value | value.asByte()); + } + + @Override + public IAny xor(IAny value) { + return new AnyInt(this.value ^ value.asByte()); + } + + @Override + public IAny range(IAny value) { + return new AnyIntRange(new IntRange(this.value, value.asInt())); + } + + @Override + public int compareTo(IAny value) { + return Integer.compare(this.value, value.asByte()); + } + + @Override + public boolean contains(IAny value) { + throw new ZenRuntimeException("int has no in operator"); + } + + @Override + public IAny member(String value) { + if (value.equals("abs")) { + return new AnyInt(Math.abs(this.value)); + } else { + throw new ZenRuntimeException("no such member in int: " + value); + } + } + + @Override + public IAny indexGet(IAny key) { + throw new ZenRuntimeException("cannot index an int value"); + } + + @Override + public void indexSet(IAny key, IAny value) { + throw new ZenRuntimeException("cannot index an int value"); + } + + @Override + public IAny call(IAny... values) { + throw new ZenRuntimeException("cannot call an int value"); + } + + @Override + public boolean asBool() { + throw new ZenRuntimeException("cannot convert an int to a bool"); + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInt() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Integer.toString(value); + } + + @Override + public T as(Class cls) { + throw new ZenRuntimeException("cannot convert a byte to an object"); + } + + @Override + public Iterator iteratorSingle() { + throw new ZenRuntimeException("cannot iterate over a byte value"); + } + + @Override + public Iterator iteratorMulti(int n) { + throw new ZenRuntimeException("cannot iterate over a byte value"); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java index 88ca801..e06f8b6 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java @@ -7,107 +7,126 @@ package stanhebben.zenscript.value.builtin; import java.util.Iterator; +import stanhebben.zenscript.ZenRuntimeException; import stanhebben.zenscript.value.IAny; +import stanhebben.zenscript.value.IntRange; /** * * @author Stanneke */ public class AnyIntRange implements IAny { + private IntRange value; + + public AnyIntRange(IntRange value) { + this.value = value; + } @Override public IAny not() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot invert an int range"); } @Override public IAny neg() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot negate an int range"); } @Override public IAny add(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot add int ranges"); } @Override public IAny sub(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot subtract int ranges"); } @Override public IAny cat(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot concatenate int ranges"); } @Override public IAny mul(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot multiply int ranges"); } @Override public IAny div(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot divide int ranges"); } @Override public IAny mod(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot perform modulo on int ranges"); } @Override public IAny and(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot perform and on int ranges"); } @Override public IAny or(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot perform or on int ranges"); } @Override public IAny xor(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot perform xor on int ranges"); } @Override public IAny range(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Already a range!"); } @Override public int compareTo(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot compare ranges"); } @Override public boolean contains(IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + int ivalue = value.asInt(); + return ivalue >= this.value.getFrom() && ivalue < this.value.getTo(); } @Override public IAny member(String value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + if (value.equals("from")) { + return new AnyInt(this.value.getFrom()); + } else if (value.equals("to")) { + return new AnyInt(this.value.getTo()); + } else { + throw new ZenRuntimeException("No member " + value + " in int range"); + } } @Override public IAny indexGet(IAny key) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + int ivalue = key.asInt(); + if (ivalue < value.getTo() - value.getFrom()) { + return new AnyInt(this.value.getFrom() + ivalue); + } else { + throw new ArrayIndexOutOfBoundsException("int range index out of bounds: " + ivalue); + } } @Override public void indexSet(IAny key, IAny value) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot modify int ranges"); } @Override public IAny call(IAny... values) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot call int ranges"); } @Override public boolean asBool() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + throw new ZenRuntimeException("Cannot cast int range to bool"); } @Override @@ -159,5 +178,4 @@ public class AnyIntRange implements IAny { public Iterator iteratorMulti(int n) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 3978a6e..46dfa2e 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -7,4 +7,5 @@ repositories { dependencies { compile gradleApi() compile 'org.ow2.asm:asm-debug-all:4.1' + compile files("libs/tools.jar") } diff --git a/buildSrc/src/main/java/minetweaker/tasks/MergeJarsTask.java b/buildSrc/src/main/java/minetweaker/tasks/MergeJarsTask.java new file mode 100644 index 0000000..d762b55 --- /dev/null +++ b/buildSrc/src/main/java/minetweaker/tasks/MergeJarsTask.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.tasks; + +import java.io.File; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +/** + * + * @author Stan + */ +public class MergeJarsTask extends DefaultTask { + @InputFile + public File mainInput; + + @InputFile + public File[] inputs; + + @OutputFile + public File output; + + @TaskAction + public void doTask() { + System.out.println("Main: " + mainInput.toString()); + System.out.println("Inputs: " + inputs.length); + System.out.println("Output: " + output.toString()); + } +} diff --git a/buildSrc/src/main/java/minetweaker/tasks/RegisterZenClassesTask.java b/buildSrc/src/main/java/minetweaker/tasks/RegisterZenClassesTask.java index 7197d75..818d274 100644 --- a/buildSrc/src/main/java/minetweaker/tasks/RegisterZenClassesTask.java +++ b/buildSrc/src/main/java/minetweaker/tasks/RegisterZenClassesTask.java @@ -55,7 +55,7 @@ public class RegisterZenClassesTask extends DefaultTask { } String fullClassName = classPackage.replace('.', '/') + '/' + className; - System.out.println("Full classname: " + fullClassName); + //System.out.println("Full classname: " + fullClassName); // generate class ClassWriter output = new ClassWriter(ClassWriter.COMPUTE_MAXS); diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/CompareType.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/CompareType.java new file mode 100644 index 0000000..df1a1f9 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/CompareType.java @@ -0,0 +1,15 @@ +package stanhebben.zenscript.annotations; + +/** + * Used to indicate comparison types. + * + * @author Stan Hebben + */ +public enum CompareType { + LT, + GT, + EQ, + NE, + LE, + GE +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableList.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableList.java new file mode 100644 index 0000000..ceca76b --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableList.java @@ -0,0 +1,17 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Makes a class implementing List expose itself as iterable over keys and values. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface IterableList { + String value(); +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableMap.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableMap.java new file mode 100644 index 0000000..732581a --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableMap.java @@ -0,0 +1,18 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Makes a class implementing Map expose itself as iterable over keys and values. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface IterableMap { + String key(); + String value(); +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableSimple.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableSimple.java new file mode 100644 index 0000000..b5530ad --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/IterableSimple.java @@ -0,0 +1,18 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a simple iterable. Annotated class must implement the iterable + * interface. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface IterableSimple { + String value(); +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/NotNull.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/NotNull.java new file mode 100644 index 0000000..6ce9999 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/NotNull.java @@ -0,0 +1,17 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the given method parameter cannot be null. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface NotNull { + +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/OperatorType.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/OperatorType.java new file mode 100644 index 0000000..3171786 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/OperatorType.java @@ -0,0 +1,28 @@ +package stanhebben.zenscript.annotations; + +/** + * Enum used to indicate operator type. + * + * @author Stan Hebben + */ +public enum OperatorType { + ADD, + SUB, + MUL, + DIV, + MOD, + CAT, + OR, + AND, + XOR, + NEG, + NOT, + INDEXSET, + INDEXGET, + RANGE, + CONTAINS, + COMPARE, + MEMBERGETTER, + MEMBERSETTER, + EQUALS +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/Optional.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/Optional.java new file mode 100644 index 0000000..1e02ad6 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/Optional.java @@ -0,0 +1,19 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes that this parameter is optional. Inside the script, this parameter + * may be omitted, in which case it is automatically filled with the default + * value of that parameter type (false, null or 0). Invalid for @NonNull type. + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Optional { + +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenCaster.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenCaster.java new file mode 100644 index 0000000..6c77c5d --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenCaster.java @@ -0,0 +1,21 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes a caster. Casters are capable of converting one type into another. + * They implement the 'as' operator. + * + * For a native class, no arguments are provided. For an expansion method, the + * argument is the source value. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenCaster { + +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenClass.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenClass.java new file mode 100644 index 0000000..c9efe12 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenClass.java @@ -0,0 +1,24 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Exposes this class to ZenScript. The class can then be imported and accessed + * into any script. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface ZenClass { + /** + * Contains the class' package and name. If omitted, the java package and + * name will be used. + * + * @return + */ + String value() default ""; +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenExpansion.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenExpansion.java new file mode 100644 index 0000000..8fab394 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenExpansion.java @@ -0,0 +1,34 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the given class contains an expansion for a certain type. + * The expansions themselves are given through annotated static methods. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface ZenExpansion { + /** + * Contains the type of the annotated class. May be any kind of ZenScript type. + * + * Examples: + *
    + *
  • int
  • + *
  • my.package.MyClass
  • + *
  • my.package.MyClass[]
  • + *
  • any
  • + *
  • any[]
  • + *
  • any[any]
  • + * + *
+ * + * @return expanded type + */ + public String value(); +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenGetter.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenGetter.java new file mode 100644 index 0000000..057193b --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenGetter.java @@ -0,0 +1,26 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a getter. Will make the given method accessible as getter, that is, + * it's called when value.name is being used. + * + * For a native class, no arguments are provided. For an expansion, a single + * argument with the source value is provided. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenGetter { + /** + * Getter name. If omitted, the method name will be used as value name. + * + * @return getter name + */ + String value() default ""; +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMemberGetter.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMemberGetter.java new file mode 100644 index 0000000..72f6073 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMemberGetter.java @@ -0,0 +1,23 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a member getter. A member getter is a special kind of operator which + * is a fallback if a member could not be resolved. Can be used to make contents + * available as if they were members. + * + * If this method is a native class, it accepts a single string parameter with + * the member name. If this method is an expansion method, it accepts the source + * value and the member name. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMemberGetter { + +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMemberSetter.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMemberSetter.java new file mode 100644 index 0000000..ceb2f1b --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMemberSetter.java @@ -0,0 +1,23 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used to indicate a member setter. Companion to member getters, but allow + * setting contents of this object as if they were members. + * + * If this method is a native class, it accepts a string parameter with + * the member name and a second parameter with the value. If this method is an + * expansion method, it accepts the source value, the member name and the value + * to be assigned. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMemberSetter { + +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMethod.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMethod.java new file mode 100644 index 0000000..1d1425f --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMethod.java @@ -0,0 +1,26 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used to indicate an exposed method. + * + * If this method is part of a native class, it will simply accept the parameters + * as provided. If it is part of an expansion method, it will also accept the + * instance value as first parameter. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMethod { + /** + * Method name. If omitted, the original method name will be used. + * + * @return method name + */ + public String value() default ""; +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMethodStatic.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMethodStatic.java new file mode 100644 index 0000000..7c1b0fa --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenMethodStatic.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used to indicate static methods for expansions. For native classes, use ZenMethod + * instead on a static method. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMethodStatic { + /** + * Method name. If omitted, the annotated method name will be used. + * + * @return method name + */ + public String value() default ""; +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenOperator.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenOperator.java new file mode 100644 index 0000000..7ac7ada --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenOperator.java @@ -0,0 +1,22 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used to indicate an operator. The arguments depend on the kind of operator. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenOperator { + /** + * Operator type for this operator. + * + * @return operator type + */ + OperatorType value(); +} diff --git a/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenSetter.java b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenSetter.java new file mode 100644 index 0000000..875e2f4 --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/annotations/ZenSetter.java @@ -0,0 +1,26 @@ +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used to annotate setters. Will make the given method accessible as setter, that is, + * it's called when value.name is being assinged a value. + * + * For a native class, a single argument with the assinged value is provided. + * For an expansion, the target object and assigned value are provided. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenSetter { + /** + * Setter name. If omitted, the method name is used as property name. + * + * @return setter name + */ + String value() default ""; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenCasterDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenCasterDoc.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenCasterDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenCasterDoc.java diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenClassDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenClassDoc.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenClassDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenClassDoc.java diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenExpansionDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenExpansionDoc.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenExpansionDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenExpansionDoc.java diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenMethodDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenMethodDoc.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenMethodDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenMethodDoc.java diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenOperatorDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenOperatorDoc.java similarity index 91% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenOperatorDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenOperatorDoc.java index bc277ef..da2dc84 100644 --- a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenOperatorDoc.java +++ b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenOperatorDoc.java @@ -7,7 +7,6 @@ package stanhebben.zenscript.docs; import com.sun.javadoc.MethodDoc; -import stanhebben.zenscript.annotations.OperatorType; /** * diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenParameterDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenParameterDoc.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenParameterDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenParameterDoc.java diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenPropertyDoc.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenPropertyDoc.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenPropertyDoc.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenPropertyDoc.java diff --git a/ZenScript/src/main/java/stanhebben/zenscript/docs/ZenScriptDoclet.java b/buildSrc/src/main/java/stanhebben/zenscript/docs/ZenScriptDoclet.java similarity index 100% rename from ZenScript/src/main/java/stanhebben/zenscript/docs/ZenScriptDoclet.java rename to buildSrc/src/main/java/stanhebben/zenscript/docs/ZenScriptDoclet.java diff --git a/buildSrc/src/main/java/stanhebben/zenscript/util/StringUtil.java b/buildSrc/src/main/java/stanhebben/zenscript/util/StringUtil.java new file mode 100644 index 0000000..fae5a8d --- /dev/null +++ b/buildSrc/src/main/java/stanhebben/zenscript/util/StringUtil.java @@ -0,0 +1,481 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.util; + +import java.util.ArrayList; + +/** + * + * @author Stanneke + */ +public class StringUtil { + private StringUtil() {} + + public static String join(String[] values, String separator) { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String value : values) { + if (first) { + first = false; + } else { + result.append('.'); + } + result.append(value); + } + return result.toString(); + } + + public static String join(Iterable values, String separator) { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String value : values) { + if (first) { + first = false; + } else { + result.append('.'); + } + result.append(value); + } + return result.toString(); + } + + /** + * Splits a string in parts, given a specified delimiter. + * + * @param value string to be split + * @param delimiter delimiter + * @return + */ + public static String[] split(String value, char delimiter) { + if (value == null) return null; + + ArrayList result = new ArrayList(); + int start = 0; + outer: for (int i = 0; i < value.length(); i++) { + if (value.charAt(i) == delimiter) { + result.add(value.substring(start, i)); + start = i + 1; + } + } + result.add(value.substring(start)); + return result.toArray(new String[result.size()]); + } + + /** + * Processes a doc comment into paragraphs. + * + * @param value input value + * @return output paragraphs + */ + public static String[] splitParagraphs(String value) { + String[] lines = split(value, '\n'); + for (int i = 0; i < lines.length; i++) { + lines[i] = lines[i].trim(); + } + ArrayList output = new ArrayList(); + StringBuilder current = new StringBuilder(); + for (int i = 0; i < lines.length; i++) { + if (lines[i].length() == 0) { + if (current.length() > 0) { + output.add(current.toString()); + current = new StringBuilder(); + } + } else { + current.append(' ').append(lines[i]); + } + } + if (current.length() > 0) { + output.add(current.toString()); + } + + return output.toArray(new String[output.size()]); + } + + // ######################## + // ### String utilities ### + // ######################## + + /** + * + * unescape_perl_string() + * + * Tom Christiansen Sun Nov 28 12:55:24 MST 2010 + * + * It's completely ridiculous that there's no standard unescape_java_string + * function. Since I have to do the damn thing myself, I might as well make + * it halfway useful by supporting things Java was too stupid to consider in + * strings: + * + * => "?" items are additions to Java string escapes but normal in Java + * regexes + * + * => "!" items are also additions to Java regex escapes + * + * Standard singletons: ?\a ?\e \f \n \r \t + * + * NB: \b is unsupported as backspace so it can pass-through to the regex + * translator untouched; I refuse to make anyone doublebackslash it as + * doublebackslashing is a Java idiocy I desperately wish would die out. + * There are plenty of other ways to write it: + * + * \cH, \12, \012, \x08 \x{8}, \u0008, \U00000008 + * + * Octal escapes: \0 \0N \0NN \N \NN \NNN Can range up to !\777 not \377 + * + * TODO: add !\o{NNNNN} last Unicode is 4177777 maxint is 37777777777 + * + * Control chars: ?\cX Means: ord(X) ^ ord('@') + * + * Old hex escapes: \xXX unbraced must be 2 xdigits + * + * Perl hex escapes: !\x{XXX} braced may be 1-8 xdigits NB: proper Unicode + * never needs more than 6, as highest valid codepoint is 0x10FFFF, not + * maxint 0xFFFFFFFF + * + * Lame Java escape: \[IDIOT JAVA PREPROCESSOR]uXXXX must be exactly 4 + * xdigits; + * + * I can't write XXXX in this comment where it belongs because the damned + * Java Preprocessor can't mind its own business. Idiots! + * + * Lame Python escape: !\UXXXXXXXX must be exactly 8 xdigits + * + * TODO: Perl translation escapes: \Q \U \L \E \[IDIOT JAVA PREPROCESSOR]u + * \l These are not so important to cover if you're passing the result to + * Pattern.compile(), since it handles them for you further downstream. Hm, + * what about \[IDIOT JAVA PREPROCESSOR]u? + * + * @param oldstr + * @return + */ + public static String unescapeString(String oldstr) { + if (oldstr.charAt(0) != '"' || oldstr.charAt(oldstr.length() - 1) != '"') { + // TODO: error + //throw new TweakerExecuteException("Not a valid string constant: " + // + oldstr); + } + oldstr = oldstr.substring(1, oldstr.length() - 1); + + /* + * In contrast to fixing Java's broken regex charclasses, this one need + * be no bigger, as unescaping shrinks the string here, where in the + * other one, it grows it. + */ + + StringBuilder newstr = new StringBuilder(oldstr.length()); + + boolean saw_backslash = false; + + for (int i = 0; i < oldstr.length(); i++) { + int cp = oldstr.codePointAt(i); + if (oldstr.codePointAt(i) > Character.MAX_VALUE) { + i++; + /**** WE HATES UTF-16! WE HATES IT FOREVERSES!!! ****/ + } + + if (!saw_backslash) { + if (cp == '\\') { + saw_backslash = true; + } else { + newstr.append(Character.toChars(cp)); + } + continue; /* switch */ + } + + if (cp == '\\') { + saw_backslash = false; + newstr.append('\\'); + newstr.append('\\'); + continue; /* switch */ + } + + switch (cp) { + + case 'r': + newstr.append('\r'); + break; /* switch */ + + case 'n': + newstr.append('\n'); + break; /* switch */ + + case 'f': + newstr.append('\f'); + break; /* switch */ + + /* PASS a \b THROUGH!! */ + case 'b': + newstr.append("\\b"); + break; /* switch */ + + case 't': + newstr.append('\t'); + break; /* switch */ + + case 'a': + newstr.append('\007'); + break; /* switch */ + + case 'e': + newstr.append('\033'); + break; /* switch */ + + /* + * A "control" character is what you get when you xor its codepoint + * with '@'==64. This only makes sense for ASCII, and may not yield + * a "control" character after all. + * + * Strange but true: "\c{" is ";", "\c}" is "=", etc. + */ + case 'c': { + if (++i == oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException("trailing \\c"); + } + cp = oldstr.codePointAt(i); + /* + * don't need to grok surrogates, as next line blows them up + */ + if (cp > 0x7f) { + // TODO: error + //throw new TweakerExecuteException( + // "expected ASCII after \\c"); + } + newstr.append(Character.toChars(cp ^ 64)); + break; /* switch */ + } + + case '8': + case '9': + // TODO: error + //throw new TweakerExecuteException("illegal octal digit"); + /* NOTREACHED */ + + /* + * may be 0 to 2 octal digits following this one so back up one + * for fallthrough to next case; unread this digit and fall + * through to next case. + */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + --i; + /* FALLTHROUGH */ + + /* + * Can have 0, 1, or 2 octal digits following a 0 this permits + * larger values than octal 377, up to octal 777. + */ + case '0': { + if (i + 1 == oldstr.length()) { + /* found \0 at end of string */ + newstr.append(Character.toChars(0)); + break; /* switch */ + } + i++; + int digits = 0; + int j; + for (j = 0; j <= 2; j++) { + if (i + j == oldstr.length()) { + break; /* for */ + } + /* safe because will unread surrogate */ + int ch = oldstr.charAt(i + j); + if (ch < '0' || ch > '7') { + break; /* for */ + } + digits++; + } + if (digits == 0) { + --i; + newstr.append('\0'); + break; /* switch */ + } + int value = 0; + try { + value = Integer + .parseInt(oldstr.substring(i, i + digits), 8); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid octal value for \\0 escape"); + } + newstr.append(Character.toChars(value)); + i += digits - 1; + break; /* switch */ + } /* end case '0' */ + + case 'x': { + if (i + 2 > oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException( + // "string too short for \\x escape"); + } + i++; + boolean saw_brace = false; + if (oldstr.charAt(i) == '{') { + /* ^^^^^^ ok to ignore surrogates here */ + i++; + saw_brace = true; + } + int j; + for (j = 0; j < 8; j++) { + + if (!saw_brace && j == 2) { + break; /* for */ + } + + /* + * ASCII test also catches surrogates + */ + int ch = oldstr.charAt(i + j); + if (ch > 127) { + // TODO: error + //throw new TweakerExecuteException( + // "illegal non-ASCII hex digit in \\x escape"); + } + + if (saw_brace && ch == '}') { + break; /* for */ + } + + if (!((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))) { + // TODO: error + //throw new TweakerExecuteException(String.format( + // "illegal hex digit #%d '%c' in \\x", ch, ch)); + } + + } + if (j == 0) { + // TODO: error + //throw new TweakerExecuteException( + // "empty braces in \\x{} escape"); + } + int value = 0; + try { + value = Integer.parseInt(oldstr.substring(i, i + j), 16); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid hex value for \\x escape"); + } + newstr.append(Character.toChars(value)); + if (saw_brace) { + j++; + } + i += j - 1; + break; /* switch */ + } + + case 'u': { + if (i + 4 > oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException( + // "string too short for \\u escape"); + } + i++; + int j; + for (j = 0; j < 4; j++) { + /* this also handles the surrogate issue */ + if (oldstr.charAt(i + j) > 127) { + // TODO: error + //throw new TweakerExecuteException( + // "illegal non-ASCII hex digit in \\u escape"); + } + } + int value = 0; + try { + value = Integer.parseInt(oldstr.substring(i, i + j), 16); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid hex value for \\u escape"); + } + newstr.append(Character.toChars(value)); + i += j - 1; + break; /* switch */ + } + + case 'U': { + if (i + 8 > oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException( + // "string too short for \\U escape"); + } + i++; + int j; + for (j = 0; j < 8; j++) { + /* this also handles the surrogate issue */ + if (oldstr.charAt(i + j) > 127) { + // TODO: error + //throw new TweakerExecuteException( + // "illegal non-ASCII hex digit in \\U escape"); + } + } + int value = 0; + try { + value = Integer.parseInt(oldstr.substring(i, i + j), 16); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid hex value for \\U escape"); + } + newstr.append(Character.toChars(value)); + i += j - 1; + break; /* switch */ + } + + default: + newstr.append('\\'); + newstr.append(Character.toChars(cp)); + /* + * say(String.format( + * "DEFAULT unrecognized escape %c passed through", cp)); + */ + break; /* switch */ + + } + saw_backslash = false; + } + + /* weird to leave one at the end */ + if (saw_backslash) { + newstr.append('\\'); + } + + return newstr.toString(); + } + + /* + * Return a string "U+XX.XXX.XXXX" etc, where each XX set is the xdigits of + * the logical Unicode code point. No bloody brain-damaged UTF-16 surrogate + * crap, just true logical characters. + */ + private static String uniplus(String s) { + if (s.length() == 0) { + return ""; + } + /* This is just the minimum; sb will grow as needed. */ + StringBuilder sb = new StringBuilder(2 + 3 * s.length()); + sb.append("U+"); + for (int i = 0; i < s.length(); i++) { + sb.append(String.format("%X", s.codePointAt(i))); + if (s.codePointAt(i) > Character.MAX_VALUE) { + i++; + /**** WE HATES UTF-16! WE HATES IT FOREVERSES!!! ****/ + } + if (i + 1 < s.length()) { + sb.append("."); + } + } + return sb.toString(); + } +}