Skip to content

Commit f9376f1

Browse files
authored
Merge pull request #455 from BentoBoxWorld/450_actionbar_support
Add ActionBar support #450
2 parents f192e38 + f5e1756 commit f9376f1

File tree

10 files changed

+161
-30
lines changed

10 files changed

+161
-30
lines changed

pom.xml

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,12 @@
123123

124124
<repositories>
125125
<repository>
126-
<id>spigot-repo</id>
127-
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
126+
<id>jitpack.io</id>
127+
<url>https://jitpack.io</url>
128+
</repository>
129+
<repository>
130+
<id>papermc</id>
131+
<url>https://repo.papermc.io/repository/maven-public/</url>
128132
</repository>
129133
<repository>
130134
<id>minecraft-repo</id>
@@ -153,18 +157,17 @@
153157
</repositories>
154158

155159
<dependencies>
156-
<!-- Spigot API -->
157160
<dependency>
158-
<groupId>org.spigotmc</groupId>
159-
<artifactId>spigot-api</artifactId>
160-
<version>${spigot.version}</version>
161-
<scope>provided</scope>
162-
</dependency>
161+
<groupId>com.github.MockBukkit</groupId>
162+
<artifactId>MockBukkit</artifactId>
163+
<version>v1.21-SNAPSHOT</version>
164+
<scope>test</scope>
165+
</dependency>
163166
<dependency>
164-
<groupId>org.spigotmc</groupId>
165-
<artifactId>plugin-annotations</artifactId>
166-
<version>1.2.3-SNAPSHOT</version>
167-
<scope>compile</scope>
167+
<groupId>io.papermc.paper</groupId>
168+
<artifactId>paper-api</artifactId>
169+
<version>1.21.10-R0.1-SNAPSHOT</version>
170+
<scope>provided</scope>
168171
</dependency>
169172
<!-- Apache Commons Text -->
170173
<dependency>
@@ -223,7 +226,6 @@
223226
<version>${items-adder.version}</version>
224227
<scope>provided</scope>
225228
</dependency>
226-
227229
</dependencies>
228230

229231
<build>
@@ -292,7 +294,6 @@
292294
<version>3.0.0-M5</version>
293295
<configuration>
294296
<argLine>
295-
${argLine}
296297
--add-opens java.base/java.lang=ALL-UNNAMED
297298
--add-opens java.base/java.math=ALL-UNNAMED
298299
--add-opens java.base/java.io=ALL-UNNAMED

