From 31d43b27d88542b4c830910dbf38a33538633785 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sun, 21 May 2017 23:40:24 +1000 Subject: [PATCH] Various minor forge 1710 - fix block extra optimize expressions better texture util error memory optimize PrimitiveList don't clear brush settings on assignment (affected craftscripts) --- build.gradle | 2 +- .../com/boydti/fawe/bukkit/BukkitMain.java | 11 + .../fawe/bukkit/v1_12/NMSRegistryDumper.java | 317 +++++++++++ core/src/main/java/com/boydti/fawe/Fawe.java | 22 +- .../main/java/com/boydti/fawe/config/BBC.java | 1 + .../jnbt/anvil/HeightMapMCAGenerator.java | 13 +- .../fawe/object/brush/BrushSettings.java | 5 + .../fawe/object/collection/PrimitiveList.java | 315 +++++++++++ .../object/pattern/ExpressionPattern.java | 6 +- .../boydti/fawe/util/CachedTextureUtil.java | 3 +- .../boydti/fawe/util/CleanTextureUtil.java | 3 +- .../boydti/fawe/util/DelegateTextureUtil.java | 3 +- .../boydti/fawe/util/FilteredTextureUtil.java | 3 +- .../boydti/fawe/util/RandomTextureUtil.java | 3 +- .../com/boydti/fawe/util/TextureUtil.java | 12 +- .../java/com/boydti/fawe/util/Updater.java | 4 +- .../sk89q/worldedit/command/MaskCommands.java | 2 +- .../worldedit/command/WorldEditCommands.java | 2 +- .../worldedit/command/tool/BrushTool.java | 2 +- .../extension/factory/DefaultMaskParser.java | 3 +- .../internal/expression/Expression.java | 177 ++++++ .../runtime/ExpressionEnvironment.java | 36 ++ .../internal/expression/runtime/Function.java | 127 +++++ .../expression/runtime/Functions.java | 511 ++++++++++++++++++ .../shape/WorldEditExpressionEnvironment.java | 68 +++ .../java/com/boydti/fawe/forge/FaweForge.java | 9 +- .../java/com/boydti/fawe/forge/ForgeMain.java | 6 +- .../fawe/forge/v110/ForgeChunk_All.java | 1 + .../java/com/boydti/fawe/forge/FaweForge.java | 13 +- .../java/com/boydti/fawe/forge/ForgeMain.java | 6 +- .../fawe/forge/v111/ForgeChunk_All.java | 1 + .../fawe/forge/v111/ForgeQueue_All.java | 2 +- .../fawe/forge/v1710/ForgeChunk_All.java | 8 +- .../fawe/forge/v1710/ForgeQueue_All.java | 14 +- .../fawe/forge/v194/ForgeChunk_All.java | 1 + 35 files changed, 1664 insertions(+), 48 deletions(-) create mode 100644 bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/NMSRegistryDumper.java create mode 100644 core/src/main/java/com/boydti/fawe/object/collection/PrimitiveList.java create mode 100644 core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java create mode 100644 core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java create mode 100644 core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java create mode 100644 core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java create mode 100644 core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java diff --git a/build.gradle b/build.gradle index 14218e4a..a5163fd0 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ ext { date = git.head().date.format("yy.MM.dd") revision = "-${git.head().abbreviatedId}" parents = git.head().parentIds; - index = -94; // Offset to mach CI + index = -95; // Offset to mach CI int major, minor, patch; major = minor = patch = 0; for (;parents != null && !parents.isEmpty();index++) { diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitMain.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitMain.java index b5b9283f..5f99c2e7 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitMain.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/BukkitMain.java @@ -6,10 +6,12 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_All; import com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10; import com.boydti.fawe.bukkit.v1_11.BukkitQueue_1_11; import com.boydti.fawe.bukkit.v1_12.BukkitQueue_1_12; +import com.boydti.fawe.bukkit.v1_12.NMSRegistryDumper; import com.boydti.fawe.bukkit.v1_7.BukkitQueue17; import com.boydti.fawe.bukkit.v1_8.BukkitQueue18R3; import com.boydti.fawe.bukkit.v1_9.BukkitQueue_1_9_R1; import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; import com.sk89q.worldedit.world.World; import java.lang.reflect.Field; import java.util.ArrayList; @@ -66,6 +68,15 @@ public class BukkitMain extends JavaPlugin { try { BukkitQueue_0.checkVersion(v.name()); this.version = v; + if (version == Version.v1_12_R1) { + try { + Fawe.debug("Running 1.12 registry dumper!"); + NMSRegistryDumper dumper = new NMSRegistryDumper(MainUtil.getFile(getDataFolder(), "extrablocks.json")); + dumper.run(); + } catch (Throwable e) { + e.printStackTrace(); + } + } break; } catch (IllegalStateException e) {} } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/NMSRegistryDumper.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/NMSRegistryDumper.java new file mode 100644 index 00000000..b5734c66 --- /dev/null +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_12/NMSRegistryDumper.java @@ -0,0 +1,317 @@ +package com.boydti.fawe.bukkit.v1_12; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import net.minecraft.server.v1_12_R1.BaseBlockPosition; +import net.minecraft.server.v1_12_R1.Block; +import net.minecraft.server.v1_12_R1.BlockStateDirection; +import net.minecraft.server.v1_12_R1.BlockStateList; +import net.minecraft.server.v1_12_R1.EnumDirection; +import net.minecraft.server.v1_12_R1.EnumPistonReaction; +import net.minecraft.server.v1_12_R1.IBlockData; +import net.minecraft.server.v1_12_R1.IBlockState; +import net.minecraft.server.v1_12_R1.Material; +import net.minecraft.server.v1_12_R1.MinecraftKey; +import net.minecraft.server.v1_12_R1.Vec3D; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class NMSRegistryDumper { + + private final Field fieldDirection; + private File file; + private Gson gson; + + public NMSRegistryDumper(File file) throws NoSuchFieldException { + this.file = file; + GsonBuilder builder = new GsonBuilder().setPrettyPrinting(); + builder.registerTypeAdapter(BaseBlockPosition.class, new BaseBlockPositionAdapter()); + builder.registerTypeAdapter(Vec3D.class, new Vec3DAdapter()); + this.gson = builder.create(); + this.fieldDirection = EnumDirection.class.getDeclaredField("m"); + this.fieldDirection.setAccessible(true); + } + + public void run() throws Exception { + List> list = new LinkedList>(); + for (Block block : Block.REGISTRY) { + MinecraftKey key = Block.REGISTRY.b(block); + list.add(getProperties(block, key)); + } + Collections.sort(list, new MapComparator()); + String out = gson.toJson(list); + this.write(out); + } + + private Map getProperties(Block b, MinecraftKey key) throws IllegalAccessException { + Map map = new LinkedHashMap(); + map.put("legacyId", Block.getId(b)); + map.put("id", key.toString()); + map.put("unlocalizedName", b.a()); + map.put("localizedName", b.getName()); + map.put("states", getStates(b)); + map.put("material", getMaterial(b)); + return map; + } + + private Map getStates(Block b) throws IllegalAccessException { + Map map = new LinkedHashMap(); + BlockStateList bs = b.s(); + Collection> props = bs.d(); + for (IBlockState prop : props) { + map.put(prop.a(), dataValues(b, prop)); + } + + return map; + } + + private final Vec3D[] rotations = { + new Vec3D(0, 0, -1), + new Vec3D(0.5, 0, -1), + new Vec3D(1, 0, -1), + new Vec3D(1, 0, -0.5), + new Vec3D(1, 0, 0), + new Vec3D(1, 0, 0.5), + new Vec3D(1, 0, 1), + new Vec3D(0.5, 0, 1), + new Vec3D(0, 0, 1), + new Vec3D(-0.5, 0, 1), + new Vec3D(-1, 0, 1), + new Vec3D(-1, 0, 0.5), + new Vec3D(-1, 0, 0), + new Vec3D(-1, 0, -0.5), + new Vec3D(-1, 0, -1), + new Vec3D(-0.5, 0, -1) + }; + + + private BaseBlockPosition addDirection(Object orig, BaseBlockPosition addend) { + if (orig instanceof BaseBlockPosition) { + BaseBlockPosition ov = ((BaseBlockPosition) orig); + return new BaseBlockPosition(addend.getX() + ov.getX(), addend.getY() + ov.getY(), addend.getZ() + ov.getZ()); + } + return addend; + } + + private Map dataValues(Block b, IBlockState prop) throws IllegalAccessException { + //BlockState bs = b.getBlockState(); + IBlockData base = b.fromLegacyData(0); + + Map dataMap = new LinkedHashMap(); + Map valueMap = new LinkedHashMap(); + List dvs = new ArrayList(); + for (Comparable val : (Iterable) prop.c()) { + Map stateMap = new LinkedHashMap(); + int dv = b.toLegacyData(base.set(prop, val)); + stateMap.put("data", dv); + + Map addAfter = null; + String addAfterName = null; + + dvs.add(dv); + + if (prop instanceof BlockStateDirection) { + EnumDirection dir = EnumDirection.valueOf(val.toString().toUpperCase()); + BaseBlockPosition vec = (BaseBlockPosition) fieldDirection.get(dir); + stateMap.put("direction", addDirection(stateMap.get("direction"), vec)); + } else if (prop.a().equals("half")) { + if (prop.a(val).equals("top")) { + stateMap.put("direction", addDirection(stateMap.get("direction"), new BaseBlockPosition(0, 1, 0))); + } else if (prop.a(val).equals("bottom")) { + stateMap.put("direction", addDirection(stateMap.get("direction"), new BaseBlockPosition(0, -1, 0))); + } + } else if (prop.a().equals("axis")) { + if (prop.a(val).equals("x")) { + stateMap.put("direction", new BaseBlockPosition(1, 0, 0)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(-1, 0, 0)); + addAfterName = "-x"; + } else if (prop.a(val).equals("y")) { + stateMap.put("direction", new BaseBlockPosition(0, 1, 0)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(0, -1, 0)); + addAfterName = "-y"; + } else if (prop.a(val).equals("z")) { + stateMap.put("direction", new BaseBlockPosition(0, 0, 1)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(0, 0, -1)); + addAfterName = "-z"; + } + } else if (prop.a().equals("rotation")) { + stateMap.put("direction", rotations[Integer.valueOf(prop.a(val))]); + } else if (prop.a().equals("facing")) { // usually already instanceof PropertyDirection, unless it's a lever + if (prop.a(val).equals("south")) { + stateMap.put("direction", new BaseBlockPosition(0, 0, 1)); + } else if (prop.a(val).equals("north")) { + stateMap.put("direction", new BaseBlockPosition(0, 0, -1)); + } else if (prop.a(val).equals("west")) { + stateMap.put("direction", new BaseBlockPosition(-1, 0, 0)); + } else if (prop.a(val).equals("east")) { + stateMap.put("direction", new BaseBlockPosition(1, 0, 0)); + } + /* + // TODO fix these levers. they disappear right now + // excluding them just means they won't get rotated + } else if (prop.getName(val).equals("up_x")) { + stateMap.put("direction", new BaseBlockPosition(1, 1, 0)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(-1, 1, 0)); + addAfterName = "up_-x"; + } else if (prop.getName(val).equals("up_z")) { + stateMap.put("direction", new BaseBlockPosition(0, 1, 1)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(0, 1, -1)); + addAfterName = "up_-z"; + } else if (prop.getName(val).equals("down_x")) { + stateMap.put("direction", new BaseBlockPosition(1, -1, 0)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(-1, -1, 0)); + addAfterName = "down_-x"; + } else if (prop.getName(val).equals("down_z")) { + stateMap.put("direction", new BaseBlockPosition(0, -1, 1)); + addAfter = new LinkedHashMap(); + addAfter.put("data", dv); + addAfter.put("direction", new BaseBlockPosition(0, -1, -1)); + addAfterName = "down_-z"; + }*/ + } + valueMap.put(prop.a(val), stateMap); + if (addAfter != null) { + valueMap.put(addAfterName, addAfter); + } + } + + // attempt to calc mask + int dataMask = 0; + for (int dv : dvs) { + dataMask |= dv; + } + dataMap.put("dataMask", dataMask); + + dataMap.put("values", valueMap); + return dataMap; + } + + private Map getMaterial(Block b) { + IBlockData bs = b.getBlockData(); + Map map = new LinkedHashMap(); + map.put("powerSource", b.isPowerSource(bs)); + map.put("lightOpacity", b.m(bs)); + map.put("lightValue", b.o(bs)); + map.put("usingNeighborLight", b.p(bs)); + map.put("hardness", getField(b, Block.class, "blockHardness", "strength")); + map.put("resistance", getField(b, Block.class, "blockResistance", "durability")); + map.put("ticksRandomly", b.isTicking()); + map.put("fullCube", b.c(bs)); + map.put("slipperiness", b.frictionFactor); + map.put("renderedAsNormalBlock", b.l(bs)); + //map.put("solidFullCube", b.isSolidFullCube()); + Material m = b.q(bs); + map.put("liquid", m.isLiquid()); + map.put("solid", m.isBuildable()); + map.put("movementBlocker", m.isSolid()); + //map.put("blocksLight", m.blocksLight()); + map.put("burnable", m.isBurnable()); + map.put("opaque", m.k()); + map.put("replacedDuringPlacement", m.isReplaceable()); + map.put("toolRequired", !m.isAlwaysDestroyable()); + map.put("fragileWhenPushed", m.getPushReaction() == EnumPistonReaction.DESTROY); + map.put("unpushable", m.getPushReaction() == EnumPistonReaction.BLOCK); + map.put("adventureModeExempt", getField(m, Material.class, "isAdventureModeExempt", "Q")); + //map.put("mapColor", rgb(m.getMaterialMapColor().colorValue)); + map.put("ambientOcclusionLightValue", b.s(bs) ? 0.2F:1.0F); + map.put("grassBlocking", false); // idk what this property was originally supposed to be...grass uses a combination of light values to check growth + return map; + } + + private Object getField(Object obj, Class clazz, String name, String obfName) { + try { + Field f; + try { + f = clazz.getDeclaredField(name); + } catch (NoSuchFieldException ignored) { + f = clazz.getDeclaredField(obfName); + } + if (f == null) return null; + f.setAccessible(true); + return f.get(obj); + } catch (IllegalAccessException ignored) { + } catch (NoSuchFieldException ignored) { + } + return null; + } + + private String rgb(int i) { + int r = (i >> 16) & 0xFF; + int g = (i >> 8) & 0xFF; + int b = i & 0xFF; + return String.format("#%02x%02x%02x", r, g, b); + } + + private void write(String s) { + try { + FileOutputStream str = new FileOutputStream(file); + str.write(s.getBytes()); + } catch (IOException e) { + System.err.printf("Error writing registry dump: %e", e); + } + } + + + public static class BaseBlockPositionAdapter extends TypeAdapter { + @Override + public BaseBlockPosition read(final JsonReader in) throws IOException { + throw new UnsupportedOperationException(); + } + @Override + public void write(final JsonWriter out, final BaseBlockPosition vec) throws IOException { + out.beginArray(); + out.value(vec.getX()); + out.value(vec.getY()); + out.value(vec.getZ()); + out.endArray(); + } + } + + public static class Vec3DAdapter extends TypeAdapter { + @Override + public Vec3D read(final JsonReader in) throws IOException { + throw new UnsupportedOperationException(); + } + @Override + public void write(final JsonWriter out, final Vec3D vec) throws IOException { + out.beginArray(); + out.value(vec.x); + out.value(vec.y); + out.value(vec.z); + out.endArray(); + } + } + + private static class MapComparator implements Comparator> { + @Override + public int compare(Map a, Map b) { + return ((Integer) a.get("legacyId")).compareTo((Integer) b.get("legacyId")); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index bb684dd5..cdd5d1ac 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -32,13 +32,14 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockData; import com.sk89q.worldedit.command.BiomeCommands; import com.sk89q.worldedit.command.BrushCommands; +import com.sk89q.worldedit.command.BrushOptionsCommands; import com.sk89q.worldedit.command.ChunkCommands; import com.sk89q.worldedit.command.ClipboardCommands; import com.sk89q.worldedit.command.FlattenedClipboardTransform; -import com.sk89q.worldedit.command.OptionsCommands; import com.sk89q.worldedit.command.GenerationCommands; import com.sk89q.worldedit.command.HistoryCommands; import com.sk89q.worldedit.command.NavigationCommands; +import com.sk89q.worldedit.command.OptionsCommands; import com.sk89q.worldedit.command.RegionCommands; import com.sk89q.worldedit.command.SchematicCommands; import com.sk89q.worldedit.command.ScriptingCommands; @@ -46,7 +47,6 @@ import com.sk89q.worldedit.command.SnapshotCommands; import com.sk89q.worldedit.command.SnapshotUtilCommands; import com.sk89q.worldedit.command.SuperPickaxeCommands; import com.sk89q.worldedit.command.ToolCommands; -import com.sk89q.worldedit.command.BrushOptionsCommands; import com.sk89q.worldedit.command.UtilityCommands; import com.sk89q.worldedit.command.WorldEditCommands; import com.sk89q.worldedit.command.composition.SelectionCommand; @@ -106,6 +106,9 @@ import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.internal.LocalWorldAdapter; +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment; +import com.sk89q.worldedit.internal.expression.runtime.Functions; import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation; import com.sk89q.worldedit.math.transform.AffineTransform; @@ -113,6 +116,7 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.EllipsoidRegion; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.regions.shape.ArbitraryShape; +import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.PasteBuilder; import com.sk89q.worldedit.session.SessionManager; @@ -128,6 +132,7 @@ import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; import com.sk89q.worldedit.util.formatting.component.MessageBox; import com.sk89q.worldedit.world.registry.BundledBlockData; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; @@ -340,8 +345,10 @@ public class Fawe { public TextureUtil getCachedTextureUtil(boolean randomize, int min, int max) { TextureUtil tu = getTextureUtil(); - tu = min == 0 && max == 100 ? tu : new CleanTextureUtil(tu, min, max); - tu = randomize ? new RandomTextureUtil(tu) : new CachedTextureUtil(tu); + try { + tu = min == 0 && max == 100 ? tu : new CleanTextureUtil(tu, min, max); + tu = randomize ? new RandomTextureUtil(tu) : new CachedTextureUtil(tu); + } catch (FileNotFoundException neverHappens) { neverHappens.printStackTrace(); } return tu; } @@ -355,7 +362,7 @@ public class Fawe { textures = tmp = new TextureUtil(); tmp.loadModTextures(); } catch (IOException e) { - e.printStackTrace(); + throw new RuntimeException(e); } } } @@ -545,6 +552,11 @@ public class Fawe { BlockReplace.inject(); // Optimizations + Features ForwardExtentCopy.inject(); // Fixes + optimizations ChangeSetExecutor.inject(); // Optimizations + // Expression + ExpressionEnvironment.inject(); // Optimizations + features + WorldEditExpressionEnvironment.inject(); // Optimizations + features + Expression.inject(); // Optimizations + Functions.inject(); // Optimizations // BlockData BlockData.inject(); // Temporary fix for 1.9.4 BundledBlockData.inject(); // Add custom rotation diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index 515fe3b6..5c6abd4f 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -201,6 +201,7 @@ public enum BBC { COMMAND_INVALID_SYNTAX("The command was not used properly (no more help available).", "WorldEdit.Command"), + COMMAND_CLARIFYING_BRACKET("&7Added clarifying bracket for &c%s0", "WorldEdit.Help"), HELP_SUGGEST("&7Couldn't find %s0. Maybe try one of &c%s1 &7?", "WorldEdit.Help"), HELP_HEADER_CATEGORIES("Command Types", "WorldEdit.Help"), HELP_HEADER_SUBCOMMANDS("Subcommands", "WorldEdit.Help"), diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java index e1c3842f..209f8123 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.world.registry.WorldData; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileNotFoundException; import java.util.Arrays; public class HeightMapMCAGenerator extends MCAWriter implements Extent { @@ -79,11 +80,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent { if (textureUtil == null) { textureUtil = Fawe.get().getTextureUtil(); } - if (randomVariation) { - return new RandomTextureUtil(textureUtil); - } else { - return new CachedTextureUtil(textureUtil); - } + try { + if (randomVariation) { + return new RandomTextureUtil(textureUtil); + } else { + return new CachedTextureUtil(textureUtil); + } + } catch (FileNotFoundException neverHappens) { neverHappens.printStackTrace(); return null; } } public void setWaterHeight(int waterHeight) { diff --git a/core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java b/core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java index b7b3c9c9..1ff831f9 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/BrushSettings.java @@ -137,6 +137,11 @@ public class BrushSettings { return this; } + public BrushSettings clearPerms() { + permissions.clear(); + return this; + } + public BrushSettings addSetting(SettingType type, String args) { constructor.put(type, args); return this; diff --git a/core/src/main/java/com/boydti/fawe/object/collection/PrimitiveList.java b/core/src/main/java/com/boydti/fawe/object/collection/PrimitiveList.java new file mode 100644 index 00000000..00f6309e --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/collection/PrimitiveList.java @@ -0,0 +1,315 @@ +package com.boydti.fawe.object.collection; + +import java.lang.reflect.Array; +import java.util.AbstractList; + +public class PrimitiveList extends AbstractList { + private final Class primitive; + private final Type type; + private int length; + private int totalLength; + private Object arr; + + private enum Type { + Byte, + Boolean, + Short, + Character, + Integer, + Float, + Long, + Double + } + + public PrimitiveList(Class type) { + try { + Class boxed; + if (type.isPrimitive()) { + this.primitive = type; + boxed = (Class) Array.get(Array.newInstance(primitive,1),0).getClass(); + } else { + this.primitive = (Class) type.getField("TYPE").get(null); + boxed = type; + } + this.type = Type.valueOf(boxed.getSimpleName()); + } catch (Throwable e) { + throw new RuntimeException(e); + } + length = 0; + totalLength = 0; + arr = Array.newInstance(primitive, 0); + } + + public PrimitiveList(T[] arr) { + try { + Class boxed = (Class) arr.getClass().getComponentType(); + this.primitive = (Class) boxed.getField("TYPE").get(null); + this.type = Type.valueOf(boxed.getSimpleName()); + } catch (Throwable e) { + throw new RuntimeException(e); + } + this.arr = Array.newInstance(primitive, arr.length); + for (int i = 0; i < arr.length; i++) { + T val = arr[i]; + if (val != null) setFast(i, val); + } + this.length = arr.length; + this.totalLength = length; + } + + public PrimitiveList(Object arr) { + if (!arr.getClass().isArray()) { + throw new IllegalArgumentException("Argument must be an array!"); + } + this.primitive = arr.getClass().getComponentType(); + Class boxed = (Class) Array.get(Array.newInstance(primitive, 1), 0).getClass(); + this.type = Type.valueOf(boxed.getSimpleName()); + this.arr = arr; + this.length = Array.getLength(arr); + this.totalLength = length; + } + + public Object getArray() { + return arr; + } + + @Override + public T get(int index) { + return (T) getFast(index); + } + + public byte getByte(int index) { + return type == Type.Double ? ((byte[]) arr)[index] : (byte) getFast(index); + } + + public boolean getBoolean(int index) { + return type == Type.Boolean ? ((boolean[]) arr)[index] : (boolean) getFast(index); + } + + public short getShort(int index) { + return type == Type.Short ? ((short[]) arr)[index] : (short) getFast(index); + } + + public char getCharacter(int index) { + return type == Type.Character ? ((char[]) arr)[index] : (char) getFast(index); + } + + public int getInt(int index) { + return type == Type.Integer ? ((int[]) arr)[index] : (int) getFast(index); + } + + public float getFloat(int index) { + return type == Type.Float ? ((float[]) arr)[index] : (float) getFast(index); + } + + public long getLong(int index) { + return type == Type.Long ? ((long[]) arr)[index] : (long) getFast(index); + } + + public double getDouble(int index) { + return type == Type.Double ? ((double[]) arr)[index] : (double) getFast(index); + } + + private final Object getFast(int index) { + switch (type) { + case Byte: + return ((byte[]) arr)[index]; + case Boolean: + return ((boolean[]) arr)[index]; + case Short: + return ((short[]) arr)[index]; + case Character: + return ((char[]) arr)[index]; + case Integer: + return ((int[]) arr)[index]; + case Float: + return ((float[]) arr)[index]; + case Long: + return ((long[]) arr)[index]; + case Double: + return ((double[]) arr)[index]; + } + return null; + } + + @Override + public T set(int index, T element) { + T value = get(index); + setFast(index, element); + return value; + } + + public void set(int index, char value) { + switch (type) { + default: + setFast(index, value); + return; + case Character: + ((char[]) arr)[index] = value; + return; + } + } + + public void set(int index, byte value) { + switch (type) { + default: + setFast(index, value); + return; + case Byte: + ((byte[]) arr)[index] = value; + return; + } + } + + public void set(int index, int value) { + switch (type) { + default: + setFast(index, value); + return; + case Integer: + ((int[]) arr)[index] = value; + return; + case Long: + ((long[]) arr)[index] = (long) value; + return; + case Double: + ((double[]) arr)[index] = (double) value; + return; + } + } + + public void set(int index, double value) { + switch (type) { + default: + setFast(index, value); + return; + case Float: + ((float[]) arr)[index] = (float) value; + return; + case Long: + ((long[]) arr)[index] = (long) value; + return; + case Double: + ((double[]) arr)[index] = value; + return; + } + } + + public final void setFast(int index, Object element) { + switch (type) { + case Byte: + ((byte[]) arr)[index] = (byte) element; + return; + case Boolean: + ((boolean[]) arr)[index] = (boolean) element; + return; + case Short: + ((short[]) arr)[index] = (short) element; + return; + case Character: + ((char[]) arr)[index] = (char) element; + return; + case Integer: + ((int[]) arr)[index] = (int) element; + return; + case Float: + ((float[]) arr)[index] = (float) element; + return; + case Long: + ((long[]) arr)[index] = (long) element; + return; + case Double: + ((double[]) arr)[index] = (double) element; + return; + } + } + + + @Override + public void add(int index, T element) { + if (index == length) { + if (totalLength == length) { + Object tmp = arr; + totalLength = (length << 1) + 16; + arr = Array.newInstance(primitive, totalLength); + System.arraycopy(tmp, 0, arr, 0, length); + } + setFast(length, element); + length++; + } else { + if (totalLength == length) { + Object tmp = arr; + totalLength = (length << 1) + 16; + arr = Array.newInstance(primitive, totalLength); + System.arraycopy(tmp, 0, arr, 0, index); + } + System.arraycopy(arr, index, arr, index + 1, length - index); + set(index, element); + length++; + } + } + + private void ensureAddCapacity() { + if (totalLength == length) { + Object tmp = arr; + totalLength = (length << 1) + 16; + arr = Array.newInstance(primitive, totalLength); + System.arraycopy(tmp, 0, arr, 0, length); + } + } + + @Override + public boolean add(T element) { + ensureAddCapacity(); + setFast(length++, element); + return true; + } + + public boolean add(int element) { + ensureAddCapacity(); + set(length++, element); + return true; + } + + public boolean add(double element) { + ensureAddCapacity(); + set(length++, element); + return true; + } + + public boolean add(byte element) { + ensureAddCapacity(); + set(length++, element); + return true; + } + + public boolean add(char element) { + ensureAddCapacity(); + set(length++, element); + return true; + } + + @Override + public T remove(int index) { + if (index < 0 || index > length) throw new IndexOutOfBoundsException(index + " not in [0, " + length + "]"); + T value = get(index); + if (index != length) { + System.arraycopy(arr, index + 1, arr, index, length - index - 1); + } + length--; + return value; + } + + @Override + public int size() { + return length; + } + + @Override + public void clear() { + if (length != 0) { + this.arr = Array.newInstance(primitive, 0); + } + length = 0; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java b/core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java index a680c31f..640012f5 100644 --- a/core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java +++ b/core/src/main/java/com/boydti/fawe/object/pattern/ExpressionPattern.java @@ -28,7 +28,7 @@ public class ExpressionPattern extends AbstractPattern { /** * Create a new instance. * - * @param expression the expression + * @param input the expression * @throws ExpressionException thrown if there is an error with the expression */ public ExpressionPattern(String input) throws ExpressionException { @@ -56,7 +56,11 @@ public class ExpressionPattern extends AbstractPattern { double combined = expression.evaluate(vector.getX(), vector.getY(), vector.getZ()); return FaweCache.CACHE_BLOCK[(char) combined]; } catch (EvaluationException e) { + e.printStackTrace(); return EditSession.nullBlock; + } catch (Throwable e) { + e.printStackTrace(); + throw e; } } diff --git a/core/src/main/java/com/boydti/fawe/util/CachedTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/CachedTextureUtil.java index 36305c53..d40ace1b 100644 --- a/core/src/main/java/com/boydti/fawe/util/CachedTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/CachedTextureUtil.java @@ -3,6 +3,7 @@ package com.boydti.fawe.util; import com.boydti.fawe.FaweCache; import com.sk89q.worldedit.blocks.BaseBlock; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.io.FileNotFoundException; public class CachedTextureUtil extends DelegateTextureUtil { private final TextureUtil parent; @@ -10,7 +11,7 @@ public class CachedTextureUtil extends DelegateTextureUtil { private transient Int2ObjectOpenHashMap colorBiomeMap; private transient Int2ObjectOpenHashMap colorLayerMap; - public CachedTextureUtil(TextureUtil parent) { + public CachedTextureUtil(TextureUtil parent) throws FileNotFoundException { super(parent); this.parent = parent; this.colorBlockMap = new Int2ObjectOpenHashMap<>(); diff --git a/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java index 0ccf7d85..56990d00 100644 --- a/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/CleanTextureUtil.java @@ -1,9 +1,10 @@ package com.boydti.fawe.util; +import java.io.FileNotFoundException; import java.util.Arrays; public class CleanTextureUtil extends TextureUtil { - public CleanTextureUtil(TextureUtil parent, int minPercent, int maxPercent) { + public CleanTextureUtil(TextureUtil parent, int minPercent, int maxPercent) throws FileNotFoundException { super(parent.getFolder()); int minIndex = ((parent.distances.length - 1) * minPercent) / 100; int maxIndex = ((parent.distances.length - 1) * maxPercent) / 100; diff --git a/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java index 19a8a2d6..b05effd9 100644 --- a/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java @@ -3,12 +3,13 @@ package com.boydti.fawe.util; import com.sk89q.worldedit.blocks.BaseBlock; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; public class DelegateTextureUtil extends TextureUtil { private final TextureUtil parent; - public DelegateTextureUtil(TextureUtil parent) { + public DelegateTextureUtil(TextureUtil parent) throws FileNotFoundException { super(parent.getFolder()); this.parent = parent; } diff --git a/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java index 6883f71c..33879ec0 100644 --- a/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/FilteredTextureUtil.java @@ -2,10 +2,11 @@ package com.boydti.fawe.util; import com.boydti.fawe.FaweCache; import com.sk89q.worldedit.blocks.BaseBlock; +import java.io.FileNotFoundException; import java.util.Set; public class FilteredTextureUtil extends TextureUtil { - public FilteredTextureUtil(TextureUtil parent, Set blocks) { + public FilteredTextureUtil(TextureUtil parent, Set blocks) throws FileNotFoundException { super(parent.getFolder()); this.validBiomes = parent.validBiomes; this.blockColors = parent.blockColors; diff --git a/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java index 2f7c53b1..c3ea0b73 100644 --- a/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/RandomTextureUtil.java @@ -5,12 +5,13 @@ import com.boydti.fawe.object.PseudoRandom; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.io.FileNotFoundException; public class RandomTextureUtil extends CachedTextureUtil { private final int grassColor; - public RandomTextureUtil(TextureUtil parent) { + public RandomTextureUtil(TextureUtil parent) throws FileNotFoundException { super(parent); this.grassColor = parent.getColor(FaweCache.getBlock(BlockID.GRASS, 0)); } diff --git a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java index 0cce6959..abf91259 100644 --- a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -13,6 +13,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; @@ -50,6 +51,7 @@ public class TextureUtil { protected int[] validLayerColors; protected char[][] validLayerBlocks; + /** * https://github.com/erich666/Mineways/blob/master/Win/biomes.cpp */ @@ -315,12 +317,15 @@ public class TextureUtil { new BiomeColor(255, "Unknown Biome", 0.8f, 0.4f, 0x92BD59, 0x77AB2F ), }; - public TextureUtil() { + public TextureUtil() throws FileNotFoundException { this(MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.TEXTURES)); } - public TextureUtil(File folder) { + public TextureUtil(File folder) throws FileNotFoundException { this.folder = folder; + if (!folder.exists()) { + throw new FileNotFoundException("Please create a `FastAsyncWorldEdit/textures` folder with `.minecraft/versions` jar or mods in it."); + } } public BaseBlock getNearestBlock(int color) { @@ -808,6 +813,9 @@ public class TextureUtil { if (all == null) { all = (String) textures.get("top"); } + if (all == null) { + all = (String) textures.get("pattern"); + } if (all != null) { String textureName = getFileName(all); // Add the model diff --git a/core/src/main/java/com/boydti/fawe/util/Updater.java b/core/src/main/java/com/boydti/fawe/util/Updater.java index bedf1a9d..ae536a36 100644 --- a/core/src/main/java/com/boydti/fawe/util/Updater.java +++ b/core/src/main/java/com/boydti/fawe/util/Updater.java @@ -16,7 +16,7 @@ public class Updater { public String getChanges() { if (changes == null) { - try (Scanner scanner = new Scanner(new URL("http://boydti.com/fawe/cl?" + Integer.toHexString(Fawe.get().getVersion().hash)).openStream(), "UTF-8")) { + try (Scanner scanner = new Scanner(new URL("http://empcraft.com/fawe/cl?" + Integer.toHexString(Fawe.get().getVersion().hash)).openStream(), "UTF-8")) { changes = scanner.useDelimiter("\\A").next(); } catch (IOException e) { e.printStackTrace(); @@ -60,7 +60,7 @@ public class Updater { fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); } Fawe.debug("Updated FAWE to " + versionString); - MainUtil.sendAdmin("&7Restart to update FAWE with these changes: &c/fawe changelog &7or&c " + "http://boydti.com/fawe/cl?" + Integer.toHexString(currentVersion.hash)); + MainUtil.sendAdmin("&7Restart to update FAWE with these changes: &c/fawe changelog &7or&c " + "http://empcraft.com/fawe/cl?" + Integer.toHexString(currentVersion.hash)); } } } diff --git a/core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java b/core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java index 3d64de46..e8588a3c 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java @@ -368,7 +368,7 @@ public class MaskCommands extends MethodCommands { max = 1 ) public Mask expression(EditSession editSession, String input) throws ExpressionException { - Expression exp = Expression.compile(input.substring(1), "x", "y", "z"); + Expression exp = Expression.compile(input, "x", "y", "z"); WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment( editSession, Vector.ONE, Vector.ZERO); exp.setEnvironment(env); diff --git a/core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index e980f3f5..08e2c2dc 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -131,7 +131,7 @@ public class WorldEditCommands { Updater updater = Fawe.get().getUpdater(); String changes = updater != null ? updater.getChanges() : null; if (changes == null) { - try (Scanner scanner = new Scanner(new URL("http://boydti.com/fawe/cl?" + Integer.toHexString(Fawe.get().getVersion().hash)).openStream(), "UTF-8")) { + try (Scanner scanner = new Scanner(new URL("http://empcraft.com/fawe/cl?" + Integer.toHexString(Fawe.get().getVersion().hash)).openStream(), "UTF-8")) { changes = scanner.useDelimiter("\\A").next(); } } diff --git a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index 00983528..216fa2e3 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -264,7 +264,7 @@ public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool public void setBrush(Brush brush, String permission, Player player) { if (player != null) clear(player); BrushSettings current = getContext(); - current.clear(); + current.clearPerms(); current.setBrush(brush); current.addPermission(permission); } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index 7e1c6254..234039f7 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -1,6 +1,7 @@ package com.sk89q.worldedit.extension.factory; import com.boydti.fawe.command.FaweParser; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.util.StringMan; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.worldedit.WorldEdit; @@ -103,7 +104,7 @@ public class DefaultMaskParser extends FaweParser { input = input.substring(input.indexOf(char0) + 1); mask = parseFromInput(char0 + "[" + input + "]", context); if (actor != null) { - actor.print("&7Added clarifying bracket for: " + char0 + "&c[&7" + input + "&c]&7"); + BBC.COMMAND_CLARIFYING_BRACKET.send(actor, char0 + "[" + input + "]"); } return mask; } diff --git a/core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java b/core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java new file mode 100644 index 00000000..3b1ec972 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java @@ -0,0 +1,177 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.expression; + +import com.sk89q.worldedit.internal.expression.lexer.Lexer; +import com.sk89q.worldedit.internal.expression.lexer.tokens.Token; +import com.sk89q.worldedit.internal.expression.parser.Parser; +import com.sk89q.worldedit.internal.expression.runtime.Constant; +import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; +import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment; +import com.sk89q.worldedit.internal.expression.runtime.Functions; +import com.sk89q.worldedit.internal.expression.runtime.RValue; +import com.sk89q.worldedit.internal.expression.runtime.ReturnException; +import com.sk89q.worldedit.internal.expression.runtime.Variable; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Compiles and evaluates expressions. + * + *

