1.13.2 Loading chunks to spawn npcs

Discussion in 'Spigot Plugin Development' started by zManu__, Feb 4, 2020.

  1. Hy guys,
    I'm making a plugin that builds multiple structures and spawns Citizens npcs. The code works properly for the first structure, but when I try to build another schematic further away, the plugin spawns multiple npcs. I thought that the problem is that the chunk isn't loaded, so I tried to load it with forceLoad method but nothing, then I tried teleporting a player to that location but nothing again.
    This is my code:
    Code (Java):
    public void spawnCell(Gang gang, int level) {
            final File file = new File(main.getDataFolder() + "/schematics/" + main.getConfig().getString("cell-schematic-name") + level + ".schem");

            try {
                Schematic clipboard = Objects.requireNonNull(ClipboardFormats.findByFile(file)).load(file);
                clipboard.paste(new BukkitWorld(gang.getMiddleBlock().getWorld()), BlockVector3.at(gang.getMiddleBlock().getX(), gang.getMiddleBlock().getY(), gang.getMiddleBlock().getZ()), false, true, null);
                Block firstSpawnBlock = gang.getMiddleBlock().getBlock().getRelative(-Objects.requireNonNull(clipboard.getClipboard()).getDimensions().getX()/2, -4, -clipboard.getClipboard().getDimensions().getZ()/2);

                Block maxBlock = null;
                Block minBlock = null;

                for (int length = 0; length<=clipboard.getClipboard().getRegion().getLength()*2; length++){
                    for (int width = 0; width<=clipboard.getClipboard().getRegion().getWidth()*2; width++){
                        for (int height = 0; height<=clipboard.getClipboard().getRegion().getHeight()*2; height++) {
                            if (firstSpawnBlock.getRelative(width, height, length).getType() != Material.AIR) {
                                if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.BARRIER){
                                    if (minBlock == null){
                                        firstSpawnBlock.getRelative(width, height, length).setType(CellProperties.getCellFromLevel(gang.getLevel()).getMin());
                                        minBlock = firstSpawnBlock.getRelative(width, height, length);
                                    } else {
                                        firstSpawnBlock.getRelative(width, height, length).setType(CellProperties.getCellFromLevel(gang.getLevel()).getMax());
                                        maxBlock = firstSpawnBlock.getRelative(width, height, length);
                                    }
                                }
                                if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.DIAMOND_BLOCK) {
                                    gang.setSpawnBlock(firstSpawnBlock.getRelative(width, height + 1, length).getLocation().add(0.5, 0, 0.5));
                                }
                                if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.IRON_BLOCK){
                                    if (firstSpawnBlock.getRelative(width, height, length).getY() < 120) {
                                        gang.setCoreBlock(firstSpawnBlock.getRelative(width, height, length).getLocation());
                                        spawnHologram(gang, firstSpawnBlock.getRelative(width, height, length));
                                    }
                                }
                                if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.TNT){
                                    spawnNpc(firstSpawnBlock.getRelative(width, height, length), level);
                                }
                            }
                        }
                    }
                }

                StringBuilder locationsProjections = new StringBuilder();

                assert minBlock != null;
                assert maxBlock != null;
                for (int x = minBlock.getX(); x <= maxBlock.getX(); x++){
                    for (int y = minBlock.getY(); y <= maxBlock.getY(); y++){
                        for (int z = minBlock.getZ()-1; z >= minBlock.getZ()-4; z--){
                            if (minBlock.getWorld().getBlockAt(x, y, z).getType() != Material.AIR){
                                locationsProjections.append(x).append(",").append(y).append(",").append(z).append(";");
                            }
                        }
                    }
                }

                main.getCellsFile().addCell(gang.getName(), minBlock, maxBlock, locationsProjections.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    private void spawnNpc(Block tntBlock, int level){
            NPC npc = CitizensAPI.getNPCRegistry().createNPC(EntityType.VILLAGER, UUID.randomUUID(), new Random().nextInt(1000000), Utils.replaceColors(Objects.requireNonNull(main.getConfig().getString("npc.name"))));
            npc.setFlyable(true);
            npc.setProtected(true);
            tntBlock.setType(Material.AIR);
            npc.spawn(new Location(tntBlock.getWorld(), tntBlock.getX()+0.5, tntBlock.getY(), tntBlock.getZ()+0.5, CellProperties.getCellFromLevel(level).getYaw(), 0));
        }
    Is there a way to load a chunk? If that's the problem.

    Thanks in advance
     
  2. Are you saying you teleported a player to the location before running this code? If so, then I don't see how chunks would be an issue - the server would've loaded those chunks just by having a player be there.

    Does the second structure load properly, and could you explain what the expected result is versus the actual? By "multiple npcs" do you mean multiple of the same NPC in different spots or multiple different NPCs in the same spot?
     
  3. Yep, I tried to teleport a player before the spawnCell call. By multiple npcs I mean that there are multiple same npc in the same location and sometimes in different locations as well with a distance of 1 block.
     
  4. Then chances are #spawnCell is being called more often than you would like. Since you're getting a block relative to the spawn block, I would think they probably overlap a few times during your loop.

    One solution would be, instead of where you call #spawnCell inside the loop, save the #getRelative block location to a Set. Since a Set can't contain duplicates, previously checked locations get discarded. Then, outside your loop, have a second loop that just iterates through the set and spawns the NPCs. To wit:
    Code (Java):
    // untested
    public void spawnCell(Gang gang, int level) {
        final File file = new File(main.getDataFolder() + "/schematics/" + main.getConfig().getString("cell-schematic-name") + level + ".schem");
        Set<Location> tntLocations = new HashSet<Location>();

        try {
            Schematic clipboard = Objects.requireNonNull(ClipboardFormats.findByFile(file)).load(file);
            clipboard.paste(new BukkitWorld(gang.getMiddleBlock().getWorld()), BlockVector3.at(gang.getMiddleBlock().getX(), gang.getMiddleBlock().getY(), gang.getMiddleBlock().getZ()), false, true, null);
            Block firstSpawnBlock = gang.getMiddleBlock().getBlock().getRelative(-Objects.requireNonNull(clipboard.getClipboard()).getDimensions().getX()/2, -4, -clipboard.getClipboard().getDimensions().getZ()/2);

            Block maxBlock = null;
            Block minBlock = null;

            for (int length = 0; length<=clipboard.getClipboard().getRegion().getLength()*2; length++){
                for (int width = 0; width<=clipboard.getClipboard().getRegion().getWidth()*2; width++){
                    for (int height = 0; height<=clipboard.getClipboard().getRegion().getHeight()*2; height++) {
                        if (firstSpawnBlock.getRelative(width, height, length).getType() != Material.AIR) {
                            if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.BARRIER){
                                if (minBlock == null){
                                    firstSpawnBlock.getRelative(width, height, length).setType(CellProperties.getCellFromLevel(gang.getLevel()).getMin());
                                    minBlock = firstSpawnBlock.getRelative(width, height, length);
                                } else {
                                    firstSpawnBlock.getRelative(width, height, length).setType(CellProperties.getCellFromLevel(gang.getLevel()).getMax());
                                    maxBlock = firstSpawnBlock.getRelative(width, height, length);
                                }
                            }
                            if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.DIAMOND_BLOCK) {
                                gang.setSpawnBlock(firstSpawnBlock.getRelative(width, height + 1, length).getLocation().add(0.5, 0, 0.5));
                                }
                            if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.IRON_BLOCK){
                                if (firstSpawnBlock.getRelative(width, height, length).getY() < 120) {
                                    gang.setCoreBlock(firstSpawnBlock.getRelative(width, height, length).getLocation());
                                    spawnHologram(gang, firstSpawnBlock.getRelative(width, height, length));
                                }
                            }
                               
                            if (firstSpawnBlock.getRelative(width, height, length).getType() == Material.TNT){
                                tntLocations.add(firstSpawnBlock.getRelative(width, height, length).getLocation())
                            }
                        }
                    }
                }
            }

            for (Location loc : tntLocations) {
                spawnNpc(loc, level);
            }

            StringBuilder locationsProjections = new StringBuilder();

            assert minBlock != null;
            assert maxBlock != null;
            for (int x = minBlock.getX(); x <= maxBlock.getX(); x++){
                for (int y = minBlock.getY(); y <= maxBlock.getY(); y++){
                    for (int z = minBlock.getZ()-1; z >= minBlock.getZ()-4; z--){
                        if (minBlock.getWorld().getBlockAt(x, y, z).getType() != Material.AIR){
                            locationsProjections.append(x).append(",").append(y).append(",").append(z).append(";");
                        }
                    }
                }
            }

            main.getCellsFile().addCell(gang.getName(), minBlock, maxBlock, locationsProjections.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void spawnNpc(Location tntLocation, int level){
        NPC npc = CitizensAPI.getNPCRegistry().createNPC(EntityType.VILLAGER, UUID.randomUUID(), new Random().nextInt(1000000), Utils.replaceColors(Objects.requireNonNull(main.getConfig().getString("npc.name"))));
        npc.setFlyable(true);
        npc.setProtected(true);
        tntLocation.getBlock().setType(Material.AIR);
        npc.spawn(new Location(tntLocation.getWorld(), tntLocation.getX()+0.5, tntLocation.getY(), tntLocation.getZ()+0.5, CellProperties.getCellFromLevel(level).getYaw(), 0));
    }
     

  5. I'm pretty sure that the spawnNpc method is called one time.

    However the bug doesn't occours in spawn chunks.
     
  6. Is there a reason you didn’t mention this in your original post? Do you mean to say the first paste works as well as additional ones in spawn chunks?

    I encourage you to at least give my code a try.