mirror of
https://github.com/BentoBoxWorld/Greenhouses.git
synced 2024-11-22 02:25:50 +01:00
Made roof search async
This commit is contained in:
parent
08e9c25172
commit
cc114027da
@ -46,4 +46,11 @@ public abstract class MinMaxXZ {
|
||||
public int getArea() {
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.greenhouses.Greenhouses;
|
||||
import world.bentobox.greenhouses.world.AsyncWorldCache;
|
||||
|
||||
/**
|
||||
* Contains the parameters of a greenhouse roof
|
||||
@ -19,6 +22,7 @@ import world.bentobox.greenhouses.Greenhouses;
|
||||
*
|
||||
*/
|
||||
public class Roof extends MinMaxXZ {
|
||||
private static final BentoBox PLUGIN = Greenhouses.getInstance().getPlugin();
|
||||
private static final List<Material> ROOF_BLOCKS;
|
||||
static {
|
||||
List<Material> r = Arrays.stream(Material.values())
|
||||
@ -29,40 +33,111 @@ public class Roof extends MinMaxXZ {
|
||||
.collect(Collectors.toList());
|
||||
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 final Location location;
|
||||
private boolean roofFound;
|
||||
|
||||
private final World world;
|
||||
|
||||
|
||||
/**
|
||||
* Finds a roof from a starting location under the roof and characterizes it
|
||||
* @param cache
|
||||
* @param loc - starting location
|
||||
*/
|
||||
public Roof(Location loc) {
|
||||
public Roof(AsyncWorldCache cache, Location loc) {
|
||||
this.cache = cache;
|
||||
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
|
||||
// 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
|
||||
// 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();
|
||||
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;
|
||||
loc = new Location(world,loc.getBlockX(),y,loc.getBlockZ());
|
||||
loc = new Vector(loc.getBlockX(),y,loc.getBlockZ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 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
|
||||
spiralSearch(loc, roofY);
|
||||
if (!roofFound) return false;
|
||||
if (!roofFound) {
|
||||
return false;
|
||||
}
|
||||
// Record the height
|
||||
this.height = loc.getBlockY();
|
||||
// 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();
|
||||
minZ = loc.getBlockZ();
|
||||
maxZ = loc.getBlockZ();
|
||||
expandCoords(world, loc.toVector());
|
||||
expandCoords(loc);
|
||||
int minx;
|
||||
int maxx;
|
||||
int minz;
|
||||
@ -84,7 +159,7 @@ public class Roof extends MinMaxXZ {
|
||||
for (int x = minx; x <= maxx; x++) {
|
||||
for (int z = minz; z <= maxz; z++) {
|
||||
// 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
|
||||
@ -93,98 +168,6 @@ public class Roof extends MinMaxXZ {
|
||||
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
|
||||
*/
|
||||
@ -192,6 +175,7 @@ public class Roof extends MinMaxXZ {
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the location
|
||||
*/
|
||||
@ -199,13 +183,47 @@ public class Roof extends MinMaxXZ {
|
||||
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
|
||||
public String toString() {
|
||||
return "Roof [location=" + location + ", minX=" + minX + ", maxX=" + maxX + ", minZ=" + minZ + ", maxZ=" + maxZ
|
||||
+ ", height=" + height + ", roofFound=" + roofFound + "]";
|
||||
return "Roof [" + (cache != null ? "cache=" + cache + ", " : "") + "height=" + height + ", "
|
||||
+ (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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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
|
||||
// Assume the player is inside the greenhouse they are trying to create
|
||||
Location loc = roof.getLocation();
|
||||
@ -65,7 +71,7 @@ public class Walls extends MinMaxXZ {
|
||||
maxZ++;
|
||||
// Find the floor again, only looking within the walls
|
||||
floor = getFloorY(world, roof.getHeight(), minX, maxX, minZ,maxZ);
|
||||
return this;
|
||||
return CompletableFuture.completedFuture(this);
|
||||
}
|
||||
|
||||
void lookAround(Location loc, WallFinder wf, Roof roof) {
|
||||
|
@ -3,6 +3,7 @@ package world.bentobox.greenhouses.managers;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@ -11,10 +12,12 @@ import org.bukkit.World;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.greenhouses.data.Greenhouse;
|
||||
import world.bentobox.greenhouses.greenhouse.Roof;
|
||||
import world.bentobox.greenhouses.greenhouse.Walls;
|
||||
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
|
||||
import world.bentobox.greenhouses.world.AsyncWorldCache;
|
||||
|
||||
public class GreenhouseFinder {
|
||||
|
||||
@ -45,32 +48,50 @@ public class GreenhouseFinder {
|
||||
/**
|
||||
* Find out if there is a greenhouse here
|
||||
* @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<>();
|
||||
redGlass.clear();
|
||||
|
||||
// Get a world cache
|
||||
AsyncWorldCache cache = new AsyncWorldCache(location.getWorld());
|
||||
// Find the roof
|
||||
Roof roof = new Roof(location);
|
||||
if (!roof.isRoofFound()) {
|
||||
result.add(GreenhouseResult.FAIL_NO_ROOF);
|
||||
return result;
|
||||
}
|
||||
// Find the walls
|
||||
Walls walls = new Walls().findWalls(roof);
|
||||
// Make the initial greenhouse
|
||||
gh = new Greenhouse(location.getWorld(), walls, roof.getHeight());
|
||||
// Set the original biome
|
||||
gh.setOriginalBiome(location.getBlock().getBiome());
|
||||
Roof roof = new Roof(cache, location);
|
||||
roof.findRoof().thenAccept(found -> {
|
||||
if (!found) {
|
||||
result.add(GreenhouseResult.FAIL_NO_ROOF);
|
||||
r.complete(result);
|
||||
return;
|
||||
}
|
||||
BentoBox.getInstance().logDebug(roof);
|
||||
// Find the walls
|
||||
new Walls().findWalls(roof).thenAccept(walls -> {
|
||||
// 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
|
||||
result.addAll(checkGreenhouse(gh, roof, walls));
|
||||
// Now check to see if the floor really is the floor and the walls follow the rules
|
||||
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<>();
|
||||
World world = roof.getLocation().getWorld();
|
||||
int y;
|
||||
@ -101,7 +122,7 @@ public class GreenhouseFinder {
|
||||
}
|
||||
|
||||
result.addAll(checkErrors(roof, y));
|
||||
return result;
|
||||
return CompletableFuture.completedFuture(result);
|
||||
}
|
||||
|
||||
Collection<GreenhouseResult> checkErrors(Roof roof, int y) {
|
||||
|
@ -3,6 +3,7 @@ package world.bentobox.greenhouses.managers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.Location;
|
||||
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
|
||||
* @param location - location to start search from
|
||||
* @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();
|
||||
Set<GreenhouseResult> resultSet = finder.find(location);
|
||||
if (!resultSet.isEmpty()) {
|
||||
// Failure!
|
||||
return new GhResult().setFinder(finder).setResults(resultSet);
|
||||
}
|
||||
// Check if the greenhouse meets the requested recipe
|
||||
if (greenhouseRecipe != null) {
|
||||
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());
|
||||
finder.find(location).thenAccept(resultSet -> {
|
||||
if (!resultSet.isEmpty()) {
|
||||
// Failure!
|
||||
r.complete(new GhResult().setFinder(finder).setResults(resultSet));
|
||||
return;
|
||||
}
|
||||
// Check if the greenhouse meets the requested recipe
|
||||
if (greenhouseRecipe != null) {
|
||||
checkRecipe(r, finder, greenhouseRecipe, resultSet);
|
||||
return;
|
||||
}
|
||||
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()
|
||||
.filter(r -> r.checkRecipe(finder.getGh()).isEmpty()).findFirst()
|
||||
.map(r -> {
|
||||
@ -185,7 +193,29 @@ public class GreenhouseManager implements Listener {
|
||||
handler.saveObjectAsync(finder.getGh());
|
||||
return map.addGreenhouse(finder.getGh());
|
||||
}).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) {
|
||||
|
@ -55,12 +55,15 @@ public class PanelClick implements ClickHandler {
|
||||
user.sendMessage("greenhouses.commands.user.make.error.already");
|
||||
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)) {
|
||||
// Success
|
||||
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()));
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
* @return list
|
||||
*/
|
||||
@ -99,7 +99,12 @@ class MakeCommand extends CompositeCommand {
|
||||
user.sendMessage("greenhouses.commands.user.make.error.already");
|
||||
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)) {
|
||||
// Success
|
||||
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)) {
|
||||
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
|
||||
|
@ -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.Settings;
|
||||
import world.bentobox.greenhouses.world.AsyncWorldCache;
|
||||
|
||||
|
||||
/**
|
||||
@ -42,6 +43,7 @@ public class RoofTest {
|
||||
@Mock
|
||||
private Greenhouses addon;
|
||||
private Settings s;
|
||||
private AsyncWorldCache cache;
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
@ -89,15 +91,17 @@ public class RoofTest {
|
||||
when(location.getBlock()).thenReturn(block);
|
||||
when(location.clone()).thenReturn(location);
|
||||
|
||||
cache = new AsyncWorldCache(world);
|
||||
// Test
|
||||
roof = new Roof(location);
|
||||
roof = new Roof(cache, location);
|
||||
roof.findRoof();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoGlass() {
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()}.
|
||||
*/
|
||||
|
@ -107,11 +107,12 @@ public class GreenhouseFinderTest {
|
||||
@Test
|
||||
public void testCheckGreenhouse() {
|
||||
Greenhouse gh2 = new Greenhouse(world, walls, ROOF_HEIGHT);
|
||||
Set<GreenhouseResult> result = gf.checkGreenhouse(gh2, roof, walls);
|
||||
assertTrue(result.isEmpty()); // Success
|
||||
assertEquals(441, gf.getWallBlockCount());
|
||||
assertEquals(0, gf.getWallDoors());
|
||||
assertEquals(0, gf.getGhHopper());
|
||||
gf.checkGreenhouse(gh2, roof, walls).thenAccept(result -> {
|
||||
assertTrue(result.isEmpty()); // Success
|
||||
assertEquals(441, gf.getWallBlockCount());
|
||||
assertEquals(0, gf.getWallDoors());
|
||||
assertEquals(0, gf.getGhHopper());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user