Solved Replace items as they were

Discussion in 'Spigot Plugin Development' started by W0lv3s, Aug 12, 2018.

  1. So, I'm making a plugin that uses arenas, and I thought it'd be neat to enable players to save their arenas to files and easily move them around their server/other servers if they felt like it. It just saves block location and info to a SQLite db and retrieves it when required... nothing special.

    It works fairly well, it keeps items that were on the ground, but I'm not entirely sure what avenue to pursue for maintaining things like armor stands, chests with stuff in them, item frames and the like... I'm hoping there's something in the APIs that does that which I'm missing because it'd be super sweet to be able to replace those things without having to assess each type of item individually (check for chests, pull inventory, check for item frames, pull item, yadda yadda)...

    Code (Text):

    public class ArenaCapture {
        private static final int BATCH_SIZE = 1000;
        private KillaBeez killaBeez;
        private World world;
        private Arena arena;

        public ArenaCapture(KillaBeez killaBeez, World world, Arena arena) {
            this.killaBeez = killaBeez;
            this.world = world;
            this.arena = arena;
        }

        public boolean capture () {
            //debug
            System.out.println("BEGIN CAPTURE");

            ArrayList<BlockData> blocks = new ArrayList<>();
            ArrayList<TrimData> attachedItems = new ArrayList<>();

            int x = arena.getX();
            int xOff = arena.getxOff();
            int y = arena.getY();
            int yOff = arena.getyOff();
            int z = arena.getZ();
            int zOff = arena.getzOff();

            if (arena.getX() > arena.getxOff()) {
                x = arena.getxOff();
                xOff = arena.getX();
            }
            if (arena.getY() > arena.getyOff()) {
                y = arena.getyOff();
                yOff = arena.getY();
            }
            if (arena.getZ() > arena.getzOff()) {
                z = arena.getzOff();
                zOff = arena.getZ();
            }

            ArrayList<Entity> evaluatedEntities = new ArrayList<>();

            for (int i = x; i <= xOff; i++) {
                for (int j = y; j <= yOff; j++) {
                    for (int k = z; k <= zOff; k++) {
                        Block block = world.getBlockAt(i,j,k);
                        blocks.add(
                            new BlockData(
                                    //why *-1? no idea, but it was building everything upside down
                                    (arena.getX() - block.getX()) * -1,
                                    (arena.getY() - block.getY()) * -1,
                                    (arena.getZ() - block.getZ()) * -1,
                                    block.getBlockData().getAsString()
                            )
                        );
                        for (Entity entity : world.getNearbyEntities(block.getLocation(),2,2,2)) {
                            if (!(entity instanceof Item) || evaluatedEntities.contains(entity)) continue;
                            Item item = (Item) entity;
                            YamlConfiguration itemStack = new YamlConfiguration();
                            itemStack.set("0",item.getItemStack());
                            attachedItems.add(new TrimData(
                                    (arena.getX() - item.getLocation().getBlockX()) * -1,
                                    (arena.getY() - item.getLocation().getBlockY()) * -1,
                                    (arena.getZ() - item.getLocation().getBlockZ()) * -1,
                                    itemStack.saveToString()
                            ));
                            evaluatedEntities.add(entity);
                        }
                    }
                }
            }
            //debug
            System.out.println("SAVING " + blocks.size() + " BLOCKS TO DB");

            new BukkitRunnable() {
                @Override
                public void run() {
                    long start = System.currentTimeMillis();
                    try {
                        Connection connection = KillaBeez.getDbManager().getNewArenaDb(arena.getName());
                        if (connection == null) return;

                        connection.setAutoCommit(false);

                        PreparedStatement stmt = connection.prepareStatement(
                                "INSERT INTO main.arena_info (uuid, name, x_pos, x_offset, y_pos, y_offset, z_pos, z_offset) " +
                                        "VALUES (?,?,?,?,?,?,?,?)");
                        stmt.setString(1,arena.getUuid().toString());
                        stmt.setString(2,arena.getName());
                        stmt.setInt(3,arena.getX());
                        stmt.setInt(4,arena.getxOff());
                        stmt.setInt(5,arena.getY());
                        stmt.setInt(6,arena.getyOff());
                        stmt.setInt(7,arena.getZ());
                        stmt.setInt(8,arena.getzOff());

                        stmt.execute();
                        connection.commit();

                        stmt = connection.prepareStatement(
                                "INSERT INTO main.arena_blocks (x, y, z, block_data) VALUES(?,?,?,?)"
                        );

                        //debug
                        System.out.println("SAVING BATCH DATA");

                        int count = 0;

                        for (BlockData block : blocks) {
                            count++;
                            stmt.setInt(1, block.x);
                            stmt.setInt(2, block.y);
                            stmt.setInt(3, block.z);
                            stmt.setString(4, block.type);
                            stmt.addBatch();
                            count = getCount(connection, stmt, count);
                        }
                        stmt.executeBatch();
                        connection.commit();
                        count = 0;


                        stmt = connection.prepareStatement("INSERT INTO attached_blocks (x, y, z, data) VALUES (?,?,?,?)");

                        for (TrimData attachedItem : attachedItems) {
                            count++;

                            stmt.setInt(1, attachedItem.x);
                            stmt.setInt(2, attachedItem.y);
                            stmt.setInt(3, attachedItem.z);
                            stmt.setString(4, attachedItem.data);

                            stmt.addBatch();

                            count = getCount(connection, stmt, count);
                        }

                        stmt.executeBatch();
                        connection.commit();

                    } catch (SQLException | IOException e) {
                        e.printStackTrace();
                        return;
                    }

                    //debug
                    System.out.println("CAPTURE IS DONE FINALLY, TOTAL TIME "
                        + ((System.currentTimeMillis() - start)/1000) + "s");
                }
            }.runTaskAsynchronously(killaBeez);
            return true;
        }

        private int getCount(Connection connection, PreparedStatement stmt, int count) throws SQLException {
            if (count == BATCH_SIZE) {
                stmt.executeBatch();
                connection.commit();
                stmt.clearParameters();
                count = 0;
            }
            return count;
        }
    }
     
    Code (Text):

    public class ArenaRebuild {
        private Arena arena;
        private Connection connection;

        public ArenaRebuild(String arenaName) throws SQLException {
            arena = Arena.getArenaFromDb(arenaName);
            connection = KillaBeez.getDbManager().getConnection(arenaName);
        }

        public boolean buildArena(KillaBeez killaBeez, World world, Location location) throws SQLException {
            if (connection == null || arena == null) return false;

            PreparedStatement stmt = connection.prepareStatement("SELECT * FROM main.arena_blocks");
            ResultSet resultSet = stmt.executeQuery();

            ArrayList<BlockData> blockData = new ArrayList<>();
            ArrayList<TrimData> trimData = new ArrayList<>();

            while (resultSet.next()) {
                blockData.add(new BlockData(
                        resultSet.getInt("x"),
                        resultSet.getInt("y"),
                        resultSet.getInt("z"),
                        resultSet.getString("block_data")
                ));
            }

            ArrayList<Block> blocks = new ArrayList<>();

            for (BlockData block : blockData) {
                world.getBlockAt(
                        location.getBlockX() + block.x,
                        location.getBlockY() + block.y,
                        location.getBlockZ() + block.z
                ).setBlockData(killaBeez.getServer().createBlockData(block.type));
            }

            stmt = connection.prepareStatement("SELECT * FROM attached_blocks");
            resultSet = stmt.executeQuery();

            while (resultSet.next()) {
                trimData.add(new TrimData(
                        resultSet.getInt("x"),
                        resultSet.getInt("y"),
                        resultSet.getInt("z"),
                        resultSet.getString("data")
                ));
            }

            for (TrimData data : trimData) {
                YamlConfiguration itemStack = new YamlConfiguration();
                try {
                    itemStack.loadFromString(data.data);
                } catch (InvalidConfigurationException e) {
                    e.printStackTrace();
                }
                world.dropItem(
                        new Location(world,
                                location.getBlockX() + data.x,
                                location.getBlockY() + data.y,
                                location.getBlockZ() + data.z),
                        itemStack.getItemStack("0")
                );
            }

            return true;
        }
    }
     
     
  2. NMS structure API.

    Save a structure of the area and transfer it to an other server. Either by saving an NBT file and copy it to another server or use the simple NBTTagCompound#toString() method to receive it as JSON string, which can be inserted into an SQL DB.

    It simply does everything you need (blocks, drops, entities, tile entities… everything).
    See also: Thread#331243.
     
    • Like Like x 2
  3. thank ya! that is super handy...

    i don't know why it broke my brain trying to break sizes larger than 32 up into segments, but it did x.x

    it works fairly well, though, and will build an array of data objects with points representing each corner for distances smaller than 32 to pretty much anything

    Code (Text):

    public class ArenaBuilder {
        private static final int MAX_LENGTH = 32;
     
        public void capture(Location corner1, Location corner2, boolean keepEntities) {
            World world = ((CraftWorld) corner1.getWorld()).getHandle();

            int x1, x2, y1, y2, z1, z2;

            if (corner1.getBlockX() > corner2.getBlockX()) {
                x2 = corner1.getBlockX();
                x1 = corner2.getBlockX();
            } else {
                x1 = corner1.getBlockX();
                x2 = corner2.getBlockX();
            }
            if (corner1.getBlockY() > corner2.getBlockY()) {
                y2 = corner1.getBlockY();
                y1 = corner2.getBlockY();
            } else {
                y1 = corner1.getBlockY();
                y2 = corner2.getBlockY();
            }
            if (corner1.getBlockZ() > corner2.getBlockZ()) {
                z2 = corner1.getBlockZ();
                z1 = corner2.getBlockZ();
            } else {
                z1 = corner1.getBlockZ();
                z2 = corner2.getBlockZ();
            }

            int[] xSeg = getSegments(x1, x2);
            int[] ySeg = getSegments(y1, y2);
            int[] zSeg = getSegments(z1, z2);

            ArrayList<CornerData> corners = new ArrayList<>();

            //build the first row
            for (int i = 0; i < xSeg.length-1; i++) {
                CornerData data = new CornerData();

                data.x1 = xSeg[i];
                data.x2 = xSeg[i+1];

                data.y1 = ySeg[0];
                data.y2 = ySeg[1];

                data.z1 = zSeg[0];
                data.z2 = zSeg[1];

                corners.add(data);
            }
            //build the rest of the rows from the first row
            ArrayList<CornerData> zRows = new ArrayList<>();
            for (int i = 1; i < zSeg.length-1; i++){
                for (CornerData xCorner : corners) {
                    CornerData data = new CornerData(xCorner);

                    data.z1 = zSeg[i];
                    data.z2 = zSeg[i+1];

                    zRows.add(data);
                }
            }
            corners.addAll(zRows);
            //build the columns
            ArrayList<CornerData> yColumns = new ArrayList<>();
            for (int i = 1; i < ySeg.length-1; i++) {
                for (CornerData row : corners) {
                    CornerData data = new CornerData(row);

                    data.y1 = ySeg[i];
                    data.y2 = ySeg[i+1];

                    yColumns.add(data);
                }
            }
            corners.addAll(yColumns);
         
            //do stuff with the corner data x.x
        }

        private int[] getSegments(int loc1, int loc2) {
            int dis = loc2-loc1;
            int segCount, remainder;

            segCount = (int) Math.floor(dis/MAX_LENGTH) + 1;
            remainder = ((MAX_LENGTH*segCount) - -dis)%MAX_LENGTH;

            int iCount = segCount;
            if (remainder != 0) segCount++;

            int[] s = new int[segCount];

            int nextLocation = loc1;
            for (int i = 0; i < iCount; i++) {
                s[i] = nextLocation;
                if (remainder != 0 && i >= iCount-1) break;
                nextLocation += MAX_LENGTH;
            }

            if (remainder != 0) s[segCount-1] = nextLocation + remainder;

            return s;
        }

        private class CornerData {
            private int x1, y1, z1, x2, y2, z2;
            public CornerData() {}
            public CornerData(CornerData data) {
                x1 = data.x1;
                x2 = data.x2;
                y1 = data.y1;
                y2 = data.y2;
                z1 = data.z1;
                z2 = data.z2;
            }
        }
    }
     
     
    #3 W0lv3s, Aug 18, 2018
    Last edited: Aug 18, 2018

Share This Page