Supported operators:

+ * + *
    + *
  • Logical: &&, ||, ! (unary)
  • + *
  • Bitwise: ~ (unary), >>, <<
  • + *
  • Arithmetic: +, -, *, /, % (modulo), ^ (power), - (unary), --, ++ (prefix only)
  • + *
  • Comparison: <=, >=, >, <, ==, !=, ~= (near)
  • + *
+ * + *

Supported functions: abs, acos, asin, atan, atan2, cbrt, ceil, cos, cosh, + * exp, floor, ln, log, log10, max, max, min, min, rint, round, sin, sinh, + * sqrt, tan, tanh and more. (See the Functions class or the wiki)

+ * + *

Constants: e, pi

+ * + *

To compile an equation, run + * {@code Expression.compile("expression here", "var1", "var2"...)}. + * If you wish to run the equation multiple times, you can then optimize it, + * by calling {@link #optimize()}. You can then run the equation as many times + * as you want by calling {@link #evaluate(double...)}. You do not need to + * pass values for all variables specified while compiling. + * To query variables after evaluation, you can use + * {@link #getVariable(String, boolean)}. To get a value out of these, use + * {@link Variable#getValue()}.

+ * + *

Variables are also supported and can be set either by passing values + * to {@link #evaluate(double...)}.

+ */ +public class Expression { + + private static final ThreadLocal> instance = new ThreadLocal>() { + @Override + protected ArrayDeque initialValue() { + return new ArrayDeque<>(); + } + }; + + private final Map variables = new HashMap(); + private final String[] variableNames; + private Variable[] variableArray; + private RValue root; + private final Functions functions = new Functions(); + private ExpressionEnvironment environment; + + public static Expression compile(String expression, String... variableNames) throws ExpressionException { + return new Expression(expression, variableNames); + } + + private Expression(String expression, String... variableNames) throws ExpressionException { + this(Lexer.tokenize(expression), variableNames); + } + + private Expression(List tokens, String... variableNames) throws ExpressionException { + variables.put("e", new Constant(-1, Math.E)); + variables.put("pi", new Constant(-1, Math.PI)); + variables.put("true", new Constant(-1, 1)); + variables.put("false", new Constant(-1, 0)); + + this.variableNames = variableNames; + variableArray = new Variable[variableNames.length]; + for (int i = 0; i < variableNames.length; i++) { + String variableName = variableNames[i]; + if (variables.containsKey(variableName)) { + throw new ExpressionException(-1, "Tried to overwrite identifier '" + variableName + "'"); + } + Variable var = new Variable(0); + variables.put(variableName, var); + variableArray[i] = var; + } + + root = Parser.parse(tokens, this); + } + + public double evaluate(double... values) throws EvaluationException { + for (int i = 0; i < values.length; i++) { + Variable var = variableArray[i]; + var.value = values[i]; + } + pushInstance(); + try { + return root.getValue(); + } catch (ReturnException e) { + return e.getValue(); + } finally { + popInstance(); + } + } + + public void optimize() throws EvaluationException { + root = root.optimize(); + } + + @Override + public String toString() { + return root.toString(); + } + + public RValue getVariable(String name, boolean create) { + RValue variable = variables.get(name); + if (variable == null && create) { + variables.put(name, variable = new Variable(0)); + } + + return variable; + } + + public static Expression getInstance() { + return instance.get().peek(); + } + + private void pushInstance() { + ArrayDeque foo = instance.get(); + foo.push(this); + } + + private void popInstance() { + ArrayDeque foo = instance.get(); + + foo.pop(); + } + + public Functions getFunctions() { + return functions; + } + + public ExpressionEnvironment getEnvironment() { + return environment; + } + + public void setEnvironment(ExpressionEnvironment environment) { + this.environment = environment; + } + + public static Class inject() { + return Expression.class; + } + +} diff --git a/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java b/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java new file mode 100644 index 00000000..7b1ac363 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/ExpressionEnvironment.java @@ -0,0 +1,36 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.expression.runtime; + +import com.sk89q.worldedit.blocks.BaseBlock; + +/** + * Represents a way to access blocks in a world. Has to accept non-rounded coordinates. + */ +public interface ExpressionEnvironment { + + BaseBlock getBlock(double x, double y, double z); + BaseBlock getBlockAbs(double x, double y, double z); + BaseBlock getBlockRel(double x, double y, double z); + + public static Class inject() { + return ExpressionEnvironment.class; + } +} diff --git a/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java b/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java new file mode 100644 index 00000000..2c3d0dbf --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Function.java @@ -0,0 +1,127 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.expression.runtime; + +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.parser.ParserException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Wrapper for a Java method and its arguments (other Nodes). + */ +public class Function extends Node { + + /** + * Add this annotation on functions that don't always return the same value + * for the same inputs and on functions with side-effects. + */ + @Retention(RetentionPolicy.RUNTIME) + public @interface Dynamic { } + + public final Method method; + public final RValue[] args; + + public Function(int position, Method method, RValue... args) { + super(position); + this.method = method; + this.method.setAccessible(true); + this.args = args; + } + + @Override + public final double getValue() throws EvaluationException { + return invokeMethod(method, args); + } + + public static double invokeMethod(Method method, Object[] args) throws EvaluationException { + try { + return (Double) method.invoke(null, args); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof EvaluationException) { + throw (EvaluationException) e.getTargetException(); + } + throw new EvaluationException(-1, "Exception caught while evaluating expression", e.getTargetException()); + } catch (IllegalAccessException e) { + throw new EvaluationException(-1, "Internal error while evaluating expression", e); + } + } + + @Override + public String toString() { + final StringBuilder ret = new StringBuilder(method.getName()).append('('); + boolean first = true; + for (Object obj : args) { + if (!first) { + ret.append(", "); + } + first = false; + ret.append(obj); + } + return ret.append(')').toString(); + } + + @Override + public char id() { + return 'f'; + } + + @Override + public RValue optimize() throws EvaluationException { + final RValue[] optimizedArgs = new RValue[args.length]; + boolean optimizable = !method.isAnnotationPresent(Dynamic.class); + int position = getPosition(); + for (int i = 0; i < args.length; ++i) { + final RValue optimized = optimizedArgs[i] = args[i].optimize(); + + if (!(optimized instanceof Constant)) { + optimizable = false; + } + + if (optimized.getPosition() < position) { + position = optimized.getPosition(); + } + } + + if (optimizable) { + return new Constant(position, invokeMethod(method, optimizedArgs)); + } else { + return new Function(position, method, optimizedArgs); + } + } + + @Override + public RValue bindVariables(Expression expression, boolean preferLValue) throws ParserException { + final Class[] parameters = method.getParameterTypes(); + for (int i = 0; i < args.length; ++i) { + final boolean argumentPrefersLValue = LValue.class.isAssignableFrom(parameters[i]); + args[i] = args[i].bindVariables(expression, argumentPrefersLValue); + } + + return this; + } + + public static Class inject() { + return Function.class; + } +} diff --git a/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java b/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java new file mode 100644 index 00000000..8b93cb83 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java @@ -0,0 +1,511 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.expression.runtime; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.runtime.Function.Dynamic; +import com.sk89q.worldedit.math.noise.PerlinNoise; +import com.sk89q.worldedit.math.noise.RidgedMultiFractalNoise; +import com.sk89q.worldedit.math.noise.VoronoiNoise; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * Contains all functions that can be used in expressions. + */ +@SuppressWarnings("UnusedDeclaration") +public final class Functions { + + private static class Overload { + private final Method method; + private final int mask; + private final boolean isSetter; + + private Overload(Method method) throws IllegalArgumentException { + this.method = method; + + boolean isSetter = false; + int accum = 0; + Class[] parameters = method.getParameterTypes(); + for (Class parameter : parameters) { + if (isSetter) { + throw new IllegalArgumentException("Method takes arguments that can't be cast to RValue."); + } + + if (double.class.equals(parameter)) { + isSetter = true; + continue; + } + + if (!RValue.class.isAssignableFrom(parameter)) { + throw new IllegalArgumentException("Method takes arguments that can't be cast to RValue."); + } + + accum <<= 2; + + if (LValue.class.isAssignableFrom(parameter)) { + accum |= 3; + } else { + accum |= 1; + } + } + mask = accum; + this.isSetter = isSetter; + } + + public boolean matches(boolean isSetter, RValue... args) { + if (this.isSetter != isSetter) { + return false; + } + + if (this.method.getParameterTypes().length != args.length) { // TODO: optimize + return false; + } + + int accum = 0; + for (RValue argument : args) { + accum <<= 2; + + if (argument instanceof LValue) { + accum |= 3; + } else { + accum |= 1; + } + } + + return (accum & mask) == mask; + } + } + + public static Function getFunction(int position, String name, RValue... args) throws NoSuchMethodException { + final Method getter = getMethod(name, false, args); + try { + Method setter = getMethod(name, true, args); + return new LValueFunction(position, getter, setter, args); + } catch (NoSuchMethodException e) { + return new Function(position, getter, args); + } + } + + private static Method getMethod(String name, boolean isSetter, RValue... args) throws NoSuchMethodException { + final List overloads = functions.get(name); + if (overloads != null) { + for (Overload overload : overloads) { + if (overload.matches(isSetter, args)) { + return overload.method; + } + } + } + + throw new NoSuchMethodException(); // TODO: return null (check for side-effects first) + } + + private static final Map> functions = new HashMap>(); + static { + for (Method method : Functions.class.getMethods()) { + try { + addFunction(method); + } catch (IllegalArgumentException ignored) { } + } + } + + + public static void addFunction(Method method) throws IllegalArgumentException { + final String methodName = method.getName(); + + Overload overload = new Overload(method); + + List overloads = functions.get(methodName); + if (overloads == null) { + functions.put(methodName, overloads = new ArrayList()); + } + + overloads.add(overload); + } + + + public static double sin(RValue x) throws EvaluationException { + return Math.sin(x.getValue()); + } + + public static double cos(RValue x) throws EvaluationException { + return Math.cos(x.getValue()); + } + + public static double tan(RValue x) throws EvaluationException { + return Math.tan(x.getValue()); + } + + + public static double asin(RValue x) throws EvaluationException { + return Math.asin(x.getValue()); + } + + public static double acos(RValue x) throws EvaluationException { + return Math.acos(x.getValue()); + } + + public static double atan(RValue x) throws EvaluationException { + return Math.atan(x.getValue()); + } + + public static double atan2(RValue y, RValue x) throws EvaluationException { + return Math.atan2(y.getValue(), x.getValue()); + } + + + public static double sinh(RValue x) throws EvaluationException { + return Math.sinh(x.getValue()); + } + + public static double cosh(RValue x) throws EvaluationException { + return Math.cosh(x.getValue()); + } + + public static double tanh(RValue x) throws EvaluationException { + return Math.tanh(x.getValue()); + } + + + public static double sqrt(RValue x) throws EvaluationException { + return Math.sqrt(x.getValue()); + } + + public static double cbrt(RValue x) throws EvaluationException { + return Math.cbrt(x.getValue()); + } + + + public static double abs(RValue x) throws EvaluationException { + return Math.abs(x.getValue()); + } + + public static double min(RValue a, RValue b) throws EvaluationException { + return Math.min(a.getValue(), b.getValue()); + } + + public static double min(RValue a, RValue b, RValue c) throws EvaluationException { + return Math.min(a.getValue(), Math.min(b.getValue(), c.getValue())); + } + + public static double max(RValue a, RValue b) throws EvaluationException { + return Math.max(a.getValue(), b.getValue()); + } + + public static double max(RValue a, RValue b, RValue c) throws EvaluationException { + return Math.max(a.getValue(), Math.max(b.getValue(), c.getValue())); + } + + + public static double ceil(RValue x) throws EvaluationException { + return Math.ceil(x.getValue()); + } + + public static double floor(RValue x) throws EvaluationException { + return Math.floor(x.getValue()); + } + + public static double rint(RValue x) throws EvaluationException { + return Math.rint(x.getValue()); + } + + public static double round(RValue x) throws EvaluationException { + return Math.round(x.getValue()); + } + + + public static double exp(RValue x) throws EvaluationException { + return Math.exp(x.getValue()); + } + + public static double ln(RValue x) throws EvaluationException { + return Math.log(x.getValue()); + } + + public static double log(RValue x) throws EvaluationException { + return Math.log(x.getValue()); + } + + public static double log10(RValue x) throws EvaluationException { + return Math.log10(x.getValue()); + } + + + public static double rotate(LValue x, LValue y, RValue angle) throws EvaluationException { + final double f = angle.getValue(); + + final double cosF = Math.cos(f); + final double sinF = Math.sin(f); + + final double xOld = x.getValue(); + final double yOld = y.getValue(); + + x.assign(xOld * cosF - yOld * sinF); + y.assign(xOld * sinF + yOld * cosF); + + return 0.0; + } + + public static double swap(LValue x, LValue y) throws EvaluationException { + final double tmp = x.getValue(); + + x.assign(y.getValue()); + y.assign(tmp); + + return 0.0; + } + + + private static final Map gmegabuf = new HashMap(); + private final Map megabuf = new HashMap(); + + public Map getMegabuf() { + return megabuf; + } + + private static double[] getSubBuffer(Map megabuf, Integer key) { + double[] ret = megabuf.get(key); + if (ret == null) { + megabuf.put(key, ret = new double[1024]); + } + return ret; + } + + private static double getBufferItem(final Map megabuf, final int index) { + return getSubBuffer(megabuf, index & ~1023)[index & 1023]; + } + + private static double setBufferItem(final Map megabuf, final int index, double value) { + return getSubBuffer(megabuf, index & ~1023)[index & 1023] = value; + } + + @Dynamic + public static double gmegabuf(RValue index) throws EvaluationException { + return getBufferItem(gmegabuf, (int) index.getValue()); + } + + @Dynamic + public static double gmegabuf(RValue index, double value) throws EvaluationException { + return setBufferItem(gmegabuf, (int) index.getValue(), value); + } + + @Dynamic + public static double megabuf(RValue index) throws EvaluationException { + return getBufferItem(Expression.getInstance().getFunctions().megabuf, (int) index.getValue()); + } + + @Dynamic + public static double megabuf(RValue index, double value) throws EvaluationException { + return setBufferItem(Expression.getInstance().getFunctions().megabuf, (int) index.getValue(), value); + } + + @Dynamic + public static double closest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException { + return findClosest( + Expression.getInstance().getFunctions().megabuf, + x.getValue(), + y.getValue(), + z.getValue(), + (int) index.getValue(), + (int) count.getValue(), + (int) stride.getValue() + ); + } + + @Dynamic + public static double gclosest(RValue x, RValue y, RValue z, RValue index, RValue count, RValue stride) throws EvaluationException { + return findClosest( + gmegabuf, + x.getValue(), + y.getValue(), + z.getValue(), + (int) index.getValue(), + (int) count.getValue(), + (int) stride.getValue() + ); + } + + private static double findClosest(Map megabuf, double x, double y, double z, int index, int count, int stride) { + int closestIndex = -1; + double minDistanceSquared = Double.MAX_VALUE; + + for (int i = 0; i < count; ++i) { + double currentX = getBufferItem(megabuf, index+0) - x; + double currentY = getBufferItem(megabuf, index+1) - y; + double currentZ = getBufferItem(megabuf, index+2) - z; + + double currentDistanceSquared = currentX*currentX + currentY*currentY + currentZ*currentZ; + + if (currentDistanceSquared < minDistanceSquared) { + minDistanceSquared = currentDistanceSquared; + closestIndex = index; + } + + index += stride; + } + + return closestIndex; + } + + + private static final Random random = new Random(); + + @Dynamic + public static double random() { + return random.nextDouble(); + } + + @Dynamic + public static double randint(RValue max) throws EvaluationException { + return random.nextInt((int) Math.floor(max.getValue())); + } + + private static final ThreadLocal localPerlin = new ThreadLocal() { + @Override + protected PerlinNoise initialValue() { + return new PerlinNoise(); + } + }; + + public static double perlin(RValue seed, RValue x, RValue y, RValue z, RValue frequency, RValue octaves, RValue persistence) throws EvaluationException { + PerlinNoise perlin = localPerlin.get(); + try { + perlin.setSeed((int) seed.getValue()); + perlin.setFrequency(frequency.getValue()); + perlin.setOctaveCount((int) octaves.getValue()); + perlin.setPersistence(persistence.getValue()); + } catch (IllegalArgumentException e) { + throw new EvaluationException(0, "Perlin noise error: " + e.getMessage()); + } + return perlin.noise(new Vector(x.getValue(), y.getValue(), z.getValue())); + } + + private static final ThreadLocal localVoronoi = new ThreadLocal() { + @Override + protected VoronoiNoise initialValue() { + return new VoronoiNoise(); + } + }; + + public static double voronoi(RValue seed, RValue x, RValue y, RValue z, RValue frequency) throws EvaluationException { + VoronoiNoise voronoi = localVoronoi.get(); + try { + voronoi.setSeed((int) seed.getValue()); + voronoi.setFrequency(frequency.getValue()); + } catch (IllegalArgumentException e) { + throw new EvaluationException(0, "Voronoi error: " + e.getMessage()); + } + return voronoi.noise(new Vector(x.getValue(), y.getValue(), z.getValue())); + } + + private static final ThreadLocal localRidgedMulti = new ThreadLocal() { + @Override + protected RidgedMultiFractalNoise initialValue() { + return new RidgedMultiFractalNoise(); + } + }; + + public static double ridgedmulti(RValue seed, RValue x, RValue y, RValue z, RValue frequency, RValue octaves) throws EvaluationException { + RidgedMultiFractalNoise ridgedMulti = localRidgedMulti.get(); + try { + ridgedMulti.setSeed((int) seed.getValue()); + ridgedMulti.setFrequency(frequency.getValue()); + ridgedMulti.setOctaveCount((int) octaves.getValue()); + } catch (IllegalArgumentException e) { + throw new EvaluationException(0, "Ridged multi error: " + e.getMessage()); + } + return ridgedMulti.noise(new Vector(x.getValue(), y.getValue(), z.getValue())); + } + + private static double queryInternal(RValue type, RValue data, double typeId, double dataValue) throws EvaluationException { + // Compare to input values and determine return value + // -1 is a wildcard, always true + final double ret = ((type.getValue() == -1 || typeId == type.getValue()) + && (data.getValue() == -1 || dataValue == data.getValue())) ? 1.0 : 0.0; + + if (type instanceof LValue) { + ((LValue) type).assign(typeId); + } + + if (data instanceof LValue) { + ((LValue) data).assign(dataValue); + } + + return ret; + } + + @Dynamic + public static double query(RValue x, RValue y, RValue z, RValue type, RValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + BaseBlock block = environment.getBlock(xp, yp, zp); + int typeId = block.getId(); + int dataValue = block.getData(); + + return queryInternal(type, data, typeId, dataValue); + } + + @Dynamic + public static double queryAbs(RValue x, RValue y, RValue z, RValue type, RValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + BaseBlock block = environment.getBlockAbs(xp, yp, zp); + int typeId = block.getId(); + int dataValue = block.getData(); + + return queryInternal(type, data, typeId, dataValue); + } + + @Dynamic + public static double queryRel(RValue x, RValue y, RValue z, RValue type, RValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + BaseBlock block = environment.getBlockRel(xp, yp, zp); + int typeId = block.getId(); + int dataValue = block.getData(); + + return queryInternal(type, data, typeId, dataValue); + } + + public static Class inject() { + return Functions.class; + } +} diff --git a/core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java b/core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java new file mode 100644 index 00000000..78f286ef --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java @@ -0,0 +1,68 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.regions.shape; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment; + +public class WorldEditExpressionEnvironment implements ExpressionEnvironment { + + private final Vector unit; + private final Vector zero2; + private Vector current = new Vector(); + private EditSession editSession; + + public WorldEditExpressionEnvironment(EditSession editSession, Vector unit, Vector zero) { + this.editSession = editSession; + this.unit = unit; + this.zero2 = zero.add(0.5, 0.5, 0.5); + } + + @Override + public BaseBlock getBlock(double x, double y, double z) { + x = x * unit.getX() + zero2.getX(); + y = y * unit.getY() + zero2.getY(); + z = z * unit.getZ() + zero2.getZ(); + return editSession.getBlock((int) x, (int) y, (int) z); + } + + @Override + public BaseBlock getBlockAbs(double x, double y, double z) { + return editSession.getBlock((int) x, (int) y, (int) z); + } + + @Override + public BaseBlock getBlockRel(double x, double y, double z) { + x = x + current.getBlockX(); + y = y + current.getBlockY(); + z = z + current.getBlockZ(); + return editSession.getBlock((int) x, (int) y, (int) z); + } + + public void setCurrentBlock(Vector current) { + this.current = current; + } + + public static Class inject() { + return WorldEditExpressionEnvironment.class; + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java index 377e1dfe..436edf76 100644 --- a/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java +++ b/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -19,10 +19,8 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.Map; import java.util.UUID; import javax.management.InstanceAlreadyExistsException; -import net.minecraft.command.ServerCommandManager; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; import net.minecraftforge.fml.common.FMLCommonHandler; @@ -75,11 +73,8 @@ public class FaweForge implements IFawe { this.commands.put(label, cmd); } - public void insertCommands() { - for (Map.Entry entry : commands.entrySet()) { - ServerCommandManager scm = (ServerCommandManager) FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager(); - scm.registerCommand(new ForgeCommand(entry.getKey(), entry.getValue())); - } + public HashMap getCommands() { + return commands; } @Override diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java index 64d1353a..8333bf72 100644 --- a/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java @@ -1,9 +1,11 @@ package com.boydti.fawe.forge; import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; import java.io.File; import java.util.List; +import java.util.Map; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraftforge.common.MinecraftForge; @@ -34,7 +36,9 @@ public class ForgeMain { @Mod.EventHandler public void serverLoad(FMLServerStartingEvent event) { - IMP.insertCommands(); + for (Map.Entry entry : IMP.getCommands().entrySet()) { + event.registerServerCommand(new ForgeCommand(entry.getKey(), entry.getValue())); + } } @SubscribeEvent(priority = EventPriority.LOWEST) diff --git a/forge110/src/main/java/com/boydti/fawe/forge/v110/ForgeChunk_All.java b/forge110/src/main/java/com/boydti/fawe/forge/v110/ForgeChunk_All.java index 2cbeaf94..fef8712e 100644 --- a/forge110/src/main/java/com/boydti/fawe/forge/v110/ForgeChunk_All.java +++ b/forge110/src/main/java/com/boydti/fawe/forge/v110/ForgeChunk_All.java @@ -356,6 +356,7 @@ public class ForgeChunk_All extends CharFaweChunk { tileEntity.readFromNBT(tag); // ReadTagIntoTile } } + sectionPalettes = null; } catch (Throwable e) { MainUtil.handleError(e); } diff --git a/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java index a31e161b..7b50b168 100644 --- a/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java +++ b/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -19,10 +19,8 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.Map; import java.util.UUID; import javax.management.InstanceAlreadyExistsException; -import net.minecraft.command.ServerCommandManager; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; import net.minecraftforge.fml.common.FMLCommonHandler; @@ -70,18 +68,15 @@ public class FaweForge implements IFawe { private HashMap commands = new HashMap<>(); + public HashMap getCommands() { + return commands; + } + @Override public void setupCommand(String label, FaweCommand cmd) { this.commands.put(label, cmd); } - public void insertCommands() { - for (Map.Entry entry : commands.entrySet()) { - ServerCommandManager scm = (ServerCommandManager) FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager(); - scm.registerCommand(new ForgeCommand(entry.getKey(), entry.getValue())); - } - } - @Override public FawePlayer wrap(Object obj) { EntityPlayerMP player = null; diff --git a/forge111/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge111/src/main/java/com/boydti/fawe/forge/ForgeMain.java index ef196577..a7eba924 100644 --- a/forge111/src/main/java/com/boydti/fawe/forge/ForgeMain.java +++ b/forge111/src/main/java/com/boydti/fawe/forge/ForgeMain.java @@ -1,8 +1,10 @@ package com.boydti.fawe.forge; import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; import java.io.File; +import java.util.Map; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraftforge.common.MinecraftForge; @@ -33,7 +35,9 @@ public class ForgeMain { @Mod.EventHandler public void serverLoad(FMLServerStartingEvent event) { - IMP.insertCommands(); + for (Map.Entry entry : IMP.getCommands().entrySet()) { + event.registerServerCommand(new ForgeCommand(entry.getKey(), entry.getValue())); + } } @SubscribeEvent(priority = EventPriority.LOWEST) diff --git a/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeChunk_All.java b/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeChunk_All.java index 4c2e7a35..584e61d0 100644 --- a/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeChunk_All.java +++ b/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeChunk_All.java @@ -379,6 +379,7 @@ public class ForgeChunk_All extends CharFaweChunk { tileEntity.readFromNBT(tag); // ReadTagIntoTile } } + sectionPalettes = null; } catch (Throwable e) { MainUtil.handleError(e); } diff --git a/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeQueue_All.java b/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeQueue_All.java index efbf0bd2..37ae0958 100644 --- a/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeQueue_All.java +++ b/forge111/src/main/java/com/boydti/fawe/forge/v111/ForgeQueue_All.java @@ -8,7 +8,6 @@ import com.boydti.fawe.forge.MutableGenLayer; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.brush.visualization.VisualChunk; -import java.util.concurrent.atomic.LongAdder; import com.boydti.fawe.object.visitor.FaweChunkVisitor; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -30,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.LongAdder; import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeChunk_All.java b/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeChunk_All.java index cf7564b4..487e4521 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeChunk_All.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeChunk_All.java @@ -131,7 +131,7 @@ public class ForgeChunk_All extends CharFaweChunk { } if (id > 255) { NibbleArray nibble = extended[i]; - if (extended == null) { + if (nibble == null) { extended[i] = nibble = new NibbleArray(4096, 4); } nibble.set(x, y & 15, z, id >> 8); @@ -315,6 +315,12 @@ public class ForgeChunk_All extends CharFaweChunk { continue; case 1: currentIdArray[k] = 0; + if (extra) { + int x = FaweCache.CACHE_X[0][k]; + int y = FaweCache.CACHE_Y[0][k]; + int z = FaweCache.CACHE_Z[0][k]; + currentExtraArray.set(x, y, z, 0); + } continue; default: solid++; diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java b/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java index b89b3619..3084d5db 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java @@ -166,10 +166,18 @@ public class ForgeQueue_All extends NMSMappedFaweQueue { tileEntity.readFromNBT(tag); // ReadTagIntoTile } } + sectionPalettes = null; } catch (Throwable e) { MainUtil.handleError(e); }