Version 1.7.4 (#109)

* Version 1.7.3

* Add ${argLine} to get jacoco coverage

* Updated Jacoco POM entry

* Address bugs reported by SonarCloud

* Updated ReadMe

* Add max mobs option #99

* Use updated Bucket event

* Added tests to cover #99

* Fixes help text for user command.

Fixes #105

* Fixed maxmobs typo instead of maxmob

* Remove unused imports

* Update to new Bukkit Loader

* Remove debug

* Create plugin.yml (#106)

* Create plugin.yml

* Update pom.xml

* Update GreenhousesPladdon.java

* Removed static getInstance usage

* Version 1.7.4

* Refactored to reduce complexity

* Update surefire plugin

* Refactored to reduce complexity

* Minor typos and grammar fixes

* Reduced complexity

* Refactor to reduce complexity

* Refactor to reduce complexity

* Update Github Action build script

* Added distribution required for Github Action

* Update pom.xml

* Fixes mob spawning when no maxmob value given.

Found while doing #108

* Code clean up.

---------

Co-authored-by: BONNe <bonne@bonne.id.lv>
This commit is contained in:
tastybento 2023-09-19 22:18:57 -07:00 committed by GitHub
parent 5fd9cbfd36
commit 4084876c4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 309 additions and 296 deletions

View File

@ -11,21 +11,22 @@ jobs:
name: Build name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
distribution: 'adopt'
java-version: 17 java-version: 17
- name: Cache SonarCloud packages - name: Cache SonarCloud packages
uses: actions/cache@v1 uses: actions/cache@v3
with: with:
path: ~/.sonar/cache path: ~/.sonar/cache
key: ${{ runner.os }}-sonar key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages - name: Cache Maven packages
uses: actions/cache@v1 uses: actions/cache@v3
with: with:
path: ~/.m2 path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

View File

@ -15,7 +15,7 @@ Greenhouses are made out of glass and must contain the blocks found in the Biome
* Craft your own self-contained biome greenhouse on an island (or elsewhere if you like) * Craft your own self-contained biome greenhouse on an island (or elsewhere if you like)
* Greenhouses can grow plants that cannot normally be grown, like sunflowers * Greenhouses can grow plants that cannot normally be grown, like sunflowers
* Friendly mobs can spawn if your greenhouse is well designed - need slimes? Build a swamp greenhouse! * Friendly mobs can spawn if your greenhouse is well-designed - need slimes? Build a swamp greenhouse!
* Blocks change in biomes over time - dirt becomes sand in a desert, dirt becomes clay in a river, for example. * Blocks change in biomes over time - dirt becomes sand in a desert, dirt becomes clay in a river, for example.
* Greenhouses can run in multiple worlds. * Greenhouses can run in multiple worlds.
* Easy to use GUI shows greenhouse recipes (e.g. **/is greenhouses**) * Easy to use GUI shows greenhouse recipes (e.g. **/is greenhouses**)
@ -27,7 +27,7 @@ This example is for when you are in the BSkyBlock world. For AcidIsland, just us
1. Make glass blocks and build a rectangular set of walls with a flat roof. 1. Make glass blocks and build a rectangular set of walls with a flat roof.
2. Put a hopper in the wall or roof. 2. Put a hopper in the wall or roof.
3. Put a door in the wall so you can get in and out. 3. Put a door in the wall, so you can get in and out.
4. Type **/island greenhouses** and read the rules for the greenhouse you want. 4. Type **/island greenhouses** and read the rules for the greenhouse you want.
5. Exit the GUI and place blocks, water, lava, and ice so that you make your desired biome. 5. Exit the GUI and place blocks, water, lava, and ice so that you make your desired biome.
6. Type **/island greenhouses** again and click on the biome to make it. 6. Type **/island greenhouses** again and click on the biome to make it.
@ -42,7 +42,7 @@ This example is for when you are in the BSkyBlock world. For AcidIsland, just us
## FAQ ## FAQ
* Can I use stained glass? Yes, you can. It's pretty. * Can I use stained-glass? Yes, you can. It's pretty.
* Can I fill my greenhouse full of water? Yes. That's an ocean. * Can I fill my greenhouse full of water? Yes. That's an ocean.
* Will a squid spawn there? Maybe... okay, yes it will if it's a big enough ocean. * Will a squid spawn there? Maybe... okay, yes it will if it's a big enough ocean.
* How do I place a door high up in the wall if the wall is all glass? Place it on a hopper. * How do I place a door high up in the wall if the wall is all glass? Place it on a hopper.

View File

@ -51,7 +51,7 @@
<!-- Revision variable removes warning about dynamic version --> <!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision> <revision>${build.version}-SNAPSHOT</revision>
<!-- This allows to change between versions and snapshots. --> <!-- This allows to change between versions and snapshots. -->
<build.version>1.7.3</build.version> <build.version>1.7.4</build.version>
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<sonar.projectKey>BentoBoxWorld_Greenhouses</sonar.projectKey> <sonar.projectKey>BentoBoxWorld_Greenhouses</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization> <sonar.organization>bentobox-world</sonar.organization>
@ -191,7 +191,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version> <version>3.1.0</version>
<configuration> <configuration>
<argLine> <argLine>
${argLine} ${argLine}
@ -252,7 +252,7 @@
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version> <version>0.8.10</version>
<configuration> <configuration>
<append>true</append> <append>true</append>
<excludes> <excludes>

View File

@ -11,6 +11,7 @@ import world.bentobox.bentobox.api.configuration.Config;
import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.Flag.Mode; import world.bentobox.bentobox.api.flags.Flag.Mode;
import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.flags.Flag.Type;
import world.bentobox.greenhouses.greenhouse.Walls;
import world.bentobox.greenhouses.managers.GreenhouseManager; import world.bentobox.greenhouses.managers.GreenhouseManager;
import world.bentobox.greenhouses.managers.RecipeManager; import world.bentobox.greenhouses.managers.RecipeManager;
import world.bentobox.greenhouses.ui.user.UserCommand; import world.bentobox.greenhouses.ui.user.UserCommand;
@ -28,19 +29,13 @@ public class Greenhouses extends Addon {
public static final Flag GREENHOUSES = new Flag.Builder("GREENHOUSE", Material.GREEN_STAINED_GLASS) public static final Flag GREENHOUSES = new Flag.Builder("GREENHOUSE", Material.GREEN_STAINED_GLASS)
.mode(Mode.BASIC) .mode(Mode.BASIC)
.type(Type.PROTECTION).build(); .type(Type.PROTECTION).build();
private static Greenhouses instance;
private final Config<Settings> config; private final Config<Settings> config;
public static Greenhouses getInstance() {
return instance;
}
/** /**
* Constructor * Constructor
*/ */
public Greenhouses() { public Greenhouses() {
super(); super();
instance = this;
config = new Config<>(this, Settings.class); config = new Config<>(this, Settings.class);
} }
@ -122,4 +117,15 @@ public class Greenhouses extends Addon {
return activeWorlds; return activeWorlds;
} }
/**
* Check if material is a wall material
* @param m - material
* @return true if wall material
*/
public boolean wallBlocks(Material m) {
return Walls.WALL_BLOCKS.contains(m)
|| (m.equals(Material.GLOWSTONE) && getSettings().isAllowGlowstone())
|| (m.name().endsWith("GLASS_PANE") && getSettings().isAllowPanes());
}
} }

View File

