Fix for issues scanning or deleting an island in Paper with async chunk loads

This commit is contained in:
Fabrizio La Rosa 2020-06-22 05:20:08 +02:00
parent 4ab5fa01ed
commit 30e4637d3f
6 changed files with 139 additions and 60 deletions

View File

@ -127,9 +127,9 @@
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.15</version>
<groupId>com.destroystokyo.papermc</groupId>
<artifactId>paper</artifactId>
<version>1.15.2</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -93,6 +93,8 @@ public class SkyBlock extends SongodaPlugin {
private CoreProtectAPI coreProtectAPI;
private boolean paper;
private final GuiManager guiManager = new GuiManager(this);
public static SkyBlock getInstance() {
@ -106,6 +108,14 @@ public class SkyBlock extends SongodaPlugin {
@Override
public void onPluginEnable() {
paper = false;
try {
Class.forName("com.destroystokyo.paper.PaperConfig");
paper = true;
Bukkit.getLogger().info("Enabling Paper hooks");
} catch (ClassNotFoundException ignored) {}
// Run Songoda Updater
SongodaCore.registerPlugin(this, 17, CompatibleMaterial.GRASS_BLOCK);
@ -135,7 +145,7 @@ public class SkyBlock extends SongodaPlugin {
inviteManager = new InviteManager(this);
biomeManager = new BiomeManager(this);
levellingManager = new IslandLevelManager();
levellingManager = new IslandLevelManager(this);
commandManager = new CommandManager(this);
structureManager = new StructureManager(this);
soundManager = new SoundManager(this);
@ -415,4 +425,8 @@ public class SkyBlock extends SongodaPlugin {
public CoreProtectAPI getCoreProtectAPI() {
return coreProtectAPI;
}
public boolean isPaper() {
return paper;
}
}

View File

@ -18,6 +18,7 @@ import com.songoda.skyblock.cooldown.CooldownType;
import com.songoda.skyblock.invite.Invite;
import com.songoda.skyblock.invite.InviteManager;
import com.songoda.skyblock.island.removal.ChunkDeleteSplitter;
import com.songoda.skyblock.levelling.ChunkUtil;
import com.songoda.skyblock.message.MessageManager;
import com.songoda.skyblock.playerdata.PlayerData;
import com.songoda.skyblock.playerdata.PlayerDataManager;
@ -56,6 +57,7 @@ import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class IslandManager {
@ -556,22 +558,15 @@ public class IslandManager {
}
}
final Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
for (IslandWorld worldList : IslandWorld.getIslandWorlds()) {
final Location location = island.getLocation(worldList, IslandEnvironment.Island);
if (location == null) continue;
final World world = worldManager.getWorld(worldList);
final List<ChunkSnapshot> list = com.songoda.skyblock.levelling.ChunkUtil.getChunksToScan(island, worldList).stream().map(chunk -> chunk.getChunkSnapshot()).collect(Collectors.toList());
snapshots.put(world, list);
if (skyblock.isPaper() && Bukkit.spigot().getPaperConfig().getBoolean("settings.async-chunks.enable", false)) {
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
startDeletition(island, worldManager);
});
} else {
startDeletition(island, worldManager);
}
ChunkDeleteSplitter.startDeletion(snapshots);
skyblock.getVisitManager().deleteIsland(island.getOwnerUUID());
skyblock.getBanManager().deleteIsland(island.getOwnerUUID());
@ -657,6 +652,40 @@ public class IslandManager {
return true;
}
private void startDeletition(Island island, WorldManager worldManager) {
final Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
for (IslandWorld worldList : IslandWorld.getIslandWorlds()) {
final Location location = island.getLocation(worldList, IslandEnvironment.Island);
if (location == null) continue;
final World world = worldManager.getWorld(worldList);
ChunkUtil chunks = new ChunkUtil();
if (skyblock.isPaper() && Bukkit.spigot().getPaperConfig().getBoolean("settings.async-chunks.enable", false)) {
chunks.getChunksToScan(island, worldList, true);
Bukkit.getScheduler().runTaskAsynchronously(skyblock, () -> {
List<Chunk> positions = new LinkedList<>();
for (CompletableFuture<Chunk> chunk : chunks.asyncPositions) {
positions.add(chunk.join());
snapshots.put(world, positions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
});
} else {
chunks.getChunksToScan(island, worldList, false);
final List<ChunkSnapshot> list = chunks.syncPositions.stream().map(Chunk::getChunkSnapshot).collect(Collectors.toList());
snapshots.put(world, list);
}
}
ChunkDeleteSplitter.startDeletion(snapshots);
}
public synchronized void deleteIslandData(UUID uuid) {
FileManager fileManager = skyblock.getFileManager();
fileManager.deleteConfig(new File(skyblock.getDataFolder().toString() + "/island-data", uuid.toString() + ".yml"));

View File

@ -3,7 +3,9 @@ package com.songoda.skyblock.levelling;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import com.sk89q.worldedit.bukkit.paperlib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
@ -13,11 +15,13 @@ import com.songoda.skyblock.island.IslandEnvironment;
import com.songoda.skyblock.island.IslandWorld;
public class ChunkUtil {
public final List<CompletableFuture<Chunk>> asyncPositions = new LinkedList<>();
public final List<Chunk> syncPositions = new LinkedList<>();
public static List<Chunk> getChunksToScan(Island island, IslandWorld islandWorld) {
public void getChunksToScan(Island island, IslandWorld islandWorld, boolean paper) {
Location islandLocation = island.getLocation(islandWorld, IslandEnvironment.Island);
if (islandLocation == null) return new ArrayList<>(0);
if (islandLocation == null) return;
World world = islandLocation.getWorld();
@ -30,14 +34,15 @@ public class ChunkUtil {
int maxX = Math.max(maxLocation.getBlockX(), minLocation.getBlockX());
int maxZ = Math.max(maxLocation.getBlockZ(), minLocation.getBlockZ());
final List<Chunk> positions = new LinkedList<>();
for (int x = minX; x < maxX + 16; x += 16) {
for (int z = minZ; z < maxZ + 16; z += 16) {
positions.add(world.getChunkAt(x >> 4, z >> 4));
if(paper){
asyncPositions.add(PaperLib.getChunkAtAsync(world, x >> 4, z >> 4));
} else {
syncPositions.add(world.getChunkAt(x >> 4, z >> 4));
}
}
}
return positions;
}
}

View File

@ -37,8 +37,10 @@ public final class IslandLevelManager {
private Map<Island, IslandScan> inScan;
private Map<CompatibleMaterial, Long> worth;
private Map<CompatibleMaterial, AmountMaterialPair> cachedPairs;
private final SkyBlock plugin;
public IslandLevelManager() {
public IslandLevelManager(SkyBlock plugin) {
this.plugin = plugin;
this.inScan = new HashMap<>();
this.worth = new EnumMap<>(CompatibleMaterial.class);
this.cachedPairs = new EnumMap<>(CompatibleMaterial.class);
@ -46,8 +48,7 @@ public final class IslandLevelManager {
reloadWorth();
}
public void startScan(Player attemptScanner, Island island) {
public void startScan(Player attemptScanner, Island island){
if (!Bukkit.isPrimaryThread()) {
Bukkit.getScheduler().runTask(SkyBlock.getInstance(), () -> startScan(attemptScanner, island));
return;
@ -73,7 +74,7 @@ public final class IslandLevelManager {
messageManager.sendMessage(attemptScanner, config.getString("Command.Island.Level.Scanning.Started.Message"));
}
inScan.put(island, new IslandScan(island).start());
inScan.put(island, new IslandScan(plugin, island).start());
}
public boolean isScanning(Island island) {

View File

@ -12,10 +12,7 @@ import com.songoda.skyblock.levelling.ChunkUtil;
import com.songoda.skyblock.levelling.rework.amount.AmountMaterialPair;
import com.songoda.skyblock.levelling.rework.amount.BlockAmount;
import com.songoda.skyblock.message.MessageManager;
import org.bukkit.Bukkit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
@ -25,6 +22,7 @@ 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 {
@ -37,13 +35,15 @@ public final class IslandScan extends BukkitRunnable {
private final Map<CompatibleMaterial, BlockAmount> amounts;
private final Configuration language;
private final int runEveryX;
private final SkyBlock plugin;
private int totalScanned;
private int blocksSize;
private Queue<BlockInfo> blocks;
public IslandScan(Island island) {
public IslandScan(SkyBlock plugin, Island island) {
if (island == null) throw new IllegalArgumentException("island cannot be null");
this.plugin = plugin;
this.island = island;
this.amounts = new EnumMap<>(CompatibleMaterial.class);
this.language = SkyBlock.getInstance().getFileManager().getConfig(new File(SkyBlock.getInstance().getDataFolder(), "language.yml")).getFileConfiguration();
@ -62,16 +62,34 @@ public final class IslandScan extends BukkitRunnable {
final Map<World, List<ChunkSnapshot>> snapshots = new HashMap<>(3);
populate(snapshots, IslandWorld.Normal);
if (hasNether) populate(snapshots, IslandWorld.Nether);
if (hasEnd) populate(snapshots, IslandWorld.End);
BlockScanner.startScanner(snapshots, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
if (skyblock.isPaper() && Bukkit.spigot().getPaperConfig().getBoolean("settings.async-chunks.enable", false)) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
populate(snapshots, IslandWorld.Normal, true);
if (hasNether) populate(snapshots, IslandWorld.Nether, true);
if (hasEnd) populate(snapshots, IslandWorld.End, true);
BlockScanner.startScanner(snapshots, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
});
} else {
populate(snapshots, IslandWorld.Normal, false);
if (hasNether) populate(snapshots, IslandWorld.Nether, false);
if (hasEnd) populate(snapshots, IslandWorld.End, false);
BlockScanner.startScanner(snapshots, (blocks) -> {
this.blocks = blocks;
this.blocksSize = blocks.size();
this.runTaskTimer(SkyBlock.getInstance(), 20, 20);
});
}
});
return this;
}
@ -133,37 +151,49 @@ public final class IslandScan extends BukkitRunnable {
SkyBlock.getInstance().getLevellingManager().stopScan(island);
}
if (language.getBoolean("Command.Island.Level.Scanning.Progress.Should-Display-Message") && executions == 1 || totalScanned == blocksSize || executions % runEveryX == 0) {
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;
final double percent = ((double) totalScanned / (double) blocksSize) * 100;
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));
message = message.replace("%percent_whole%", String.valueOf((int) percent));
message = message.replace("%percent%", FORMATTER.format(percent));
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));
message = message.replace("%percent_whole%", String.valueOf((int) percent));
message = message.replace("%percent%", FORMATTER.format(percent));
final boolean displayComplete = totalScanned == blocksSize && language.getBoolean("Command.Island.Level.Scanning.Finished.Should-Display-Message");
final MessageManager messageManager = SkyBlock.getInstance().getMessageManager();
final boolean displayComplete = totalScanned == blocksSize && language.getBoolean("Command.Island.Level.Scanning.Finished.Should-Display-Message");
final MessageManager messageManager = SkyBlock.getInstance().getMessageManager();
for (Player player : SkyBlock.getInstance().getIslandManager().getPlayersAtIsland(island)) {
for (Player player : SkyBlock.getInstance().getIslandManager().getPlayersAtIsland(island)) {
messageManager.sendMessage(player, message);
if (displayComplete)
messageManager.sendMessage(player, language.getString("Command.Island.Level.Scanning.Finished.Message"));
messageManager.sendMessage(player, message);
if (displayComplete)
messageManager.sendMessage(player, language.getString("Command.Island.Level.Scanning.Finished.Message"));
// Check for level ups
island.getLevel().checkLevelUp();
// Check for level ups
island.getLevel().checkLevelUp();
}
}
}
});
}
private void populate(Map<World, List<ChunkSnapshot>> snapshots, IslandWorld world) {
private void populate(Map<World, List<ChunkSnapshot>> snapshots, IslandWorld world, boolean paper) {
final SkyBlock skyblock = SkyBlock.getInstance();
snapshots.put(skyblock.getWorldManager().getWorld(world), ChunkUtil.getChunksToScan(island, world).stream().map(org.bukkit.Chunk::getChunkSnapshot).collect(Collectors.toList()));
ChunkUtil chunks = new ChunkUtil();
chunks.getChunksToScan(island, world, paper);
if(paper){
List<Chunk> positions = new LinkedList<>();
for(CompletableFuture<Chunk> chunk : chunks.asyncPositions){
positions.add(chunk.join());
snapshots.put(skyblock.getWorldManager().getWorld(world), positions.stream().map(org.bukkit.Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
} else {
snapshots.put(skyblock.getWorldManager().getWorld(world), chunks.syncPositions.stream().map(org.bukkit.Chunk::getChunkSnapshot).collect(Collectors.toList()));
}
}
public Set<Location> getDoubleBlocks() {