javagame-physicsminecraftbukkit

set Blocks Bukkit performance friendly


I am currently programming a game, which includes a massive amount of blockplacing. When a player moves, a stained glass walkway will be set under the player with some block in front and some behind.

I have already checked for duplicate placement and sufficient movement of the player to actually change a distance of one block.

I've tried using the method sendBlockChange, but it doesn't help performance because the sendBlockChange has to be applied to every online player and anti cheat-Plugins will go crazy.

Below I provided the code for setting blocks during the moveEvent and would love to here suggestions to increase performance.

At the moment, a server needs about 5 GB RAM for 3-5 players.

public class MoveListener implements Listener {

    @EventHandler
    public void onMove(PlayerMoveEvent e) {
        Location l = e.getTo();
        // if player is below 254 and is inside the arena and the moved at least 1 block
        if(e.getFrom().getBlockY()<254 
        && YmlMethods.isInArea(e.getPlayer(),e.getPlayer().getLocation())
        && !(e.getFrom().getBlockX() == e.getTo().getBlockX() 
            && e.getFrom().getBlockY() == e.getTo().getBlockY() 
            && e.getFrom().getBlockZ() == e.getTo().getBlockZ()
        )){
            // if -135 < yaw <= -45 or 215 < yaw <= 305
            if ((l.getYaw() <= -45 && l.getYaw() > -135.0) ||
                (l.getYaw() <= 305 && l.getYaw() > 215)) {
                setEWArea(-1, 1, l, e.getPlayer());
            // if 0 < yaw 45 or -360 < yaw <= -315
            } else if ((l.getYaw() <= -305 
                    || (l.getYaw() > -45 && l.getYaw() <= 0)) 
                || ((l.getYaw() <= 45 && l.getYaw() >= 0) 
                    || l.getYaw() > 305)) {
                setNSArea(1, 1, l, e.getPlayer());
            // if -305 < yaw <= -215 or 45 < yaw <= 135
            } else if ((l.getYaw() <= -215 && l.getYaw() > -305) 
                    || (l.getYaw() <= 135 && l.getYaw() > 45)){
                setEWArea(-1, 1, l, e.getPlayer());
            // if -135 < yaw <= -45 or 215 < yaw <= 305
            } else {
                setNSArea(-1, -1, l, e.getPlayer());
            }
        }
        
    }

    private void setNSArea(int x, int z, Location start, Player p) {
        // 3x3 area around position
        int[] positionsX = {-x, 0, x};
        int[] positionsZ = {-z, 0, z};
        // stained glass colors
        int[] blocks = {14, 4, 9};
        
        // player is looking down, let him fall
        if (p.getLocation().getPitch() > 75) {
            for (int i = 0; i < positionsX.length; i++) {
                for (int pZ : positionsZ) {
                    setAir(positionsX[i], pZ, start, blocks[i]);
                }
            }
        // player is looking up, let him fly
        } else {
            for (int i = 0; i < positionsX.length; i++) {
                for (int pZ : positionsZ) {
                    setBlock(positionsX[i], pZ, start, blocks[i]);
                }
            }
        }
    }

    private void setEWArea(int x, int z, Location start, Player p) {
        // 3x3 area around position
        int[] positionsX = {-x, 0, x};
        int[] positionsZ = {-z, 0, z};
        // stained glass colors
        int[] blocks = {14, 4, 9};
        if (p.getLocation().getPitch() > 75) {
            for (int i = 0; i < positionsZ.length; i++) {
                for (int pX : positionsX) {
                    setAir(positionsZ[i], pX, start, blocks[i]);
                }
            }
        } else {
            for (int i = 0; i < positionsZ.length; i++) {
                for (int pX : positionsX) {
                    setBlock(positionsZ[i], pX, start, blocks[i]);
                }
            }
        }
    }

    private void setBlock( int x, int z, Location start, int data) {
        // get block below player                                     
        Location below = new Location(start.getWorld(), start.getBlockX() + x, start.getBlockY() - 1, start.getBlockZ() + z);
        Material block = below.getBlock().getType();
        // if block is air, set to stained glass
        if (block == Material.AIR) {
            below.getBlock().setType(Material.STAINED_GLASS);
            BlockState bs = below.getBlock().getState();
            bs.setRawData((byte)data);
            bs.update();
            // and schedule removal
            queueBlockRemove(below);
        }
    }

    private void setAir(int x, int z, Location start, int data) {
        // get block below player                                             
        Location below = new Location(start.getWorld(), start.getBlockX() + x, start.getBlockY() - 1, start.getBlockZ() + z);
        // if block is stained glass, set to air
        if (below.getBlock().getType() == Material.STAINED_GLASS) {
            below.getBlock().setType(Material.AIR);
        }

        // get block 3 below player
        Location threeBelow = new Location(start.getWorld(), start.getBlockX() + x, start.getBlockY() - 3, start.getBlockZ() + z);
        Material block = threeBelow.getBlock().getType();
        // if block is air, set to stained glass
        if (block == Material.AIR) {
            threeBelow.getBlock().setType(Material.STAINED_GLASS);
            BlockState bs = threeBelow.getBlock().getState();
            bs.setRawData((byte)data);
            bs.update();
            // and schedule removal
            queueBlockRemove(threeBelow);
        }
    }

    private void queueBlockRemove(Location remove) {        
        Bukkit.getScheduler().scheduleSyncDelayedTask(Nyanfighters.getInstance(), () -> remove.getBlock().setType(Material.AIR), 20 * 5);
    }
}

Notice: There are some code brackets as comments marked. those are all Extensions, which are the target, but already not used to set performance down.


Solution

  • Try implementing a small waiting period between updating the blocks. For example, the first time a player triggers a block update, store an entry in a map, keyed on their UUID and with a value of the current time. Then the next time the event is activated, check that a certain time period has passed between the current time and the last time the event activated. If the specified amount of time hasn't passed, don't do anything. Even if you change it to still update once a second, that will result in a 20x improvement over updating once per tick.