@ -209,24 +209,30 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
* @return set of results from the check * @return set of results from the check
*/ */
private Set<GreenhouseResult> checkRecipeAsync(CompletableFuture<Set<GreenhouseResult>> r, Greenhouse gh) { private Set<GreenhouseResult> checkRecipeAsync(CompletableFuture<Set<GreenhouseResult>> r, Greenhouse gh) {
AsyncWorldCache cache = new AsyncWorldCache(gh.getWorld()); AsyncWorldCache cache = new AsyncWorldCache(addon, gh.getWorld());
Set<GreenhouseResult> result = new HashSet<>();
long area = gh.getArea(); long area = gh.getArea();
Map<Material, Integer> blockCount = new EnumMap<>(Material.class);
// Look through the greenhouse and count what is in there // Look through the greenhouse and count what is in there
for (int y = gh.getFloorHeight(); y< gh.getCeilingHeight();y++) { Map<Material, Integer> blockCount = countBlocks(gh, cache);
for (int x = (int) (gh.getBoundingBox().getMinX()+1); x < gh.getBoundingBox().getMaxX(); x++) {
for (int z = (int) (gh.getBoundingBox().getMinZ()+1); z < gh.getBoundingBox().getMaxZ(); z++) { // Calculate % water, ice and lava ratios and check them
Material t = cache.getBlockType(x, y, z); Set<GreenhouseResult> result = checkRatios(blockCount, area);
if (!t.equals(Material.AIR)) {
blockCount.putIfAbsent(t, 0); // Compare to the required blocks
blockCount.merge(t, 1, Integer::sum); Map<Material, Integer> missingBlocks = requiredBlocks.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() - blockCount.getOrDefault(e.getKey(), 0)));
} // Remove any entries that are 0 or less
} missingBlocks.values().removeIf(v -> v <= 0);
} if (!missingBlocks.isEmpty()) {
result.add(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS);
gh.setMissingBlocks(missingBlocks);
} }
// Calculate % water, ice and lava ratios // Return to main thread to complete
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> r.complete(result));
return result;
}
private Set<GreenhouseResult> checkRatios(Map<Material, Integer> blockCount, long area) {
Set<GreenhouseResult> result = new HashSet<>();
double waterRatio = (double)blockCount.getOrDefault(Material.WATER, 0)/area * 100; double waterRatio = (double)blockCount.getOrDefault(Material.WATER, 0)/area * 100;
double lavaRatio = (double)blockCount.getOrDefault(Material.LAVA, 0)/area * 100; double lavaRatio = (double)blockCount.getOrDefault(Material.LAVA, 0)/area * 100;
int ice = blockCount.entrySet().stream().filter(en -> en.getKey().equals(Material.ICE) int ice = blockCount.entrySet().stream().filter(en -> en.getKey().equals(Material.ICE)
@ -254,47 +260,57 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
if (iceCoverage > 0 && iceRatio < iceCoverage) { if (iceCoverage > 0 && iceRatio < iceCoverage) {
result.add(GreenhouseResult.FAIL_INSUFFICIENT_ICE); result.add(GreenhouseResult.FAIL_INSUFFICIENT_ICE);
} }
// Compare to the required blocks
Map<Material, Integer> missingBlocks = requiredBlocks.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() - blockCount.getOrDefault(e.getKey(), 0)));
// Remove any entries that are 0 or less
missingBlocks.values().removeIf(v -> v <= 0);
if (!missingBlocks.isEmpty()) {
result.add(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS);
gh.setMissingBlocks(missingBlocks);
}
// Return to main thread to complete
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> r.complete(result));
return result; return result;
} }
private Map<Material, Integer> countBlocks(Greenhouse gh, AsyncWorldCache cache) {
Map<Material, Integer> blockCount = new EnumMap<>(Material.class);
for (int y = gh.getFloorHeight(); y< gh.getCeilingHeight();y++) {
for (int x = (int) (gh.getBoundingBox().getMinX()+1); x < gh.getBoundingBox().getMaxX(); x++) {
for (int z = (int) (gh.getBoundingBox().getMinZ()+1); z < gh.getBoundingBox().getMaxZ(); z++) {
Material t = cache.getBlockType(x, y, z);
if (!t.equals(Material.AIR)) {
blockCount.putIfAbsent(t, 0);
blockCount.merge(t, 1, Integer::sum);
}
}
}
}
return blockCount;
}
/** /**
* Check if block should be converted * Check if block should be converted
* @param b - block to check * @param b - block to check
*/ */
public void convertBlock(Block b) { public void convertBlock(Block b) {
Material bType = b.getType(); Material bType = b.getType();
// Check if there is a block conversion for this block, as while the rest of the method wont do anything if .get() returns nothing anyway it still seems to be quite expensive // Check if there is a block conversion for this block, as while the rest of the method won't do anything if .get() returns nothing anyway it still seems to be quite expensive
if(conversionBlocks.keySet().contains(bType)) { if(conversionBlocks.keySet().contains(bType)) {
for(GreenhouseBlockConversions conversion_option : conversionBlocks.get(bType)) { for(GreenhouseBlockConversions conversionOption : conversionBlocks.get(bType)) {
rollTheDice(b, conversionOption);
// Roll the dice before bothering with checking the surrounding block as I think it's more common for greenhouses to be filled with convertable blocks and thus this dice roll wont be "wasted"
if(ThreadLocalRandom.current().nextDouble() < conversion_option.probability()) {
// Check if any of the adjacent blocks matches the required LocalMaterial, if there are any required LocalMaterials
if(conversion_option.localMaterial() != null) {
for(BlockFace adjacent_block : ADJ_BLOCKS) {
if(b.getRelative(adjacent_block).getType() == conversion_option.localMaterial()) {
b.setType(conversion_option.newMaterial());
break;
}
}
} else {
b.setType(conversion_option.newMaterial());
}
}
} }
} }
} }
private void rollTheDice(Block b, GreenhouseBlockConversions conversion_option) {
// Roll the dice before bothering with checking the surrounding block as I think it's more common for greenhouses to be filled with convertable blocks and thus this dice roll wont be "wasted"
if(ThreadLocalRandom.current().nextDouble() < conversion_option.probability()) {
// Check if any of the adjacent blocks matches the required LocalMaterial, if there are any required LocalMaterials
if(conversion_option.localMaterial() != null) {
for(BlockFace adjacent_block : ADJ_BLOCKS) {
if(b.getRelative(adjacent_block).getType() == conversion_option.localMaterial()) {
b.setType(conversion_option.newMaterial());
break;
}
}
} else {
b.setType(conversion_option.newMaterial());
}
}
}
/** /**
* @return the type * @return the type
*/ */
@ -376,7 +392,7 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
} }
// Center spawned mob // Center spawned mob
Location spawnLoc = b.getLocation().clone().add(new Vector(0.5, 0, 0.5)); Location spawnLoc = b.getLocation().clone().add(new Vector(0.5, 0, 0.5));
boolean result = getRandomMob() return getRandomMob()
// Check if the spawn on block matches, if it exists // Check if the spawn on block matches, if it exists
.filter(m -> Optional.of(m.mobSpawnOn()) .filter(m -> Optional.of(m.mobSpawnOn())
.map(b.getRelative(BlockFace.DOWN).getType()::equals) .map(b.getRelative(BlockFace.DOWN).getType()::equals)
@ -396,7 +412,6 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
return true; return true;
}).orElse(false); }).orElse(false);
}).orElse(false); }).orElse(false);
return result;
} }
/** /**
@ -435,7 +450,10 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
// Grow a random plant that can grow // Grow a random plant that can grow
double r = random.nextDouble(); double r = random.nextDouble();
Double key = underwater ? underwaterPlants.ceilingKey(r) : plantTree.ceilingKey(r); Double key = underwater ? underwaterPlants.ceilingKey(r) : plantTree.ceilingKey(r);
return key == null ? Optional.empty() : Optional.ofNullable(underwater ? underwaterPlants.get(key) : plantTree.get(key)); if (key == null) {
return Optional.empty();
}
return Optional.ofNullable(underwater ? underwaterPlants.get(key) : plantTree.get(key));
} }
/** /**
@ -461,11 +479,9 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
public boolean growPlant(GrowthBlock block, boolean underwater) { public boolean growPlant(GrowthBlock block, boolean underwater) {
Block bl = block.block(); Block bl = block.block();
return getRandomPlant(underwater).map(p -> { return getRandomPlant(underwater).map(p -> {
if (bl.getY() != 0 && canGrowOn(block, p)) { if (bl.getY() != 0 && canGrowOn(block, p) && plantIt(bl, p)) {
if (plantIt(bl, p)) { bl.getWorld().spawnParticle(Particle.SNOWBALL, bl.getLocation(), 10, 2, 2, 2);
bl.getWorld().spawnParticle(Particle.SNOWBALL, bl.getLocation(), 10, 2, 2, 2); return true;
return true;
}
} }
return false; return false;
}).orElse(false); }).orElse(false);
@ -554,14 +570,12 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
BlockFace d = null; BlockFace d = null;
boolean waterLogged = false; boolean waterLogged = false;
for (BlockFace adj : ADJ_BLOCKS) { for (BlockFace adj : ADJ_BLOCKS) {
if (b.getRelative(adj).getType().equals(Material.AIR)) { Material type = b.getRelative(adj).getType();
if (type.equals(Material.AIR) || type.equals(Material.WATER)) {
d = adj; d = adj;
break; if (type.equals(Material.WATER)) {
} waterLogged = true;
// Lichen can grow under water too }
if (b.getRelative(adj).getType().equals(Material.WATER)) {
d = adj;
waterLogged = true;
break; break;
} }
} }

View File

@ -23,33 +23,17 @@ import world.bentobox.greenhouses.world.AsyncWorldCache;
* @author tastybento * @author tastybento
* *
*/ */
@SuppressWarnings("deprecation")
public class Roof extends MinMaxXZ { public class Roof extends MinMaxXZ {
private static final List<Material> ROOF_BLOCKS; private static final List<Material> ROOF_BLOCKS = Arrays.stream(Material.values())
static { .filter(Material::isBlock) // Blocks only, no items
// Roof blocks .filter(m -> Tag.TRAPDOORS.isTagged(m) // All trapdoors
ROOF_BLOCKS = Arrays.stream(Material.values()) || (m.name().contains("GLASS") && !m.name().contains("GLASS_PANE")) // All glass blocks
.filter(m -> !m.isLegacy()) || m.equals(Material.HOPPER)).toList();
.filter(Material::isBlock) // Blocks only, no items
.filter(m -> Tag.TRAPDOORS.isTagged(m) // All trapdoors
|| (m.name().contains("GLASS") && !m.name().contains("GLASS_PANE")) // All glass blocks
|| m.equals(Material.HOPPER)).toList();
}
/**
* Check if material is a roof material
* @param m - material
* @return true if roof material
*/
public static boolean roofBlocks(@NonNull Material m) {
return ROOF_BLOCKS.contains(Objects.requireNonNull(m))
|| (m.equals(Material.GLOWSTONE) && Greenhouses.getInstance().getSettings().isAllowGlowstone())
|| (m.name().endsWith("GLASS_PANE") && Greenhouses.getInstance().getSettings().isAllowPanes());
}
private final AsyncWorldCache cache; private final AsyncWorldCache cache;
private int height; private int height;
private final Location location; private final Location location;
private boolean roofFound; private boolean roofFound;
private final Greenhouses addon;
private final World world; private final World world;
@ -58,13 +42,23 @@ public class Roof extends MinMaxXZ {
* @param cache async world cache * @param cache async world cache
* @param loc - starting location * @param loc - starting location
*/ */
public Roof(AsyncWorldCache cache, Location loc) { public Roof(AsyncWorldCache cache, Location loc, Greenhouses addon) {
this.cache = cache; this.cache = cache;
this.location = loc; this.location = loc;
this.addon = addon;
this.world = loc.getWorld(); this.world = loc.getWorld();
} }
/**
* Check if material is a roof material
* @param m - material
* @return true if roof material
*/
public boolean roofBlocks(@NonNull Material m) {
return ROOF_BLOCKS.contains(Objects.requireNonNull(m))
|| (m.equals(Material.GLOWSTONE) && addon.getSettings().isAllowGlowstone())
|| (m.name().endsWith("GLASS_PANE") && addon.getSettings().isAllowPanes());
}
/** /**
* This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks * This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks
@ -124,7 +118,7 @@ public class Roof extends MinMaxXZ {
} }
boolean findRoof(Vector loc) { 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 an 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 startY = loc.getBlockY(); int startY = loc.getBlockY();
for (int y = startY; y < world.getMaxHeight(); y++) { for (int y = startY; y < world.getMaxHeight(); y++) {
@ -136,7 +130,7 @@ public class Roof extends MinMaxXZ {
} }
} }
// 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
if (!roofFound) { if (!roofFound) {
loc = spiralSearch(loc, startY); loc = spiralSearch(loc, startY);
if (!roofFound) { if (!roofFound) {
@ -205,13 +199,13 @@ public class Roof extends MinMaxXZ {
} }
/** /**
* Get highest roof block * Get the highest roof block
* @param x - x coord of current search * @param x - x coord of current search
* @param startY - starting y coord * @param startY - starting y coord
* @param z - z coord of current search * @param z - z coord of current search
*/ */
private Optional<Vector> checkVertically(final int x, final int startY, final int z) { private Optional<Vector> checkVertically(final int x, final int startY, final int z) {
if (!Walls.wallBlocks(cache.getBlockType(x, startY, z))) { if (!addon.wallBlocks(cache.getBlockType(x, startY, z))) {
// Look up // Look up
for (int y = startY; y < world.getMaxHeight() && !roofFound; y++) { for (int y = startY; y < world.getMaxHeight() && !roofFound; y++) {
if (roofBlocks(cache.getBlockType(x,y,z))) { if (roofBlocks(cache.getBlockType(x,y,z))) {

View File

@ -9,25 +9,17 @@ import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.world.AsyncWorldCache; import world.bentobox.greenhouses.world.AsyncWorldCache;
@SuppressWarnings("deprecation")
public class Walls extends MinMaxXZ { public class Walls extends MinMaxXZ {
private static final List<Material> WALL_BLOCKS; public static final List<Material> WALL_BLOCKS = Arrays.stream(Material.values())
static { .filter(Material::isBlock) // Blocks only, no items
// Hoppers .filter(m -> !m.name().contains("TRAPDOOR")) // No trap doors
WALL_BLOCKS = Arrays.stream(Material.values()) .filter(m -> m.name().contains("DOOR") // All doors
.filter(Material::isBlock) // Blocks only, no items || (m.name().contains("GLASS") && !m.name().contains("GLASS_PANE")) // All glass blocks
.filter(m -> !m.isLegacy()) || m.equals(Material.HOPPER)).toList();
.filter(m -> !m.name().contains("TRAPDOOR")) // No trap doors
.filter(m -> m.name().contains("DOOR") // All doors
|| (m.name().contains("GLASS") && !m.name().contains("GLASS_PANE")) // All glass blocks
|| m.equals(Material.HOPPER)).toList();
}
private int floor; private int floor;
private final AsyncWorldCache cache; private final AsyncWorldCache cache;
static class WallFinder { static class WallFinder {
@ -177,17 +169,6 @@ public class Walls extends MinMaxXZ {
} }
/**
* Check if material is a wall material
* @param m - material
* @return true if wall material
*/
public static boolean wallBlocks(Material m) {
return WALL_BLOCKS.contains(m)
|| (m.equals(Material.GLOWSTONE) && Greenhouses.getInstance().getSettings().isAllowGlowstone())
|| (m.name().endsWith("GLASS_PANE") && Greenhouses.getInstance().getSettings().isAllowPanes());
}
/** /**
* @return the floor * @return the floor
*/ */

View File

@ -53,7 +53,7 @@ public class GreenhouseGuard implements Listener {
if (from.isPresent() && addon.getSettings().isAllowFlowOut()) { if (from.isPresent() && addon.getSettings().isAllowFlowOut()) {
return; return;
} }
// Otherwise cancel - the flow is not allowed // Otherwise, cancel - the flow is not allowed
e.setCancelled(true); e.setCancelled(true);
} }

View File

@ -95,17 +95,17 @@ public class EcoSystemManager {
return; return;
} }
final BoundingBox ibb = gh.getInternalBoundingBox(); final BoundingBox ibb = gh.getInternalBoundingBox();
int gh_min_x = NumberConversions.floor(ibb.getMinX()); int ghMinX = NumberConversions.floor(ibb.getMinX());
int gh_max_x = NumberConversions.floor(ibb.getMaxX()); int ghMaxX = NumberConversions.floor(ibb.getMaxX());
int gh_min_y = NumberConversions.floor(gh.getBoundingBox().getMinY()); // Note: this gets the floor int ghMinY = NumberConversions.floor(gh.getBoundingBox().getMinY()); // Note: this gets the floor
int gh_max_y = NumberConversions.floor(ibb.getMaxY()); int ghMaxY = NumberConversions.floor(ibb.getMaxY());
int gh_min_z = NumberConversions.floor(ibb.getMinZ()); int ghMinZ = NumberConversions.floor(ibb.getMinZ());
int gh_max_z = NumberConversions.floor(ibb.getMaxZ()); int ghMaxZ = NumberConversions.floor(ibb.getMaxZ());
BiomeRecipe biomeRecipe = gh.getBiomeRecipe(); BiomeRecipe biomeRecipe = gh.getBiomeRecipe();
for (int x = gh_min_x; x < gh_max_x; x++) { for (int x = ghMinX; x < ghMaxX; x++) {
for (int z = gh_min_z; z < gh_max_z; z++) { for (int z = ghMinZ; z < ghMaxZ; z++) {
for (int y = gh_min_y; y < gh_max_y; y++) { for (int y = ghMinY; y < ghMaxY; y++) {
Block b = world.getBlockAt(x, y, z); Block b = world.getBlockAt(x, y, z);
if(!b.isEmpty()) { if(!b.isEmpty()) {
@ -167,7 +167,7 @@ public class EcoSystemManager {
Collections.shuffle(list, new Random(System.currentTimeMillis())); Collections.shuffle(list, new Random(System.currentTimeMillis()));
Iterator<GrowthBlock> it = list.iterator(); Iterator<GrowthBlock> it = list.iterator();
// Check if the greenhouse is full // Check if the greenhouse is full
if (sum >= gh.getBiomeRecipe().getMaxMob()) { if (gh.getBiomeRecipe().getMaxMob() > -1 && sum >= gh.getBiomeRecipe().getMaxMob()) {
return false; return false;
} }
while (it.hasNext() && (sum == 0 || gh.getArea() / sum >= gh.getBiomeRecipe().getMobLimit())) { while (it.hasNext() && (sum == 0 || gh.getArea() / sum >= gh.getBiomeRecipe().getMobLimit())) {
@ -241,36 +241,39 @@ public class EcoSystemManager {
for (double z = ibb.getMinZ(); z < ibb.getMaxZ(); z++) { for (double z = ibb.getMinZ(); z < ibb.getMaxZ(); z++) {
for (double y = ibb.getMaxY() - 1; y >= bb.getMinY(); y--) { for (double y = ibb.getMaxY() - 1; y >= bb.getMinY(); y--) {
Block b = gh.getWorld().getBlockAt(NumberConversions.floor(x), NumberConversions.floor(y), NumberConversions.floor(z)); Block b = gh.getWorld().getBlockAt(NumberConversions.floor(x), NumberConversions.floor(y), NumberConversions.floor(z));
if (checkBlock(result, b, ignoreLiquid)) {
// Check floor blocks break;
if (!ignoreLiquid) {
// Check ceiling blocks
if (b.isEmpty() && !b.getRelative(BlockFace.UP).isEmpty()) {
result.add(new GrowthBlock(b, false));
}
if (!b.isEmpty() && !Tag.LEAVES.isTagged(b.getType())
&& (b.getRelative(BlockFace.UP).isEmpty()
|| b.getRelative(BlockFace.UP).isPassable()
|| Tag.LEAVES.isTagged(b.getRelative(BlockFace.UP).getType())
)
) {
result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true));
break;
}
} else {
if (!b.isEmpty() && !b.isLiquid() && b.getRelative(BlockFace.UP).isLiquid()) {
result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true));
break;
}
} }
} }
} }
} }
return result; return result;
} }
private boolean checkBlock(List<GrowthBlock> result, Block b, boolean ignoreLiquid) {
// Check floor blocks
if (!ignoreLiquid) {
// Check ceiling blocks
if (b.isEmpty() && !b.getRelative(BlockFace.UP).isEmpty()) {
result.add(new GrowthBlock(b, false));
}
if (!b.isEmpty() && !Tag.LEAVES.isTagged(b.getType())
&& (b.getRelative(BlockFace.UP).isEmpty()
|| b.getRelative(BlockFace.UP).isPassable()
|| Tag.LEAVES.isTagged(b.getRelative(BlockFace.UP).getType())
)
) {
result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true));
return true;
}
} else {
if (!b.isEmpty() && !b.isLiquid() && b.getRelative(BlockFace.UP).isLiquid()) {
result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true));
return true;
}
}
return false;
}
private int getBoneMeal(Greenhouse gh) { private int getBoneMeal(Greenhouse gh) {
Hopper hopper = getHopper(gh); Hopper hopper = getHopper(gh);

View File

@ -12,6 +12,7 @@ import org.bukkit.Tag;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.Greenhouses;
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;
@ -28,10 +29,11 @@ public class GreenhouseFinder {
// If this is the bottom layer, the player has most likely uneven walls // If this is the bottom layer, the player has most likely uneven walls
private int otherBlockLayer = -1; private int otherBlockLayer = -1;
private int wallBlockCount; private int wallBlockCount;
private final Greenhouses addon;
/** /**
* This is the count of the various items * This is the count of the various items
*/ */
private CounterCheck cc = new CounterCheck(); private CounterCheck counterCheck = new CounterCheck();
static class CounterCheck { static class CounterCheck {
int doorCount; int doorCount;
@ -40,6 +42,13 @@ public class GreenhouseFinder {
boolean otherBlock; boolean otherBlock;
} }
/**
* @param addon Addon
*/
public GreenhouseFinder(Greenhouses addon) {
this.addon = addon;
}
/** /**
* Find out if there is a greenhouse here * Find out if there is a greenhouse here
* @param location - start location * @param location - start location
@ -51,9 +60,9 @@ public class GreenhouseFinder {
redGlass.clear(); redGlass.clear();
// Get a world cache // Get a world cache
AsyncWorldCache cache = new AsyncWorldCache(location.getWorld()); AsyncWorldCache cache = new AsyncWorldCache(addon, location.getWorld());
// Find the roof // Find the roof
Roof roof = new Roof(cache, location); Roof roof = new Roof(cache, location, addon);
roof.findRoof().thenAccept(found -> { roof.findRoof().thenAccept(found -> {
if (Boolean.FALSE.equals(found)) { if (Boolean.FALSE.equals(found)) {
result.add(GreenhouseResult.FAIL_NO_ROOF); result.add(GreenhouseResult.FAIL_NO_ROOF);
@ -87,30 +96,26 @@ public class GreenhouseFinder {
*/ */
CompletableFuture<Set<GreenhouseResult>> checkGreenhouse(AsyncWorldCache cache, Roof roof, Walls walls) { CompletableFuture<Set<GreenhouseResult>> checkGreenhouse(AsyncWorldCache cache, Roof roof, Walls walls) {
CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>(); CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> checkGHAsync(r, cache, roof, walls)); Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> checkGreenhouseAsync(r, cache, roof, walls));
return r; return r;
} }
private Set<GreenhouseResult> checkGHAsync(CompletableFuture<Set<GreenhouseResult>> r, AsyncWorldCache cache, private Set<GreenhouseResult> checkGreenhouseAsync(CompletableFuture<Set<GreenhouseResult>> r, AsyncWorldCache cache,
Roof roof, Walls walls) { Roof roof, Walls walls) {
cc = new CounterCheck(); counterCheck = new CounterCheck();
int y; int y;
for (y = roof.getHeight(); y > walls.getFloor(); y--) { for (y = roof.getHeight(); y > walls.getFloor(); y--) {
wallBlockCount = 0; wallBlockCount = 0;
for (int x = walls.getMinX(); x <= walls.getMaxX(); x++) { for (int x = walls.getMinX(); x <= walls.getMaxX(); x++) {
for (int z = walls.getMinZ(); z <= walls.getMaxZ(); z++) { for (int z = walls.getMinZ(); z <= walls.getMaxZ(); z++) {
checkBlock(cc, cache.getBlockType(x,y,z), roof, walls, new Vector(x, y, z)); checkBlock(counterCheck, cache.getBlockType(x,y,z), roof, walls, new Vector(x, y, z));
} }
} }
if (wallBlockCount == 0 && y < roof.getHeight()) { if (wallBlockCount == 0 && y < roof.getHeight()) {
// This is the floor // This is the floor
break; break;
} else { } else if (counterCheck.otherBlock && otherBlockLayer < 0) {
if (cc.otherBlock) { otherBlockLayer = y;
if (otherBlockLayer < 0) {
otherBlockLayer = y;
}
}
} }
} }
@ -138,7 +143,7 @@ public class GreenhouseFinder {
// Roof blocks must be glass, glowstone, doors or a hopper. // Roof blocks must be glass, glowstone, doors or a hopper.
result.add(GreenhouseResult.FAIL_BAD_ROOF_BLOCKS); result.add(GreenhouseResult.FAIL_BAD_ROOF_BLOCKS);
} else if (isOtherBlocks()) { } else if (isOtherBlocks()) {
// "Wall blocks must be glass, glowstone, doors or a hopper. // Wall blocks must be glass, glowstone, doors or a hopper.
result.add(GreenhouseResult.FAIL_BAD_WALL_BLOCKS); result.add(GreenhouseResult.FAIL_BAD_WALL_BLOCKS);
} }
if (this.getWallDoors() > 8) { if (this.getWallDoors() > 8) {
@ -166,7 +171,8 @@ public class GreenhouseFinder {
// Check wall blocks only // Check wall blocks only
if (y == roof.getHeight() || x == walls.getMinX() || x == walls.getMaxX() || z == walls.getMinZ() || z== walls.getMaxZ()) { 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 // 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 ((y != roof.getHeight() && !addon.wallBlocks(m))
|| (y == roof.getHeight() && !roof.roofBlocks(m))) {
if (m.equals(Material.AIR)) { if (m.equals(Material.AIR)) {
// Air hole found // Air hole found
cc.airHole = true; cc.airHole = true;
@ -241,28 +247,28 @@ public class GreenhouseFinder {
* @return the wallDoors * @return the wallDoors
*/ */
int getWallDoors() { int getWallDoors() {
return cc.doorCount; return counterCheck.doorCount;
} }
/** /**
* @return the ghHopper * @return the ghHopper
*/ */
int getGhHopper() { int getGhHopper() {
return cc.hopperCount; return counterCheck.hopperCount;
} }
/** /**
* @return the airHoles * @return the airHoles
*/ */
boolean isAirHoles() { boolean isAirHoles() {
return cc.airHole; return counterCheck.airHole;
} }
/** /**
* @return the otherBlocks * @return the otherBlocks
*/ */
boolean isOtherBlocks() { boolean isOtherBlocks() {
return cc.otherBlock; return counterCheck.otherBlock;
} }
/** /**
@ -315,21 +321,21 @@ public class GreenhouseFinder {
} }
public void setGhHopper(int i) { public void setGhHopper(int i) {
cc.hopperCount = i; counterCheck.hopperCount = i;
} }
public void setWallDoors(int i) { public void setWallDoors(int i) {
cc.doorCount = i; counterCheck.doorCount = i;
} }
public void setAirHoles(boolean b) { public void setAirHoles(boolean b) {
cc.airHole = b; counterCheck.airHole = b;
} }
public void setOtherBlocks(boolean b) { public void setOtherBlocks(boolean b) {
cc.otherBlock = b; counterCheck.otherBlock = b;
} }

View File

@ -96,19 +96,19 @@ public class GreenhouseManager implements Listener {
handler.loadObjects().forEach(g -> { handler.loadObjects().forEach(g -> {
GreenhouseResult result = map.addGreenhouse(g); GreenhouseResult result = map.addGreenhouse(g);
switch (result) { switch (result) {
case FAIL_NO_ISLAND -> case FAIL_NO_ISLAND ->
// Delete the failed greenhouse // Delete the failed greenhouse
toBeRemoved.add(g); toBeRemoved.add(g);
case FAIL_OVERLAPPING -> addon.logError("Greenhouse overlaps with another greenhouse. Skipping..."); case FAIL_OVERLAPPING -> addon.logError("Greenhouse overlaps with another greenhouse. Skipping...");
case NULL -> addon.logError("Null location of greenhouse. Cannot load. Skipping..."); case NULL -> addon.logError("Null location of greenhouse. Cannot load. Skipping...");
case SUCCESS -> activateGreenhouse(g); case SUCCESS -> activateGreenhouse(g);
case FAIL_NO_WORLD -> addon.logError("Database contains greenhouse for a non-loaded world. Skipping..."); case FAIL_NO_WORLD -> addon.logError("Database contains greenhouse for a non-loaded world. Skipping...");
case FAIL_UNKNOWN_RECIPE -> { case FAIL_UNKNOWN_RECIPE -> {
addon.logError("Greenhouse uses a recipe that does not exist in the biomes.yml. Skipping..."); addon.logError("Greenhouse uses a recipe that does not exist in the biomes.yml. Skipping...");
addon.logError("Greenhouse Id " + g.getUniqueId()); addon.logError("Greenhouse Id " + g.getUniqueId());
} }
default -> { default -> {
} }
} }
}); });
addon.log("Loaded " + map.getSize() + " greenhouses."); addon.log("Loaded " + map.getSize() + " greenhouses.");
@ -153,7 +153,7 @@ public class GreenhouseManager implements Listener {
*/ */
public CompletableFuture<GhResult> tryToMakeGreenhouse(Location location, BiomeRecipe greenhouseRecipe) { public CompletableFuture<GhResult> tryToMakeGreenhouse(Location location, BiomeRecipe greenhouseRecipe) {
CompletableFuture<GhResult> r = new CompletableFuture<>(); CompletableFuture<GhResult> r = new CompletableFuture<>();
GreenhouseFinder finder = new GreenhouseFinder(); GreenhouseFinder finder = new GreenhouseFinder(addon);
finder.find(location).thenAccept(resultSet -> { finder.find(location).thenAccept(resultSet -> {
if (!resultSet.isEmpty()) { if (!resultSet.isEmpty()) {
// Failure! // Failure!

View File

@ -6,7 +6,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.bukkit.Location; import org.bukkit.Location;
@ -138,7 +137,7 @@ public class GreenhouseMap {
* @return a list of all the Greenhouses * @return a list of all the Greenhouses
*/ */
public List<Greenhouse> getGreenhouses() { public List<Greenhouse> getGreenhouses() {
return greenhouses.values().stream().flatMap(List::stream).collect(Collectors.toList()); return greenhouses.values().stream().flatMap(List::stream).toList();
} }
/** /**

View File

@ -30,6 +30,7 @@ public class RecipeManager {
private static final int MAXIMUM_INVENTORY_SIZE = 49; private static final int MAXIMUM_INVENTORY_SIZE = 49;
private final Greenhouses addon; private final Greenhouses addon;
private static final List<BiomeRecipe> biomeRecipes = new ArrayList<>(); private static final List<BiomeRecipe> biomeRecipes = new ArrayList<>();
private static final String COULD_NOT_PARSE = "Could not parse ";
public RecipeManager(Greenhouses addon) { public RecipeManager(Greenhouses addon) {
this.addon = addon; this.addon = addon;
@ -179,45 +180,54 @@ public class RecipeManager {
ConfigurationSection conversionSec = biomeRecipeConfig.getConfigurationSection("conversions"); ConfigurationSection conversionSec = biomeRecipeConfig.getConfigurationSection("conversions");
if (conversionSec != null) { if (conversionSec != null) {
for (String oldMat : conversionSec.getKeys(false)) { for (String oldMat : conversionSec.getKeys(false)) {
try { parseConversions(oldMat, conversionSec, b);
Material oldMaterial = Material.valueOf(oldMat.toUpperCase(Locale.ENGLISH));
String conversions = conversionSec.getString(oldMat);
if (!Objects.requireNonNull(conversions).isEmpty()) {
String[] split = conversions.split(":");
double convChance = Double.parseDouble(split[0]);
Material newMaterial = Material.valueOf(split[1]);
Material localMaterial = null;
if(split.length > 2) {
localMaterial = Material.valueOf(split[2]);
}
b.addConvBlocks(oldMaterial, newMaterial, convChance, localMaterial);
}
} catch (Exception e) {
addon.logError("Could not parse " + oldMat);
}
} }
} }
// Get the list of conversions // Get the list of conversions
for (String oldMat : biomeRecipeConfig.getStringList("conversion-list")) { for (String oldMat : biomeRecipeConfig.getStringList("conversion-list")) {
try { parseConversionList(oldMat, b);
// Split the string
String[] split = oldMat.split(":");
Material oldMaterial = Material.valueOf(split[0].toUpperCase());
double convChance = Double.parseDouble(split[1]);
Material newMaterial = Material.valueOf(split[2]);
Material localMaterial = null;
if(split.length > 3) {
localMaterial = Material.valueOf(split[3]);
}
b.addConvBlocks(oldMaterial, newMaterial, convChance, localMaterial);
} catch (Exception e) {
addon.logError("Could not parse " + oldMat);
}
} }
} }
private void parseConversionList(String oldMat, BiomeRecipe b) {
try {
// Split the string
String[] split = oldMat.split(":");
Material oldMaterial = Material.valueOf(split[0].toUpperCase());
double convChance = Double.parseDouble(split[1]);
Material newMaterial = Material.valueOf(split[2]);
Material localMaterial = null;
if(split.length > 3) {
localMaterial = Material.valueOf(split[3]);
}
b.addConvBlocks(oldMaterial, newMaterial, convChance, localMaterial);
} catch (Exception e) {
addon.logError(COULD_NOT_PARSE + oldMat);
}
}
private void parseConversions(String oldMat, ConfigurationSection conversionSec, BiomeRecipe b) {
try {
Material oldMaterial = Material.valueOf(oldMat.toUpperCase(Locale.ENGLISH));
String conversions = conversionSec.getString(oldMat);
if (!Objects.requireNonNull(conversions).isEmpty()) {
String[] split = conversions.split(":");
double convChance = Double.parseDouble(split[0]);
Material newMaterial = Material.valueOf(split[1]);
Material localMaterial = null;
if(split.length > 2) {
localMaterial = Material.valueOf(split[2]);
}
b.addConvBlocks(oldMaterial, newMaterial, convChance, localMaterial);
}
} catch (Exception e) {
addon.logError(COULD_NOT_PARSE + oldMat);
}
}
private void loadMobs(ConfigurationSection biomeRecipeConfig, BiomeRecipe b) { private void loadMobs(ConfigurationSection biomeRecipeConfig, BiomeRecipe b) {
ConfigurationSection temp = biomeRecipeConfig.getConfigurationSection("mobs"); ConfigurationSection temp = biomeRecipeConfig.getConfigurationSection("mobs");
// Mob EntityType: Probability:Spawn on Material // Mob EntityType: Probability:Spawn on Material
@ -235,7 +245,7 @@ public class RecipeManager {
Material mobSpawnOn = Material.valueOf(split[1]); Material mobSpawnOn = Material.valueOf(split[1]);
b.addMobs(mobType, mobProbability, mobSpawnOn); b.addMobs(mobType, mobProbability, mobSpawnOn);
} catch (Exception e) { } catch (Exception e) {
addon.logError("Could not parse " + s.getKey()); addon.logError(COULD_NOT_PARSE + s.getKey());
} }
} }

View File

@ -27,14 +27,16 @@ public class AsyncWorldCache {
private final World world; private final World world;
private final Map<Pair<Integer, Integer>, ChunkSnapshot> cache; private final Map<Pair<Integer, Integer>, ChunkSnapshot> cache;
private final Greenhouses addon;
/** /**
* Chunk cache. This class is designed to be run async and blocks futures * Chunk cache. This class is designed to be run async and blocks futures
* @param world - world to cache * @param world - world to cache
*/ */
public AsyncWorldCache(World world) { public AsyncWorldCache(Greenhouses addon, World world) {
this.world = world; this.world = world;
cache = new HashMap<>(); cache = new HashMap<>();
this.addon = addon;
} }
/** /**
@ -59,7 +61,7 @@ public class AsyncWorldCache {
*/ */
private CompletableFuture<ChunkSnapshot> getAChunk(int x, int z) { private CompletableFuture<ChunkSnapshot> getAChunk(int x, int z) {
CompletableFuture<ChunkSnapshot> r = new CompletableFuture<>(); CompletableFuture<ChunkSnapshot> r = new CompletableFuture<>();
Bukkit.getScheduler().runTask(Greenhouses.getInstance().getPlugin(), () -> Bukkit.getScheduler().runTask(addon.getPlugin(), () ->
Util.getChunkAtAsync(world, x, z).thenAccept(chunk -> r.complete(chunk.getChunkSnapshot()))); Util.getChunkAtAsync(world, x, z).thenAccept(chunk -> r.complete(chunk.getChunkSnapshot())));
return r; return r;
} }
@ -104,7 +106,7 @@ public class AsyncWorldCache {
try { try {
return Objects.requireNonNull(getSnap(x, z)).getBlockType(xx, y, zz); return Objects.requireNonNull(getSnap(x, z)).getBlockType(xx, y, zz);
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
Greenhouses.getInstance().logError("Chunk could not be obtained async! " + e); addon.logError("Chunk could not be obtained async! " + e);
// Restore interrupted state... // Restore interrupted state...
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
return Material.AIR; return Material.AIR;

View File

@ -59,9 +59,9 @@ greenhouses:
FAIL_TOO_MANY_DOORS: "&c You cannot have more than 4 doors in the greenhouse!" FAIL_TOO_MANY_DOORS: "&c You cannot have more than 4 doors in the greenhouse!"
FAIL_TOO_MANY_HOPPERS: "&c Only one hopper is allowed in the walls or roof." FAIL_TOO_MANY_HOPPERS: "&c Only one hopper is allowed in the walls or roof."
FAIL_UNEVEN_WALLS: "&c The walls are uneven. Red glass blocks should show the problem blocks." FAIL_UNEVEN_WALLS: "&c The walls are uneven. Red glass blocks should show the problem blocks."
FAIL_INSUFFICIENT_ICE: "&c Insufficent ice to make this recipe" FAIL_INSUFFICIENT_ICE: "&c Insufficient ice to make this recipe"
FAIL_INSUFFICIENT_LAVA: "&c Insufficent lava to make this recipe" FAIL_INSUFFICIENT_LAVA: "&c Insufficient lava to make this recipe"
FAIL_INSUFFICIENT_WATER: "&c Insufficent water to make this recipe" FAIL_INSUFFICIENT_WATER: "&c Insufficient water to make this recipe"
FAIL_NO_ICE: "&c Ice is required to make this recipe" FAIL_NO_ICE: "&c Ice is required to make this recipe"
FAIL_NO_LAVA: "&c Lava is required to make this recipe" FAIL_NO_LAVA: "&c Lava is required to make this recipe"
FAIL_NO_WATER: "&c Water is required to make this recipe" FAIL_NO_WATER: "&c Water is required to make this recipe"

View File

@ -5,7 +5,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -61,7 +60,7 @@ public class GreenhouseTest {
// RecipeManager // RecipeManager
PowerMockito.mockStatic(RecipeManager.class); PowerMockito.mockStatic(RecipeManager.class);
when(br.getName()).thenReturn("test"); when(br.getName()).thenReturn("test");
when(RecipeManager.getBiomeRecipies(eq("test"))).thenReturn(Optional.of(br)); when(RecipeManager.getBiomeRecipies("test")).thenReturn(Optional.of(br));
// Walls // Walls
when(walls.getMinX()).thenReturn(MINX); when(walls.getMinX()).thenReturn(MINX);
when(walls.getMinZ()).thenReturn(MINZ); when(walls.getMinZ()).thenReturn(MINZ);

View File

@ -151,7 +151,7 @@ public class BiomeRecipeTest {
double convChance = 100D; double convChance = 100D;
Material localMaterial = Material.WATER; Material localMaterial = Material.WATER;
br.addConvBlocks(oldMaterial, newMaterial, convChance, localMaterial); br.addConvBlocks(oldMaterial, newMaterial, convChance, localMaterial);
verify(addon).log(eq(" 100.0% chance for Sand to convert to Clay")); verify(addon).log(" 100.0% chance for Sand to convert to Clay");
} }
/** /**
@ -163,7 +163,7 @@ public class BiomeRecipeTest {
int mobProbability = 50; int mobProbability = 50;
Material mobSpawnOn = Material.GRASS_BLOCK; Material mobSpawnOn = Material.GRASS_BLOCK;
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
verify(addon).log(eq(" 50.0% chance for Cat to spawn on Grass Block.")); verify(addon).log(" 50.0% chance for Cat to spawn on Grass Block.");
} }
/** /**
@ -177,7 +177,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
verify(addon).logError(eq("Mob chances add up to > 100% in BADLANDS biome recipe! Skipping CAT")); verify(addon).logError("Mob chances add up to > 100% in BADLANDS biome recipe! Skipping CAT");
} }
/** /**
@ -190,7 +190,7 @@ public class BiomeRecipeTest {
Material mobSpawnOn = Material.GRASS_BLOCK; Material mobSpawnOn = Material.GRASS_BLOCK;
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
verify(addon).logError(eq("Mob chances add up to > 100% in BADLANDS biome recipe! Skipping CAT")); verify(addon).logError("Mob chances add up to > 100% in BADLANDS biome recipe! Skipping CAT");
} }
/** /**
@ -202,7 +202,7 @@ public class BiomeRecipeTest {
int plantProbability = 20; int plantProbability = 20;
Material plantGrowOn = Material.DIRT; Material plantGrowOn = Material.DIRT;
br.addPlants(plantMaterial, plantProbability, plantGrowOn); br.addPlants(plantMaterial, plantProbability, plantGrowOn);
verify(addon).log(eq(" 20.0% chance for Jungle Sapling to grow on Dirt")); verify(addon).log(" 20.0% chance for Jungle Sapling to grow on Dirt");
} }
/** /**
@ -215,7 +215,7 @@ public class BiomeRecipeTest {
Material plantGrowOn = Material.DIRT; Material plantGrowOn = Material.DIRT;
br.addPlants(plantMaterial, plantProbability, plantGrowOn); br.addPlants(plantMaterial, plantProbability, plantGrowOn);
br.addPlants(plantMaterial, plantProbability, plantGrowOn); br.addPlants(plantMaterial, plantProbability, plantGrowOn);
verify(addon).logError(eq("Plant chances add up to > 100% in BADLANDS biome recipe! Skipping JUNGLE_SAPLING")); verify(addon).logError("Plant chances add up to > 100% in BADLANDS biome recipe! Skipping JUNGLE_SAPLING");
} }
/** /**
@ -226,7 +226,7 @@ public class BiomeRecipeTest {
Material blockMaterial = Material.BLACK_CONCRETE; Material blockMaterial = Material.BLACK_CONCRETE;
int blockQty = 30; int blockQty = 30;
br.addReqBlocks(blockMaterial, blockQty); br.addReqBlocks(blockMaterial, blockQty);
verify(addon).log(eq(" BLACK_CONCRETE x 30")); verify(addon).log(" BLACK_CONCRETE x 30");
} }
/** /**
@ -451,7 +451,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
assertFalse(br.spawnMob(block)); assertFalse(br.spawnMob(block));
verify(world).spawnEntity(eq(location), eq(EntityType.CAT)); verify(world).spawnEntity(location, EntityType.CAT);
verify(location).add(any(Vector.class)); verify(location).add(any(Vector.class));
} }
@ -477,7 +477,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
assertTrue(br.spawnMob(block)); assertTrue(br.spawnMob(block));
verify(world).spawnEntity(eq(location), eq(EntityType.CAT)); verify(world).spawnEntity(location, EntityType.CAT);
verify(location).add(any(Vector.class)); verify(location).add(any(Vector.class));
} }
@ -504,7 +504,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
assertTrue(br.spawnMob(block)); assertTrue(br.spawnMob(block));
verify(world).spawnEntity(eq(location), eq(EntityType.HOGLIN)); verify(world).spawnEntity(location, EntityType.HOGLIN);
verify(location).add(any(Vector.class)); verify(location).add(any(Vector.class));
verify(hoglin).setImmuneToZombification(true); verify(hoglin).setImmuneToZombification(true);
} }
@ -532,7 +532,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
assertTrue(br.spawnMob(block)); assertTrue(br.spawnMob(block));
verify(world).spawnEntity(eq(location), eq(EntityType.PIGLIN)); verify(world).spawnEntity(location, EntityType.PIGLIN);
verify(location).add(any(Vector.class)); verify(location).add(any(Vector.class));
verify(piglin).setImmuneToZombification(true); verify(piglin).setImmuneToZombification(true);
} }
@ -562,7 +562,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
assertTrue(br.spawnMob(block)); assertTrue(br.spawnMob(block));
verify(world).spawnEntity(eq(location), eq(EntityType.PIGLIN)); verify(world).spawnEntity(location, EntityType.PIGLIN);
verify(location).add(any(Vector.class)); verify(location).add(any(Vector.class));
verify(piglin, never()).setImmuneToZombification(true); verify(piglin, never()).setImmuneToZombification(true);
} }
@ -586,7 +586,7 @@ public class BiomeRecipeTest {
br.addMobs(mobType, mobProbability, mobSpawnOn); br.addMobs(mobType, mobProbability, mobSpawnOn);
assertFalse(br.spawnMob(block)); assertFalse(br.spawnMob(block));
verify(world, never()).spawnEntity(eq(location), eq(EntityType.CAT)); verify(world, never()).spawnEntity(location, EntityType.CAT);
} }
/** /**
@ -706,8 +706,8 @@ public class BiomeRecipeTest {
assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK)); assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK));
assertTrue(br.growPlant(new GrowthBlock(block, true), false)); assertTrue(br.growPlant(new GrowthBlock(block, true), false));
verify(world).spawnParticle(eq(Particle.SNOWBALL), any(Location.class), anyInt(), anyDouble(), anyDouble(), anyDouble()); verify(world).spawnParticle(eq(Particle.SNOWBALL), any(Location.class), anyInt(), anyDouble(), anyDouble(), anyDouble());
verify(bisected).setHalf(eq(Half.BOTTOM)); verify(bisected).setHalf(Half.BOTTOM);
verify(bisected).setHalf(eq(Half.TOP)); verify(bisected).setHalf(Half.TOP);
} }
/** /**
@ -722,8 +722,8 @@ public class BiomeRecipeTest {
when(block.isEmpty()).thenReturn(true); when(block.isEmpty()).thenReturn(true);
Block ob = mock(Block.class); Block ob = mock(Block.class);
when(ob.getType()).thenReturn(Material.GRASS_BLOCK); when(ob.getType()).thenReturn(Material.GRASS_BLOCK);
when(block.getRelative(eq(BlockFace.DOWN))).thenReturn(ob); when(block.getRelative(BlockFace.DOWN)).thenReturn(ob);
when(block.getRelative(eq(BlockFace.UP))).thenReturn(ob); when(block.getRelative(BlockFace.UP)).thenReturn(ob);
assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK)); assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK));
assertFalse(br.growPlant(new GrowthBlock(block, true), false)); assertFalse(br.growPlant(new GrowthBlock(block, true), false));
} }

View File

@ -50,8 +50,6 @@ public class RoofTest {
public void setUp() { public void setUp() {
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true); when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true);
PowerMockito.mockStatic(Greenhouses.class, Mockito.RETURNS_MOCKS);
when(Greenhouses.getInstance()).thenReturn(addon);
s = new Settings(); s = new Settings();
when(addon.getSettings()).thenReturn(s); when(addon.getSettings()).thenReturn(s);
@ -89,14 +87,14 @@ public class RoofTest {
when(location.clone()).thenReturn(location); when(location.clone()).thenReturn(location);
// Test // Test
roof = new Roof(cache, location); roof = new Roof(cache, location, addon);
assertTrue(roof.findRoof(new Vector(10,10,10))); assertTrue(roof.findRoof(new Vector(10,10,10)));
} }
@Test @Test
public void testNoGlass() { public void testNoGlass() {
when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.AIR); when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.AIR);
roof = new Roof(cache, location); roof = new Roof(cache, location, addon);
assertFalse(roof.findRoof(new Vector(10,10,10))); assertFalse(roof.findRoof(new Vector(10,10,10)));
} }
@ -169,13 +167,13 @@ public class RoofTest {
*/ */
@Test @Test
public void testWallBlocks() { public void testWallBlocks() {
assertFalse(Roof.roofBlocks(Material.ACACIA_BOAT)); assertFalse(roof.roofBlocks(Material.ACACIA_BOAT));
assertTrue(Roof.roofBlocks(Material.GLASS)); assertTrue(roof.roofBlocks(Material.GLASS));
assertTrue(Roof.roofBlocks(Material.GLOWSTONE)); assertTrue(roof.roofBlocks(Material.GLOWSTONE));
assertFalse(Roof.roofBlocks(Material.ACACIA_DOOR)); assertFalse(roof.roofBlocks(Material.ACACIA_DOOR));
assertTrue(Roof.roofBlocks(Material.HOPPER)); assertTrue(roof.roofBlocks(Material.HOPPER));
assertTrue(Roof.roofBlocks(Material.PURPLE_STAINED_GLASS_PANE)); assertTrue(roof.roofBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertTrue(Roof.roofBlocks(Material.BIRCH_TRAPDOOR)); assertTrue(roof.roofBlocks(Material.BIRCH_TRAPDOOR));
} }
/** /**
@ -185,12 +183,12 @@ public class RoofTest {
public void testWallBlocksNoGlowStoneNoPanes() { public void testWallBlocksNoGlowStoneNoPanes() {
s.setAllowGlowstone(false); s.setAllowGlowstone(false);
s.setAllowPanes(false); s.setAllowPanes(false);
assertFalse(Roof.roofBlocks(Material.ACACIA_BOAT)); assertFalse(roof.roofBlocks(Material.ACACIA_BOAT));
assertTrue(Roof.roofBlocks(Material.GLASS)); assertTrue(roof.roofBlocks(Material.GLASS));
assertFalse(Roof.roofBlocks(Material.GLOWSTONE)); assertFalse(roof.roofBlocks(Material.GLOWSTONE));
assertFalse(Roof.roofBlocks(Material.ACACIA_DOOR)); assertFalse(roof.roofBlocks(Material.ACACIA_DOOR));
assertTrue(Roof.roofBlocks(Material.HOPPER)); assertTrue(roof.roofBlocks(Material.HOPPER));
assertFalse(Roof.roofBlocks(Material.PURPLE_STAINED_GLASS_PANE)); assertFalse(roof.roofBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertTrue(Roof.roofBlocks(Material.BIRCH_TRAPDOOR)); assertTrue(roof.roofBlocks(Material.BIRCH_TRAPDOOR));
} }
} }

View File

@ -64,12 +64,10 @@ public class WallsTest {
when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true); when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true);
// Declare mock after mocking Bukkit // Declare mock after mocking Bukkit
roof = mock(Roof.class); roof = mock(Roof.class);
PowerMockito.mockStatic(Greenhouses.class, Mockito.RETURNS_MOCKS);
when(Greenhouses.getInstance()).thenReturn(addon);
s = new Settings(); s = new Settings();
when(addon.getSettings()).thenReturn(s); when(addon.getSettings()).thenReturn(s);
when(addon.getPlugin()).thenReturn(plugin); when(addon.getPlugin()).thenReturn(plugin);
when(addon.wallBlocks(any())).thenCallRealMethod();
walls = new Walls(cache); walls = new Walls(cache);
when(world.getMaxHeight()).thenReturn(255); when(world.getMaxHeight()).thenReturn(255);
when(location.getWorld()).thenReturn(world); when(location.getWorld()).thenReturn(world);
@ -201,13 +199,13 @@ public class WallsTest {
*/ */
@Test @Test
public void testWallBlocks() { public void testWallBlocks() {
assertFalse(Walls.wallBlocks(Material.ACACIA_BOAT)); assertFalse(addon.wallBlocks(Material.ACACIA_BOAT));
assertTrue(Walls.wallBlocks(Material.GLASS)); assertTrue(addon.wallBlocks(Material.GLASS));
assertTrue(Walls.wallBlocks(Material.GLOWSTONE)); assertTrue(addon.wallBlocks(Material.GLOWSTONE));
assertTrue(Walls.wallBlocks(Material.ACACIA_DOOR)); assertTrue(addon.wallBlocks(Material.ACACIA_DOOR));
assertTrue(Walls.wallBlocks(Material.HOPPER)); assertTrue(addon.wallBlocks(Material.HOPPER));
assertTrue(Walls.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE)); assertTrue(addon.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertFalse(Walls.wallBlocks(Material.BIRCH_TRAPDOOR)); assertFalse(addon.wallBlocks(Material.BIRCH_TRAPDOOR));
} }
/** /**
@ -217,13 +215,13 @@ public class WallsTest {
public void testWallBlocksNoGlowStoneNoPanes() { public void testWallBlocksNoGlowStoneNoPanes() {
s.setAllowGlowstone(false); s.setAllowGlowstone(false);
s.setAllowPanes(false); s.setAllowPanes(false);
assertFalse(Walls.wallBlocks(Material.ACACIA_BOAT)); assertFalse(addon.wallBlocks(Material.ACACIA_BOAT));
assertTrue(Walls.wallBlocks(Material.GLASS)); assertTrue(addon.wallBlocks(Material.GLASS));
assertFalse(Walls.wallBlocks(Material.GLOWSTONE)); assertFalse(addon.wallBlocks(Material.GLOWSTONE));
assertTrue(Walls.wallBlocks(Material.ACACIA_DOOR)); assertTrue(addon.wallBlocks(Material.ACACIA_DOOR));
assertTrue(Walls.wallBlocks(Material.HOPPER)); assertTrue(addon.wallBlocks(Material.HOPPER));
assertFalse(Walls.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE)); assertFalse(addon.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertFalse(Walls.wallBlocks(Material.BIRCH_TRAPDOOR)); assertFalse(addon.wallBlocks(Material.BIRCH_TRAPDOOR));
} }
/** /**

View File

@ -83,22 +83,22 @@ public class EcoSystemManagerTest {
// Liquid false // Liquid false
when(air.isEmpty()).thenReturn(true); when(air.isEmpty()).thenReturn(true);
when(air.isPassable()).thenReturn(true); when(air.isPassable()).thenReturn(true);
when(air.getRelative(eq(BlockFace.UP))).thenReturn(air); when(air.getRelative(BlockFace.UP)).thenReturn(air);
// Plant // Plant
// Empty false // Empty false
// Liquid false // Liquid false
when(plant.isPassable()).thenReturn(true); when(plant.isPassable()).thenReturn(true);
when(plant.getRelative(eq(BlockFace.UP))).thenReturn(air); when(plant.getRelative(BlockFace.UP)).thenReturn(air);
// Liquid // Liquid
// Empty false // Empty false
when(liquid.isLiquid()).thenReturn(true); when(liquid.isLiquid()).thenReturn(true);
when(liquid.isPassable()).thenReturn(true); when(liquid.isPassable()).thenReturn(true);
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(air); when(liquid.getRelative(BlockFace.UP)).thenReturn(air);
// Default for block // Default for block
// Empty false // Empty false
// Passable false // Passable false
// Liquid false // Liquid false
when(block.getRelative(eq(BlockFace.UP))).thenReturn(air); when(block.getRelative(BlockFace.UP)).thenReturn(air);
// Recipe // Recipe
when(recipe.noMobs()).thenReturn(true); when(recipe.noMobs()).thenReturn(true);

View File

@ -71,12 +71,14 @@ public class GreenhouseFinderTest {
when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true); when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true);
// Declare mock after mocking Bukkit // Declare mock after mocking Bukkit
roof = mock(Roof.class); roof = mock(Roof.class);
when(roof.roofBlocks(any())).thenCallRealMethod();
// Location // Location
when(location.getBlockX()).thenReturn(5); when(location.getBlockX()).thenReturn(5);
when(location.getBlockY()).thenReturn(14); when(location.getBlockY()).thenReturn(14);
when(location.getBlockZ()).thenReturn(25); when(location.getBlockZ()).thenReturn(25);
when(location.getWorld()).thenReturn(world); when(location.getWorld()).thenReturn(world);
// Addon
when(addon.wallBlocks(any())).thenCallRealMethod();
// Block // Block
when(cache.getBlockType(any())).thenReturn(Material.GLASS); when(cache.getBlockType(any())).thenReturn(Material.GLASS);
when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.GLASS); when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.GLASS);
@ -94,7 +96,7 @@ public class GreenhouseFinderTest {
when(cache.getMaxHeight()).thenReturn(30); when(cache.getMaxHeight()).thenReturn(30);
gf = new GreenhouseFinder(); gf = new GreenhouseFinder(addon);
cc = new CounterCheck(); cc = new CounterCheck();
} }