Storing all data in a serializable way for a bunch of blocks.

Discussion in 'Plugin Development' started by Jnorr44, May 12, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    Jnorr44

    I have been using the following class to store a bunch of information on a bunch of blocks. The problem with this is that when built, certain blocks (not signs, since I accounted for them manually) do not hold all data that they started with. I was wondering if the getData() method of MaterialData holds all information on a block, including those with extra information such as CommandBlocks, and Signs. If getData() works, or if there is any other way, please let me know, I really need to figure this out.

    Code:
    public class BlockFormation implements Serializable {
        private static final long serialVersionUID = -8042352178889935444L;
        private byte[][][] formation;
        private int buildSchedulerId = -1, ranThroughBuild = 0;
        private static final String signLinesStart = "|123signLines321|";
        private static final String newSignLine = "|123newLine321|";
     
        /* Reduces the coordinates of the formation to those of a distance away from the bottom-left location */
        private static byte[][][] reduce(byte[][][] formation, int lowX, int lowY, int lowZ) {
            for (int i = 0; i < formation.length; i++) {
                formation[i][0][0] -= lowX;
                formation[i][1][0] -= lowY;
                formation[i][2][0] -= lowZ;
            }
            return formation;
        }
     
        /**
        * Converts an array of locations into a BlockFormation.
        * @param locations The array of locations used in the creation of a BlockFormation.
        * @return A new, serializable BlockFormation instance based on the given locations.
        */
        public static BlockFormation toBlockFormation(Location[] locations) {
            ArrayList<byte[][]> dataHolder = new ArrayList<byte[][]>();
            int largestDataSize = 1;
            for (Location location : locations) {
                int x = location.getBlockX();
                int y = location.getBlockY();
                int z = location.getBlockZ();
                Block block = location.getBlock();
                int id = block.getTypeId();
                byte[] data = Integer.toString(block.getData()).getBytes();
                if (block.getState() instanceof Sign) {
                    try {
                        String lines = new String(data, "UTF-8") + signLinesStart;
                        for (String line : ((Sign) block.getState()).getLines()) {
                            lines += line + newSignLine;
                        }
                        System.out.println(lines);//TODO remove
                        data = lines.getBytes("UTF-8");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                if (data.length > largestDataSize) {
                    largestDataSize = data.length;
                }
                byte[][] blockData = new byte[][] { {(byte) x}, {(byte) y}, {(byte) z}, {(byte) id}, data};
                dataHolder.add(blockData);
            }
            byte[][][] formation = new byte[dataHolder.size()][5][largestDataSize];
            formation = dataHolder.toArray(formation);
            return new BlockFormation(formation);
        }
     
        /**
        * Creates a new BlockFormation with the given 2D integer-array formation.
        * @param formation The 2D array that consists of {X, Y, Z, ID, DATA} in that order.
        */
        public BlockFormation(byte[][][] formation) {
            if (formation != null) {
                int lowX = Integer.MAX_VALUE, lowY = Integer.MAX_VALUE, lowZ = Integer.MAX_VALUE;
                for (int i = 0; i < formation.length; i++) {
                    int x = formation[i][0][0];
                    int y = formation[i][1][0];
                    int z = formation[i][2][0];
                    lowX = (x < lowX) ? x : lowX;
                    lowY = (y < lowY) ? y : lowY;
                    lowZ = (z < lowZ) ? z : lowZ;
                }
                this.formation = reduce(formation, lowX, lowY, lowZ);
            }
        }
     
        /**
        * Builds this formation at the location given. The location given will be the bottom-left corner of the formation being built.
        * @param bottomLeft The bottom-left location of the formation.
        */
        public void build(final Location bottomLeft, JavaPlugin plugin) {//TODO lookup how WE copy/pastes
            buildSchedulerId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() {
                @Override public void run() {
                    boolean redo = false;
                    ranThroughBuild++;
                    for (int i = 0; i < formation.length; i++) {
                        Location modification = bottomLeft.clone().add(formation[i][0][0], formation[i][1][0], formation[i][2][0]);
                        Block block = modification.getBlock();
                        if (block.getTypeId() != formation[i][3][0]) {
                            redo = true;
                        }
                    }
                    if (!redo || ranThroughBuild >= 10) {
                        Bukkit.getScheduler().cancelTask(buildSchedulerId);
                        buildSchedulerId = -1;
                        return;
                    }
                    for (int i = 0; i < formation.length; i++) {
                        Location modification = bottomLeft.clone().add(formation[i][0][0], formation[i][1][0], formation[i][2][0]);
                        Block block = modification.getBlock();
                        if (formation[i][3][0] < 0) {
                            formation[i][3][0] = 0;
                        }
                        block.setTypeId(formation[i][3][0]);
                        byte[] data = formation[i][4];
                        block.setData(data[0]);
                        String cumulative = "";
                        try {
                            cumulative = new String(data, "UTF-8");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if (block.getState() instanceof Sign) {
                            String[] lines = cumulative.substring(signLinesStart.length()).split(newSignLine);
                            System.out.println(cumulative);//TODO remove
                            for (int j = 0; j < lines.length; j++) {
                                String line = lines[j];
                                if (j < 4) {
                                    ((Sign) block.getState()).setLine(j, line);
                                }
                            }
                        }
                    }
                }
            }, 40, 40);
        }
       
        public boolean isBuilding() {
            return buildSchedulerId != -1;
        }
     
        /**
        * Removes a formation equivalent to this formation, starting at the bottom-left location given.
        * @param bottomLeft The bottom-left location to start removing blocks from.
        */
        public void remove(Location bottomLeft) {
            for (int i = 0; i < formation.length; i++) {
                Location modification = bottomLeft.clone().add(formation[i][0][0], formation[i][1][0], formation[i][2][0]);
                Block block = modification.getBlock();
                block.setTypeId(0);
            }
        }
     
        /**
        * Rotates the formation along the x, y, or z axis given, at an angle of the given int "degrees".
        * @param xyz The axis to rotate on.
        * @param degrees The degrees (in degrees, not radians) to rotate.
        */
        public void rotate(char xyz, int degrees) {
            for (int i = 0; i < formation.length; i++) {
                int x = formation[i][0][0];
                int y = formation[i][1][0];
                int z = formation[i][2][0];
                double sinDegrees = Math.sin(Math.toRadians(degrees));
                double cosDegrees = Math.cos(Math.toRadians(degrees));
                if (xyz == 'x') {
                    y = (int) Math.round(y * cosDegrees - z * sinDegrees);
                    z = (int) Math.round(y * sinDegrees + z * cosDegrees);
                } else if (xyz == 'y') {
                    z = (int) Math.round(z * cosDegrees - x * sinDegrees);
                    x = (int) Math.round(z * sinDegrees + x * cosDegrees);
                } else if (xyz == 'z') {
                    x = (int) Math.round(x * cosDegrees - y * sinDegrees);
                    y = (int) Math.round(x * sinDegrees + y * cosDegrees);
                }
                formation[i][0][0] = (byte) x;
                formation[i][1][0] = (byte) y;
                formation[i][2][0] = (byte) z;
            }
        }
     
        /**
        * Dilates the BlockFormation by the dilation short specified. This does not actually build the formation, or change an existing physical formation, but simply changes the
        * coordinates inside of this instance.
        * @param dilation The size of the dilation.
        */
        public void dilate(short dilation) {
            ArrayList<byte[][]> blockDataArrays = new ArrayList<byte[][]>();
            int largestDataSize = 1;
            for (int i = 0; i < formation.length; i++) {
                int x = formation[i][0][0];
                int y = formation[i][1][0];
                int z = formation[i][2][0];
                int id = formation[i][3][0];
                byte[] data = formation[i][4];
                if (data.length > largestDataSize) {
                    largestDataSize = data.length;
                }
                for (short mod = 1; mod <= dilation; mod++) {
                    for (int j = 0; j <= 2; j++) {
                        byte[][] newBlockData = new byte[][] { {(byte) ((j == 0) ? x * mod : x)}, {(byte) ((j == 1) ? y * mod : y)}, {(byte) ((j == 2) ? z * mod : z)}, {(byte) id}, data};
                        blockDataArrays.add(newBlockData);
                    }
                }
            }
            byte[][][] newFormation = new byte[blockDataArrays.size()][5][largestDataSize];
            newFormation = blockDataArrays.toArray(newFormation);
            formation = newFormation;
        }
     
        /**
        * Reflects the BlockFormation over the given axis. This does not actually build the formation, or change an existing physical formation, but simply changes the coordinates
        * inside of this instance.
        * @param xyz Whether the reflection should be along the X, Y or Z axis. This can be set to the char 'x', 'y', or 'z' only.
        */
        public void reflect(char xyz) {
            int numAxis = 2;// over x, z changes
            numAxis = (xyz == 'y') ? 0 : numAxis;// over y, x changes
            numAxis = (xyz == 'z') ? 1 : numAxis;// over z, y changes
            for (int i = 0; i < formation.length; i++) {
                formation[i][numAxis][0] *= -1;
            }
        }
     
        /**
        * Gets the raw formation used in this BlockFormation instance.
        * @return The raw formation as a 3D byte array.
        */
        public byte[][][] getRaw() {
            return formation;
        }
    }
     
  2. Offline

    socram8888

    Special blocks extends BlockState. You can use block.getState() to get a BlockState, and cast that BlockState to a Furnace, Chest, Sign...
    For example, getting contents from a Furnace is done like this:
    Code:
    Furnace furnace = (Furnace) furnaceBlock.getState();
    ItemStack[] contents = furnace.getInventory().getContents();
    Have a look at https://github.com/Bukkit/Bukkit/tree/master/src/main/java/org/bukkit/block for every type of block that contains extra data and how to get it.
     
  3. Offline

    Jnorr44

    Thats what I ended up doing, but I was really looking for one method that gets it all.
     
Thread Status:
Not open for further replies.

Share This Page