Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions src/main/java/world/bentobox/topblock/TopBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import org.bukkit.World;

Expand Down Expand Up @@ -65,23 +66,38 @@ public void onEnable() {
// Hook into whichever supported game modes are present. Game-mode classes are
// only referenced inside the hook constructors, which run after the presence
// check, so a game mode that is not installed is never class-loaded.
findGameMode("aoneblock").ifPresent(gm -> {
log("TopBlock hooking into AOneBlock");
registerCommands(gm);
hooks.add(new AOneBlockHook(gm));
});
findGameMode("chunkblock").ifPresent(gm -> {
log("TopBlock hooking into ChunkBlock");
registerCommands(gm);
hooks.add(new ChunkBlockHook(gm));
});
findGameMode("aoneblock").ifPresent(gm -> hook(gm, "AOneBlock", () -> new AOneBlockHook(gm)));
findGameMode("chunkblock").ifPresent(gm -> hook(gm, "ChunkBlock", () -> new ChunkBlockHook(gm)));

if (hooks.isEmpty()) {
logError("Could not hook into AOneBlock or ChunkBlock. Is at least one loaded?");
this.setState(State.DISABLED);
}
}

/**
* Add a hook for a game mode that the BentoBox API says is present and enabled.
* The game mode's classes still have to be visible to this addon's class loader, which
* is not guaranteed - e.g. if the game mode is installed as a BentoBox addon jar while
* TopBlock is loaded as a Pladdon plugin. If they are not, skip that game mode rather
* than letting the LinkageError mark the whole addon as incompatible.
* @param gm game mode addon
* @param name friendly name of the game mode, for logging
* @param hookSupplier supplier of the hook. Must be a lambda so that the game mode's
* classes are only loaded when it is called.
*/
private void hook(GameModeAddon gm, String name, Supplier<TopBlockHook> hookSupplier) {
try {
TopBlockHook hook = hookSupplier.get();
registerCommands(gm);
hooks.add(hook);
log("TopBlock hooking into " + name);
} catch (LinkageError e) {
logError(name + " is loaded, but TopBlock cannot see its classes: " + e.getMessage());
logError("Install " + name + " as a plugin (jar in the plugins folder) so that TopBlock can hook into it.");
}
}

private Optional<GameModeAddon> findGameMode(String name) {
return getPlugin().getAddonsManager().getAddonByName(name)
.filter(Addon::isEnabled)
Expand Down
1 change: 1 addition & 0 deletions src/main/java/world/bentobox/topblock/TopBlockManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ void refresh(TopBlockHook hook) {
List<TopTenData> data = new ArrayList<>();
hook.getAllIslandData().stream().filter(i -> i.lifetime() > 0).forEach(i ->
addon.getIslands().getIslandById(i.uniqueId())
.filter(island -> hook.getGameMode().inWorld(island.getWorld()))
.filter(this::ownerInTopTen)
.ifPresent(island ->
data.add(new TopTenData(island, i.blockNumber(), i.lifetime(), i.phaseName()))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ private void populateIslandIcon(PanelItemBuilder builder, ItemTemplateRecord tem
} else {
builder.icon(owner.getName());
}
} else if (template.icon() != null) {
} else if (template.icon() != null
&& template.icon().getType() != Material.PLAYER_HEAD) {
builder.icon(template.icon().clone());
} else if (owner != null) {
builder.icon(owner.getName());
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ main: world.bentobox.topblock.TopBlockPladdon
version: ${project.version}${build.number}
api-version: "1.21"

# Pladdon names of the game modes we hook into. Soft, because either alone is enough,
# but required so that our plugin class loader can see their classes.
softdepend:
- BentoBox-AOneBlock
- BentoBox-ChunkBlock

authors: [tastybento]
contributors: ["The BentoBoxWorld Community"]
website: https://bentobox.world
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package world.bentobox.topblock;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
Expand Down Expand Up @@ -51,6 +52,8 @@ public void setUp() throws Exception {
when(addon.getIslands()).thenReturn(im);
when(addon.getPlayers()).thenReturn(playersMgr);
when(hook.getGameMode()).thenReturn(gameMode);
when(gameMode.inWorld(any(org.bukkit.World.class))).thenReturn(true);
when(island.getWorld()).thenReturn(world);
when(im.getIslandById(anyString())).thenReturn(Optional.of(island));

// Single island in top ten
Expand Down
20 changes: 20 additions & 0 deletions src/test/java/world/bentobox/topblock/TopBlockManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;

import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.topblock.TopBlockManager.TopTenData;
import world.bentobox.topblock.config.ConfigSettings;
import world.bentobox.topblock.hooks.IslandBlockData;
Expand All @@ -29,6 +31,8 @@ class TopBlockManagerTest extends CommonTestSetup {
private TopBlock addon;
@Mock
private TopBlockHook hook;
@Mock
private GameModeAddon gma;

private TopBlockManager tbm;
private ConfigSettings settings;
Expand All @@ -45,6 +49,8 @@ public void setUp() throws Exception {
when(addon.getIslands()).thenReturn(im);
when(im.getIslandById(anyString())).thenReturn(Optional.of(island));
when(island.getWorld()).thenReturn(world);
when(hook.getGameMode()).thenReturn(gma);
when(gma.inWorld(world)).thenReturn(true);
when(iwm.getPermissionPrefix(any())).thenReturn("aoneblock.");

tbm = new TopBlockManager(addon);
Expand Down Expand Up @@ -111,6 +117,9 @@ void testRefreshReplacesPreviousResults() {
@Test
void testTopTensAreSeparatePerHook() {
TopBlockHook hook2 = mock(TopBlockHook.class);
GameModeAddon gma2 = mock(GameModeAddon.class);
when(hook2.getGameMode()).thenReturn(gma2);
when(gma2.inWorld(world)).thenReturn(true);
when(addon.getHooks()).thenReturn(List.of(hook, hook2));
when(hook.getAllIslandData()).thenReturn(List.of(ib(80, 250, "Underground")));
when(hook2.getAllIslandData()).thenReturn(List.of(
Expand Down Expand Up @@ -231,6 +240,17 @@ void testRefreshExcludesIslandWithoutOwner() {
assertTrue(tbm.getTopTen(hook, 10).isEmpty());
}

@Test
void testRefreshFiltersIslandsFromWrongGameMode() {
World otherWorld = mock(World.class);
when(island.getWorld()).thenReturn(otherWorld);
when(gma.inWorld(otherWorld)).thenReturn(false);
when(hook.getAllIslandData()).thenReturn(List.of(ib(80, 250, "Underground")));
tbm.refreshAll();

assertTrue(tbm.getTopTen(hook, 10).isEmpty());
}

@Test
void testTopTenDataRecordFields() {
TopTenData d = new TopTenData(island, 42, 1234L, "phasy");
Expand Down
Loading