Replace items as they were

Discussion in 'Spigot Plugin Development' started by W0lv3s, Aug 12, 2018 at 7:52 PM.

  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 1

Share This Page