Changed island information creation to be async.
Flooder no longer uses it's own interface for flooding.
This commit is contained in:
parent
78f0a846da
commit
ea4ba47427
@ -1,11 +1,11 @@
|
||||
package ca.recrown.islandsurvivalcraft.interaction.commands;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.interaction.commands.runnables.HighlightIslandCommand;
|
||||
import ca.recrown.islandsurvivalcraft.interaction.commands.runnables.HighlightCommand;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.interaction.commands.runnables.*;
|
||||
|
||||
public enum RegisteredCommands {
|
||||
HIGHLIGHT(new HighlightIslandCommand()),
|
||||
HIGHLIGHT(new HighlightCommand()),
|
||||
HELP(new HelpCommand()),
|
||||
VALUE(new ValueRunnable()),
|
||||
;
|
||||
|
@ -1,8 +1,6 @@
|
||||
package ca.recrown.islandsurvivalcraft.interaction.commands.runnables;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
@ -18,7 +16,7 @@ import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.world.WorldInfo;
|
||||
import ca.recrown.islandsurvivalcraft.world.Information.IslandInformation;
|
||||
|
||||
public class HighlightIslandCommand implements CommandRunnable {
|
||||
public class HighlightCommand implements CommandRunnable {
|
||||
private boolean requiresInit = true;
|
||||
private final Particle.DustOptions EDGE_DUST_OPTIONS = new Particle.DustOptions(Color.RED, 1f);
|
||||
private final Particle.DustOptions ORIGIN_DUST_OPTIONS = new Particle.DustOptions(Color.GREEN, 1f);
|
||||
@ -26,7 +24,6 @@ public class HighlightIslandCommand implements CommandRunnable {
|
||||
private final double OFFSET_X = 0.0d, OFFSET_Y = 6.0d, OFFSET_Z = 0.0d;
|
||||
private final double EXTRA = 0d;
|
||||
HashSet<Player> playersHighlighting = new HashSet<>();
|
||||
LinkedList<Player> waitingList = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
@ -45,7 +42,6 @@ public class HighlightIslandCommand implements CommandRunnable {
|
||||
}
|
||||
if (args[0].toLowerCase().equals("start")) {
|
||||
if (playersHighlighting.add(player)) {
|
||||
waitingList.add(player);
|
||||
sender.sendMessage("You are now highlighting islands.");
|
||||
} else {
|
||||
sender.sendMessage("You are already highlighting islands.");
|
||||
@ -68,8 +64,9 @@ public class HighlightIslandCommand implements CommandRunnable {
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(islandsurvivalcraft, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Player player = waitingList.poll();
|
||||
if (player == null || !player.isOnline() || !playersHighlighting.contains(player)) {
|
||||
for (Player player : playersHighlighting) {
|
||||
if (player == null) return;
|
||||
if (!player.isOnline()) {
|
||||
playersHighlighting.remove(player);
|
||||
return;
|
||||
}
|
||||
@ -81,15 +78,12 @@ public class HighlightIslandCommand implements CommandRunnable {
|
||||
IslandInformation islandInfo = worldInfo.getIslandInfoManager().getIslandInformation(playerCoords);
|
||||
if (islandInfo != null) {
|
||||
Set<Point2> islandBorder = islandInfo.getEdgeCoordinates();
|
||||
Iterator<Point2> islandborderIterable = islandBorder.iterator();
|
||||
for (int i = 0; i < islandBorder.size(); i+= 2) {
|
||||
Point2 current = islandborderIterable.next();
|
||||
for (Point2 current : islandBorder) {
|
||||
spawnParticle(world, current.x, playerY, current.y, EDGE_DUST_OPTIONS);
|
||||
islandborderIterable.next();
|
||||
}
|
||||
spawnParticle(world, islandInfo.getIslandOrigin().x, playerY, islandInfo.getIslandOrigin().y, ORIGIN_DUST_OPTIONS);
|
||||
}
|
||||
waitingList.add(player);
|
||||
}
|
||||
}
|
||||
}, 0, 10);
|
||||
|
||||
@ -101,7 +95,9 @@ public class HighlightIslandCommand implements CommandRunnable {
|
||||
|
||||
@Override
|
||||
public String getDetailedDescription() {
|
||||
return "A helpful debugging command to analyze what the plugin considers an island by displaying particle effects around the player that is a part of the island. The <start | stop> argument is required to dicate whether or not to start the highlighting or stop it.";
|
||||
return "A helpful debugging command to analyze what the plugin considers an island by displaying particle effects around the player that is a part of the island. " +
|
||||
"Red particles indicate the islands border, and green particles indicate the origin. " +
|
||||
"The <start | stop> argument is required to dicate whether or not to start the highlighting or stop it.";
|
||||
}
|
||||
|
||||
@Override
|
@ -0,0 +1,58 @@
|
||||
package ca.recrown.islandsurvivalcraft.utilities.drawing;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
|
||||
|
||||
public class Circle {
|
||||
public final Point2 center;
|
||||
public final CoordinateValidatable drawer;
|
||||
public final int r;
|
||||
int p, x, y;
|
||||
|
||||
/**
|
||||
* A circle drawing object.
|
||||
* @param center The center point.
|
||||
* @param radius The radius of the circle to be drawn.
|
||||
* @param drawer The coordinate validator to be used to draw. The value the validator returns is ignored.
|
||||
*/
|
||||
public Circle(Point2 center, int radius, CoordinateValidatable drawer) {
|
||||
if (center == null) throw new NullPointerException("Center cannot be null.");
|
||||
if (radius <= 0) throw new IllegalArgumentException("radius cannot be 0 or less.");
|
||||
if (drawer == null) throw new NullPointerException("Drawer cannot be null.");
|
||||
this.center = center;
|
||||
this.r = radius;
|
||||
this.drawer = drawer;
|
||||
|
||||
p = 1 - radius;
|
||||
x = 0;
|
||||
y = this.r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true if done.
|
||||
*/
|
||||
public boolean step() {
|
||||
if (x > y) return true;
|
||||
drawer.validate(new Point2(center.x + x, center.y + y));
|
||||
drawer.validate(new Point2(center.x + x, center.y - y));
|
||||
drawer.validate(new Point2(center.x - x, center.y + y));
|
||||
drawer.validate(new Point2(center.x - x, center.y - y));
|
||||
drawer.validate(new Point2(center.x + y, center.y + x));
|
||||
drawer.validate(new Point2(center.x + y, center.y - x));
|
||||
drawer.validate(new Point2(center.x - y, center.y + x));
|
||||
drawer.validate(new Point2(center.x - y, center.y - x));
|
||||
if (p < 0) {
|
||||
p += 2*x + 3;
|
||||
} else {
|
||||
p += 2 * x - 2 * y + 5;
|
||||
y--;
|
||||
}
|
||||
x++;
|
||||
return x > y;
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
while (!step());
|
||||
}
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
package ca.recrown.islandsurvivalcraft.utilities.floodfill;
|
||||
package ca.recrown.islandsurvivalcraft.utilities.drawing;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
|
||||
|
||||
public class Flooder {
|
||||
private final Point2 start;
|
||||
private final Floodable floodable;
|
||||
private final CoordinateValidatable floodable;
|
||||
|
||||
public Flooder(Point2 start, Floodable floodable) {
|
||||
public Flooder(Point2 start, CoordinateValidatable floodable) {
|
||||
this.start = start;
|
||||
this.floodable = floodable;
|
||||
}
|
||||
|
||||
public Flooder(int xStart, int yStart, Floodable floodable) {
|
||||
public Flooder(int xStart, int yStart, CoordinateValidatable floodable) {
|
||||
this(new Point2(xStart, yStart), floodable);
|
||||
}
|
||||
|
||||
@ -24,22 +25,22 @@ public class Flooder {
|
||||
LinkedList<Point2> queue = new LinkedList<>();
|
||||
|
||||
if (!checked.add(start)) return;
|
||||
if (!floodable.flood(start)) return;
|
||||
if (!floodable.validate(start)) return;
|
||||
queue.add(start);
|
||||
while (!queue.isEmpty()) {
|
||||
Point2 p = queue.pop();
|
||||
|
||||
Point2 pE = new Point2(p.x + 1, p.y);
|
||||
if (checked.add(pE) && floodable.flood(pE)) queue.add(pE);
|
||||
if (checked.add(pE) && floodable.validate(pE)) queue.add(pE);
|
||||
|
||||
Point2 pW = new Point2(p.x - 1, p.y);
|
||||
if (checked.add(pW) && floodable.flood(pW)) queue.add(pW);
|
||||
if (checked.add(pW) && floodable.validate(pW)) queue.add(pW);
|
||||
|
||||
Point2 pN = new Point2(p.x, p.y + 1);
|
||||
if (checked.add(pN) && floodable.flood(pN)) queue.add(pN);
|
||||
if (checked.add(pN) && floodable.validate(pN)) queue.add(pN);
|
||||
|
||||
Point2 pS = new Point2(p.x, p.y - 1);
|
||||
if (checked.add(pS) && floodable.flood(pS)) queue.add(pS);
|
||||
if (checked.add(pS) && floodable.validate(pS)) queue.add(pS);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package ca.recrown.islandsurvivalcraft.utilities.floodfill;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
|
||||
public interface Floodable {
|
||||
/**
|
||||
* Called for every block. If it shouldn't be flooded, return false.
|
||||
* Code to execute for flooding should be placed here.
|
||||
* @param x the x coordinate.
|
||||
* @param y the y coordinate.
|
||||
* @return if this was a successful flooding.
|
||||
*/
|
||||
public boolean flood(Point2 point);
|
||||
}
|
@ -2,6 +2,11 @@ package ca.recrown.islandsurvivalcraft.world.Information;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.utilities.GeneralUtilities;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
@ -9,6 +14,15 @@ import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
|
||||
|
||||
public class IslandInformationManager {
|
||||
private final IslandWorldMap islandMap;
|
||||
private final ExecutorService executors = Executors.newFixedThreadPool(2, new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName("ISC-Chunk-Info-Loader");
|
||||
return thread;
|
||||
}
|
||||
});
|
||||
private final ConcurrentHashMap<Point2, Future<Void>> building = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Point2, HashSet<IslandInformation>> islandSets = new ConcurrentHashMap<>();
|
||||
|
||||
public IslandInformationManager(IslandWorldMap islandWorldMap) {
|
||||
@ -23,7 +37,7 @@ public class IslandInformationManager {
|
||||
surrounding[3] = chunkCoords.shift(0, 1);
|
||||
HashSet<Point2> checkedCoordinates = new HashSet<>();
|
||||
for (Point2 chunk : surrounding) {
|
||||
HashSet<IslandInformation> chunkIslandSet = getChunkIslandInformationSet(chunk);
|
||||
HashSet<IslandInformation> chunkIslandSet = islandSets.computeIfAbsent(chunk, (p) -> new HashSet<>());
|
||||
for (IslandInformation islandInformation : chunkIslandSet) {
|
||||
if (islandInformation.isIslandInChunk(chunkCoords)) {
|
||||
checkedCoordinates.addAll(islandInformation.getIslandCoordinates());
|
||||
@ -31,7 +45,6 @@ public class IslandInformationManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int x = 0; x < GeneralUtilities.CHUNK_SIZE; x++) {
|
||||
for (int z = 0; z < GeneralUtilities.CHUNK_SIZE; z++) {
|
||||
int worldX = chunkCoords.x * GeneralUtilities.CHUNK_SIZE + x;
|
||||
@ -39,7 +52,8 @@ public class IslandInformationManager {
|
||||
if (islandMap.isIsland(worldX, worldZ)) {
|
||||
IslandInformationBuilder infoBuilder = new IslandInformationBuilder();
|
||||
Point2 origin = islandMap.findIslandOrigin(worldX, worldZ, (p) -> {
|
||||
if (!checkedCoordinates.add(p)) return true;
|
||||
if (!checkedCoordinates.add(p))
|
||||
return true;
|
||||
infoBuilder.addCoordinate(p, islandMap.isEdgeOfIsland(p.x, p.y));
|
||||
infoBuilder.addChunk(GeneralUtilities.worldToChunkCoordinates(p));
|
||||
return false;
|
||||
@ -47,7 +61,7 @@ public class IslandInformationManager {
|
||||
if (origin != null) {
|
||||
IslandInformation islandInfo = infoBuilder.build(origin);
|
||||
for (Point2 chunk : infoBuilder.getChunksUsed()) {
|
||||
getChunkIslandInformationSet(chunk).add(islandInfo);
|
||||
islandSets.computeIfAbsent(chunk, (p) -> new HashSet<>()).add(islandInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,17 +69,33 @@ public class IslandInformationManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadChunkIslandInformationAsync(Point2 chunkCoords) {
|
||||
building.put(chunkCoords, executors.submit(() -> {
|
||||
loadChunkIslandInformation(chunkCoords);
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
public void unloadChunkIslandInformation(Point2 chunkCoords) {
|
||||
islandSets.remove(chunkCoords);
|
||||
building.remove(chunkCoords);
|
||||
}
|
||||
|
||||
public HashSet<IslandInformation> getChunkIslandInformationSet(Point2 chunkCoords) {
|
||||
if (!islandSets.containsKey(chunkCoords)) islandSets.put(chunkCoords, new HashSet<>());
|
||||
public HashSet<IslandInformation> getChunkIslandInformationSet(Point2 chunkCoords, boolean checkBuilding) {
|
||||
if (!islandSets.containsKey(chunkCoords)) {
|
||||
if (checkBuilding && building.containsKey(chunkCoords)) {
|
||||
try {
|
||||
building.get(chunkCoords).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
building.remove(chunkCoords);
|
||||
return islandSets.get(chunkCoords);
|
||||
}
|
||||
|
||||
public IslandInformation getIslandInformation(Point2 coords) {
|
||||
HashSet<IslandInformation> chunkIslandSet = getChunkIslandInformationSet(GeneralUtilities.worldToChunkCoordinates(coords));
|
||||
HashSet<IslandInformation> chunkIslandSet = getChunkIslandInformationSet(GeneralUtilities.worldToChunkCoordinates(coords), true);
|
||||
for (IslandInformation islandInformation : chunkIslandSet) {
|
||||
if (islandInformation.isWithinIsland(coords)) return islandInformation;
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ import ca.recrown.islandsurvivalcraft.world.generation.chunks.IslandWorldChunkGe
|
||||
public class WorldInfoManager implements Listener {
|
||||
public final ConcurrentHashMap<String, WorldInfo> worldInformation = new ConcurrentHashMap<>();
|
||||
|
||||
public WorldInfoManager() {
|
||||
}
|
||||
/**
|
||||
* Return the world info requested for the given world.
|
||||
* Should not be null.
|
||||
@ -50,7 +52,7 @@ public class WorldInfoManager implements Listener {
|
||||
if (!worldInformation.containsKey(event.getWorld().getName())) return;
|
||||
WorldInfo worldInfo = retrieve(event.getWorld().getName());
|
||||
if (!worldInfo.isInitialized()) worldInfo.initialize(event.getWorld());
|
||||
worldInfo.getIslandInfoManager().loadChunkIslandInformation(new Point2(chunk.getX(), chunk.getZ()));
|
||||
worldInfo.getIslandInfoManager().loadChunkIslandInformationAsync(new Point2(chunk.getX(), chunk.getZ()));
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
|
@ -5,8 +5,7 @@ import ca.recrown.islandsurvivalcraft.utilities.biomes.BiomeInfo;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.caching.Cache;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Reference;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Floodable;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Flooder;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.drawing.Flooder;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeMap;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
|
||||
@ -46,9 +45,9 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
|
||||
if (currentIslandInfo.value == null) {
|
||||
currentIslandInfo.value = biomeSelector.getLandBiomeInfo(temperature, islandOrigin.x, islandOrigin.y);
|
||||
}
|
||||
Flooder flooder = new Flooder(worldCoords, new Floodable() {
|
||||
Flooder flooder = new Flooder(worldCoords, new CoordinateValidatable() {
|
||||
@Override
|
||||
public boolean flood(Point2 point) {
|
||||
public boolean validate(Point2 point) {
|
||||
Point2 chunkCoords = GeneralUtilities.worldToChunkCoordinates(point);
|
||||
if (chunkCoords.x != chunkX || chunkCoords.y != chunkZ || !islandMap.isIsland(point.x, point.y)) return false;
|
||||
biomeCache.set(point, currentIslandInfo.value);
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ca.recrown.islandsurvivalcraft.utilities.floodfill;
|
||||
package ca.recrown.islandsurvivalcraft.utilities.drawing;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -14,14 +14,14 @@ import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.floodfill.Floodable;
|
||||
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
|
||||
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
public class FloodFillTest {
|
||||
public byte[][] mapA;
|
||||
public byte[][] mapB;
|
||||
|
||||
private class Flood implements Floodable {
|
||||
private class Flood implements CoordinateValidatable {
|
||||
private final byte[][] map;
|
||||
|
||||
public Flood(byte[][] map) {
|
||||
@ -29,7 +29,7 @@ public class FloodFillTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flood(Point2 point) {
|
||||
public boolean validate(Point2 point) {
|
||||
try {
|
||||
if (map[point.y][point.x] < 1) return false;
|
||||
} catch (IndexOutOfBoundsException e) {
|
Loading…
Reference in New Issue
Block a user