src/main/java/world/bentobox/aoneblock/AOneBlock.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,15 @@ public class AOneBlock extends GameModeAddon {
8989
.listener(bossBar)
9090
.defaultSetting(true)
9191
.build();
92-
92+
/**
93+
* Flag to enable or disable the OneBlock action bar.
94+
*/
95+
public final Flag ONEBLOCK_ACTIONBAR = new Flag.Builder("ONEBLOCK_ACTIONBAR", Material.IRON_BARS)
96+
.mode(Mode.BASIC)
97+
.type(Type.SETTING)
98+
.listener(bossBar)
99+
.defaultSetting(true)
100+
.build();
93101
/**
94102
* Flag to set who can break the magic block.
95103
*/
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package world.bentobox.aoneblock.commands.island;
2+
3+
import java.util.List;
4+
5+
import world.bentobox.aoneblock.AOneBlock;
6+
import world.bentobox.bentobox.api.commands.CompositeCommand;
7+
import world.bentobox.bentobox.api.user.User;
8+
9+
public class IslandActionBarCommand extends CompositeCommand {
10+
11+
private AOneBlock addon;
12+
13+
public IslandActionBarCommand(CompositeCommand islandCommand, String label, String[] aliases)
14+
{
15+
super(islandCommand, label, aliases);
16+
}
17+
18+
@Override
19+
public void setup() {
20+
setDescription("aoneblock.commands.island.actionbar.description");
21+
setOnlyPlayer(true);
22+
// Permission
23+
setPermission("island.actionbar");
24+
addon = getAddon();
25+
}
26+
27+
@Override
28+
public boolean execute(User user, String label, List<String> args) {
29+
addon.getBossBar().toggleUser(user);
30+
getIslands().getIslandAt(user.getLocation()).ifPresent(i -> {
31+
if (!i.isAllowed(addon.ONEBLOCK_ACTIONBAR)) {
32+
user.sendMessage("aoneblock.actionbar.not-active");
33+
}
34+
});
35+
return true;
36+
}
37+
}

src/main/java/world/bentobox/aoneblock/listeners/BossBarListener.java

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import org.bukkit.event.player.PlayerQuitEvent;
1818
import org.eclipse.jdt.annotation.NonNull;
1919

20+
import net.kyori.adventure.text.Component;
21+
import net.kyori.adventure.text.format.NamedTextColor;
22+
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
2023
import world.bentobox.aoneblock.AOneBlock;
2124
import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
2225
import world.bentobox.aoneblock.events.MagicBlockEvent;
@@ -30,6 +33,12 @@
3033
public class BossBarListener implements Listener {
3134

3235
private static final String AONEBLOCK_BOSSBAR = "aoneblock.bossbar";
36+
private static final String AONEBLOCK_ACTIONBAR = "aoneblock.actionbar";
37+
38+
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.builder()
39+
.character('&')
40+
.hexColors() // Enables support for modern hex codes (e.g., &#FF0000) alongside legacy codes.
41+
.build();
3342

3443
public BossBarListener(AOneBlock addon) {
3544
super();
@@ -45,12 +54,14 @@ public BossBarListener(AOneBlock addon) {
4554
public void onBreakBlockEvent(MagicBlockEvent e) {
4655
// Update boss bar
4756
tryToShowBossBar(e.getPlayerUUID(), e.getIsland());
57+
tryToShowActionBar(e.getPlayerUUID(), e.getIsland());
4858
}
4959

5060
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
5161
public void onEnterIsland(IslandEnterEvent event) {
5262
if (addon.inWorld(event.getIsland().getWorld())) {
5363
tryToShowBossBar(event.getPlayerUUID(), event.getIsland());
64+
tryToShowActionBar(event.getPlayerUUID(), event.getIsland());
5465
}
5566
}
5667

@@ -59,10 +70,56 @@ public void onFlagChange(FlagSettingChangeEvent e) {
5970
if (e.getEditedFlag() == addon.ONEBLOCK_BOSSBAR) {
6071
// Show to players on island. If it isn't allowed then this will clean up the boss bar too
6172
e.getIsland().getPlayersOnIsland().stream().map(Player::getUniqueId)
62-
.forEach(uuid -> this.tryToShowBossBar(uuid, e.getIsland()));
73+
.forEach(uuid -> {
74+
tryToShowBossBar(uuid, e.getIsland());
75+
tryToShowActionBar(uuid, e.getIsland());
76+
});
6377
}
6478
}
6579

80+
/**
81+
* Converts a string containing Bukkit color codes ('&') into an Adventure Component.
82+
*
83+
* @param legacyString The string with Bukkit color and format codes.
84+
* @return The resulting Adventure Component.
85+
*/
86+
public static Component bukkitToAdventure(String legacyString) {
87+
if (legacyString == null) {
88+
return Component.empty();
89+
}
90+
return LEGACY_SERIALIZER.deserialize(legacyString);
91+
}
92+
93+
private void tryToShowActionBar(UUID uuid, Island island) {
94+
User user = User.getInstance(uuid);
95+
Player player = Bukkit.getPlayer(uuid);
96+
97+
// Only show if enabled for island
98+
if (!island.isAllowed(addon.ONEBLOCK_ACTIONBAR)) {
99+
return;
100+
}
101+
// Default to showing boss bar unless it is explicitly turned off
102+
if (!user.getMetaData(AONEBLOCK_ACTIONBAR).map(MetaDataValue::asBoolean).orElse(true)) {
103+
// Remove any boss bar from user if they are in the world
104+
removeBar(user, island);
105+
// Do not show a boss bar
106+
return;
107+
}
108+
// Get the progress
109+
@NonNull
110+
OneBlockIslands obi = addon.getOneBlocksIsland(island);
111+
112+
// --- Create the Action Bar Component ---
113+
int numBlocksToGo = addon.getOneBlockManager().getNextPhaseBlocks(obi);
114+
int phaseBlocks = addon.getOneBlockManager().getPhaseBlocks(obi);
115+
int done = phaseBlocks - numBlocksToGo;
116+
String translation = user.getTranslationOrNothing("aoneblock.actionbar.status", "[togo]",
117+
String.valueOf(numBlocksToGo), "[total]", String.valueOf(phaseBlocks), "[done]", String.valueOf(done),
118+
"[phase-name]", obi.getPhaseName(), "[percent-done]",
119+
Math.round(addon.getOneBlockManager().getPercentageDone(obi)) + "%");
120+
// Send
121+
player.sendActionBar(bukkitToAdventure(translation));
122+
}
66123
/**
67124
* Try to show the bossbar to the player
68125
* @param uuid player's UUID
@@ -128,7 +185,6 @@ private void tryToShowBossBar(UUID uuid, Island island) {
128185
}
129186
// Save the boss bar for later reference (e.g., when updating or removing)
130187
islandBossBars.put(island, bar);
131-
132188
}
133189

134190
private void removeBar(User user, Island island) {
@@ -159,7 +215,7 @@ public void onJoin(PlayerJoinEvent e) {
159215
return;
160216
}
161217
addon.getIslands().getIslandAt(e.getPlayer().getLocation())
162-
.ifPresent(is -> this.tryToShowBossBar(e.getPlayer().getUniqueId(), is));
218+
.ifPresent(is -> this.tryToShowBossBar(e.getPlayer().getUniqueId(), is));
163219
}
164220

165221
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@@ -179,7 +235,7 @@ public void toggleUser(User user) {
179235
if (newState) {
180236
// If the player is on an island then show the bar
181237
addon.getIslands().getIslandAt(user.getLocation()).filter(is -> addon.inWorld(is.getWorld()))
182-
.ifPresent(is -> this.tryToShowBossBar(user.getUniqueId(), is));
238+
.ifPresent(is -> this.tryToShowBossBar(user.getUniqueId(), is));
183239
user.sendMessage("aoneblock.commands.island.bossbar.status_on");
184240
} else {
185241
// Remove player from any boss bars. Adding happens automatically

src/main/java/world/bentobox/aoneblock/oneblocks/OneBlocksManager.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.util.jar.JarFile;
2020
import java.util.stream.Collectors;
2121

22-
import org.apache.commons.lang.math.NumberUtils;
22+
import org.apache.commons.lang3.math.NumberUtils;
2323
import org.bukkit.Material;
2424
import org.bukkit.NamespacedKey;
2525
import org.bukkit.Registry;
@@ -260,7 +260,7 @@ private Map<Integer, OneBlockObject> parseFirstBlocksConfig(ConfigurationSection
260260
Map<Integer, OneBlockObject> result = new HashMap<>();
261261

262262
for (String key : firstBlocksConfig.getKeys(false)) {
263-
if (!NumberUtils.isNumber(key)) {
263+
if (!NumberUtils.isCreatable(key)) {
264264
addon.logError("Fixed block key must be an integer. " + key);
265265
continue;
266266
}
@@ -335,7 +335,7 @@ private void addHologramLines(OneBlockPhase obPhase, ConfigurationSection fb) {
335335
return;
336336
Map<Integer, String> result = new HashMap<>();
337337
for (String key : fb.getKeys(false)) {
338-
if (!NumberUtils.isNumber(key)) {
338+
if (!NumberUtils.isCreatable(key)) {
339339
addon.logError("Fixed block key must be an integer. " + key);
340340
continue;
341341
}

src/main/resources/locales/en-US.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ protection:
2424
description: |
2525
&b Shows a status bar
2626
&b for each phase.
27+
28+
ONEBLOCK_ACTIONBAR:
29+
name: Action Bar
30+
description: |
31+
&b Shows a status
32+
&b for each phase
33+
&b in the Action Bar.
34+
2735
aoneblock:
2836
bossbar:
2937
title: "Blocks remaining"
@@ -35,6 +43,9 @@ aoneblock:
3543
# SOLID, SEGMENTED_6, SEGMENTED_10, SEGMENTED_12, SEGMENTED_20
3644
style: SOLID
3745
not-active: "&c Boss Bar is not active for this island"
46+
actionbar:
47+
status: "&a Phase: &b [phase-name] &d | &a Blocks: &b [done] &d / &b [total] &d | &a Progression: &b [percent-done]"
48+
not-active: "&c Action Bar is not active for this island"
3849
commands:
3950
admin:
4051
setcount:
@@ -71,6 +82,10 @@ aoneblock:
7182
description: "toggles phase boss bar"
7283
status_on: "&b Bossbar turned &a on"
7384
status_off: "&b Bossbar turned &c off"
85+
actionbar:
86+
description: "toggles phase action bar"
87+
status_on: "&b Action Bar turned &a on"
88+
status_off: "&b Action Bar turned &c off"
7489
setcount:
7590
parameters: "<count>"
7691
description: "set block count to previously completed value"

src/test/java/world/bentobox/aoneblock/AOneBlockTest.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import static org.junit.Assert.assertTrue;
88
import static org.mockito.ArgumentMatchers.any;
99
import static org.mockito.ArgumentMatchers.anyString;
10+
import static org.mockito.Mockito.RETURNS_MOCKS;
1011
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.mockitoSession;
1113
import static org.mockito.Mockito.never;
1214
import static org.mockito.Mockito.verify;
1315
import static org.mockito.Mockito.when;
@@ -40,6 +42,7 @@
4042
import org.junit.BeforeClass;
4143
import org.junit.Test;
4244
import org.junit.runner.RunWith;
45+
import org.mockbukkit.mockbukkit.MockBukkit;
4346
import org.mockito.Mock;
4447
import org.mockito.Mockito;
4548
import org.mockito.stubbing.Answer;
@@ -110,7 +113,7 @@ public static void beforeClass() throws IllegalAccessException, InvocationTarget
110113

111114
@After
112115
public void tearDown() throws IOException {
113-
ServerMocks.unsetBukkitServer();
116+
MockBukkit.unmock();
114117
User.clearUsers();
115118
Mockito.framework().clearInlineMocks();
116119
deleteAll(new File("database"));
@@ -132,7 +135,8 @@ private void deleteAll(File file) throws IOException {
132135
*/
133136
@Before
134137
public void setUp() throws Exception {
135-
Server server = ServerMocks.newServer();
138+
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
139+
Server server = MockBukkit.mock();
136140
// Set up plugin
137141
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
138142
when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());
@@ -174,7 +178,7 @@ public void setUp() throws Exception {
174178
.thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
175179

176180
// Server
177-
PowerMockito.mockStatic(Bukkit.class);
181+
PowerMockito.mockStatic(Bukkit.class, RETURNS_MOCKS);
178182
when(Bukkit.getServer()).thenReturn(server);
179183
when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger());
180184
when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class));

src/test/java/world/bentobox/aoneblock/listeners/BlockProtectTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.bukkit.Location;
2121
import org.bukkit.Material;
2222
import org.bukkit.Particle;
23+
import org.bukkit.Server;
2324
import org.bukkit.World;
2425
import org.bukkit.block.Block;
2526
import org.bukkit.block.BlockFace;
@@ -39,11 +40,13 @@
3940
import org.junit.Before;
4041
import org.junit.Test;
4142
import org.junit.runner.RunWith;
43+
import org.mockbukkit.mockbukkit.MockBukkit;
4244
import org.mockito.Mock;
4345
import org.powermock.modules.junit4.PowerMockRunner;
4446

4547
import world.bentobox.aoneblock.AOneBlock;
4648
import world.bentobox.aoneblock.Settings;
49+
import world.bentobox.aoneblock.mocks.ServerMocks;
4750
import world.bentobox.bentobox.database.objects.Island;
4851
import world.bentobox.bentobox.managers.IslandsManager;
4952

@@ -75,6 +78,8 @@ public class BlockProtectTest {
7578
*/
7679
@Before
7780
public void setUp() throws Exception {
81+
82+
Server server = MockBukkit.mock();
7883

7984
when(p.getWorld()).thenReturn(world);
8085
// In World
@@ -105,6 +110,7 @@ public void setUp() throws Exception {
105110
*/
106111
@After
107112
public void tearDown() throws Exception {
113+
MockBukkit.unmock();
108114
}
109115

110116
/**

0 commit comments

Comments
 (0)