Compare commits

...

120 Commits

Author SHA1 Message Date
tastybento
93e3152efc Improved errors. 2024-01-21 08:24:40 -08:00
tastybento
0fc5857a4a Added mobs to biomes 2024-01-21 08:21:30 -08:00
tastybento
d1e771bad6 Version 1.8.0 2024-01-21 08:21:14 -08:00
tastybento
04a519ad51
Merge branch 'master' into develop 2024-01-20 07:48:02 -08:00
tastybento
3fc14f5cd8 Update to latest Spigot API 2024-01-13 09:29:53 -08:00
tastybento
8ce5cc0e3c
Update pom.xml 1.7.5 2024-01-13 07:09:18 -08:00
tastybento
15c2cea475 BentoBox 2.0.0 2023-11-12 13:53:05 -08:00
tastybento
4084876c4b
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>
2023-09-19 22:18:57 -07:00
tastybento
a7eeef7edc Code clean up. 2023-09-19 22:15:46 -07:00
tastybento
ca4f1a3f43
Merge branch 'master' into develop 2023-09-19 21:52:51 -07:00
tastybento
6bc1c5e4bc Merge branch 'develop' of https://github.com/BentoBoxWorld/Greenhouses.git into develop 2023-09-19 21:48:54 -07:00
tastybento
53cb40e7c4 Fixes mob spawning when no maxmob value given.
Found while doing #108
2023-09-19 21:48:45 -07:00
tastybento
e05e2806fe
Update pom.xml 2023-07-10 21:44:43 -07:00
tastybento
02c2135501 Added distribution required for Github Action 2023-06-24 13:55:46 -07:00
tastybento
605144e9f9 Update Github Action build script 2023-06-24 13:03:05 -07:00
tastybento
3f4647b547 Refactor to reduce complexity 2023-06-04 17:44:01 -07:00
tastybento
e75780e710 Refactor to reduce complexity 2023-06-04 17:36:08 -07:00
tastybento
7e4f0764e6 Reduced complexity 2023-06-04 09:19:15 -07:00
tastybento
398c8e91d5 Minor typos and grammar fixes 2023-06-04 09:11:15 -07:00
tastybento
2eed3dcd56 Refactored to reduce complexity 2023-06-04 09:03:38 -07:00
tastybento
3dd950459f Update surefire plugin 2023-06-04 08:57:01 -07:00
tastybento
cce4a655c6 Refactored to reduce complexity 2023-06-04 08:55:02 -07:00
tastybento
a2c2975329 Version 1.7.4 2023-06-04 08:46:33 -07:00
tastybento
e6a1cd17bb Removed static getInstance usage 2023-06-04 08:44:55 -07:00
BONNe
5fd9cbfd36
Release 1.7.3 (#107)
* 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

---------

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2023-04-15 15:19:55 -07:00
BONNe
db5ef4d5da
Create plugin.yml (#106)
* Create plugin.yml

* Update pom.xml

* Update GreenhousesPladdon.java
2023-04-08 18:39:46 +03:00
tastybento
ac1d6e6638 Remove debug 2023-03-26 10:25:40 -07:00
BONNe
3740268dfc
Update to new Bukkit Loader 2023-03-26 14:04:06 +03:00
tastybento
308dc225cd Remove unused imports 2023-03-18 11:17:52 -04:00
tastybento
95474d6c53 Fixed maxmobs typo instead of maxmob 2023-03-16 22:40:43 -04:00
tastybento
d2801dcd75 Fixes help text for user command.
Fixes #105
2023-03-10 08:50:18 -08:00
tastybento
e4bbb70acb Added tests to cover #99 2023-03-01 18:30:35 -08:00
tastybento
a4fc49689b Use updated Bucket event 2023-03-01 17:59:10 -08:00
tastybento
66270cf3e7 Add max mobs option #99 2023-03-01 08:26:34 -08:00
tastybento
4351247de2 Updated ReadMe 2023-02-25 08:22:44 -08:00
tastybento
9e407e659e Address bugs reported by SonarCloud 2023-02-09 17:27:57 -08:00
tastybento
d6df904fbc Updated Jacoco POM entry 2023-02-09 17:09:21 -08:00
tastybento
9af8816995 Merge branch 'develop' of https://github.com/BentoBoxWorld/Greenhouses.git into develop 2023-02-09 17:08:13 -08:00
tastybento
ce0eeae546
Add ${argLine} to get jacoco coverage 2023-02-09 15:18:43 -08:00
tastybento
cf276bbb24 Version 1.7.3 2023-01-01 22:31:46 -08:00
tastybento
de6a939bb9 Adds support for COCOA #97 2023-01-01 22:29:02 -08:00
tastybento
10a1d63778 Remove debug
https://github.com/BentoBoxWorld/Greenhouses/issues/97
2023-01-01 21:17:39 -08:00
tastybento
bba0b33133 Version 1.7.2 2022-12-29 20:00:16 -08:00
tastybento
4b69c39496 Version 1.7.1
Fix - not sure why it didn't survive the merge...
2022-12-29 19:59:14 -08:00
tastybento
fe0a794e68 Merge branch 'develop' 2022-12-29 19:47:48 -08:00
tastybento
1975df2732 Fixes #92
A world check was not being done in the overlap check.
2022-12-29 19:33:59 -08:00
tastybento
1f0b579d5a Enables water plants to be grown under water.
Fixes #93
2022-12-29 18:59:50 -08:00
tastybento
e5c62047ff Use new switch statement 2022-12-29 11:33:34 -08:00
tastybento
02490d3b85 Fix for glow lichen growing
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/96
2022-12-29 09:57:21 -08:00
tastybento
717903e346 Version 1.17.1
Java 17 update
2022-12-29 09:56:49 -08:00
Zorua162
61478ff4d5
Fixed issue where greenhouses could not be created below y=0 (#95)
* Fixed issue where greenhouses could not be greated below y=0

* Made the world floor change work with different lower world limits by using World#getMinHeight()
2022-10-30 18:50:40 +02:00
BONNe
a96d4d4c67
Release 1.7.0 (#91)
* Version 1.6.1

* Version 1.6.1

* Added Blue Orchids spawning in the Swamp biome

* Version 1.7.0

1.18.1 update

* German translation (#86)

* Translate de.yml via GitLocalize

* Translate de.yml via GitLocalize

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: xXjojojXx <sven.f92@gmx.de>

* Translate hu.yml via GitLocalize (#87)

Co-authored-by: driverdakid <tamascsiszar99@icloud.com>

* Translate ja.yml via GitLocalize (#88)

Co-authored-by: tastybento <tastybento@wasteofplastic.com>

* Translate zh-CN.yml via GitLocalize (#89)

Co-authored-by: tastybento <tastybento@wasteofplastic.com>

* Code refactoring around BoundingBox

* Fix test.

* Remove saving of greenhouses on exit.

https://github.com/BentoBoxWorld/Greenhouses/issues/85

* Add Pladdon Class (#90)

* Add Pladdon Class

Add Greenhouses Pladdon class.

* Add Spigot Annotations API

* Fix test. Avoid ambiguity.

* Add natural spawn protection listener.

Fixes #84

* Update gitignore for Eclipse

* Remove some code smells.

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: Florian CUNY <poslovitch@bentobox.world>
Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: xXjojojXx <sven.f92@gmx.de>
Co-authored-by: driverdakid <tamascsiszar99@icloud.com>
2022-05-16 14:49:06 +03:00
tastybento
8af2b2057d Remove some code smells. 2022-03-20 15:09:45 +00:00
tastybento
6f6745e849 Update gitignore for Eclipse 2022-03-20 15:01:53 +00:00
BONNe
0a016c2eaa Add natural spawn protection listener.
Fixes #84
2022-02-27 21:33:21 +02:00
tastybento
d64be1c518 Fix test. Avoid ambiguity. 2022-02-19 11:34:27 -08:00
BONNe
e2b779a9cb
Add Pladdon Class (#90)
* Add Pladdon Class

Add Greenhouses Pladdon class.

* Add Spigot Annotations API
2022-02-19 11:17:08 -08:00
tastybento
06230e4957 Remove saving of greenhouses on exit.
https://github.com/BentoBoxWorld/Greenhouses/issues/85
2022-01-01 16:03:04 -08:00
tastybento
0e76838721 Fix test. 2021-12-31 15:38:19 -08:00
tastybento
9a7cad85af Merge branch 'develop' of https://github.com/BentoBoxWorld/Greenhouses.git into develop 2021-12-31 13:32:15 -08:00
tastybento
5808c5933a Code refactoring around BoundingBox 2021-12-31 13:32:03 -08:00
gitlocalize-app[bot]
cf7b1c7164
Translate zh-CN.yml via GitLocalize (#89)
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2021-12-31 09:25:28 -08:00
gitlocalize-app[bot]
c5b80b2f4a
Translate ja.yml via GitLocalize (#88)
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2021-12-31 09:24:08 -08:00
gitlocalize-app[bot]
b90559122b
Translate hu.yml via GitLocalize (#87)
Co-authored-by: driverdakid <tamascsiszar99@icloud.com>
2021-12-31 09:20:45 -08:00
gitlocalize-app[bot]
df60720e75
German translation (#86)
* Translate de.yml via GitLocalize

* Translate de.yml via GitLocalize

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: xXjojojXx <sven.f92@gmx.de>
2021-12-31 09:18:46 -08:00
tastybento
6e41588405 Merged branch 'develop' https://github.com/BentoBoxWorld/Greenhouses.git
into develop

Conflicts:
	pom.xml
2021-12-21 14:29:52 -08:00
tastybento
b7c30cb608 Version 1.7.0
1.18.1 update
2021-12-21 14:28:31 -08:00
Florian CUNY
925e50eb46
Added Blue Orchids spawning in the Swamp biome 2021-10-31 11:16:06 +01:00
Florian CUNY
61e1db5f43
Version 1.6.1 2021-10-31 11:14:13 +01:00
tastybento
024b311180 Version 1.6.1 2021-09-26 13:01:01 -07:00
tastybento
4aec983c81 Cauldron will fill up with snow
Also, snow will pile up as expected.

https://github.com/BentoBoxWorld/Greenhouses/issues/83
2021-09-26 12:54:46 -07:00
tastybento
c660575456 Remove lag from legacy Material support issue
Spigot pulls in all the legacy marerials if you loop through the
material values.
2021-09-25 11:34:36 -07:00
tastybento
354806c060 Fills up cauldrons with snow.
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/82
2021-09-25 11:19:39 -07:00
tastybento
47bead9145 Fixes bug where floor blocks were not converted.
This was introduced by commit db3054ab0c

https://github.com/BentoBoxWorld/Greenhouses/issues/81
2021-09-25 11:04:01 -07:00
tastybento
8a3e0eb403 Conde clean up from IntelliJ 2021-09-18 10:33:48 -07:00
tastybento
4708a70e24 Added support for ceiling growing plants.
VINES, WEEPING_VINES_PLANT, SPORE_BLOSSOM, CAVE_VINES_PLANT,
TWISTING_VINES_PLANT

https://github.com/BentoBoxWorld/Greenhouses/issues/81
2021-09-18 10:12:47 -07:00
tastybento
308cb7f16a Version 1.6.0
Changes to Java 16 surefire module declarations.
2021-09-18 10:08:24 -07:00
tastybento
818f6fc925 Null checks. 2021-08-01 16:17:12 -07:00
tastybento
0b9019fbfa Fix test 2021-08-01 15:29:36 -07:00
tastybento
e61e76134b Make BiomeRecipe nonNull 2021-08-01 15:26:21 -07:00
tastybento
a44d9d1578 Java 16 Action 2021-07-31 23:13:22 -07:00
tastybento
38de645e6b Move to Java 16 2021-07-31 23:10:07 -07:00
tastybento
de4a4c17c0 Merge branch 'develop' of https://github.com/BentoBoxWorld/Greenhouses.git into develop 2021-07-31 15:09:30 -07:00
Rosskaman
d1d3004262
Allow omitting LocalMaterial in biomes.yml if you don't want a local blo… (#79) 2021-05-09 11:47:54 -07:00
Rosskaman
db3054ab0c
Optimize convertBlocks and convertBlock methods by reducing the amoun… (#78) 2021-04-19 07:26:43 -07:00
tastybento
9621b2f5eb Version 1.5.4 2021-04-08 17:15:08 -07:00
tastybento
7154877f14 Blocks under the walls were classed as walls.
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/77
2021-04-08 17:13:32 -07:00
tastybento
80c985ad7d Version 1.5.3 2021-03-07 13:46:34 -08:00
tastybento
f345496a6c Version 1.5.2 2021-03-07 13:45:01 -08:00
tastybento
19cd685900 Prevent pistons pulling blocks out.
Allow piston pushing of blocks above or below the greenhouse because
biomes are 3D now.

https://github.com/BentoBoxWorld/Greenhouses/issues/77
2021-02-18 17:09:42 -08:00
tastybento
999ea07ef0 Prevents piston-pushing of any wall or roof block
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/77
2021-02-17 16:49:42 -08:00
tastybento
29f04ef673 Fix bug with water in offhand and nether greenhouse
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/76
2021-02-17 12:43:39 -08:00
tastybento
f2faeb71df Correct comment 2021-02-13 10:30:05 -08:00
tastybento
69818923fc Setting hopper to null is valid
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/75
2021-02-13 10:27:57 -08:00
tastybento
54f956cf23 Merge branch 'develop' of https://github.com/BentoBoxWorld/Greenhouses.git into develop 2021-02-03 17:40:53 -08:00
tastybento
88b655ee00 Trapdoors in roof were not counted as doors.
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/73
2021-02-03 17:40:42 -08:00
Huynh Tien
0e2a6159c5
add Vietnamese (#72) 2021-02-03 12:15:43 -08:00
tastybento
39a88a4f84 Fix bug where water couldn't be placed outside of greenhouse. 2021-01-31 18:58:06 -08:00
tastybento
b0a0ae6e88 Fixes water placement in nether-related areas
https://github.com/BentoBoxWorld/Greenhouses/issues/70
https://github.com/BentoBoxWorld/Greenhouses/issues/71
2021-01-30 11:45:56 -08:00
tastybento
a8dec9a89b Prevents placing of water in nether biome greenhouses.
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/71
2021-01-29 20:13:34 -08:00
tastybento
79d1c743c1 Corrected tests. 2021-01-28 20:21:29 -08:00
tastybento
63e6a5d3b0 Do not dump water when clicking on a door in the nether.
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/70
2021-01-28 20:13:27 -08:00
tastybento
8118280129 Version 1.5.1 2021-01-28 19:50:27 -08:00
tastybento
01a5548119 Removed admin commands 2021-01-17 11:42:54 -08:00
tastybento
0397f8fcfa Restore interrupted state 2021-01-17 11:34:41 -08:00
tastybento
1e83703f9b Deal with potential exceptions better. 2021-01-17 10:57:30 -08:00
tastybento
07763cafdf Fixes reporting of success.
https://github.com/BentoBoxWorld/Greenhouses/issues/68
2021-01-17 09:58:04 -08:00
tastybento
5c583284ff Cleans up locale 2021-01-17 09:57:46 -08:00
tastybento
b90cba0b60 WIP 2021-01-17 09:35:06 -08:00
tastybento
148c92ecf8 Perform SonarCloud analysis via GitHub actions 2021-01-17 08:46:07 -08:00
tastybento
27df88aaf3 Made greenhouse ecosystem checking async.
https://github.com/BentoBoxWorld/Greenhouses/issues/68
2021-01-17 08:41:50 -08:00
tastybento
cd5a0ce2a1 Async greenhouse building
https://github.com/BentoBoxWorld/Greenhouses/issues/68
2021-01-16 18:57:01 -08:00
tastybento
baa933881d Walls are now async 2021-01-16 14:27:29 -08:00
tastybento
ff487a7990 Fixed RoofTest 2021-01-16 09:58:25 -08:00
tastybento
cc114027da Made roof search async 2021-01-16 09:16:09 -08:00
tastybento
08e9c25172 Shuffles where plants are grown in the greenhouse
Fixes https://github.com/BentoBoxWorld/Greenhouses/issues/54
2021-01-11 17:12:18 -08:00
tastybento
2ccb25e3fc Convert grass and gravel in desert 2021-01-10 11:57:17 -08:00
tastybento
5a4d1a35c3 Use internal bounding box 2021-01-10 11:57:02 -08:00
tastybento
fad9936e24 Added setting for glass panes
Added test cases and class for settings

https://github.com/BentoBoxWorld/Greenhouses/issues/66
2021-01-10 10:51:35 -08:00
tastybento
7dd9212797 Version 1.5.0 2021-01-10 10:20:20 -08:00
51 changed files with 3054 additions and 1959 deletions

38
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Build
on:
push:
branches:
- develop
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: 17
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/target/
/.classpath
/.project
/.DS_Store
/Greenhouses-addon.iml
/.idea/

View File

@ -1,17 +0,0 @@
language: java
sudo: false
addons:
sonarcloud:
organization: "bentobox-world"
jdk:
- openjdk11
script:
# the following command line builds the project, runs the tests with coverage and then execute the SonarCloud analysis
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install sonar:sonar -Dsonar.projectKey=BentoBoxWorld_Greenhouses
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'

View File

@ -1,8 +1,9 @@
# Greenhouses - an add-on for BentoBox # Greenhouses - an add-on for BentoBox
## Note for 1.15.x + servers [![Build Status](https://ci.codemc.org/buildStatus/icon?job=BentoBoxWorld/Greenhouses)](https://ci.codemc.org/job/BentoBoxWorld/job/Greenhouses/)[
![Bugs](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_Greenhouses&metric=bugs)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_Greenhouses)
Biomes have changed so that they take up a 4x4 area and so greenhouse biomes now can bleed outside of the greenhouse. Unfortunately, this cannot be mitigated (as far as I know). If you have a good imagination, you can say that the biome of the greenhouse influences the surroundings a bit and it is natural! So it's a feature, not a bug! [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_Greenhouses&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_Greenhouses)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_Greenhouses&metric=ncloc)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_Greenhouses)
## About ## About
@ -14,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**)
@ -26,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.
@ -41,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.

73
pom.xml
View File

@ -43,16 +43,19 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>17</java.version>
<powermock.version>2.0.2</powermock.version> <powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions --> <!-- More visible way how to change dependency versions -->
<spigot.version>1.16.3-R0.1-SNAPSHOT</spigot.version> <spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.15.4</bentobox.version> <bentobox.version>2.0.0-SNAPSHOT</bentobox.version>
<!-- 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.4.2</build.version> <build.version>1.8.0</build.version>
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<sonar.projectKey>BentoBoxWorld_Greenhouses</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
</properties> </properties>
<profiles> <profiles>
@ -93,30 +96,6 @@
<build.number></build.number> <build.number></build.number>
</properties> </properties>
</profile> </profile>
<profile>
<id>sonar</id>
<properties>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>bentobox-world</sonar.organization>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.7.0.1746</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sonar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles> </profiles>
<repositories> <repositories>
@ -147,7 +126,7 @@
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>3.0.0</version> <version>3.11.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -206,14 +185,21 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version> <version>3.7.0</version>
<configuration> <configuration>
<source>${java.version}</source> <release>${java.version}</release>
<target>${java.version}</target>
</configuration> </configuration>
</plugin> </plugin>
<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>2.22.0</version> <version>3.1.0</version>
<configuration>
<argLine>
${argLine}
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
</argLine>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@ -228,10 +214,12 @@
<show>public</show> <show>public</show>
<failOnError>false</failOnError> <failOnError>false</failOnError>
<additionalJOption>-Xdoclint:none</additionalJOption> <additionalJOption>-Xdoclint:none</additionalJOption>
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<id>attach-javadocs</id> <id>attach-javadocs</id>
<phase>install</phase>
<goals> <goals>
<goal>jar</goal> <goal>jar</goal>
</goals> </goals>
@ -264,30 +252,37 @@
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version> <version>0.8.10</version>
<configuration> <configuration>
<append>true</append> <append>true</append>
<excludes> <excludes>
<!-- This is required to prevent Jacoco from adding synthetic fields <!-- This is required to prevent Jacoco from adding
to a JavaBean class (causes errors in testing) --> synthetic fields to a JavaBean class (causes errors in testing) -->
<exclude>**/*Names*</exclude> <exclude>**/*Names*</exclude>
<!-- Prevents the Material is too large to mock error -->
<exclude>org/bukkit/Material*</exclude>
</excludes> </excludes>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<id>pre-unit-test</id> <id>prepare-agent</id>
<goals> <goals>
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
</execution> </execution>
<execution> <execution>
<id>post-unit-test</id> <id>report</id>
<goals> <goals>
<goal>report</goal> <goal>report</goal>
</goals> </goals>
<configuration>
<formats>
<format>XML</format>
</formats>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

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);
} }
@ -92,9 +87,8 @@ public class Greenhouses extends Addon {
*/ */
@Override @Override
public void onDisable() { public void onDisable() {
if (manager != null) { if (manager != null && manager.getEcoMgr() != null) {
manager.saveGreenhouses(); manager.getEcoMgr().cancel();
if (manager.getEcoMgr() != null) manager.getEcoMgr().cancel();
} }
} }
@ -123,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

@ -0,0 +1,18 @@
package world.bentobox.greenhouses;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.Pladdon;
/**
* @author tastybento
*/
public class GreenhousesPladdon extends Pladdon
{
@Override
public Addon getAddon()
{
return new Greenhouses();
}
}

View File

@ -23,10 +23,13 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "greenhouses.game-modes") @ConfigEntry(path = "greenhouses.game-modes")
private List<String> gameModes = new ArrayList<>(); private List<String> gameModes = new ArrayList<>();
@ConfigComment("")
@ConfigComment("Show loaded recipe details during startup of server") @ConfigComment("Show loaded recipe details during startup of server")
@ConfigEntry(path = "greenhouses.startup-log") @ConfigEntry(path = "greenhouses.startup-log")
private boolean startupLog = false; private boolean startupLog = false;
@ConfigComment("")
@ConfigComment("Weather and ecosystem settings") @ConfigComment("Weather and ecosystem settings")
@ConfigComment("How often it should snow in the g/h when the weather is raining, in seconds") @ConfigComment("How often it should snow in the g/h when the weather is raining, in seconds")
@ConfigEntry(path = "greenhouses.snowspeed") @ConfigEntry(path = "greenhouses.snowspeed")
@ -40,6 +43,7 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "greenhouses.snowdensity") @ConfigEntry(path = "greenhouses.snowdensity")
private double snowDensity = 0.1; private double snowDensity = 0.1;
@ConfigComment("")
@ConfigComment("Biome activity") @ConfigComment("Biome activity")
@ConfigComment("How often should greenhouse biomes be checked to make sure they are still valid") @ConfigComment("How often should greenhouse biomes be checked to make sure they are still valid")
@ConfigEntry(path = "greenhouses.ecotick") @ConfigEntry(path = "greenhouses.ecotick")
@ -56,6 +60,7 @@ public class Settings implements ConfigObject {
private int mobTick = 5; private int mobTick = 5;
@ConfigComment("")
@ConfigComment("Default settings for greenhouse actions") @ConfigComment("Default settings for greenhouse actions")
@ConfigComment("Allow lava or water to flow out of a greenhouse, e.g. through the door, floor") @ConfigComment("Allow lava or water to flow out of a greenhouse, e.g. through the door, floor")
@ConfigEntry(path = "greenhouses.allowflowout") @ConfigEntry(path = "greenhouses.allowflowout")
@ -64,10 +69,15 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "greenhouses.allowflowin") @ConfigEntry(path = "greenhouses.allowflowin")
private boolean allowFlowIn; private boolean allowFlowIn;
@ConfigComment("")
@ConfigComment("Allow glowstone to be used as well as glass in roof and walls") @ConfigComment("Allow glowstone to be used as well as glass in roof and walls")
@ConfigEntry(path = "greenhouses.allowglowstone") @ConfigEntry(path = "greenhouses.allowglowstone")
private boolean allowGlowstone = true; private boolean allowGlowstone = true;
@ConfigComment("")
@ConfigComment("Allow glass panes to be used to build greenhouses")
@ConfigEntry(path = "greenhouses.allowpanes")
private boolean allowPanes = true;
/** /**
* @return the gameModes * @return the gameModes
*/ */
@ -212,5 +222,17 @@ public class Settings implements ConfigObject {
public void setAllowGlowstone(boolean allowGlowstone) { public void setAllowGlowstone(boolean allowGlowstone) {
this.allowGlowstone = allowGlowstone; this.allowGlowstone = allowGlowstone;
} }
/**
* @return the allowPanes
*/
public boolean isAllowPanes() {
return allowPanes;
}
/**
* @param allowPanes the allowPanes to set
*/
public void setAllowPanes(boolean allowPanes) {
this.allowPanes = allowPanes;
}
} }

View File

@ -1,6 +1,8 @@
package world.bentobox.greenhouses.data; package world.bentobox.greenhouses.data;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Location; import org.bukkit.Location;
@ -8,6 +10,7 @@ import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.util.BoundingBox; import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -50,6 +53,12 @@ public class Greenhouse implements DataObject {
*/ */
public Greenhouse() {} public Greenhouse() {}
/**
* Create a greenhouse
* @param world - world
* @param walls - wall object
* @param ceilingHeight - ceiling height
*/
public Greenhouse(World world, Walls walls, int ceilingHeight) { public Greenhouse(World world, Walls walls, int ceilingHeight) {
this.location = new Location(world, walls.getMinX(), walls.getFloor(), walls.getMinZ()); this.location = new Location(world, walls.getMinX(), walls.getFloor(), walls.getMinZ());
Location location2 = new Location(world, walls.getMaxX() + 1D, ceilingHeight + 1D, walls.getMaxZ() + 1D); Location location2 = new Location(world, walls.getMaxX() + 1D, ceilingHeight + 1D, walls.getMaxZ() + 1D);
@ -147,18 +156,30 @@ public class Greenhouse implements DataObject {
} }
/** /**
* @param roofHopperLocation the roofHopperLocation to set * @param v the roofHopperLocation to set
*/ */
public void setRoofHopperLocation(Location roofHopperLocation) { public void setRoofHopperLocation(@Nullable Vector v) {
this.roofHopperLocation = roofHopperLocation; if (v == null || getWorld() == null) {
this.roofHopperLocation = null;
} else {
this.roofHopperLocation = v.toLocation(getWorld());
}
} }
/** /**
* @return the boundingBox * @return the boundingBox
*/ */
@Nullable @NonNull
public BoundingBox getBoundingBox() { public BoundingBox getBoundingBox() {
return boundingBox; return Objects.requireNonNull(boundingBox);
}
/**
* @return a bounding box of the greenhouse that does not include the walls or roof
*/
@NonNull
public BoundingBox getInternalBoundingBox() {
return getBoundingBox().clone().expand(-1D);
} }
/** /**
@ -188,9 +209,9 @@ public class Greenhouse implements DataObject {
/** /**
* @return the world * @return the world
*/ */
@NonNull @Nullable
public World getWorld() { public World getWorld() {
return this.getLocation().getWorld(); return this.getLocation() == null ? null : this.getLocation().getWorld();
} }
/** /**
@ -199,7 +220,9 @@ public class Greenhouse implements DataObject {
* @return true if inside the greenhouse * @return true if inside the greenhouse
*/ */
public boolean contains(Location location2) { public boolean contains(Location location2) {
return location.getWorld().equals(location2.getWorld()) && boundingBox.contains(location2.toVector()); return getLocation() != null && getLocation().getWorld() != null
&& getLocation().getWorld().equals(location2.getWorld())
&& getBoundingBox().contains(location2.toVector());
} }
/** /**
@ -213,11 +236,11 @@ public class Greenhouse implements DataObject {
/** /**
* Get the biome recipe for this greenhouse * Get the biome recipe for this greenhouse
* @return biome recipe or null * @return biome recipe or a degenerate recipe
*/ */
@Nullable @NonNull
public BiomeRecipe getBiomeRecipe() { public BiomeRecipe getBiomeRecipe() {
return RecipeManager.getBiomeRecipies(biomeRecipeName).orElse(null); return RecipeManager.getBiomeRecipies(biomeRecipeName).orElse(new BiomeRecipe());
} }
/** /**
@ -230,8 +253,24 @@ public class Greenhouse implements DataObject {
/** /**
* @return the missingBlocks * @return the missingBlocks
*/ */
@Nullable @NonNull
public Map<Material, Integer> getMissingBlocks() { public Map<Material, Integer> getMissingBlocks() {
return missingBlocks; return Objects.requireNonNullElseGet(missingBlocks, HashMap::new);
}
/**
* Check if a location is a wall or roof block
* @param l - location
* @return true if wall or roof block
*/
public boolean isRoofOrWallBlock(Location l) {
final BoundingBox bb = getBoundingBox();
return (l.getBlockY() > this.getFloorHeight()
&& ((l.getBlockY() == getCeilingHeight() - 1)
|| l.getBlockX() == (int)bb.getMinX()
|| l.getBlockX() == (int)bb.getMaxX() - 1
|| l.getBlockZ() == (int)bb.getMinZ()
|| l.getBlockZ() == (int)bb.getMaxZ() - 1
));
} }
} }

View File

@ -1,17 +1,21 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Particle; import org.bukkit.Particle;
@ -21,11 +25,13 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Bisected; import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.block.data.type.Cocoa;
import org.bukkit.block.data.type.GlowLichen;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Hoglin; import org.bukkit.entity.Hoglin;
import org.bukkit.entity.Piglin; import org.bukkit.entity.Piglin;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import com.google.common.base.Enums; import com.google.common.base.Enums;
@ -35,7 +41,9 @@ import com.google.common.collect.Multimap;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.data.Greenhouse; import world.bentobox.greenhouses.data.Greenhouse;
import world.bentobox.greenhouses.managers.EcoSystemManager.GrowthBlock;
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult; import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
import world.bentobox.greenhouses.world.AsyncWorldCache;
public class BiomeRecipe implements Comparable<BiomeRecipe> { public class BiomeRecipe implements Comparable<BiomeRecipe> {
private static final String CHANCE_FOR = "% chance for "; private static final String CHANCE_FOR = "% chance for ";
@ -46,13 +54,37 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
private String name; private String name;
private String friendlyName; private String friendlyName;
private static final List<Material> CEILING_PLANTS = new ArrayList<>();
static {
CEILING_PLANTS.add(Material.VINE);
Enums.getIfPresent(Material.class, "SPORE_BLOSSOM").toJavaUtil().ifPresent(CEILING_PLANTS::add);
Enums.getIfPresent(Material.class, "CAVE_VINES_PLANT").toJavaUtil().ifPresent(CEILING_PLANTS::add);
Enums.getIfPresent(Material.class, "TWISTING_VINES_PLANT").toJavaUtil().ifPresent(CEILING_PLANTS::add);
Enums.getIfPresent(Material.class, "WEEPING_VINES_PLANT").toJavaUtil().ifPresent(CEILING_PLANTS::add);
}
private static final List<BlockFace> ADJ_BLOCKS = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST); private static final List<BlockFace> ADJ_BLOCKS = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST);
private static final List<BlockFace> SIDE_BLOCKS = Arrays.asList( BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST);
private static final List<Material> UNDERWATER_PLANTS;
static {
List<Material> m = new ArrayList<>();
Arrays.stream(Material.values()).filter(c -> c.name().contains("CORAL")).forEach(m::add);
m.add(Material.SEA_LANTERN);
m.add(Material.SEA_PICKLE);
m.add(Material.SEAGRASS);
m.add(Material.KELP);
m.add(Material.GLOW_LICHEN);
UNDERWATER_PLANTS = Collections.unmodifiableList(m);
}
// Content requirements // Content requirements
// Material, Type, Qty. There can be more than one type of material required // Material, Type, Qty. There can be more than one type of material required
private final Map<Material, Integer> requiredBlocks = new EnumMap<>(Material.class); private final Map<Material, Integer> requiredBlocks = new EnumMap<>(Material.class);
// Plants /**
* Tree map of plants
*/
private final TreeMap<Double, GreenhousePlant> plantTree = new TreeMap<>(); private final TreeMap<Double, GreenhousePlant> plantTree = new TreeMap<>();
private final TreeMap<Double, GreenhousePlant> underwaterPlants = new TreeMap<>();
// Mobs // Mobs
// Entity Type, Material to Spawn on, Probability // Entity Type, Material to Spawn on, Probability
@ -60,8 +92,7 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
// Conversions // Conversions
// Original Material, Original Type, New Material, New Type, Probability // Original Material, Original Type, New Material, New Type, Probability
//private final Map<Material, GreenhouseBlockConversions> conversionBlocks = new EnumMap<>(Material.class); private final Multimap<Material, GreenhouseBlockConversions> conversionBlocks = ArrayListMultimap.create();
private Multimap<Material, GreenhouseBlockConversions> conversionBlocks = ArrayListMultimap.create();
private int mobLimit; private int mobLimit;
private int waterCoverage; private int waterCoverage;
@ -70,8 +101,12 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
private String permission = ""; private String permission = "";
private final Random random = new Random(); private final Random random = new Random();
private int maxMob;
/**
* Create a degenerate recipe with nothing in it
*/
public BiomeRecipe() {} public BiomeRecipe() {}
/** /**
@ -104,7 +139,7 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
/** /**
* @param mobType - entity type * @param mobType - entity type
* @param mobProbability - reltive probability * @param mobProbability - relative probability
* @param mobSpawnOn - material to spawn on * @param mobSpawnOn - material to spawn on
* @return true if add is successful * @return true if add is successful
*/ */
@ -118,7 +153,7 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
mobTree.put(lastProb + probability, new GreenhouseMob(mobType, mobSpawnOn)); mobTree.put(lastProb + probability, new GreenhouseMob(mobType, mobSpawnOn));
return true; return true;
} else { } else {
addon.logError("Mob chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + mobType.toString()); addon.logError("Mob chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + mobType);
return false; return false;
} }
} }
@ -133,11 +168,12 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
*/ */
public boolean addPlants(Material plantMaterial, double plantProbability, Material plantGrowOn) { public boolean addPlants(Material plantMaterial, double plantProbability, Material plantGrowOn) {
double probability = plantProbability/100; double probability = plantProbability/100;
TreeMap<Double, GreenhousePlant> map = UNDERWATER_PLANTS.contains(plantMaterial) ? underwaterPlants : plantTree;
// Add up all the probabilities in the list so far // Add up all the probabilities in the list so far
double lastProb = plantTree.isEmpty() ? 0D : plantTree.lastKey(); double lastProb = map.isEmpty() ? 0D : map.lastKey();
if ((1D - lastProb) >= probability) { if ((1D - lastProb) >= probability) {
// Add to probability tree // Add to probability tree
plantTree.put(lastProb + probability, new GreenhousePlant(plantMaterial, plantGrowOn)); map.put(lastProb + probability, new GreenhousePlant(plantMaterial, plantGrowOn));
} else { } else {
addon.logError("Plant chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + plantMaterial.toString()); addon.logError("Plant chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + plantMaterial.toString());
return false; return false;
@ -155,29 +191,48 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
startupLog(" " + blockMaterial + " x " + blockQty); startupLog(" " + blockMaterial + " x " + blockQty);
} }
// Check required blocks
/** /**
* Checks greenhouse meets recipe requirements. * Checks greenhouse meets recipe requirements.
* @return GreenhouseResult - result * @return GreenhouseResult - result
*/ */
public Set<GreenhouseResult> checkRecipe(Greenhouse gh) { public CompletableFuture<Set<GreenhouseResult>> checkRecipe(Greenhouse gh) {
Set<GreenhouseResult> result = new HashSet<>(); CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> checkRecipeAsync(r, gh));
return r;
}
/**
* Check greenhouse meets recipe requirements. Expected to be run async.
* @param r - future to complete when done
* @param gh - greenhouse
* @return set of results from the check
*/
private Set<GreenhouseResult> checkRecipeAsync(CompletableFuture<Set<GreenhouseResult>> r, Greenhouse gh) {
AsyncWorldCache cache = new AsyncWorldCache(addon, gh.getWorld());
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
Block b = gh.getWorld().getBlockAt(x, y, z); Set<GreenhouseResult> result = checkRatios(blockCount, area);
Material t = b.getType();
if (!t.equals(Material.AIR)) { // Compare to the required blocks
blockCount.putIfAbsent(t, 0); Map<Material, Integer> missingBlocks = requiredBlocks.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() - blockCount.getOrDefault(e.getKey(), 0)));
blockCount.merge(t, 1, Integer::sum); // 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)
@ -205,35 +260,55 @@ 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 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 gh - greenhouse
* @param b - block to check * @param b - block to check
*/ */
public void convertBlock(Greenhouse gh, Block b) { public void convertBlock(Block b) {
conversionBlocks.get(b.getType()).stream().filter(Objects::nonNull) Material bType = b.getType();
.filter(bc -> random.nextDouble() < bc.getProbability()) // 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
.forEach(bc -> { if(conversionBlocks.keySet().contains(bType)) {
// Check if the block is in the right area, up, down, n,s,e,w for(GreenhouseBlockConversions conversionOption : conversionBlocks.get(bType)) {
if (ADJ_BLOCKS.stream().map(b::getRelative) rollTheDice(b, conversionOption);
.filter(r -> gh.contains(r.getLocation()))
.map(Block::getType)
.anyMatch(m -> bc.getLocalMaterial() == null || m == bc.getLocalMaterial())) {
// Convert!
b.setType(bc.getNewMaterial());
} }
}); }
}
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());
}
}
} }
/** /**
@ -319,28 +394,24 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
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));
return 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 -> m.getMobSpawnOn().map(b.getRelative(BlockFace.DOWN).getType()::equals).orElse(true)) .filter(m -> Optional.of(m.mobSpawnOn())
.map(b.getRelative(BlockFace.DOWN).getType()::equals)
.orElse(true))
// If spawn occurs, check if it can fit inside greenhouse // If spawn occurs, check if it can fit inside greenhouse
.map(m -> { .map(m -> {
Entity entity = b.getWorld().spawnEntity(spawnLoc, m.getMobType()); Entity entity = b.getWorld().spawnEntity(spawnLoc, m.mobType());
if (entity != null) { preventZombie(entity);
preventZombie(entity); return addon
return addon .getManager()
.getManager() .getMap()
.getMap() .getGreenhouse(b.getLocation()).map(gh -> {
.getGreenhouse(b.getLocation()).map(gh -> { if (!gh.getInternalBoundingBox().contains(entity.getBoundingBox())) {
BoundingBox interior = gh.getBoundingBox().clone(); entity.remove();
interior.expand(-1, -1, -1); return false;
if (!interior.contains(entity.getBoundingBox())) { }
entity.remove(); return true;
return false; }).orElse(false);
}
return true;
}).orElse(false);
}
return false;
}).orElse(false); }).orElse(false);
} }
/** /**
@ -357,13 +428,11 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
return; return;
} }
if (entity instanceof Piglin) { if (entity instanceof Piglin p) {
Piglin p = (Piglin)entity;
p.setImmuneToZombification(true); p.setImmuneToZombification(true);
return; return;
} }
if (entity instanceof Hoglin) { if (entity instanceof Hoglin h) {
Hoglin h = (Hoglin)entity;
h.setImmuneToZombification(true); h.setImmuneToZombification(true);
} }
} }
@ -377,18 +446,21 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
return key == null ? Optional.empty() : Optional.ofNullable(mobTree.get(key)); return key == null ? Optional.empty() : Optional.ofNullable(mobTree.get(key));
} }
private Optional<GreenhousePlant> getRandomPlant() { private Optional<GreenhousePlant> getRandomPlant(boolean underwater) {
// Grow a random plant that can grow // Grow a random plant that can grow
double r = random.nextDouble(); double r = random.nextDouble();
Double key = plantTree.ceilingKey(r); Double key = underwater ? underwaterPlants.ceilingKey(r) : plantTree.ceilingKey(r);
return key == null ? Optional.empty() : Optional.ofNullable(plantTree.get(key)); if (key == null) {
return Optional.empty();
}
return Optional.ofNullable(underwater ? underwaterPlants.get(key) : plantTree.get(key));
} }
/** /**
* @return a list of blocks that are required for this recipe * @return a list of blocks that are required for this recipe
*/ */
public List<String> getRecipeBlocks() { public List<String> getRecipeBlocks() {
return requiredBlocks.entrySet().stream().map(en -> Util.prettifyText(en.getKey().toString()) + " x " + en.getValue()).collect(Collectors.toList()); return requiredBlocks.entrySet().stream().map(en -> Util.prettifyText(en.getKey().toString()) + " x " + en.getValue()).toList();
} }
/** /**
@ -400,29 +472,14 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
/** /**
* Plants a plant on block bl if it makes sense. * Plants a plant on block bl if it makes sense.
* @param bl - block * @param block - block that can have growth
* @param underwater - if the block is underwater or not
* @return true if successful * @return true if successful
*/ */
public boolean growPlant(Block bl) { public boolean growPlant(GrowthBlock block, boolean underwater) {
if (!bl.isEmpty()) { Block bl = block.block();
return false; return getRandomPlant(underwater).map(p -> {
} if (bl.getY() != 0 && canGrowOn(block, p) && plantIt(bl, p)) {
return getRandomPlant().map(p -> {
if (bl.getY() != 0 && p.getPlantGrownOn().map(m -> m.equals(bl.getRelative(BlockFace.DOWN).getType())).orElse(false)) {
BlockData dataBottom = p.getPlantMaterial().createBlockData();
if (dataBottom instanceof Bisected) {
((Bisected) dataBottom).setHalf(Bisected.Half.BOTTOM);
BlockData dataTop = p.getPlantMaterial().createBlockData();
((Bisected) dataTop).setHalf(Bisected.Half.TOP);
if (bl.getRelative(BlockFace.UP).getType().equals(Material.AIR)) {
bl.setBlockData(dataBottom, false);
bl.getRelative(BlockFace.UP).setBlockData(dataTop, false);
} else {
return false; // No room
}
} else {
bl.setBlockData(dataBottom, false);
}
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;
} }
@ -430,6 +487,144 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
}).orElse(false); }).orElse(false);
} }
/**
* Plants the plant
* @param bl - block to turn into a plant
* @param p - the greenhouse plant to be grown
* @return true if successful, false if not
*/
private boolean plantIt(Block bl, GreenhousePlant p) {
boolean underwater = bl.getType().equals(Material.WATER);
BlockData dataBottom = p.plantMaterial().createBlockData();
// Check if this is a double-height plant
if (dataBottom instanceof Bisected bi) {
// Double-height plant
bi.setHalf(Bisected.Half.BOTTOM);
BlockData dataTop = p.plantMaterial().createBlockData();
((Bisected) dataTop).setHalf(Bisected.Half.TOP);
if (bl.getRelative(BlockFace.UP).getType().equals(Material.AIR)) {
bl.setBlockData(dataBottom, false);
bl.getRelative(BlockFace.UP).setBlockData(dataTop, false);
} else {
return false; // No room
}
} else if (p.plantMaterial().equals(Material.GLOW_LICHEN)) {
return placeLichen(bl);
} else if (p.plantMaterial().equals(Material.COCOA)) {
return placeCocoa(bl);
} else {
if (dataBottom instanceof Waterlogged wl) {
wl.setWaterlogged(underwater);
bl.setBlockData(wl, false);
} else {
// Single height plant
bl.setBlockData(dataBottom, false);
}
}
return true;
}
private boolean placeCocoa(Block bl) {
// Get the source block below this one
Block b = bl.getRelative(BlockFace.DOWN);
if (!b.getType().equals(Material.JUNGLE_LOG)) {
return false;
}
// Find a spot for cocoa
BlockFace d = null;
for (BlockFace adj : SIDE_BLOCKS) {
if (b.getRelative(adj).getType().equals(Material.AIR)) {
d = adj;
break;
}
}
if (d == null) {
return false;
}
Block bb = b.getRelative(d);
bb.setType(Material.COCOA);
BlockFace opp = d.getOppositeFace();
if(bb.getBlockData() instanceof Cocoa v){
v.setFacing(opp);
bb.setBlockData(v);
bb.getState().setBlockData(v);
bb.getState().update(true);
return true;
}
return false;
}
/**
* Handles the placing of Glow Lichen. This needs to stick to a block rather than grow on it.
* If the block is set to Glow Lichen then it appears as an air block with 6 sides of lichen so
* they need to be switched off and only the side next to the block should be set.
* @param bl - block where plants would usually be placed
* @return true if successful, false if not
*/
private boolean placeLichen(Block bl) {
// Get the source block below this one
Block b = bl.getRelative(BlockFace.DOWN);
// Find a spot for licen
BlockFace d = null;
boolean waterLogged = false;
for (BlockFace adj : ADJ_BLOCKS) {
Material type = b.getRelative(adj).getType();
if (type.equals(Material.AIR) || type.equals(Material.WATER)) {
d = adj;
if (type.equals(Material.WATER)) {
waterLogged = true;
}
break;
}
}
if (d == null) {
return false;
}
Block bb = b.getRelative(d);
bb.setType(Material.GLOW_LICHEN);
BlockFace opp = d.getOppositeFace();
if(bb.getBlockData() instanceof GlowLichen v){
for (BlockFace f : v.getAllowedFaces()) {
v.setFace(f, false);
}
v.setFace(opp, true);
v.setWaterlogged(waterLogged);
bb.setBlockData(v);
bb.getState().setBlockData(v);
bb.getState().update(true);
return true;
}
return false;
}
/**
* Checks if a particular plant can group at the location of a block
* @param block - block being checked
* @param p - greenhouse plant
* @return true if it can be grown otherwise false
*/
private boolean canGrowOn(GrowthBlock block, GreenhousePlant p) {
// Ceiling plants can only grow on ceiling blocks
if (CEILING_PLANTS.contains(p.plantMaterial()) && Boolean.TRUE.equals(block.floor())) {
return false;
}
// Underwater plants can only be placed in water and regular plants cannot be placed in water
if (block.block().getType().equals(Material.WATER)) {
if (!UNDERWATER_PLANTS.contains(p.plantMaterial())) {
return false;
}
} else if (UNDERWATER_PLANTS.contains(p.plantMaterial())) {
return false;
}
return p.plantGrownOn().equals(block.block().getRelative(Boolean.TRUE.equals(block.floor()) ?
BlockFace.DOWN :
BlockFace.UP).getType());
}
/** /**
* @param friendlyName - set the friendly name * @param friendlyName - set the friendly name
*/ */
@ -531,7 +726,22 @@ public class BiomeRecipe implements Comparable<BiomeRecipe> {
* @return the mob types that may spawn due to this recipe * @return the mob types that may spawn due to this recipe
*/ */
public Set<EntityType> getMobTypes() { public Set<EntityType> getMobTypes() {
return mobTree.values().stream().map(GreenhouseMob::getMobType).collect(Collectors.toSet()); return mobTree.values().stream().map(GreenhouseMob::mobType).collect(Collectors.toSet());
}
/**
* Set the maximum number of mobs in a greenhouse
* @param maxMob maximum
*/
public void setMaxMob(int maxMob) {
this.maxMob = maxMob;
}
/**
* @return the maxMob
*/
public int getMaxMob() {
return maxMob;
} }

View File

@ -2,41 +2,4 @@ package world.bentobox.greenhouses.greenhouse;
import org.bukkit.Material; import org.bukkit.Material;
class GreenhouseBlockConversions { record GreenhouseBlockConversions (Material oldMaterial, Material newMaterial, double probability, Material localMaterial) { }
private final Material oldMaterial;
private final Material newMaterial;
private final double probability;
private final Material localMaterial;
public GreenhouseBlockConversions(Material oldMaterial, Material newMaterial, double probability, Material localMaterial) {
this.oldMaterial = oldMaterial;
this.newMaterial = newMaterial;
this.probability = probability;
this.localMaterial = localMaterial;
}
/**
* @return the oldMaterial
*/
public Material getOldMaterial() {
return oldMaterial;
}
/**
* @return the newMaterial
*/
public Material getNewMaterial() {
return newMaterial;
}
/**
* @return the probability
*/
public double getProbability() {
return probability;
}
/**
* @return the localMaterial
*/
public Material getLocalMaterial() {
return localMaterial;
}
}

View File

@ -1,31 +1,6 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import java.util.Optional;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
class GreenhouseMob { record GreenhouseMob(EntityType mobType, Material mobSpawnOn) { }
private final EntityType mobType;
private final Material mobSpawnOn;
/**
* @param mobType - entity type of mob
* @param mobSpawnOn - material on which it much spawn, or null if any
*/
public GreenhouseMob(EntityType mobType, Material mobSpawnOn) {
this.mobType = mobType;
this.mobSpawnOn = mobSpawnOn;
}
/**
* @return the mobType
*/
public EntityType getMobType() {
return mobType;
}
/**
* @return the mobSpawnOn
*/
public Optional<Material> getMobSpawnOn() {
return Optional.of(mobSpawnOn);
}
}

View File

@ -1,32 +1,5 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import java.util.Optional;
import org.bukkit.Material; import org.bukkit.Material;
class GreenhousePlant { record GreenhousePlant(Material plantMaterial,Material plantGrownOn) { }
private final Material plantMaterial;
private final Material plantGrownOn;
/**
* Describes a recipe plant
* @param plantMaterial - material
* @param plantGrownOn - material on which this grows
*/
public GreenhousePlant(Material plantMaterial,Material plantGrownOn) {
this.plantMaterial = plantMaterial;
this.plantGrownOn = plantGrownOn;
}
/**
* @return the plantMaterial
*/
public Material getPlantMaterial() {
return plantMaterial;
}
/**
* @return the plantGrownOn
*/
public Optional<Material> getPlantGrownOn() {
return Optional.ofNullable(plantGrownOn);
}
}

View File

@ -46,4 +46,11 @@ public abstract class MinMaxXZ {
public int getArea() { public int getArea() {
return (maxX - minX) * (maxZ - minZ); return (maxX - minX) * (maxZ - minZ);
} }
@Override
public String toString() {
return "MinMaxXZ [minX=" + minX + ", maxX=" + maxX + ", minZ=" + minZ + ", maxZ=" + maxZ + "]";
}
} }

View File

@ -1,17 +1,22 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.world.AsyncWorldCache;
/** /**
* Contains the parameters of a greenhouse roof * Contains the parameters of a greenhouse roof
@ -19,50 +24,119 @@ import world.bentobox.greenhouses.Greenhouses;
* *
*/ */
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
List<Material> r = Arrays.stream(Material.values()) .filter(m -> Tag.TRAPDOORS.isTagged(m) // All trapdoors
.filter(Material::isBlock) // Blocks only, no items || (m.name().contains("GLASS") && !m.name().contains("GLASS_PANE")) // All glass blocks
.filter(m -> m.name().contains("TRAPDOOR") // All trapdoors || m.equals(Material.HOPPER)).toList();
|| m.name().contains("GLASS") // All glass blocks private final AsyncWorldCache cache;
|| m.equals(Material.HOPPER)) // Hoppers
.collect(Collectors.toList());
ROOF_BLOCKS = Collections.unmodifiableList(r);
}
private final Location location;
private int height; private int height;
private final Location location;
private boolean roofFound; private boolean roofFound;
private final Greenhouses addon;
private final World world;
/** /**
* Finds a roof from a starting location under the roof and characterizes it * Finds a roof from a starting location under the roof and characterizes it
* @param cache async world cache
* @param loc - starting location * @param loc - starting location
*/ */
public Roof(Location loc) { public Roof(AsyncWorldCache cache, Location loc, Greenhouses addon) {
this.cache = cache;
this.location = loc; this.location = loc;
roofFound = findRoof(loc); this.addon = addon;
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());
}
private boolean findRoof(Location loc) { /**
World world = loc.getWorld(); * This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks
* up to 100 in any direction
* @param vector - vector to start search
*/
private void expandCoords(Vector vector) {
Vector maxx = vector.clone();
Vector minx = vector.clone();
Vector maxz = vector.clone();
Vector minz = vector.clone();
int limit = 0;
while (roofBlocks(cache.getBlockType(maxx)) && limit < 100) {
limit++;
maxx.add(new Vector(1,0,0));
}
// Set Max x
if (maxx.getBlockX() - 1 > maxX) {
maxX = maxx.getBlockX() - 1;
}
limit = 0;
while (roofBlocks(cache.getBlockType(minx)) && limit < 100) {
limit++;
minx.subtract(new Vector(1,0,0));
}
if (minx.getBlockX() + 1 < minX) {
minX = minx.getBlockX() + 1;
}
limit = 0;
while (roofBlocks(cache.getBlockType(maxz)) && limit < 100) {
limit++;
maxz.add(new Vector(0,0,1));
}
if (maxz.getBlockZ() - 1 > maxZ) {
maxZ = maxz.getBlockZ() - 1;
}
limit = 0;
while (roofBlocks(cache.getBlockType(minz)) && limit < 100) {
limit++;
minz.subtract(new Vector(0,0,1));
}
if (minz.getBlockZ() + 1 < minZ) {
minZ = minz.getBlockZ() + 1;
}
}
public CompletableFuture<Boolean> findRoof() {
CompletableFuture<Boolean> r = new CompletableFuture<>();
Vector loc = location.toVector();
// This section tries to find a roof block // This section tries to find a roof block
// Try just going up - this covers every case except if the player is standing under a hole // Try just going up - this covers every case except if the player is standing under a hole
roofFound = false; Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
boolean found = findRoof(loc);
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> r.complete(found));
});
return r;
}
// This does a ever-growing check around the player to find a wall block. It is possible for the player boolean findRoof(Vector loc) {
// 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 roofY = loc.getBlockY(); int startY = loc.getBlockY();
for (int y = roofY; y < world.getMaxHeight(); y++) { for (int y = startY; y < world.getMaxHeight(); y++) {
if (roofBlocks(world.getBlockAt(loc.getBlockX(),y,loc.getBlockZ()).getType())) { Vector v = new Vector(loc.getBlockX(),y,loc.getBlockZ());
if (roofBlocks(cache.getBlockType(v))) {
roofFound = true; roofFound = true;
loc = new Location(world,loc.getBlockX(),y,loc.getBlockZ()); loc = v;
break; break;
} }
} }
// If the roof was not found start going around in circles until something is found // If the roof was not found start going around in circles until something is found
// Expand in ever increasing squares around location until a wall block is found // Expand in ever-increasing squares around location until a wall block is found
spiralSearch(loc, roofY); if (!roofFound) {
if (!roofFound) return false; loc = spiralSearch(loc, startY);
if (!roofFound) {
return false;
}
}
// Record the height // Record the height
this.height = loc.getBlockY(); this.height = loc.getBlockY();
// Now we have a roof block, find how far we can go NSWE // Now we have a roof block, find how far we can go NSWE
@ -70,7 +144,7 @@ public class Roof extends MinMaxXZ {
maxX = loc.getBlockX(); maxX = loc.getBlockX();
minZ = loc.getBlockZ(); minZ = loc.getBlockZ();
maxZ = loc.getBlockZ(); maxZ = loc.getBlockZ();
expandCoords(world, loc.toVector()); expandCoords(loc);
int minx; int minx;
int maxx; int maxx;
int minz; int minz;
@ -84,7 +158,7 @@ public class Roof extends MinMaxXZ {
for (int x = minx; x <= maxx; x++) { for (int x = minx; x <= maxx; x++) {
for (int z = minz; z <= maxz; z++) { for (int z = minz; z <= maxz; z++) {
// This will push out the coords if possible // This will push out the coords if possible
expandCoords(world, new Vector(x, loc.getBlockY(), z)); expandCoords(new Vector(x, loc.getBlockY(), z));
} }
} }
// Repeat until nothing changes // Repeat until nothing changes
@ -93,96 +167,6 @@ public class Roof extends MinMaxXZ {
return true; return true;
} }
private void spiralSearch(Location loc, int roofY) {
for (int radius = 0; radius < 3 && !roofFound; radius++) {
for (int x = loc.getBlockX() - radius; x <= loc.getBlockX() + radius && !roofFound; x++) {
for (int z = loc.getBlockZ() - radius; z <= loc.getBlockZ() + radius && !roofFound; z++) {
if (!((x > loc.getBlockX() - radius && x < loc.getBlockX() + radius) && (z > loc.getBlockZ() - radius && z < loc.getBlockZ() + radius))) {
checkVertically(loc, x, roofY, z);
}
}
}
}
}
private void checkVertically(Location loc, int x, int roofY, int z) {
World world = loc.getWorld();
Block b = world.getBlockAt(x, roofY, z);
if (!Walls.wallBlocks(b.getType())) {
// Look up
for (int y = roofY; y < world.getMaxHeight() && !roofFound; y++) {
if (roofBlocks(world.getBlockAt(x,y,z).getType())) {
roofFound = true;
loc = new Location(world,x,y,z);
}
}
}
}
/**
* This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks
* up to 100 in any direction
* @param height - location to start search
*/
private void expandCoords(World world, Vector height) {
Location maxx = height.toLocation(world);
Location minx = height.toLocation(world);
Location maxz = height.toLocation(world);
Location minz = height.toLocation(world);
int limit = 0;
while (ROOF_BLOCKS
.contains(world.getBlockAt(maxx).getType()) && limit < 100) {
limit++;
maxx.add(new Vector(1,0,0));
}
if (maxx.getBlockX()-1 > maxX) {
maxX = maxx.getBlockX()-1;
}
while (roofBlocks(world.getBlockAt(minx).getType()) && limit < 200) {
limit++;
minx.subtract(new Vector(1,0,0));
}
if (minx.getBlockX() + 1 < minX) {
minX = minx.getBlockX() + 1;
}
while (roofBlocks(world.getBlockAt(maxz).getType()) && limit < 300) {
limit++;
maxz.add(new Vector(0,0,1));
}
if (maxz.getBlockZ() - 1 > maxZ) {
maxZ = maxz.getBlockZ() - 1;
}
while (roofBlocks(world.getBlockAt(minz).getType()) && limit < 400) {
limit++;
minz.subtract(new Vector(0,0,1));
}
if (minz.getBlockZ() + 1 < minZ) {
minZ = minz.getBlockZ() + 1;
}
}
/**
* Check if material is a roof material
* @param m - material
* @return true if roof material
*/
public static boolean roofBlocks(Material m) {
return ROOF_BLOCKS.contains(m) || (m.equals(Material.GLOWSTONE) && Greenhouses.getInstance().getSettings().isAllowGlowstone());
}
/**
* @return the roofFound
*/
public boolean isRoofFound() {
return roofFound;
}
/** /**
* @return the height * @return the height
*/ */
@ -190,6 +174,7 @@ public class Roof extends MinMaxXZ {
return height; return height;
} }
/** /**
* @return the location * @return the location
*/ */
@ -197,13 +182,48 @@ public class Roof extends MinMaxXZ {
return location; return location;
} }
private Vector spiralSearch(Vector v, int startY) {
for (int radius = 0; radius < 3; radius++) {
for (int x = v.getBlockX() - radius; x <= v.getBlockX() + radius; x++) {
for (int z = v.getBlockZ() - radius; z <= v.getBlockZ() + radius; z++) {
if (!((x > v.getBlockX() - radius && x < v.getBlockX() + radius) && (z > v.getBlockZ() - radius && z < v.getBlockZ() + radius))) {
Optional<Vector> r = checkVertically(x, startY, z);
if (r.isPresent()) {
return r.get();
}
}
}
}
}
return v;
}
/* (non-Javadoc) /**
* @see java.lang.Object#toString() * Get the highest roof block
* @param x - x coord of current search
* @param startY - starting y coord
* @param z - z coord of current search
*/ */
private Optional<Vector> checkVertically(final int x, final int startY, final int z) {
if (!addon.wallBlocks(cache.getBlockType(x, startY, z))) {
// Look up
for (int y = startY; y < world.getMaxHeight() && !roofFound; y++) {
if (roofBlocks(cache.getBlockType(x,y,z))) {
roofFound = true;
// Roof block found
return Optional.of(new Vector(x,y,z));
}
}
}
return Optional.empty();
}
@Override @Override
public String toString() { public String toString() {
return "Roof [location=" + location + ", minX=" + minX + ", maxX=" + maxX + ", minZ=" + minZ + ", maxZ=" + maxZ return "Roof [height=" + height + ", roofFound=" + roofFound + ", minX=" + minX + ", maxX=" + maxX + ", minZ="
+ ", height=" + height + ", roofFound=" + roofFound + "]"; + minZ + ", maxZ=" + maxZ + "]";
} }
} }

