diff --git a/src/main/java/me/goodandevil/skyblock/levelling/Chunk.java b/src/main/java/me/goodandevil/skyblock/levelling/Chunk.java index d8be70d3..4de54f8c 100644 --- a/src/main/java/me/goodandevil/skyblock/levelling/Chunk.java +++ b/src/main/java/me/goodandevil/skyblock/levelling/Chunk.java @@ -1,84 +1,169 @@ package me.goodandevil.skyblock.levelling; import java.io.File; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.ChunkSnapshot; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.scheduler.BukkitRunnable; import me.goodandevil.skyblock.SkyBlock; import me.goodandevil.skyblock.island.Island; import me.goodandevil.skyblock.island.IslandEnvironment; import me.goodandevil.skyblock.island.IslandWorld; +import org.bukkit.entity.Player; public class Chunk { + private static final int MAX_CHUNKS = 150; private final SkyBlock skyblock; private Island island; - private List chunkSnapshots = new ArrayList<>(); - private boolean complete; + private int initialNumberOfChunks = -1; + private Set chunkPositions = new HashSet<>(); + private Set chunkSnapshots = new HashSet<>(); + private boolean isReady = false; + private boolean isFinished = false; public Chunk(SkyBlock skyblock, Island island) { this.skyblock = skyblock; this.island = island; - - complete = false; } - public void prepare() { - new BukkitRunnable() { - @Override - public void run() { - prepareChunkSnapshots(); - } - }.runTask(skyblock); - } + public void prepareInitial() { + Bukkit.getScheduler().runTask(this.skyblock, () -> { + FileConfiguration config = this.skyblock.getFileManager().getConfig(new File(this.skyblock.getDataFolder(), "config.yml")).getFileConfiguration(); + FileConfiguration islandData = this.skyblock.getFileManager().getConfig(new File(new File(this.skyblock.getDataFolder().toString() + "/island-data"), this.island.getOwnerUUID().toString() + ".yml")).getFileConfiguration(); - public List getChunkSnapshots() { - return chunkSnapshots; - } + boolean hasNether = config.getBoolean("Island.World.Nether.Enable") && islandData.getBoolean("Unlocked.Nether", false); + boolean hasEnd = config.getBoolean("Island.World.End.Enable") && islandData.getBoolean("Unlocked.End", false); - public boolean isComplete() { - return complete; - } - - private void prepareChunkSnapshots() { - FileConfiguration config = skyblock.getFileManager().getConfig(new File(skyblock.getDataFolder(), "config.yml")).getFileConfiguration(); - FileConfiguration islandData = skyblock.getFileManager().getConfig(new File(new File(skyblock.getDataFolder().toString() + "/island-data"), island.getOwnerUUID().toString() + ".yml")).getFileConfiguration(); - boolean hasNormal = true; - boolean hasNether = config.getBoolean("Island.World.Nether.Enable") && islandData.getBoolean("Unlocked.Nether", false); - boolean hasEnd = config.getBoolean("Island.World.End.Enable") && islandData.getBoolean("Unlocked.End", false); - - for (IslandWorld iWorld : IslandWorld.values()) { - if ((iWorld == IslandWorld.Normal && hasNormal) || (iWorld == IslandWorld.Nether && hasNether) || (iWorld == IslandWorld.End && hasEnd)) { - Location islandLocation = island.getLocation(iWorld, IslandEnvironment.Island); - - Location minLocation = new Location(islandLocation.getWorld(), - islandLocation.getBlockX() - island.getRadius(), 0, - islandLocation.getBlockZ() - island.getRadius()); - Location maxLocation = new Location(islandLocation.getWorld(), - islandLocation.getBlockX() + island.getRadius(), islandLocation.getWorld().getMaxHeight(), - islandLocation.getBlockZ() + island.getRadius()); - - int MinX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()); - int MinZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ()); - - int MaxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX()); - int MaxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ()); - - for (int x = MinX - 16; x <= MaxX + 16; x += 16) { - for (int z = MinZ - 16; z <= MaxZ + 16; z += 16) { - org.bukkit.Chunk chunk = islandLocation.getWorld().getBlockAt(x, 0, z).getChunk(); - chunkSnapshots.add(chunk.getChunkSnapshot()); - } + for (IslandWorld islandWorld : IslandWorld.values()) { + if (islandWorld == IslandWorld.Normal || (islandWorld == IslandWorld.Nether && hasNether) || (islandWorld == IslandWorld.End && hasEnd)) { + this.getChunksToScan(islandWorld); } } + + this.initialNumberOfChunks = this.chunkPositions.size(); + + this.prepareNextChunkSnapshots(); + }); + } + + public boolean isReadyToScan() { + return this.isReady; + } + + public Set getAvailableChunkSnapshots() { + this.isReady = false; + return this.chunkSnapshots; + } + + public boolean isFinished() { + return this.isFinished; + } + + public void prepareNextChunkSnapshots() { + Bukkit.getScheduler().runTask(this.skyblock, () -> { + this.chunkSnapshots.clear(); + + Iterator it = this.chunkPositions.iterator(); + if (!it.hasNext()) { + this.isReady = true; + this.isFinished = true; + this.sendFinishedMessage(); + return; + } + + int percentComplete = (int)((1 - ((double)this.chunkPositions.size() / this.initialNumberOfChunks)) * 100); + this.sendPercentMessage(percentComplete); + + while (it.hasNext() && this.chunkSnapshots.size() < MAX_CHUNKS) { + ChunkPosition chunkPosition = it.next(); + World world = chunkPosition.getWorld(); + int x = chunkPosition.getX(); + int z = chunkPosition.getZ(); + if (!world.isChunkLoaded(x, z)) { + world.loadChunk(x, z); + this.chunkSnapshots.add(world.getChunkAt(x, z).getChunkSnapshot()); + world.unloadChunk(x, z); + } else { + this.chunkSnapshots.add(world.getChunkAt(x, z).getChunkSnapshot()); + } + it.remove(); + } + + this.isReady = true; + }); + } + + private void sendPercentMessage(int percent) { + String message = ChatColor.translateAlternateColorCodes('&', + this.skyblock.getFileManager() + .getConfig(new File(this.skyblock.getDataFolder(), "language.yml")) + .getFileConfiguration().getString("Command.Island.Level.Scanning.Progress.Message") + .replace("%percent", String.valueOf(percent))); + for (Player player : this.skyblock.getIslandManager().getPlayersAtIsland(this.island)) { + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); + } + } + + private void sendFinishedMessage() { + String message = ChatColor.translateAlternateColorCodes('&', this.skyblock.getFileManager() + .getConfig(new File(this.skyblock.getDataFolder(), "language.yml")) + .getFileConfiguration().getString("Command.Island.Level.Scanning.Finished.Message")); + for (Player player : this.skyblock.getIslandManager().getPlayersAtIsland(this.island)) { + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); + } + } + + private void getChunksToScan(IslandWorld islandWorld) { + Location islandLocation = this.island.getLocation(islandWorld, IslandEnvironment.Island); + World world = islandLocation.getWorld(); + + Location minLocation = new Location(world, islandLocation.getBlockX() - this.island.getRadius(), 0, islandLocation.getBlockZ() - this.island.getRadius()); + Location maxLocation = new Location(world, islandLocation.getBlockX() + this.island.getRadius(), world.getMaxHeight(), islandLocation.getBlockZ() + this.island.getRadius()); + + int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()); + int minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ()); + + int maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX()); + int maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ()); + + for (int x = minX; x < maxX + 16; x += 16) { + for (int z = minZ; z < maxZ + 16; z += 16) { + this.chunkPositions.add(new ChunkPosition(world, x >> 4, z >> 4)); + } + } + } + + private class ChunkPosition { + private World world; + private int x, z; + + public ChunkPosition(World world, int x, int z) { + this.world = world; + this.x = x; + this.z = z; } - complete = true; + public World getWorld() { + return this.world; + } + + public int getX() { + return this.x; + } + + public int getZ() { + return this.z; + } } } diff --git a/src/main/java/me/goodandevil/skyblock/levelling/LevellingManager.java b/src/main/java/me/goodandevil/skyblock/levelling/LevellingManager.java index b9b739a0..9f901255 100644 --- a/src/main/java/me/goodandevil/skyblock/levelling/LevellingManager.java +++ b/src/main/java/me/goodandevil/skyblock/levelling/LevellingManager.java @@ -42,37 +42,44 @@ public class LevellingManager { StackableManager stackableManager = skyblock.getStackableManager(); Chunk chunk = new Chunk(skyblock, island); - chunk.prepare(); + chunk.prepareInitial(); int NMSVersion = NMSUtil.getVersionNumber(); + int height = 0; + + for (IslandWorld worldList : IslandWorld.values()) { + org.bukkit.World world = worldManager.getWorld(worldList); + + if (height == 0 || height > world.getMaxHeight()) { + height = world.getMaxHeight(); + } + } + + int worldMaxHeight = height; + + boolean isEpicSpawnersEnabled = Bukkit.getPluginManager().isPluginEnabled("EpicSpawners"); + boolean isWildStackerEnabled = Bukkit.getPluginManager().isPluginEnabled("WildStacker"); + + Map levellingData = new HashMap<>(); + new BukkitRunnable() { @Override public void run() { - if (!chunk.isComplete()) return; - cancel(); + if (!chunk.isReadyToScan()) return; + + if (chunk.isFinished()) { + finalizeMaterials(levellingData, player, island); + cancel(); + return; + } Method getBlockTypeMethod = null; Method getBlockTypeIdMethod = null; Method getBlockTypeDataMethod = null; Method getMaterialMethod = null; - int worldMaxHeight = 0; - - for (IslandWorld worldList : IslandWorld.values()) { - org.bukkit.World world = worldManager.getWorld(worldList); - - if (worldMaxHeight == 0 || worldMaxHeight > world.getMaxHeight()) { - worldMaxHeight = world.getMaxHeight(); - } - } - - boolean isEpicSpawnersEnabled = Bukkit.getPluginManager().isPluginEnabled("EpicSpawners"); - boolean isWildStackerEnabled = Bukkit.getPluginManager().isPluginEnabled("WildStacker"); - - Map levellingData = new HashMap<>(); - - for (ChunkSnapshot chunkSnapshotList : chunk.getChunkSnapshots()) { + for (ChunkSnapshot chunkSnapshotList : chunk.getAvailableChunkSnapshots()) { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < worldMaxHeight; y++) { @@ -175,38 +182,42 @@ public class LevellingManager { } } - Map materials = new HashMap<>(); - for (LevellingData data : levellingData.keySet()) { - long amount = levellingData.get(data); - if (data.getMaterials() != null) { - materials.put(data.getMaterials().name(), amount); - } - } - - if (materials.size() == 0) { - if (player != null) { - skyblock.getMessageManager().sendMessage(player, skyblock.getFileManager() - .getConfig(new File(skyblock.getDataFolder(), "language.yml")) - .getFileConfiguration().getString("Command.Island.Level.Materials.Message")); - skyblock.getSoundManager().playSound(player, Sounds.VILLAGER_NO.bukkitSound(), 1.0F, 1.0F); - } - } else { - IslandLevel level = island.getLevel(); - level.setLastCalculatedPoints(level.getPoints()); - level.setLastCalculatedLevel(level.getLevel()); - level.setMaterials(materials); - - Bukkit.getServer().getPluginManager().callEvent( - new IslandLevelChangeEvent(island.getAPIWrapper(), island.getAPIWrapper().getLevel())); - - if (player != null) { - me.goodandevil.skyblock.menus.Levelling.getInstance().open(player); - } - } + chunk.prepareNextChunkSnapshots(); } }.runTaskTimerAsynchronously(skyblock, 0L, 1L); } + private void finalizeMaterials(Map levellingData, Player player, Island island) { + Map materials = new HashMap<>(); + for (LevellingData data : levellingData.keySet()) { + long amount = levellingData.get(data); + if (data.getMaterials() != null) { + materials.put(data.getMaterials().name(), amount); + } + } + + if (materials.size() == 0) { + if (player != null) { + skyblock.getMessageManager().sendMessage(player, skyblock.getFileManager() + .getConfig(new File(skyblock.getDataFolder(), "language.yml")) + .getFileConfiguration().getString("Command.Island.Level.Materials.Message")); + skyblock.getSoundManager().playSound(player, Sounds.VILLAGER_NO.bukkitSound(), 1.0F, 1.0F); + } + } else { + IslandLevel level = island.getLevel(); + level.setLastCalculatedPoints(level.getPoints()); + level.setLastCalculatedLevel(level.getLevel()); + level.setMaterials(materials); + + Bukkit.getServer().getPluginManager().callEvent( + new IslandLevelChangeEvent(island.getAPIWrapper(), island.getAPIWrapper().getLevel())); + + if (player != null) { + me.goodandevil.skyblock.menus.Levelling.getInstance().open(player); + } + } + } + public void registerMaterials() { Config config = skyblock.getFileManager().getConfig(new File(skyblock.getDataFolder(), "levelling.yml")); FileConfiguration configLoad = config.getFileConfiguration(); diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 8d2a3d07..f980ffa8 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -458,6 +458,11 @@ Command: Message: "&bSkyBlock &8| &aInfo&8: &7&oProcessing Island level request... Please wait!" Loading: Message: "&bSkyBlock &8| &aInfo&8: &7&oLoading Island Materials... Please wait!" + Scanning: + Progress: + Message: "&eScanning island level: &b%percent%" + Finished: + Message: "&aIsland level scan complete!" Owner: Yourself: Message: "&bSkyBlock &8| &cError&8: &eYou are not an Island owner."