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.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.util.Vector; 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 { private Greenhouse gh; private final Set redGlass = new HashSet<>(); // Ceiling issue private boolean inCeiling = false; // The y height where other blocks were found // If this is the bottom layer, the player has most likely uneven walls private int otherBlockLayer = -1; private int wallBlockCount; /** * This is the count of the various items */ private CounterCheck cc = new CounterCheck(); static class CounterCheck { int doorCount; int hopperCount; boolean airHole; boolean otherBlock; } /** * Find out if there is a greenhouse here * @param location - start location * @return future GreenhouseResult class */ public CompletableFuture> find(Location location) { CompletableFuture> r = new CompletableFuture<>(); Set result = new HashSet<>(); redGlass.clear(); // Get a world cache AsyncWorldCache cache = new AsyncWorldCache(location.getWorld()); // Find the roof Roof roof = new Roof(cache, location); roof.findRoof().thenAccept(found -> { if (Boolean.FALSE.equals(found)) { result.add(GreenhouseResult.FAIL_NO_ROOF); r.complete(result); return; } // Find the walls new Walls(cache).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 checkGreenhouse(cache, roof, walls).thenAccept(c -> { result.addAll(c); r.complete(result); }); }); }); return r; } /** * Check the greenhouse has the right number of everything * @param cache async world cache * @param roof - roof object * @param walls - walls object * @return future set of Greenhouse Results */ CompletableFuture> checkGreenhouse(AsyncWorldCache cache, Roof roof, Walls walls) { CompletableFuture> r = new CompletableFuture<>(); Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> checkGHAsync(r, cache, roof, walls)); return r; } private Set checkGHAsync(CompletableFuture> r, AsyncWorldCache cache, Roof roof, Walls walls) { cc = new CounterCheck(); int y; for (y = roof.getHeight(); y > walls.getFloor(); y--) { wallBlockCount = 0; for (int x = walls.getMinX(); x <= walls.getMaxX(); x++) { for (int z = walls.getMinZ(); z <= walls.getMaxZ(); z++) { checkBlock(cc, cache.getBlockType(x,y,z), roof, walls, new Vector(x, y, z)); } } if (wallBlockCount == 0 && y < roof.getHeight()) { // This is the floor break; } else { if (cc.otherBlock) { if (otherBlockLayer < 0) { otherBlockLayer = y; } } } } Set result = new HashSet<>(checkErrors(roof, y)); Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> r.complete(result)); return result; } Collection checkErrors(Roof roof, int y) { Set result = new HashSet<>(); // Check that the player is vertically in the greenhouse if (roof.getLocation().getBlockY() <= y) { result.add(GreenhouseResult.FAIL_BELOW); } // Show errors if (isAirHoles() && !inCeiling) { result.add(GreenhouseResult.FAIL_HOLE_IN_WALL); } else if (isAirHoles() && inCeiling) { result.add(GreenhouseResult.FAIL_HOLE_IN_ROOF); } if (isOtherBlocks() && otherBlockLayer == y + 1) { // Walls must be even all the way around result.add(GreenhouseResult.FAIL_UNEVEN_WALLS); } else if (isOtherBlocks() && otherBlockLayer == roof.getHeight()) { // Roof blocks must be glass, glowstone, doors or a hopper. result.add(GreenhouseResult.FAIL_BAD_ROOF_BLOCKS); } else if (isOtherBlocks()) { // "Wall blocks must be glass, glowstone, doors or a hopper. result.add(GreenhouseResult.FAIL_BAD_WALL_BLOCKS); } if (this.getWallDoors() > 8) { result.add(GreenhouseResult.FAIL_TOO_MANY_DOORS); } if (this.getGhHopper() > 1) { result.add(GreenhouseResult.FAIL_TOO_MANY_HOPPERS); } return result; } /** * Check if block is allowed to be in that location * @param cc - Counter Check object * @param m - material of the block * @param roof - roof object * @param walls - walls object * @param v - vector location of the block * @return true if block is acceptable, false if not */ boolean checkBlock(CounterCheck cc, Material m, Roof roof, Walls walls, Vector v) { final int x = v.getBlockX(); final int y = v.getBlockY(); final int z = v.getBlockZ(); // Check wall blocks only if (y == roof.getHeight() || x == walls.getMinX() || x == walls.getMaxX() || z == walls.getMinZ() || z== walls.getMaxZ()) { // Check for non-wall blocks or non-roof blocks at the top of walls if ((y != roof.getHeight() && !Walls.wallBlocks(m)) || (y == roof.getHeight() && !Roof.roofBlocks(m))) { if (m.equals(Material.AIR)) { // Air hole found cc.airHole = true; if (y == roof.getHeight()) { // Air hole is in ceiling inCeiling = true; } } else { // A non-wall or roof block found cc.otherBlock = true; } // Record the incorrect location redGlass.add(v); return false; } else { // Normal wall blocks wallBlockCount++; return checkDoorsHoppers(cc, m, v); } } return true; } /** * Check the count of doors and hopper and set the hopper location if it is found * @param cc counter check * @param m material of block * @param v vector position of block * @return false if there is an error, true if ok */ boolean checkDoorsHoppers(CounterCheck cc, Material m, Vector v) { // Count doors if (Tag.TRAPDOORS.isTagged(m) || Tag.DOORS.isTagged(m)) { cc.doorCount = Tag.TRAPDOORS.isTagged(m) ? cc.doorCount + 2 : cc.doorCount + 1; // If we already have 8 doors add these blocks to the red list if (cc.doorCount > 8) { redGlass.add(v); return false; } } // Count hoppers if (m.equals(Material.HOPPER)) { cc.hopperCount++; if (cc.hopperCount > 1) { // Problem! Add extra hoppers to the red glass list redGlass.add(v); return false; } else { // This is the first hopper gh.setRoofHopperLocation(v); } } return true; } /** * @return the greenhouse */ public Greenhouse getGh() { return gh; } /** * @return the redGlass */ public Set getRedGlass() { return redGlass; } /** * @return the wallDoors */ int getWallDoors() { return cc.doorCount; } /** * @return the ghHopper */ int getGhHopper() { return cc.hopperCount; } /** * @return the airHoles */ boolean isAirHoles() { return cc.airHole; } /** * @return the otherBlocks */ boolean isOtherBlocks() { return cc.otherBlock; } /** * @return the inCeiling */ boolean isInCeiling() { return inCeiling; } /** * @return the otherBlockLayer */ int getOtherBlockLayer() { return otherBlockLayer; } /** * @return the wallBlockCount */ int getWallBlockCount() { return wallBlockCount; } /** * @param inCeiling the inCeiling to set */ void setInCeiling(boolean inCeiling) { this.inCeiling = inCeiling; } /** * @param otherBlockLayer the otherBlockLayer to set */ void setOtherBlockLayer(int otherBlockLayer) { this.otherBlockLayer = otherBlockLayer; } /** * @param wallBlockCount the wallBlockCount to set */ void setWallBlockCount(int wallBlockCount) { this.wallBlockCount = wallBlockCount; } /** * @param gh the gh to set */ protected void setGh(Greenhouse gh) { this.gh = gh; } public void setGhHopper(int i) { cc.hopperCount = i; } public void setWallDoors(int i) { cc.doorCount = i; } public void setAirHoles(boolean b) { cc.airHole = b; } public void setOtherBlocks(boolean b) { cc.otherBlock = b; } }