View File

@ -1,35 +1,28 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.world.AsyncWorldCache;
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
List<Material> w = Arrays.stream(Material.values()) .filter(m -> !m.name().contains("TRAPDOOR")) // No trap doors
.filter(Material::isBlock) // Blocks only, no items .filter(m -> m.name().contains("DOOR") // All doors
.filter(m -> !m.name().contains("TRAPDOOR")) // No trap doors || (m.name().contains("GLASS") && !m.name().contains("GLASS_PANE")) // All glass blocks
.filter(m -> m.name().contains("DOOR") // All doors || m.equals(Material.HOPPER)).toList();
|| m.name().contains("GLASS") // All glass blocks
|| m.equals(Material.HOPPER)) // Hoppers
.collect(Collectors.toList());
WALL_BLOCKS = Collections.unmodifiableList(w);
}
private int floor; private int floor;
private final AsyncWorldCache cache;
private static final List<BlockFace> ORDINALS = Arrays.asList(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST); static class WallFinder {
class WallFinder {
int radiusMinX; int radiusMinX;
int radiusMaxX; int radiusMaxX;
int radiusMinZ; int radiusMinZ;
@ -41,14 +34,34 @@ public class Walls extends MinMaxXZ {
boolean isSearching() { boolean isSearching() {
return !stopMinX || !stopMaxX || !stopMinZ || !stopMaxZ; return !stopMinX || !stopMaxX || !stopMinZ || !stopMaxZ;
} }
@Override
public String toString() {
return "WallFinder [radiusMinX=" + radiusMinX + ", radiusMaxX=" + radiusMaxX + ", radiusMinZ=" + radiusMinZ
+ ", radiusMaxZ=" + radiusMaxZ + ", stopMinX=" + stopMinX + ", stopMaxX=" + stopMaxX + ", stopMinZ="
+ stopMinZ + ", stopMaxZ=" + stopMaxZ + "]";
}
} }
public Walls findWalls(Roof roof) { public Walls(AsyncWorldCache cache) {
this.cache = cache;
}
/**
* Find walls given a roof
* @param roof - the roof
* @return Future walls
*/
public CompletableFuture<Walls> findWalls(final Roof roof) {
CompletableFuture<Walls> r = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> findWalls(r, roof));
return r;
}
Walls findWalls(CompletableFuture<Walls> r, Roof roof) {
// The player is under the roof // The player is under the roof
// Assume the player is inside the greenhouse they are trying to create // Assume the player is inside the greenhouse they are trying to create
Location loc = roof.getLocation(); final Location loc = roof.getLocation();
World world = loc.getWorld(); floor = getFloorY(roof.getHeight(), roof.getMinX(), roof.getMaxX(), roof.getMinZ(), roof.getMaxZ(), loc.getWorld().getMinHeight());
floor = getFloorY(world, roof.getHeight(), roof.getMinX(), roof.getMaxX(), roof.getMinZ(), roof.getMaxZ());
// Now start with the player's x and z location // Now start with the player's x and z location
WallFinder wf = new WallFinder(); WallFinder wf = new WallFinder();
minX = loc.getBlockX(); minX = loc.getBlockX();
@ -64,12 +77,14 @@ public class Walls extends MinMaxXZ {
minZ--; minZ--;
maxZ++; maxZ++;
// Find the floor again, only looking within the walls // Find the floor again, only looking within the walls
floor = getFloorY(world, roof.getHeight(), minX, maxX, minZ,maxZ); floor = getFloorY(roof.getHeight(), minX, maxX, minZ,maxZ,loc.getWorld().getMinHeight());
// Complete on main thread
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> r.complete(this));
return this; return this;
} }
void lookAround(Location loc, WallFinder wf, Roof roof) { void lookAround(final Location loc, WallFinder wf, final Roof roof) {
World world = loc.getWorld();
// Look around player in an ever expanding cube // Look around player in an ever expanding cube
minX = loc.getBlockX() - wf.radiusMinX; minX = loc.getBlockX() - wf.radiusMinX;
maxX = loc.getBlockX() + wf.radiusMaxX; maxX = loc.getBlockX() + wf.radiusMaxX;
@ -81,7 +96,7 @@ public class Walls extends MinMaxXZ {
// Only look around outside edge // Only look around outside edge
if (!((x > minX && x < maxX) && (z > minZ && z < maxZ))) { if (!((x > minX && x < maxX) && (z > minZ && z < maxZ))) {
// Look at block faces // Look at block faces
lookAtBlockFaces(wf, world, x, y, z); lookAtBlockFaces(wf, x, y, z);
} }
} }
} }
@ -117,67 +132,43 @@ public class Walls extends MinMaxXZ {
} }
} }
void lookAtBlockFaces(WallFinder wf, World world, int x, int y, int z) { void lookAtBlockFaces(WallFinder wf, int x, int y, int z) {
for (BlockFace bf: ORDINALS) { // positive x
switch (bf) { if (WALL_BLOCKS.contains(cache.getBlockType(x + 1, y, z))) {
case EAST: wf.stopMaxX = true;
// positive x }
if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) { // negative x
wf.stopMaxX = true; if (WALL_BLOCKS.contains(cache.getBlockType(x - 1, y, z))) {
} wf.stopMinX = true;
break; }
case WEST: // negative Z
// negative x if (WALL_BLOCKS.contains(cache.getBlockType(x, y, z - 1))) {
if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) { wf.stopMinZ = true;
wf.stopMinX = true; }
} // positive Z
break; if (WALL_BLOCKS.contains(cache.getBlockType(x, y, z + 1))) {
case NORTH: wf.stopMaxZ = true;
// negative Z
if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) {
wf.stopMinZ = true;
}
break;
case SOUTH:
// positive Z
if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) {
wf.stopMaxZ = true;
}
break;
default:
break;
}
} }
} }
int getFloorY(World world, int y, int minX, int maxX, int minZ, int maxZ) { int getFloorY(int y, int minX, int maxX, int minZ, int maxZ, int minY) {
// Find the floor - defined as the last y under the roof where there are no wall blocks // Find the floor - defined as the last y under the roof where there are no wall blocks
int wallBlockCount; int wallBlockCount;
do { do {
wallBlockCount = 0; wallBlockCount = 0;
for (int x = minX; x <= maxX; x++) { for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) { for (int z = minZ; z <= maxZ; z++) {
if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getType())) { if (WALL_BLOCKS.contains(cache.getBlockType(x, y, z))) {
wallBlockCount++; wallBlockCount++;
} }
} }
} }
} while( y-- > 0 && wallBlockCount > 0); } while( y-- > minY && wallBlockCount > 0);
return y + 1; return y + 1;
} }
/**
* 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());
}
/** /**
* @return the floor * @return the floor
*/ */

View File

@ -1,20 +1,21 @@
package world.bentobox.greenhouses.listeners; package world.bentobox.greenhouses.listeners;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
@ -29,6 +30,11 @@ import world.bentobox.greenhouses.data.Greenhouse;
*/ */
public class GreenhouseEvents implements Listener { public class GreenhouseEvents implements Listener {
private static final String BIOME = "[biome]"; private static final String BIOME = "[biome]";
private static final Set<Biome> NETHER_BIOMES;
static {
NETHER_BIOMES = Set.of(Biome.NETHER_WASTES, Biome.WARPED_FOREST, Biome.CRIMSON_FOREST,
Biome.SOUL_SAND_VALLEY, Biome.BASALT_DELTAS);
}
private final Greenhouses addon; private final Greenhouses addon;
public GreenhouseEvents(final Greenhouses addon) { public GreenhouseEvents(final Greenhouses addon) {
@ -39,16 +45,30 @@ public class GreenhouseEvents implements Listener {
* Permits water to be placed in the Nether if in a greenhouse and in an acceptable biome * Permits water to be placed in the Nether if in a greenhouse and in an acceptable biome
* @param e - event * @param e - event
*/ */
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled=true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true)
public void onPlayerInteractInNether(PlayerInteractEvent e) { public void onPlayerInteractInNether(PlayerBucketEmptyEvent e) {
if (!e.getPlayer().getWorld().getEnvironment().equals(World.Environment.NETHER)) { if (!e.getBucket().equals(Material.WATER_BUCKET)) {
return; return;
} }
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getItem() != null && e.getItem().getType().equals(Material.WATER_BUCKET) Block b = e.getBlockClicked().getRelative(e.getBlockFace());
&& addon.getManager().getMap().inGreenhouse(e.getClickedBlock().getLocation())) { if (e.getPlayer().getWorld().getEnvironment().equals(World.Environment.NETHER)
&& !addon.getManager().getMap().getGreenhouse(b.getLocation())
.map(gh -> gh.getBiomeRecipe().getBiome()).map(NETHER_BIOMES::contains).orElse(true)) {
// In Nether not a nether greenhouse
b.setType(Material.WATER);
} else if (!e.getPlayer().getWorld().getEnvironment().equals(World.Environment.NETHER)
&& addon.getManager().getMap().getGreenhouse(b.getLocation())
.map(gh -> gh.getBiomeRecipe().getBiome()).map(NETHER_BIOMES::contains).orElse(false)) {
// Not in Nether, in a nether greenhouse
e.setCancelled(true); e.setCancelled(true);
e.getClickedBlock().getRelative(e.getBlockFace()).setType(Material.WATER); if (e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.WATER_BUCKET)) {
e.getItem().setType(Material.BUCKET); e.getPlayer().getInventory().getItemInMainHand().setType(Material.BUCKET);
} else if (e.getPlayer().getInventory().getItemInOffHand().getType().equals(Material.WATER_BUCKET)) {
e.getPlayer().getInventory().getItemInOffHand().setType(Material.BUCKET);
}
b.getWorld().spawnParticle(Particle.SMOKE_NORMAL, b.getLocation(), 10);
b.getWorld().playSound(b.getLocation(), Sound.ENTITY_GENERIC_EXTINGUISH_FIRE, 1F, 5F);
} }
} }
@ -56,15 +76,25 @@ public class GreenhouseEvents implements Listener {
* Makes water in the Nether if ice is broken and in a greenhouse * Makes water in the Nether if ice is broken and in a greenhouse
* @param e - event * @param e - event
*/ */
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled=true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true)
public void onIceBreak(BlockBreakEvent e) { public void onIceBreak(BlockBreakEvent e) {
if (!e.getBlock().getWorld().getEnvironment().equals(World.Environment.NETHER) if (!Tag.ICE.isTagged(e.getBlock().getType())) {
|| !Tag.ICE.isTagged(e.getBlock().getType())) {
return; return;
} }
if (addon.getManager().getMap().inGreenhouse(e.getBlock().getLocation())) { Block b = e.getBlock();
if (b.getWorld().getEnvironment().equals(World.Environment.NETHER)
&& !addon.getManager().getMap().getGreenhouse(b.getLocation())
.map(gh -> gh.getBiomeRecipe().getBiome()).map(NETHER_BIOMES::contains).orElse(true)) {
//
e.setCancelled(true); e.setCancelled(true);
e.getBlock().setType(Material.WATER); b.setType(Material.WATER);
} else if (!e.getPlayer().getWorld().getEnvironment().equals(World.Environment.NETHER)
&& addon.getManager().getMap().getGreenhouse(b.getLocation())
.map(gh -> gh.getBiomeRecipe().getBiome()).map(NETHER_BIOMES::contains).orElse(false)) {
// Not in Nether, in a nether greenhouse
e.setCancelled(true);
b.setType(Material.AIR);
b.getWorld().playSound(b.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
} }
} }
@ -78,10 +108,18 @@ public class GreenhouseEvents implements Listener {
handleTransition(User.getInstance(e.getPlayer()), e.getTo(), e.getFrom()); handleTransition(User.getInstance(e.getPlayer()), e.getTo(), e.getFrom());
} }
/**
* @param user user
* @param toLoc to location
* @param fromLoc from location
*/
private void handleTransition(User user, Location toLoc, Location fromLoc) { private void handleTransition(User user, Location toLoc, Location fromLoc) {
if (user == null) {
return;
}
Optional<Greenhouse> to = addon.getManager().getMap().getGreenhouse(toLoc); Optional<Greenhouse> to = addon.getManager().getMap().getGreenhouse(toLoc);
Optional<Greenhouse> from = addon.getManager().getMap().getGreenhouse(fromLoc); Optional<Greenhouse> from = addon.getManager().getMap().getGreenhouse(fromLoc);
if (!to.isPresent() && !from.isPresent()) { if (to.isEmpty() && from.isEmpty()) {
return; return;
} }
if (to.isPresent() && from.isPresent()) { if (to.isPresent() && from.isPresent()) {
@ -94,15 +132,13 @@ public class GreenhouseEvents implements Listener {
return; return;
} }
// from is a greenhouse // from is a greenhouse
if (from.isPresent() && !to.isPresent()) { if (from.isPresent()) {
// Exiting // Exiting
user.sendMessage("greenhouses.event.leaving", BIOME, from.get().getBiomeRecipe().getFriendlyName()); user.sendMessage("greenhouses.event.leaving", BIOME, from.get().getBiomeRecipe().getFriendlyName());
return; return;
} }
if (!from.isPresent()) { // Entering
// Entering user.sendMessage("greenhouses.event.entering", BIOME, to.get().getBiomeRecipe().getFriendlyName());
user.sendMessage("greenhouses.event.entering", BIOME, to.get().getBiomeRecipe().getFriendlyName());
}
} }
@ -123,48 +159,15 @@ public class GreenhouseEvents implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled=true)
public void onBlockBreak(final BlockBreakEvent e) { public void onBlockBreak(final BlockBreakEvent e) {
User user = User.getInstance(e.getPlayer()); User user = User.getInstance(e.getPlayer());
addon.getManager().getMap().getGreenhouse(e.getBlock().getLocation()).ifPresent(g -> { addon.getManager().getMap().getGreenhouse(e.getBlock().getLocation())
// Check to see if wall or roof block broken .filter(g -> g.isRoofOrWallBlock(e.getBlock().getLocation()))
if ((e.getBlock().getLocation().getBlockY() == g.getCeilingHeight() - 1) .ifPresent(g -> {
|| e.getBlock().getLocation().getBlockX() == (int)g.getBoundingBox().getMinX() if (g.getOriginalBiome() != null) {
|| e.getBlock().getLocation().getBlockX() == (int)g.getBoundingBox().getMaxX() - 1
|| e.getBlock().getLocation().getBlockZ() == (int)g.getBoundingBox().getMinZ()
|| e.getBlock().getLocation().getBlockZ() == (int)g.getBoundingBox().getMaxZ() - 1
) {
user.sendMessage("greenhouses.event.broke", BIOME, Util.prettifyText(g.getOriginalBiome().name())); user.sendMessage("greenhouses.event.broke", BIOME, Util.prettifyText(g.getOriginalBiome().name()));
addon.getManager().removeGreenhouse(g);
} }
addon.getManager().removeGreenhouse(g);
}); });
} }
/**
* Prevents placing of blocks above the greenhouses
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true)
public void onPlayerBlockPlace(final BlockPlaceEvent e) {
if (checkBlockHeight(e.getBlock())) {
e.setCancelled(true);
User user = User.getInstance(e.getPlayer());
user.sendMessage("greenhouses.error.cannot-place");
}
}
private boolean checkBlockHeight(Block block) {
return addon.getManager().getMap().getGreenhouse(block.getLocation())
.filter(g -> g.getCeilingHeight() < block.getY())
.filter(g -> !block.getWorld().getEnvironment().equals(World.Environment.NETHER))
.isPresent();
}
/**
* Check to see if anyone is sneaking a block over a greenhouse by using a piston
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true)
public void onPistonPush(final BlockPistonExtendEvent e) {
e.setCancelled(e.getBlocks().stream().anyMatch(this::checkBlockHeight));
}
} }

View File

@ -2,10 +2,15 @@ package world.bentobox.greenhouses.listeners;
import java.util.Optional; import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.data.Greenhouse; import world.bentobox.greenhouses.data.Greenhouse;
@ -18,7 +23,7 @@ public class GreenhouseGuard implements Listener {
} }
// Stop lava flow or water into or out of a greenhouse // Stop lava flow or water into or out of a greenhouse
@EventHandler(priority = EventPriority.NORMAL) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onFlow(final BlockFromToEvent e) { public void onFlow(final BlockFromToEvent e) {
// Flow may be allowed anyway // Flow may be allowed anyway
if (addon.getSettings().isAllowFlowIn() && addon.getSettings().isAllowFlowOut()) { if (addon.getSettings().isAllowFlowIn() && addon.getSettings().isAllowFlowOut()) {
@ -34,7 +39,7 @@ public class GreenhouseGuard implements Listener {
// 1. inside district or outside - always ok // 1. inside district or outside - always ok
// 2. inside to outside - allowFlowOut determines // 2. inside to outside - allowFlowOut determines
// 3. outside to inside - allowFlowIn determines // 3. outside to inside - allowFlowIn determines
if (!to.isPresent() && !from.isPresent()) { if (to.isEmpty() && from.isEmpty()) {
return; return;
} }
if (to.isPresent() && from.isPresent() && to.equals(from)) { if (to.isPresent() && from.isPresent() && to.equals(from)) {
@ -48,10 +53,51 @@ 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);
} }
/**
* Prevents pistons from pushing greenhouse wall or roof blocks
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPistonPush(BlockPistonExtendEvent e) {
e.setCancelled(e.getBlocks().stream()
.map(Block::getLocation)
.anyMatch(this::inGreenhouse));
}
/**
* Prevents sticky pistons from pulling greenhouse wall or roof blocks
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPistonPull(BlockPistonRetractEvent e) {
e.setCancelled(e.getBlocks().stream()
.map(Block::getLocation)
.anyMatch(this::inGreenhouse));
}
/**
* Guard Greenhouse from natural entity spawning.
*
* @param spawnEvent the spawn event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onCreatureSpawn(CreatureSpawnEvent spawnEvent)
{
if (CreatureSpawnEvent.SpawnReason.NATURAL == spawnEvent.getSpawnReason())
{
// Natural spawn events should be cancelled. Greenhouse spawns its own mobs.
spawnEvent.setCancelled(this.inGreenhouse(spawnEvent.getLocation()));
}
}
private boolean inGreenhouse(Location l) {
return addon.getManager().getMap().getGreenhouse(l).map(g -> g.isRoofOrWallBlock(l)).orElse(false);
}
} }

View File

@ -13,10 +13,10 @@ import world.bentobox.greenhouses.Greenhouses;
*/ */
public class IslandChangeEvents implements Listener { public class IslandChangeEvents implements Listener {
private Greenhouses addon; private final Greenhouses addon;
/** /**
* @param addon * @param addon greenhouse addon
*/ */
public IslandChangeEvents(Greenhouses addon) { public IslandChangeEvents(Greenhouses addon) {
this.addon = addon; this.addon = addon;

View File

@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
@ -12,12 +14,17 @@ import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.Hopper; import org.bukkit.block.Hopper;
import org.bukkit.block.data.Levelled;
import org.bukkit.block.data.type.Snow;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.weather.WeatherChangeEvent; import org.bukkit.event.weather.WeatherChangeEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.BoundingBox;
import com.google.common.base.Enums;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.Greenhouses;
@ -41,13 +48,22 @@ public class SnowTracker implements Listener {
} }
/**
* @param gh - greenhouse
* @return true if snow was create, false if not.
*/
private boolean getAirBlocks(Greenhouse gh) { private boolean getAirBlocks(Greenhouse gh) {
if (gh.getLocation() == null) {
// Greenhouse does not have a location for some reason.
return false;
}
boolean createdSnow = false; boolean createdSnow = false;
List<Block> waterBlocks = new ArrayList<>(); List<Block> waterBlocks = new ArrayList<>();
for (int x = (int)gh.getBoundingBox().getMinX() + 1; x < (int)gh.getBoundingBox().getMaxX() -1; x++) { final BoundingBox bb = gh.getBoundingBox();
for (int z = (int)gh.getBoundingBox().getMinZ() + 1; z < (int)gh.getBoundingBox().getMaxZ() - 1; z++) { for (int x = (int)bb.getMinX() + 1; x < (int)bb.getMaxX() -1; x++) {
for (int y = (int)gh.getBoundingBox().getMaxY() - 2; y >= (int)gh.getBoundingBox().getMinY(); y--) { for (int z = (int)bb.getMinZ() + 1; z < (int)bb.getMaxZ() - 1; z++) {
Block b = gh.getLocation().getWorld().getBlockAt(x, y, z); for (int y = (int)bb.getMaxY() - 2; y >= (int)bb.getMinY(); y--) {
Block b = Objects.requireNonNull(gh.getLocation().getWorld()).getBlockAt(x, y, z);
Material type = b.getType(); Material type = b.getType();
if (type.equals(Material.AIR) || type.equals(Material.SNOW)) { if (type.equals(Material.AIR) || type.equals(Material.SNOW)) {
b.getWorld().spawnParticle(Particle.SNOWBALL, b.getLocation(), 5); b.getWorld().spawnParticle(Particle.SNOWBALL, b.getLocation(), 5);
@ -59,9 +75,11 @@ public class SnowTracker implements Listener {
// Not water // Not water
if (Math.random() < addon.getSettings().getSnowDensity() if (Math.random() < addon.getSettings().getSnowDensity()
&& !b.isLiquid() && !b.isLiquid()
&& b.getRelative(BlockFace.UP).getType().equals(Material.AIR)) { && (b.getRelative(BlockFace.UP).getType().equals(Material.AIR)
b.getRelative(BlockFace.UP).setType(Material.SNOW); || b.getRelative(BlockFace.UP).getType().equals(Material.SNOW))) {
createdSnow = true;
createdSnow = placeSnow(b);
} }
} }
@ -81,9 +99,49 @@ public class SnowTracker implements Listener {
return createdSnow; return createdSnow;
} }
private boolean placeSnow(Block b) {
Optional<Material> snowCauldron = Enums.getIfPresent(Material.class, "POWDER_SNOW_CAULDRON").toJavaUtil();
if (snowCauldron.isPresent()) {
if (b.getType().equals(Material.CAULDRON)) {
b.setType(snowCauldron.get());
return true;
} else if (b.getType().equals(snowCauldron.get())) {
// Fill up the snow cauldron some more
return incrementLevel(b);
}
}
// Pile up snow
if (b.getRelative(BlockFace.UP).getType().equals(Material.SNOW)) {
return incrementLevel(b.getRelative(BlockFace.UP));
} else {
b.getRelative(BlockFace.UP).setType(Material.SNOW);
}
return true;
}
private boolean incrementLevel(Block b) {
if (b.getBlockData() instanceof Levelled data) {
int max = data.getMaximumLevel();
if (data.getLevel() < max) {
data.setLevel(data.getLevel() + 1);
b.setBlockData(data);
return true;
}
}
if (b.getBlockData() instanceof Snow data) {
int max = data.getMaximumLayers();
if (data.getLayers() < max) {
data.setLayers(data.getLayers() + 1);
b.setBlockData(data);
return true;
}
}
return false;
}
/** /**
* TODO finish * TODO finish
* @param e * @param e block form event
*/ */
@EventHandler @EventHandler
public void onBlockFormEvent(final BlockFormEvent e) { public void onBlockFormEvent(final BlockFormEvent e) {
@ -108,7 +166,7 @@ public class SnowTracker implements Listener {
private void removeWaterBucketAndShake(Greenhouse g) { private void removeWaterBucketAndShake(Greenhouse g) {
// Scatter snow // Scatter snow
if (getAirBlocks(g)) { if (getAirBlocks(g) && g.getRoofHopperLocation() != null) {
Hopper h = ((Hopper)g.getRoofHopperLocation().getBlock().getState()); Hopper h = ((Hopper)g.getRoofHopperLocation().getBlock().getState());
h.getInventory().removeItem(new ItemStack(Material.WATER_BUCKET)); h.getInventory().removeItem(new ItemStack(Material.WATER_BUCKET));
h.getInventory().addItem(new ItemStack(Material.BUCKET)); h.getInventory().addItem(new ItemStack(Material.BUCKET));
@ -117,18 +175,16 @@ public class SnowTracker implements Listener {
private void shakeGlobes(World world) { private void shakeGlobes(World world) {
addon.getManager().getMap().getGreenhouses().stream().filter(g -> g.getBiomeRecipe().getIceCoverage() > 0) addon.getManager().getMap().getGreenhouses().stream().filter(g -> g.getBiomeRecipe().getIceCoverage() > 0)
.filter(g -> (g.getLocation().getWorld().isChunkLoaded(((int) g.getBoundingBox().getMaxX()) >> 4, ((int) g.getBoundingBox().getMaxZ()) >> 4) && g.getLocation().getWorld().isChunkLoaded(((int) g.getBoundingBox().getMinX()) >> 4, ((int) g.getBoundingBox().getMinZ()) >> 4))) .filter(g -> (Objects.requireNonNull(Objects.requireNonNull(g.getLocation()).getWorld()).isChunkLoaded(((int) g.getBoundingBox().getMaxX()) >> 4, ((int) g.getBoundingBox().getMaxZ()) >> 4) && g.getLocation().getWorld().isChunkLoaded(((int) g.getBoundingBox().getMinX()) >> 4, ((int) g.getBoundingBox().getMinZ()) >> 4)))
.filter(g -> g.getLocation().getWorld().equals(world)) .filter(g -> g.getLocation().getWorld().equals(world))
.filter(g -> !g.isBroken()) .filter(g -> !g.isBroken())
.filter(g -> g.getRoofHopperLocation() != null) .filter(g -> g.getRoofHopperLocation() != null)
.forEach(g -> { .forEach(g -> Util.getChunkAtAsync(g.getRoofHopperLocation()).thenRun(() -> {
Util.getChunkAtAsync(g.getRoofHopperLocation()).thenRun(() -> { if (g.getRoofHopperLocation().getBlock().getType().equals(Material.HOPPER)
if (g.getRoofHopperLocation().getBlock().getType().equals(Material.HOPPER) && ((Hopper)g.getRoofHopperLocation().getBlock().getState()).getInventory().contains(Material.WATER_BUCKET)) {
&& ((Hopper)g.getRoofHopperLocation().getBlock().getState()).getInventory().contains(Material.WATER_BUCKET)) { removeWaterBucketAndShake(g);
removeWaterBucketAndShake(g); }
} }));
});
});
} }
private void startSnow(World world) { private void startSnow(World world) {

View File

@ -9,14 +9,19 @@ import java.util.Objects;
import java.util.Random; import java.util.Random;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.Hopper; import org.bukkit.block.Hopper;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.NumberConversions;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.data.Greenhouse; import world.bentobox.greenhouses.data.Greenhouse;
import world.bentobox.greenhouses.greenhouse.BiomeRecipe;
/** /**
* Runs the ecosystem for a greenhouse * Runs the ecosystem for a greenhouse
@ -53,7 +58,7 @@ public class EcoSystemManager {
} }
// Kick block conversion growing // Kick block conversion growing
long blockTick = addon.getSettings().getBlockTick() * 60 * 20l; // In minutes long blockTick = addon.getSettings().getBlockTick() * 60 * 20L; // In minutes
if (blockTick > 0) { if (blockTick > 0) {
addon.log("Kicking off block conversion scheduler every " + addon.getSettings().getBlockTick() + MINUTES); addon.log("Kicking off block conversion scheduler every " + addon.getSettings().getBlockTick() + MINUTES);
@ -82,45 +87,74 @@ public class EcoSystemManager {
} }
private void convertBlocks(Greenhouse gh) { private void convertBlocks(Greenhouse gh) {
if(!gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMaxX()) >> 4, ((int) gh.getBoundingBox().getMaxZ()) >> 4) || !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMinX()) >> 4, ((int) gh.getBoundingBox().getMinZ()) >> 4)){ final World world = gh.getWorld();
final BoundingBox bb = gh.getBoundingBox();
if(world == null || gh.getLocation() == null || gh.getLocation().getWorld() == null
|| !gh.getLocation().getWorld().isChunkLoaded(((int) bb.getMaxX()) >> 4, ((int) bb.getMaxZ()) >> 4)
|| !gh.getLocation().getWorld().isChunkLoaded(((int) bb.getMinX()) >> 4, ((int) bb.getMinZ()) >> 4)){
return; return;
} }
for (int x = (int)gh.getBoundingBox().getMinX() + 1; x < (int)gh.getBoundingBox().getMaxX(); x++) { final BoundingBox ibb = gh.getInternalBoundingBox();
for (int z = (int)gh.getBoundingBox().getMinZ() + 1; z < (int)gh.getBoundingBox().getMaxZ(); z++) { int ghMinX = NumberConversions.floor(ibb.getMinX());
for (int y = (int)gh.getBoundingBox().getMaxY() - 2; y >= (int)gh.getBoundingBox().getMinY() && y > 0; y--) { int ghMaxX = NumberConversions.floor(ibb.getMaxX());
Block b = gh.getWorld().getBlockAt(x, y, z).getRelative(BlockFace.DOWN); int ghMinY = NumberConversions.floor(gh.getBoundingBox().getMinY()); // Note: this gets the floor
if (!b.isEmpty()) gh.getBiomeRecipe().convertBlock(gh, b); int ghMaxY = NumberConversions.floor(ibb.getMaxY());
int ghMinZ = NumberConversions.floor(ibb.getMinZ());
int ghMaxZ = NumberConversions.floor(ibb.getMaxZ());
BiomeRecipe biomeRecipe = gh.getBiomeRecipe();
for (int x = ghMinX; x < ghMaxX; x++) {
for (int z = ghMinZ; z < ghMaxZ; z++) {
for (int y = ghMinY; y < ghMaxY; y++) {
Block b = world.getBlockAt(x, y, z);
if(!b.isEmpty()) {
biomeRecipe.convertBlock(b);
}
} }
} }
} }
} }
private void verify(Greenhouse gh) { private void verify(Greenhouse gh) {
if(!gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMaxX()) >> 4, ((int) gh.getBoundingBox().getMaxZ()) >> 4) || !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMinX()) >> 4, ((int) gh.getBoundingBox().getMinZ()) >> 4)){ if(gh.getLocation() == null || gh.getLocation().getWorld() == null
//addon.log("Skipping verify for unloaded greenhouse at " + gh.getLocation()); || !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMaxX()) >> 4, ((int) gh.getBoundingBox().getMaxZ()) >> 4)
|| !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMinX()) >> 4, ((int) gh.getBoundingBox().getMinZ()) >> 4)){
// Skipping verify for unloaded greenhouse
return; return;
} }
if (!gh.getBiomeRecipe().checkRecipe(gh).isEmpty()) { gh.getBiomeRecipe().checkRecipe(gh).thenAccept(rs -> {
addon.log("Greenhouse failed verification at " + gh.getLocation()); if (!rs.isEmpty()) {
g.removeGreenhouse(gh); addon.log("Greenhouse failed verification at " + gh.getLocation());
} g.removeGreenhouse(gh);
}
});
} }
private void addMobs(Greenhouse gh) { /**
if(!gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMaxX()) >> 4, ((int) gh.getBoundingBox().getMaxZ()) >> 4) || !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMinX()) >> 4, ((int) gh.getBoundingBox().getMinZ()) >> 4)){ * Try to spawn mobs in a greenhouse
//addon.log("Skipping addmobs for unloaded greenhouse at " + gh.getLocation()); * @param gh greenhouse
return; * @return true if mobs were spawned, false if not
*/
boolean addMobs(Greenhouse gh) {
final BoundingBox bb = gh.getBoundingBox();
if(gh.getLocation() == null || gh.getLocation().getWorld() == null || gh.getWorld() == null
|| !gh.getLocation().getWorld().isChunkLoaded(((int) bb.getMaxX()) >> 4, ((int) bb.getMaxZ()) >> 4)
|| !gh.getLocation().getWorld().isChunkLoaded(((int) bb.getMinX()) >> 4, ((int) bb.getMinZ()) >> 4)){
// Skipping addmobs for unloaded greenhouse
return false;
} }
if (gh.getBiomeRecipe().noMobs()) { if (gh.getBiomeRecipe().noMobs()) {
return; return false;
} }
// Check greenhouse chunks are loaded // Check greenhouse chunks are loaded
for (double blockX = gh.getBoundingBox().getMinX(); blockX < gh.getBoundingBox().getMaxX(); blockX+=16) { for (double blockX = bb.getMinX(); blockX < bb.getMaxX(); blockX+=16) {
for (double blockZ = gh.getBoundingBox().getMinZ(); blockZ < gh.getBoundingBox().getMaxZ(); blockZ+=16) { for (double blockZ = bb.getMinZ(); blockZ < bb.getMaxZ(); blockZ+=16) {
int chunkX = (int)(blockX / 16); int chunkX = (int)(blockX / 16);
int chunkZ = (int)(blockZ / 16); int chunkZ = (int)(blockZ / 16);
if (!gh.getWorld().isChunkLoaded(chunkX, chunkZ)) { if (!gh.getWorld().isChunkLoaded(chunkX, chunkZ)) {
return; return false;
} }
} }
} }
@ -129,17 +163,21 @@ public class EcoSystemManager {
.filter(e -> gh.getBiomeRecipe().getMobTypes().contains(e.getType())) .filter(e -> gh.getBiomeRecipe().getMobTypes().contains(e.getType()))
.filter(e -> gh.contains(e.getLocation())).count(); .filter(e -> gh.contains(e.getLocation())).count();
// Get the blocks in the greenhouse where spawning could occur // Get the blocks in the greenhouse where spawning could occur
List<Block> list = new ArrayList<>(getAvailableBlocks(gh, false)); List<GrowthBlock> list = new ArrayList<>(getAvailableBlocks(gh, false));
Collections.shuffle(list, new Random(System.currentTimeMillis())); Collections.shuffle(list, new Random(System.currentTimeMillis()));
Iterator<Block> it = list.iterator(); Iterator<GrowthBlock> it = list.iterator();
// Check if the greenhouse is full // Check if the greenhouse is full
if (gh.getBiomeRecipe().getMaxMob() > -1 && sum >= gh.getBiomeRecipe().getMaxMob()) {
return false;
}
while (it.hasNext() && (sum == 0 || gh.getArea() / sum >= gh.getBiomeRecipe().getMobLimit())) { while (it.hasNext() && (sum == 0 || gh.getArea() / sum >= gh.getBiomeRecipe().getMobLimit())) {
// Spawn something if chance says so // Spawn something if chance says so
if (gh.getBiomeRecipe().spawnMob(it.next())) { if (gh.getBiomeRecipe().spawnMob(it.next().block())) {
// Add a mob to the sum in the greenhouse // Add a mob to the sum in the greenhouse
sum++; sum++;
} }
} }
return sum > 0;
} }
/** /**
@ -147,14 +185,22 @@ public class EcoSystemManager {
* @param gh - greenhouse * @param gh - greenhouse
*/ */
private void growPlants(Greenhouse gh) { private void growPlants(Greenhouse gh) {
if(!gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMaxX()) >> 4, ((int) gh.getBoundingBox().getMaxZ()) >> 4) || !gh.getLocation().getWorld().isChunkLoaded(((int) gh.getBoundingBox().getMinX()) >> 4, ((int) gh.getBoundingBox().getMinZ()) >> 4)){ final BoundingBox bb = gh.getBoundingBox();
//addon.log("Skipping growplants for unloaded greenhouse at " + gh.getLocation()); if (gh.getLocation() == null || gh.getLocation().getWorld() == null
|| !gh.getLocation().getWorld().isChunkLoaded(((int) bb.getMaxX()) >> 4, ((int) bb.getMaxZ()) >> 4) || !gh.getLocation().getWorld().isChunkLoaded(((int) bb.getMinX()) >> 4, ((int) bb.getMinZ()) >> 4)){
//Skipping growplants for unloaded greenhouse
return; return;
} }
int bonemeal = getBoneMeal(gh); int bonemeal = getBoneMeal(gh);
if (bonemeal > 0) { if (bonemeal > 0) {
// Get a list of all available blocks // Get a list of all available blocks
int plantsGrown = getAvailableBlocks(gh, true).stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl) ? 1 : 0).sum(); List<GrowthBlock> list = getAvailableBlocks(gh, false);
Collections.shuffle(list);
int plantsGrown = list.stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl, false) ? 1 : 0).sum();
// Underwater plants
list = getAvailableBlocks(gh, true);
Collections.shuffle(list);
plantsGrown += list.stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl, true) ? 1 : 0).sum();
if (plantsGrown > 0) { if (plantsGrown > 0) {
setBoneMeal(gh, bonemeal - (int)Math.ceil((double)plantsGrown / PLANTS_PER_BONEMEAL )); setBoneMeal(gh, bonemeal - (int)Math.ceil((double)plantsGrown / PLANTS_PER_BONEMEAL ));
} }
@ -177,24 +223,25 @@ public class EcoSystemManager {
} }
public record GrowthBlock(Block block, Boolean floor) {}
/** /**
* Get a list of the lowest level blocks inside the greenhouse. May be air, liquid or plants. * Get a list of the lowest level blocks inside the greenhouse. May be air, liquid or plants.
* These blocks sit just above solid blocks * These blocks sit just above solid blocks. Leaves are ignored too.
* @param gh - greenhouse * @param gh - greenhouse
* @param ignoreliquid - true if liquid blocks should be treated like air blocks * @param ignoreLiquid - true if liquid blocks should be treated like air blocks
* @return List of blocks * @return List of blocks
*/ */
List<Block> getAvailableBlocks(Greenhouse gh, boolean ignoreLiquid) { protected List<GrowthBlock> getAvailableBlocks(Greenhouse gh, boolean ignoreLiquid) {
List<Block> result = new ArrayList<>(); final BoundingBox bb = gh.getBoundingBox();
for (int x = (int)gh.getBoundingBox().getMinX() + 1; x < (int)gh.getBoundingBox().getMaxX(); x++) { final BoundingBox ibb = gh.getInternalBoundingBox();
for (int z = (int)gh.getBoundingBox().getMinZ() + 1; z < (int)gh.getBoundingBox().getMaxZ(); z++) { List<GrowthBlock> result = new ArrayList<>();
for (int y = (int)gh.getBoundingBox().getMaxY() - 2; y >= (int)gh.getBoundingBox().getMinY(); y--) { if (gh.getWorld() == null) return result;
Block b = gh.getWorld().getBlockAt(x, y, z); for (double x = ibb.getMinX(); x < ibb.getMaxX(); x++) {
if (!(b.isEmpty() || (ignoreLiquid && b.isLiquid())) for (double z = ibb.getMinZ(); z < ibb.getMaxZ(); z++) {
&& (b.getRelative(BlockFace.UP).isEmpty() for (double y = ibb.getMaxY() - 1; y >= bb.getMinY(); y--) {
|| (b.getRelative(BlockFace.UP).isPassable() && !b.isLiquid()) Block b = gh.getWorld().getBlockAt(NumberConversions.floor(x), NumberConversions.floor(y), NumberConversions.floor(z));
|| (ignoreLiquid && b.isLiquid() && b.getRelative(BlockFace.UP).isPassable()))) { if (checkBlock(result, b, ignoreLiquid)) {
result.add(b.getRelative(BlockFace.UP));
break; break;
} }
} }
@ -203,6 +250,31 @@ public class EcoSystemManager {
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);
if (hopper == null || !hopper.getInventory().contains(Material.BONE_MEAL)) { if (hopper == null || !hopper.getInventory().contains(Material.BONE_MEAL)) {
@ -213,12 +285,14 @@ public class EcoSystemManager {
.mapToInt(ItemStack::getAmount).sum(); .mapToInt(ItemStack::getAmount).sum();
} }
/**
* Get the hopper
* @param gh greenhouse
* @return hopper block or null if it does not exist
*/
private Hopper getHopper(Greenhouse gh) { private Hopper getHopper(Greenhouse gh) {
if (gh.getRoofHopperLocation() == null) { // Check if the hopper block is still a hopper
return null; if (gh.getRoofHopperLocation() == null || !gh.getRoofHopperLocation().getBlock().getType().equals(Material.HOPPER)) {
}
// Check if there are any bonemeal in the hopper
if (gh.getRoofHopperLocation().getBlock().getType() != Material.HOPPER) {
gh.setRoofHopperLocation(null); gh.setRoofHopperLocation(null);
return null; return null;
} }

View File

@ -3,104 +3,124 @@ package world.bentobox.greenhouses.managers;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.util.Vector;
import org.bukkit.World.Environment;
import org.bukkit.block.Block;
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;
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult; import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
import world.bentobox.greenhouses.world.AsyncWorldCache;
public class GreenhouseFinder { public class GreenhouseFinder {
private Greenhouse gh = new Greenhouse(); private Greenhouse gh;
private final Set<Location> redGlass = new HashSet<>(); private final Set<Vector> redGlass = new HashSet<>();
// Counts
private int wallDoors = 0;
// Hoppers
private int ghHopper = 0;
// Air
private boolean airHoles = false;
// Other blocks
private boolean otherBlocks = false;
// Ceiling issue // Ceiling issue
private boolean inCeiling = false; private boolean inCeiling = false;
// The y height where other blocks were found // The y height where other blocks were found
// 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
*/
private CounterCheck counterCheck = new CounterCheck();
class CounterCheck { static class CounterCheck {
int doorCount; int doorCount;
int hopperCount; int hopperCount;
boolean airHole; boolean airHole;
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
* @return GreenhouseResult class * @return future GreenhouseResult class
*/ */
public Set<GreenhouseResult> find(Location location) { public CompletableFuture<Set<GreenhouseResult>> find(Location location) {
CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
Set<GreenhouseResult> result = new HashSet<>(); Set<GreenhouseResult> result = new HashSet<>();
redGlass.clear(); redGlass.clear();
// Get a world cache
AsyncWorldCache cache = new AsyncWorldCache(addon, location.getWorld());
// Find the roof // Find the roof
Roof roof = new Roof(location); Roof roof = new Roof(cache, location, addon);
if (!roof.isRoofFound()) { roof.findRoof().thenAccept(found -> {
result.add(GreenhouseResult.FAIL_NO_ROOF); if (Boolean.FALSE.equals(found)) {
return result; result.add(GreenhouseResult.FAIL_NO_ROOF);
} r.complete(result);
// Find the walls return;
Walls walls = new Walls().findWalls(roof); }
// Make the initial greenhouse // Find the walls
gh = new Greenhouse(location.getWorld(), walls, roof.getHeight()); new Walls(cache).findWalls(roof).thenAccept(walls -> {
// Set the original biome // Make the initial greenhouse
gh.setOriginalBiome(location.getBlock().getBiome()); gh = new Greenhouse(location.getWorld(), walls, roof.getHeight());
// Set the original biome
gh.setOriginalBiome(location.getBlock().getBiome());
// Now check to see if the floor really is the floor and the walls follow the rules // Now check to see if the floor really is the floor and the walls follow the rules
result.addAll(checkGreenhouse(gh, roof, walls)); checkGreenhouse(cache, roof, walls).thenAccept(c -> {
result.addAll(c);
r.complete(result);
});
});
return result; });
return r;
} }
Set<GreenhouseResult> checkGreenhouse(Greenhouse gh2, Roof roof, Walls walls) { /**
Set<GreenhouseResult> result = new HashSet<>(); * Check the greenhouse has the right number of everything
World world = roof.getLocation().getWorld(); * @param cache async world cache
* @param roof - roof object
* @param walls - walls object
* @return future set of Greenhouse Results
*/
CompletableFuture<Set<GreenhouseResult>> checkGreenhouse(AsyncWorldCache cache, Roof roof, Walls walls) {
CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> checkGreenhouseAsync(r, cache, roof, walls));
return r;
}
private Set<GreenhouseResult> checkGreenhouseAsync(CompletableFuture<Set<GreenhouseResult>> r, AsyncWorldCache cache,
Roof roof, Walls walls) {
counterCheck = new CounterCheck();
int y; int y;
for (y = world.getMaxHeight() - 1; y >= walls.getFloor(); y--) { for (y = roof.getHeight(); y > walls.getFloor(); y--) {
CounterCheck cc = new CounterCheck();
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++) {
result.addAll(checkBlock(cc, roof, walls, world.getBlockAt(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) {
wallDoors += cc.doorCount; otherBlockLayer = y;
ghHopper += cc.hopperCount;
if (cc.airHole) {
airHoles = true;
}
if (cc.otherBlock) {
otherBlocks = true;
if (otherBlockLayer < 0) {
otherBlockLayer = y;
}
}
} }
} }
result.addAll(checkErrors(roof, y)); Set<GreenhouseResult> result = new HashSet<>(checkErrors(roof, y));
Bukkit.getScheduler().runTask(BentoBox.getInstance(), () -> r.complete(result));
return result; return result;
} }
@ -111,102 +131,102 @@ public class GreenhouseFinder {
result.add(GreenhouseResult.FAIL_BELOW); result.add(GreenhouseResult.FAIL_BELOW);
} }
// Show errors // Show errors
if (airHoles && !inCeiling) { if (isAirHoles() && !inCeiling) {
result.add(GreenhouseResult.FAIL_HOLE_IN_WALL); result.add(GreenhouseResult.FAIL_HOLE_IN_WALL);
} else if (airHoles && inCeiling) { } else if (isAirHoles() && inCeiling) {
result.add(GreenhouseResult.FAIL_HOLE_IN_ROOF); result.add(GreenhouseResult.FAIL_HOLE_IN_ROOF);
} }
if (otherBlocks && otherBlockLayer == y + 1) { if (isOtherBlocks() && otherBlockLayer == y + 1) {
// Walls must be even all the way around // Walls must be even all the way around
result.add(GreenhouseResult.FAIL_UNEVEN_WALLS); result.add(GreenhouseResult.FAIL_UNEVEN_WALLS);
} else if (otherBlocks && otherBlockLayer == roof.getHeight()) { } else if (isOtherBlocks() && otherBlockLayer == roof.getHeight()) {
// 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 (otherBlocks) { } 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 (wallDoors > 8) { if (this.getWallDoors() > 8) {
result.add(GreenhouseResult.FAIL_TOO_MANY_DOORS); result.add(GreenhouseResult.FAIL_TOO_MANY_DOORS);
} }
if (ghHopper > 1) { if (this.getGhHopper() > 1) {
result.add(GreenhouseResult.FAIL_TOO_MANY_HOPPERS); result.add(GreenhouseResult.FAIL_TOO_MANY_HOPPERS);
} }
return result; return result;
} }
Set<GreenhouseResult> checkBlock(CounterCheck cc, Roof roof, Walls walls, Block block) {
Set<GreenhouseResult> result = new HashSet<>();
World world = block.getWorld();
// Checking above greenhouse - no blocks allowed
if (block.getY() > roof.getHeight()) {
// We are above the greenhouse
if (!world.getEnvironment().equals(Environment.NETHER) && !block.isEmpty()) {
result.add(GreenhouseResult.FAIL_BLOCKS_ABOVE);
redGlass.add(block.getLocation());
}
} else {
// Check just the walls
checkWalls(block, roof, walls, cc);
}
return result;
}
/** /**
* Check a wall block * Check if block is allowed to be in that location
* @param block - block * @param cc - Counter Check object
* @param m - material of the block
* @param roof - roof object * @param roof - roof object
* @param walls - wall object * @param walls - walls object
* @param cc - count * @param v - vector location of the block
* @return true if block was in the wall * @return true if block is acceptable, false if not
*/ */
boolean checkWalls(Block block, Roof roof, Walls walls, CounterCheck cc) { boolean checkBlock(CounterCheck cc, Material m, Roof roof, Walls walls, Vector v) {
int x = block.getX(); final int x = v.getBlockX();
int y = block.getY(); final int y = v.getBlockY();
int z = block.getZ(); final int z = v.getBlockZ();
// 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(block.getType())) || (y == roof.getHeight() && !Roof.roofBlocks(block.getType()))) { if ((y != roof.getHeight() && !addon.wallBlocks(m))
if (block.isEmpty()) { || (y == roof.getHeight() && !roof.roofBlocks(m))) {
if (m.equals(Material.AIR)) {
// Air hole found
cc.airHole = true; cc.airHole = true;
if (y == roof.getHeight()) { if (y == roof.getHeight()) {
// Air hole is in ceiling
inCeiling = true; inCeiling = true;
} }
} else { } else {
// A non-wall or roof block found
cc.otherBlock = true; cc.otherBlock = true;
} }
redGlass.add(block.getLocation()); // Record the incorrect location
redGlass.add(v);
return false;
} else { } else {
// Normal wall blocks // Normal wall blocks
wallBlockCount++; wallBlockCount++;
checkDoorsHoppers(cc, block); return checkDoorsHoppers(cc, m, v);
} }
return true;
} }
return false; return true;
} }
void checkDoorsHoppers(CounterCheck cc, Block block) { /**
* 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 // Count doors
if (Tag.DOORS.isTagged(block.getType())) { if (Tag.TRAPDOORS.isTagged(m) || Tag.DOORS.isTagged(m)) {
cc.doorCount++; 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 we already have 8 doors add these blocks to the red list
if (wallDoors == 8) { if (cc.doorCount > 8) {
redGlass.add(block.getLocation()); redGlass.add(v);
return false;
} }
} }
// Count hoppers // Count hoppers
if (block.getType().equals(Material.HOPPER)) { if (m.equals(Material.HOPPER)) {
cc.hopperCount++; cc.hopperCount++;
if (ghHopper > 0) { if (cc.hopperCount > 1) {
// Problem! Add extra hoppers to the red glass list // Problem! Add extra hoppers to the red glass list
redGlass.add(block.getLocation()); redGlass.add(v);
return false;
} else { } else {
// This is the first hopper // This is the first hopper
gh.setRoofHopperLocation(block.getLocation()); gh.setRoofHopperLocation(v);
} }
} }
return true;
} }
/** /**
@ -219,7 +239,7 @@ public class GreenhouseFinder {
/** /**
* @return the redGlass * @return the redGlass
*/ */
public Set<Location> getRedGlass() { public Set<Vector> getRedGlass() {
return redGlass; return redGlass;
} }
@ -227,35 +247,28 @@ public class GreenhouseFinder {
* @return the wallDoors * @return the wallDoors
*/ */
int getWallDoors() { int getWallDoors() {
return wallDoors; return counterCheck.doorCount;
}
/**
* @param wallDoors the wallDoors to set
*/
void setWallDoors(int wallDoors) {
this.wallDoors = wallDoors;
} }
/** /**
* @return the ghHopper * @return the ghHopper
*/ */
int getGhHopper() { int getGhHopper() {
return ghHopper; return counterCheck.hopperCount;
} }
/** /**
* @return the airHoles * @return the airHoles
*/ */
boolean isAirHoles() { boolean isAirHoles() {
return airHoles; return counterCheck.airHole;
} }
/** /**
* @return the otherBlocks * @return the otherBlocks
*/ */
boolean isOtherBlocks() { boolean isOtherBlocks() {
return otherBlocks; return counterCheck.otherBlock;
} }
/** /**
@ -279,27 +292,6 @@ public class GreenhouseFinder {
return wallBlockCount; return wallBlockCount;
} }
/**
* @param ghHopper the ghHopper to set
*/
void setGhHopper(int ghHopper) {
this.ghHopper = ghHopper;
}
/**
* @param airHoles the airHoles to set
*/
void setAirHoles(boolean airHoles) {
this.airHoles = airHoles;
}
/**
* @param otherBlocks the otherBlocks to set
*/
void setOtherBlocks(boolean otherBlocks) {
this.otherBlocks = otherBlocks;
}
/** /**
* @param inCeiling the inCeiling to set * @param inCeiling the inCeiling to set
*/ */
@ -321,4 +313,30 @@ public class GreenhouseFinder {
this.wallBlockCount = wallBlockCount; this.wallBlockCount = wallBlockCount;
} }
/**
* @param gh the gh to set
*/
protected void setGh(Greenhouse gh) {
this.gh = gh;
}
public void setGhHopper(int i) {
counterCheck.hopperCount = i;
}
public void setWallDoors(int i) {
counterCheck.doorCount = i;
}
public void setAirHoles(boolean b) {
counterCheck.airHole = b;
}
public void setOtherBlocks(boolean b) {
counterCheck.otherBlock = b;
}
} }

View File

@ -1,13 +1,18 @@
package world.bentobox.greenhouses.managers; package world.bentobox.greenhouses.managers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.util.BoundingBox;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.Database;
@ -91,29 +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);
break; case FAIL_OVERLAPPING -> addon.logError("Greenhouse overlaps with another greenhouse. Skipping...");
case FAIL_OVERLAPPING: case NULL -> addon.logError("Null location of greenhouse. Cannot load. Skipping...");
addon.logError("Greenhouse overlaps with another greenhouse. Skipping..."); case SUCCESS -> activateGreenhouse(g);
break; case FAIL_NO_WORLD -> addon.logError("Database contains greenhouse for a non-loaded world. Skipping...");
case NULL: case FAIL_UNKNOWN_RECIPE -> {
addon.logError("Null location of greenhouse. Cannot load. Skipping...");
break;
case SUCCESS:
activateGreenhouse(g);
break;
case FAIL_NO_WORLD:
addon.logError("Database contains greenhouse for a non-loaded world. Skipping...");
break;
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());
break; }
default: default -> {
break; }
} }
}); });
addon.log("Loaded " + map.getSize() + " greenhouses."); addon.log("Loaded " + map.getSize() + " greenhouses.");
@ -121,27 +116,28 @@ public class GreenhouseManager implements Listener {
toBeRemoved.forEach(handler::deleteObject); toBeRemoved.forEach(handler::deleteObject);
} }
/**
* Saves all the greenhouses to database
*/
public void saveGreenhouses() {
addon.log("Saving greenhouses...");
map.getGreenhouses().forEach(handler::saveObjectAsync);
}
/** /**
* Removes the greenhouse from the world and resets biomes * Removes the greenhouse from the world and resets biomes
* @param g - greenhouse * @param gh - greenhouse
*/ */
public void removeGreenhouse(Greenhouse g) { public void removeGreenhouse(Greenhouse gh) {
handler.deleteObject(g); handler.deleteObject(gh);
map.removeGreenhouse(g); map.removeGreenhouse(gh);
addon.log("Returning biome to original state: " + g.getOriginalBiome().toString()); if (gh.getOriginalBiome() == null) {
for (int x = (int)g.getBoundingBox().getMinX(); x<= (int)g.getBoundingBox().getMaxX(); x+=4) { addon.logError("Greenhouse had no original biome: " + gh.getLocation());
for (int z = (int)g.getBoundingBox().getMinZ(); z<= (int)g.getBoundingBox().getMaxZ(); z+=4) { return;
for (int y = (int)g.getBoundingBox().getMinY(); y<= (int)g.getBoundingBox().getMaxY(); y+=4) { }
if (gh.getLocation() == null || gh.getLocation().getWorld() == null) {
// Greenhouse is messed up. It's being deleted anyway.
return;
}
addon.log("Returning biome to original state: " + gh.getOriginalBiome().toString());
final BoundingBox bb = gh.getBoundingBox();
for (int x = (int)bb.getMinX(); x<= (int)bb.getMaxX(); x+=4) {
for (int z = (int)bb.getMinZ(); z<= (int)bb.getMaxZ(); z+=4) {
for (int y = (int)bb.getMinY(); y<= (int)bb.getMaxY(); y+=4) {
// Set back to the original biome // Set back to the original biome
g.getLocation().getWorld().setBiome(x, y, z, g.getOriginalBiome()); gh.getLocation().getWorld().setBiome(x, y, z, gh.getOriginalBiome());
} }
} }
} }
@ -153,39 +149,83 @@ public class GreenhouseManager implements Listener {
* If type is stated then only this specific type will be checked * If type is stated then only this specific type will be checked
* @param location - location to start search from * @param location - location to start search from
* @param greenhouseRecipe - recipe requested, or null for a best-effort search * @param greenhouseRecipe - recipe requested, or null for a best-effort search
* @return - greenhouse result {@link GhResult} * @return - future greenhouse result {@link GhResult}
*/ */
public GhResult tryToMakeGreenhouse(Location location, BiomeRecipe greenhouseRecipe) { public CompletableFuture<GhResult> tryToMakeGreenhouse(Location location, BiomeRecipe greenhouseRecipe) {
GreenhouseFinder finder = new GreenhouseFinder(); CompletableFuture<GhResult> r = new CompletableFuture<>();
Set<GreenhouseResult> resultSet = finder.find(location); GreenhouseFinder finder = new GreenhouseFinder(addon);
if (!resultSet.isEmpty()) { finder.find(location).thenAccept(resultSet -> {
// Failure! if (!resultSet.isEmpty()) {
return new GhResult().setFinder(finder).setResults(resultSet); // Failure!
r.complete(new GhResult().setFinder(finder).setResults(resultSet));
return;
}
// Check if the greenhouse meets the requested recipe
if (greenhouseRecipe != null) {
checkRecipe(finder, greenhouseRecipe, resultSet).thenAccept(r::complete);
return;
}
// Try ordered recipes
findRecipe(finder).thenAccept(rs -> {
resultSet.addAll(rs);
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
*/
private CompletableFuture<Set<GreenhouseResult>> findRecipe(GreenhouseFinder finder) {
CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
// Get sorted list of all recipes
List<BiomeRecipe> list = addon.getRecipes().getBiomeRecipes().stream().sorted().collect(Collectors.toList());
findRecipe(r, list, finder);
return r;
}
private void findRecipe(CompletableFuture<Set<GreenhouseResult>> r, List<BiomeRecipe> list,
GreenhouseFinder finder) {
if (list.isEmpty()) {
r.complete(Collections.singleton(GreenhouseResult.FAIL_NO_RECIPE_FOUND));
return;
} }
// Check if the greenhouse meets the requested recipe BiomeRecipe br = list.get(0);
if (greenhouseRecipe != null) { list.remove(0);
resultSet = greenhouseRecipe.checkRecipe(finder.getGh()); br.checkRecipe(finder.getGh()).thenAccept(results -> {
if (resultSet.isEmpty()) { if (results.isEmpty()) {
r.complete(Collections.singleton(GreenhouseResult.SUCCESS));
} else {
findRecipe(r, list, finder);
}
});
}
/**
* Checks to see if the greenhouse meets the designated recipe and returns the result
* @param finder - finder object
* @param greenhouseRecipe - recipe requested
* @param resultSet - result set from finder
* @return Greenhouse result
*/
CompletableFuture<GhResult> checkRecipe(GreenhouseFinder finder, BiomeRecipe greenhouseRecipe, Set<GreenhouseResult> resultSet) {
CompletableFuture<GhResult> r = new CompletableFuture<>();
greenhouseRecipe.checkRecipe(finder.getGh()).thenAccept(rs -> {
if (rs.isEmpty()) {
// Success - set recipe and add to map // Success - set recipe and add to map
finder.getGh().setBiomeRecipe(greenhouseRecipe); finder.getGh().setBiomeRecipe(greenhouseRecipe);
resultSet.add(map.addGreenhouse(finder.getGh())); resultSet.add(map.addGreenhouse(finder.getGh()));
activateGreenhouse(finder.getGh()); activateGreenhouse(finder.getGh());
handler.saveObjectAsync(finder.getGh()); handler.saveObjectAsync(finder.getGh());
rs.addAll(resultSet);
} }
return new GhResult().setFinder(finder).setResults(resultSet); GhResult recipe = new GhResult().setFinder(finder).setResults(rs);
} r.complete(recipe);
});
// Try ordered recipes return r;
resultSet.add(addon.getRecipes().getBiomeRecipes().stream().sorted()
.filter(r -> r.checkRecipe(finder.getGh()).isEmpty()).findFirst()
.map(r -> {
// Success - set recipe and add to map
finder.getGh().setBiomeRecipe(r);
activateGreenhouse(finder.getGh());
handler.saveObjectAsync(finder.getGh());
return map.addGreenhouse(finder.getGh());
}).orElse(GreenhouseResult.FAIL_NO_RECIPE_FOUND));
return new GhResult().setFinder(finder).setResults(resultSet);
} }
private void activateGreenhouse(Greenhouse gh) { private void activateGreenhouse(Greenhouse gh) {
@ -194,10 +234,11 @@ public class GreenhouseManager implements Listener {
addon.logError("Biome recipe error - no such biome for " + gh.getBiomeRecipe().getName()); addon.logError("Biome recipe error - no such biome for " + gh.getBiomeRecipe().getName());
return; return;
} }
for (int x = (int)gh.getBoundingBox().getMinX(); x < gh.getBoundingBox().getMaxX(); x+=4) { final BoundingBox bb = gh.getBoundingBox();
for (int z = (int)gh.getBoundingBox().getMinZ(); z < gh.getBoundingBox().getMaxZ(); z+=4) { for (int x = (int)bb.getMinX(); x < bb.getMaxX(); x+=4) {
for (int y = (int)gh.getBoundingBox().getMinY(); y < gh.getBoundingBox().getMaxY(); y+=4) { for (int z = (int)bb.getMinZ(); z < bb.getMaxZ(); z+=4) {
gh.getWorld().setBiome(x, y, z, ghBiome); for (int y = (int)bb.getMinY(); y < bb.getMaxY(); y+=4) {
Objects.requireNonNull(gh.getWorld()).setBiome(x, y, z, ghBiome);
} }
} }
} }
@ -207,7 +248,7 @@ public class GreenhouseManager implements Listener {
* Result of the greenhouse make effort * Result of the greenhouse make effort
* *
*/ */
public class GhResult { public static class GhResult {
private Set<GreenhouseResult> results; private Set<GreenhouseResult> results;
private GreenhouseFinder finder; private GreenhouseFinder finder;

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;
@ -34,7 +33,7 @@ public class GreenhouseMap {
*/ */
public GreenhouseResult addGreenhouse(Greenhouse greenhouse) { public GreenhouseResult addGreenhouse(Greenhouse greenhouse) {
// Validation checks // Validation checks
if (greenhouse.getBiomeRecipe() == null) { if (greenhouse.getBiomeRecipe().getBiome() == null) {
return GreenhouseResult.FAIL_UNKNOWN_RECIPE; return GreenhouseResult.FAIL_UNKNOWN_RECIPE;
} }
if (greenhouse.getWorld() == null) { if (greenhouse.getWorld() == null) {
@ -105,9 +104,11 @@ public class GreenhouseMap {
} }
private boolean isOverlapping(Greenhouse greenhouse) { private boolean isOverlapping(Greenhouse greenhouse) {
return addon.getIslands().getIslandAt(greenhouse.getLocation()).map(i -> { return greenhouse.getLocation() != null && addon.getIslands().getIslandAt(greenhouse.getLocation()).map(i -> {
greenhouses.putIfAbsent(i, new ArrayList<>()); greenhouses.putIfAbsent(i, new ArrayList<>());
return greenhouses.get(i).stream().anyMatch(g -> g.getBoundingBox().overlaps(greenhouse.getBoundingBox())); return greenhouses.get(i).stream().anyMatch(g ->
g.getLocation().getWorld().equals(greenhouse.getLocation().getWorld()) &&
g.getBoundingBox().overlaps(greenhouse.getBoundingBox()));
}).orElse(false); }).orElse(false);
} }
@ -117,13 +118,15 @@ public class GreenhouseMap {
* @param greenhouse - greenhouse * @param greenhouse - greenhouse
*/ */
protected void removeGreenhouse(Greenhouse greenhouse) { protected void removeGreenhouse(Greenhouse greenhouse) {
addon.getIslands().getIslandAt(greenhouse.getLocation()).ifPresent(i -> { if (greenhouse.getLocation() != null) {
if (greenhouses.containsKey(i)) greenhouses.get(i).remove(greenhouse); addon.getIslands().getIslandAt(greenhouse.getLocation()).ifPresent(i -> {
}); if (greenhouses.containsKey(i)) greenhouses.get(i).remove(greenhouse);
});
}
} }
/** /**
* @param island * @param island island
*/ */
public void removeGreenhouses(Island island) { public void removeGreenhouses(Island island) {
greenhouses.remove(island); greenhouses.remove(island);
@ -134,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

@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -29,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;
@ -68,6 +70,7 @@ public class RecipeManager {
} }
ConfigurationSection biomeSection = biomeConfig.getConfigurationSection("biomes"); ConfigurationSection biomeSection = biomeConfig.getConfigurationSection("biomes");
// Loop through all the entries // Loop through all the entries
assert biomeSection != null;
for (String type: biomeSection.getValues(false).keySet()) { for (String type: biomeSection.getValues(false).keySet()) {
processEntries(type, biomeSection); processEntries(type, biomeSection);
// Check maximum number // Check maximum number
@ -82,6 +85,7 @@ public class RecipeManager {
private void processEntries(String biomeType, ConfigurationSection biomeSection) { private void processEntries(String biomeType, ConfigurationSection biomeSection) {
try { try {
ConfigurationSection biomeRecipeConfig = biomeSection.getConfigurationSection(biomeType); ConfigurationSection biomeRecipeConfig = biomeSection.getConfigurationSection(biomeType);
assert biomeRecipeConfig != null;
Biome thisBiome = loadBiome(biomeType, biomeRecipeConfig); Biome thisBiome = loadBiome(biomeType, biomeRecipeConfig);
if (thisBiome == null) return; if (thisBiome == null) return;
int priority = biomeRecipeConfig.getInt("priority", 0); int priority = biomeRecipeConfig.getInt("priority", 0);
@ -124,7 +128,7 @@ public class RecipeManager {
addon.logError("No biome defined in the biome reciepe " + biomeType + ". Skipping..."); addon.logError("No biome defined in the biome reciepe " + biomeType + ". Skipping...");
return null; return null;
} }
String name = biomeRecipeConfig.getString("biome").toUpperCase(Locale.ENGLISH); String name = Objects.requireNonNull(biomeRecipeConfig.getString("biome")).toUpperCase(Locale.ENGLISH);
if (Enums.getIfPresent(Biome.class, name).isPresent()) { if (Enums.getIfPresent(Biome.class, name).isPresent()) {
return Biome.valueOf(name); return Biome.valueOf(name);
} }
@ -152,6 +156,7 @@ public class RecipeManager {
b.setLavacoverage(biomeRecipeConfig.getInt("lavacoverage",-1)); b.setLavacoverage(biomeRecipeConfig.getInt("lavacoverage",-1));
b.setIcecoverage(biomeRecipeConfig.getInt("icecoverage",-1)); b.setIcecoverage(biomeRecipeConfig.getInt("icecoverage",-1));
b.setMobLimit(biomeRecipeConfig.getInt("moblimit", 9)); b.setMobLimit(biomeRecipeConfig.getInt("moblimit", 9));
b.setMaxMob(biomeRecipeConfig.getInt("maxmobs", -1));
return b; return b;
} }
@ -175,44 +180,59 @@ 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 (!conversions.isEmpty()) {
String[] split = conversions.split(":");
double convChance = Double.parseDouble(split[0]);
Material newMaterial = Material.valueOf(split[1]);
Material 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 = 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
if (temp != null) { if (temp != null) {
((HashMap<String,Object>)temp.getValues(false)).entrySet().forEach(s -> parseMob(s,b)); temp.getValues(false).entrySet().forEach(s -> parseMob(s,b));
} }
} }
@ -225,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

@ -1,110 +0,0 @@
package world.bentobox.greenhouses.ui.admin;
import java.util.List;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.greenhouses.Greenhouses;
/**
* This class handles commands for admins
*
*/
class AdminCmd extends CompositeCommand {
public AdminCmd(Greenhouses greenhouses) {
super(greenhouses, "gadmin");
}
@Override
public void setup() {
this.setPermission("greenhouses.admin");
this.setOnlyPlayer(false);
this.setParametersHelp("greenhouses.admin.parameters");
this.setDescription("greenhouses.admin.description");
new GreenhousesAdminReloadCommand(this);
new GreenhousesAdminInfoCommand(this);
}
@Override
public boolean execute(User user, String label, List<String> args) {
return false;
}
/*
case 1:
if (split[0].equalsIgnoreCase("reload")) {
plugin.reloadConfig();
plugin.loadPluginConfig();
plugin.loadBiomeRecipes();
plugin.ecoTick();
sender.sendMessage(ChatColor.YELLOW + Locale.reloadconfigReloaded);
return true;
} else if (split[0].equalsIgnoreCase("info")) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + Locale.admininfoerror);
return true;
}
Player player = (Player)sender;
Greenhouse greenhouse = players.getInGreenhouse(player);
if (greenhouse == null) {
sender.sendMessage(ChatColor.RED + Locale.admininfoerror2);
return true;
}
sender.sendMessage(ChatColor.GREEN + Locale.infoinfo);
sender.sendMessage(ChatColor.GREEN + Locale.generalowner + ":" + greenhouse.getPlayerName());
sender.sendMessage(ChatColor.GREEN + Locale.admininfoflags);
for (String flag : greenhouse.getFlags().keySet()) {
sender.sendMessage(flag + ": " + greenhouse.getFlags().get(flag));
}
return true;
} else {
sender.sendMessage(ChatColor.RED + Locale.errorunknownCommand);
return false;
}
case 2:
if (split[0].equalsIgnoreCase("info")) {
sender.sendMessage(ChatColor.GREEN + Locale.infoinfo);
int index = 0;
boolean found = false;
for (Greenhouse g : plugin.getGreenhouses()) {
if (g.getPlayerName().equalsIgnoreCase(split[1])) {
if (!found)
sender.sendMessage(ChatColor.GREEN + Locale.generalowner + ":" + g.getPlayerName());
found = true;
sender.sendMessage("Greenhouse #" + (++index));
sender.sendMessage("Biome: " + g.getBiome().name());
sender.sendMessage("Recipe: " + g.getBiomeRecipe().getFriendlyName());
sender.sendMessage(g.getWorld().getName());
sender.sendMessage(g.getPos1().getBlockX() + ", " + g.getPos1().getBlockZ() + " to " + g.getPos2().getBlockX() + ", " + g.getPos2().getBlockZ());
sender.sendMessage("Base at " + g.getPos1().getBlockY());
sender.sendMessage("Height = " + g.getHeight());
sender.sendMessage("Area = " + g.getArea());
}
}
if (found) {
if (index == 0) {
sender.sendMessage("Player has no greenhouses.");
} else {
Player player = plugin.getServer().getPlayer(split[1]);
if (player != null) {
sender.sendMessage("Player has " + index + " greenhouses and is allowed to build " + plugin.getMaxGreenhouses(player));
} else {
sender.sendMessage("Player has " + index + " greenhouses. Player is offline.");
}
}
} else {
sender.sendMessage(ChatColor.RED + "Cannot find that player. (May not have logged on recently)");
}
return true;
} else {
sender.sendMessage(ChatColor.RED + Locale.errorunknownCommand);
return false;
}
default:
return false;
}
}*/
}

View File

@ -1,39 +0,0 @@
package world.bentobox.greenhouses.ui.admin;
import java.util.List;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
/**
* @author tastybento
*
*/
class GreenhousesAdminInfoCommand extends CompositeCommand {
/**
* @param parent - parent user command, e.g, /island
*/
public GreenhousesAdminInfoCommand(CompositeCommand parent) {
super(parent, "info");
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup()
*/
@Override
public void setup() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)
*/
@Override
public boolean execute(User user, String label, List<String> args) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -1,39 +0,0 @@
package world.bentobox.greenhouses.ui.admin;
import java.util.List;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
/**
* @author tastybento
*
*/
class GreenhousesAdminReloadCommand extends CompositeCommand {
/**
* @param parent - parent command
*/
public GreenhousesAdminReloadCommand(CompositeCommand parent) {
super(parent, "reload");
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup()
*/
@Override
public void setup() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)
*/
@Override
public boolean execute(User user, String label, List<String> args) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -13,7 +13,7 @@ import world.bentobox.greenhouses.greenhouse.BiomeRecipe;
public class Panel { public class Panel {
private static final String COVERAGE = "[coverage]"; private static final String COVERAGE = "[coverage]";
private Greenhouses addon; private final Greenhouses addon;
public Panel(Greenhouses addon) { public Panel(Greenhouses addon) {
super(); super();

View File

@ -22,8 +22,8 @@ import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
*/ */
public class PanelClick implements ClickHandler { public class PanelClick implements ClickHandler {
private Greenhouses addon; private final Greenhouses addon;
private BiomeRecipe br; private final BiomeRecipe br;
public PanelClick(Greenhouses addon, BiomeRecipe br) { public PanelClick(Greenhouses addon, BiomeRecipe br) {
this.addon = addon; this.addon = addon;
@ -55,22 +55,24 @@ public class PanelClick implements ClickHandler {
user.sendMessage("greenhouses.commands.user.make.error.already"); user.sendMessage("greenhouses.commands.user.make.error.already");
return false; return false;
} }
GhResult result = addon.getManager().tryToMakeGreenhouse(location, br); addon.getManager().tryToMakeGreenhouse(location, br).thenAccept(r -> processResult(user, r));
return true;
}
void processResult(User user, GhResult result) {
if (result.getResults().contains(GreenhouseResult.SUCCESS)) { if (result.getResults().contains(GreenhouseResult.SUCCESS)) {
// Success // Success
user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName()); user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName());
return true; return;
} }
result.getResults().forEach(r -> user.sendMessage("greenhouses.commands.user.make.error." + r.name())); result.getResults().forEach(r -> user.sendMessage("greenhouses.commands.user.make.error." + r.name()));
if (!result.getFinder().getRedGlass().isEmpty()) { if (!result.getFinder().getRedGlass().isEmpty()) {
// Show red glass // Show red glass
result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg, Material.RED_STAINED_GLASS.createBlockData())); result.getFinder().getRedGlass().stream().map(v -> v.toLocation(user.getWorld())).forEach(rg -> user.getPlayer().sendBlockChange(rg, Material.RED_STAINED_GLASS.createBlockData()));
Bukkit.getScheduler().runTaskLater(addon.getPlugin(), () -> result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg, rg.getBlock().getBlockData())), 120L); Bukkit.getScheduler().runTaskLater(addon.getPlugin(), () -> result.getFinder().getRedGlass().stream().map(v -> v.toLocation(user.getWorld())).forEach(rg -> user.getPlayer().sendBlockChange(rg, rg.getBlock().getBlockData())), 120L);
} }
if (result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) { if (result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) {
result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v))); result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v)));
} }
return true;
} }
} }

View File

@ -52,7 +52,7 @@ class MakeCommand extends CompositeCommand {
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
if (args.isEmpty()) { if (args.isEmpty()) {
new Panel((Greenhouses)this.getAddon()).showPanel(user); new Panel(this.getAddon()).showPanel(user);
return true; return true;
} }
// Check recipe given matches // Check recipe given matches
@ -76,16 +76,21 @@ class MakeCommand extends CompositeCommand {
return getRecipes(user).get(arg); return getRecipes(user).get(arg);
} }
/** /**
* Get a string list of recipies the player has permission to use * Get a string list of recipes the player has permission to use
* @param user - user * @param user - user
* @return list * @return list
*/ */
private Map<String, BiomeRecipe> getRecipes(User user) { private Map<String, BiomeRecipe> getRecipes(User user) {
return ((Greenhouses)getAddon()).getRecipes().getBiomeRecipes().stream() return ((Greenhouses)getAddon()).getRecipes().getBiomeRecipes().stream()
.filter(br -> user.hasPermission(br.getPermission())) .filter(br -> user.hasPermission(br.getPermission()))
.collect(Collectors.toMap(br -> br.getName(), br -> br)); .collect(Collectors.toMap(BiomeRecipe::getName, br -> br));
} }
/**
* @param user - user
* @param br requested biome recipe, or null to try anything
* @return true if successful
*/
private boolean makeGreenhouse(User user, BiomeRecipe br) { private boolean makeGreenhouse(User user, BiomeRecipe br) {
// Check flag // Check flag
if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.isAllowed(user, Greenhouses.GREENHOUSES)).orElse(false)) { if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.isAllowed(user, Greenhouses.GREENHOUSES)).orElse(false)) {
@ -99,7 +104,12 @@ class MakeCommand extends CompositeCommand {
user.sendMessage("greenhouses.commands.user.make.error.already"); user.sendMessage("greenhouses.commands.user.make.error.already");
return false; return false;
} }
GhResult result = ((Greenhouses)this.getAddon()).getManager().tryToMakeGreenhouse(location, br); // Try to make the greenhouse
((Greenhouses)this.getAddon()).getManager().tryToMakeGreenhouse(location, br).thenAccept(result -> informUser(user, br, result));
return true;
}
private boolean informUser(User user, BiomeRecipe br, GhResult result) {
if (result.getResults().contains(GreenhouseResult.SUCCESS)) { if (result.getResults().contains(GreenhouseResult.SUCCESS)) {
// Success // Success
user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName()); user.sendMessage("greenhouses.commands.user.make.success", "[biome]", result.getFinder().getGh().getBiomeRecipe().getFriendlyName());
@ -108,18 +118,18 @@ class MakeCommand extends CompositeCommand {
result.getResults().forEach(r -> user.sendMessage("greenhouses.commands.user.make.error." + r.name())); result.getResults().forEach(r -> user.sendMessage("greenhouses.commands.user.make.error." + r.name()));
if (!result.getFinder().getRedGlass().isEmpty()) { if (!result.getFinder().getRedGlass().isEmpty()) {
// Show red glass // Show red glass
result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg, Material.RED_STAINED_GLASS.createBlockData())); result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg.toLocation(user.getWorld()), Material.RED_STAINED_GLASS.createBlockData()));
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> result.getFinder().getRedGlass().forEach(rg -> user.getPlayer().sendBlockChange(rg, rg.getBlock().getBlockData())), 120L); Bukkit.getScheduler().runTaskLater(getPlugin(), () -> result.getFinder().getRedGlass().stream().map(v -> v.toLocation(user.getWorld())).forEach(rg -> user.getPlayer().sendBlockChange(rg, rg.getBlock().getBlockData())), 120L);
} }
if (br != null && result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) { if (br != null && result.getResults().contains(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS)) {
result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v))); result.getFinder().getGh().getMissingBlocks().forEach((k,v) -> user.sendMessage("greenhouses.commands.user.make.missing-blocks", "[material]", Util.prettifyText(k.toString()), TextVariables.NUMBER, String.valueOf(v)));
} }
return true; return false;
} }
@Override @Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) { public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
return Optional.of(new ArrayList<String>(this.getRecipes(user).keySet())); return Optional.of(new ArrayList<>(this.getRecipes(user).keySet()));
} }
} }

View File

@ -40,7 +40,7 @@ class RemoveCommand extends CompositeCommand {
user.sendMessage("greenhouses.errors.no-rank"); user.sendMessage("greenhouses.errors.no-rank");
return false; return false;
} }
Greenhouses addon = ((Greenhouses)this.getAddon()); Greenhouses addon = this.getAddon();
// Remove greenhouse if it exists // Remove greenhouse if it exists
if (!addon.getManager().getMap().getGreenhouse(user.getLocation()).map(gh -> { if (!addon.getManager().getMap().getGreenhouse(user.getLocation()).map(gh -> {
user.sendMessage("general.success"); user.sendMessage("general.success");

View File

@ -30,8 +30,7 @@ public class UserCommand extends CompositeCommand {
public void setup() { public void setup() {
this.setPermission("greenhouses.player"); this.setPermission("greenhouses.player");
this.setOnlyPlayer(true); this.setOnlyPlayer(true);
this.setParametersHelp("greenhouses.command.parameters"); this.setDescription("greenhouses.commands.user.description");
this.setDescription("greenhouses.command.description");
//new InfoCommand(this); //new InfoCommand(this);
//new ListCommand(this); //new ListCommand(this);

View File

@ -0,0 +1,134 @@
package world.bentobox.greenhouses.world;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
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.World.Environment;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.Nullable;
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;
private final Greenhouses addon;
/**
* Chunk cache. This class is designed to be run async and blocks futures
* @param world - world to cache
*/
public AsyncWorldCache(Greenhouses addon, World world) {
this.world = world;
cache = new HashMap<>();
this.addon = addon;
}
/**
* @return the world's environment
*/
public Environment getEnvironment() {
return world.getEnvironment();
}
/**
* @return maximum height of this world
*/
public int getMaxHeight() {
return world.getMaxHeight();
}
/**
* 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(addon.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 or null if there's an error getting the chunk
* @throws ExecutionException - if the chunk getting throws an exception
* @throws InterruptedException - if the future is interrupted
*/
@Nullable
private ChunkSnapshot getSnap(final int x, final int z) throws InterruptedException, ExecutionException {
// 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);
}
// Block on getting the chunk because this is running async
ChunkSnapshot cs = getAChunk(key.x, key.z).get();
// 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 or Material.AIR if there is an exception
*/
public Material getBlockType(final int x, final int y, final int z) {
// Convert block coords to chunk coords
// TODO: simplify this - it must be easier than this!
int xx = x >= 0 ? x % 16 : (16 + (x % 16)) % 16;
int zz = z >= 0 ? z % 16 : (16 + (z % 16)) % 16;
try {
return Objects.requireNonNull(getSnap(x, z)).getBlockType(xx, y, zz);
} catch (InterruptedException | ExecutionException e) {
addon.logError("Chunk could not be obtained async! " + e);
// Restore interrupted state...
Thread.currentThread().interrupt();
return Material.AIR;
}
}
/**
* 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());
}
/**
* Check if block is AIR
* @param vector - vector
* @return true if AIR
*/
public boolean isEmpty(Vector vector) {
return getBlockType(vector).equals(Material.AIR);
}
}

View File

@ -35,11 +35,17 @@ biomes:
# Entity name: % chance:Block on which the mob will spawn # Entity name: % chance:Block on which the mob will spawn
mobs: mobs:
SQUID: 10:WATER SQUID: 10:WATER
GLOW_SQUID: 5:WATER
TURTLE: 10:SAND
# The minimum number of blocks each mob requires. # The minimum number of blocks each mob requires.
# Mobs will not spawn if there is more than 1 per this number of # Mobs will not spawn if there is more than 1 per this number of
# blocks in the greenhouse. e.g., in this case only 2 mobs will spawn if the # blocks in the greenhouse. e.g., in this case only 2 mobs will spawn if the
# greenhouse area is 18 blocks # greenhouse area is 18 blocks. This enables bigger greenhouses to spawn more.
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 5
Snowy_beach: Snowy_beach:
friendlyname: "Snowy beach" friendlyname: "Snowy beach"
biome: SNOWY_BEACH biome: SNOWY_BEACH
@ -49,6 +55,9 @@ biomes:
SAND: 1 SAND: 1
watercoverage: 50 watercoverage: 50
icecoverage: 10 icecoverage: 10
mobs:
SQUID: 10:WATER
GLOW_SQUID: 10:WATER
ThreeWolfMoon: ThreeWolfMoon:
friendlyname: "Three Wolf Moon Forest" friendlyname: "Three Wolf Moon Forest"
# Could do with more wolves, but the magic works with 3. # Could do with more wolves, but the magic works with 3.
@ -63,11 +72,17 @@ biomes:
plants: plants:
TALL_GRASS: 10:GRASS_BLOCK TALL_GRASS: 10:GRASS_BLOCK
mobs: mobs:
WOLF: 10:SNOW WOLF: 15:SNOW
FOX: 15:GRASS_BLOCK
RABBIT: 7:GRASS_BLOCK
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 5
Cold_Rabbit: Cold_Rabbit:
friendlyname: "Cold Taiga Forest" friendlyname: "Cold Taiga Forest"
biome: TAIGA_HILLS biome: OLD_GROWTH_SPRUCE_TAIGA
icon: SPRUCE_SAPLING icon: SPRUCE_SAPLING
priority: 20 priority: 20
contents: contents:
@ -79,7 +94,12 @@ biomes:
TALL_GRASS: 10:GRASS_BLOCK TALL_GRASS: 10:GRASS_BLOCK
mobs: mobs:
RABBIT: 10:SNOW RABBIT: 10:SNOW
FOX: 7:GRASS_BLOCK
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 20
DESERT: DESERT:
friendlyname: "Desert" friendlyname: "Desert"
biome: DESERT biome: DESERT
@ -101,6 +121,16 @@ biomes:
# Multiple conversions can be listed # Multiple conversions can be listed
conversion-list: conversion-list:
- DIRT:30:SAND:SAND - DIRT:30:SAND:SAND
- GRASS_BLOCK:30:SAND:SAND
- COARSE_DIRT:30:GRAVEL:SAND
mobs:
RABBIT: 10:SAND
HUSK: 10:SAND
moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 20
FOREST: FOREST:
friendlyname: "Flowery forest" friendlyname: "Flowery forest"
biome: FLOWER_FOREST biome: FLOWER_FOREST
@ -115,6 +145,17 @@ biomes:
ORANGE_TULIP: 2:GRASS_BLOCK ORANGE_TULIP: 2:GRASS_BLOCK
SUNFLOWER: 4:GRASS_BLOCK SUNFLOWER: 4:GRASS_BLOCK
TALL_GRASS: 20:GRASS_BLOCK TALL_GRASS: 20:GRASS_BLOCK
mobs:
SHEEP: 10:GRASS_BLOCK
CHICKEN: 7:GRASS_BLOCK
PIG: 10:GRASS_BLOCK
COW: 10:GRASS_BLOCK
WOLF: 5:GRASS_BLOCK
moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 20
NETHER: NETHER:
friendlyname: "&cNether" friendlyname: "&cNether"
biome: NETHER_WASTES biome: NETHER_WASTES
@ -132,6 +173,10 @@ biomes:
STRIDER: 10:LAVA STRIDER: 10:LAVA
ENDERMAN: 5:NETHERRACK ENDERMAN: 5:NETHERRACK
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 50
permission: greenhouses.biome.nether permission: greenhouses.biome.nether
SOUL_SAND_VALLEY: SOUL_SAND_VALLEY:
friendlyname: "&cSoul Sand Valley" friendlyname: "&cSoul Sand Valley"
@ -146,7 +191,14 @@ biomes:
watercoverage: 0 watercoverage: 0
mobs: mobs:
SKELETON: 10:SOUL_SAND SKELETON: 10:SOUL_SAND
GHAST: 10:SOUL_SAND
ENDERMAN: 1:SOUL_SAND
STRIDER: 20:LAVA
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 50
permission: greenhouses.biome.nether permission: greenhouses.biome.nether
# Conversion list - in this case, an adjacent block is required to convert # Conversion list - in this case, an adjacent block is required to convert
# Format is: # Format is:
@ -171,6 +223,10 @@ biomes:
PIGLIN: 10:CRIMSON_NYLIUM PIGLIN: 10:CRIMSON_NYLIUM
HOGLIN: 10:CRIMSON_NYLIUM HOGLIN: 10:CRIMSON_NYLIUM
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 50
permission: greenhouses.biome.nether permission: greenhouses.biome.nether
WARPED_FOREST: WARPED_FOREST:
friendlyname: "&cWarped Forest" friendlyname: "&cWarped Forest"
@ -188,6 +244,10 @@ biomes:
STRIDER: 10:LAVA STRIDER: 10:LAVA
ENDERMAN: 20:WARPED_NYLIUM ENDERMAN: 20:WARPED_NYLIUM
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 50
permission: greenhouses.biome.nether permission: greenhouses.biome.nether
JUNGLE: JUNGLE:
biome: JUNGLE biome: JUNGLE
@ -203,6 +263,11 @@ biomes:
ROSE_BUSH: 20:GRASS_BLOCK ROSE_BUSH: 20:GRASS_BLOCK
FERN: 20:GRASS_BLOCK FERN: 20:GRASS_BLOCK
TALL_GRASS: 20:GRASS_BLOCK TALL_GRASS: 20:GRASS_BLOCK
COCOA: 10:JUNGLE_LOG
mobs:
PARROT: 30:GRASS_BLOCK
CHICKEN: 20:GRASS_BLOCK
PANDA: 1:GRASS_BLOCK
MUSHROOM_FIELDS: MUSHROOM_FIELDS:
friendlyname: "Mushroom Fields" friendlyname: "Mushroom Fields"
biome: MUSHROOM_FIELDS biome: MUSHROOM_FIELDS
@ -218,6 +283,10 @@ biomes:
mobs: mobs:
MUSHROOM_COW: 10:MYCELIUM MUSHROOM_COW: 10:MYCELIUM
moblimit: 9 moblimit: 9
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 20
OCEAN: OCEAN:
biome: OCEAN biome: OCEAN
icon: WATER_BUCKET icon: WATER_BUCKET
@ -226,7 +295,13 @@ biomes:
watercoverage: 95 watercoverage: 95
mobs: mobs:
SQUID: 10:WATER SQUID: 10:WATER
DROWNED: 1:WATER
COD: 40:WATER
DOLPHIN: 20:WATER
SQUID: 20:WATER
GLOW_SQUID: 10:WATER
moblimit: 9 moblimit: 9
maxmobs: 20
PLAINS: PLAINS:
friendlyname: "Horse Plains" friendlyname: "Horse Plains"
biome: PLAINS biome: PLAINS
@ -237,8 +312,17 @@ biomes:
plants: plants:
TALL_GRASS: 10:GRASS_BLOCK TALL_GRASS: 10:GRASS_BLOCK
mobs: mobs:
HORSE: 10:GRASS_BLOCK HORSE: 18:GRASS_BLOCK
DONKEY: 2:GRASS_BLOCK
COW: 20:GRASS_BLOCK
CHICKEN: 25:GRASS_BLOCK
PIG: 25:GRASS_BLOCK
SHEEP: 25:GRASS_BLOCK
moblimit: 1 moblimit: 1
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 10
RIVER: RIVER:
friendlyname: "Clay river" friendlyname: "Clay river"
biome: RIVER biome: RIVER
@ -254,6 +338,15 @@ biomes:
# So, for below, dirt has a 50% chance of changing into clay if it is next to water! # So, for below, dirt has a 50% chance of changing into clay if it is next to water!
conversion-list: conversion-list:
- DIRT:50:CLAY:WATER - DIRT:50:CLAY:WATER
mobs:
SALMON: 10:WATER
SQUID: 10:WATER
GLOW_SQUID: 5:WATER
moblimit: 1
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 10
SAVANNA: SAVANNA:
biome: SAVANNA biome: SAVANNA
icon: ACACIA_LEAVES icon: ACACIA_LEAVES
@ -264,6 +357,18 @@ biomes:
GRASS_BLOCK: 4 GRASS_BLOCK: 4
plants: plants:
TALL_GRASS: 10:GRASS_BLOCK TALL_GRASS: 10:GRASS_BLOCK
mobs:
HORSE: 2:GRASS_BLOCK
DONKEY: 2:GRASS_BLOCK
COW: 20:GRASS_BLOCK
CHICKEN: 25:GRASS_BLOCK
PIG: 25:GRASS_BLOCK
SHEEP: 25:GRASS_BLOCK
moblimit: 1
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 10
SWAMP: SWAMP:
friendlyname: "&2Slimy Swamp" friendlyname: "&2Slimy Swamp"
biome: SWAMP biome: SWAMP
@ -278,7 +383,37 @@ biomes:
plants: plants:
RED_MUSHROOM: 20:GRASS_BLOCK RED_MUSHROOM: 20:GRASS_BLOCK
BROWN_MUSHROOM: 20:GRASS_BLOCK BROWN_MUSHROOM: 20:GRASS_BLOCK
BLUE_ORCHID: 10:GRASS_BLOCK
LILY_PAD: 5:WATER LILY_PAD: 5:WATER
mobs: mobs:
SLIME: 5:WATER SLIME: 5:WATER
FROG: 20:WATER
moblimit: 3 moblimit: 3
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 10
dripstone_caves:
friendlyname: "&6Drippy Drops"
biome: dripstone_caves
icon: DRIPSTONE_BLOCK
priority: 15
contents:
STONE: 8
CLAY: 8
# 50% water coverage required
watercoverage: 25
conversions:
CLAY: 50:DRIPSTONE_BLOCK:WATER
STONE: 0.005:COPPER_ORE:STONE
plants:
GLOW_LICHEN: 20:STONE
mobs:
skeleton: 5:STONE
glow_squid: 5:WATER
BAT: 10:STONE
moblimit: 5
# Maxmobs - this is the maximum number of greenhouse-spawed mobs allowed in
# the greenhouse at once. Spawning will stop when this limit is reached.
# If this value is not given, there is no maximum.
maxmobs: 25

View File

@ -1,4 +1,4 @@
# Greenhouses Configuration # Greenhouses Configuration {$version}
# #
greenhouses: greenhouses:
# BentoBox GameModes that will use Greenhouses # BentoBox GameModes that will use Greenhouses
@ -7,9 +7,10 @@ greenhouses:
- AcidIsland - AcidIsland
- SkyGrid - SkyGrid
- AOneBlock - AOneBlock
- CaveBlock #
# Show loaded recipe details during startup of server # Show loaded recipe details during startup of server
startup-log: false startup-log: false
#
# Weather and ecosystem settings # Weather and ecosystem settings
# How often it should snow in the g/h when the weather is raining, in seconds # How often it should snow in the g/h when the weather is raining, in seconds
snowspeed: 30.0 snowspeed: 30.0
@ -18,6 +19,7 @@ greenhouses:
snowchance: 1.0 snowchance: 1.0
# How many blocks should get snow 1 = all of them, 0 = none, 0.1 = 1 in 10 # How many blocks should get snow 1 = all of them, 0 = none, 0.1 = 1 in 10
snowdensity: 0.1 snowdensity: 0.1
#
# Biome activity # Biome activity
# How often should greenhouse biomes be checked to make sure they are still valid # How often should greenhouse biomes be checked to make sure they are still valid
ecotick: 5 ecotick: 5
@ -28,10 +30,15 @@ greenhouses:
blocktick: 2 blocktick: 2
# How often should mobs be potentially spawned in a greenhouse, in minutes # How often should mobs be potentially spawned in a greenhouse, in minutes
mobtick: 5 mobtick: 5
#
# Default settings for greenhouse actions # Default settings for greenhouse actions
# Allow lava or water to flow out of a greenhouse, e.g. through the door, floor # Allow lava or water to flow out of a greenhouse, e.g. through the door, floor
allowflowout: false allowflowout: false
# Allow lava or water to flow into a greenhouse, e.g., through the door # Allow lava or water to flow into a greenhouse, e.g., through the door
allowflowin: false allowflowin: false
#
# Allow glowstone to be used as well as glass in roof and walls # Allow glowstone to be used as well as glass in roof and walls
allowglowstone: true allowglowstone: true
#
# Allow glass panes to be used to build greenhouses
allowpanes: true

View File

@ -0,0 +1,163 @@
---
protection:
flags:
GREENHOUSE:
name: Gewächshäuser
description: |-
&bUmschalten, wer
&bdie Gewächshäuser kontrollieren kann
greenhouses:
general:
greenhouses: Gewächshäuser
errors:
move: Gehe zuerst in ein Gewächshaus, das dir gehört.
no-rank: "& cDu hast keinen Rang, um das zu tun."
notyours: Das ist nicht dein Gewächshaus!
not-inside: "& cDu bist nicht in einem Gewächshaus!"
tooexpensive: Du kannst dir das nicht leisten [price]
alreadyexists: Gewächshaus existiert bereits!
norecipe: Kann kein Gewächshaus bauen!
event:
broke: Du hast das Gewächshaus kaputt gemacht! Biome auf [biome] umstellen!
entering: Betreten des Gewächshauses [biome]
leaving: Das Gewächshaus verlassen [biome]
recipe:
blockscolor: "&f"
title: "[[biome] recipe]"
watermustbe: Wasser > [coverage]% der Bodenfläche.
icemustbe: Eisblöcke > [coverage]% der Bodenfläche.
lavamustbe: Lava > [coverage]% der Bodenfläche.
minimumblockstitle: "[Minimum blocks required]"
nootherblocks: Keine weiteren Blöcke erforderlich.
missing: Gewächshaus fehlt
commands:
user:
remove:
description: Entfernt ein Gewächshaus, in dem Sie stehen, wenn Sie der Eigentümer
sind
make:
description: Versuche ein Gewächshaus zu bauen
parameters: "<recipe>"
error:
already: "&cEs gibt hier schon ein Gewächshaus!"
FAIL_BAD_ROOF_BLOCKS: "&cDach enthält unzulässige Blöcke!"
FAIL_BAD_WALL_BLOCKS: "&cWand enthält unzulässige Blöcke!"
FAIL_BELOW: "&cDu musst im Gewächshaus sein, um es zu versuchen."
FAIL_BLOCKS_ABOVE: "&cEs dürfen keine Blöcke über dem Gewächshaus sein!
Rote Glasblöcke sollten die Problemblöcke anzeigen."
FAIL_HOLE_IN_ROOF: "&cIn dem Dach ist ein Loch oder es ist nicht flach!
Rote Glasblöcke sollten das Problem anzeigen."
FAIL_HOLE_IN_WALL: "&cIn der Wand ist ein Loch!"
FAIL_NO_ROOF: "&cEs scheint kein Dach zu geben!"
FAIL_TOO_MANY_DOORS: "&cEs dürfen nicht mehr als 4 Türen im Gewächshaus
sein!"
FAIL_TOO_MANY_HOPPERS: "&cIn den Wänden oder im Dach ist nur ein Trichter
zulässig."
FAIL_UNEVEN_WALLS: "&cDie Wände sind uneben. Rote Glasblöcke sollten die
Problemblöcke anzeigen."
FAIL_INSUFFICIENT_ICE: "&cZu wenig Eis für dieses Rezept"
FAIL_INSUFFICIENT_LAVA: "&cZu wenig Lava für dieses Rezept"
FAIL_INSUFFICIENT_WATER: "&cZu wenig Wasser für dieses Rezept"
FAIL_NO_ICE: "&cFür dieses Rezept wird Eis benötigt"
FAIL_NO_LAVA: "&cFür dieses Rezept wird Lava benötigt"
FAIL_NO_WATER: "&cFür dieses Rezept wird Wasser benötigt"
FAIL_NO_RECIPE_FOUND: "&c Zu diesem Gewächshaus konnte kein passendes Rezept
gefunden werden"
FAIL_INSUFFICIENT_BLOCKS: "&cWeitere Blöcke sind erforderlich, um dieses
Rezept zu erstellen!"
FAIL_OVERLAPPING: "&cGewächshäuser können sich keine Wände teilen, sorry."
success: "&2Du hast erfolgreich ein [biome] Biom Gewächshaus gebaut! Beim
nächsten Teleport oder Login wird Biom synchronisiert."
missing-blocks: "[material] x [number] &cFehlt"
unknown-recipe: "&Unbekanntes Rezept"
try-these: "&cVersuche einen von diesen:"
recipe-format: "&3[name]"
info:
title: "&A[Wie man ein Gewächshaus baut]"
instructions: "&EStelle einen Kasten aus Glas mit 4 Wänden und einem flachen
&EGlasdach her und füge bis zu &F4 Türen &Ein den Wänden hinzu. &EPlatziere
&F1 Trichter &Ein eine Wand oder ein Dach und füge Wassereimer hinzu. &EUm
Schnee und/oder Knochenmehl herzustellen, um Pflanzen automatisch wachsen
zu lassen. &EPrüfe die Biom-Rezepte, welche Blöcke in einem Gewächshaus
sein müssen, um ein &EGewächshaus erfolgreich zu machen."
help:
help: Hilfe
make: Versucht ein Gewächshaus zu bauen
remove: Entfernt ein Gewächshaus, in dem du stehst, wenn du der Besitzer bist
info: Wie man ein Gewächshaus baut
list: Listet alle Gewächshausbiome auf, die hergestellt werden können
recipe: Erklärt dir, wie man das Gewächshaus biome macht
opengui: Öffnet die Gewächshaus-GUI
list:
title: "[Gewächshaus-Biom-Rezepte]"
info: Benutze /greenhouse recipe <number> um Details über die Herstellung jedes
Gewächshauses zu sehen
error:
greenhouseProtected: Gewächshaus geschützt
move: Gehe zuerst in ein Gewächshaus, das dir gehört.
notowner: Du musst der Besitzer dieses Gewächshauses sein, um das zu tun.
removing: Gewächshaus entfernen!
notyours: Das ist nicht dein Gewächshaus!
notinside: Du bist nicht in einem Gewächshaus!
tooexpensive: du kannst dir das nicht leisten [price]
alreadyexists: Gewächshaus existiert bereits!
norecipe: Kann kein Gewächshaus bauen!
messages:
enter: Betreten des [biome] Gewächshauses von [owner]!
leave: Verlässt jetzt das Gewächshaus von [owner].
youarein: Du bist jetzt in [owner]'s [biome] Gewächshaus!
removed: Dieses Gewächshaus ist nicht mehr ...
removedmessage: Ein [biome] Gewächshaus von dir ist nicht mehr!
ecolost: Dein Gewächshaus in [location] hat sein Ökosystem verloren und wurde entfernt.
info:
title: "&A [Gewächshaus bauen]"
instructions: "&EStelle einen Kasten aus Glas mit 4 Wänden und einem flachen &EGlasdach
her und füge bis zu &F4 Türen &Ein den Wänden hinzu. &EPlatziere &F1 Trichter
&Ein eine Wand oder ein Dach und füge Wassereimer hinzu. &EUm Schnee und/oder
Knochenmehl herzustellen, um Pflanzen automatisch wachsen zu lassen. &EPrüfe die
\ Biom-Rezepte, welche Blöcke in einem Gewächshaus sein müssen, um ein &EGewächshaus
erfolgreich zu machen."
info: "[Gewächshaus Info]"
none: Keiner
nomore: "&4Du kannst keine Gewächshäuser mehr bauen!"
onemore: "&6Du kannst noch ein weiteres Gewächshaus bauen."
youcanbuild: "&ADu kannst bis zu [number] mehr Gewächshäuser bauen!"
unlimited: "&ADu kannst eine unbegrenzte Anzahl von Gewächshäusern bauen!"
welcome: "&BHerzlich willkommen! Klicken Sie hier für Anweisungen"
recipe:
blockscolor: "&f"
hint: benutze /greenhouse list, um eine Liste von Rezeptnummern zu sehen!
wrongnumber: Die Rezeptnummer muss zwischen 1 und [size] liegen
title: "[[biome] recipe]"
nowater: Kein Wasser erlaubt.
noice: Kein Eis erlaubt.
nolava: Keine Lava erlaubt.
watermustbe: Wasser > [coverage]% der Bodenfläche.
icemustbe: Eisblöcke > [coverage]% der Bodenfläche.
lavamustbe: Lava > [coverage]% der Bodenfläche.
minimumblockstitle: "[Minimum blocks required]"
nootherblocks: Keine weiteren Blöcke erforderlich.
missing: Gewächshaus fehlt
event:
broke: Du hast das Gewächshaus kaputt gemacht! Biome auf [biome] umstellen!
fix: repariere das Gewächshaus und baue es dann wieder auf
cannotplace: Blöcke können nicht über einem Gewächshaus platziert werden!
pistonerror: Kolben können keine Blöcke über ein Gewächshaus schieben!
limits:
noneallowed: Die Berechtigungen erlauben dir keine Gewächshäuser, deshalb wurden
[nummer] entfernt.
limitedto: Die Berechtigungen beschränken dich auf [limit] Gewächshäuser, so dass
[number] entfernt wurden.
adminHelp:
reload: Konfiguration aus Datei neu laden.
info: Liefert Informationen über das Gewächshaus, in dem du dich befindest
reload:
configReloaded: Konfiguration aus Datei neu geladen.
admininfo:
error: Gewächshaus-Info nur im Spiel verfügbar
error2: Versetzt dich in ein Gewächshaus, um Infos zu sehen.
flags: "[Greenhouse Flags]"
news:
headline: "[Greenhouse News]"
controlpanel:
title: "&AGewächshäuser"

View File

@ -8,8 +8,8 @@ protection:
GREENHOUSE: GREENHOUSE:
name: Greenhouses name: Greenhouses
description: | description: |
&bToggle who can &b Toggle who can
&bcontrol greenhouses &b control greenhouses
greenhouses: greenhouses:
general: general:
@ -17,9 +17,9 @@ greenhouses:
errors: errors:
move: "Move to a greenhouse you own first." move: "Move to a greenhouse you own first."
no-rank: "&cYou do not have rank to do that." no-rank: "&c You do not have rank to do that."
notyours: "This is not your greenhouse!" notyours: "This is not your greenhouse!"
not-inside: "&cYou are not in a greenhouse!" not-inside: "&c You are not in a greenhouse!"
tooexpensive: "You cannot afford [price]" tooexpensive: "You cannot afford [price]"
alreadyexists: "Greenhouse already exists!" alreadyexists: "Greenhouse already exists!"
norecipe: "Cannot make a greenhouse!" norecipe: "Cannot make a greenhouse!"
@ -38,9 +38,10 @@ greenhouses:
minimumblockstitle: "[Minimum blocks required]" minimumblockstitle: "[Minimum blocks required]"
nootherblocks: "No other blocks required." nootherblocks: "No other blocks required."
missing: "Greenhouse is missing" missing: "Greenhouse is missing"
commands: commands:
user: user:
description: "Opens the Greenhouse selection GUI"
remove: remove:
description: "Removes a greenhouse that you are standing in if you are the owner" description: "Removes a greenhouse that you are standing in if you are the owner"
make: make:
@ -48,38 +49,42 @@ greenhouses:
parameters: "<recipe>" parameters: "<recipe>"
error: error:
already: "&cThere is already a greenhouse here!" already: "&cThere is already a greenhouse here!"
FAIL_BAD_ROOF_BLOCKS: "&cRoof contains disallowed blocks!" FAIL_BAD_ROOF_BLOCKS: "&c Roof contains disallowed blocks!"
FAIL_BAD_WALL_BLOCKS: "&cWall contains disallowed blocks!" FAIL_BAD_WALL_BLOCKS: "&c Wall contains disallowed blocks!"
FAIL_BELOW: "&cYou must be inside the greenhouse to try to make it" FAIL_BELOW: "&c You must be inside the greenhouse to try to make it"
FAIL_BLOCKS_ABOVE: "&cThere can be no blocks above the greenhouse! Red glass blocks should show the problem blocks." FAIL_BLOCKS_ABOVE: "&c There can be no blocks above the greenhouse! Red glass blocks should show the problem blocks."
FAIL_HOLE_IN_ROOF: "&cThere is a hole in the roof or it is not flat! Red glass blocks should show the problem." FAIL_HOLE_IN_ROOF: |
FAIL_HOLE_IN_WALL: "&cThere is a hole in the wall!" &c There is a hole in the roof or it is not flat!
FAIL_NO_ROOF: "&cThere seems to be no roof!" &c Red glass blocks should show the problem.
FAIL_TOO_MANY_DOORS: "&cYou cannot have more than 4 doors in the greenhouse!" &c Make sure you are inside your greenhouse to make it.
FAIL_TOO_MANY_HOPPERS: "&cOnly one hopper is allowed in the walls or roof." FAIL_HOLE_IN_WALL: "&c There is a hole in the wall!"
FAIL_UNEVEN_WALLS: "&cThe walls are uneven. Red glass blocks should show the problem blocks." FAIL_NO_ROOF: "&c There seems to be no roof! Make sure you are inside the greenhouse to make it."
FAIL_INSUFFICIENT_ICE: "&cInsufficent ice to make this recipe" FAIL_TOO_MANY_DOORS: "&c You cannot have more than 4 doors in the greenhouse!"
FAIL_INSUFFICIENT_LAVA: "&cInsufficent lava to make this recipe" FAIL_TOO_MANY_HOPPERS: "&c Only one hopper is allowed in the walls or roof."
FAIL_INSUFFICIENT_WATER: "&cInsufficent water to make this recipe" FAIL_UNEVEN_WALLS: "&c The walls are uneven. Red glass blocks should show the problem blocks."
FAIL_NO_ICE: "&cIce is required to make this recipe" FAIL_INSUFFICIENT_ICE: "&c Insufficient ice to make this recipe"
FAIL_NO_LAVA: "&cLava is required to make this recipe" FAIL_INSUFFICIENT_LAVA: "&c Insufficient lava to make this recipe"
FAIL_NO_WATER: "&cWater is required to make this recipe" FAIL_INSUFFICIENT_WATER: "&c Insufficient water to make this recipe"
FAIL_INSUFFICIENT_BLOCKS: "&cMore blocks are required to make this recipe!" FAIL_NO_ICE: "&c Ice is required to make this recipe"
FAIL_OVERLAPPING: "&cGreenhouses cannot share walls, sorry." FAIL_NO_LAVA: "&c Lava is required to make this recipe"
success: "&2You successfully made a [biome] biome greenhouse! Biome will sync at next teleport or login." FAIL_NO_WATER: "&c Water is required to make this recipe"
missing-blocks: "&cMissing [material] x [number]" FAIL_NO_RECIPE_FOUND: "&c No recipe could be found that matches this greenhouse"
unknown-recipe: "&cUnknown recipe" FAIL_INSUFFICIENT_BLOCKS: "&c More blocks are required to make this recipe!"
try-these: "&cTry one of these:" FAIL_OVERLAPPING: "&c Greenhouses cannot share walls, sorry."
success: "&2 You successfully made a [biome] biome greenhouse! Biome will sync at next teleport or login."
missing-blocks: "&c Missing [material] x [number]"
unknown-recipe: "&c Unknown recipe"
try-these: "&c Try one of these:"
recipe-format: "&3[name]" recipe-format: "&3[name]"
info: info:
title: "&A[How To Build A Greenhouse]" title: "&a [How To Build A Greenhouse]"
instructions: | instructions: |
&EMake a box out of out of glass with 4 walls and a flat glass &e Make a box out of out of glass with 4 walls and a flat glass
&Eroof and add up to &F4 doors &Ein the walls. &e roof and add up to &f 4 doors &Ein the walls.
&EPlace &F1 hopper &Ein a wall or roof and add water buckets. &e Place &f 1 hopper &e in a wall or roof and add water buckets.
&Eto make snow and/or bonemeal to grow plants automatically. &e to make snow and/or bonemeal to grow plants automatically.
&ECheck the biome recipes for what blocks must be inside a &e Check the biome recipes for what blocks must be inside a
&Egreenhouse to make one successfully. &e greenhouse to make one successfully.
######### Old locale for reference ######### Old locale for reference
@ -122,19 +127,19 @@ messages:
info: info:
title: "&A[How To Build A Greenhouse]" title: "&A[How To Build A Greenhouse]"
instructions: | instructions: |
&EMake a box out of out of glass with 4 walls and a flat glass &E Make a box out of out of glass with 4 walls and a flat glass
&Eroof and add up to &F4 doors &Ein the walls. &E roof and add up to &F 4 doors &Ein the walls.
&EPlace &F1 hopper &Ein a wall or roof and add water buckets. &E Place &F 1 hopper &E in a wall or roof and add water buckets.
&Eto make snow and/or bonemeal to grow plants automatically. &E to make snow and/or bonemeal to grow plants automatically.
&ECheck the biome recipes for what blocks must be inside a &E Check the biome recipes for what blocks must be inside a
&Egreenhouse to make one successfully. &E greenhouse to make one successfully.
info: "[Greenhouse Info]" info: "[Greenhouse Info]"
none: "None" none: "None"
nomore: "&4You cannot build any more greenhouses!" nomore: "&4 You cannot build any more greenhouses!"
onemore: "&6You can build one more greenhouse." onemore: "&6 You can build one more greenhouse."
youcanbuild: "&AYou can build up to [number] more greenhouses!" youcanbuild: "&A You can build up to [number] more greenhouses!"
unlimited: "&AYou can build an unlimited number of greenhouses!" unlimited: "&A You can build an unlimited number of greenhouses!"
welcome: "&BWelcome! Click here for instructions" welcome: "&B Welcome! Click here for instructions"
recipe: recipe:
blockscolor: "&f" blockscolor: "&f"

View File

@ -1,189 +1,153 @@
########################################################################################### ---
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
# If this file is deleted, then it will be recreate at the next reload. #
###########################################################################################
protection: protection:
flags: flags:
GREENHOUSE: GREENHOUSE:
name: Greenhouses name: Greenhouses
description: | description: "&bÁllítsd be, hogy ki\n&bkezelheti az üvegházat \n"
&bÁllítsd be, hogy ki
&bkezelheti az üvegházat
greenhouses: greenhouses:
general: general:
greenhouses: "Üvegházak" greenhouses: Üvegházak
errors: errors:
move: "Menj a saját üvegházadhoz." move: Menj a saját üvegházadhoz.
no-rank: "&cNincs rangod ehhez." no-rank: "&cNincs rangod ehhez."
notyours: "Ez nem a te üvegházad!" notyours: Ez nem a te üvegházad!
not-inside: "&cNem vagy üvegházban!" not-inside: "&cNem vagy üvegházban!"
tooexpensive: "Ehhez nincs elég pénzed, ennyi szükséges: [price]" tooexpensive: 'Ehhez nincs elég pénzed, ennyi szükséges: [price]'
alreadyexists: "Az üvegház már létezik!" alreadyexists: Az üvegház már létezik!
norecipe: "Nem tudsz üvegházat csinálni!" norecipe: Nem tudsz üvegházat csinálni!
event: event:
broke: "Összetörted az üvegházad! Visszaállítjuk a biome-ot [biome]-ra/re!" broke: Összetörted az üvegházad! Visszaállítjuk a biome-ot [biome]-ra/re!
entering: "Belépés a(z) [biome] üvegházba." entering: Belépés a(z) [biome] üvegházba.
leaving: "Kilépés a(z) [biome] üvegházból." leaving: Kilépés a(z) [biome] üvegházból.
recipe: recipe:
blockscolor: "&f" blockscolor: "&f"
title: "[[biome] recept]" title: "[[biome] recept]"
watermustbe: "Víz > [coverage]% kell, hogy legyen az alapterületen." watermustbe: Víz > [coverage]% kell, hogy legyen az alapterületen.
icemustbe: "Jég blokk > [coverage]% kell, hogy legyen az alapterületen." icemustbe: Jég blokk > [coverage]% kell, hogy legyen az alapterületen.
lavamustbe: "Láva > [coverage]% kell, hogy legyen az alapterületen." lavamustbe: Láva > [coverage]% kell, hogy legyen az alapterületen.
minimumblockstitle: "[Minimum blokkmennyiség]" minimumblockstitle: "[Minimum blokkmennyiség]"
nootherblocks: "Nem szükséges több blokk." nootherblocks: Nem szükséges több blokk.
missing: "Hiányzó üvegház." missing: Hiányzó üvegház.
commands: commands:
user: user:
remove: remove:
description: "Ha az üvegházadban állsz és te vagy a tulaja, akkor törli azt." description: Ha az üvegházadban állsz és te vagy a tulaja, akkor törli azt.
make: make:
description: "Megpróbál készíteni egy üvegházat." description: Megpróbál készíteni egy üvegházat.
parameters: "<recipe>" parameters: "<recipe>"
error: error:
already: "Az üvegház már létezik!" already: Az üvegház már létezik!
FAIL_BAD_ROOF_BLOCKS: "&cA tető nem engedélyezett blokkokat tartalmaz!" FAIL_BAD_ROOF_BLOCKS: "&cA tető nem engedélyezett blokkokat tartalmaz!"
FAIL_BAD_WALL_BLOCKS: "&cA fal nem engedélyezett blokkokat tartalmaz!" FAIL_BAD_WALL_BLOCKS: "&cA fal nem engedélyezett blokkokat tartalmaz!"
FAIL_BELOW: "&cAz üvegházban kell lenned, hogy megpróbáld elkészíteni" FAIL_BELOW: "&cAz üvegházban kell lenned, hogy megpróbáld elkészíteni"
FAIL_BLOCKS_ABOVE: "&cAz üvegház felett nem lehetnek blokkok! A piros üvegblokkoknak meg kell mutatniuk a problémás blokkokat." FAIL_BLOCKS_ABOVE: "&cAz üvegház felett nem lehetnek blokkok! A piros üvegblokkoknak
FAIL_HOLE_IN_ROOF: "&cVan egy lyuk a tetőn, vagy nem sík! A piros üvegblokkoknak meg kell mutatniuk a problémát." meg kell mutatniuk a problémás blokkokat."
FAIL_HOLE_IN_ROOF: "&cVan egy lyuk a tetőn, vagy nem sík! A piros üvegblokkoknak
meg kell mutatniuk a problémát."
FAIL_HOLE_IN_WALL: "&cVan egy lyuk a falban!" FAIL_HOLE_IN_WALL: "&cVan egy lyuk a falban!"
FAIL_NO_ROOF: "&cÚgy tűnik, nincs tető!" FAIL_NO_ROOF: "&cÚgy tűnik, nincs tető!"
FAIL_TOO_MANY_DOORS: "&cAz üvegházban nem lehet négynél több ajtó!" FAIL_TOO_MANY_DOORS: "&cAz üvegházban nem lehet négynél több ajtó!"
FAIL_TOO_MANY_HOPPERS: "&cCsak egy tölcsér megengedett a falakban vagy a tetőben." FAIL_TOO_MANY_HOPPERS: "&cCsak egy tölcsér megengedett a falakban vagy a
FAIL_UNEVEN_WALLS: "&cA falak egyenetlenek. A piros üvegblokkoknak meg kell mutatniuk a problémás blokkokat." tetőben."
FAIL_UNEVEN_WALLS: "&cA falak egyenetlenek. A piros üvegblokkoknak meg kell
mutatniuk a problémás blokkokat."
FAIL_INSUFFICIENT_ICE: "&cNem elegendő a jég ehhez a recept elkészítéséhez." FAIL_INSUFFICIENT_ICE: "&cNem elegendő a jég ehhez a recept elkészítéséhez."
FAIL_INSUFFICIENT_LAVA: "&cNem elegendő a láva ehhez a recept elkészítéséhez." FAIL_INSUFFICIENT_LAVA: "&cNem elegendő a láva ehhez a recept elkészítéséhez."
FAIL_INSUFFICIENT_WATER: "&cNem elegendő a víz ehhez a recept elkészítéséhez." FAIL_INSUFFICIENT_WATER: "&cNem elegendő a víz ehhez a recept elkészítéséhez."
FAIL_NO_ICE: "&cA jég szükséges ehhez a recepthez." FAIL_NO_ICE: "&cA jég szükséges ehhez a recepthez."
FAIL_NO_LAVA: "&cA láva szükséges ehhez a recepthez." FAIL_NO_LAVA: "&cA láva szükséges ehhez a recepthez."
FAIL_NO_WATER: "&cA víz szükséges ehhez a recepthez." FAIL_NO_WATER: "&cA víz szükséges ehhez a recepthez."
FAIL_NO_RECIPE_FOUND: "&c Nem található ilyen recept ebben a melegházban."
FAIL_INSUFFICIENT_BLOCKS: "&cTovábbi blokkokra van szükség a recept elkészítéséhez!" FAIL_INSUFFICIENT_BLOCKS: "&cTovábbi blokkokra van szükség a recept elkészítéséhez!"
FAIL_OVERLAPPING: "&cAz üvegházak nem oszthatják meg a falakat, bocs." FAIL_OVERLAPPING: "&cAz üvegházak nem oszthatják meg a falakat, bocs."
success: "Sikeresen elkészítettél egy [biome] üvegházat! Az éghajlat szinkronizálódik a következő teleportálásnál, vagy belépésnél." success: Sikeresen elkészítettél egy [biome] üvegházat! Az éghajlat szinkronizálódik
a következő teleportálásnál, vagy belépésnél.
missing-blocks: "&cHiányzik [material] x [number]" missing-blocks: "&cHiányzik [material] x [number]"
unknown-recipe: "&cIsmeretlen recept" unknown-recipe: "&cIsmeretlen recept"
try-these: "&cPróbálj meg egyet az alábbiak közül:" try-these: "&cPróbálj meg egyet az alábbiak közül:"
recipe-format: "&3[name]" recipe-format: "&3[name]"
info: info:
title: "&A[Hogyan Készíts Üvegházat]" title: "&A[Hogyan Készíts Üvegházat]"
instructions: | instructions: "&EKészíts egy üvegdobozt 4 fallal, egy üvegtetővel,\n&Eés akár
&EKészíts egy üvegdobozt 4 fallal, egy üvegtetővel, 4 ajtót is tehetsz a falba.\n&ERakj a falba vagy a tetőbe &F1 tölcsért,
&Eés akár 4 ajtót is tehetsz a falba. &Eés helyezz bele vizes vödröket,\n&Ehogy havat és/vagy csontlisztet készíts,
&ERakj a falba vagy a tetőbe &F1 tölcsért, &Eés helyezz bele vizes vödröket, mellyel automatikusan tudod növeszteni a növényeidet.\n&ENézd meg az éghajlatokhoz
&Ehogy havat és/vagy csontlisztet készíts, mellyel automatikusan tudod növeszteni a növényeidet. tartozó recepteket, hogy megtudd milyen blokkok legyenek feltétlenül\n&Eaz
&ENézd meg az éghajlatokhoz tartozó recepteket, hogy megtudd milyen blokkok legyenek feltétlenül üvegházban ahhoz, hogy sikeresen elkészítsd. \n"
&Eaz üvegházban ahhoz, hogy sikeresen elkészítsd.
######### Old locale for reference
help: help:
help: "help" help: help
make: "Tries to make a greenhouse" make: Tries to make a greenhouse
remove: "Removes a greenhouse that you are standing in if you are the owner" remove: Removes a greenhouse that you are standing in if you are the owner
info: "How to make a greenhouse" info: How to make a greenhouse
list: "Lists all the greenhouse biomes that can be made" list: Lists all the greenhouse biomes that can be made
recipe: "Tells you how to make greenhouse biome" recipe: Tells you how to make greenhouse biome
opengui: "Opens the Greenhouse GUI" opengui: Opens the Greenhouse GUI
list: list:
title: "[Greenhouse Biome Recipes]" title: "[Greenhouse Biome Recipes]"
info: "Use /greenhouse recipe <number> to see details on how to make each greenhouse" info: Use /greenhouse recipe <number> to see details on how to make each greenhouse
################
#General Errors#
################
error: error:
greenhouseProtected: "Greenhouse protected" greenhouseProtected: Greenhouse protected
move: "Move to a greenhouse you own first." move: Move to a greenhouse you own first.
notowner: "You must be the owner of this greenhouse to do that." notowner: You must be the owner of this greenhouse to do that.
removing: "Removing greenhouse!" removing: Removing greenhouse!
notyours: "This is not your greenhouse!" notyours: This is not your greenhouse!
notinside: "You are not in a greenhouse!" notinside: You are not in a greenhouse!
tooexpensive: "You cannot afford [price]" tooexpensive: You cannot afford [price]
alreadyexists: "Greenhouse already exists!" alreadyexists: Greenhouse already exists!
norecipe: "Cannot make a greenhouse!" norecipe: Cannot make a greenhouse!
messages: messages:
enter: "Entering [owner]'s [biome] greenhouse!" enter: Entering [owner]'s [biome] greenhouse!
leave: "Now leaving [owner]'s greenhouse." leave: Now leaving [owner]'s greenhouse.
youarein: "You are now in [owner]'s [biome] greenhouse!" youarein: You are now in [owner]'s [biome] greenhouse!
removed: "This greenhouse is no more..." removed: This greenhouse is no more...
removedmessage: "A [biome] greenhouse of yours is no more!" removedmessage: A [biome] greenhouse of yours is no more!
ecolost: "Your greenhouse at [location] lost its eco system and was removed." ecolost: Your greenhouse at [location] lost its eco system and was removed.
info: info:
title: "&A[How To Build A Greenhouse]" title: "&A[How To Build A Greenhouse]"
instructions: | instructions: "&EMake a box out of out of glass with 4 walls and a flat glass\n&Eroof
&EMake a box out of out of glass with 4 walls and a flat glass and add up to &F4 doors &Ein the walls.\n&EPlace &F1 hopper &Ein a wall or roof
&Eroof and add up to &F4 doors &Ein the walls. and add water buckets.\n&Eto make snow and/or bonemeal to grow plants automatically.\n&ECheck
&EPlace &F1 hopper &Ein a wall or roof and add water buckets. the biome recipes for what blocks must be inside a\n&Egreenhouse to make one successfully.
&Eto make snow and/or bonemeal to grow plants automatically. \n"
&ECheck the biome recipes for what blocks must be inside a info: "[Greenhouse Info]"
&Egreenhouse to make one successfully. none: None
info: "[Greenhouse Info]" nomore: "&4You cannot build any more greenhouses!"
none: "None" onemore: "&6You can build one more greenhouse."
nomore: "&4You cannot build any more greenhouses!" youcanbuild: "&AYou can build up to [number] more greenhouses!"
onemore: "&6You can build one more greenhouse." unlimited: "&AYou can build an unlimited number of greenhouses!"
youcanbuild: "&AYou can build up to [number] more greenhouses!" welcome: "&BWelcome! Click here for instructions"
unlimited: "&AYou can build an unlimited number of greenhouses!"
welcome: "&BWelcome! Click here for instructions"
recipe: recipe:
blockscolor: "&f" blockscolor: "&f"
hint: "Use /greenhouse list to see a list of recipe numbers!" hint: Use /greenhouse list to see a list of recipe numbers!
wrongnumber: "Recipe number must be between 1 and [size]" wrongnumber: Recipe number must be between 1 and [size]
title: "[[biome] recipe]" title: "[[biome] recipe]"
nowater: "No water allowed." nowater: No water allowed.
noice: "No ice allowed." noice: No ice allowed.
nolava: "No lava allowed." nolava: No lava allowed.
watermustbe: "Water > [coverage]% of floor area." watermustbe: Water > [coverage]% of floor area.
icemustbe: "Ice blocks > [coverage]% of floor area." icemustbe: Ice blocks > [coverage]% of floor area.
lavamustbe: "Lava > [coverage]% of floor area." lavamustbe: Lava > [coverage]% of floor area.
minimumblockstitle: "[Minimum blocks required]" minimumblockstitle: "[Minimum blocks required]"
nootherblocks: "No other blocks required." nootherblocks: No other blocks required.
missing: "Greenhouse is missing" missing: Greenhouse is missing
event: event:
broke: "You broke this greenhouse! Reverting biome to [biome]!" broke: You broke this greenhouse! Reverting biome to [biome]!
fix: "Fix the greenhouse and then make it again." fix: Fix the greenhouse and then make it again.
cannotplace: "Blocks cannot be placed above a greenhouse!" cannotplace: Blocks cannot be placed above a greenhouse!
pistonerror: "Pistons cannot push blocks over a greenhouse!" pistonerror: Pistons cannot push blocks over a greenhouse!
limits: limits:
noneallowed: "Permissions do not allow you any greenhouses so [number] were removed." noneallowed: Permissions do not allow you any greenhouses so [number] were removed.
limitedto: "Permissions limit you to [limit] greenhouses so [number] were removed." limitedto: Permissions limit you to [limit] greenhouses so [number] were removed.
##################################
#Admin commands that use /gadmin #
##################################
#Help
adminHelp: adminHelp:
reload: "reload configuration from file." reload: reload configuration from file.
info: "provides info on the greenhouse you are in" info: provides info on the greenhouse you are in
#reload
reload: reload:
configReloaded: "Configuration reloaded from file." configReloaded: Configuration reloaded from file.
admininfo: admininfo:
error: "Greenhouse info only available in-game" error: Greenhouse info only available in-game
error2: "Put yourself in a greenhouse to see info." error2: Put yourself in a greenhouse to see info.
flags: "[Greenhouse Flags]" flags: "[Greenhouse Flags]"
news: news:
headline: "[Greenhouse News]" headline: "[Greenhouse News]"
controlpanel: controlpanel:
title: "&AGreenhouses" title: "&AGreenhouses"

View File

@ -1,37 +1,42 @@
--- ---
adminHelp: protection:
info: あなたがいる温室に関する情報を提供します flags:
reload: ファイルから設定をリロードします。 GREENHOUSE:
admininfo: name: 温室
error: ゲーム内でのみ利用可能な温室情報 description: |-
error2: 温室で情報を確認してください。 &b誰が温室を制御できるかを
flags: "[温室旗]" &b設定する
controlpanel:
title: "A温室"
error:
alreadyexists: 温室はすでに存在します!
greenhouseProtected: 温室保護
move: 最初に所有する温室に移動します。
norecipe: 温室を作ることができません!
notinside: あなたは温室の中にいません!
notowner: それを行うには、この温室の所有者でなければなりません。
notyours: これはあなたの温室ではありません!
removing: 温室を撤去!
tooexpensive: あなたは余裕がない[price]
event:
broke: あなたはこの温室を壊しました!バイオームを[biome]に戻しています!
cannotplace: ブロックを温室の上に置くことはできません!
fix: 温室を修理してから、もう一度作ります。
pistonerror: ピストンは温室の上でブロックを押すことができません!
greenhouses: greenhouses:
general:
greenhouses: 温室
errors:
move: 最初に所有する温室に移動します。
no-rank: "cあなたにはそれをするランクがありません。"
notyours: これはあなたの温室ではありません!
not-inside: "&cあなたは温室の中にいません"
tooexpensive: あなたは余裕がない[price]
alreadyexists: 温室はすでに存在します!
norecipe: 温室を作ることができません!
event:
broke: "&c温室を壊しましたバイオームを [biome]に戻しています!"
entering: "[biome]の温室に入る"
leaving: "[biome]の温室を離れる"
recipe:
blockscolor: "f"
title: "[[biome]レシピ]"
watermustbe: 水>[coverage]床面積の%。
icemustbe: 氷のブロック>床面積の[coverage]%。
lavamustbe: 溶岩> [coverage]床面積の%。
minimumblockstitle: "[必要な最小ブロック]"
nootherblocks: 他のブロックは必要ありません。
missing: 温室がありません
commands: commands:
user: user:
info: remove:
title: "A [温室の作り方]" description: あなたが所有者である場合、あなたが立っている温室を取り除きます
instructions: 4つの壁と平らなガラス屋根でガラスから箱を作り、壁に最大4つのドアを追加します。 1つのホッパーを壁または屋根に置き、水バケツを追加します。
雪や骨粉を作り、植物を自動的に育てます。 バイオームレシピを確認して、温室内で正常にブロックするために必要なブロックを確認してください。
make: make:
description: 温室を作ってみる description: 温室を作ってみる
parameters: "<レシピ>"
error: error:
already: "cここには温室がすでにあります" already: "cここには温室がすでにあります"
FAIL_BAD_ROOF_BLOCKS: "c屋根には許可されていないブロックが含まれています" FAIL_BAD_ROOF_BLOCKS: "c屋根には許可されていないブロックが含まれています"
@ -40,97 +45,99 @@ greenhouses:
FAIL_BLOCKS_ABOVE: "c温室の上にブロックを置くことはできません赤いガラスブロックに問題のあるブロックが表示されます。" FAIL_BLOCKS_ABOVE: "c温室の上にブロックを置くことはできません赤いガラスブロックに問題のあるブロックが表示されます。"
FAIL_HOLE_IN_ROOF: "c屋根に穴があるか、平らではありません赤いガラスブロックが問題を示しているはずです。" FAIL_HOLE_IN_ROOF: "c屋根に穴があるか、平らではありません赤いガラスブロックが問題を示しているはずです。"
FAIL_HOLE_IN_WALL: "c壁に穴が開いています" FAIL_HOLE_IN_WALL: "c壁に穴が開いています"
FAIL_INSUFFICIENT_ICE: "cこのレシピを作成するには氷が足りません"
FAIL_INSUFFICIENT_LAVA: "cこのレシピを作るには溶岩が足りません"
FAIL_INSUFFICIENT_WATER: "cこのレシピを作るのに水が足りません"
FAIL_NO_ROOF: "c屋根がないようです" FAIL_NO_ROOF: "c屋根がないようです"
FAIL_TOO_MANY_DOORS: "c温室には4つ以上のドアを置くことはできません" FAIL_TOO_MANY_DOORS: "c温室には4つ以上のドアを置くことはできません"
FAIL_TOO_MANY_HOPPERS: "c壁または屋根に使用できるホッパーは1つだけです。" FAIL_TOO_MANY_HOPPERS: "c壁または屋根に使用できるホッパーは1つだけです。"
FAIL_UNEVEN_WALLS: "c壁が不均一です。赤いガラスブロックに問題のあるブロックが表示されます。" FAIL_UNEVEN_WALLS: "c壁が不均一です。赤いガラスブロックに問題のあるブロックが表示されます。"
FAIL_INSUFFICIENT_ICE: "cこのレシピを作成するには氷が足りません"
FAIL_INSUFFICIENT_LAVA: "cこのレシピを作るには溶岩が足りません"
FAIL_INSUFFICIENT_WATER: "cこのレシピを作るのに水が足りません"
FAIL_NO_ICE: このレシピを作成するにはc氷が必要です FAIL_NO_ICE: このレシピを作成するにはc氷が必要です
FAIL_NO_LAVA: このレシピを作成するにはc溶岩が必要です FAIL_NO_LAVA: このレシピを作成するにはc溶岩が必要です
FAIL_NO_WATER: このレシピを作成するにはc水が必要です FAIL_NO_WATER: このレシピを作成するにはc水が必要です
parameters: "<レシピ>" FAIL_NO_RECIPE_FOUND: "cこの温室に一致するレシピが見つかりませんでした"
FAIL_INSUFFICIENT_BLOCKS: "&cこのレシピを作成するには、さらに多くのブロックが必要です。"
FAIL_OVERLAPPING: "c温室は壁を共有できません。申し訳ありません。"
success: "2あなたは[biome]バイオーム温室を無事に作成しました!バイオームは次のテレポートまたはログインで同期します。" success: "2あなたは[biome]バイオーム温室を無事に作成しました!バイオームは次のテレポートまたはログインで同期します。"
remove: missing-blocks: "&c欠落[material] x [number]"
description: あなたが所有者である場合、あなたが立っている温室を取り除きます unknown-recipe: "c不明なレシピ"
errors: try-these: "c次のいずれかを試してください。"
alreadyexists: 温室はすでに存在します! recipe-format: "&3[name]"
move: 最初に所有する温室に移動します。 info:
no-rank: "cあなたにはそれをするランクがありません。" title: "A [温室の作り方]"
norecipe: 温室を作ることができません! instructions: 4つの壁と平らなガラス屋根でガラスから箱を作り、壁に最大4つのドアを追加します。 1つのホッパーを壁または屋根に置き、水バケツを追加します。
not-inside: "cあなたは温室の中にいません" 雪や骨粉を作り、植物を自動的に育てます。 バイオームレシピを確認して、温室内で正常にブロックするために必要なブロックを確認してください。
notyours: これはあなたの温室ではありません!
tooexpensive: あなたは余裕がない[price]
general:
greenhouses: 温室
recipe:
blockscolor: "f"
minimumblockstitle: "[必要な最小ブロック]"
missing: 温室がありません
nootherblocks: 他のブロックは必要ありません。
title: "[[biome]レシピ]"
watermustbe: 水>[coverage]床面積の%。
icemustbe: 氷のブロック>床面積の[coverage]%。
lavamustbe: 溶岩> [coverage]床面積の%。
event:
broke: "&c温室を壊しましたバイオームを [biome]に戻しています!"
entering: "[biome]の温室に入る"
leaving: "[biome]の温室を離れる"
help: help:
help: 手助け help: 手助け
make: 温室を作ろうとする
remove: あなたが所有者である場合、あなたが立っている温室を取り除きます
info: 温室の作り方 info: 温室の作り方
list: 作成可能なすべての温室バイオームをリストします list: 作成可能なすべての温室バイオームをリストします
make: 温室を作ろうとする
recipe: 温室バイオームの作り方を説明します recipe: 温室バイオームの作り方を説明します
remove: あなたが所有者である場合、あなたが立っている温室を取り除きます
opengui: 温室のGUI opengui: 温室のGUI
info:
info: "[温室情報]"
instructions: "E 4つの壁と平らなガラスのE屋根でガラスから箱を作り、壁にF 4のドアEを追加します。 E壁と屋根にF 1ホッパーEを配置し、ウォーターバケツEを追加して雪や骨粉を作り、植物を自動的に成長させます。
Eバイオームレシピを確認して、E温室内で正常にブロックを作成するために必要なブロックを確認します。"
nomore: "4これ以上温室を建設することはできません"
none: 無し
onemore: "6温室をもう1つ構築できます。"
title: "A [温室の作り方]"
unlimited: "A温室を無制限に構築できます"
welcome: "B ようこそ!手順についてはここをクリックしてください"
youcanbuild: "A温室を最大[number]個まで構築できます!"
limits:
limitedto: 許可により温室が[limit]に制限されるため、[number]が削除されました。
noneallowed: 許可により温室が許可されないため、[number]は削除されました。
list: list:
title: "[温室効果バイオームのレシピ]" title: "[温室効果バイオームのレシピ]"
info: "/greenhouse recipe <番号>を使用する各温室の作り方の詳細を見る" info: "/greenhouse recipe <番号>を使用する各温室の作り方の詳細を見る"
error:
greenhouseProtected: 温室保護
move: 最初に所有する温室に移動します。
notowner: それを行うには、この温室の所有者でなければなりません。
removing: 温室を撤去!
notyours: これはあなたの温室ではありません!
notinside: あなたは温室の中にいません!
tooexpensive: あなたは余裕がない[price]
alreadyexists: 温室はすでに存在します!
norecipe: 温室を作ることができません!
messages: messages:
ecolost: "[location]の温室はエコシステムを失い、撤去されました。"
leave: "[owner]の温室を離れます。"
removed: この温室はもうありません...
enter: "[owner]の[biome]の温室に入る!" enter: "[owner]の[biome]の温室に入る!"
leave: "[owner]の温室を離れます。"
youarein: あなたは今[owner]の[biome]の温室にいます! youarein: あなたは今[owner]の[biome]の温室にいます!
removed: この温室はもうありません...
removedmessage: あなたの[biome]の温室はもうありません! removedmessage: あなたの[biome]の温室はもうありません!
news: ecolost: "[location]の温室はエコシステムを失い、撤去されました。"
headline: "[温室ニュース]" info:
protection: title: "A [温室の作り方]"
flags: instructions: "E 4つの壁と平らなガラスのE屋根でガラスから箱を作り、壁にF 4のドアEを追加します。 E壁と屋根にF 1ホッパーEを配置し、ウォーターバケツEを追加して雪や骨粉を作り、植物を自動的に成長させます。
GREENHOUSE: Eバイオームレシピを確認して、E温室内で正常にブロックを作成するために必要なブロックを確認します。"
name: 温室 info: "[温室情報]"
description: |- none: 無し
b誰が温室を制御できるかを nomore: "4これ以上温室を建設することはできません"
b設定する onemore: "6温室をもう1つ構築できます。"
youcanbuild: "A温室を最大[number]個まで構築できます!"
unlimited: "A温室を無制限に構築できます"
welcome: "B ようこそ!手順についてはここをクリックしてください"
recipe: recipe:
blockscolor: "f" blockscolor: "f"
hint: "/ greenhouse listを使用して、レシピ番号のリストを表示します" hint: "/ greenhouse listを使用して、レシピ番号のリストを表示します"
wrongnumber: レシピ番号は1から[size]の間でなければなりません
title: "[[biome]レシピ]"
nowater: 水は許可されません。
noice: 氷は許可されていません。
nolava: 溶岩は許可されていません。
watermustbe: 水> [coverage]床面積の%。
icemustbe: 氷のブロック&gt;床面積の[coverage]%。 icemustbe: 氷のブロック&gt;床面積の[coverage]%。
lavamustbe: 溶岩> [coverage]床面積の%。 lavamustbe: 溶岩> [coverage]床面積の%。
minimumblockstitle: "[必要な最小ブロック]" minimumblockstitle: "[必要な最小ブロック]"
missing: 温室がありません
noice: 氷は許可されていません。
nolava: 溶岩は許可されていません。
nootherblocks: 他のブロックは必要ありません。 nootherblocks: 他のブロックは必要ありません。
nowater: 水は許可されません。 missing: 温室がありません
title: "[[biome]レシピ]" event:
watermustbe: 水> [coverage]床面積の%。 broke: あなたはこの温室を壊しました!バイオームを[biome]に戻しています!
wrongnumber: レシピ番号は1から[size]の間でなければなりません fix: 温室を修理してから、もう一度作ります。
cannotplace: ブロックを温室の上に置くことはできません!
pistonerror: ピストンは温室の上でブロックを押すことができません!
limits:
noneallowed: 許可により温室が許可されないため、[number]は削除されました。
limitedto: 許可により温室が[limit]に制限されるため、[number]が削除されました。
adminHelp:
reload: ファイルから設定をリロードします。
info: あなたがいる温室に関する情報を提供します
reload: reload:
configReloaded: ファイルから構成が再ロードされました。 configReloaded: ファイルから構成が再ロードされました。
admininfo:
error: ゲーム内でのみ利用可能な温室情報
error2: 温室で情報を確認してください。
flags: "[温室旗]"
news:
headline: "[温室ニュース]"
controlpanel:
title: "A温室"

View File

@ -0,0 +1,190 @@
###########################################################################################
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
# If this file is deleted, then it will be recreate at the next reload. #
###########################################################################################
protection:
flags:
GREENHOUSE:
name: Nhà kính
description: |
&b Chỉnh cho ai có thể
&b điều khiển nhà kính
greenhouses:
general:
greenhouses: "Nhà kính"
errors:
move: "Di chuyển đến nhà kính của bạn trước."
no-rank: "&c Bạn không có cấp bậc để làm điều đó."
notyours: "Đây không phải nhà kính của bạn!"
not-inside: "&c Bạn không có ở trong nhà kính của bạn!"
tooexpensive: "Bạn không có đủ [price]"
alreadyexists: "Nhà kính đã tồn tại!"
norecipe: "Không thể tạo nhà kính!"
event:
broke: "Bạn đã đập nhà kính! Chuyển về [biome]!"
entering: "Đã vào nhà kính [biome]"
leaving: "Đã rời nhà kính [biome]"
recipe:
blockscolor: "&f"
title: "[Công thức [biome]]"
watermustbe: "Nước > [coverage]% trong một khu vực."
icemustbe: "Băng > [coverage]% trong một khu vực."
lavamustbe: "Nham thạch > [coverage]% trong một khu vực."
minimumblockstitle: "[Cần số khối tối thiểu]"
nootherblocks: "Không cần khối khác."
missing: "Nhà kính đang bị thiếu"
commands:
user:
remove:
description: "Xóa nhà kính ở chỗ bạn đứng nếu bạn là chủ"
make:
description: "Tạo một nhà kính"
parameters: "<công thức>"
error:
already: "&cNhà kính đã tồn tại!"
FAIL_BAD_ROOF_BLOCKS: "&c Trần nhà có khối không hợp lệ!"
FAIL_BAD_WALL_BLOCKS: "&c Tường nhà có khối không hợp lệ!"
FAIL_BELOW: "&c Bạn phải ở trong nhà kính để tạo nó"
FAIL_BLOCKS_ABOVE: "&c Không có khối ở trên nhà kính! Khối kính đỏ sẽ cho biết chỗ có vấn đề."
FAIL_HOLE_IN_ROOF: "&c Có một lỗ trên trần hoặc trần không phẳng! Khối kính đỏ sẽ cho biết chỗ có vấn đề."
FAIL_HOLE_IN_WALL: "&c Có một lỗ trên tường!"
FAIL_NO_ROOF: "&c Có vẻ chưa có trần nhà!"
FAIL_TOO_MANY_DOORS: "&c Bạn không thể có hơn 4 cánh cửa trong nhà kính!"
FAIL_TOO_MANY_HOPPERS: "&c Chỉ có một phễu được cho phép trên tường hoặc trên trần."
FAIL_UNEVEN_WALLS: "&c Tường không cân bằng. Khối kính đỏ sẽ cho biết chỗ có vấn đề."
FAIL_INSUFFICIENT_ICE: "&c Không đủ băng để làm công thức này"
FAIL_INSUFFICIENT_LAVA: "&c Không đủ nham thạch để làm công thức này"
FAIL_INSUFFICIENT_WATER: "&c Không đủ nước để làm công thức này"
FAIL_NO_ICE: "&c Cần băng để làm công thức này"
FAIL_NO_LAVA: "&c Cần nham thạch để làm công thức này"
FAIL_NO_WATER: "&c Cần nước để làm công thức này"
FAIL_NO_RECIPE_FOUND: "&c Không có công thức phù hợp cho nhà kính này"
FAIL_INSUFFICIENT_BLOCKS: "&c Cần nhiều khối hơn để làm công thức này!"
FAIL_OVERLAPPING: "&c Nhà kính không thể có tường chung, xin lỗi."
success: "&2 Bạn đã tạo nhà kính [biome]! Nó sẽ đồng bộ ở lần dịch chuyển hoặc đăng nhập tiếp theo."
missing-blocks: "&c Thiếu [material] x [number]"
unknown-recipe: "&c Công thức không tồn tại"
try-these: "&c Thử một trong những cái này:"
recipe-format: "&3[name]"
info:
title: "&a [Cách tạo một nhà kính]"
instructions: |
&e Tạo một hộp bằng kính với 4 bức tường và một cái trần phẳng
&e và thêm vào tối đa &f 4 cánh cửa &Etrên tường.
&e Đặt &f 1 cái phễu &e trên tường hoặc trần nhà và thêm nào đó các xô nước.
&e để tạo tuyết hoặc thêm bột xương để mọc cây tự động.
&e Kiểm tra các công thức để biết các khối cần có
&e trong nhà kính để tạo một cái.
######### Old locale for reference
help:
help: "hướng dẫn"
make: "Tạo một nhà kính"
remove: "Xóa nhà kính ở chỗ bạn đứng nếu bạn là chủ"
info: "Cách tạo một nhà kính"
list: "Liệt kê các biome trong nhà kính bạn có thể tạo"
recipe: "Hướng dẫn cách tạo một biome trong nhà kính"
opengui: "Mở Menu nhà kính"
list:
title: "[Công thức Biome nhà kính]"
info: "Dùng /greenhouse recipe <số> để xem thông tin cách tạo nhà kính"
################
#General Errors#
################
error:
greenhouseProtected: "Đã bảo vệ nhà kính"
move: "Di chuyển đến nhà kính của bạn trước."
notowner: "Bạn phải là chủ nhà kính để làm điều này."
removing: "Đang xóa nhà kính!"
notyours: "Đây không phải nhà kính của bạn!"
notinside: "Bạn không có ở trong nhà kính!"
tooexpensive: "Bạn không có đủ [price]"
alreadyexists: "Nhà kính đã tồn tại!"
norecipe: "Không thể tạo nhà kính!"
messages:
enter: "Đang vào nhà kính [biome] của [owner]!"
leave: "Đang rời nhà kính của [owner]."
youarein: "Bạn đang ở nhà kính [biome] của [owner]!"
removed: "Nhà kính này không còn nữa..."
removedmessage: "Một nhà kính [biome] của bạn không còn nữa!"
ecolost: "Nhà kính của bạn ở [location] mất hệ sinh thái và đã bị xóa."
info:
title: "&A[Cách tạo một nhà kính]"
instructions: |
&e Tạo một hộp bằng kính với 4 bức tường và một cái trần phẳng
&e và thêm vào tối đa &f 4 cánh cửa &Etrên tường.
&e Đặt &f 1 cái phễu &e trên tường hoặc trần nhà và thêm nào đó các xô nước.
&e để tạo tuyết hoặc thêm bột xương để mọc cây tự động.
&e Kiểm tra các công thức để biết các khối cần có
&e trong nhà kính để tạo một cái.
info: "[Thông tin nhà kính]"
none: "Không có"
nomore: "&4 Bạn không thể xây thêm nhà kính!"
onemore: "&6 Bạn có thể xây thêm một nhà kính."
youcanbuild: "&A Bạn có thể xây thêm tối đa [number] nhà kính nữa!"
unlimited: "&A Bạn có thể xây vô hạn nhà kính!"
welcome: "&B Xin chào! Nhấp vào đây để xem hướng dẫn"
recipe:
blockscolor: "&f"
hint: "Dùng /greenhouse list để xem số công thức!"
wrongnumber: "Số công thức phải từ 1 đến [size]"
title: "[Công thức [biome]]"
nowater: "Không cho phép có nước."
noice: "Không cho phép có băng."
nolava: "Không cho phép có nham thạch."
watermustbe: "Nước > [coverage]% trong một khu vực."
icemustbe: "Băng > [coverage]% trong một khu vực."
lavamustbe: "Nham thạch > [coverage]% trong một khu vực."
minimumblockstitle: "[Cần số khối tối thiểu]"
nootherblocks: "Không cần khối khác."
missing: "Nhà kính đang bị thiếu"
event:
broke: "Bạn đã đập nhà kính! Chuyển về [biome]!"
fix: "Sửa nhà kính và tạo lại."
cannotplace: "Các khối không thể đặt trên nhà kính!"
pistonerror: "Pít tông không thể đẩy khối vào một nhà kính!"
limits:
noneallowed: "Quyền không cho phép bạn nên [number] đã bị xóa."
limitedto: "Quyền đã giới hạn bạn chỉ [limit] nhà kính nên [number] đã bị xóa."
##################################
#Admin commands that use /gadmin #
##################################
#Help
adminHelp:
reload: "nạp các tùy chỉnh từ tệp."
info: "xem các thông tin về nhà kính bạn đang đứng"
#reload
reload:
configReloaded: "Đã nạp các tùy chỉnh từ tệp."
admininfo:
error: "Thông tin nhà kính chỉ tồn tại trong game"
error2: "Đặt bạn vào một nhà kính để xem thông tin."
flags: "[Cờ Nhà Kính]"
news:
headline: "[Bản Tin Nhà Kính]"
controlpanel:
title: "&ANhà kính"

View File

@ -1,7 +1,4 @@
# ---
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
# If this file is deleted, then it will be recreate at the next reload. #
protection: protection:
flags: flags:
GREENHOUSE: GREENHOUSE:
@ -12,9 +9,9 @@ greenhouses:
greenhouses: 温室 greenhouses: 温室
errors: errors:
move: 移动到你拥有的第一个温室. move: 移动到你拥有的第一个温室.
no-rank: '&c你没有足够的阶级去做这个.' no-rank: "&c你没有足够的阶级去做这个."
notyours: 这不是你的温室! notyours: 这不是你的温室!
not-inside: '&c你并不处于一个温室之中!' not-inside: "&c你并不处于一个温室之中!"
tooexpensive: 你的钱不足以支付 [price] tooexpensive: 你的钱不足以支付 [price]
alreadyexists: 温室已经存在! alreadyexists: 温室已经存在!
norecipe: 无法创建一个温室! norecipe: 无法创建一个温室!
@ -23,12 +20,12 @@ greenhouses:
entering: 你进入一个 [biome] 温室 entering: 你进入一个 [biome] 温室
leaving: 你离开了 [biome] 温室 leaving: 你离开了 [biome] 温室
recipe: recipe:
blockscolor: '&f' blockscolor: "&f"
title: '[[biome] 配方]' title: "[[biome] 配方]"
watermustbe: 水在地面上的占比需要大于[coverage]%. watermustbe: 水在地面上的占比需要大于[coverage]%.
icemustbe: 冰在地面上的占比需要大于[coverage]%. icemustbe: 冰在地面上的占比需要大于[coverage]%.
lavamustbe: 岩浆在地面上的占比需要大于[coverage]%. lavamustbe: 岩浆在地面上的占比需要大于[coverage]%.
minimumblockstitle: '[最少所需的方块数目]' minimumblockstitle: "[最少所需的方块数目]"
nootherblocks: 没有其它需要的方块了. nootherblocks: 没有其它需要的方块了.
missing: 温室缺失了 missing: 温室缺失了
commands: commands:
@ -37,39 +34,37 @@ greenhouses:
description: 如果你是你现在所处的温室的主人则移除这个温室 description: 如果你是你现在所处的温室的主人则移除这个温室
make: make:
description: 尝试建造一个温室 description: 尝试建造一个温室
parameters: <recipe> parameters: "<recipe>"
error: error:
already: '&c这已经有一个温室了!' already: "&c这已经有一个温室了!"
FAIL_BAD_ROOF_BLOCKS: '&c屋顶包含了不被允许的方块!' FAIL_BAD_ROOF_BLOCKS: "&c屋顶包含了不被允许的方块!"
FAIL_BAD_WALL_BLOCKS: '&c墙壁包含了不被允许的方块!' FAIL_BAD_WALL_BLOCKS: "&c墙壁包含了不被允许的方块!"
FAIL_BELOW: '&c你必须处于温室结构内才能尝试建造它' FAIL_BELOW: "&c你必须处于温室结构内才能尝试建造它"
FAIL_BLOCKS_ABOVE: '&c温室的上方不能有方块违规方块已被标红.' FAIL_BLOCKS_ABOVE: "&c温室的上方不能有方块违规方块已被标红."
FAIL_HOLE_IN_ROOF: '&c屋顶上有一个洞或者屋顶并不平整违规方块已被标红.' FAIL_HOLE_IN_ROOF: "&c屋顶上有一个洞或者屋顶并不平整违规方块已被标红."
FAIL_HOLE_IN_WALL: '&c墙上有一个洞!' FAIL_HOLE_IN_WALL: "&c墙上有一个洞!"
FAIL_NO_ROOF: '&c这儿看起来没有屋顶!' FAIL_NO_ROOF: "&c这儿看起来没有屋顶!"
FAIL_TOO_MANY_DOORS: '&c你的温室最多只能有四个门!' FAIL_TOO_MANY_DOORS: "&c你的温室最多只能有四个门!"
FAIL_TOO_MANY_HOPPERS: '&c墙或屋顶上至多有一个漏斗.' FAIL_TOO_MANY_HOPPERS: "&c墙或屋顶上至多有一个漏斗."
FAIL_UNEVEN_WALLS: '&c墙壁不平坦违规方块已被标红.' FAIL_UNEVEN_WALLS: "&c墙壁不平坦违规方块已被标红."
FAIL_INSUFFICIENT_ICE: '&c冰的数目不足以建造这个温室' FAIL_INSUFFICIENT_ICE: "&c冰的数目不足以建造这个温室"
FAIL_INSUFFICIENT_LAVA: '&c岩浆的数目不足以建造这个温室' FAIL_INSUFFICIENT_LAVA: "&c岩浆的数目不足以建造这个温室"
FAIL_INSUFFICIENT_WATER: '&c水的数目不足以建造这个温室' FAIL_INSUFFICIENT_WATER: "&c水的数目不足以建造这个温室"
FAIL_NO_ICE: '&c对于这个温室来说冰是必须的' FAIL_NO_ICE: "&c对于这个温室来说冰是必须的"
FAIL_NO_LAVA: '&c对于这个温室来说岩浆是必须的' FAIL_NO_LAVA: "&c对于这个温室来说岩浆是必须的"
FAIL_NO_WATER: '&c对于这个温室来说水是必须的' FAIL_NO_WATER: "&c对于这个温室来说水是必须的"
FAIL_INSUFFICIENT_BLOCKS: '&c你所用的方块数目不足!' FAIL_NO_RECIPE_FOUND: "&c 找不到与此温室相匹配的配方"
FAIL_OVERLAPPING: '&c温室间不能共享墙壁.' FAIL_INSUFFICIENT_BLOCKS: "&c你所用的方块数目不足!"
success: '&2你成功的建造了一个 [biome] 群系温室! 在你下次登陆时生物群系将会刷新.' FAIL_OVERLAPPING: "&c温室间不能共享墙壁."
missing-blocks: '&c缺少了 [material] x [number]' success: "&2你成功的建造了一个 [biome] 群系温室! 在你下次登陆时生物群系将会刷新."
unknown-recipe: '&c未知的温室配方' missing-blocks: "&c缺少了 [material] x [number]"
try-these: '&c尝试以下操作:' unknown-recipe: "&c未知的温室配方"
recipe-format: '&3[name]' try-these: "&c尝试以下操作:"
recipe-format: "&3[name]"
info: info:
title: '&A[温室建造指南]' title: "&A[温室建造指南]"
instructions: "&E用玻璃建造一个有四面墙和屋顶的平整的立方体\n\ instructions: "&E用玻璃建造一个有四面墙和屋顶的平整的立方体\n&E最多只能有 &F4 扇门. &E\n在墙壁或屋顶内&E放置 &F一个漏斗
&E最多只能有 &F4 扇门. &E\n在墙壁或屋顶内&E放置 &F一个漏斗\ \n&E来放置骨粉或雪 来让作物自动生长.\n&E检查温室配方确保 你的温室包含足够的必要方块\n&E温室就顺利建造了. \n"
\ \n&E来放置骨粉或雪\
\ 来让作物自动生长.\n&E检查温室配方确保\
\ 你的温室包含足够的必要方块\n&E温室就顺利建造了. \n"
help: help:
help: 帮助 help: 帮助
make: 尝试建造一个温室 make: 尝试建造一个温室
@ -79,7 +74,7 @@ help:
recipe: 告知你如何制造一个温室 recipe: 告知你如何制造一个温室
opengui: 打开温室GUI opengui: 打开温室GUI
list: list:
title: '[温室建造指南]' title: "[温室建造指南]"
info: 使用指令 /greenhouse recipe <number> 来查看建造每一个温室的细节 info: 使用指令 /greenhouse recipe <number> 来查看建造每一个温室的细节
error: error:
greenhouseProtected: 温室已被保护 greenhouseProtected: 温室已被保护
@ -99,31 +94,28 @@ messages:
removedmessage: 你的 [biome] 温室已经被移除! removedmessage: 你的 [biome] 温室已经被移除!
ecolost: 你位于 [location] 的温室生态环境遭到破坏,它已经不再是温室了. ecolost: 你位于 [location] 的温室生态环境遭到破坏,它已经不再是温室了.
info: info:
title: '&A[如何建造一个温室]' title: "&A[如何建造一个温室]"
instructions: "&E用玻璃建造一个有四面墙和屋顶的平整的立方体\n\ instructions: "&E用玻璃建造一个有四面墙和屋顶的平整的立方体\n&E最多只能有 &F4 扇门. &E\n在墙壁或屋顶内&E放置 &F一个漏斗 \n&E来放置骨粉或雪
&E最多只能有 &F4 扇门. &E\n在墙壁或屋顶内&E放置 &F一个漏斗\ 来让作物自动生长.\n&E检查温室配方确保 你的温室包含足够的必要方块\n&E温室就顺利建造了. \n"
\ \n&E来放置骨粉或雪\ info: "[温室信息]"
\ 来让作物自动生长.\n&E检查温室配方确保\
\ 你的温室包含足够的必要方块\n&E温室就顺利建造了. \n"
info: '[温室信息]'
none: none:
nomore: '&4你无法建造更多的温室了!' nomore: "&4你无法建造更多的温室了!"
onemore: '&6你可以再建造一个温室.' onemore: "&6你可以再建造一个温室."
youcanbuild: '&A你最多能建造 [number] 个温室!' youcanbuild: "&A你最多能建造 [number] 个温室!"
unlimited: '&A你能建造无限多个温室!' unlimited: "&A你能建造无限多个温室!"
welcome: '&B你好! 点击这里获得更多提示' welcome: "&B你好! 点击这里获得更多提示"
recipe: recipe:
blockscolor: '&f' blockscolor: "&f"
hint: 使用 /greenhouse list 来看见所有温室配方的编号! hint: 使用 /greenhouse list 来看见所有温室配方的编号!
wrongnumber: 温室配方编号必须位于 1 与 [size] 之间 wrongnumber: 温室配方编号必须位于 1 与 [size] 之间
title: '[[biome] 配方]' title: "[[biome] 配方]"
nowater: 不允许有水. nowater: 不允许有水.
noice: 不允许有冰. noice: 不允许有冰.
nolava: 不允许有岩浆. nolava: 不允许有岩浆.
watermustbe: 水在地面上的占比需要大于 [coverage]%. watermustbe: 水在地面上的占比需要大于 [coverage]%.
icemustbe: 冰在地面上的占比需要大于 [coverage]%. icemustbe: 冰在地面上的占比需要大于 [coverage]%.
lavamustbe: 岩浆在地面上的占比需要大于 [coverage]%. lavamustbe: 岩浆在地面上的占比需要大于 [coverage]%.
minimumblockstitle: '[最少所需的方块]' minimumblockstitle: "[最少所需的方块]"
nootherblocks: 没有其它必要的方块了. nootherblocks: 没有其它必要的方块了.
missing: 温室缺失了 missing: 温室缺失了
event: event:
@ -142,8 +134,8 @@ reload:
admininfo: admininfo:
error: 查看温室信息功能仅能在游戏中使用 error: 查看温室信息功能仅能在游戏中使用
error2: 进入一个温室才能查看其信息. error2: 进入一个温室才能查看其信息.
flags: '[Greenhouse Flags]' flags: "[Greenhouse Flags]"
news: news:
headline: '[温室新闻]' headline: "[温室新闻]"
controlpanel: controlpanel:
title: '&A温室' title: "&A温室"

View File

@ -0,0 +1,9 @@
name: BentoBox-Greenhouses
main: world.bentobox.greenhouses.GreenhousesPladdon
version: ${project.version}${build.number}
api-version: "1.19"
authors: [tastybento]
contributors: ["The BentoBoxWorld Community"]
website: https://bentobox.world
description: ${project.description}

View File

@ -0,0 +1,175 @@
package world.bentobox.greenhouses;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
public class SettingsTest {
private Settings s;
@Before
public void setUp() {
s = new Settings();
}
@Test
public void testGetGameModes() {
assertTrue(s.getGameModes().isEmpty());
}
@Test
public void testGetSnowSpeed() {
assertEquals(30D, s.getSnowSpeed(), 0D);
}
@Test
public void testGetSnowChanceGlobal() {
assertEquals(1D, s.getSnowChanceGlobal(), 0D);
}
@Test
public void testGetSnowDensity() {
assertEquals(0.1D, s.getSnowDensity(), 0D);
}
@Test
public void testGetEcoTick() {
assertEquals(5, s.getEcoTick());
}
@Test
public void testGetPlantTick() {
assertEquals(1, s.getPlantTick());
}
@Test
public void testGetBlockTick() {
assertEquals(2, s.getBlockTick());
}
@Test
public void testGetMobTick() {
assertEquals(5, s.getMobTick());
}
@Test
public void testIsStartupLog() {
assertFalse(s.isStartupLog());
}
@Test
public void testSetStartupLog() {
s.setStartupLog(true);
assertTrue(s.isStartupLog());
}
@Test
public void testIsAllowFlowOut() {
assertFalse(s.isAllowFlowOut());
}
@Test
public void testIsAllowFlowIn() {
assertFalse(s.isAllowFlowIn());
}
@Test
public void testSetGameModes() {
s.setGameModes(Collections.singletonList("BSkyBlock"));
assertEquals("BSkyBlock", s.getGameModes().get(0));
}
@Test
public void testSetSnowSpeed() {
s.setSnowSpeed(50);
assertEquals(50D, s.getSnowSpeed(), 0D);
}
@Test
public void testSetSnowChanceGlobal() {
s.setSnowChanceGlobal(50);
assertEquals(50D, s.getSnowChanceGlobal(), 0D);
}
@Test
public void testSetSnowDensity() {
s.setSnowDensity(50);
assertEquals(50D, s.getSnowDensity(), 0D);
}
@Test
public void testSetEcoTick() {
s.setEcoTick(50);
assertEquals(50, s.getEcoTick());
}
@Test
public void testSetPlantTick() {
s.setPlantTick(50);
assertEquals(50, s.getPlantTick());
}
@Test
public void testSetBlockTick() {
s.setBlockTick(50);
assertEquals(50, s.getBlockTick());
}
@Test
public void testSetMobTick() {
s.setMobTick(50);
assertEquals(50, s.getMobTick());
}
@Test
public void testSetAllowFlowOut() {
assertFalse(s.isAllowFlowOut());
s.setAllowFlowOut(true);
assertTrue(s.isAllowFlowOut());
}
@Test
public void testSetAllowFlowIn() {
assertFalse(s.isAllowFlowIn());
s.setAllowFlowIn(true);
assertTrue(s.isAllowFlowIn());
}
@Test
public void testIsAllowGlowstone() {
assertTrue(s.isAllowGlowstone());
}
@Test
public void testSetAllowGlowstone() {
assertTrue(s.isAllowGlowstone());
s.setAllowGlowstone(false);
assertFalse(s.isAllowGlowstone());
}
@Test
public void testIsAllowPanes() {
assertTrue(s.isAllowPanes());
}
@Test
public void testSetAllowPanes() {
assertTrue(s.isAllowPanes());
s.setAllowPanes(false);
assertFalse(s.isAllowPanes());
}
}

View File

@ -2,9 +2,9 @@ package world.bentobox.greenhouses.data;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
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;
@ -16,6 +16,7 @@ import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.util.BoundingBox; import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -54,15 +55,12 @@ public class GreenhouseTest {
@Mock @Mock
private BiomeRecipe br; private BiomeRecipe br;
/**
* @throws java.lang.Exception
*/
@Before @Before
public void setUp() throws Exception { public void setUp() {
// 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);
@ -73,10 +71,9 @@ public class GreenhouseTest {
} }
/** /**
* @throws java.lang.Exception
*/ */
@After @After
public void tearDown() throws Exception { public void tearDown() {
Mockito.framework().clearInlineMocks(); Mockito.framework().clearInlineMocks();
} }
@ -188,13 +185,15 @@ public class GreenhouseTest {
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.data.Greenhouse#setRoofHopperLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.greenhouses.data.Greenhouse#setRoofHopperLocation(Vector)}.
*/ */
@Test @Test
public void testSetRoofHopperLocation() { public void testSetRoofHopperLocation() {
Location l = new Location(world, 1,2,3); gh.setRoofHopperLocation(new Vector(1,2,3));
gh.setRoofHopperLocation(l); assertEquals(world, gh.getRoofHopperLocation().getWorld());
assertEquals(l, gh.getRoofHopperLocation()); assertEquals(1, gh.getRoofHopperLocation().getBlockX());
assertEquals(2, gh.getRoofHopperLocation().getBlockY());
assertEquals(3, gh.getRoofHopperLocation().getBlockZ());
} }
/** /**
@ -276,7 +275,7 @@ public class GreenhouseTest {
*/ */
@Test @Test
public void testGetBiomeRecipe() { public void testGetBiomeRecipe() {
assertNull(gh.getBiomeRecipe()); assertNotNull(gh.getBiomeRecipe());
} }
/** /**
@ -285,7 +284,7 @@ public class GreenhouseTest {
@Test @Test
public void testSetMissingBlocks() { public void testSetMissingBlocks() {
gh.setMissingBlocks(Collections.singletonMap(Material.ACACIA_BOAT, 20)); gh.setMissingBlocks(Collections.singletonMap(Material.ACACIA_BOAT, 20));
assertTrue(gh.getMissingBlocks().get(Material.ACACIA_BOAT) == 20); assertEquals(20, (int) gh.getMissingBlocks().get(Material.ACACIA_BOAT));
} }
/** /**
@ -293,7 +292,7 @@ public class GreenhouseTest {
*/ */
@Test @Test
public void testGetMissingBlocks() { public void testGetMissingBlocks() {
assertNull(gh.getMissingBlocks()); assertNotNull(gh.getMissingBlocks());
} }
} }

View File

@ -13,7 +13,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -47,8 +46,8 @@ import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.Settings; import world.bentobox.greenhouses.Settings;
import world.bentobox.greenhouses.data.Greenhouse; import world.bentobox.greenhouses.data.Greenhouse;
import world.bentobox.greenhouses.managers.EcoSystemManager.GrowthBlock;
import world.bentobox.greenhouses.managers.GreenhouseManager; import world.bentobox.greenhouses.managers.GreenhouseManager;
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
import world.bentobox.greenhouses.managers.GreenhouseMap; import world.bentobox.greenhouses.managers.GreenhouseMap;
/** /**
@ -63,7 +62,6 @@ public class BiomeRecipeTest {
@Mock @Mock
private Greenhouses addon; private Greenhouses addon;
private Biome type;
@Mock @Mock
private Greenhouse gh; private Greenhouse gh;
@ -87,20 +85,19 @@ public class BiomeRecipeTest {
@Mock @Mock
private Settings settings; private Settings settings;
/**
* @throws java.lang.Exception
*/
@Before @Before
public void setUp() throws Exception { public void setUp() {
PowerMockito.mockStatic(Bukkit.class); PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.createBlockData(any(Material.class))).thenReturn(bd); when(Bukkit.createBlockData(any(Material.class))).thenReturn(bd);
type = Biome.BADLANDS; Biome type = Biome.BADLANDS;
// Greenhouse // Greenhouse
when(gh.getArea()).thenReturn(100); when(gh.getArea()).thenReturn(100);
when(gh.getFloorHeight()).thenReturn(100); when(gh.getFloorHeight()).thenReturn(100);
when(gh.getCeilingHeight()).thenReturn(120); when(gh.getCeilingHeight()).thenReturn(120);
bb = new BoundingBox(10, 100, 10, 20, 120, 20); bb = new BoundingBox(10, 100, 10, 20, 120, 20);
when(gh.getBoundingBox()).thenReturn(bb); when(gh.getBoundingBox()).thenReturn(bb);
BoundingBox ibb = bb.clone().expand(-1);
when(gh.getInternalBoundingBox()).thenReturn(ibb);
when(gh.getWorld()).thenReturn(world); when(gh.getWorld()).thenReturn(world);
when(gh.contains(any())).thenReturn(true); when(gh.contains(any())).thenReturn(true);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block); when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block);
@ -154,50 +151,50 @@ 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");
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addMobs(org.bukkit.entity.EntityType, int, org.bukkit.Material)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addMobs(org.bukkit.entity.EntityType, double, org.bukkit.Material)}.
*/ */
@Test @Test
public void testAddMobs() { public void testAddMobs() {
EntityType mobType = EntityType.CAT; EntityType mobType = EntityType.CAT;
int mobProbability = 50; int mobProbability = 50;
Material mobSpawnOn = Material.GRASS_PATH; 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 Path.")); verify(addon).log(" 50.0% chance for Cat to spawn on Grass Block.");
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addMobs(org.bukkit.entity.EntityType, int, org.bukkit.Material)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addMobs(org.bukkit.entity.EntityType, double, org.bukkit.Material)}.
*/ */
@Test @Test
public void testAddMobsOver100Percent() { public void testAddMobsOver100Percent() {
EntityType mobType = EntityType.CAT; EntityType mobType = EntityType.CAT;
int mobProbability = 50; int mobProbability = 50;
Material mobSpawnOn = Material.GRASS_PATH; 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);
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");
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addMobs(org.bukkit.entity.EntityType, int, org.bukkit.Material)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addMobs(org.bukkit.entity.EntityType, double, org.bukkit.Material)}.
*/ */
@Test @Test
public void testAddMobsOver100PercentDouble() { public void testAddMobsOver100PercentDouble() {
EntityType mobType = EntityType.CAT; EntityType mobType = EntityType.CAT;
double mobProbability = 50.5; double mobProbability = 50.5;
Material mobSpawnOn = Material.GRASS_PATH; 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");
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addPlants(org.bukkit.Material, int, org.bukkit.Material)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addPlants(org.bukkit.Material, double, org.bukkit.Material)}.
*/ */
@Test @Test
public void testAddPlants() { public void testAddPlants() {
@ -205,11 +202,11 @@ 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");
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addPlants(org.bukkit.Material, int, org.bukkit.Material)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#addPlants(org.bukkit.Material, double, org.bukkit.Material)}.
*/ */
@Test @Test
public void testAddPlantsOver100Percent() { public void testAddPlantsOver100Percent() {
@ -218,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");
} }
/** /**
@ -229,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");
} }
/** /**
@ -237,8 +234,8 @@ public class BiomeRecipeTest {
*/ */
@Test @Test
public void testCheckRecipe() { public void testCheckRecipe() {
Set<GreenhouseResult> result = br.checkRecipe(gh); br.checkRecipe(gh).thenAccept(result ->
assertTrue(result.isEmpty()); assertTrue(result.isEmpty()));
} }
/** /**
@ -247,8 +244,8 @@ public class BiomeRecipeTest {
@Test @Test
public void testCheckRecipeNotEnough() { public void testCheckRecipeNotEnough() {
br.addReqBlocks(Material.ACACIA_LEAVES, 3); br.addReqBlocks(Material.ACACIA_LEAVES, 3);
Set<GreenhouseResult> result = br.checkRecipe(gh); br.checkRecipe(gh).thenAccept(result ->
assertFalse(result.isEmpty()); assertFalse(result.isEmpty()));
} }
@ -265,29 +262,10 @@ public class BiomeRecipeTest {
Block ab = mock(Block.class); Block ab = mock(Block.class);
when(ab.getType()).thenReturn(Material.WATER); when(ab.getType()).thenReturn(Material.WATER);
when(b.getRelative(any())).thenReturn(ab); when(b.getRelative(any())).thenReturn(ab);
br.convertBlock(gh, b); br.convertBlock(b);
verify(b).setType(Material.CLAY); verify(b).setType(Material.CLAY);
} }
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#convertBlock(org.bukkit.block.Block)}.
*/
@Test
public void testConvertBlockNotInGreenhouse() {
// Setup
this.testAddConvBlocks();
// Mock
Block b = mock(Block.class);
when(b.getType()).thenReturn(Material.SAND);
Block ab = mock(Block.class);
when(ab.getType()).thenReturn(Material.WATER);
when(b.getRelative(any())).thenReturn(ab);
when(ab.getLocation()).thenReturn(location);
when(gh.contains(any())).thenReturn(false);
br.convertBlock(gh, b);
verify(b, never()).setType(any());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#convertBlock(org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#convertBlock(org.bukkit.block.Block)}.
*/ */
@ -301,7 +279,7 @@ public class BiomeRecipeTest {
Block ab = mock(Block.class); Block ab = mock(Block.class);
when(ab.getType()).thenReturn(Material.SAND); when(ab.getType()).thenReturn(Material.SAND);
when(b.getRelative(any())).thenReturn(ab); when(b.getRelative(any())).thenReturn(ab);
br.convertBlock(gh, b); br.convertBlock(b);
verify(b, never()).setType(Material.CLAY); verify(b, never()).setType(Material.CLAY);
} }
@ -313,10 +291,30 @@ public class BiomeRecipeTest {
// Mock // Mock
Block b = mock(Block.class); Block b = mock(Block.class);
when(b.getType()).thenReturn(Material.SAND); when(b.getType()).thenReturn(Material.SAND);
br.convertBlock(gh, b); br.convertBlock(b);
verify(b, never()).setType(Material.CLAY); verify(b, never()).setType(Material.CLAY);
} }
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#convertBlock(org.bukkit.block.Block)}.
*/
@Test
public void testConvertBlockNoLocalBlock() {
// Setup
Material oldMaterial = Material.SAND;
Material newMaterial = Material.CLAY;
double convChance = 100D;
br.addConvBlocks(oldMaterial, newMaterial, convChance, null);
// Mock
Block b = mock(Block.class);
when(b.getType()).thenReturn(Material.SAND);
br.convertBlock(b);
verify(b, never()).getRelative(any());
verify(b).setType(Material.CLAY);
}
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#convertBlock(org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#convertBlock(org.bukkit.block.Block)}.
*/ */
@ -335,7 +333,7 @@ public class BiomeRecipeTest {
Block ab = mock(Block.class); Block ab = mock(Block.class);
when(ab.getType()).thenReturn(Material.WATER); when(ab.getType()).thenReturn(Material.WATER);
when(b.getRelative(any())).thenReturn(ab); when(b.getRelative(any())).thenReturn(ab);
br.convertBlock(gh, b); br.convertBlock(b);
verify(b, never()).setType(Material.CLAY); verify(b, never()).setType(Material.CLAY);
} }
@ -437,12 +435,12 @@ public class BiomeRecipeTest {
@Test @Test
public void testSpawnMobOutsideWall() { public void testSpawnMobOutsideWall() {
when(block.getY()).thenReturn(10); when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_PATH); when(block.getType()).thenReturn(Material.GRASS_BLOCK);
when(block.getRelative(any())).thenReturn(block); when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.CAT; EntityType mobType = EntityType.CAT;
int mobProbability = 100; int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH; Material mobSpawnOn = Material.GRASS_BLOCK;
Entity cat = mock(Cat.class); Entity cat = mock(Cat.class);
// Same box as greenhouse // Same box as greenhouse
@ -453,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));
} }
@ -463,12 +461,12 @@ public class BiomeRecipeTest {
@Test @Test
public void testSpawnMob() { public void testSpawnMob() {
when(block.getY()).thenReturn(10); when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_PATH); when(block.getType()).thenReturn(Material.GRASS_BLOCK);
when(block.getRelative(any())).thenReturn(block); when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.CAT; EntityType mobType = EntityType.CAT;
int mobProbability = 100; int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH; Material mobSpawnOn = Material.GRASS_BLOCK;
Entity cat = mock(Cat.class); Entity cat = mock(Cat.class);
// Exactly 1 block smaller than the greenhouse blocks // Exactly 1 block smaller than the greenhouse blocks
@ -479,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));
} }
@ -489,12 +487,12 @@ public class BiomeRecipeTest {
@Test @Test
public void testSpawnMobHoglin() { public void testSpawnMobHoglin() {
when(block.getY()).thenReturn(10); when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_PATH); when(block.getType()).thenReturn(Material.GRASS_BLOCK);
when(block.getRelative(any())).thenReturn(block); when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.HOGLIN; EntityType mobType = EntityType.HOGLIN;
int mobProbability = 100; int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH; Material mobSpawnOn = Material.GRASS_BLOCK;
Hoglin hoglin = mock(Hoglin.class); Hoglin hoglin = mock(Hoglin.class);
// Exactly 1 block smaller than the greenhouse blocks // Exactly 1 block smaller than the greenhouse blocks
@ -506,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);
} }
@ -517,12 +515,12 @@ public class BiomeRecipeTest {
@Test @Test
public void testSpawnMobPiglin() { public void testSpawnMobPiglin() {
when(block.getY()).thenReturn(10); when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_PATH); when(block.getType()).thenReturn(Material.GRASS_BLOCK);
when(block.getRelative(any())).thenReturn(block); when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.PIGLIN; EntityType mobType = EntityType.PIGLIN;
int mobProbability = 100; int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH; Material mobSpawnOn = Material.GRASS_BLOCK;
Piglin piglin = mock(Piglin.class); Piglin piglin = mock(Piglin.class);
// Exactly 1 block smaller than the greenhouse blocks // Exactly 1 block smaller than the greenhouse blocks
@ -534,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);
} }
@ -547,12 +545,12 @@ public class BiomeRecipeTest {
when(world.getEnvironment()).thenReturn(Environment.NETHER); when(world.getEnvironment()).thenReturn(Environment.NETHER);
when(block.getY()).thenReturn(10); when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_PATH); when(block.getType()).thenReturn(Material.GRASS_BLOCK);
when(block.getRelative(any())).thenReturn(block); when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.PIGLIN; EntityType mobType = EntityType.PIGLIN;
int mobProbability = 100; int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH; Material mobSpawnOn = Material.GRASS_BLOCK;
Piglin piglin = mock(Piglin.class); Piglin piglin = mock(Piglin.class);
// Exactly 1 block smaller than the greenhouse blocks // Exactly 1 block smaller than the greenhouse blocks
@ -564,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);
} }
@ -575,12 +573,12 @@ public class BiomeRecipeTest {
@Test @Test
public void testSpawnMobWrongSurface() { public void testSpawnMobWrongSurface() {
when(block.getY()).thenReturn(10); when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_BLOCK); when(block.getType()).thenReturn(Material.STONE);
when(block.getRelative(any())).thenReturn(block); when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.CAT; EntityType mobType = EntityType.CAT;
int mobProbability = 100; int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH; Material mobSpawnOn = Material.GRASS_BLOCK;
Entity cat = mock(Cat.class); Entity cat = mock(Cat.class);
when(world.spawnEntity(any(), any())).thenReturn(cat); when(world.spawnEntity(any(), any())).thenReturn(cat);
@ -588,28 +586,9 @@ 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);
} }
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#spawnMob(org.bukkit.block.Block)}.
*/
@Test
public void testSpawnMobFailToSpawn() {
when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.GRASS_PATH);
when(block.getRelative(any())).thenReturn(block);
EntityType mobType = EntityType.CAT;
int mobProbability = 100;
Material mobSpawnOn = Material.GRASS_PATH;
br.addMobs(mobType, mobProbability, mobSpawnOn);
assertFalse(br.spawnMob(block));
verify(world).spawnEntity(eq(location), eq(EntityType.CAT));
}
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#getRecipeBlocks()}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#getRecipeBlocks()}.
*/ */
@ -632,7 +611,7 @@ public class BiomeRecipeTest {
@Test @Test
public void testGrowPlantNotAir() { public void testGrowPlantNotAir() {
when(block.getType()).thenReturn(Material.SOUL_SAND); when(block.getType()).thenReturn(Material.SOUL_SAND);
assertFalse(br.growPlant(block)); assertFalse(br.growPlant(new GrowthBlock(block, true), false));
} }
/** /**
@ -642,7 +621,7 @@ public class BiomeRecipeTest {
public void testGrowPlantNoPlants() { public void testGrowPlantNoPlants() {
when(block.getType()).thenReturn(Material.AIR); when(block.getType()).thenReturn(Material.AIR);
when(block.isEmpty()).thenReturn(true); when(block.isEmpty()).thenReturn(true);
assertFalse(br.growPlant(block)); assertFalse(br.growPlant(new GrowthBlock(block, true), false));
} }
/** /**
@ -654,7 +633,7 @@ public class BiomeRecipeTest {
when(block.getType()).thenReturn(Material.AIR); when(block.getType()).thenReturn(Material.AIR);
when(block.isEmpty()).thenReturn(true); when(block.isEmpty()).thenReturn(true);
assertTrue(br.addPlants(Material.BAMBOO_SAPLING, 100, Material.GRASS_BLOCK)); assertTrue(br.addPlants(Material.BAMBOO_SAPLING, 100, Material.GRASS_BLOCK));
assertFalse(br.growPlant(block)); assertFalse(br.growPlant(new GrowthBlock(block, true), false));
} }
/** /**
@ -670,11 +649,46 @@ public class BiomeRecipeTest {
when(block.getRelative(any())).thenReturn(ob); when(block.getRelative(any())).thenReturn(ob);
assertTrue(br.addPlants(Material.BAMBOO_SAPLING, 100, Material.GRASS_BLOCK)); assertTrue(br.addPlants(Material.BAMBOO_SAPLING, 100, Material.GRASS_BLOCK));
assertTrue(br.growPlant(block)); 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(block).setBlockData(eq(bd), eq(false)); verify(block).setBlockData(eq(bd), eq(false));
} }
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#growPlant(org.bukkit.block.Block)}.
*/
@Test
public void testGrowPlantCeilingPlants() {
when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.AIR);
when(block.isEmpty()).thenReturn(true);
Block ob = mock(Block.class);
when(ob.getType()).thenReturn(Material.GLASS);
when(block.getRelative(any())).thenReturn(ob);
assertTrue(br.addPlants(Material.SPORE_BLOSSOM, 100, Material.GLASS));
assertTrue(br.growPlant(new GrowthBlock(block, false), false));
verify(world).spawnParticle(eq(Particle.SNOWBALL), any(Location.class), anyInt(), anyDouble(), anyDouble(), anyDouble());
verify(block).setBlockData(eq(bd), eq(false));
}
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#growPlant(org.bukkit.block.Block)}.
*/
@Test
public void testGrowPlantCeilingPlantsFail() {
when(block.getY()).thenReturn(10);
when(block.getType()).thenReturn(Material.AIR);
when(block.isEmpty()).thenReturn(true);
Block ob = mock(Block.class);
when(ob.getType()).thenReturn(Material.GLASS);
when(block.getRelative(any())).thenReturn(ob);
assertTrue(br.addPlants(Material.SPORE_BLOSSOM, 100, Material.GLASS));
// Not a ceiling block
assertFalse(br.growPlant(new GrowthBlock(block, true), false));
}
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#growPlant(org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#growPlant(org.bukkit.block.Block)}.
*/ */
@ -687,13 +701,13 @@ 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(block); when(block.getRelative(BlockFace.UP)).thenReturn(block);
assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK)); assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK));
assertTrue(br.growPlant(block)); 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);
} }
/** /**
@ -708,10 +722,10 @@ 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(block)); assertFalse(br.growPlant(new GrowthBlock(block, true), false));
} }
/** /**
@ -753,8 +767,8 @@ public class BiomeRecipeTest {
*/ */
@Test @Test
public void testSetType() { public void testSetType() {
br.setType(Biome.BADLANDS_PLATEAU); br.setType(Biome.BADLANDS);
assertEquals(Biome.BADLANDS_PLATEAU, br.getBiome()); assertEquals(Biome.BADLANDS, br.getBiome());
} }
/** /**

View File

@ -1,45 +1,61 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.util.Vector;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.Settings;
import world.bentobox.greenhouses.world.AsyncWorldCache;
/** /**
* @author tastybento * @author tastybento
* *
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({Greenhouses.class, Bukkit.class})
public class RoofTest { public class RoofTest {
private Roof roof; private Roof roof;
@Mock @Mock
private Block block;
@Mock
private Location location; private Location location;
@Mock @Mock
private World world; private World world;
@Mock
private Greenhouses addon;
private Settings s;
@Mock
private AsyncWorldCache cache;
/**
* @throws java.lang.Exception
*/
@Before @Before
public void setUp() throws Exception { public void setUp() {
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true);
s = new Settings();
when(addon.getSettings()).thenReturn(s);
when(world.getMaxHeight()).thenReturn(255); when(world.getMaxHeight()).thenReturn(255);
// Block // Block
when(block.getType()).thenReturn(Material.AIR, Material.AIR, Material.AIR, Material.AIR, when(cache.getBlockType(any())).thenReturn(Material.AIR, Material.AIR, Material.AIR, Material.AIR,
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
@ -64,24 +80,22 @@ public class RoofTest {
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
Material.AIR); Material.AIR);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block);
when(world.getBlockAt(any(Location.class))).thenReturn(block);
when(location.getWorld()).thenReturn(world); when(location.getWorld()).thenReturn(world);
when(location.getBlockX()).thenReturn(10); when(location.getBlockX()).thenReturn(10);
when(location.getBlockY()).thenReturn(10); when(location.getBlockY()).thenReturn(10);
when(location.getBlockZ()).thenReturn(10); when(location.getBlockZ()).thenReturn(10);
when(location.getBlock()).thenReturn(block);
when(location.clone()).thenReturn(location); when(location.clone()).thenReturn(location);
// Test // Test
roof = new Roof(location); roof = new Roof(cache, location, addon);
assertTrue(roof.findRoof(new Vector(10,10,10)));
} }
@Test @Test
public void testNoGlass() { public void testNoGlass() {
when(block.getType()).thenReturn(Material.AIR); when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.AIR);
roof = new Roof(location); roof = new Roof(cache, location, addon);
assertFalse(roof.findRoof(new Vector(10,10,10)));
} }
/** /**
@ -124,14 +138,6 @@ public class RoofTest {
assertEquals(1406, roof.getArea()); assertEquals(1406, roof.getArea());
} }
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#isRoofFound()}.
*/
@Test
public void testIsRoofFound() {
assertTrue(roof.isRoofFound());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#getHeight()}. * Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#getHeight()}.
*/ */
@ -153,7 +159,36 @@ public class RoofTest {
*/ */
@Test @Test
public void testToString() { public void testToString() {
assertTrue(roof.toString().endsWith("minX=-9, maxX=28, minZ=-9, maxZ=29, height=14, roofFound=true]")); assertEquals("Roof [height=14, roofFound=true, minX=-9, maxX=28, minZ=-9, maxZ=29]", roof.toString());
} }
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#roofBlocks(org.bukkit.Material)}.
*/
@Test
public void testWallBlocks() {
assertFalse(roof.roofBlocks(Material.ACACIA_BOAT));
assertTrue(roof.roofBlocks(Material.GLASS));
assertTrue(roof.roofBlocks(Material.GLOWSTONE));
assertFalse(roof.roofBlocks(Material.ACACIA_DOOR));
assertTrue(roof.roofBlocks(Material.HOPPER));
assertTrue(roof.roofBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertTrue(roof.roofBlocks(Material.BIRCH_TRAPDOOR));
}
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Roof#roofBlocks(org.bukkit.Material)}.
*/
@Test
public void testWallBlocksNoGlowStoneNoPanes() {
s.setAllowGlowstone(false);
s.setAllowPanes(false);
assertFalse(roof.roofBlocks(Material.ACACIA_BOAT));
assertTrue(roof.roofBlocks(Material.GLASS));
assertFalse(roof.roofBlocks(Material.GLOWSTONE));
assertFalse(roof.roofBlocks(Material.ACACIA_DOOR));
assertTrue(roof.roofBlocks(Material.HOPPER));
assertFalse(roof.roofBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertTrue(roof.roofBlocks(Material.BIRCH_TRAPDOOR));
}
} }

View File

@ -1,39 +1,45 @@
package world.bentobox.greenhouses.greenhouse; package world.bentobox.greenhouses.greenhouse;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.After; import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.World;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.Greenhouses;
import world.bentobox.greenhouses.Settings;
import world.bentobox.greenhouses.greenhouse.Walls.WallFinder; import world.bentobox.greenhouses.greenhouse.Walls.WallFinder;
import world.bentobox.greenhouses.world.AsyncWorldCache;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
/** /**
* @author tastybento * @author tastybento
* *
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, Greenhouses.class})
public class WallsTest { public class WallsTest {
@Mock
private Roof roof; private Roof roof;
@Mock @Mock
private Block block;
@Mock
private Location location; private Location location;
@Mock @Mock
private World world; private World world;
@ -41,35 +47,40 @@ public class WallsTest {
* Class under test * Class under test
*/ */
private Walls walls; private Walls walls;
@Mock
private Greenhouses addon;
private Settings s;
@Mock
private BentoBox plugin;
@Mock
private AsyncWorldCache cache;
private CompletableFuture<Walls> r;
/**
* @throws java.lang.Exception
*/
@Before @Before
public void setUp() throws Exception { public void setUp() {
walls = new Walls(); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Tag.TRAPDOORS.isTagged(Material.BIRCH_TRAPDOOR)).thenReturn(true);
// Declare mock after mocking Bukkit
roof = mock(Roof.class);
s = new Settings();
when(addon.getSettings()).thenReturn(s);
when(addon.getPlugin()).thenReturn(plugin);
when(addon.wallBlocks(any())).thenCallRealMethod();
walls = new Walls(cache);
when(world.getMaxHeight()).thenReturn(255); when(world.getMaxHeight()).thenReturn(255);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block);
when(world.getBlockAt(any(Location.class))).thenReturn(block);
when(location.getWorld()).thenReturn(world); when(location.getWorld()).thenReturn(world);
when(location.getBlockX()).thenReturn(10); when(location.getBlockX()).thenReturn(10);
when(location.getBlockY()).thenReturn(10); when(location.getBlockY()).thenReturn(10);
when(location.getBlockZ()).thenReturn(10); when(location.getBlockZ()).thenReturn(10);
when(location.getBlock()).thenReturn(block);
when(location.clone()).thenReturn(location); when(location.clone()).thenReturn(location);
when(block.getRelative(any())).thenReturn(block); when(cache.getBlockType(any())).thenReturn(Material.GLASS);
when(block.getType()).thenReturn(Material.GLASS); when(cache.getBlockType(anyInt(),anyInt(),anyInt())).thenReturn(Material.GLASS);
when(roof.getHeight()).thenReturn(1); when(roof.getHeight()).thenReturn(1);
when(roof.getLocation()).thenReturn(location); when(roof.getLocation()).thenReturn(location);
} r = new CompletableFuture<>();
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
} }
/** /**
@ -77,7 +88,7 @@ public class WallsTest {
*/ */
@Test @Test
public void testFindWalls() { public void testFindWalls() {
walls.findWalls(roof); walls.findWalls(r, roof);
assertEquals("Walls [minX=-2, maxX=11, minZ=-2, maxZ=11, floor=0]", walls.toString()); assertEquals("Walls [minX=-2, maxX=11, minZ=-2, maxZ=11, floor=0]", walls.toString());
} }
@ -86,7 +97,7 @@ public class WallsTest {
*/ */
@Test @Test
public void testLookAround() { public void testLookAround() {
WallFinder wf = walls.new WallFinder(); WallFinder wf = new WallFinder();
walls.lookAround(location, wf, roof); walls.lookAround(location, wf, roof);
assertTrue(wf.stopMaxX); assertTrue(wf.stopMaxX);
assertTrue(wf.stopMaxZ); assertTrue(wf.stopMaxZ);
@ -103,7 +114,7 @@ public class WallsTest {
*/ */
@Test @Test
public void testAnalyzeFindings() { public void testAnalyzeFindings() {
WallFinder wf = walls.new WallFinder(); WallFinder wf = new WallFinder();
walls.analyzeFindings(wf, roof); walls.analyzeFindings(wf, roof);
assertFalse(wf.stopMaxX); assertFalse(wf.stopMaxX);
assertFalse(wf.stopMaxZ); assertFalse(wf.stopMaxZ);
@ -124,7 +135,7 @@ public class WallsTest {
walls.maxX = 1; walls.maxX = 1;
walls.minZ = -1; walls.minZ = -1;
walls.maxZ = 1; walls.maxZ = 1;
WallFinder wf = walls.new WallFinder(); WallFinder wf = new WallFinder();
walls.analyzeFindings(wf, roof); walls.analyzeFindings(wf, roof);
assertTrue(wf.stopMaxX); assertTrue(wf.stopMaxX);
assertTrue(wf.stopMaxZ); assertTrue(wf.stopMaxZ);
@ -137,12 +148,12 @@ public class WallsTest {
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#lookAtBlockFaces(world.bentobox.greenhouses.greenhouse.Walls.WallFinder, org.bukkit.World, int, int, int)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#lookAtBlockFaces(WallFinder, int, int, int)}.
*/ */
@Test @Test
public void testLookAtBlockFaces() { public void testLookAtBlockFaces() {
WallFinder wf = walls.new WallFinder(); WallFinder wf = new WallFinder();
walls.lookAtBlockFaces(wf, world, 0, 5, -1); walls.lookAtBlockFaces(wf, 0, 5, -1);
assertTrue(wf.stopMaxX); assertTrue(wf.stopMaxX);
assertTrue(wf.stopMaxZ); assertTrue(wf.stopMaxZ);
assertTrue(wf.stopMinX); assertTrue(wf.stopMinX);
@ -150,13 +161,13 @@ public class WallsTest {
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#lookAtBlockFaces(world.bentobox.greenhouses.greenhouse.Walls.WallFinder, org.bukkit.World, int, int, int)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#lookAtBlockFaces(WallFinder, int, int, int)}.
*/ */
@Test @Test
public void testLookAtBlockFacesNoGlass() { public void testLookAtBlockFacesNoGlass() {
when(block.getType()).thenReturn(Material.AIR); when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.AIR);
WallFinder wf = walls.new WallFinder(); WallFinder wf = new WallFinder();
walls.lookAtBlockFaces(wf, world, 0, 5, -1); walls.lookAtBlockFaces(wf, 0, 5, -1);
assertFalse(wf.stopMaxX); assertFalse(wf.stopMaxX);
assertFalse(wf.stopMaxZ); assertFalse(wf.stopMaxZ);
assertFalse(wf.stopMinX); assertFalse(wf.stopMinX);
@ -164,23 +175,23 @@ public class WallsTest {
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#getFloorY(org.bukkit.World, int, int, int, int, int)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#getFloorY(int, int, int, int, int, int)}.
*/ */
@Test @Test
public void testGetFloorYZeroY() { public void testGetFloorYZeroY() {
assertEquals(0, walls.getFloorY(world, 10, 0, 1, 0, 1)); assertEquals(-64, walls.getFloorY(10, 0, 1, 0, 1, -64));
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#getFloorY(org.bukkit.World, int, int, int, int, int)}. * Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#getFloorY(int, int, int, int, int, int)}.
*/ */
@Test @Test
public void testGetFloorY() { public void testGetFloorY() {
when(block.getType()).thenReturn(Material.GLASS, Material.GLASS, when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.GLASS, Material.GLASS,
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
Material.GLASS, Material.GLASS, Material.GLASS, Material.GLASS,
Material.AIR); Material.AIR);
assertEquals(8, walls.getFloorY(world, 10, 0, 1, 0, 1)); assertEquals(8, walls.getFloorY(10, 0, 1, 0, 1, -64));
} }
/** /**
@ -188,12 +199,29 @@ 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.ACACIA_DOOR)); assertTrue(addon.wallBlocks(Material.GLOWSTONE));
assertTrue(Walls.wallBlocks(Material.HOPPER)); assertTrue(addon.wallBlocks(Material.ACACIA_DOOR));
assertTrue(Walls.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE)); assertTrue(addon.wallBlocks(Material.HOPPER));
assertFalse(Walls.wallBlocks(Material.BIRCH_TRAPDOOR)); assertTrue(addon.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertFalse(addon.wallBlocks(Material.BIRCH_TRAPDOOR));
}
/**
* Test method for {@link world.bentobox.greenhouses.greenhouse.Walls#wallBlocks(org.bukkit.Material)}.
*/
@Test
public void testWallBlocksNoGlowStoneNoPanes() {
s.setAllowGlowstone(false);
s.setAllowPanes(false);
assertFalse(addon.wallBlocks(Material.ACACIA_BOAT));
assertTrue(addon.wallBlocks(Material.GLASS));
assertFalse(addon.wallBlocks(Material.GLOWSTONE));
assertTrue(addon.wallBlocks(Material.ACACIA_DOOR));
assertTrue(addon.wallBlocks(Material.HOPPER));
assertFalse(addon.wallBlocks(Material.PURPLE_STAINED_GLASS_PANE));
assertFalse(addon.wallBlocks(Material.BIRCH_TRAPDOOR));
} }
/** /**

View File

@ -1,6 +1,5 @@
package world.bentobox.greenhouses.listeners; package world.bentobox.greenhouses.listeners;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@ -10,33 +9,27 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.BoundingBox; import org.bukkit.inventory.PlayerInventory;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -83,12 +76,13 @@ public class GreenhouseEventsTest {
private Greenhouse gh1; private Greenhouse gh1;
@Mock @Mock
private Greenhouse gh2; private Greenhouse gh2;
@Mock
private PlayerInventory inv;
@Mock
private ItemStack waterBucket;
/**
* @throws java.lang.Exception
*/
@Before @Before
public void setUp() throws Exception { public void setUp() {
PowerMockito.mockStatic(User.class); PowerMockito.mockStatic(User.class);
when(User.getInstance(any(Player.class))).thenReturn(user); when(User.getInstance(any(Player.class))).thenReturn(user);
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
@ -101,8 +95,10 @@ public class GreenhouseEventsTest {
when(map.getGreenhouse(eq(location2))).thenReturn(Optional.of(gh2)); when(map.getGreenhouse(eq(location2))).thenReturn(Optional.of(gh2));
BiomeRecipe br = new BiomeRecipe(); BiomeRecipe br = new BiomeRecipe();
br.setFriendlyName("recipe1"); br.setFriendlyName("recipe1");
br.setType(Biome.PLAINS);
BiomeRecipe br2 = new BiomeRecipe(); BiomeRecipe br2 = new BiomeRecipe();
br2.setFriendlyName("recipe2"); br2.setFriendlyName("recipe2");
br2.setType(Biome.NETHER_WASTES);
// Names // Names
when(gh1.getBiomeRecipe()).thenReturn(br); when(gh1.getBiomeRecipe()).thenReturn(br);
when(gh2.getBiomeRecipe()).thenReturn(br2); when(gh2.getBiomeRecipe()).thenReturn(br2);
@ -110,6 +106,9 @@ public class GreenhouseEventsTest {
when(player.getWorld()).thenReturn(world); when(player.getWorld()).thenReturn(world);
when(world.getEnvironment()).thenReturn(Environment.NETHER); when(world.getEnvironment()).thenReturn(Environment.NETHER);
when(player.getInventory()).thenReturn(inv);
when(inv.getItemInMainHand()).thenReturn(waterBucket);
// Location // Location
when(location.getBlockX()).thenReturn(5); when(location.getBlockX()).thenReturn(5);
when(location.getBlockY()).thenReturn(15); when(location.getBlockY()).thenReturn(15);
@ -128,32 +127,57 @@ public class GreenhouseEventsTest {
} }
/** /**
* @throws java.lang.Exception * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(PlayerBucketEmptyEvent)}.
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(org.bukkit.event.player.PlayerInteractEvent)}.
*/ */
@Test @Test
public void testOnPlayerInteractInNether() { public void testOnPlayerInteractInNetherInGreenhouse() {
Block clickedBlock = mock(Block.class); Block clickedBlock = mock(Block.class);
when(clickedBlock.getLocation()).thenReturn(location); when(clickedBlock.getLocation()).thenReturn(location);
Block nextBlock = mock(Block.class); Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock); when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
when(nextBlock.getLocation()).thenReturn(location);
ItemStack item = mock(ItemStack.class); ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET); when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerInteractEvent e = new PlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST, EquipmentSlot.HAND); PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, nextBlock, clickedBlock, BlockFace.UP, Material.WATER_BUCKET, item, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e); ghe.onPlayerInteractInNether(e);
assertEquals(Result.DENY, e.useItemInHand());
verify(nextBlock).setType(Material.WATER); verify(nextBlock).setType(Material.WATER);
verify(item).setType(Material.BUCKET);
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(org.bukkit.event.player.PlayerInteractEvent)}. * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(PlayerBucketEmptyEvent)}.
*/
@Test
public void testOnPlayerInteractInNetherOutsideOfGreenhouse() {
Block clickedBlock = mock(Block.class);
when(clickedBlock.getLocation()).thenReturn(location);
Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
when(nextBlock.getLocation()).thenReturn(mock(Location.class));
ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, nextBlock, clickedBlock, BlockFace.UP, Material.WATER_BUCKET, item, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e);
verify(nextBlock, never()).setType(Material.WATER);
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(PlayerBucketEmptyEvent)}.
*/
@Test
public void testOnPlayerInteractInNetherGreenhouse() {
Block clickedBlock = mock(Block.class);
when(clickedBlock.getLocation()).thenReturn(location2);
Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, nextBlock, clickedBlock, BlockFace.UP, Material.WATER_BUCKET, item, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e);
verify(nextBlock, never()).setType(Material.WATER);
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(PlayerBucketEmptyEvent)}.
*/ */
@Test @Test
public void testOnPlayerInteractInNetherNotInNether() { public void testOnPlayerInteractInNetherNotInNether() {
@ -162,17 +186,17 @@ public class GreenhouseEventsTest {
when(clickedBlock.getLocation()).thenReturn(location); when(clickedBlock.getLocation()).thenReturn(location);
Block nextBlock = mock(Block.class); Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock); when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
when(clickedBlock.getWorld()).thenReturn(world);
when(nextBlock.getWorld()).thenReturn(world);
ItemStack item = mock(ItemStack.class); ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET); when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerInteractEvent e = new PlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST, EquipmentSlot.HAND); PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, nextBlock, clickedBlock, BlockFace.UP, Material.WATER_BUCKET, item, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e); ghe.onPlayerInteractInNether(e);
assertEquals(Result.DEFAULT, e.useItemInHand());
verify(nextBlock, never()).setType(Material.WATER); verify(nextBlock, never()).setType(Material.WATER);
verify(item, never()).setType(Material.BUCKET);
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(org.bukkit.event.player.PlayerInteractEvent)}. * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(PlayerBucketEmptyEvent)}.
*/ */
@Test @Test
public void testOnPlayerInteractInNetherNotWaterBucket() { public void testOnPlayerInteractInNetherNotWaterBucket() {
@ -182,66 +206,26 @@ public class GreenhouseEventsTest {
when(clickedBlock.getRelative(any())).thenReturn(nextBlock); when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
ItemStack item = mock(ItemStack.class); ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.ACACIA_BOAT); when(item.getType()).thenReturn(Material.ACACIA_BOAT);
PlayerInteractEvent e = new PlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST, EquipmentSlot.HAND); PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, nextBlock, clickedBlock, BlockFace.UP, Material.WATER, item, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e); ghe.onPlayerInteractInNether(e);
assertEquals(Result.DEFAULT, e.useItemInHand());
verify(nextBlock, never()).setType(Material.WATER); verify(nextBlock, never()).setType(Material.WATER);
verify(item, never()).setType(Material.BUCKET);
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(org.bukkit.event.player.PlayerInteractEvent)}. * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(PlayerBucketEmptyEvent)}.
*/ */
@Test @Test
public void testOnPlayerInteractInNetherNotInGreenhouse() { public void testOnPlayerInteractInNetherNotInGreenhouse() {
when(map.inGreenhouse(any())).thenReturn(false); when(map.getGreenhouse(eq(location))).thenReturn(Optional.empty());
Block clickedBlock = mock(Block.class); Block clickedBlock = mock(Block.class);
when(clickedBlock.getLocation()).thenReturn(location); when(clickedBlock.getLocation()).thenReturn(location);
Block nextBlock = mock(Block.class); Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock); when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
ItemStack item = mock(ItemStack.class); ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET); when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerInteractEvent e = new PlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, item, clickedBlock, BlockFace.EAST, EquipmentSlot.HAND); PlayerBucketEmptyEvent e = new PlayerBucketEmptyEvent(player, nextBlock, clickedBlock, BlockFace.UP, Material.WATER_BUCKET, item, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e); ghe.onPlayerInteractInNether(e);
assertEquals(Result.DEFAULT, e.useItemInHand());
verify(nextBlock, never()).setType(Material.WATER); verify(nextBlock, never()).setType(Material.WATER);
verify(item, never()).setType(Material.BUCKET);
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(org.bukkit.event.player.PlayerInteractEvent)}.
*/
@Test
public void testOnPlayerInteractInNetherNullItem() {
Block clickedBlock = mock(Block.class);
when(clickedBlock.getLocation()).thenReturn(location);
Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerInteractEvent e = new PlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, null, clickedBlock, BlockFace.EAST, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e);
assertEquals(Result.DEFAULT, e.useItemInHand());
verify(nextBlock, never()).setType(Material.WATER);
verify(item, never()).setType(Material.BUCKET);
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerInteractInNether(org.bukkit.event.player.PlayerInteractEvent)}.
*/
@Test
public void testOnPlayerInteractInNetherNotBlockClick() {
Block clickedBlock = mock(Block.class);
when(clickedBlock.getLocation()).thenReturn(location);
Block nextBlock = mock(Block.class);
when(clickedBlock.getRelative(any())).thenReturn(nextBlock);
ItemStack item = mock(ItemStack.class);
when(item.getType()).thenReturn(Material.WATER_BUCKET);
PlayerInteractEvent e = new PlayerInteractEvent(player, Action.RIGHT_CLICK_AIR, item, clickedBlock, BlockFace.EAST, EquipmentSlot.HAND);
ghe.onPlayerInteractInNether(e);
assertEquals(Result.DEFAULT, e.useItemInHand());
verify(nextBlock, never()).setType(Material.WATER);
verify(item, never()).setType(Material.BUCKET);
} }
/** /**
@ -254,12 +238,30 @@ public class GreenhouseEventsTest {
Block block = mock(Block.class); Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.ICE); when(block.getType()).thenReturn(Material.ICE);
when(block.getWorld()).thenReturn(world); when(block.getWorld()).thenReturn(world);
when(block.getLocation()).thenReturn(location);
BlockBreakEvent e = new BlockBreakEvent(block, player); BlockBreakEvent e = new BlockBreakEvent(block, player);
ghe.onIceBreak(e); ghe.onIceBreak(e);
verify(block).setType(Material.WATER); verify(block).setType(Material.WATER);
assertTrue(e.isCancelled()); assertTrue(e.isCancelled());
} }
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}.
*/
@Test
public void testOnIceBreakNetherBiomeGreenhouse() {
when(Tag.ICE.isTagged(any(Material.class))).thenReturn(true);
Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.ICE);
when(block.getWorld()).thenReturn(world);
when(block.getLocation()).thenReturn(location2);
BlockBreakEvent e = new BlockBreakEvent(block, player);
ghe.onIceBreak(e);
verify(block, never()).setType(Material.WATER);
assertFalse(e.isCancelled());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}. * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}.
*/ */
@ -270,6 +272,8 @@ public class GreenhouseEventsTest {
Block block = mock(Block.class); Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.ACACIA_BOAT); when(block.getType()).thenReturn(Material.ACACIA_BOAT);
when(block.getWorld()).thenReturn(world); when(block.getWorld()).thenReturn(world);
// Nether gh
when(block.getLocation()).thenReturn(location2);
BlockBreakEvent e = new BlockBreakEvent(block, player); BlockBreakEvent e = new BlockBreakEvent(block, player);
ghe.onIceBreak(e); ghe.onIceBreak(e);
verify(block, never()).setType(Material.WATER); verify(block, never()).setType(Material.WATER);
@ -280,19 +284,42 @@ public class GreenhouseEventsTest {
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}. * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}.
*/ */
@Test @Test
public void testOnIceBreakNotNether() { public void testOnIceBreakNotNetherNetherGreenhouse() {
when(world.getEnvironment()).thenReturn(Environment.THE_END); when(world.getEnvironment()).thenReturn(Environment.THE_END);
when(Tag.ICE.isTagged(any(Material.class))).thenReturn(true); when(Tag.ICE.isTagged(any(Material.class))).thenReturn(true);
Block block = mock(Block.class); Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.ICE); when(block.getType()).thenReturn(Material.ICE);
when(block.getWorld()).thenReturn(world); when(block.getWorld()).thenReturn(world);
// Nether gh
when(block.getLocation()).thenReturn(location2);
BlockBreakEvent e = new BlockBreakEvent(block, player); BlockBreakEvent e = new BlockBreakEvent(block, player);
ghe.onIceBreak(e); ghe.onIceBreak(e);
verify(block, never()).setType(Material.WATER); verify(block).setType(Material.AIR);
assertFalse(e.isCancelled()); assertTrue(e.isCancelled());
verify(world).playSound(any(Location.class), eq(Sound.BLOCK_GLASS_BREAK), eq(1F), eq(1F));
} }
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}.
*/
@Test
public void testOnIceBreakNotNetherNotNetherGreenhouse() {
when(world.getEnvironment()).thenReturn(Environment.THE_END);
when(Tag.ICE.isTagged(any(Material.class))).thenReturn(true);
Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.ICE);
when(block.getWorld()).thenReturn(world);
when(block.getLocation()).thenReturn(location);
BlockBreakEvent e = new BlockBreakEvent(block, player);
ghe.onIceBreak(e);
assertFalse(e.isCancelled());
verify(block, never()).setType(any());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}. * Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onIceBreak(org.bukkit.event.block.BlockBreakEvent)}.
*/ */
@ -304,6 +331,7 @@ public class GreenhouseEventsTest {
Block block = mock(Block.class); Block block = mock(Block.class);
when(block.getType()).thenReturn(Material.ICE); when(block.getType()).thenReturn(Material.ICE);
when(block.getWorld()).thenReturn(world); when(block.getWorld()).thenReturn(world);
BlockBreakEvent e = new BlockBreakEvent(block, player); BlockBreakEvent e = new BlockBreakEvent(block, player);
ghe.onIceBreak(e); ghe.onIceBreak(e);
verify(block, never()).setType(Material.WATER); verify(block, never()).setType(Material.WATER);
@ -389,8 +417,7 @@ public class GreenhouseEventsTest {
*/ */
@Test @Test
public void testOnBlockBreak() { public void testOnBlockBreak() {
BoundingBox bb = BoundingBox.of(location, location2); when(gh1.isRoofOrWallBlock(any())).thenReturn(true);
when(gh1.getBoundingBox()).thenReturn(bb);
// Location is a wall block // Location is a wall block
Block block = mock(Block.class); Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location); when(block.getLocation()).thenReturn(location);
@ -400,91 +427,4 @@ public class GreenhouseEventsTest {
verify(gm).removeGreenhouse(any()); verify(gm).removeGreenhouse(any());
} }
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}.
*/
@Test
public void testOnPlayerBlockPlace() {
Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location);
when(block.getY()).thenReturn(255);
when(block.getWorld()).thenReturn(world);
when(world.getEnvironment()).thenReturn(Environment.NORMAL);
BlockState bs = mock(BlockState.class);
Block pa = mock(Block.class);
ItemStack item = mock(ItemStack.class);
BlockPlaceEvent e = new BlockPlaceEvent(block, bs, pa, item, player, true, EquipmentSlot.HAND);
ghe.onPlayerBlockPlace(e);
assertTrue(e.isCancelled());
verify(user).sendMessage(eq("greenhouses.error.cannot-place"));
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}.
*/
@Test
public void testOnPlayerBlockPlaceNether() {
Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location);
when(block.getY()).thenReturn(255);
when(block.getWorld()).thenReturn(world);
when(world.getEnvironment()).thenReturn(Environment.NETHER);
BlockState bs = mock(BlockState.class);
Block pa = mock(Block.class);
ItemStack item = mock(ItemStack.class);
BlockPlaceEvent e = new BlockPlaceEvent(block, bs, pa, item, player, true, EquipmentSlot.HAND);
ghe.onPlayerBlockPlace(e);
assertFalse(e.isCancelled());
verify(user, never()).sendMessage(eq("greenhouses.error.cannot-place"));
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPlayerBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}.
*/
@Test
public void testOnPlayerBlockPlaceBelowGH() {
Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location);
when(block.getY()).thenReturn(0);
when(block.getWorld()).thenReturn(world);
when(world.getEnvironment()).thenReturn(Environment.NORMAL);
BlockState bs = mock(BlockState.class);
Block pa = mock(Block.class);
ItemStack item = mock(ItemStack.class);
BlockPlaceEvent e = new BlockPlaceEvent(block, bs, pa, item, player, true, EquipmentSlot.HAND);
ghe.onPlayerBlockPlace(e);
assertFalse(e.isCancelled());
verify(user, never()).sendMessage(eq("greenhouses.error.cannot-place"));
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPistonPush(org.bukkit.event.block.BlockPistonExtendEvent)}.
*/
@Test
public void testOnPistonPush() {
Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location);
when(block.getY()).thenReturn(255);
when(block.getWorld()).thenReturn(world);
when(world.getEnvironment()).thenReturn(Environment.NORMAL);
BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, Collections.singletonList(block), BlockFace.EAST);
ghe.onPistonPush(e);
assertTrue(e.isCancelled());
}
/**
* Test method for {@link world.bentobox.greenhouses.listeners.GreenhouseEvents#onPistonPush(org.bukkit.event.block.BlockPistonExtendEvent)}.
*/
@Test
public void testOnPistonPushUnderGH() {
Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location);
when(block.getY()).thenReturn(0);
when(block.getWorld()).thenReturn(world);
when(world.getEnvironment()).thenReturn(Environment.NORMAL);
BlockPistonExtendEvent e = new BlockPistonExtendEvent(block, Collections.singletonList(block), BlockFace.EAST);
ghe.onPistonPush(e);
assertFalse(e.isCancelled());
}
} }

