Made roof search async
This commit is contained in:
parent
08e9c25172
commit
cc114027da
|
@ -46,4 +46,11 @@ public abstract class MinMaxXZ {
|
||||||
public int getArea() {
|
public int getArea() {
|
||||||
return (maxX - minX) * (maxZ - minZ);
|
return (maxX - minX) * (maxZ - minZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MinMaxXZ [minX=" + minX + ", maxX=" + maxX + ", minZ=" + minZ + ", maxZ=" + maxZ + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,18 @@ package world.bentobox.greenhouses.greenhouse;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.greenhouses.Greenhouses;
|
import world.bentobox.greenhouses.Greenhouses;
|
||||||
|
import world.bentobox.greenhouses.world.AsyncWorldCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the parameters of a greenhouse roof
|
* Contains the parameters of a greenhouse roof
|
||||||
|
@ -19,6 +22,7 @@ import world.bentobox.greenhouses.Greenhouses;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Roof extends MinMaxXZ {
|
public class Roof extends MinMaxXZ {
|
||||||
|
private static final BentoBox PLUGIN = Greenhouses.getInstance().getPlugin();
|
||||||
private static final List<Material> ROOF_BLOCKS;
|
private static final List<Material> ROOF_BLOCKS;
|
||||||
static {
|
static {
|
||||||
List<Material> r = Arrays.stream(Material.values())
|
List<Material> r = Arrays.stream(Material.values())
|
||||||
|
@ -29,40 +33,111 @@ public class Roof extends MinMaxXZ {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
ROOF_BLOCKS = Collections.unmodifiableList(r);
|
ROOF_BLOCKS = Collections.unmodifiableList(r);
|
||||||
}
|
}
|
||||||
private final Location location;
|
/**
|
||||||
|
* Check if material is a roof material
|
||||||
|
* @param m - material
|
||||||
|
* @return true if roof material
|
||||||
|
*/
|
||||||
|
public static boolean roofBlocks(Material m) {
|
||||||
|
return ROOF_BLOCKS.contains(m)
|
||||||
|
|| (m.equals(Material.GLOWSTONE) && Greenhouses.getInstance().getSettings().isAllowGlowstone())
|
||||||
|
|| (m.name().endsWith("GLASS_PANE") && Greenhouses.getInstance().getSettings().isAllowPanes());
|
||||||
|
}
|
||||||
|
private final AsyncWorldCache cache;
|
||||||
private int height;
|
private int height;
|
||||||
|
private final Location location;
|
||||||
private boolean roofFound;
|
private boolean roofFound;
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a roof from a starting location under the roof and characterizes it
|
* Finds a roof from a starting location under the roof and characterizes it
|
||||||
|
* @param cache
|
||||||
* @param loc - starting location
|
* @param loc - starting location
|
||||||
*/
|
*/
|
||||||
public Roof(Location loc) {
|
public Roof(AsyncWorldCache cache, Location loc) {
|
||||||
|
this.cache = cache;
|
||||||
this.location = loc;
|
this.location = loc;
|
||||||
roofFound = findRoof(loc);
|
this.world = loc.getWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean findRoof(Location loc) {
|
|
||||||
World world = loc.getWorld();
|
/**
|
||||||
|
* This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks
|
||||||
|
* up to 100 in any direction
|
||||||
|
* @param vector - vector to start search
|
||||||
|
*/
|
||||||
|
private void expandCoords(Vector vector) {
|
||||||
|
Vector maxx = vector.clone();
|
||||||
|
Vector minx = vector.clone();
|
||||||
|
Vector maxz = vector.clone();
|
||||||
|
Vector minz = vector.clone();
|
||||||
|
int limit = 0;
|
||||||
|
while (roofBlocks(cache.getBlockType(maxx)) && limit < 100) {
|
||||||
|
limit++;
|
||||||
|
maxx.add(new Vector(1,0,0));
|
||||||
|
}
|
||||||
|
// Set Max x
|
||||||
|
if (maxx.getBlockX() - 1 > maxX) {
|
||||||
|
maxX = maxx.getBlockX() - 1;
|
||||||
|
}
|
||||||
|
limit = 0;
|
||||||
|
while (roofBlocks(cache.getBlockType(minx)) && limit < 100) {
|
||||||
|
limit++;
|
||||||
|
minx.subtract(new Vector(1,0,0));
|
||||||
|
}
|
||||||
|
if (minx.getBlockX() + 1 < minX) {
|
||||||
|
minX = minx.getBlockX() + 1;
|
||||||
|
}
|
||||||
|
limit = 0;
|
||||||
|
while (roofBlocks(cache.getBlockType(maxz)) && limit < 100) {
|
||||||
|
limit++;
|
||||||
|
maxz.add(new Vector(0,0,1));
|
||||||
|
}
|
||||||
|
if (maxz.getBlockZ() - 1 > maxZ) {
|
||||||
|
maxZ = maxz.getBlockZ() - 1;
|
||||||
|
}
|
||||||
|
limit = 0;
|
||||||
|
while (roofBlocks(cache.getBlockType(minz)) && limit < 100) {
|
||||||
|
limit++;
|
||||||
|
minz.subtract(new Vector(0,0,1));
|
||||||
|
}
|
||||||
|
if (minz.getBlockZ() + 1 < minZ) {
|
||||||
|
minZ = minz.getBlockZ() + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Boolean> findRoof() {
|
||||||
|
CompletableFuture<Boolean> r = new CompletableFuture<>();
|
||||||
|
Vector loc = location.toVector();
|
||||||
// This section tries to find a roof block
|
// This section tries to find a roof block
|
||||||
// Try just going up - this covers every case except if the player is standing under a hole
|
// Try just going up - this covers every case except if the player is standing under a hole
|
||||||
roofFound = false;
|
Bukkit.getScheduler().runTaskAsynchronously(PLUGIN, () -> {
|
||||||
|
boolean found = findRoof(loc);
|
||||||
|
Bukkit.getScheduler().runTask(PLUGIN, () -> r.complete(found));
|
||||||
|
});
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean findRoof(Vector loc) {
|
||||||
// This does a ever-growing check around the player to find a wall block. It is possible for the player
|
// This does a ever-growing check around the player to find a wall block. It is possible for the player
|
||||||
// to be outside the greenhouse in this situation, so a check is done later to make sure the player is inside
|
// to be outside the greenhouse in this situation, so a check is done later to make sure the player is inside
|
||||||
int roofY = loc.getBlockY();
|
int roofY = loc.getBlockY();
|
||||||
for (int y = roofY; y < world.getMaxHeight(); y++) {
|
for (int y = roofY; y < world.getMaxHeight(); y++) {
|
||||||
if (roofBlocks(world.getBlockAt(loc.getBlockX(),y,loc.getBlockZ()).getType())) {
|
if (roofBlocks(cache.getBlockType(loc.getBlockX(),y,loc.getBlockZ()))) {
|
||||||
roofFound = true;
|
roofFound = true;
|
||||||
loc = new Location(world,loc.getBlockX(),y,loc.getBlockZ());
|
loc = new Vector(loc.getBlockX(),y,loc.getBlockZ());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the roof was not found start going around in circles until something is found
|
// If the roof was not found start going around in circles until something is found
|
||||||
// Expand in ever increasing squares around location until a wall block is found
|
// Expand in ever increasing squares around location until a wall block is found
|
||||||
spiralSearch(loc, roofY);
|
spiralSearch(loc, roofY);
|
||||||
if (!roofFound) return false;
|
if (!roofFound) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Record the height
|
// Record the height
|
||||||
this.height = loc.getBlockY();
|
this.height = loc.getBlockY();
|
||||||
// Now we have a roof block, find how far we can go NSWE
|
// Now we have a roof block, find how far we can go NSWE
|
||||||
|
@ -70,7 +145,7 @@ public class Roof extends MinMaxXZ {
|
||||||
maxX = loc.getBlockX();
|
maxX = loc.getBlockX();
|
||||||
minZ = loc.getBlockZ();
|
minZ = loc.getBlockZ();
|
||||||
maxZ = loc.getBlockZ();
|
maxZ = loc.getBlockZ();
|
||||||
expandCoords(world, loc.toVector());
|
expandCoords(loc);
|
||||||
int minx;
|
int minx;
|
||||||
int maxx;
|
int maxx;
|
||||||
int minz;
|
int minz;
|
||||||
|
@ -84,7 +159,7 @@ public class Roof extends MinMaxXZ {
|
||||||
for (int x = minx; x <= maxx; x++) {
|
for (int x = minx; x <= maxx; x++) {
|
||||||
for (int z = minz; z <= maxz; z++) {
|
for (int z = minz; z <= maxz; z++) {
|
||||||
// This will push out the coords if possible
|
// This will push out the coords if possible
|
||||||
expandCoords(world, new Vector(x, loc.getBlockY(), z));
|
expandCoords(new Vector(x, loc.getBlockY(), z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Repeat until nothing changes
|
// Repeat until nothing changes
|
||||||
|
@ -93,98 +168,6 @@ public class Roof extends MinMaxXZ {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spiralSearch(Location loc, int roofY) {
|
|
||||||
for (int radius = 0; radius < 3 && !roofFound; radius++) {
|
|
||||||
for (int x = loc.getBlockX() - radius; x <= loc.getBlockX() + radius && !roofFound; x++) {
|
|
||||||
for (int z = loc.getBlockZ() - radius; z <= loc.getBlockZ() + radius && !roofFound; z++) {
|
|
||||||
if (!((x > loc.getBlockX() - radius && x < loc.getBlockX() + radius) && (z > loc.getBlockZ() - radius && z < loc.getBlockZ() + radius))) {
|
|
||||||
checkVertically(loc, x, roofY, z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkVertically(Location loc, int x, int roofY, int z) {
|
|
||||||
World world = loc.getWorld();
|
|
||||||
Block b = world.getBlockAt(x, roofY, z);
|
|
||||||
if (!Walls.wallBlocks(b.getType())) {
|
|
||||||
// Look up
|
|
||||||
for (int y = roofY; y < world.getMaxHeight() && !roofFound; y++) {
|
|
||||||
if (roofBlocks(world.getBlockAt(x,y,z).getType())) {
|
|
||||||
roofFound = true;
|
|
||||||
loc = new Location(world,x,y,z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks
|
|
||||||
* up to 100 in any direction
|
|
||||||
* @param height - location to start search
|
|
||||||
*/
|
|
||||||
private void expandCoords(World world, Vector height) {
|
|
||||||
Location maxx = height.toLocation(world);
|
|
||||||
Location minx = height.toLocation(world);
|
|
||||||
Location maxz = height.toLocation(world);
|
|
||||||
Location minz = height.toLocation(world);
|
|
||||||
int limit = 0;
|
|
||||||
while (ROOF_BLOCKS
|
|
||||||
.contains(world.getBlockAt(maxx).getType()) && limit < 100) {
|
|
||||||
limit++;
|
|
||||||
maxx.add(new Vector(1,0,0));
|
|
||||||
}
|
|
||||||
if (maxx.getBlockX()-1 > maxX) {
|
|
||||||
maxX = maxx.getBlockX()-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (roofBlocks(world.getBlockAt(minx).getType()) && limit < 200) {
|
|
||||||
limit++;
|
|
||||||
minx.subtract(new Vector(1,0,0));
|
|
||||||
}
|
|
||||||
if (minx.getBlockX() + 1 < minX) {
|
|
||||||
minX = minx.getBlockX() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (roofBlocks(world.getBlockAt(maxz).getType()) && limit < 300) {
|
|
||||||
limit++;
|
|
||||||
maxz.add(new Vector(0,0,1));
|
|
||||||
}
|
|
||||||
if (maxz.getBlockZ() - 1 > maxZ) {
|
|
||||||
maxZ = maxz.getBlockZ() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (roofBlocks(world.getBlockAt(minz).getType()) && limit < 400) {
|
|
||||||
limit++;
|
|
||||||
minz.subtract(new Vector(0,0,1));
|
|
||||||
}
|
|
||||||
if (minz.getBlockZ() + 1 < minZ) {
|
|
||||||
minZ = minz.getBlockZ() + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if material is a roof material
|
|
||||||
* @param m - material
|
|
||||||
* @return true if roof material
|
|
||||||
*/
|
|
||||||
public static boolean roofBlocks(Material m) {
|
|
||||||
return ROOF_BLOCKS.contains(m)
|
|
||||||
|| (m.equals(Material.GLOWSTONE) && Greenhouses.getInstance().getSettings().isAllowGlowstone())
|
|
||||||
|| (m.name().endsWith("GLASS_PANE") && Greenhouses.getInstance().getSettings().isAllowPanes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the roofFound
|
|
||||||
*/
|
|
||||||
public boolean isRoofFound() {
|
|
||||||
return roofFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the height
|
* @return the height
|
||||||
*/
|
*/
|
||||||
|
@ -192,6 +175,7 @@ public class Roof extends MinMaxXZ {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the location
|
* @return the location
|
||||||
*/
|
*/
|
||||||
|
@ -199,13 +183,47 @@ public class Roof extends MinMaxXZ {
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void spiralSearch(Vector v, int roofY) {
|
||||||
|
for (int radius = 0; radius < 3 && !roofFound; radius++) {
|
||||||
|
for (int x = v.getBlockX() - radius; x <= v.getBlockX() + radius && !roofFound; x++) {
|
||||||
|
for (int z = v.getBlockZ() - radius; z <= v.getBlockZ() + radius && !roofFound; z++) {
|
||||||
|
if (!((x > v.getBlockX() - radius && x < v.getBlockX() + radius) && (z > v.getBlockZ() - radius && z < v.getBlockZ() + radius))) {
|
||||||
|
checkVertically(v, x, roofY, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
}
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
|
/**
|
||||||
|
* Get highest roof block
|
||||||
|
* @param v - vector of search block
|
||||||
|
* @param x - x coord of current search
|
||||||
|
* @param roofY - roof y coord
|
||||||
|
* @param z - z coord of current search
|
||||||
*/
|
*/
|
||||||
|
private void checkVertically(Vector v, final int x, final int roofY, final int z) {
|
||||||
|
if (!Walls.wallBlocks(cache.getBlockType(x, roofY, z))) {
|
||||||
|
// Look up
|
||||||
|
for (int y = roofY; y < world.getMaxHeight() && !roofFound; y++) {
|
||||||
|
if (roofBlocks(cache.getBlockType(x,y,z))) {
|
||||||
|
roofFound = true;
|
||||||
|
// Move roof up because there is a higher block
|
||||||
|
v = new Vector(x,y,z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Roof [location=" + location + ", minX=" + minX + ", maxX=" + maxX + ", minZ=" + minZ + ", maxZ=" + maxZ
|
return "Roof [" + (cache != null ? "cache=" + cache + ", " : "") + "height=" + height + ", "
|
||||||
+ ", height=" + height + ", roofFound=" + roofFound + "]";
|
+ (location != null ? "location=" + location + ", " : "") + "roofFound=" + roofFound + ", "
|
||||||
|
+ (world != null ? "world=" + world + ", " : "") + "minX=" + minX + ", maxX=" + maxX + ", minZ=" + minZ
|
||||||
|
+ ", maxZ=" + maxZ + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package world.bentobox.greenhouses.greenhouse;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
@ -43,7 +44,12 @@ public class Walls extends MinMaxXZ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Walls findWalls(Roof roof) {
|
/**
|
||||||
|
* Find walls given a roof
|
||||||
|
* @param roof - the roof
|
||||||
|
* @return Future walls
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Walls> findWalls(Roof roof) {
|
||||||
// The player is under the roof
|
// The player is under the roof
|
||||||
// Assume the player is inside the greenhouse they are trying to create
|
// Assume the player is inside the greenhouse they are trying to create
|
||||||
Location loc = roof.getLocation();
|
Location loc = roof.getLocation();
|
||||||
|
@ -65,7 +71,7 @@ public class Walls extends MinMaxXZ {
|
||||||
maxZ++;
|
maxZ++;
|
||||||
// Find the floor again, only looking within the walls
|
// Find the floor again, only looking within the walls
|
||||||
floor = getFloorY(world, roof.getHeight(), minX, maxX, minZ,maxZ);
|
floor = getFloorY(world, roof.getHeight(), minX, maxX, minZ,maxZ);
|
||||||
return this;
|
return CompletableFuture.completedFuture(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lookAround(Location loc, WallFinder wf, Roof roof) {
|
void lookAround(Location loc, WallFinder wf, Roof roof) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package world.bentobox.greenhouses.managers;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
@ -11,10 +12,12 @@ import org.bukkit.World;
|
||||||
import org.bukkit.World.Environment;
|
import org.bukkit.World.Environment;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.greenhouses.data.Greenhouse;
|
import world.bentobox.greenhouses.data.Greenhouse;
|
||||||
import world.bentobox.greenhouses.greenhouse.Roof;
|
import world.bentobox.greenhouses.greenhouse.Roof;
|
||||||
import world.bentobox.greenhouses.greenhouse.Walls;
|
import world.bentobox.greenhouses.greenhouse.Walls;
|
||||||
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
|
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
|
||||||
|
import world.bentobox.greenhouses.world.AsyncWorldCache;
|
||||||
|
|
||||||
public class GreenhouseFinder {
|
public class GreenhouseFinder {
|
||||||
|
|
||||||
|
@ -45,32 +48,50 @@ public class GreenhouseFinder {
|
||||||
/**
|
/**
|
||||||
* Find out if there is a greenhouse here
|
* Find out if there is a greenhouse here
|
||||||
* @param location - start location
|
* @param location - start location
|
||||||
* @return GreenhouseResult class
|
* @return future GreenhouseResult class
|
||||||
*/
|
*/
|
||||||
public Set<GreenhouseResult> find(Location location) {
|
public CompletableFuture<Set<GreenhouseResult>> find(Location location) {
|
||||||
|
CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
|
||||||
Set<GreenhouseResult> result = new HashSet<>();
|
Set<GreenhouseResult> result = new HashSet<>();
|
||||||
redGlass.clear();
|
redGlass.clear();
|
||||||
|
|
||||||
|
// Get a world cache
|
||||||
|
AsyncWorldCache cache = new AsyncWorldCache(location.getWorld());
|
||||||
// Find the roof
|
// Find the roof
|
||||||
Roof roof = new Roof(location);
|
Roof roof = new Roof(cache, location);
|
||||||
if (!roof.isRoofFound()) {
|
roof.findRoof().thenAccept(found -> {
|
||||||
result.add(GreenhouseResult.FAIL_NO_ROOF);
|
if (!found) {
|
||||||
return result;
|
result.add(GreenhouseResult.FAIL_NO_ROOF);
|
||||||
}
|
r.complete(result);
|
||||||
// Find the walls
|
return;
|
||||||
Walls walls = new Walls().findWalls(roof);
|
}
|
||||||
// Make the initial greenhouse
|
BentoBox.getInstance().logDebug(roof);
|
||||||
gh = new Greenhouse(location.getWorld(), walls, roof.getHeight());
|
// Find the walls
|
||||||
// Set the original biome
|
new Walls().findWalls(roof).thenAccept(walls -> {
|
||||||
gh.setOriginalBiome(location.getBlock().getBiome());
|
// Make the initial greenhouse
|
||||||
|
gh = new Greenhouse(location.getWorld(), walls, roof.getHeight());
|
||||||
|
// Set the original biome
|
||||||
|
gh.setOriginalBiome(location.getBlock().getBiome());
|
||||||
|
|
||||||
// Now check to see if the floor really is the floor and the walls follow the rules
|
// Now check to see if the floor really is the floor and the walls follow the rules
|
||||||
result.addAll(checkGreenhouse(gh, roof, walls));
|
checkGreenhouse(gh, roof, walls).thenAccept(c -> {
|
||||||
|
result.addAll(c);
|
||||||
|
r.complete(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
});
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<GreenhouseResult> checkGreenhouse(Greenhouse gh2, Roof roof, Walls walls) {
|
/**
|
||||||
|
* Check the greenhouse has the right number of everything
|
||||||
|
* @param gh2 - greenhouse
|
||||||
|
* @param roof - roof object
|
||||||
|
* @param walls - walls object
|
||||||
|
* @return future set of Greenhouse Results
|
||||||
|
*/
|
||||||
|
CompletableFuture<Set<GreenhouseResult>> checkGreenhouse(Greenhouse gh2, Roof roof, Walls walls) {
|
||||||
Set<GreenhouseResult> result = new HashSet<>();
|
Set<GreenhouseResult> result = new HashSet<>();
|
||||||
World world = roof.getLocation().getWorld();
|
World world = roof.getLocation().getWorld();
|
||||||
int y;
|
int y;
|
||||||
|
@ -101,7 +122,7 @@ public class GreenhouseFinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
result.addAll(checkErrors(roof, y));
|
result.addAll(checkErrors(roof, y));
|
||||||
return result;
|
return CompletableFuture.completedFuture(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<GreenhouseResult> checkErrors(Roof roof, int y) {
|
Collection<GreenhouseResult> checkErrors(Roof roof, int y) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package world.bentobox.greenhouses.managers;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
|
@ -153,29 +154,36 @@ public class GreenhouseManager implements Listener {
|
||||||
* If type is stated then only this specific type will be checked
|
* If type is stated then only this specific type will be checked
|
||||||
* @param location - location to start search from
|
* @param location - location to start search from
|
||||||
* @param greenhouseRecipe - recipe requested, or null for a best-effort search
|
* @param greenhouseRecipe - recipe requested, or null for a best-effort search
|
||||||
* @return - greenhouse result {@link GhResult}
|
* @return - future greenhouse result {@link GhResult}
|
||||||
*/
|
*/
|
||||||
public GhResult tryToMakeGreenhouse(Location location, BiomeRecipe greenhouseRecipe) {
|
public CompletableFuture<GhResult> tryToMakeGreenhouse(Location location, BiomeRecipe greenhouseRecipe) {
|
||||||
|
CompletableFuture<GhResult> r = new CompletableFuture<>();
|
||||||
GreenhouseFinder finder = new GreenhouseFinder();
|
GreenhouseFinder finder = new GreenhouseFinder();
|
||||||
Set<GreenhouseResult> resultSet = finder.find(location);
|
finder.find(location).thenAccept(resultSet -> {
|
||||||
if (!resultSet.isEmpty()) {
|
if (!resultSet.isEmpty()) {
|
||||||
// Failure!
|
// Failure!
|
||||||
return new GhResult().setFinder(finder).setResults(resultSet);
|
r.complete(new GhResult().setFinder(finder).setResults(resultSet));
|
||||||
}
|
return;
|
||||||
// Check if the greenhouse meets the requested recipe
|
}
|
||||||
if (greenhouseRecipe != null) {
|
// Check if the greenhouse meets the requested recipe
|
||||||
resultSet = greenhouseRecipe.checkRecipe(finder.getGh());
|
if (greenhouseRecipe != null) {
|
||||||
if (resultSet.isEmpty()) {
|
checkRecipe(r, finder, greenhouseRecipe, resultSet);
|
||||||
// Success - set recipe and add to map
|
return;
|
||||||
finder.getGh().setBiomeRecipe(greenhouseRecipe);
|
|
||||||
resultSet.add(map.addGreenhouse(finder.getGh()));
|
|
||||||
activateGreenhouse(finder.getGh());
|
|
||||||
handler.saveObjectAsync(finder.getGh());
|
|
||||||
}
|
}
|
||||||
return new GhResult().setFinder(finder).setResults(resultSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try ordered recipes
|
// Try ordered recipes
|
||||||
|
findRecipe(finder, resultSet);
|
||||||
|
r.complete(new GhResult().setFinder(finder).setResults(resultSet));
|
||||||
|
});
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to match the greenhouse to a recipe by going through all of them in order
|
||||||
|
* @param finder - finder object
|
||||||
|
* @param resultSet - result set from find
|
||||||
|
*/
|
||||||
|
private void findRecipe(GreenhouseFinder finder, Set<GreenhouseResult> resultSet) {
|
||||||
resultSet.add(addon.getRecipes().getBiomeRecipes().stream().sorted()
|
resultSet.add(addon.getRecipes().getBiomeRecipes().stream().sorted()
|
||||||
.filter(r -> r.checkRecipe(finder.getGh()).isEmpty()).findFirst()
|
.filter(r -> r.checkRecipe(finder.getGh()).isEmpty()).findFirst()
|
||||||
.map(r -> {
|
.map(r -> {
|
||||||
|
@ -185,7 +193,29 @@ public class GreenhouseManager implements Listener {
|
||||||
handler.saveObjectAsync(finder.getGh());
|
handler.saveObjectAsync(finder.getGh());
|
||||||
return map.addGreenhouse(finder.getGh());
|
return map.addGreenhouse(finder.getGh());
|
||||||
}).orElse(GreenhouseResult.FAIL_NO_RECIPE_FOUND));
|
}).orElse(GreenhouseResult.FAIL_NO_RECIPE_FOUND));
|
||||||
return new GhResult().setFinder(finder).setResults(resultSet);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if the greenhouse meets the designated recipe and returns the result
|
||||||
|
* @param r - completable future
|
||||||
|
* @param finder - finder object
|
||||||
|
* @param greenhouseRecipe - recipe requested
|
||||||
|
* @param resultSet - result set from finder
|
||||||
|
* @return Greenhouse result
|
||||||
|
*/
|
||||||
|
GhResult checkRecipe(CompletableFuture<GhResult> r, GreenhouseFinder finder, BiomeRecipe greenhouseRecipe, Set<GreenhouseResult> resultSet) {
|
||||||
|
resultSet = greenhouseRecipe.checkRecipe(finder.getGh());
|
||||||
|
if (resultSet.isEmpty()) {
|
||||||
|
// Success - set recipe and add to map
|
||||||
|
finder.getGh().setBiomeRecipe(greenhouseRecipe);
|
||||||
|
resultSet.add(map.addGreenhouse(finder.getGh()));
|
||||||
|
activateGreenhouse(finder.getGh());
|
||||||
|
handler.saveObjectAsync(finder.getGh());
|
||||||
|
}
|
||||||
|
GhResult recipe = new GhResult().setFinder(finder).setResults(resultSet);
|
||||||
|
r.complete(recipe);
|
||||||
|
return recipe;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void activateGreenhouse(Greenhouse gh) {
|
private void activateGreenhouse(Greenhouse gh) {
|
||||||
|
|
|
@ -55,12 +55,15 @@ public class PanelClick implements ClickHandler {
|
||||||
user.sendMessage("greenhouses.commands.user.make.error.already");
|
user.sendMessage("greenhouses.commands.user.make.error.already");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GhResult result = addon.getManager().tryToMakeGreenhouse(location, br);
|
addon.getManager().tryToMakeGreenhouse(location, br).thenAccept(r -> processResult(user, r));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processResult(User user, GhResult result) {
|
||||||
if (result.getResults().contains(GreenhouseResult.SUCCESS)) {
|
if (result.getResults().contains(GreenhouseResult.SUCCESS)) {
|
||||||
// Success
|
// Success
|
||||||
user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName());
|
user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName());
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
result.getResults().forEach(r -> user.sendMessage("greenhouses.commands.user.make.error." + r.name()));
|
result.getResults().forEach(r -> user.sendMessage("greenhouses.commands.user.make.error." + r.name()));
|
||||||
if (!result.getFinder().getRedGlass().isEmpty()) {
|
if (!result.getFinder().getRedGlass().isEmpty()) {
|
||||||
|
@ -69,8 +72,7 @@ public class PanelClick implements ClickHandler {
|
||||||
Bukkit.getScheduler().runTaskLater(addon.getPlugin(), () -> result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg, rg.getBlock().getBlockData())), 120L);
|
Bukkit.getScheduler().runTaskLater(addon.getPlugin(), () -> result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg, rg.getBlock().getBlockData())), 120L);
|
||||||
}
|
}
|
||||||
if (result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) {
|
if (result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) {
|
||||||
result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v)));
|
result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v)));
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ class MakeCommand extends CompositeCommand {
|
||||||
return getRecipes(user).get(arg);
|
return getRecipes(user).get(arg);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get a string list of recipies the player has permission to use
|
* Get a string list of recipes the player has permission to use
|
||||||
* @param user - user
|
* @param user - user
|
||||||
* @return list
|
* @return list
|
||||||
*/
|
*/
|
||||||
|
@ -99,7 +99,12 @@ class MakeCommand extends CompositeCommand {
|
||||||
user.sendMessage("greenhouses.commands.user.make.error.already");
|
user.sendMessage("greenhouses.commands.user.make.error.already");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GhResult result = ((Greenhouses)this.getAddon()).getManager().tryToMakeGreenhouse(location, br);
|
// Try to make the greenhouse
|
||||||
|
((Greenhouses)this.getAddon()).getManager().tryToMakeGreenhouse(location, br).thenAccept(result -> informUser(user, br, result));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean informUser(User user, BiomeRecipe br, GhResult result) {
|
||||||
if (result.getResults().contains(GreenhouseResult.SUCCESS)) {
|
if (result.getResults().contains(GreenhouseResult.SUCCESS)) {
|
||||||
// Success
|
// Success
|
||||||
user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName());
|
user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName());
|
||||||
|
@ -114,7 +119,7 @@ class MakeCommand extends CompositeCommand {
|
||||||
if (br != null && result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) {
|
if (br != null && result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) {
|
||||||
result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v)));
|
result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v)));
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package world.bentobox.greenhouses.world;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChunkSnapshot;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.util.Pair;
|
||||||
|
import world.bentobox.bentobox.util.Util;
|
||||||
|
import world.bentobox.greenhouses.Greenhouses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a thread-safe cache world chunks
|
||||||
|
* @author tastybento
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AsyncWorldCache {
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
private final Map<Pair<Integer, Integer>, ChunkSnapshot> cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunk cache. This class is designed to be run async and blocks futures
|
||||||
|
* @param world - world to cache
|
||||||
|
*/
|
||||||
|
public AsyncWorldCache(World world) {
|
||||||
|
this.world = world;
|
||||||
|
cache = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get chunk snapshot from world
|
||||||
|
* @param x - coord
|
||||||
|
* @param z - coord
|
||||||
|
* @return future chunk snapshot
|
||||||
|
*/
|
||||||
|
private CompletableFuture<ChunkSnapshot> getAChunk(int x, int z) {
|
||||||
|
CompletableFuture<ChunkSnapshot> r = new CompletableFuture<>();
|
||||||
|
Bukkit.getScheduler().runTask(Greenhouses.getInstance().getPlugin(), () ->
|
||||||
|
Util.getChunkAtAsync(world, x, z).thenAccept(chunk -> r.complete(chunk.getChunkSnapshot())));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get snapshot from cache or world
|
||||||
|
* @param x - block coord
|
||||||
|
* @param z - block coord
|
||||||
|
* @return chunk snapshot
|
||||||
|
*/
|
||||||
|
private ChunkSnapshot getSnap(int x, int z) {
|
||||||
|
// Convert from block to chunk coords
|
||||||
|
Pair<Integer, Integer> key = new Pair<>((x >> 4), (z >> 4));
|
||||||
|
// Get from cache if it is available
|
||||||
|
if (cache.containsKey(key)) {
|
||||||
|
return cache.get(key);
|
||||||
|
}
|
||||||
|
ChunkSnapshot cs;
|
||||||
|
try {
|
||||||
|
// Block on getting the chunk because this is running async
|
||||||
|
cs = getAChunk(key.x, key.z).get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
// Try again...
|
||||||
|
return getSnap(x,z);
|
||||||
|
}
|
||||||
|
// Store in cache
|
||||||
|
cache.put(key, cs);
|
||||||
|
return cs;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get block material for block at corresponding coordinates
|
||||||
|
*
|
||||||
|
* @param x block coordinate
|
||||||
|
* @param y 0-255
|
||||||
|
* @param z block coordinate
|
||||||
|
* @return material type
|
||||||
|
*/
|
||||||
|
public Material getBlockType(int x, int y, int z) {
|
||||||
|
// Convert block coords to chunk coords
|
||||||
|
int xx = x >= 0 ? x % 16 : 15 + (x % 16);
|
||||||
|
int zz = z >= 0 ? z % 16 : 15 + (z % 16);
|
||||||
|
Material m = getSnap(x,z).getBlockType(xx, y, zz);
|
||||||
|
BentoBox.getInstance().logDebug(m);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get block material for block at corresponding coordinates
|
||||||
|
* @param v - vector
|
||||||
|
* @return Material
|
||||||
|
*/
|
||||||
|
public Material getBlockType(Vector v) {
|
||||||
|
return getBlockType(v.getBlockX(), v.getBlockY(), v.getBlockZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import org.powermock.modules.junit4.PowerMockRunner;
|
||||||
|
|
||||||
import world.bentobox.greenhouses.Greenhouses;
|
import world.bentobox.greenhouses.Greenhouses;
|
||||||
import world.bentobox.greenhouses.Settings;
|
import world.bentobox.greenhouses.Settings;
|
||||||
|
import world.bentobox.greenhouses.world.AsyncWorldCache;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,6 +43,7 @@ public class RoofTest {
|
||||||
@Mock
|
@Mock
|
||||||
private Greenhouses addon;
|
private Greenhouses addon;
|
||||||
private Settings s;
|
private Settings s;
|
||||||
|
private AsyncWorldCache cache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws java.lang.Exception
|
* @throws java.lang.Exception
|
||||||
|
@ -89,15 +91,17 @@ public class RoofTest {
|
||||||
when(location.getBlock()).thenReturn(block);
|
when(location.getBlock()).thenReturn(block);
|
||||||
when(location.clone()).thenReturn(location);
|
when(location.clone()).thenReturn(location);
|
||||||
|
|
||||||
|
cache = new AsyncWorldCache(world);
|
||||||
// Test
|
// Test
|
||||||
roof = new Roof(location);
|
roof = new Roof(cache, location);
|
||||||
|
roof.findRoof();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoGlass() {
|
public void testNoGlass() {
|
||||||
when(block.getType()).thenReturn(Material.AIR);
|
when(block.getType()).thenReturn(Material.AIR);
|
||||||
roof = new Roof(location);
|
roof = new Roof(cache, location);
|
||||||
|
roof.findRoof();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,14 +144,6 @@ public class RoofTest {
|
||||||
assertEquals(1406, roof.getArea());
|
assertEquals(1406, roof.getArea());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#isRoofFound()}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testIsRoofFound() {
|
|
||||||
assertTrue(roof.isRoofFound());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#getHeight()}.
|
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#getHeight()}.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -107,11 +107,12 @@ public class GreenhouseFinderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCheckGreenhouse() {
|
public void testCheckGreenhouse() {
|
||||||
Greenhouse gh2 = new Greenhouse(world, walls, ROOF_HEIGHT);
|
Greenhouse gh2 = new Greenhouse(world, walls, ROOF_HEIGHT);
|
||||||
Set<GreenhouseResult> result = gf.checkGreenhouse(gh2, roof, walls);
|
gf.checkGreenhouse(gh2, roof, walls).thenAccept(result -> {
|
||||||
assertTrue(result.isEmpty()); // Success
|
assertTrue(result.isEmpty()); // Success
|
||||||
assertEquals(441, gf.getWallBlockCount());
|
assertEquals(441, gf.getWallBlockCount());
|
||||||
assertEquals(0, gf.getWallDoors());
|
assertEquals(0, gf.getWallDoors());
|
||||||
assertEquals(0, gf.getGhHopper());
|
assertEquals(0, gf.getGhHopper());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue