Compare commits

...

144 Commits

Author SHA1 Message Date
tastybento
e7563b2f1c
Merge pull request #333 from BentoBoxWorld/332_non-items_in_the_level_value_menu
Fixes an issue where items can be a block but not an item #332
2024-09-18 14:50:32 -07:00
tastybento
db2ebac45d Remove unused import 2024-09-18 14:50:13 -07:00
tastybento
b66ecb23d1 Fixes an issue where items can be a block but not an item #332 2024-09-18 14:49:29 -07:00
tastybento
58bcf9e08b Version 2.16.2 2024-09-18 14:32:54 -07:00
tastybento
2c21c8da71 Version 2.16.1 2024-09-16 20:42:16 -07:00
tastybento
10d1709c6f Skip null worlds that have not been loaded but in database. 2024-09-16 20:41:06 -07:00
tastybento
dbf3621cee
Merge pull request #330 from BentoBoxWorld/329_Add_+/-_option_to_handicap_setting
Adds a +/- tp the sethandicap admin command #329
2024-09-16 19:08:41 -07:00
tastybento
09c83fa48e Adds a +/- tp the sethandicap admin command #329 2024-09-16 19:08:26 -07:00
tastybento
8caf0c45c8
Merge pull request #327 from BentoBoxWorld/custom_units
Custom units
2024-08-29 08:25:10 -07:00
tastybento
a09f68e8af Custom units for shorthand levels added to config.yml 2024-08-29 08:24:55 -07:00
tastybento
48fbd4b38d Version 2.16.0 2024-08-29 08:24:38 -07:00
tastybento
e2a62c17ed Fix POM and update API 2024-08-21 08:19:11 -07:00
tastybento
2d0382a14b Updated comments on tipped arrow 2024-08-21 07:39:06 -07:00
tastybento
4a5f967706
Merge pull request #326 from BentoBoxWorld/324_hide_in_values_panel
Use hidden blocks in values panel #324
2024-08-06 07:17:24 -07:00
tastybento
95a119c7cd Use hidden blocks in values panel #324 2024-08-06 07:16:50 -07:00
tastybento
7c98b17073
Merge pull request #325 from BentoBoxWorld/324_hide_blocks
Add the ability to hide blocks from the GUI #324
2024-08-05 17:41:24 -07:00
tastybento
b9b5668fb9 Add the ability to hide blocks from the GUI #324 2024-08-01 14:00:49 -07:00
tastybento
61719cdf3a
Merge pull request #322 from BentoBoxWorld/273_show_placed_and_limit_in_value_hand
Adds the number placed and limit to the value hand command #273
2024-07-20 10:36:00 -07:00
tastybento
fb107038ca Adds the number placed and limit to the value hand command #273 2024-07-20 10:15:08 -07:00
tastybento
1d1623813c
Merge pull request #321 from BentoBoxWorld/302_filter_on_value
Makes the default block tab show blocks with value only #302
2024-07-20 09:32:10 -07:00
tastybento
8255073706 Makes the default block tab show blocks with value only #302 2024-07-20 09:23:09 -07:00
tastybento
8636239e90
Merge pull request #320 from BentoBoxWorld/311_Request_for_Additional_Placeholders
311 request for additional placeholders
2024-07-20 08:48:09 -07:00
tastybento
92a4560879 Add placeholder that only gives level value for the owner #311 2024-07-20 08:40:30 -07:00
tastybento
a42d5c90a1 Version 2.15.0 2024-07-20 08:40:09 -07:00
tastybento
972b65847c Version 2.14.1 2024-07-05 10:31:41 -07:00
tastybento
b641d0f450 Return pladdon that was made. 2024-07-02 09:32:35 -07:00
tastybento
c0653790b1 Test fixes 2024-06-30 14:22:33 -07:00
tastybento
e374dbd1c1
Merge branch 'master' into merge-resolutions 2024-06-30 14:13:24 -07:00
Minecraft_15
7e8392d4f0
Update zh-CN.yml (#315)
* Delete src/main/resources/locales/zh-CN.yml

* Update zh-CN.yml

Update language files to adapt to the latest version of Bentobox-Level
2024-06-08 08:51:17 -07:00
tastybento
00f6fee1bf Remove unneeded import and reference to MultiLib 2024-06-01 10:32:11 -07:00
tastybento
f2da5ba104 Use new BentoBox API to avoid loading islands into cache on startup
Relates to #312
2024-06-01 08:40:29 -07:00
tastybento
c3e03a4f59 Uses better approach #313
This uses CompleteableFutures instead of a recurring Bukkit task to
check if collections have been removed. This is a much more reliable way
to do it because it will complete when all the tasks are done and not
before.
2024-05-31 17:59:56 -07:00
tastybento
7a241f898d Fix tests to run with latest BentoBox 2024-05-31 17:13:05 -07:00
tastybento
d37f9ddcdd Version 2.14.0 2024-05-31 16:17:05 -07:00
tastybento
0bb6eacaf7
Update IslandLevelCalculator.java (#314)
Fix for occasional errors likely due to the use of the remove() method within a lambda expression inside the thenAccept method. This lambda expression is executed asynchronously, which means that the iterator may not be in a consistent state when remove() is called.
2024-05-31 13:11:57 -07:00
tastybento
3983764353
Release 2.13.0 (#310)
* Update tipped arrows in GUI Panel

* Version 2.13.0

* Add more string replacements for /is level output (#303)

* Update hooks and fix UltimateStacker API (#305)

* Isolate UltimateStacker imports so no errors if US is not installed (#307) Fixes #306

* Implement a cache for top tens (#309)
2024-05-05 13:40:14 -07:00
tastybento
5dee0d2426 Fix space in locale file 2024-05-05 13:37:24 -07:00
tastybento
1369ffb8c8 Remove unused imports. 2024-05-05 13:34:25 -07:00
tastybento
e2cbd18f17
Merge branch 'master' into develop 2024-05-05 13:32:47 -07:00
tastybento
4d16e9c227
Implement a cache for top tens (#309) 2024-05-04 12:36:03 -07:00
tastybento
a4f8d12138
Isolate UltimateStacker imports so no errors if US is not installed (#307)
Fixes #306
2024-04-20 09:50:17 -07:00
Huynh Tien
1bd6219f94
Update hooks and fix UltimateStacker API (#305) 2024-04-19 07:31:23 -07:00
add5tar
2d1f618676
Add more string replacements for /is level output (#303)
* Add more string replacements to output

* Forgot to include the locale change
2024-04-16 17:55:17 -07:00
tastybento
774bbd034c Version 2.13.0 2024-02-03 08:28:44 -08:00
tastybento
02a19d1bdb Update tipped arrows in GUI Panel 2024-01-21 09:08:35 -08:00
tastybento
952a2a6e81 Version 2.12.1 2024-01-21 09:08:30 -08:00
tastybento
79f988f460 Fix merge error 2024-01-13 19:48:14 -08:00
tastybento
ae030eb548
Merge branch 'master' into develop 2024-01-13 19:45:40 -08:00
tastybento
76e0bad88a Added test class for EquationEvaluator 2024-01-13 08:50:37 -08:00
tastybento
2b373f62d9
Uses latest Visit API to avoid chat spam. Fixes #299 (#300) 2024-01-02 14:19:16 +09:00
tastybento
5f83a81f18 Add protection around unknown blockconfig.yml entries. GRASS>SHORT_GRASS 2024-01-02 11:24:07 +09:00
tastybento
82174649b4 Added comments on the panel templates. 2023-12-18 17:57:36 -08:00
tastybento
cec620162b Move to 1.20.4
Refactored the calculator code for clarity.

Added Jacoco line to prvent issues with the bigger Material class.
2023-12-10 10:06:33 -08:00
gitlocalize-app[bot]
cfb35909f0
Translate uk.yml via GitLocalize (#297)
Co-authored-by: GIGABAIT <freebitcoin93@gmail.com>
2023-11-26 10:06:35 -08:00
tastybento
117d15f3d0 Added more placeholders. #296
Refactored how the top ten maps are structured. In the future, it may be
best to have the key be the island.
2023-11-24 09:33:16 -08:00
tastybento
9d1a5c7476
Changed top ten internally to use islands instead of players as keys (#295)
Added %[gamemode]_top_weighted_value_x% placeholder
https://github.com/BentoBoxWorld/Level/issues/294
2023-11-19 18:08:47 -08:00
tastybento
26d4839f6a Try lower version of jacoco 2023-11-18 15:26:33 -08:00
tastybento
43c898ecf7 Added test for Stats command 2023-11-18 15:22:05 -08:00
tastybento
77884f0a11 Update to BentoBox 2.0.0 API 2023-11-18 14:51:09 -08:00
tastybento
1a4077be8c Update tests 2023-11-18 14:44:49 -08:00
tastybento
3a3c8a320c Adds an admin stats command. See #293 2023-11-18 14:41:18 -08:00
tastybento
eb71b35c5c Version 2.12.0 2023-11-18 13:06:44 -08:00
tastybento
cdd4366a2a Merge branch 'develop' of https://github.com/BentoBoxWorld/Level.git
into develop
2023-11-12 09:43:42 -08:00
tastybento
8f567cc328 Use 2.0.0 BentoBox API 2023-11-12 09:42:43 -08:00
PapiCapi
913eed9c77
Add config option to disable plugin hooks (#291)
* Update UltimateStacker dependency

* Add config option to disable plugin hooks
2023-10-06 16:14:45 -07:00
tastybento
2b0a6d82ef
Update pom.xml 2023-07-10 21:44:22 -07:00
tastybento
a3537f5b80 Update Jacoco 2023-07-10 21:23:33 -07:00
tastybento
1b02f11220 Added distribution required for Github Action 2023-06-24 13:56:20 -07:00
tastybento
0c42ad866a Merge branch 'develop' of https://github.com/BentoBoxWorld/Level.git into develop 2023-06-24 13:45:15 -07:00
tastybento
deb09992f1 Update Github Build script 2023-06-24 13:45:07 -07:00
tastybento
1b20605791 Version 2.11.0 2023-06-03 09:15:32 -07:00
gitlocalize-app[bot]
951b0f5549
Spanish (#285)
* Translate es.yml via GitLocalize

* Translate es.yml via GitLocalize

---------

Co-authored-by: ChrissTM03 <criisbr193@gmail.com>
Co-authored-by: Espan <zcraftyt@gmail.com>
2023-05-29 09:47:02 -07:00
gitlocalize-app[bot]
30217ba509
French (#286)
* Translate fr.yml via GitLocalize

* Translate fr.yml via GitLocalize

* Translate fr.yml via GitLocalize

---------

Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com>
Co-authored-by: organizatsiya <organizatsiya.wildguns@gmail.com>
Co-authored-by: Florian CUNY <poslovitch@bentobox.world>
2023-05-29 09:46:48 -07:00
gitlocalize-app[bot]
f8d50a43d6
Translate id.yml via GitLocalize (#287)
Co-authored-by: Dusty <siapa-yg-mau-diblokir.kfrxp@simplelogin.com>
2023-05-29 09:46:35 -07:00
gitlocalize-app[bot]
dfd30d148b
Chinese (#288)
* Translate zh-CN.yml via GitLocalize

* Translate zh-CN.yml via GitLocalize

---------

Co-authored-by: Jeansou <bettertreebot@gmail.com>
Co-authored-by: dawnTak <lanlongxiaode@outlook.com>
2023-05-29 09:46:22 -07:00
tastybento
5a66bf162b Add blocks that should be zero by default as they are available
on the ocean floor. https://github.com/BentoBoxWorld/Level/issues/284
2023-05-17 15:30:38 -07:00
tastybento
ad3d02c29a Version 2.10.1 2023-04-15 16:45:58 -07:00
tastybento
34b2b52979 Merge remote-tracking branch 'origin/master' into develop 2023-04-15 16:45:07 -07:00
tastybento
3c79c68e80 Updated dependencies 2023-04-15 11:38:19 -07:00
BONNe
78c67b6d2f
Create plugin.yml (#282)
* Create plugin.yml

The annotations do not provide the option to define the version dynamically from maven. This should fix that.

* Remove Spigot Plugin Annotations

* Remove plugin-annotation repo
2023-04-08 08:22:41 -07:00
ceze88
9c42a8d007
Add UltimateStacker hook for stacked blocks (#281) 2023-04-05 12:14:16 +01:00
tastybento
c093796a6e Remove dependency 2023-03-25 09:53:41 -07:00
tastybento
713a409584
Refactor placeholders (#279)
* Update ReadMe

* Fix Jacoco

* Remove unused imports

* Remove placeholders from main class

Created a separate class for cleaner code and added a test class.
2023-02-25 11:31:49 -08:00
tastybento
0cdb15403b
Sonar Cloud code smell clean up (#278) 2023-02-09 19:32:13 -08:00
DevSolaris
a493c12f6e
Add shulker to in chest count (#275) 2023-02-09 19:31:35 -08:00
tastybento
42249a8fc9 Updated Jacoco POM 2023-02-09 17:05:27 -08:00
tastybento
f469e37702
Add ${argLine} to get jacoco coverage 2023-02-09 15:15:15 -08:00
gitlocalize-app[bot]
29b148052a
Translate nl.yml via GitLocalize (#277)
Co-authored-by: DevSolaris <solaris.dev.2002@gmail.com>
2023-01-24 14:46:02 +02:00
gitlocalize-app[bot]
f1db2a9284
Translate zh-CN.yml via GitLocalize (#276)
Co-authored-by: dawnTak <lanlongxiaode@outlook.com>
2023-01-23 20:44:30 +02:00
tastybento
dc9d460e1e Fix JavaDoc 2023-01-16 15:07:00 -08:00
evlad
fba73948c6
feat: add island total points + placeholder (#264)
* feat: add island total points + placeholder

* Update IslandLevels.java
2023-01-16 23:00:40 +00:00
tastybento
ac6bead52e Add natural log to level-calc formula parsing
Relates to #274
2023-01-16 14:16:14 -08:00
tastybento
93869cb34a Only shows Members or higher in the top members placeholder
Fixes #267
2022-11-26 18:29:03 -08:00
tastybento
f3ee8a381c Adds %Level_[gamemode]_island_level_max% placeholder
This records the lifetime maximum level the island has ever had.
Addresses #271
2022-11-26 18:20:14 -08:00
tastybento
3988659dcc Update Github workflow to Java 17 2022-11-26 17:52:31 -08:00
tastybento
51338d280d Update to Java 17 2022-11-26 17:48:55 -08:00
gitlocalize-app[bot]
97d9522563
Translate fr.yml via GitLocalize (#272)
Co-authored-by: organizatsiya <organizatsiya.wildguns@gmail.com>
2022-10-31 08:22:33 +02:00
gitlocalize-app[bot]
32690630d6
Translate pl.yml via GitLocalize (#269)
Co-authored-by: wiktorm12 <wiktorm12@gmail.com>
2022-08-27 15:14:55 +03:00
BONNe
2ca4e0a070 Fixes a Level addon crash on startup.
Level addon crashed at the startup if Visit or Warps addon were not installed. It happened because Level addon main class were implementing Listener interface.
To avoid it and fix the crash, I moved migration listener to a separate class.

Fixes #2012
2022-08-21 17:31:42 +03:00
BONNe
dae3db6c98 Implements visit/warp actions in top gui
Add 2 new actions for island buttons in TOP GUI:
- Visit -> allows to visit island, but it requires Visit Addon
- Warp -> allows to warp to island, but it requires Warp Addon

Requested via Discord.
2022-08-21 13:00:56 +03:00
DeadSilenceIV
90ae98e599
Support for AdvancedChests was updated. (#266) 2022-07-15 01:53:27 +03:00
BONNe
47053fde31
Implement customizable Values GUI. (#262)
This GUI shows value to all items in game. It also shows max limit of blocks, if it is set.

Fixes of #192
2022-06-17 14:40:10 +03:00
KrazyxWolf
cc90579f51
Update es.yml (#261) 2022-06-16 21:21:40 +03:00
BONNe
1914fc11e0
Implement calculated value for blocks. (#260)
It is ~ value, as calculation formula cannot be applied per block. At least I think so.

Part of #192
2022-06-16 17:38:09 +03:00
BONNe
4948689fe8
Implement feature that allows to sort items in detail panel. (#259)
Apparently, because it is 2 years old request, it got in a state -> implement or drop.

Fixes #192
2022-06-16 17:07:09 +03:00
tastybento
fcf6e76599 Added repo for maven plugin snapshots 2022-03-19 18:33:36 +00:00
tastybento
d9288c7e61 Remove blank file 2022-03-19 18:27:58 +00:00
BONNe
eb8c105be5 Fix failing test. 2022-03-13 15:24:12 +02:00
BONNe
15ff515078 Implement customizable DetailsPanel.
Remove old DetailsGUITab due to new implementation.
2022-03-13 14:28:08 +02:00
BONNe
43fcde5781 Fixes some small issues with TopLevelPanel
Add Utils class that contains some useful things.
2022-03-13 14:26:56 +02:00
BONNe
e16fad882e Update to BentoBox API 1.20.
Replace plugin.yml with spigot-annotations.

Implement customizable TopLevelPanel.
2022-03-12 12:52:44 +02:00
tastybento
0a79b7fa58 Avoid async chunk snapshotting.
Fixes https://github.com/BentoBoxWorld/Level/issues/256
2022-02-06 08:47:20 -08:00
tastybento
5d9aa00c13 Fix error lon loading id locale 2022-02-06 08:40:56 -08:00
tastybento
490fe6c942 Merge branch 'develop' of https://github.com/BentoBoxWorld/Level.git into develop 2022-01-28 22:15:52 -08:00
tastybento
336e8d47bf Attempt to handle WildStacker spawners 2022-01-28 22:15:34 -08:00
tastybento
a3d06ee41a Version 2.9.1 2022-01-28 22:15:19 -08:00
gitlocalize-app[bot]
6f174e2b3a
Translate hu.yml via GitLocalize (#254)
Co-authored-by: András Marczinkó <marczinkoandris@gmail.com>
2022-01-01 18:41:49 -08:00
gitlocalize-app[bot]
322ea825ea
German Translation (#253)
* Translate de.yml via GitLocalize

* Update de.yml

Co-authored-by: Rikamo045 <rik.amos.krajinovic@gmail.com>
Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2022-01-01 18:40:51 -08:00
gitlocalize-app[bot]
488c6ac9d3
Korean translation (#252)
* Translate ko.yml via GitLocalize

* Translate ko.yml via GitLocalize

Co-authored-by: chickiyeah <ruddls030@naver.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
2022-01-01 18:39:15 -08:00
gitlocalize-app[bot]
840a8c1d79
Translate fr.yml via GitLocalize (#251)
Co-authored-by: organizatsiya <organizatsiya.wildguns@gmail.com>
2022-01-01 18:38:15 -08:00
gitlocalize-app[bot]
34da24d719
Translate id.yml via GitLocalize (#250)
Co-authored-by: Nathan Adhitya <nathanadhitya@outlook.com>
2022-01-01 18:36:45 -08:00
gitlocalize-app[bot]
cbaf14e5f0
Chinese Translation (#249)
* Translate zh-CN.yml via GitLocalize

* Translate zh-CN.yml via GitLocalize

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: 织梦 <493733933@qq.com>
2022-01-01 18:35:59 -08:00
tastybento
7e92a45736
Merge branch 'master' into develop 2021-12-25 08:40:27 -08:00
tastybento
5ce71798a6 Version 2.9.0 2021-12-25 08:39:09 -08:00
Pierre Dedrie
4a21e4b30c
Changed IslandLevelCalculator minHeight to world minHeight for negative blocks height support since 1.18. (#246) 2021-12-18 17:22:28 -08:00
tastybento
50074ac1df
Merge branch 'master' into develop 2021-11-26 13:16:36 -08:00
Rubén
cc977d8562
Raw island level placeholder (#241) 2021-11-26 13:11:30 -08:00
Huynh Tien
4de5b80ab4
add Vietnamese (#240) 2021-11-26 13:10:53 -08:00
tastybento
893d8d46a0 Merge branch 'develop' of https://github.com/BentoBoxWorld/Level.git into develop 2021-11-21 20:08:26 -08:00
tastybento
b1d117d344 Speeds up level calculation by doing more chunk scans async.
If chests are scanned, then it will take longer because these have to be
done sync.

https://github.com/BentoBoxWorld/Level/issues/243
2021-11-21 20:08:05 -08:00
tastybento
11618085ff Version 2.8.1 2021-11-21 08:14:35 -08:00
tastybento
60f2a268b9
Merge branch 'master' into develop 2021-10-16 16:09:54 -07:00
tastybento
4c59d4d4ae Back support for BentoBox 1.16.5. 2021-10-04 23:01:57 -07:00
tastybento
9b8bbdac5f Open up modules for testing access. 2021-10-01 17:41:39 -07:00
tastybento
d212fcee99 Update to BentoBox API 1.18 2021-10-01 17:40:07 -07:00
tastybento
1b29f7f6ac Added new placeholders
%Level_%gamemode%_top_island_name_%rank% - lists the island name
%Level_%gamemode%_top_island_members_%rank% - a comma separated list of
team members

https://github.com/BentoBoxWorld/Level/issues/224
https://github.com/BentoBoxWorld/Level/issues/211
https://github.com/BentoBoxWorld/Level/issues/132
https://github.com/BentoBoxWorld/Level/issues/107
https://github.com/BentoBoxWorld/Level/issues/105
2021-09-06 11:57:32 -07:00
tastybento
b16d458cae Version 2.8.0 2021-09-06 11:23:38 -07:00
tastybento
7b6f921b10 Made plugin a Pladdon. 2021-08-15 08:53:16 -07:00
tastybento
bd6c264f4d
Rosestacker (#232)
* Add support for RoseStacker 1.3.0
2021-08-09 20:00:55 -07:00
tastybento
d55f66f868
No save on disable (#231)
* Release 2.6.4

* Remove saving to database on disable.

https://github.com/BentoBoxWorld/Level/issues/229

First, the top ten tables are never actually used or loaded. They are
created in memory by loading the island levels. So there is no reason to
keep saving them.
Second, the island level data is saved every time it is changed, so
there is no need to save all of the cache on exit.

* Fixes tests
2021-08-08 11:09:36 -07:00
tastybento
76a2688556 Added placeholder %Level_[gamemode]_rank_value
Fixes https://github.com/BentoBoxWorld/Level/issues/228
2021-07-24 14:54:06 -07:00
tastybento
4661bcd109 Use Java 9's takeWhile 2021-07-24 14:26:45 -07:00
tastybento
383ede3d59 Version 2.7.2 2021-07-10 22:04:17 -07:00
tastybento
0ce89dea7f Version 2.7.1 2021-07-03 15:50:37 -07:00
26 changed files with 1908 additions and 1576 deletions

29
pom.xml
View File

@ -59,7 +59,7 @@
<powermock.version>2.0.9</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.20.4-R0.1-SNAPSHOT</spigot.version> <spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>2.0.0-SNAPSHOT</bentobox.version> <bentobox.version>2.5.1-SNAPSHOT</bentobox.version>
<!-- Warps addon version --> <!-- Warps addon version -->
<warps.version>1.12.0</warps.version> <warps.version>1.12.0</warps.version>
<!-- Visit addon version --> <!-- Visit addon version -->
@ -71,7 +71,7 @@
<!-- Do not change unless you want different name for local builds. --> <!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<!-- This allows to change between versions. --> <!-- This allows to change between versions. -->
<build.version>2.12.0</build.version> <build.version>2.16.2</build.version>
<sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey> <sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization> <sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url> <sonar.host.url>https://sonarcloud.io</sonar.host.url>
@ -129,8 +129,8 @@
<repositories> <repositories>
<!--Wild Stacker repo --> <!--Wild Stacker repo -->
<repository> <repository>
<id>jitpack.io</id> <id>bg-repo</id>
<url>https://jitpack.io</url> <url>https://repo.bg-software.com/repository/api/</url>
</repository> </repository>
<!-- RoseStacker repo --> <!-- RoseStacker repo -->
<repository> <repository>
@ -139,8 +139,8 @@
</repository> </repository>
<!-- UltimateStacker repo --> <!-- UltimateStacker repo -->
<repository> <repository>
<id>songoda-public</id> <id>songoda-plugins</id>
<url>https://repo.songoda.com/repository/public/</url> <url>https://repo.songoda.com/repository/minecraft-plugins/</url>
</repository> </repository>
<repository> <repository>
<id>spigot-repo</id> <id>spigot-repo</id>
@ -154,6 +154,10 @@
<id>codemc-public</id> <id>codemc-public</id>
<url>https://repo.codemc.org/repository/maven-public/</url> <url>https://repo.codemc.org/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -208,9 +212,9 @@
</dependency> </dependency>
<!-- Wild Stacker dependency --> <!-- Wild Stacker dependency -->
<dependency> <dependency>
<groupId>com.github.OmerBenGera</groupId> <groupId>com.bgsoftware</groupId>
<artifactId>WildStackerAPI</artifactId> <artifactId>WildStackerAPI</artifactId>
<version>b18</version> <version>2023.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Static analysis --> <!-- Static analysis -->
@ -231,13 +235,14 @@
<dependency> <dependency>
<groupId>dev.rosewood</groupId> <groupId>dev.rosewood</groupId>
<artifactId>rosestacker</artifactId> <artifactId>rosestacker</artifactId>
<version>1.3.0</version> <version>1.5.27</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Ultimate Stacker dependency -->
<dependency> <dependency>
<groupId>com.songoda</groupId> <groupId>com.craftaro</groupId>
<artifactId>UltimateStacker</artifactId> <artifactId>UltimateStacker-API</artifactId>
<version>2.4.0</version> <version>1.0.0-20240329.173606-35</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -9,8 +9,14 @@ import world.bentobox.bentobox.api.addons.Pladdon;
* *
*/ */
public class LevelPladdon extends Pladdon { public class LevelPladdon extends Pladdon {
private Addon addon;
@Override @Override
public Addon getAddon() { public Addon getAddon() {
return new Level(); if (addon == null) {
addon = new Level();
}
return addon;
} }
} }

View File

@ -2,6 +2,7 @@ package world.bentobox.level;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.time.Instant;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -31,19 +32,12 @@ import world.bentobox.level.events.IslandPreLevelEvent;
import world.bentobox.level.objects.IslandLevels; import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.objects.TopTenData; import world.bentobox.level.objects.TopTenData;
import world.bentobox.level.util.CachedData;
public class LevelsManager { public class LevelsManager {
private static final String INTOPTEN = "intopten"; private static final String INTOPTEN = "intopten";
private static final TreeMap<BigInteger, String> LEVELS; private static final TreeMap<BigInteger, String> LEVELS = new TreeMap<>();
private static final BigInteger THOUSAND = BigInteger.valueOf(1000); private static final BigInteger THOUSAND = BigInteger.valueOf(1000);
static {
LEVELS = new TreeMap<>();
LEVELS.put(THOUSAND, "k");
LEVELS.put(THOUSAND.pow(2), "M");
LEVELS.put(THOUSAND.pow(3), "G");
LEVELS.put(THOUSAND.pow(4), "T");
}
private final Level addon; private final Level addon;
// Database handler for level data // Database handler for level data
@ -52,6 +46,8 @@ public class LevelsManager {
private final Map<String, IslandLevels> levelsCache; private final Map<String, IslandLevels> levelsCache;
// Top ten lists // Top ten lists
private final Map<World, TopTenData> topTenLists; private final Map<World, TopTenData> topTenLists;
// Cache for top tens
private Map<World, CachedData> cache = new HashMap<>();
public LevelsManager(Level addon) { public LevelsManager(Level addon) {
this.addon = addon; this.addon = addon;
@ -63,6 +59,12 @@ public class LevelsManager {
levelsCache = new HashMap<>(); levelsCache = new HashMap<>();
// Initialize top ten lists // Initialize top ten lists
topTenLists = new ConcurrentHashMap<>(); topTenLists = new ConcurrentHashMap<>();
// Units
LEVELS.put(THOUSAND, addon.getSettings().getKilo());
LEVELS.put(THOUSAND.pow(2), addon.getSettings().getMega());
LEVELS.put(THOUSAND.pow(3), addon.getSettings().getGiga());
LEVELS.put(THOUSAND.pow(4), addon.getSettings().getTera());
} }
public void migrate() { public void migrate() {
@ -105,7 +107,8 @@ public class LevelsManager {
* @return true if successful, false if not added * @return true if successful, false if not added
*/ */
private boolean addToTopTen(Island island, long lv) { private boolean addToTopTen(Island island, long lv) {
if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) { if (island != null && island.getOwner() != null && island.getWorld() != null
&& hasTopTenPerm(island.getWorld(), island.getOwner())) {
topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen() topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen()
.put(island.getUniqueId(), lv); .put(island.getUniqueId(), lv);
return true; return true;
@ -212,15 +215,32 @@ public class LevelsManager {
* *
* @param world - world where the island is * @param world - world where the island is
* @param targetPlayer - target player UUID * @param targetPlayer - target player UUID
* @param ownerOnly - return level only if the target player is the owner
* @return Level of the player's island or zero if player is unknown or UUID is * @return Level of the player's island or zero if player is unknown or UUID is
* null * null
*/ */
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) {
return getIslandLevel(world, targetPlayer, false);
}
/**
* Get level of island from cache for a player.
*
* @param world - world where the island is
* @param targetPlayer - target player UUID
* @param ownerOnly - return level only if the target player is the owner
* @return Level of the player's island or zero if player is unknown or UUID is
* null
*/
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer, boolean ownerOnly) {
if (targetPlayer == null) if (targetPlayer == null)
return 0L; return 0L;
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? 0L : getLevelsData(island).getLevel(); if (island == null || island.getOwner() == null || (ownerOnly && !island.getOwner().equals(targetPlayer))) {
return 0L;
}
return getLevelsData(island).getLevel();
} }
/** /**
@ -325,7 +345,6 @@ public class LevelsManager {
// Return the unmodifiable map // Return the unmodifiable map
return Collections.unmodifiableMap(weightedTopTen); return Collections.unmodifiableMap(weightedTopTen);
} }
/** /**
@ -339,7 +358,19 @@ public class LevelsManager {
@NonNull @NonNull
public Map<String, Long> getTopTen(@NonNull World world, int size) { public Map<String, Long> getTopTen(@NonNull World world, int size) {
createAndCleanRankings(world); createAndCleanRankings(world);
// Return the sorted map CachedData cachedData = cache.get(world);
Instant now = Instant.now();
if (cachedData != null && cachedData.getLastUpdated().plusSeconds(1).isAfter(now)) {
return cachedData.getCachedMap();
} else {
Map<String, Long> newTopTen = calculateTopTen(world, size);
cache.put(world, new CachedData(newTopTen, now));
return newTopTen;
}
}
private Map<String, Long> calculateTopTen(@NonNull World world, int size) {
return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream() return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
.filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(size) .limit(size)
@ -399,7 +430,8 @@ public class LevelsManager {
addon.log("Generating rankings"); addon.log("Generating rankings");
handler.loadObjects().forEach(il -> { handler.loadObjects().forEach(il -> {
if (il.getLevel() > 0) { if (il.getLevel() > 0) {
addon.getIslands().getIslandById(il.getUniqueId()) // Load islands, but don't cache them
addon.getIslands().getIslandById(il.getUniqueId(), false)
.ifPresent(i -> this.addToTopTen(i, il.getLevel())); .ifPresent(i -> this.addToTopTen(i, il.getLevel()));
} }
}); });
@ -416,8 +448,9 @@ public class LevelsManager {
public void removeEntry(World world, String uuid) { public void removeEntry(World world, String uuid) {
if (topTenLists.containsKey(world)) { if (topTenLists.containsKey(world)) {
topTenLists.get(world).getTopTen().remove(uuid); topTenLists.get(world).getTopTen().remove(uuid);
// Invalidate the cache because of this deletion
cache.remove(world);
} }
} }
/** /**

View File

@ -41,6 +41,9 @@ public class PlaceholderManager {
// Island Level // Island Level
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level", bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level",
user -> addon.getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId())); user -> addon.getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId()));
// Island Level owner only
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_owner",
user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId(), true)));
// Unformatted island level // Unformatted island level
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_raw", bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_raw",
user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId()))); user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId())));

View File

@ -40,9 +40,6 @@ import com.bgsoftware.wildstacker.api.objects.StackedBarrel;
import com.google.common.collect.Multiset; import com.google.common.collect.Multiset;
import com.google.common.collect.Multiset.Entry; import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets; import com.google.common.collect.Multisets;
import com.songoda.ultimatestacker.UltimateStacker;
import com.songoda.ultimatestacker.core.compatibility.CompatibleMaterial;
import com.songoda.ultimatestacker.stackable.block.BlockStack;
import dev.rosewood.rosestacker.api.RoseStackerAPI; import dev.rosewood.rosestacker.api.RoseStackerAPI;
import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI; import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI;
@ -449,47 +446,33 @@ public class IslandLevelCalculator {
// Only count to the highest block in the world for some optimization // Only count to the highest block in the world for some optimization
for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) { for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z); BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
Material m = blockData.getMaterial();
boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight; boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
// Slabs can be doubled, so check them twice // Slabs can be doubled, so check them twice
if (Tag.SLABS.isTagged(blockData.getMaterial())) { if (Tag.SLABS.isTagged(m)) {
Slab slab = (Slab) blockData; Slab slab = (Slab) blockData;
if (slab.getType().equals(Slab.Type.DOUBLE)) { if (slab.getType().equals(Slab.Type.DOUBLE)) {
checkBlock(blockData.getMaterial(), belowSeaLevel); checkBlock(m, belowSeaLevel);
} }
} }
// Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real // Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
// chunk // chunk
if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON) if (addon.isStackersEnabled() && (m.equals(Material.CAULDRON) || m.equals(Material.SPAWNER))) {
|| blockData.getMaterial().equals(Material.SPAWNER))) {
stackedBlocks.add(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y, stackedBlocks.add(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y,
(double) z + cp.chunkSnapshot.getZ() * 16)); (double) z + cp.chunkSnapshot.getZ() * 16));
} }
Block block = cp.chunk.getBlock(x, y, z); if (addon.isUltimateStackerEnabled() && !m.isAir()) {
Location l = new Location(cp.chunk.getWorld(), x, y, z);
if (addon.isUltimateStackerEnabled()) { UltimateStackerCalc.addStackers(m, l, results, belowSeaLevel, limitCount(m));
if (!blockData.getMaterial().equals(Material.AIR)) {
BlockStack stack = UltimateStacker.getInstance().getBlockStackManager().getBlock(block,
CompatibleMaterial.getMaterial(block));
if (stack != null) {
int value = limitCount(blockData.getMaterial());
if (belowSeaLevel) {
results.underWaterBlockCount.addAndGet((long) stack.getAmount() * value);
results.uwCount.add(blockData.getMaterial());
} else {
results.rawBlockCount.addAndGet((long) stack.getAmount() * value);
results.mdCount.add(blockData.getMaterial());
}
}
}
} }
// Scan chests // Scan chests
if (addon.getSettings().isIncludeChests() && CHESTS.contains(blockData.getMaterial())) { if (addon.getSettings().isIncludeChests() && CHESTS.contains(m)) {
chestBlocks.add(cp.chunk); chestBlocks.add(cp.chunk);
} }
// Add the value of the block's material // Add the value of the block's material
checkBlock(blockData.getMaterial(), belowSeaLevel); checkBlock(m, belowSeaLevel);
} }
} }
} }
@ -625,41 +608,32 @@ public class IslandLevelCalculator {
// Done // Done
pipeliner.getInProcessQueue().remove(this); pipeliner.getInProcessQueue().remove(this);
// Chunk finished // Chunk finished
// This was the last chunk // This was the last chunk. Handle stacked blocks, then chests and exit
handleStackedBlocks(); handleStackedBlocks().thenCompose(v -> handleChests()).thenRun(() -> {
handleChests();
long checkTime = System.currentTimeMillis();
finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
// Check every half second if all the chests and stacks have been cleared
if ((chestBlocks.isEmpty() && stackedBlocks.isEmpty())
|| System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
this.tidyUp(); this.tidyUp();
this.getR().complete(getResults()); this.getR().complete(getResults());
finishTask.cancel(); });
}
}, 0, 10L);
} }
}); });
} }
private void handleChests() { private CompletableFuture<Void> handleChests() {
Iterator<Chunk> it = chestBlocks.iterator(); List<CompletableFuture<Void>> futures = new ArrayList<>();
while (it.hasNext()) { for (Chunk v : chestBlocks) {
Chunk v = it.next(); CompletableFuture<Void> future = Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
scanChests(c); scanChests(c);
it.remove();
}); });
futures.add(future);
} }
// Return a CompletableFuture that completes when all futures are done
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
} }
private void handleStackedBlocks() { private CompletableFuture<Void> handleStackedBlocks() {
// Deal with any stacked blocks // Deal with any stacked blocks
Iterator<Location> it = stackedBlocks.iterator(); List<CompletableFuture<Void>> futures = new ArrayList<>();
while (it.hasNext()) { for (Location v : stackedBlocks) {
Location v = it.next(); CompletableFuture<Void> future = Util.getChunkAtAsync(v).thenAccept(c -> {
Util.getChunkAtAsync(v).thenAccept(c -> {
Block stackedBlock = v.getBlock(); Block stackedBlock = v.getBlock();
boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight; boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) { if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
@ -674,8 +648,12 @@ public class IslandLevelCalculator {
checkBlock(stackedBlock.getType(), belowSeaLevel); checkBlock(stackedBlock.getType(), belowSeaLevel);
} }
} }
it.remove();
}); });
futures.add(future);
} }
// Return a CompletableFuture that completes when all futures are done
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
} }
} }

View File

@ -0,0 +1,29 @@
package world.bentobox.level.calculators;
import org.bukkit.Location;
import org.bukkit.Material;
import com.craftaro.ultimatestacker.api.UltimateStackerApi;
import com.craftaro.ultimatestacker.api.utils.Stackable;
import world.bentobox.bentobox.BentoBox;
/**
* Isolates UltimateStacker imports so that they are only loaded if the plugin exists
*/
public class UltimateStackerCalc {
public static void addStackers(Material material, Location location, Results results, boolean belowSeaLevel,
int value) {
Stackable stack = UltimateStackerApi.getBlockStackManager().getBlock(location);
if (stack != null) {
if (belowSeaLevel) {
results.underWaterBlockCount.addAndGet((long) stack.getAmount() * value);
results.uwCount.add(material);
} else {
results.rawBlockCount.addAndGet((long) stack.getAmount() * value);
results.mdCount.add(material);
}
}
}
}

View File

@ -46,10 +46,20 @@ public class AdminSetInitialLevelCommand extends CompositeCommand {
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
String initialLevel = String.valueOf(addon.getManager().getInitialLevel(island)); long initialLevel = addon.getManager().getInitialLevel(island);
long lv = Long.parseLong(args.get(1)); long lv = 0;
if (args.get(1).startsWith("+")) {
String change = args.get(1).substring(1);
lv = initialLevel + Long.parseLong(change);
} else if (args.get(1).startsWith("-")) {
String change = args.get(1).substring(1);
lv = initialLevel - Long.parseLong(change);
} else {
lv = Long.parseLong(args.get(1));
}
addon.getManager().setInitialIslandLevel(island, lv); addon.getManager().setInitialIslandLevel(island, lv);
user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, initialLevel, "[new_number]", String.valueOf(lv)); user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, String.valueOf(initialLevel),
"[new_number]", String.valueOf(lv));
return true; return true;
} }
@ -64,11 +74,21 @@ public class AdminSetInitialLevelCommand extends CompositeCommand {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false; return false;
} }
// Check if this is a add or remove
if (args.get(1).startsWith("+") || args.get(1).startsWith("-")) {
String change = args.get(1).substring(1);
if (!Util.isInteger(change, true)) {
user.sendMessage("admin.level.sethandicap.invalid-level");
return false;
}
// Value is okay
} else {
// Check value // Check value
if (!Util.isInteger(args.get(1), true)) { if (!Util.isInteger(args.get(1), true)) {
user.sendMessage("admin.level.sethandicap.invalid-level"); user.sendMessage("admin.level.sethandicap.invalid-level");
return false; return false;
} }
}
// Check island // Check island
island = getAddon().getIslands().getIsland(getWorld(), targetUUID); island = getAddon().getIslands().getIsland(getWorld(), targetUUID);
if (island == null) { if (island == null) {

View File

@ -111,7 +111,11 @@ public class IslandLevelCommand extends CompositeCommand {
} }
// Send player how many points are required to reach next island level // Send player how many points are required to reach next island level
if (results.getPointsToNextLevel() >= 0) { if (results.getPointsToNextLevel() >= 0) {
user.sendMessage("island.level.required-points-to-next-level", "[points]", String.valueOf(results.getPointsToNextLevel())); user.sendMessage("island.level.required-points-to-next-level",
"[points]", String.valueOf(results.getPointsToNextLevel()),
"[progress]", String.valueOf(this.addon.getSettings().getLevelCost()-results.getPointsToNextLevel()),
"[levelcost]", String.valueOf(this.addon.getSettings().getLevelCost())
);
} }
// Tell other team members // Tell other team members
if (results.getLevel() != oldLevel) { if (results.getLevel() != oldLevel) {

View File

@ -8,11 +8,14 @@ import java.util.Optional;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.level.Level; import world.bentobox.level.Level;
import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.panels.ValuePanel; import world.bentobox.level.panels.ValuePanel;
import world.bentobox.level.util.Utils; import world.bentobox.level.util.Utils;
@ -112,6 +115,19 @@ public class IslandValueCommand extends CompositeCommand
"[value]", (underWater * value) + ""), "[value]", (underWater * value) + ""),
MATERIAL, Utils.prettifyObject(material, user)); MATERIAL, Utils.prettifyObject(material, user));
} }
// Show how many have been placed and how many are allowed
@NonNull
IslandLevels lvData = this.addon.getManager()
.getLevelsData(getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()));
int count = lvData.getMdCount().getOrDefault(material, 0) + lvData.getUwCount().getOrDefault(material, 0);
user.sendMessage("level.conversations.you-have", TextVariables.NUMBER,
String.valueOf(count));
int limit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(material, -1);
if (limit > 0) {
user.sendMessage("level.conversations.you-can-place", TextVariables.NUMBER,
String.valueOf(limit));
}
} }
else else
{ {

View File

@ -2,9 +2,12 @@ package world.bentobox.level.config;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -26,6 +29,7 @@ public class BlockConfig {
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class); private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
private Map<Material, Integer> blockValues = new EnumMap<>(Material.class); private Map<Material, Integer> blockValues = new EnumMap<>(Material.class);
private final Map<World, Map<Material, Integer>> worldBlockValues = new HashMap<>(); private final Map<World, Map<Material, Integer>> worldBlockValues = new HashMap<>();
private final List<Material> hiddenBlocks;
private Level addon; private Level addon;
/** /**
@ -49,6 +53,16 @@ public class BlockConfig {
if (blockValues.isConfigurationSection("worlds")) { if (blockValues.isConfigurationSection("worlds")) {
loadWorlds(blockValues); loadWorlds(blockValues);
} }
// Hidden
hiddenBlocks = blockValues.getStringList("hidden-blocks").stream().map(name -> {
try {
return Material.valueOf(name.toUpperCase(Locale.ENGLISH));
} catch (Exception e) {
return null;
}
}).filter(Objects::nonNull).toList();
// All done // All done
blockValues.save(file); blockValues.save(file);
} }
@ -159,5 +173,22 @@ public class BlockConfig {
return null; return null;
} }
/**
* Return true if the block should be hidden
* @param m block material
* @return true if hidden
*/
public boolean isHiddenBlock(Material m) {
return hiddenBlocks.contains(m);
}
/**
* Return true if the block should not be hidden
* @param m block material
* @return false if hidden
*/
public boolean isNotHiddenBlock(Material m) {
return !hiddenBlocks.contains(m);
}
} }

View File

@ -3,6 +3,7 @@ package world.bentobox.level.config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigComment;
@ -120,6 +121,17 @@ public class ConfigSettings implements ConfigObject {
@ConfigComment("Shows large level values rounded down, e.g., 10,345 -> 10k") @ConfigComment("Shows large level values rounded down, e.g., 10,345 -> 10k")
@ConfigEntry(path = "shorthand") @ConfigEntry(path = "shorthand")
private boolean shorthand = false; private boolean shorthand = false;
@ConfigComment("Shorthand units")
@ConfigEntry(path = "units.kilo")
private String kilo = "k";
@ConfigEntry(path = "units.mega")
private String mega = "M";
@ConfigEntry(path = "units.giga")
private String giga = "G";
@ConfigEntry(path = "units.tera")
private String tera = "T";
@ConfigComment("") @ConfigComment("")
@ConfigComment("Include Shulker Box content in chests in level calculations.") @ConfigComment("Include Shulker Box content in chests in level calculations.")
@ConfigComment("Will count blocks in Shulker Boxes inside of chests.") @ConfigComment("Will count blocks in Shulker Boxes inside of chests.")
@ -419,4 +431,60 @@ public class ConfigSettings implements ConfigObject {
public void setDisabledPluginHooks(List<String> disabledPluginHooks) { public void setDisabledPluginHooks(List<String> disabledPluginHooks) {
this.disabledPluginHooks = disabledPluginHooks; this.disabledPluginHooks = disabledPluginHooks;
} }
/**
* @return the kilo
*/
public String getKilo() {
return Objects.requireNonNullElse(kilo, "k");
}
/**
* @param kilo the kilo to set
*/
public void setKilo(String kilo) {
this.kilo = kilo;
}
/**
* @return the mega
*/
public String getMega() {
return Objects.requireNonNullElse(mega, "M");
}
/**
* @param mega the mega to set
*/
public void setMega(String mega) {
this.mega = mega;
}
/**
* @return the giga
*/
public String getGiga() {
return Objects.requireNonNullElse(giga, "G");
}
/**
* @param giga the giga to set
*/
public void setGiga(String giga) {
this.giga = giga;
}
/**
* @return the tera
*/
public String getTera() {
return Objects.requireNonNullElse(tera, "T");
}
/**
* @param tera the tera to set
*/
public void setTera(String tera) {
this.tera = tera;
}
} }

View File

@ -62,7 +62,7 @@ public class IslandLevels implements DataObject {
private Map<Material, Integer> uwCount; private Map<Material, Integer> uwCount;
/** /**
* MaterialData count - count of all blocks * MaterialData count - count of all blocks excluding under water
*/ */
@Expose @Expose
private Map<Material, Integer> mdCount; private Map<Material, Integer> mdCount;
@ -162,6 +162,7 @@ public class IslandLevels implements DataObject {
} }
/** /**
* The count of underwater blocks
* @return the uwCount * @return the uwCount
*/ */
public Map<Material, Integer> getUwCount() { public Map<Material, Integer> getUwCount() {
@ -169,6 +170,7 @@ public class IslandLevels implements DataObject {
} }
/** /**
* Underwater blocks
* @param uwCount the uwCount to set * @param uwCount the uwCount to set
*/ */
public void setUwCount(Map<Material, Integer> uwCount) { public void setUwCount(Map<Material, Integer> uwCount) {
@ -176,6 +178,7 @@ public class IslandLevels implements DataObject {
} }
/** /**
* All blocks count except for underwater blocks
* @return the mdCount * @return the mdCount
*/ */
public Map<Material, Integer> getMdCount() { public Map<Material, Integer> getMdCount() {
@ -183,6 +186,7 @@ public class IslandLevels implements DataObject {
} }
/** /**
* All blocks except for underwater blocks
* @param mdCount the mdCount to set * @param mdCount the mdCount to set
*/ */
public void setMdCount(Map<Material, Integer> mdCount) { public void setMdCount(Map<Material, Integer> mdCount) {

View File

@ -59,7 +59,7 @@ public class DetailsPanel {
} }
// By default no-filters are active. // By default no-filters are active.
this.activeTab = Tab.ALL_BLOCKS; this.activeTab = Tab.VALUE_BLOCKS;
this.activeFilter = Filter.NAME; this.activeFilter = Filter.NAME;
this.materialCountList = new ArrayList<>(Material.values().length); this.materialCountList = new ArrayList<>(Material.values().length);
@ -111,6 +111,31 @@ public class DetailsPanel {
this.materialCountList.clear(); this.materialCountList.clear();
switch (this.activeTab) { switch (this.activeTab) {
case VALUE_BLOCKS -> {
Map<Material, Integer> materialCountMap = new EnumMap<>(Material.class);
materialCountMap.putAll(this.levelsData.getMdCount());
// Add underwater blocks.
this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material,
materialCountMap.computeIfAbsent(material, key -> 0) + count));
// Remove zero value blocks
materialCountMap.entrySet().removeIf(en -> {
Integer value = this.addon.getBlockConfig().getValue(world, en.getKey());
return value == null || value == 0;
});
// Remove any hidden blocks
materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock);
materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).forEachOrdered(entry -> {
if (entry.getValue() > 0) {
this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()));
}
});
}
case ALL_BLOCKS -> { case ALL_BLOCKS -> {
Map<Material, Integer> materialCountMap = new EnumMap<>(Material.class); Map<Material, Integer> materialCountMap = new EnumMap<>(Material.class);
@ -120,16 +145,24 @@ public class DetailsPanel {
this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material, this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material,
materialCountMap.computeIfAbsent(material, key -> 0) + count)); materialCountMap.computeIfAbsent(material, key -> 0) + count));
// Remove any hidden blocks
materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock);
materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())) materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey()))
.forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
} }
case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream().sorted((Map.Entry.comparingByKey())) case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream()
.filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey()))
.sorted((Map.Entry.comparingByKey()))
.forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream().sorted((Map.Entry.comparingByKey())) case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream()
.filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey()))
.sorted((Map.Entry.comparingByKey()))
.forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
case SPAWNER -> { case SPAWNER -> {
if (this.addon.getBlockConfig().isNotHiddenBlock(Material.SPAWNER)) {
int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0); int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0);
int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0); int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0);
@ -137,6 +170,7 @@ public class DetailsPanel {
this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater)); this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater));
} }
} }
}
Comparator<Pair<Material, Integer>> sorter; Comparator<Pair<Material, Integer>> sorter;
@ -220,7 +254,7 @@ public class DetailsPanel {
builder.description(this.user.getTranslation(this.world, template.description())); builder.description(this.user.getTranslation(this.world, template.description()));
} }
Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.ALL_BLOCKS); Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.VALUE_BLOCKS);
// Get only possible actions, by removing all inactive ones. // Get only possible actions, by removing all inactive ones.
List<ItemTemplateRecord.ActionRecords> activeActions = new ArrayList<>(template.actions()); List<ItemTemplateRecord.ActionRecords> activeActions = new ArrayList<>(template.actions());
@ -604,6 +638,10 @@ public class DetailsPanel {
* All block Tab * All block Tab
*/ */
ALL_BLOCKS, ALL_BLOCKS,
/**
* Blocks that have value
*/
VALUE_BLOCKS,
/** /**
* Above Sea level Tab. * Above Sea level Tab.
*/ */

View File

@ -58,7 +58,9 @@ public class ValuePanel
this.activeFilter = Filter.NAME_ASC; this.activeFilter = Filter.NAME_ASC;
this.materialRecordList = Arrays.stream(Material.values()). this.materialRecordList = Arrays.stream(Material.values()).
filter(Material::isBlock). filter(Material::isBlock).
filter(Material::isItem). // Remove things like PITCHER_CROP
filter(m -> !m.name().startsWith("LEGACY_")). filter(m -> !m.name().startsWith("LEGACY_")).
filter(this.addon.getBlockConfig()::isNotHiddenBlock).
map(material -> map(material ->
{ {
Integer value = this.addon.getBlockConfig().getValue(this.world, material); Integer value = this.addon.getBlockConfig().getValue(this.world, material);
@ -583,6 +585,7 @@ public class ValuePanel
return null; return null;
} }
@SuppressWarnings("deprecation")
int index = this.pageIndex * slot.amountMap().getOrDefault(BLOCK, 1) + slot.slot(); int index = this.pageIndex * slot.amountMap().getOrDefault(BLOCK, 1) + slot.slot();
if (index >= this.elementList.size()) if (index >= this.elementList.size())

View File

@ -0,0 +1,30 @@
package world.bentobox.level.util;
import java.time.Instant;
import java.util.Map;
/**
* Cache for top tens
*/
public class CachedData {
private Map<String, Long> cachedMap;
private Instant lastUpdated;
public CachedData(Map<String, Long> cachedMap, Instant lastUpdated) {
this.cachedMap = cachedMap;
this.lastUpdated = lastUpdated;
}
public Map<String, Long> getCachedMap() {
return cachedMap;
}
public Instant getLastUpdated() {
return lastUpdated;
}
public void updateCache(Map<String, Long> newMap, Instant newUpdateTime) {
this.cachedMap = newMap;
this.lastUpdated = newUpdateTime;
}
}

View File

@ -2,7 +2,7 @@ name: Level
main: world.bentobox.level.Level main: world.bentobox.level.Level
version: ${version}${build.number} version: ${version}${build.number}
icon: DIAMOND icon: DIAMOND
api-version: 1.16.5 api-version: 2.5.1
authors: tastybento authors: tastybento

View File

@ -10,6 +10,10 @@
limits: limits:
COBBLESTONE: 10000 COBBLESTONE: 10000
NETHERRACK: 1000 NETHERRACK: 1000
# These blocks will never be shown in the GUI even if they have value
hidden-blocks:
- BEDROCK
- AIR
blocks: blocks:
ACACIA_BUTTON: 1 ACACIA_BUTTON: 1
ACACIA_DOOR: 2 ACACIA_DOOR: 2

View File

@ -19,7 +19,7 @@ calculation-timeout: 5
# #
# Zero island levels on new island or island reset # Zero island levels on new island or island reset
# If true, Level will calculate the starter island's level and remove it from any future level calculations. # If true, Level will calculate the starter island's level and remove it from any future level calculations.
# If this is false, the player's starter island blocks will count towards their level. # If false, the player's starter island blocks will count towards their level.
# This will reduce CPU if false. # This will reduce CPU if false.
zero-new-island-levels: true zero-new-island-levels: true
# #
@ -72,3 +72,18 @@ sumteamdeaths: false
# Shorthand island level # Shorthand island level
# Shows large level values rounded down, e.g., 10,345 -> 10k # Shows large level values rounded down, e.g., 10,345 -> 10k
shorthand: false shorthand: false
units:
# Shorthand units
kilo: k
mega: M
giga: G
tera: T
#
# Include Shulker Box content in chests in level calculations.
# Will count blocks in Shulker Boxes inside of chests.
# NOTE: include-chests needs to be enabled for this to work!.
include-shulkers-in-chest: false
#
# Disables hooking with other plugins.
# Example: disabled-plugin-hooks: [UltimateStacker, RoseStacker]
disabled-plugin-hooks: []

View File

@ -8,8 +8,12 @@ admin:
parameters: "<player>" parameters: "<player>"
description: "calculate the island level for player" description: "calculate the island level for player"
sethandicap: sethandicap:
parameters: <player> <handicap> parameters: <player> [+/-]<handicap>
description: "set the island handicap, usually the level of the starter island" description: |
set or change the island *handicap*
e.g. +10 will remove 10 levels,
30 will set handicap to 30,
-20 will add 20 levels
changed: "&a Initial island handicap changed from [number] to [new_number]." changed: "&a Initial island handicap changed from [number] to [new_number]."
invalid-level: "&c Invalid handicap. Use an integer." invalid-level: "&c Invalid handicap. Use an integer."
levelstatus: levelstatus:
@ -42,7 +46,7 @@ island:
estimated-wait: "&a Estimated wait: [number] seconds" estimated-wait: "&a Estimated wait: [number] seconds"
in-queue: "&a You are number [number] in the queue" in-queue: "&a You are number [number] in the queue"
island-level-is: "&a Island level is &b[level]" island-level-is: "&a Island level is &b[level]"
required-points-to-next-level: "&a [points] points required until the next level" required-points-to-next-level: "&a Level progress: &6 [progress]&b /&e [levelcost] &a points"
deaths: "&c ([number] deaths)" deaths: "&c ([number] deaths)"
cooldown: "&c You must wait &b[time] &c seconds until you can do that again" cooldown: "&c You must wait &b[time] &c seconds until you can do that again"
in-progress: "&6 Island level calculation is in progress..." in-progress: "&6 Island level calculation is in progress..."
@ -112,6 +116,11 @@ level:
limit: "&7 Block limit: &e [number]" limit: "&7 Block limit: &e [number]"
count: "&7 Number of blocks: &e [number]" count: "&7 Number of blocks: &e [number]"
calculated: "&7 Calculated value: &e [number]" calculated: "&7 Calculated value: &e [number]"
value_blocks:
name: "&f&l All Blocks With Value"
description: |-
&7 Display all blocks
&7 with value on island.
all_blocks: all_blocks:
name: "&f&l All Blocks" name: "&f&l All Blocks"
description: |- description: |-
@ -211,3 +220,7 @@ level:
value-underwater: "&7 The value of '[material]' below sea-level: &e[value]" value-underwater: "&7 The value of '[material]' below sea-level: &e[value]"
# Message that is sent to user when he does not hold any items in hand. # Message that is sent to user when he does not hold any items in hand.
empty-hand: "&c There are no blocks in your hand" empty-hand: "&c There are no blocks in your hand"
# Message when showing how many have been placed of a block
you-have: "&7 You have [number] at last count."
# Message about the limit
you-can-place: "&7 You can place up to [number] and have them count"

210
src/main/resources/locales/zh-CN.yml Executable file → Normal file
View File

@ -1,79 +1,93 @@
---
admin: admin:
level: level:
parameters: "<player>" parameters: <player>
description: 计算指定玩家的岛屿等级 description: 计算指定玩家的岛屿等级
sethandicap: sethandicap:
parameters: "<player> <handicap>" parameters: <player> <handicap>
description: 设置偏差值,通常用于抵消初始岛屿等级,来保证岛屿等级从零开始。实际岛屿等级 - <handicap> = 最终的岛屿等级 description: 设置偏差值, 通常用于抵消初始岛屿等级, 来保证岛屿等级从零开始. 实际岛屿等级 - <handicap> = 最终的岛屿等级
changed: "&a 岛屿的偏差值从 [number] 更改为 [new_number]" changed: '&a岛屿的偏差值从[number]更改为[new_number]'
invalid-level: "&c 偏差值无效,请使用整数" invalid-level: '&c偏差值无效, 请使用整数'
levelstatus: levelstatus:
description: 显示等级计算队列中的岛屿 description: 显示等级计算队列中的岛屿
islands-in-queue: "&a 列队中的岛屿:[number]" islands-in-queue: '&a列队中的岛屿: [number]'
top: top:
description: 显示前十名 description: 显示前十名
unknown-world: "&c 未知的世界!" unknown-world: '&c未知的世界!'
display: "&f[rank]. &a[name] &7- &b[level]" display: '&f[rank]. &a[name] &7- &b[level]'
remove: remove:
description: 将玩家移出前十名 description: 将玩家移出前十名
parameters: "<player>" parameters: <player>
stats:
description: 显示该服务器上岛屿的统计数据
title: 服务器岛屿数据
world: '&a[name]'
no-data: '&c没有数据.'
average-level: '平均岛屿等级: [number]'
median-level: '中位数岛屿等级: [number]'
mode-level: '众数岛屿等级: [number]'
highest-level: '最高岛屿等级: [number]'
lowest-level: '最低岛屿等级: [number]'
distribution: '岛屿等级分布:'
islands: 个岛屿
island: island:
level: level:
parameters: "[player]" parameters: '[player]'
description: 计算你或指定玩家[player]的岛屿等级 description: 计算你或指定玩家[player]的岛屿等级
calculating: "&a 等级计算中..." calculating: '&a等级计算中...'
estimated-wait: "&a 预计等待时间:[number] 秒" estimated-wait: '&a预计等待时间: [number]秒'
in-queue: "&a 你处于队列中第 [number] 个" in-queue: '&a你处于队列中第[number]个'
island-level-is: "&a 岛屿等级为 &b[level]" island-level-is: '&a岛屿等级为: &b[level]'
required-points-to-next-level: "&a 还需 [points] 点数才能到达下一级" required-points-to-next-level: '&a还需[points]点数才能到达下一级'
deaths: "&c([number] 次死亡)" deaths: '&c([number]次死亡)'
cooldown: "&c 还需等待 &b[time] &c秒才能再次使用该指令" cooldown: '&c还需等待&b[time]&c秒才能再次使用该指令'
in-progress: "&6 岛级等级正在计算中..." in-progress: '&6岛屿等级正在计算中...'
time-out: "&c 等级计算超时。请稍后再试" time-out: '&c等级计算超时, 请稍后再试'
top: top:
description: 显示前十名 description: 显示前十名
gui-title: "&a 前十" gui-title: '&a前十'
gui-heading: "&6[name]: &B[rank]" gui-heading: '&6[name]: &B[rank]'
island-level: "&b 等级 [level]" island-level: '&b等级: [level]'
warp-to: "&a 正在传送到 [name] 的岛屿" warp-to: '&a正在传送到[name]的岛屿'
level-details: level-details:
above-sea-level-blocks: 海平面以上的方块 above-sea-level-blocks: 海平面以上的方块
spawners: 刷怪笼 spawners: 刷怪笼
underwater-blocks: 水下的方块 underwater-blocks: 水下的方块
all-blocks: 所有方块 all-blocks: 所有方块
no-island: "&c 没有岛屿!" no-island: '&c没有岛屿!'
names-island: "[name] 的岛屿" names-island: '[name]的岛屿'
syntax: "[name] x [number]" syntax: '[name] x [number]'
hint: "&c 运行level指令查看方块报告" hint: '&c运行level指令查看方块报告'
level: level:
commands: commands:
value: value:
parameters: "[hand|<material>]" parameters: '[hand|<material>]'
description: 显示方块的价值。在末尾添加 'hand' 可显示手中方块的价值 description: 显示方块的价值. 在末尾添加'hand'可显示手中方块的价值
gui: gui:
titles: titles:
top: "&0&l 岛屿排行榜" top: '&0&l岛屿排行榜'
detail-panel: "&0&l [name] 的岛屿" detail-panel: '&0&l[name]的岛屿'
value-panel: "&0&l 方块价值" value-panel: '&0&l方块价值'
buttons: buttons:
island: island:
empty: "&f&l 第 [name] 名" empty: '&f&l第[name]名'
name: "&f&l [name]" name: '&f&l[name]'
description: |- description: |-
[owner] [owner]
[members] [members]
[place] [place]
[level] [level]
owners-island: "[player] 的岛屿" owners-island: '[player]的岛屿'
owner: "&7&l 岛主:&r&b [player]" owner: '&7&l岛主: &r&b[player]'
members-title: "&7&l 成员:" members-title: '&7&l成员: '
member: "&b - [player]" member: '&b- [player]'
unknown: 未知 unknown: 未知
place: "&7第 &7&o[number] &r&7名" place: '&7第&7&o[number]&r&7名'
level: "&7 等级: &o [number]" level: '&7等级: &o[number]'
material: material:
name: "&f&l [number] x [material]" name: '&f&l [number] x [material]'
description: |- description: |-
[description] [description]
[count] [count]
@ -81,83 +95,79 @@ level:
[calculated] [calculated]
[limit] [limit]
[id] [id]
id: "&7 方块ID&e [id]" id: '&7方块ID: &e[id]'
value: "&7 方块价值:&e [number]" value: '&7方块价值: &e[number]'
limit: "&7 方块限制:&e [number]" limit: '&7方块限制: &e[number]'
count: "&7 方块数量:&e [number]" count: '&7方块数量: &e[number]'
calculated: "&7 计算值:&e [number]" calculated: '&7计算值: &e[number]'
all_blocks: all_blocks:
name: "&f&l 所有方块" name: '&f&l所有方块'
description: "&7 显示岛屿上所有的方块" description: '&7显示岛屿上所有的方块'
above_sea_level: above_sea_level:
name: "&f&l 海平面以上的方块" name: '&f&l海平面以上的方块'
description: |- description: '&7只显示所有海平面以上的方块'
&7 只显示所有
&7 海平面以上的方块
underwater: underwater:
name: "&f&l 海平面以下的方块" name: '&f&l海平面以下的方块'
description: |- description: 只显示所有海平面以下的方块
&7 只显示所有
&7 海平面以下的方块
spawner: spawner:
name: "&f&l 刷怪笼" name: '&f&l刷怪笼'
description: "&7 只显示刷怪笼" description: '&7只显示刷怪笼'
filters: filters:
name: name:
name: "&f&l 按名称排序" name: '&f&l按名称排序'
description: "&7 通过名称排序所有的方块" description: '&7通过名称排序所有的方块'
value: value:
name: "&f&l 按价值排序" name: '&f&l按价值排序'
description: "&7 通过价值排序所有的方块" description: '&7通过价值排序所有的方块'
count: count:
name: "&f&l 按数量排序" name: '&f&l按数量排序'
description: "&7 通过数量排序所有方块" description: '&7通过数量排序所有方块'
value: value:
name: "&f&l [material]" name: '&f&l[material]'
description: |- description: |-
[description] [description]
[value] [value]
[underwater] [underwater]
[limit] [limit]
[id] [id]
id: "&7 方块ID&e [id]" id: '&7方块ID: &e[id]'
value: "&7 方块价值:&e [number]" value: '&7方块价值: &e[number]'
underwater: "&7 海平面以下方块的价值:&e [number]" underwater: '&7海平面以下方块的价值: &e[number]'
limit: "&7 方块限制:&e [number]" limit: '&7方块限制: &e[number]'
previous: previous:
name: "&f&l 上一页" name: '&f&l上一页'
description: "&7 切换到第 [number] 页" description: '&7切换到第[number]页'
next: next:
name: "&f&l 下一页" name: '&f&l下一页'
description: "&7 切换到第 [number] 页" description: '&7切换到第[number]页'
search: search:
name: "&f&l 搜索" name: '&f&l搜索'
description: "&7 搜索特定的内容" description: '&7搜索特定的内容'
search: "&b 搜索值:[value]" search: '&b搜索值: [value]'
tips: tips:
click-to-view: "&e 点击 &7 查看" click-to-view: '&e点击 &7查看'
click-to-previous: "&e 点击 &7 查看上一页" click-to-previous: '&e点击 &7查看上一页'
click-to-next: "&e 点击 &7 查看下一页" click-to-next: '&e点击 &7查看下一页'
click-to-select: "&e 点击 &7 选择" click-to-select: '&e点击 &7选择'
left-click-to-cycle-up: "&e 左键点击 &7 向上循环" left-click-to-cycle-up: '&e左键 &7向上循环'
right-click-to-cycle-down: "&e 右键点击 &7 向下循环" right-click-to-cycle-down: '&e右键 &7向下循环'
left-click-to-change: "&e 左键点击 &7 编辑" left-click-to-change: '&e左键 &7编辑'
right-click-to-clear: "&e 右键点击 &7 清除" right-click-to-clear: '&e右键 &7清除'
click-to-asc: "&e 点击 &7 以升序排序" click-to-asc: '&e点击 &7以升序排序'
click-to-desc: "&e 点击 &7 以降序排序" click-to-desc: '&e点击 &7以降序排序'
click-to-warp: "&e 点击 &7 去岛屿传送点" click-to-warp: '&e点击 &7去岛屿传送点'
click-to-visit: "&e 点击 &7 参观" click-to-visit: '&e点击 &7参观'
right-click-to-visit: "&e 右键点击 &7 查看" right-click-to-visit: '&e右键 &7查看'
conversations: conversations:
prefix: "&l&6 [BentoBox]: &r" prefix: '&l&6[BentoBox]: &r'
no-data: "&c 运行level指令查看方块报告" no-data: '&c运行level指令查看方块报告'
cancel-string: cancel cancel-string: cancel
exit-string: cancel, exit, quit exit-string: cancel, exit, quit
write-search: "&e 请输入要搜索的值. (输入 'cancel' 退出)" write-search: '&e请输入要搜索的值. (输入''cancel''退出)'
search-updated: "&a 搜索值已更新" search-updated: '&a搜索值已更新'
cancelled: "&c 对话已取消!" cancelled: '&c对话已取消'
no-value: "&c 这件物品一文不值" no-value: '&c这件物品一文不值'
unknown-item: "&c 物品 '[material]' 在游戏中不存在" unknown-item: '&c物品''[material]''在游戏中不存在'
value: "&7 物品 '[material]' 的价值:&e[value]" value: '&7物品''[material]''的价值: &e[value]'
value-underwater: "&7 物品 '[material]' 在海平面以下的价值:&e[value]" value-underwater: '&7物品''[material]''在海平面以下的价值: &e[value]'
empty-hand: "&c 你的手中没有拿着方块" empty-hand: '&c你的手中没有拿着方块'

View File

@ -24,6 +24,28 @@ detail_panel:
1: 1:
# Column number # Column number
2: 2:
# Icon is a Bukkit Material.
icon: ICE
# Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file
title: level.gui.buttons.value_blocks.name
# Description of the button shown to the user in the lore. This is a reference and the reference will be translatable in the locale file
description: level.gui.buttons.value_blocks.description
# The data section is a key-value list of data relavent for this button. It is interpreted by the code implemented the panel.
# The convention is to specify the type and the panel tab that will open if pressed. These are Enums in the code.
data:
# Type button will go to the ALL_BLOCKS tab when clicked.
type: TAB
tab: VALUE_BLOCKS
# Actions cover what happens if the button is clicked or the mouse is moved over it. There can be multiple actions possible for different
# click-types.
actions:
# Each action has an arbitrary descriptive name to define it.
view:
# The click-type is the same as the bukkit {@link org.bukkit.event.inventory.ClickType}. UNKNOWN is the default.
click-type: unknown
# tooltip is a locale reference that will be translated for the user and shown when they hover over the button.
tooltip: level.gui.tips.click-to-view
3:
# Icon is a Bukkit Material. # Icon is a Bukkit Material.
icon: STONE icon: STONE
# Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file # Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file
@ -45,7 +67,7 @@ detail_panel:
click-type: unknown click-type: unknown
# tooltip is a locale reference that will be translated for the user and shown when they hover over the button. # tooltip is a locale reference that will be translated for the user and shown when they hover over the button.
tooltip: level.gui.tips.click-to-view tooltip: level.gui.tips.click-to-view
3: 4:
icon: GRASS_BLOCK icon: GRASS_BLOCK
title: level.gui.buttons.above_sea_level.name title: level.gui.buttons.above_sea_level.name
description: level.gui.buttons.above_sea_level.description description: level.gui.buttons.above_sea_level.description
@ -56,7 +78,7 @@ detail_panel:
view: view:
click-type: unknown click-type: unknown
tooltip: level.gui.tips.click-to-view tooltip: level.gui.tips.click-to-view
4: 5:
icon: WATER_BUCKET icon: WATER_BUCKET
title: level.gui.buttons.underwater.name title: level.gui.buttons.underwater.name
description: level.gui.buttons.underwater.description description: level.gui.buttons.underwater.description
@ -67,7 +89,7 @@ detail_panel:
view: view:
click-type: unknown click-type: unknown
tooltip: level.gui.tips.click-to-view tooltip: level.gui.tips.click-to-view
5: 6:
icon: SPAWNER icon: SPAWNER
title: level.gui.buttons.spawner.name title: level.gui.buttons.spawner.name
description: level.gui.buttons.spawner.description description: level.gui.buttons.spawner.description
@ -110,18 +132,11 @@ detail_panel:
8: material_button 8: material_button
3: 3:
1: 1:
# In this case, the icon is defined as a TIPPED_ARROW with and enchantment applied. The format for the enchantment is # In this case, the icon is defined as a TIPPED_ARROW with a color.
# define in {@link world.bentobox.bentobox.util.ItemParser} and available for POTIONS or TIPPED_ARROWs. # CustomPotionColor uses the Decimal description of a Color, just as leather armor does.
# Format TIPPED_ARROW:NAME:<LEVEL>:<EXTENDED>:<SPLASH/LINGER>:QTY # All you need to do is take a hex code of a color (like #ff00aa) which represents red,
# LEVEL, EXTENDED, SPLASH, LINGER are optional. # green, blue as 2 hex digits each and convert that number into a decimal, using a hex to decimal calculator.
# LEVEL is a number, 1 or 2 icon: tipped_arrow{CustomPotionColor:11546150}
# LINGER is for V1.9 servers and later
# Examples:
# TIPPED_ARROW:STRENGTH:1:EXTENDED:SPLASH:1
# TIPPED_ARROW:INSTANT_DAMAGE:2::LINGER:2
# TIPPED_ARROW:JUMP:2:NOTEXTENDED:NOSPLASH:1
# TIPPED_ARROW:WEAKNESS::::1 - any weakness enchantment
icon: TIPPED_ARROW:INSTANT_HEAL::::1
title: level.gui.buttons.previous.name title: level.gui.buttons.previous.name
description: level.gui.buttons.previous.description description: level.gui.buttons.previous.description
data: data:
@ -139,7 +154,7 @@ detail_panel:
7: material_button 7: material_button
8: material_button 8: material_button
9: 9:
icon: TIPPED_ARROW:JUMP::::1 icon: tipped_arrow{CustomPotionColor:8439583}
title: level.gui.buttons.next.name title: level.gui.buttons.next.name
description: level.gui.buttons.next.description description: level.gui.buttons.next.description
data: data:

View File

@ -64,7 +64,7 @@ value_panel:
8: material_button 8: material_button
3: 3:
1: 1:
icon: TIPPED_ARROW:INSTANT_HEAL::::1 icon: tipped_arrow{CustomPotionColor:11546150}
title: level.gui.buttons.previous.name title: level.gui.buttons.previous.name
description: level.gui.buttons.previous.description description: level.gui.buttons.previous.description
data: data:
@ -82,7 +82,7 @@ value_panel:
7: material_button 7: material_button
8: material_button 8: material_button
9: 9:
icon: TIPPED_ARROW:JUMP::::1 icon: tipped_arrow{CustomPotionColor:8439583}
title: level.gui.buttons.next.name title: level.gui.buttons.next.name
description: level.gui.buttons.next.description description: level.gui.buttons.next.description
data: data:

View File

@ -61,6 +61,7 @@ import world.bentobox.bentobox.managers.FlagsManager;
import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.managers.PlaceholdersManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.level.config.BlockConfig; import world.bentobox.level.config.BlockConfig;
import world.bentobox.level.config.ConfigSettings; import world.bentobox.level.config.ConfigSettings;
import world.bentobox.level.listeners.IslandActivitiesListeners; import world.bentobox.level.listeners.IslandActivitiesListeners;
@ -72,7 +73,7 @@ import world.bentobox.level.listeners.JoinLeaveListener;
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({ Bukkit.class, BentoBox.class, User.class }) @PrepareForTest({ Bukkit.class, BentoBox.class, User.class, Util.class })
public class LevelTest { public class LevelTest {
private static File jFile; private static File jFile;
@ -189,6 +190,11 @@ public class LevelTest {
when(Bukkit.getServer()).thenReturn(server); when(Bukkit.getServer()).thenReturn(server);
when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger());
when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class)); when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class));
when(Bukkit.getBukkitVersion()).thenReturn("");
// Util
PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS);
when(Util.inTest()).thenReturn(true);
// Addon // Addon
addon = new Level(); addon = new Level();
@ -221,7 +227,6 @@ public class LevelTest {
when(fm.getFlags()).thenReturn(Collections.emptyList()); when(fm.getFlags()).thenReturn(Collections.emptyList());
// Bukkit // Bukkit
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getScheduler()).thenReturn(scheduler); when(Bukkit.getScheduler()).thenReturn(scheduler);
ItemMeta meta = mock(ItemMeta.class); ItemMeta meta = mock(ItemMeta.class);
ItemFactory itemFactory = mock(ItemFactory.class); ItemFactory itemFactory = mock(ItemFactory.class);

View File

@ -94,7 +94,7 @@ public class LevelsManagerTest {
private World world; private World world;
@Mock @Mock
private Player player; private Player player;
@Mock
private ConfigSettings settings; private ConfigSettings settings;
@Mock @Mock
private User user; private User user;
@ -165,6 +165,7 @@ public class LevelsManagerTest {
when(im.hasIsland(eq(world), any(UUID.class))).thenReturn(true); when(im.hasIsland(eq(world), any(UUID.class))).thenReturn(true);
when(im.getIsland(world, uuid)).thenReturn(island); when(im.getIsland(world, uuid)).thenReturn(island);
when(im.getIslandById(anyString())).thenReturn(Optional.of(island)); when(im.getIslandById(anyString())).thenReturn(Optional.of(island));
when(im.getIslandById(anyString(), eq(false))).thenReturn(Optional.of(island));
// Player // Player
when(player.getUniqueId()).thenReturn(uuid); when(player.getUniqueId()).thenReturn(uuid);
@ -174,6 +175,7 @@ public class LevelsManagerTest {
when(world.getName()).thenReturn("bskyblock-world"); when(world.getName()).thenReturn("bskyblock-world");
// Settings // Settings
settings = new ConfigSettings();
when(addon.getSettings()).thenReturn(settings); when(addon.getSettings()).thenReturn(settings);
// User // User
@ -332,7 +334,7 @@ public class LevelsManagerTest {
@Test @Test
public void testFormatLevel() { public void testFormatLevel() {
assertEquals("123456789", lm.formatLevel(123456789L)); assertEquals("123456789", lm.formatLevel(123456789L));
when(settings.isShorthand()).thenReturn(true); settings.setShorthand(true);
assertEquals("123.5M", lm.formatLevel(123456789L)); assertEquals("123.5M", lm.formatLevel(123456789L));
assertEquals("1.2k", lm.formatLevel(1234L)); assertEquals("1.2k", lm.formatLevel(1234L));
assertEquals("123.5G", lm.formatLevel(123456789352L)); assertEquals("123.5G", lm.formatLevel(123456789352L));
@ -395,8 +397,8 @@ public class LevelsManagerTest {
lm.loadTopTens(); lm.loadTopTens();
PowerMockito.verifyStatic(Bukkit.class); // 1 PowerMockito.verifyStatic(Bukkit.class); // 1
Bukkit.getScheduler(); Bukkit.getScheduler();
verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture()); verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture()); // Capture the task in the scheduler
task.getValue().run(); task.getValue().run(); // run it
verify(addon).log("Generating rankings"); verify(addon).log("Generating rankings");
verify(addon).log("Generated rankings for bskyblock-world"); verify(addon).log("Generated rankings for bskyblock-world");

View File

@ -9,9 +9,9 @@ import static org.mockito.Mockito.mock;
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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -138,7 +138,7 @@ public class PlaceholderManagerTest {
when(im.getIsland(any(World.class), any(User.class))).thenReturn(island); when(im.getIsland(any(World.class), any(User.class))).thenReturn(island);
when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island)); when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island));
when(im.getIslandById(anyString())).thenAnswer((Answer<Optional<Island>>) invocation -> Optional.of(islands.get(invocation.getArgument(0, String.class)))); when(im.getIslandById(anyString())).thenAnswer((Answer<Optional<Island>>) invocation -> Optional.of(islands.get(invocation.getArgument(0, String.class))));
when(im.getIslands(any(), any(UUID.class))).thenReturn(new HashSet<>(islands.values())); when(im.getIslands(any(), any(UUID.class))).thenReturn(new ArrayList<>(islands.values()));
when(addon.getIslands()).thenReturn(im); when(addon.getIslands()).thenReturn(im);
// Levels Manager // Levels Manager

View File

@ -10,7 +10,7 @@ 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.Collections;
import java.util.Set; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -120,8 +120,8 @@ public class AdminTopRemoveCommandTest {
when(island.getOwner()).thenReturn(uuid); when(island.getOwner()).thenReturn(uuid);
// Island Manager // Island Manager
when(plugin.getIslands()).thenReturn(im); when(plugin.getIslands()).thenReturn(im);
when(im.getIslands(any(), any(User.class))).thenReturn(Set.of(island)); when(im.getIslands(any(), any(User.class))).thenReturn(List.of(island));
when(im.getIslands(any(), any(UUID.class))).thenReturn(Set.of(island)); when(im.getIslands(any(), any(UUID.class))).thenReturn(List.of(island));
// Bukkit // Bukkit
PowerMockito.mockStatic(Bukkit.class); PowerMockito.mockStatic(Bukkit.class);