Merge pull request #318 from BentoBoxWorld/develop

Release 2.14.0
This commit is contained in:
tastybento 2024-06-30 14:58:10 -07:00 committed by GitHub
commit 45cfe39a76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 298 additions and 285 deletions

View File

@ -59,7 +59,7 @@
<powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>2.0.0-SNAPSHOT</bentobox.version>
<bentobox.version>2.4.0-SNAPSHOT</bentobox.version>
<!-- Warps addon version -->
<warps.version>1.12.0</warps.version>
<!-- Visit addon version -->
@ -71,7 +71,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>2.13.0</build.version>
<build.version>2.14.0</build.version>
<sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>

View File

@ -414,7 +414,8 @@ public class LevelsManager {
addon.log("Generating rankings");
handler.loadObjects().forEach(il -> {
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()));
}
});

View File

@ -608,41 +608,32 @@ public class IslandLevelCalculator {
// Done
pipeliner.getInProcessQueue().remove(this);
// Chunk finished
// This was the last chunk
handleStackedBlocks();
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 was the last chunk. Handle stacked blocks, then chests and exit
handleStackedBlocks().thenCompose(v -> handleChests()).thenRun(() -> {
this.tidyUp();
this.getR().complete(getResults());
finishTask.cancel();
}
}, 0, 10L);
});
}
});
}
private void handleChests() {
Iterator<Chunk> it = chestBlocks.iterator();
while (it.hasNext()) {
Chunk v = it.next();
Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
private CompletableFuture<Void> handleChests() {
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (Chunk v : chestBlocks) {
CompletableFuture<Void> future = Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(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
Iterator<Location> it = stackedBlocks.iterator();
while (it.hasNext()) {
Location v = it.next();
Util.getChunkAtAsync(v).thenAccept(c -> {
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (Location v : stackedBlocks) {
CompletableFuture<Void> future = Util.getChunkAtAsync(v).thenAccept(c -> {
Block stackedBlock = v.getBlock();
boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
@ -657,8 +648,12 @@ public class IslandLevelCalculator {
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

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

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

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

View File

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

View File

@ -4,6 +4,7 @@ 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.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -165,6 +166,7 @@ public class LevelsManagerTest {
when(im.hasIsland(eq(world), any(UUID.class))).thenReturn(true);
when(im.getIsland(world, uuid)).thenReturn(island);
when(im.getIslandById(anyString())).thenReturn(Optional.of(island));
when(im.getIslandById(anyString(), eq(false))).thenReturn(Optional.of(island));
// Player
when(player.getUniqueId()).thenReturn(uuid);
@ -395,8 +397,8 @@ public class LevelsManagerTest {
lm.loadTopTens();
PowerMockito.verifyStatic(Bukkit.class); // 1
Bukkit.getScheduler();
verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture());
task.getValue().run();
verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture()); // Capture the task in the scheduler
task.getValue().run(); // run it
verify(addon).log("Generating rankings");
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.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
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.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.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);
// Levels Manager

View File

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