Optimized island scan

This commit is contained in:
Fabrizio La Rosa 2020-08-04 18:32:56 +02:00
parent 77cfd8a656
commit ccbe8de7e0
6 changed files with 159 additions and 64 deletions

View File

@ -2,10 +2,14 @@ package com.songoda.skyblock.blockscanner;
import com.google.common.collect.Lists;
import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.core.compatibility.ServerVersion;
import com.songoda.skyblock.SkyBlock;
import com.songoda.skyblock.utils.version.NMSUtil;
import com.songoda.skyblock.island.Island;
import com.songoda.skyblock.island.IslandEnvironment;
import com.songoda.skyblock.world.WorldManager;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
@ -19,6 +23,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
public final class BlockScanner extends BukkitRunnable {
@ -29,9 +34,7 @@ public final class BlockScanner extends BukkitRunnable {
try {
temp = ChunkSnapshot.class.getMethod("getBlockTypeId", int.class, int.class, int.class);
} catch (NoSuchMethodException ignored) {
}
} catch (NoSuchMethodException ignored) {}
ID_FIELD = temp;
}
@ -49,22 +52,30 @@ public final class BlockScanner extends BukkitRunnable {
return id;
}
private final static int VERSION = NMSUtil.getVersionNumber();
private int completedNum;
private final AtomicInteger completedNum;
private final int threadCount;
private final Queue<BlockInfo> blocks;
private final ScannerTasks tasks;
private final Island island;
private final boolean ignoreLiquids;
private final boolean ignoreAir;
private BlockScanner(Map<World, List<ChunkSnapshot>> snapshots, boolean ignoreLiquids, boolean ignoreLiquidsY, boolean ignoreAir, boolean ignoreY, ScannerTasks tasks) {
private BlockScanner(Map<World, List<ChunkSnapshot>> snapshots,
Island island,
boolean ignoreLiquids,
boolean ignoreLiquidsY,
boolean ignoreAir,
boolean ignoreY,
ScannerTasks tasks) {
this.ignoreLiquids = ignoreLiquids;
this.ignoreAir = ignoreAir;
this.blocks = new ConcurrentLinkedQueue<>();
this.tasks = tasks;
this.completedNum = new AtomicInteger();
this.island = island;
FileConfiguration config = SkyBlock.getInstance().getFileManager().getConfig(new File(SkyBlock.getInstance().getDataFolder(), "config.yml")).getFileConfiguration();
@ -73,7 +84,7 @@ public final class BlockScanner extends BukkitRunnable {
for (Entry<World, List<ChunkSnapshot>> entry : snapshots.entrySet()) {
final List<List<ChunkSnapshot>> parts = Lists.partition(entry.getValue(), 16);
threadCount += parts.size();
World world = entry.getKey();
@ -109,21 +120,62 @@ public final class BlockScanner extends BukkitRunnable {
}
private void queueWork(World world, int scanY, List<ChunkSnapshot> subList) {
WorldManager worldManager = SkyBlock.getInstance().getWorldManager();
Bukkit.getServer().getScheduler().runTaskAsynchronously(SkyBlock.getInstance(), () -> {
LocationBounds bounds = null;
if(island != null) {
Location islandLocation = island.getLocation(worldManager.getIslandWorld(world), IslandEnvironment.Island);
Location minLocation = new Location(world, islandLocation.getBlockX() - island.getRadius(), 0, islandLocation.getBlockZ() - island.getRadius());
Location maxLocation = new Location(world, islandLocation.getBlockX() + island.getRadius(), world.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());
bounds = new LocationBounds(minX, minZ, maxX, maxZ);
}
for (ChunkSnapshot shot : subList) {
final int cX = shot.getX() << 4;
final int cZ = shot.getZ() << 4;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = scanY; y < shot.getHighestBlockYAt(x, z); y++) {
final CompatibleMaterial type = CompatibleMaterial.getBlockMaterial(VERSION > 12
? shot.getBlockType(x, y, z) : MaterialIDHelper.getLegacyMaterial(getBlockTypeID(shot, x, y, z)));
int initX = 0;
int initZ = 0;
int lastX = 15;
int lastZ = 15;
if(bounds != null) {
initX = Math.max(cX, bounds.getMinX())&0x000F;
initZ = Math.max(cZ, bounds.getMinZ())&0x000F;
lastX = Math.min(cX | 15, bounds.getMaxX()-1)&0x000F;
lastZ = Math.min(cZ | 15, bounds.getMaxZ()-1)&0x000F;
}
int finalInitX = initX;
int finalInitZ = initZ;
int finalLastZ = lastZ;
int finalLastX = lastX;
Bukkit.getScheduler().runTask(SkyBlock.getInstance(), () -> {
world.getChunkAt(shot.getX(), shot.getZ()).getBlock(finalInitX, 80, finalInitZ).setType(CompatibleMaterial.GOLD_BLOCK.getBlockMaterial());
world.getChunkAt(shot.getX(), shot.getZ()).getBlock(finalLastX, 80, finalLastZ).setType(CompatibleMaterial.CRYING_OBSIDIAN.getBlockMaterial());
Bukkit.getScheduler().runTaskLater(SkyBlock.getInstance(), () -> {
world.getChunkAt(shot.getX(), shot.getZ()).getBlock(finalInitX, 80, finalInitZ).setType(CompatibleMaterial.AIR.getBlockMaterial());
world.getChunkAt(shot.getX(), shot.getZ()).getBlock(finalLastX, 80, finalLastZ).setType(CompatibleMaterial.AIR.getBlockMaterial());
}, 300L);
});
for (int x = initX; x <= lastX; x++) {
for (int z = initZ; z <= lastZ; z++) {
for (int y = scanY; y < world.getMaxHeight(); y++) {
final CompatibleMaterial type = CompatibleMaterial.getBlockMaterial(
ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)
? shot.getBlockType(x, y, z) :
MaterialIDHelper.getLegacyMaterial(getBlockTypeID(shot, x, y, z)));
if(type == null){
continue;
} else if(type.equals(CompatibleMaterial.AIR) && ignoreAir){
@ -142,12 +194,11 @@ public final class BlockScanner extends BukkitRunnable {
}
private synchronized int increment() {
completedNum += 1;
return completedNum;
return completedNum.getAndIncrement();
}
private synchronized int get() {
return completedNum;
return completedNum.get();
}
@Override
@ -158,12 +209,12 @@ public final class BlockScanner extends BukkitRunnable {
cancel();
}
public static void startScanner(Map<World, List<ChunkSnapshot>> snapshots, boolean ignoreLiquids, boolean ignoreLiquidsY, boolean ignoreAir, boolean ignoreY, ScannerTasks tasks) {
public static void startScanner(Map<World, List<ChunkSnapshot>> snapshots, Island island, boolean ignoreLiquids, boolean ignoreLiquidsY, boolean ignoreAir, boolean ignoreY, ScannerTasks tasks) {
if (snapshots == null) throw new IllegalArgumentException("snapshots cannot be null");
if (tasks == null) throw new IllegalArgumentException("tasks cannot be null");
final BlockScanner scanner = new BlockScanner(snapshots, ignoreLiquids, ignoreLiquidsY, ignoreAir, ignoreY, tasks);
final BlockScanner scanner = new BlockScanner(snapshots, island, ignoreLiquids, ignoreLiquidsY, ignoreAir, ignoreY, tasks);
scanner.runTaskTimer(SkyBlock.getInstance(), 5, 5);
}

View File

@ -20,19 +20,24 @@ public class ChunkLoader extends BukkitRunnable {
private ChunkScannerTask generalTask;
private ChunkForChunkScannerTask chunkTask;
private boolean chunkForChunk;
private boolean paper;
private final boolean chunkForChunk;
private final boolean paper;
private World world;
private Island island;
private final Island island;
private int x;
private int z;
private int minZ;
private int maxX;
private int maxZ;
private int chunkPerTick;
private CompleteTask completeTask;
private final int chunkPerTick;
private final CompleteTask completeTask;
private ChunkLoader(Island island, IslandWorld islandWorld, boolean paper, boolean chunkForChunk, ChunkForChunkScannerTask chunkTask, CompleteTask complete) {
private ChunkLoader(Island island,
IslandWorld islandWorld,
boolean paper,
boolean chunkForChunk,
ChunkForChunkScannerTask chunkTask,
CompleteTask complete) {
chunkPerTick = SkyBlock.getInstance().getFileManager()
.getConfig(new File(SkyBlock.getInstance().getDataFolder(), "config.yml"))
.getFileConfiguration().getInt("Island.Performance.ChunkPerTick", 25);
@ -51,11 +56,11 @@ public class ChunkLoader extends BukkitRunnable {
Location minLocation = new Location(world, islandLocation.getBlockX() - island.getRadius(), 0, islandLocation.getBlockZ() - island.getRadius());
Location maxLocation = new Location(world, islandLocation.getBlockX() + island.getRadius(), world.getMaxHeight(), islandLocation.getBlockZ() + island.getRadius());
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX());
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ());
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4 ;
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ()) >> 4 << 4;
maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX());
maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ());
maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4 | 15;
maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ()) >> 4 << 4 | 15;
x = minX;
z = minZ;
@ -67,7 +72,12 @@ public class ChunkLoader extends BukkitRunnable {
}
}
private ChunkLoader(Island island, IslandWorld islandWorld, boolean paper, boolean chunkForChunk, ChunkScannerTask generalTask, CompleteTask complete) {
private ChunkLoader(Island island,
IslandWorld islandWorld,
boolean paper,
boolean chunkForChunk,
ChunkScannerTask generalTask,
CompleteTask complete) {
chunkPerTick = SkyBlock.getInstance().getFileManager()
.getConfig(new File(SkyBlock.getInstance().getDataFolder(), "config.yml"))
.getFileConfiguration().getInt("Island.Performance.ChunkPerTick", 25);
@ -83,18 +93,26 @@ public class ChunkLoader extends BukkitRunnable {
world = islandLocation.getWorld();
Location minLocation = new Location(world, islandLocation.getBlockX() - island.getRadius(), 0, islandLocation.getBlockZ() - island.getRadius());
Location maxLocation = new Location(world, islandLocation.getBlockX() + island.getRadius(), world.getMaxHeight(), islandLocation.getBlockZ() + island.getRadius());
Location minLocation = new Location(
world,
islandLocation.getBlockX() - island.getRadius(),
0,
islandLocation.getBlockZ() - island.getRadius());
Location maxLocation = new Location(
world,
islandLocation.getBlockX() + island.getRadius(),
world.getMaxHeight(),
islandLocation.getBlockZ() + island.getRadius());
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4;
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ()) >> 4 << 4;
int minX = Math.min(maxLocation.getBlockX(), minLocation.getBlockX());
minZ = Math.min(maxLocation.getBlockZ(), minLocation.getBlockZ());
maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX());
maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ());
maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX()) >> 4 << 4 | 15;
maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ()) >> 4 << 4 | 15;
x = minX;
z = minZ;
if(paper){
this.runTaskAsynchronously(SkyBlock.getInstance());
} else {
@ -105,8 +123,8 @@ public class ChunkLoader extends BukkitRunnable {
@Override
public void run() { // TODO New algorithm that start from the center of the island
for(int i = 0; i < chunkPerTick || paper; i++){
if(x < maxX){
if(z < maxZ){
if(x <= maxX){
if(z <= maxZ){
if(!chunkForChunk){
positions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
} else {

View File

@ -0,0 +1,28 @@
package com.songoda.skyblock.blockscanner;
public class LocationBounds {
private final int minX, minZ, maxX, maxZ;
public LocationBounds(int minX, int minZ, int maxX, int maxZ) {
this.minX = minX;
this.minZ = minZ;
this.maxX = maxX;
this.maxZ = maxZ;
}
public int getMinX() {
return minX;
}
public int getMinZ() {
return minZ;
}
public int getMaxX() {
return maxX;
}
public int getMaxZ() {
return maxZ;
}
}

View File

@ -1,6 +1,6 @@
package com.songoda.skyblock.blockscanner;
import com.songoda.skyblock.utils.version.NMSUtil;
import com.songoda.core.compatibility.ServerVersion;
import org.bukkit.Material;
import java.util.HashMap;
@ -9,18 +9,14 @@ import java.util.Map;
@SuppressWarnings("deprecation")
public final class MaterialIDHelper {
private final static int VERSION = NMSUtil.getVersionNumber();
private MaterialIDHelper() {
}
private MaterialIDHelper() {}
private final static Map<Integer, Material> MATERIALS;
static {
MATERIALS = new HashMap<>();
if (VERSION > 12) {
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
for (Material type : Material.values()) {
if (type.isLegacy()) MATERIALS.put(type.getId(), type);
}

View File

@ -25,7 +25,7 @@ public class ChunkDeleteSplitter extends BukkitRunnable {
}
private void start() {
BlockScanner.startScanner(snapshots, false, true, true, false, (blocks) -> {
BlockScanner.startScanner(snapshots, null,false, true, true, false, (blocks) -> {
this.blocks = blocks;
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});

View File

@ -12,7 +12,10 @@ import com.songoda.skyblock.island.IslandWorld;
import com.songoda.skyblock.levelling.amount.AmountMaterialPair;
import com.songoda.skyblock.levelling.amount.BlockAmount;
import com.songoda.skyblock.message.MessageManager;
import org.bukkit.*;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
@ -22,8 +25,6 @@ import java.io.File;
import java.text.NumberFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public final class IslandScan extends BukkitRunnable {
@ -81,28 +82,25 @@ public final class IslandScan extends BukkitRunnable {
populate(snapshots, IslandWorld.Nether, plugin.isPaperAsync(), () -> {
if (hasEnd) {
populate(snapshots, IslandWorld.End, plugin.isPaperAsync(), () -> {
BlockScanner.startScanner(snapshots, true, true, true, false, (blocks) -> {
BlockScanner.startScanner(snapshots, island, true, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
});
} else {
BlockScanner.startScanner(snapshots, true, true, true, false, (blocks) -> {
BlockScanner.startScanner(snapshots, island, true, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
}
});
} else {
BlockScanner.startScanner(snapshots, true, true, true, false, (blocks) -> {
BlockScanner.startScanner(snapshots, island, true, true, true, false, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
}
});
@ -169,8 +167,12 @@ public final class IslandScan extends BukkitRunnable {
Bukkit.getScheduler().runTask(plugin, () -> {
if (language.getBoolean("Command.Island.Level.Scanning.Progress.Should-Display-Message") && executions == 1 || totalScanned == blocksSize || executions % runEveryX == 0) {
final double percent = ((double) totalScanned / (double) blocksSize) * 100;
double percent = ((double) totalScanned / (double) blocksSize) * 100;
if(Double.isNaN(percent)) {
percent = 0d;
}
String message = language.getString("Command.Island.Level.Scanning.Progress.Message");
message = message.replace("%current_scanned_blocks%", String.valueOf(totalScanned));
message = message.replace("%max_blocks%", String.valueOf(blocksSize));