mirror of
https://github.com/songoda/FabledSkyBlock.git
synced 2024-11-22 10:15:54 +01:00
Merge branch 'development'
This commit is contained in:
commit
ab89b312fd
@ -1,16 +0,0 @@
|
||||
image: maven:latest
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .m2/repository/
|
||||
- target/
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- mvn compile
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- mvn test
|
2
pom.xml
2
pom.xml
@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.songoda</groupId>
|
||||
<artifactId>skyblock</artifactId>
|
||||
<version>2.3.19</version>
|
||||
<version>2.3.21</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
@ -7,6 +7,7 @@ import com.songoda.skyblock.SkyBlock;
|
||||
import com.songoda.skyblock.island.Island;
|
||||
import com.songoda.skyblock.island.IslandEnvironment;
|
||||
import com.songoda.skyblock.world.WorldManager;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Location;
|
||||
@ -15,26 +16,33 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public final class BlockScanner extends BukkitRunnable {
|
||||
|
||||
private static final Method ID_FIELD;
|
||||
private static final int MAX_CHUNKS_PER_ITERATION = 2;
|
||||
private static final int MAX_EMPTY_ITERATIONS = 20;
|
||||
|
||||
static {
|
||||
Method temp = null;
|
||||
|
||||
try {
|
||||
temp = ChunkSnapshot.class.getMethod("getBlockTypeId", int.class, int.class, int.class);
|
||||
} catch (NoSuchMethodException ignored) {}
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
|
||||
ID_FIELD = temp;
|
||||
}
|
||||
@ -91,28 +99,28 @@ public final class BlockScanner extends BukkitRunnable {
|
||||
final String env;
|
||||
|
||||
switch (world.getEnvironment()) {
|
||||
case NETHER:
|
||||
env = "Nether";
|
||||
break;
|
||||
case THE_END:
|
||||
env = "End";
|
||||
break;
|
||||
default:
|
||||
env = "Normal";
|
||||
break;
|
||||
case NETHER:
|
||||
env = "Nether";
|
||||
break;
|
||||
case THE_END:
|
||||
env = "End";
|
||||
break;
|
||||
default:
|
||||
env = "Normal";
|
||||
break;
|
||||
}
|
||||
|
||||
final ConfigurationSection liquidSection = config.getConfigurationSection("Island.World." + env + ".Liquid");
|
||||
|
||||
int startY;
|
||||
if(ignoreY){
|
||||
if (ignoreY) {
|
||||
startY = 255;
|
||||
} else {
|
||||
startY = !ignoreLiquidsY && liquidSection.getBoolean("Enable") && !config.getBoolean("Island.Levelling.ScanLiquid") ? liquidSection.getInt("Height") + 1 : 0;
|
||||
}
|
||||
|
||||
for (List<CachedChunk> sub : parts) {
|
||||
queueWork(world, startY, sub);
|
||||
queueWork(world, startY, sub);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,9 +130,24 @@ public final class BlockScanner extends BukkitRunnable {
|
||||
private void queueWork(World world, int scanY, List<CachedChunk> subList) {
|
||||
WorldManager worldManager = SkyBlock.getInstance().getWorldManager();
|
||||
|
||||
// The chunks that couldn't be taken snapshot async
|
||||
List<CachedChunk> pendingChunks = new ArrayList<>();
|
||||
|
||||
// The chunks that are ready to be processed asynchronously
|
||||
List<CachedChunk> readyChunks = new ArrayList<>();
|
||||
|
||||
// This lock will help to make the bukkit task wait after all the chunks that could be processed async are processed
|
||||
Lock lock = new ReentrantLock();
|
||||
|
||||
// This is the actual object that we will use to wait
|
||||
Condition emptyCondition = lock.newCondition();
|
||||
|
||||
Bukkit.getServer().getScheduler().runTaskAsynchronously(SkyBlock.getInstance(), () -> {
|
||||
// We need to hold the lock on the thread calling the await
|
||||
lock.lock();
|
||||
|
||||
LocationBounds bounds = null;
|
||||
if(island != 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());
|
||||
@ -138,46 +161,134 @@ public final class BlockScanner extends BukkitRunnable {
|
||||
|
||||
bounds = new LocationBounds(minX, minZ, maxX, maxZ);
|
||||
}
|
||||
|
||||
for (CachedChunk shot : subList) {
|
||||
final int cX = shot.getX() << 4;
|
||||
final int cZ = shot.getZ() << 4;
|
||||
if (!shot.isSnapshotAvailable() && !areAsyncChunksAvailable()) {
|
||||
pendingChunks.add(shot);
|
||||
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
||||
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.getSnapshot().getBlockType(x, y, z) :
|
||||
MaterialIDHelper.getLegacyMaterial(getBlockTypeID(shot, x, y, z)));
|
||||
|
||||
if(type == null){
|
||||
continue;
|
||||
} else if(type.equals(CompatibleMaterial.AIR) && ignoreAir){
|
||||
continue;
|
||||
} else if(type.equals(CompatibleMaterial.WATER) && ignoreLiquids){
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks.add(new BlockInfo(world, x + (cX), y, z + (cZ)));
|
||||
}
|
||||
}
|
||||
}
|
||||
processCachedChunk(world, scanY, shot, bounds);
|
||||
}
|
||||
|
||||
// Don't wait for the condition if the async chunks are available, since it would never be signalled
|
||||
if (areAsyncChunksAvailable()) {
|
||||
increment();
|
||||
|
||||
lock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
emptyCondition.await();
|
||||
} catch (InterruptedException e) {
|
||||
// Pass the interruption
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// process the pending chunks
|
||||
for (CachedChunk shot : readyChunks) {
|
||||
processCachedChunk(world, scanY, shot, bounds);
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
increment();
|
||||
});
|
||||
|
||||
if (!areAsyncChunksAvailable()) {
|
||||
startChunkSnapshotTask(pendingChunks, readyChunks, emptyCondition, lock);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areAsyncChunksAvailable() {
|
||||
return PaperLib.isVersion(9) && PaperLib.isPaper();
|
||||
}
|
||||
|
||||
private void startChunkSnapshotTask(List<CachedChunk> pendingChunks, List<CachedChunk> readyChunks, Condition emptyCondition, Lock lock) {
|
||||
new BukkitRunnable() {
|
||||
// The number of iterations with the pendingChunks list empty
|
||||
private int emptyIterations = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.lock();
|
||||
int updatedChunks = 0;
|
||||
|
||||
Iterator<CachedChunk> chunkIterator = pendingChunks.iterator();
|
||||
|
||||
try {
|
||||
while (chunkIterator.hasNext()) {
|
||||
CachedChunk pendingChunk = chunkIterator.next();
|
||||
|
||||
if (updatedChunks >= MAX_CHUNKS_PER_ITERATION) {
|
||||
break;
|
||||
}
|
||||
|
||||
// take the snapshot
|
||||
pendingChunk.takeSnapshot();
|
||||
|
||||
chunkIterator.remove();
|
||||
readyChunks.add(pendingChunk);
|
||||
|
||||
updatedChunks++;
|
||||
}
|
||||
|
||||
if (pendingChunks.isEmpty()) {
|
||||
if (emptyIterations >= MAX_EMPTY_ITERATIONS) {
|
||||
// Send the signal to unlock the async thread and continue with the processing
|
||||
emptyCondition.signalAll();
|
||||
this.cancel();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
emptyIterations++;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(SkyBlock.getInstance(), 1, 1);
|
||||
}
|
||||
|
||||
private void processCachedChunk(World world, int scanY, CachedChunk shot, LocationBounds bounds) {
|
||||
final int cX = shot.getX() << 4;
|
||||
final int cZ = shot.getZ() << 4;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.getSnapshot().getBlockType(x, y, z) :
|
||||
MaterialIDHelper.getLegacyMaterial(getBlockTypeID(shot, x, y, z)));
|
||||
|
||||
if (type == null) {
|
||||
continue;
|
||||
} else if (type.equals(CompatibleMaterial.AIR) && ignoreAir) {
|
||||
continue;
|
||||
} else if (type.equals(CompatibleMaterial.WATER) && ignoreLiquids) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks.add(new BlockInfo(world, x + (cX), y, z + (cZ)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized int increment() {
|
||||
|
@ -49,6 +49,10 @@ public class CachedChunk {
|
||||
return PaperLib.getChunkAtAsync(world, this.x, this.z);
|
||||
}
|
||||
|
||||
public boolean isSnapshotAvailable() {
|
||||
return latestSnapshot != null;
|
||||
}
|
||||
|
||||
public ChunkSnapshot getSnapshot() {
|
||||
if (latestSnapshot == null)
|
||||
return takeSnapshot();
|
||||
|
@ -29,7 +29,7 @@ public class UpgradeCommand extends SubCommand {
|
||||
configLoad.getString("Command.Island.Upgrade.Owner.Message"));
|
||||
soundManager.playSound(player, CompatibleSound.BLOCK_ANVIL_LAND.getSound(), 1.0F, 1.0F);
|
||||
} else {
|
||||
if (!economy.isEnabled()) {
|
||||
if (economy == null || !economy.isEnabled()) {
|
||||
messageManager.sendMessage(player, configLoad.getString("Command.Island.Upgrade.Disabled.Message"));
|
||||
soundManager.playSound(player, CompatibleSound.BLOCK_ANVIL_LAND.getSound(), 1.0F, 1.0F);
|
||||
return;
|
||||
|
@ -1,11 +1,16 @@
|
||||
package com.songoda.skyblock.island;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum IslandStatus {
|
||||
OPEN,
|
||||
CLOSED,
|
||||
WHITELISTED;
|
||||
|
||||
public static IslandStatus getEnum(String value) {
|
||||
return valueOf(value.toUpperCase());
|
||||
return Arrays.stream(values())
|
||||
.filter(status -> value.toUpperCase().equals(status.name()))
|
||||
.findFirst()
|
||||
.orElse(OPEN);
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
|
||||
@ -55,7 +54,7 @@ public class QueuedIslandScan {
|
||||
update();
|
||||
|
||||
if (toScan.isEmpty()) {
|
||||
finalize();
|
||||
finalizeScan();
|
||||
return false;
|
||||
}
|
||||
IslandWorld world = toScan.poll();
|
||||
@ -63,8 +62,7 @@ public class QueuedIslandScan {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
|
||||
public void finalizeScan() {
|
||||
final Map<String, Long> materials = new HashMap<>(amounts.size());
|
||||
|
||||
for (Map.Entry<CompatibleMaterial, BlockAmount> entry : amounts.entrySet()) {
|
||||
|
@ -4,6 +4,7 @@ import com.songoda.skyblock.SkyBlock;
|
||||
import com.songoda.skyblock.utils.version.NMSUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -14,7 +15,8 @@ import java.lang.reflect.Method;
|
||||
public class SchematicUtil {
|
||||
|
||||
public static Float[] pasteSchematic(File schematicFile, org.bukkit.Location location) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("WorldEdit"))
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
if (!pluginManager.isPluginEnabled("WorldEdit") && !pluginManager.isPluginEnabled("AsyncWorldEdit") && !pluginManager.isPluginEnabled("FastAsyncWorldEdit"))
|
||||
throw new IllegalStateException("Tried to generate an island using a schematic file without WorldEdit installed!");
|
||||
|
||||
Runnable pasteTask = () -> {
|
||||
|
@ -47,9 +47,9 @@ public class WorldManager {
|
||||
String netherWorldGeneratorName = configLoad.getString("Island.World.End.CustomWorldGenerator");
|
||||
String endWorldGeneratorName = configLoad.getString("Island.World.End.CustomWorldGenerator");
|
||||
|
||||
normalWorldWorldGenerator = getWorldGenerator(normalWorldName, normalWorldGeneratorName);
|
||||
netherWorldWorldGenerator = getWorldGenerator(netherWorldName, netherWorldGeneratorName);
|
||||
endWorldWorldGenerator = getWorldGenerator(endWorldName, endWorldGeneratorName);
|
||||
normalWorldWorldGenerator = getWorldGenerator(normalWorldName, normalWorldGeneratorName, IslandWorld.Normal);
|
||||
netherWorldWorldGenerator = getWorldGenerator(netherWorldName, netherWorldGeneratorName, IslandWorld.Nether);
|
||||
endWorldWorldGenerator = getWorldGenerator(endWorldName, endWorldGeneratorName, IslandWorld.End);
|
||||
|
||||
normalWorld = Bukkit.getServer().getWorld(normalWorldName);
|
||||
netherWorld = Bukkit.getServer().getWorld(netherWorldName);
|
||||
@ -138,9 +138,9 @@ public class WorldManager {
|
||||
return location;
|
||||
}
|
||||
|
||||
private ChunkGenerator getWorldGenerator(String mapName, String worldGeneratorName) {
|
||||
private ChunkGenerator getWorldGenerator(String mapName, String worldGeneratorName, IslandWorld islandWorld) {
|
||||
if (worldGeneratorName == null || worldGeneratorName == "default" || worldGeneratorName.length() == 0) {
|
||||
return new VoidGenerator();
|
||||
return new VoidGenerator(islandWorld);
|
||||
}
|
||||
|
||||
ChunkGenerator customWorldGenerator = WorldCreator.getGeneratorForName(mapName, worldGeneratorName, null);
|
||||
@ -149,7 +149,7 @@ public class WorldManager {
|
||||
return customWorldGenerator;
|
||||
}
|
||||
|
||||
return new VoidGenerator();
|
||||
return new VoidGenerator(islandWorld);
|
||||
}
|
||||
|
||||
public ChunkGenerator getWorldGeneratorForMapName(String mapName) {
|
||||
@ -159,6 +159,6 @@ public class WorldManager {
|
||||
|
||||
if (endWorld != null && endWorld.getName().equals(mapName)) return endWorldWorldGenerator;
|
||||
|
||||
return new VoidGenerator();
|
||||
return new VoidGenerator(IslandWorld.Normal);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,11 @@ import java.util.Random;
|
||||
|
||||
public class VoidGenerator extends ChunkGenerator {
|
||||
|
||||
private final IslandWorld islandWorld;
|
||||
public VoidGenerator(IslandWorld islandWorld) {
|
||||
this.islandWorld = islandWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nonnull ChunkData generateChunkData(@Nonnull World world, @Nonnull Random random, int chunkX, int chunkZ, @Nonnull BiomeGrid biomeGrid) {
|
||||
final ChunkData chunkData = createChunkData(world);
|
||||
@ -33,7 +38,10 @@ public class VoidGenerator extends ChunkGenerator {
|
||||
|
||||
switch (world.getEnvironment()) {
|
||||
case NORMAL:
|
||||
biome = CompatibleBiome.valueOf(configLoad.getString("Island.Biome.Default.Type", "PLAINS").toUpperCase()).getBiome();
|
||||
biome = Arrays.stream(CompatibleBiome.values())
|
||||
.filter(compatibleBiome -> compatibleBiome.getBiome().name().equals(configLoad.getString("Island.Biome.Default.Type", "PLAINS").toUpperCase()))
|
||||
.findFirst()
|
||||
.orElse(CompatibleBiome.PLAINS).getBiome();
|
||||
break;
|
||||
case NETHER:
|
||||
biome = CompatibleBiome.NETHER_WASTES.getBiome();
|
||||
@ -51,25 +59,18 @@ public class VoidGenerator extends ChunkGenerator {
|
||||
setChunkBiome2D(biome, biomeGrid);
|
||||
}
|
||||
|
||||
for (IslandWorld worldList : IslandWorld.values()) {
|
||||
if (world.getEnvironment() == World.Environment.NETHER
|
||||
|| world.getEnvironment() == World.Environment.NORMAL
|
||||
|| world.getEnvironment() == World.Environment.THE_END) {
|
||||
ConfigurationSection section = worldSection.getConfigurationSection(islandWorld.name());
|
||||
|
||||
ConfigurationSection section = worldSection.getConfigurationSection(worldList.name());
|
||||
|
||||
if (section.getBoolean("Liquid.Enable")) {
|
||||
if (section.getBoolean("Liquid.Lava")) {
|
||||
setBlock(chunkData, CompatibleMaterial.LAVA.getBlockMaterial(), section.getInt("Liquid.Height"));
|
||||
} else {
|
||||
setBlock(chunkData, CompatibleMaterial.WATER.getBlockMaterial(), section.getInt("Liquid.Height"));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
if (section.getBoolean("Liquid.Enable")) {
|
||||
if (section.getBoolean("Liquid.Lava")) {
|
||||
setBlock(chunkData, CompatibleMaterial.LAVA.getBlockMaterial(), section.getInt("Liquid.Height"));
|
||||
} else {
|
||||
setBlock(chunkData, CompatibleMaterial.WATER.getBlockMaterial(), section.getInt("Liquid.Height"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return chunkData;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user