View File

@ -1,41 +1,49 @@
/**
*
*/
package world.bentobox.greenhouses.managers; package world.bentobox.greenhouses.managers;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.Location;
import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.util.BoundingBox; import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.greenhouses.data.Greenhouse; import world.bentobox.greenhouses.data.Greenhouse;
import world.bentobox.greenhouses.greenhouse.BiomeRecipe;
import world.bentobox.greenhouses.managers.EcoSystemManager.GrowthBlock;
/** /**
* @author tastybento * @author tastybento
* *
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, BentoBox.class}) @PrepareForTest({Bukkit.class, BentoBox.class, Tag.class, RecipeManager.class})
public class EcoSystemManagerTest { public class EcoSystemManagerTest {
@Mock
private Greenhouse gh; private Greenhouse gh;
@Mock @Mock
private World world; private World world;
@ -47,119 +55,138 @@ public class EcoSystemManagerTest {
private Block liquid; private Block liquid;
@Mock @Mock
private Block plant; private Block plant;
@Mock
private BiomeRecipe recipe;
// CUT // CUT
private EcoSystemManager eco; private EcoSystemManager eco;
/** /**
* @throws java.lang.Exception
*/ */
@Before @Before
public void setUp() throws Exception { public void setUp() {
PowerMockito.mockStatic(Tag.class, Mockito.RETURNS_MOCKS);
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
@SuppressWarnings("unchecked")
Tag<Keyed> tag = mock(Tag.class);
when(Bukkit.getTag(anyString(), any(), any())).thenReturn(tag);
gh = new Greenhouse();
// 4x4x4 greenhouse // 4x4x4 greenhouse
BoundingBox bb = BoundingBox.of(new Vector(0,0,0), new Vector(5,5,5)); BoundingBox bb = BoundingBox.of(new Vector(0,0,0), new Vector(6,5,6));
when(gh.getBoundingBox()).thenReturn(bb); gh.setBoundingBox(bb);
// World // World
when(gh.getWorld()).thenReturn(world); Location l = new Location(world, 0,0,0);
gh.setLocation(l);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block); when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block);
// Blocks // Blocks
// Air // Air
// 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
when(recipe.noMobs()).thenReturn(true);
PowerMockito.mockStatic(RecipeManager.class, Mockito.RETURNS_MOCKS);
when(RecipeManager.getBiomeRecipies(any())).thenReturn(Optional.of(recipe));
eco = new EcoSystemManager(null, null); eco = new EcoSystemManager(null, null);
} }
/** /**
* @throws java.lang.Exception * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}.
*/ */
@Test @Test
public void testGetAvailableBlocksAirAboveBlock() { public void testGetAvailableBlocksAirAboveBlock() {
List<Block> result = eco.getAvailableBlocks(gh, false); List<GrowthBlock> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size()); assertEquals(16, result.size());
assertEquals(air, result.get(0)); assertEquals(air, result.get(0).block());
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksPlantAboveBlock() { public void testGetAvailableBlocksPlantAboveBlock() {
when(block.getRelative(eq(BlockFace.UP))).thenReturn(plant); when(block.getRelative(eq(BlockFace.UP))).thenReturn(plant);
List<Block> result = eco.getAvailableBlocks(gh, false); List<GrowthBlock> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size()); assertEquals(16, result.size());
assertEquals(plant, result.get(0)); assertEquals(plant, result.get(0).block());
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksAllAir() { public void testGetAvailableBlocksAllAir() {
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(air); when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(air);
List<Block> result = eco.getAvailableBlocks(gh, false); List<GrowthBlock> result = eco.getAvailableBlocks(gh, false);
assertEquals(0, result.size()); assertEquals(0, result.size());
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksAllLiquid() { public void testGetAvailableBlocksAllLiquid() {
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(liquid); when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(liquid); when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, false); List<GrowthBlock> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/
@Test
public void testGetAvailableBlocksAllLiquid2() {
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(liquid);
List<GrowthBlock> result = eco.getAvailableBlocks(gh, true);
assertEquals(0, result.size()); assertEquals(0, result.size());
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksAllPlant() { public void testGetAvailableBlocksAllPlant() {
when(plant.getRelative(eq(BlockFace.UP))).thenReturn(plant); when(plant.getRelative(eq(BlockFace.UP))).thenReturn(plant);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(plant); when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(plant);
List<Block> result = eco.getAvailableBlocks(gh, false); List<GrowthBlock> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size()); assertEquals(16, result.size());
assertEquals(plant, result.get(0)); assertEquals(plant, result.get(0).block());
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksLiquidAboveBlockIgnoreLiquids() { public void testGetAvailableBlocksLiquidAboveBlockIgnoreLiquids() {
when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid); when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, true); List<GrowthBlock> result = eco.getAvailableBlocks(gh, true);
assertEquals(16, result.size()); assertEquals(16, result.size());
assertEquals(liquid, result.get(0)); assertEquals(liquid, result.get(0).block());
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksAirAboveLiquidNotIgnoreLiquids() { public void testGetAvailableBlocksAirAboveLiquidNotIgnoreLiquids() {
@ -169,15 +196,15 @@ public class EcoSystemManagerTest {
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(air); when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(air);
when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid); when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, false); List<GrowthBlock> result = eco.getAvailableBlocks(gh, false);
assertEquals(16, result.size()); assertEquals(16, result.size());
for (int i = 0; i< result.size(); i++) { for (GrowthBlock value : result) {
assertEquals(air, result.get(i)); assertEquals(air, value.block());
} }
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(world.bentobox.greenhouses.data.Greenhouse)}. * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}.
*/ */
@Test @Test
public void testGetAvailableBlocksAirAboveLiquidIgnoreLiquids() { public void testGetAvailableBlocksAirAboveLiquidIgnoreLiquids() {
@ -187,10 +214,63 @@ public class EcoSystemManagerTest {
when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(air); when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(air);
when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid); when(block.getRelative(eq(BlockFace.UP))).thenReturn(liquid);
List<Block> result = eco.getAvailableBlocks(gh, true); List<GrowthBlock> result = eco.getAvailableBlocks(gh, true);
assertEquals(16, result.size()); assertEquals(16, result.size());
for (int i = 0; i< result.size(); i++) { for (GrowthBlock value : result) {
assertEquals(liquid, result.get(i)); assertEquals(liquid, value.block());
} }
} }
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#addMobs(Greenhouse)}.
*/
@Test
public void testAddMobsChunkNotLoaded() {
assertFalse(eco.addMobs(gh));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#addMobs(Greenhouse)}.
*/
@Test
public void testAddMobsChunkLoadedNoMobs() {
when(world.isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
assertFalse(eco.addMobs(gh));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#addMobs(Greenhouse)}.
*/
@Test
public void testAddMobsChunkLoadedWithMobsInRecipeMaxMobsZero() {
when(world.isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
when(recipe.noMobs()).thenReturn(false);
assertFalse(eco.addMobs(gh));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#addMobs(Greenhouse)}.
*/
@Test
public void testAddMobsChunkLoadedWithMobsInRecipeMaxMobsNotZero() {
// Nothing spawned here
when(world.isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
when(recipe.noMobs()).thenReturn(false);
when(recipe.getMaxMob()).thenReturn(10);
assertFalse(eco.addMobs(gh));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#addMobs(Greenhouse)}.
*/
@Test
public void testAddMobsSpawnMob() {
// Nothing spawned here
when(world.isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
when(recipe.noMobs()).thenReturn(false);
when(recipe.getMaxMob()).thenReturn(10);
when(recipe.spawnMob(any())).thenReturn(true);
assertTrue(eco.addMobs(gh));
}
} }

View File

@ -2,15 +2,14 @@ package world.bentobox.greenhouses.managers;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
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.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.Collection; import java.util.Collection;
import java.util.Set;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -18,7 +17,7 @@ import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.Block; import org.bukkit.util.Vector;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -35,6 +34,7 @@ import world.bentobox.greenhouses.greenhouse.Roof;
import world.bentobox.greenhouses.greenhouse.Walls; import world.bentobox.greenhouses.greenhouse.Walls;
import world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck; import world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck;
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult; import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
import world.bentobox.greenhouses.world.AsyncWorldCache;
/** /**
* @author tastybento * @author tastybento
@ -54,34 +54,34 @@ public class GreenhouseFinderTest {
private Location location; private Location location;
// Class under test // Class under test
private GreenhouseFinder gf; private GreenhouseFinder gf;
@Mock
private Block block;
private CounterCheck cc; private CounterCheck cc;
@Mock
private Roof roof; private Roof roof;
@Mock @Mock
private Walls walls; private Walls walls;
@Mock
private AsyncWorldCache cache;
/** /**
* @throws java.lang.Exception
*/ */
@Before @Before
public void setUp() throws Exception { 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);
// Declare mock after mocking Bukkit
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(block.getX()).thenReturn(5); when(cache.getBlockType(any())).thenReturn(Material.GLASS);
when(block.getY()).thenReturn(14); when(cache.getBlockType(anyInt(), anyInt(), anyInt())).thenReturn(Material.GLASS);
when(block.getZ()).thenReturn(25);
when(block.getType()).thenReturn(Material.GLASS);
when(block.getLocation()).thenReturn(location);
when(block.getWorld()).thenReturn(world);
// Roof // Roof
when(roof.getHeight()).thenReturn(ROOF_HEIGHT); when(roof.getHeight()).thenReturn(ROOF_HEIGHT);
when(walls.getMinX()).thenReturn(5); when(walls.getMinX()).thenReturn(5);
@ -92,26 +92,25 @@ public class GreenhouseFinderTest {
when(roof.getLocation()).thenReturn(location); when(roof.getLocation()).thenReturn(location);
// World // World
when(world.getEnvironment()).thenReturn(Environment.NORMAL); when(cache.getEnvironment()).thenReturn(Environment.NORMAL);
when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(block); when(cache.getMaxHeight()).thenReturn(30);
when(world.getMaxHeight()).thenReturn(30);
gf = new GreenhouseFinder(); gf = new GreenhouseFinder(addon);
cc = gf.new CounterCheck(); cc = new CounterCheck();
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkGreenhouse(world.bentobox.greenhouses.data.Greenhouse, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls)}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkGreenhouse(AsyncWorldCache, Roof, Walls)}.
*/ */
@Test @Test
public void testCheckGreenhouse() { public void testCheckGreenhouse() {
Greenhouse gh2 = new Greenhouse(world, walls, ROOF_HEIGHT); gf.checkGreenhouse(cache, roof, walls).thenAccept(result -> {
Set<GreenhouseResult> result = gf.checkGreenhouse(gh2, roof, walls); assertTrue(result.isEmpty()); // Success
assertTrue(result.isEmpty()); // Success assertEquals(441, gf.getWallBlockCount());
assertEquals(441, gf.getWallBlockCount()); assertEquals(0, gf.getWallDoors());
assertEquals(0, gf.getWallDoors()); assertEquals(0, gf.getGhHopper());
assertEquals(0, gf.getGhHopper()); });
} }
/** /**
@ -180,188 +179,77 @@ public class GreenhouseFinderTest {
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkBlock(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkBlock(CounterCheck, Material, Roof, Walls, Vector)}
*/
@Test
public void testCheckBlock() {
// Block has to be > roof height
when(block.getY()).thenReturn(ROOF_HEIGHT + 1);
Set<GreenhouseResult> result = gf.checkBlock(cc, roof, walls, block);
result.forEach(gr -> assertEquals(GreenhouseResult.FAIL_BLOCKS_ABOVE, gr));
gf.getRedGlass().forEach(l -> assertEquals(location, l));
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkBlock(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, org.bukkit.block.Block)}.
*/ */
@Test @Test
public void testCheckBlockRoofHeight() { public void testCheckBlockRoofHeight() {
// Block has to be > roof height // Glass block should be ok at roof height
when(block.getY()).thenReturn(ROOF_HEIGHT); assertTrue(gf.checkBlock(cc, Material.GLASS, roof, walls, new Vector(0, ROOF_HEIGHT, 0)));
Set<GreenhouseResult> result = gf.checkBlock(cc, roof, walls, block);
assertTrue(result.isEmpty());
} }
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkBlock(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, org.bukkit.block.Block)}.
*/
@Test
public void testCheckBlockNether() {
when(world.getEnvironment()).thenReturn(Environment.NETHER);
// Block has to be > roof height
when(block.getY()).thenReturn(ROOF_HEIGHT + 1);
Set<GreenhouseResult> result = gf.checkBlock(cc, roof, walls, block);
assertTrue(result.isEmpty());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkBlock(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkBlock(CounterCheck, Material, Roof, Walls, Vector)}
*/ */
@Test @Test
public void testCheckBlockAir() { public void testCheckBlockAir() {
when(block.isEmpty()).thenReturn(true); // Glass air should be not allowed at roof height
// Block has to be > roof height assertFalse(gf.checkBlock(cc, Material.AIR, roof, walls, new Vector(0, ROOF_HEIGHT, 0)));
when(block.getY()).thenReturn(ROOF_HEIGHT + 1);
Set<GreenhouseResult> result = gf.checkBlock(cc, roof, walls, block);
assertTrue(result.isEmpty());
} }
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkWalls(org.bukkit.block.Block, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck)}.
*/
@Test
public void testCheckWallsAirHole() {
// Make block AIR
when(block.isEmpty()).thenReturn(true);
when(block.getType()).thenReturn(Material.AIR);
assertTrue(gf.checkWalls(block, roof, walls, cc));
assertFalse(gf.getRedGlass().isEmpty());
gf.getRedGlass().forEach(l -> assertEquals(location, l));
assertTrue(cc.airHole);
assertFalse(gf.isInCeiling());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkWalls(org.bukkit.block.Block, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck)}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(CounterCheck, Material, Vector)}
*/
@Test
public void testCheckWallsAirHoleInRoof() {
// Make block AIR
when(block.isEmpty()).thenReturn(true);
when(block.getType()).thenReturn(Material.AIR);
when(block.getY()).thenReturn(ROOF_HEIGHT);
assertTrue(gf.checkWalls(block, roof, walls, cc));
assertFalse(gf.getRedGlass().isEmpty());
gf.getRedGlass().stream().forEach(l -> assertEquals(location, l));
assertTrue(cc.airHole);
assertTrue(gf.isInCeiling());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkWalls(org.bukkit.block.Block, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck)}.
*/
@Test
public void testCheckWalls() {
// Make block GLASS
when(block.isEmpty()).thenReturn(false);
when(block.getType()).thenReturn(Material.GLASS);
assertTrue(gf.checkWalls(block, roof, walls, cc));
assertTrue(gf.getRedGlass().isEmpty());
assertFalse(cc.airHole);
assertFalse(gf.isInCeiling());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkWalls(org.bukkit.block.Block, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck)}.
*/
@Test
public void testCheckWallsInRoof() {
// Make block GLASS
when(block.isEmpty()).thenReturn(false);
when(block.getType()).thenReturn(Material.GLASS);
when(block.getY()).thenReturn(ROOF_HEIGHT);
assertTrue(gf.checkWalls(block, roof, walls, cc));
assertTrue(gf.getRedGlass().isEmpty());
assertFalse(cc.airHole);
assertFalse(gf.isInCeiling());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkWalls(org.bukkit.block.Block, world.bentobox.greenhouses.greenhouse.Roof, world.bentobox.greenhouses.greenhouse.Walls, world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck)}.
*/
@Test
public void testCheckWallsNotInWall() {
when(block.getX()).thenReturn(0);
when(block.getY()).thenReturn(0);
when(block.getZ()).thenReturn(0);
// Make block GLASS
when(block.isEmpty()).thenReturn(false);
when(block.getType()).thenReturn(Material.GLASS);
assertFalse(gf.checkWalls(block, roof, walls, cc));
assertTrue(gf.getRedGlass().isEmpty());
assertFalse(cc.airHole);
assertFalse(gf.isInCeiling());
}
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, org.bukkit.block.Block)}.
*/ */
@Test @Test
public void testCheckDoorsHoppers() { public void testCheckDoorsHoppers() {
// Doors are 2 blocks
when(Tag.DOORS.isTagged(any(Material.class))).thenReturn(true); when(Tag.DOORS.isTagged(any(Material.class))).thenReturn(true);
when(block.getType()).thenReturn(Material.ACACIA_DOOR); for (int i = 0; i < 8; i++) {
gf.checkDoorsHoppers(cc, block); assertTrue("Door number " + i, gf.checkDoorsHoppers(cc, Material.ACACIA_DOOR, new Vector(0,0,0)));
assertTrue(gf.getRedGlass().isEmpty()); }
// 5th door will fail
assertFalse(gf.checkDoorsHoppers(cc, Material.ACACIA_DOOR, new Vector(0,0,0)));
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(CounterCheck, Material, Vector)}
*/ */
@Test @Test
public void testCheckDoorsHoppersTooManyDoors() { public void testCheckDoorsHoppersTrapdoors() {
gf.setWallDoors(8); // Trapdoors are one block
when(Tag.DOORS.isTagged(any(Material.class))).thenReturn(true); for (int i = 0; i < 4; i++) {
when(block.getType()).thenReturn(Material.ACACIA_DOOR); assertTrue("Door number " + i, gf.checkDoorsHoppers(cc, Material.BIRCH_TRAPDOOR, new Vector(0,0,0)));
CounterCheck cc = gf.new CounterCheck(); }
gf.checkDoorsHoppers(cc, block); // 5th door will fail
assertFalse(gf.getRedGlass().isEmpty()); assertFalse(gf.checkDoorsHoppers(cc, Material.BIRCH_TRAPDOOR, new Vector(0,0,0)));
} }
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, org.bukkit.block.Block)}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(CounterCheck, Material, Vector)}.
*/ */
@Test @Test
public void testCheckDoorsHoppersHopper() { public void testCheckDoorsHoppersHopper() {
Greenhouse gh = new Greenhouse(world, walls, 10);
// Set the greenhouse so the world is known
gf.setGh(gh);
when(Tag.DOORS.isTagged(any(Material.class))).thenReturn(false); when(Tag.DOORS.isTagged(any(Material.class))).thenReturn(false);
when(block.getType()).thenReturn(Material.HOPPER); CounterCheck cc = new CounterCheck();
when(block.getLocation()).thenReturn(location); assertTrue(gf.checkDoorsHoppers(cc, Material.HOPPER, new Vector(5,14,25)));
CounterCheck cc = gf.new CounterCheck();
gf.checkDoorsHoppers(cc, block);
assertTrue(gf.getRedGlass().isEmpty()); assertTrue(gf.getRedGlass().isEmpty());
assertEquals(location, gf.getGh().getRoofHopperLocation()); assertEquals(5, gf.getGh().getRoofHopperLocation().getBlockX());
assertEquals(14, gf.getGh().getRoofHopperLocation().getBlockY());
assertEquals(25, gf.getGh().getRoofHopperLocation().getBlockZ());
assertFalse(gf.checkDoorsHoppers(cc, Material.HOPPER, new Vector(5,14,25)));
} }
/**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#checkDoorsHoppers(world.bentobox.greenhouses.managers.GreenhouseFinder.CounterCheck, org.bukkit.block.Block)}.
*/
@Test
public void testCheckDoorsHoppersTooManyHoppers() {
gf.setGhHopper(3);
when(Tag.DOORS.isTagged(any(Material.class))).thenReturn(false);
when(block.getType()).thenReturn(Material.HOPPER);
when(block.getLocation()).thenReturn(location);
CounterCheck cc = gf.new CounterCheck();
gf.checkDoorsHoppers(cc, block);
assertFalse(gf.getRedGlass().isEmpty());
assertNull(gf.getGh().getRoofHopperLocation());
}
/** /**
* Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#getGh()}. * Test method for {@link world.bentobox.greenhouses.managers.GreenhouseFinder#getGh()}.
*/ */
@Test @Test
public void testGetGh() { public void testGetGh() {
assertNotNull(gf.getGh()); assertNull(gf.getGh());
} }
/** /**