refactor: drop lolib, add own scheduler, inventory GUI and void generator
This commit is contained in:
parent
cc521dc284
commit
7e227e39a4
47 changed files with 708 additions and 113 deletions
|
|
@ -11,9 +11,9 @@ import dev.loki.loparkour.schematic.core.SchematicManager;
|
|||
import dev.loki.loparkour.storage.Storage;
|
||||
import dev.loki.loparkour.world.core.Divider;
|
||||
import dev.loki.loparkour.world.core.World;
|
||||
import dev.lolib.core.LoPlugin;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ import java.util.logging.Level;
|
|||
*
|
||||
* @author loki
|
||||
*/
|
||||
public final class LoParkour extends LoPlugin {
|
||||
public final class LoParkour extends JavaPlugin {
|
||||
|
||||
public static final String NAME = "&#FF6464&lLoParkour&r";
|
||||
public static final String PREFIX = NAME + " 񢩈» &#A0A0A0";
|
||||
|
|
@ -87,13 +87,13 @@ public final class LoParkour extends LoPlugin {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
PluginBootstrap.enable(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
public void onDisable() {
|
||||
PluginBootstrap.disable(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ import org.bstats.charts.SimplePie;
|
|||
import org.bstats.charts.SingleLineChart;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Wires plugin subsystems during {@link LoParkour#enable()} and {@link LoParkour#disable()}.
|
||||
* Wires plugin subsystems during {@link LoParkour#onEnable()} and {@link LoParkour#onDisable()}.
|
||||
*/
|
||||
public final class PluginBootstrap {
|
||||
|
||||
|
|
@ -117,16 +119,16 @@ public final class PluginBootstrap {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
plugin.loLogger().info("Creating parkour world...");
|
||||
plugin.getLogger().info("Creating parkour world...");
|
||||
World.create();
|
||||
plugin.loLogger().info("Parkour world created successfully!");
|
||||
plugin.getLogger().info("Parkour world created successfully!");
|
||||
} catch (Exception ex) {
|
||||
plugin.loLogger().error("Failed to create parkour world", ex);
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to create parkour world", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerEventsAndCommands(@NotNull LoParkour plugin) {
|
||||
dev.lolib.gui.GUIManager.init(plugin);
|
||||
dev.loki.loparkour.menu.gui.inventory.GuiManager.init(plugin);
|
||||
|
||||
final var pm = plugin.getServer().getPluginManager();
|
||||
pm.registerEvents(new PlayerConnectionListener(), plugin);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package dev.loki.loparkour.ghost.core;
|
|||
import dev.loki.loparkour.ghost.model.GhostFrame;
|
||||
import dev.loki.loparkour.ghost.model.GhostData;
|
||||
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.lolib.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
|
@ -17,9 +17,9 @@ import org.jetbrains.annotations.NotNull;
|
|||
*
|
||||
* <p>Bugs fixed vs original:
|
||||
* <ul>
|
||||
* <li>Duplicate import of {@code dev.lolib.scheduler.Scheduler} — removed.</li>
|
||||
* <li>Duplicate field declaration {@code dev.lolib.scheduler.ScheduledTask task}
|
||||
* shadowed the already-imported type — removed the fully-qualified duplicate.</li>
|
||||
* <li>Duplicate scheduler import — removed.</li>
|
||||
* <li>Duplicate {@code ScheduledTask task} field shadowed the already-imported
|
||||
* type — removed the fully-qualified duplicate.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class GhostPlayer {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import dev.loki.loparkour.ghost.model.GhostData;
|
|||
import dev.loki.loparkour.ghost.model.GhostFrame;
|
||||
import dev.loki.loparkour.ghost.replay.PlayerGhost;
|
||||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.lolib.scheduler.ScheduledTask;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import dev.loki.loparkour.leaderboard.core.LeaderboardSorter;
|
|||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.core.Config;
|
||||
import dev.loki.loparkour.storage.Storage;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ public class ParkourRestrictionListener implements Listener {
|
|||
return;
|
||||
}
|
||||
if (event.getInventory().getType() == InventoryType.CHEST) {
|
||||
return; // LoLib GUI
|
||||
return; // menu GUI
|
||||
}
|
||||
if (event.getInventory().getType() == InventoryType.CRAFTING) {
|
||||
return; // own inv
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import dev.loki.loparkour.menu.core.LPMenu;
|
|||
import dev.loki.loparkour.menu.core.Menus;
|
||||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||
import dev.loki.loparkour.mode.base.Mode;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -58,7 +58,7 @@ public class LeaderboardsMenu extends LPMenu {
|
|||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
// Add items for current page
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import dev.loki.loparkour.menu.core.LPMenu;
|
|||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||
import dev.loki.loparkour.mode.base.Mode;
|
||||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
|
@ -49,7 +49,7 @@ public class SingleLeaderboardMenu extends LPMenu {
|
|||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
final int slot = FIRST_SLOT + (i - startIndex); // Slots 10-16
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package dev.loki.loparkour.menu.core;
|
|||
import dev.loki.loparkour.config.locale.Locales;
|
||||
import dev.loki.loparkour.config.options.Option;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
|
@ -14,13 +14,11 @@ import org.jetbrains.annotations.NotNull;
|
|||
/**
|
||||
* Base class for all LoParkour menus.
|
||||
*
|
||||
* Real LoLib API:
|
||||
* InventoryGUI.create().title(String).rows(n).fill(item).setItem(slot, item, Consumer<InventoryClickEvent>).open(player)
|
||||
* ScrollableGUI.create().title(String).rows(n).addItem(item, Consumer<InventoryClickEvent>)
|
||||
* .scrollUpButton(item,slot).scrollDownButton(item,slot).open(player)
|
||||
* Menu API ({@link InventoryGui}):
|
||||
* InventoryGui.create().title(String).rows(n).fill(item)
|
||||
* .setItem(slot, item, Consumer<InventoryClickEvent>).open(player)
|
||||
*
|
||||
* cancelAllClicks = true by default — items cannot be taken out automatically.
|
||||
* Handlers receive Consumer<InventoryClickEvent>, event is already cancelled before handler runs.
|
||||
* Every click is cancelled before the handler runs — items cannot be taken out.
|
||||
*/
|
||||
public abstract class LPMenu {
|
||||
|
||||
|
|
@ -74,8 +72,8 @@ public abstract class LPMenu {
|
|||
* Create a base GUI filled with filler items.
|
||||
* Uses .fill() — only fills empty slots, so setItem calls after this override correctly.
|
||||
*/
|
||||
protected InventoryGUI baseGui(String title, int rows) {
|
||||
return InventoryGUI.create()
|
||||
protected InventoryGui baseGui(String title, int rows) {
|
||||
return InventoryGui.create()
|
||||
.title(ColorUtil.color(title))
|
||||
.rows(rows)
|
||||
.fill(filler());
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package dev.loki.loparkour.menu.core;
|
||||
|
||||
/**
|
||||
* Placeholder during menu migration; real flows use {@link Menus} / LoLib GUI.
|
||||
* Placeholder during menu migration; real flows use {@link Menus} / InventoryGui.
|
||||
*/
|
||||
public final class MenuStub {
|
||||
private MenuStub() {}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public abstract class DynamicMenu {
|
|||
* @param item The item
|
||||
* @param shouldDisplay Whether this item should be displayed right now.
|
||||
*/
|
||||
// Stub: migrate to LoLib GUI when dynamic rows are wired up.
|
||||
// Stub: migrate to InventoryGui when dynamic rows are wired up.
|
||||
/*
|
||||
public void registerMainItem(
|
||||
int row, int id,
|
||||
|
|
@ -55,7 +55,7 @@ public abstract class DynamicMenu {
|
|||
* @param player The player
|
||||
* @param menu The menu
|
||||
*/
|
||||
// Stub: migrate to LoLib GUI when dynamic rows are wired up.
|
||||
// Stub: migrate to InventoryGui when dynamic rows are wired up.
|
||||
/*
|
||||
public void display(Player player, Menu menu) {
|
||||
ParkourUser user = ParkourUser.getUser(player);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import dev.loki.loparkour.config.locale.Locales;
|
|||
import dev.loki.loparkour.menu.core.LPMenu;
|
||||
import dev.loki.loparkour.menu.core.Menus;
|
||||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -51,7 +51,7 @@ public class MainMenu extends LPMenu {
|
|||
|
||||
final int[] slots = distributeEvenly(count, SLOT_START, SLOT_END);
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int idx = i;
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package dev.loki.loparkour.menu.gui;
|
||||
|
||||
import dev.loki.loparkour.config.locale.Locales;
|
||||
import dev.loki.loparkour.util.item.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Legacy menu stub — real menus use LoLib {@link dev.lolib.gui.InventoryGUI}.
|
||||
*/
|
||||
public class Menu {
|
||||
private final int rows;
|
||||
private final String title;
|
||||
|
||||
public Menu(int rows, String title) {
|
||||
this.rows = rows;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Menu item(int slot, Item item) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Menu distributeRowsEvenly() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void open(Player player) {
|
||||
Locales.send(player, "other.menu_unavailable", title, rows);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import org.bukkit.entity.Player;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Legacy paged menu stub — real menus use LoLib GUI.
|
||||
* Legacy paged menu stub — real menus use InventoryGui.
|
||||
*/
|
||||
public class PagedMenu {
|
||||
private final int rows;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package dev.loki.loparkour.menu.gui.inventory;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Routes inventory clicks and closes to the {@link InventoryGui} opened for a player.
|
||||
* One GUI per player UUID; entries are dropped on close and on quit.
|
||||
*/
|
||||
public final class GuiManager implements Listener {
|
||||
|
||||
private static final Map<UUID, OpenGui> ACTIVE = new ConcurrentHashMap<>();
|
||||
|
||||
private GuiManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the click/close listener. Safe to call once during plugin enable.
|
||||
*
|
||||
* @param plugin the owning plugin
|
||||
*/
|
||||
public static void init(Plugin plugin) {
|
||||
plugin.getServer().getPluginManager().registerEvents(new GuiManager(), plugin);
|
||||
}
|
||||
|
||||
static void register(Player player, InventoryGui gui, Inventory inventory) {
|
||||
ACTIVE.put(player.getUniqueId(), new OpenGui(gui, inventory));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
final OpenGui open = ACTIVE.get(player.getUniqueId());
|
||||
if (open == null || !open.inventory().equals(event.getInventory())) {
|
||||
return;
|
||||
}
|
||||
open.gui().handleClick(event);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent event) {
|
||||
if (!(event.getPlayer() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
final OpenGui open = ACTIVE.get(player.getUniqueId());
|
||||
if (open == null || !open.inventory().equals(event.getInventory())) {
|
||||
return;
|
||||
}
|
||||
ACTIVE.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
ACTIVE.remove(event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
/** The GUI a player currently has open, together with its inventory. */
|
||||
private record OpenGui(InventoryGui gui, Inventory inventory) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package dev.loki.loparkour.menu.gui.inventory;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Inventory menu built fluently: fill background, place items with click handlers,
|
||||
* then open it for a player. Clicks are always cancelled, so menu items cannot be
|
||||
* taken out; only the registered handlers run.
|
||||
*
|
||||
* <p>Replaces the LoLib {@code InventoryGUI} (TODO, Задача 1). Handler dispatch lives
|
||||
* in {@link GuiManager}, which tracks the inventory opened for each player.
|
||||
*/
|
||||
public final class InventoryGui {
|
||||
|
||||
private static final int SLOTS_PER_ROW = 9;
|
||||
private static final int MIN_ROWS = 1;
|
||||
private static final int MAX_ROWS = 6;
|
||||
private static final String DEFAULT_TITLE = "GUI";
|
||||
|
||||
private final Map<Integer, PlacedItem> items = new HashMap<>();
|
||||
|
||||
private String title = DEFAULT_TITLE;
|
||||
private int rows = MIN_ROWS;
|
||||
|
||||
private InventoryGui() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new empty menu
|
||||
*/
|
||||
public static InventoryGui create() {
|
||||
return new InventoryGui();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newTitle the (already colored) inventory title
|
||||
* @return this menu
|
||||
*/
|
||||
public InventoryGui title(String newTitle) {
|
||||
this.title = newTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newRows inventory rows, 1..6
|
||||
* @return this menu
|
||||
*/
|
||||
public InventoryGui rows(int newRows) {
|
||||
if (newRows < MIN_ROWS || newRows > MAX_ROWS) {
|
||||
throw new IllegalArgumentException("Rows must be between " + MIN_ROWS + " and " + MAX_ROWS);
|
||||
}
|
||||
this.rows = newRows;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the item into every slot that does not have one yet.
|
||||
*
|
||||
* @param item background item
|
||||
* @return this menu
|
||||
*/
|
||||
public InventoryGui fill(ItemStack item) {
|
||||
for (int slot = 0; slot < rows * SLOTS_PER_ROW; slot++) {
|
||||
items.putIfAbsent(slot, new PlacedItem(item, null));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param slot inventory slot
|
||||
* @param item item to show
|
||||
* @return this menu
|
||||
*/
|
||||
public InventoryGui setItem(int slot, ItemStack item) {
|
||||
items.put(slot, new PlacedItem(item, null));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param slot inventory slot
|
||||
* @param item item to show
|
||||
* @param onClick click handler, invoked after the event was cancelled
|
||||
* @return this menu
|
||||
*/
|
||||
public InventoryGui setItem(int slot, ItemStack item, Consumer<InventoryClickEvent> onClick) {
|
||||
items.put(slot, new PlacedItem(item, onClick));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the inventory, registers it for the player and opens it.
|
||||
*
|
||||
* @param player the player to show this menu to
|
||||
*/
|
||||
public void open(Player player) {
|
||||
final Inventory inventory = Bukkit.createInventory(null, rows * SLOTS_PER_ROW, title);
|
||||
items.forEach((slot, placed) -> {
|
||||
if (slot < inventory.getSize() && placed.item() != null) {
|
||||
inventory.setItem(slot, placed.item());
|
||||
}
|
||||
});
|
||||
GuiManager.register(player, this, inventory);
|
||||
player.openInventory(inventory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the click and runs the handler registered for the clicked slot.
|
||||
*
|
||||
* @param event the click event
|
||||
*/
|
||||
void handleClick(InventoryClickEvent event) {
|
||||
event.setCancelled(true);
|
||||
final PlacedItem placed = items.get(event.getRawSlot());
|
||||
if (placed != null && placed.onClick() != null) {
|
||||
placed.onClick().accept(event);
|
||||
}
|
||||
}
|
||||
|
||||
/** Snapshot of the placed items — package-private seam for tests. */
|
||||
Map<Integer, PlacedItem> items() {
|
||||
return Map.copyOf(items);
|
||||
}
|
||||
|
||||
/** An item and its optional click handler. */
|
||||
record PlacedItem(ItemStack item, Consumer<InventoryClickEvent> onClick) {
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import dev.loki.loparkour.player.core.ParkourUser;
|
|||
import dev.loki.loparkour.session.core.Session;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.loki.loparkour.world.core.Divider;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
|
@ -50,7 +50,7 @@ public class DuelsMenu extends LPMenu {
|
|||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, sessions.size());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
if (sessions.isEmpty()) {
|
||||
gui = gui.setItem(SLOT_EMPTY, localeItem(player, "play.multi.duels.menu_empty"));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
|||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.loki.loparkour.session.core.Session;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
|
@ -36,7 +36,7 @@ public class LobbyMenu extends LPMenu {
|
|||
final boolean isHost = !user.session.getPlayers().isEmpty()
|
||||
&& user.session.getPlayers().get(0) == user;
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
if (ParkourOption.PLAYER_MANAGEMENT.mayPerform(player) && isHost) {
|
||||
gui = gui.setItem(SLOT_PLAYER_MANAGEMENT, localeItem(player, "lobby.player_management.item"),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import dev.loki.loparkour.player.core.ParkourPlayer;
|
|||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.loki.loparkour.session.core.Session;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
|
@ -42,7 +42,7 @@ public class PlayerManagementMenu extends LPMenu {
|
|||
others.addAll(session.getPlayers().stream().filter(p -> p != viewer).toList());
|
||||
others.addAll(session.getSpectators());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS)
|
||||
InventoryGui gui = baseGui(title, ROWS)
|
||||
.setItem(SLOT_CLOSE, closeButton(player), e -> Menus.LOBBY.open(player));
|
||||
|
||||
for (int i = 0; i < Math.min(others.size(), MAX_PLAYERS); i++) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import dev.loki.loparkour.config.locale.Locales;
|
|||
import dev.loki.loparkour.menu.core.LPMenu;
|
||||
import dev.loki.loparkour.mode.base.Mode;
|
||||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -46,7 +46,7 @@ public class SingleMenu extends LPMenu {
|
|||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
final Mode mode = availableModes.get(i);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import dev.loki.loparkour.mode.base.Modes;
|
|||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.loki.loparkour.session.core.Session;
|
||||
import dev.loki.loparkour.world.core.Divider;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
|
@ -43,7 +43,7 @@ public class SpectatorMenu extends LPMenu {
|
|||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
final Session session = sessions.get(i);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import dev.loki.loparkour.menu.core.LPMenu;
|
|||
import dev.loki.loparkour.menu.core.Menus;
|
||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
|
@ -40,7 +40,7 @@ public class LangMenu extends LPMenu {
|
|||
|
||||
final List<String> langs = new ArrayList<>(Locales.getLocaleKeys());
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
for (int i = 0; i < Math.min(langs.size(), MAX_LANGS); i++) {
|
||||
final String lang = langs.get(i);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
|||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||
import dev.loki.loparkour.style.core.Style;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
|
@ -168,7 +168,7 @@ public class ParkourSettingsMenu extends LPMenu {
|
|||
final String locale = pp.locale;
|
||||
final String title = Locales.getString(locale, "settings.parkour_settings.styles.name");
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS_STYLE);
|
||||
InventoryGui gui = baseGui(title, ROWS_STYLE);
|
||||
|
||||
int slot = FIRST_STYLE_SLOT;
|
||||
for (final Style style : Registry.getStyles()) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import dev.loki.loparkour.menu.core.Menus;
|
|||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.lolib.gui.InventoryGUI;
|
||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ public class SettingsMenu extends LPMenu {
|
|||
final String locale = locale(player);
|
||||
final String title = Locales.getString(locale, "settings.name");
|
||||
|
||||
InventoryGUI gui = baseGui(title, ROWS);
|
||||
InventoryGui gui = baseGui(title, ROWS);
|
||||
|
||||
if (ParkourOption.PARKOUR_SETTINGS.mayPerform(player) && ParkourPlayer.isPlayer(player)) {
|
||||
gui = gui.setItem(SLOT_PARKOUR_SETTINGS, localeItem(player, "settings.parkour_settings.item"), e -> {
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public class RaceMode implements Mode {
|
|||
registerScore(time, "1.0", targetScore);
|
||||
|
||||
// Return to lobby after 5 seconds — guarded against null/closed session
|
||||
dev.lolib.scheduler.Scheduler.get(LoParkour.getPlugin())
|
||||
dev.loki.loparkour.scheduler.Scheduler.get(LoParkour.getPlugin())
|
||||
.runLater(() -> {
|
||||
if (session != null && !state.stopped) {
|
||||
reset(false);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import dev.loki.loparkour.mode.base.Mode;
|
|||
import dev.loki.loparkour.mode.base.Modes;
|
||||
|
||||
import dev.loki.loparkour.util.item.Item;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.lolib.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.core.Config;
|
||||
import dev.loki.loparkour.config.locale.Locales;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package dev.loki.loparkour.player.core;
|
|||
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.storage.Storage;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
|
||||
/**
|
||||
* Handles persistence of player data to the configured storage backend.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package dev.loki.loparkour.player.core;
|
|||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.core.Config;
|
||||
import dev.loki.loparkour.player.service.ui.ParkourHotbar;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package dev.loki.loparkour.player.data;
|
|||
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.core.Config;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import dev.loki.loparkour.config.locale.Locales;
|
|||
import dev.loki.loparkour.player.data.PreviousData;
|
||||
import dev.loki.loparkour.session.core.Session;
|
||||
import dev.loki.loparkour.util.text.ColorUtil;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.lolib.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.GameMode;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package dev.loki.loparkour.scheduler;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Scheduler backend for Bukkit/Paper servers.
|
||||
*/
|
||||
final class BukkitBackend implements Scheduler.Backend {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
BukkitBackend(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask run(Runnable task) {
|
||||
return Bukkit.getScheduler().runTask(plugin, task)::cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask runAsync(Runnable task) {
|
||||
return Bukkit.getScheduler().runTaskAsynchronously(plugin, task)::cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask runLater(Runnable task, long delayTicks) {
|
||||
return Bukkit.getScheduler().runTaskLater(plugin, task, delayTicks)::cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask runTimer(Runnable task, long delayTicks, long periodTicks) {
|
||||
return Bukkit.getScheduler().runTaskTimer(plugin, task, delayTicks, periodTicks)::cancel;
|
||||
}
|
||||
}
|
||||
92
src/main/java/dev/loki/loparkour/scheduler/FoliaBackend.java
Normal file
92
src/main/java/dev/loki/loparkour/scheduler/FoliaBackend.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package dev.loki.loparkour.scheduler;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Scheduler backend for Folia servers: the global region scheduler drives sync tasks
|
||||
* and the async scheduler drives worker tasks, mirroring the Bukkit semantics above.
|
||||
*
|
||||
* <p>The Folia API is resolved reflectively because the build matrix compiles against
|
||||
* Spigot/Paper API artifacts that do not ship it (see TODO, Задача 1 / п.143).
|
||||
*/
|
||||
final class FoliaBackend implements Scheduler.Backend {
|
||||
|
||||
private static final String SCHEDULER_PACKAGE = "io.papermc.paper.threadedregions.scheduler.";
|
||||
|
||||
private static final String GLOBAL_SCHEDULER_CLASS = "GlobalRegionScheduler";
|
||||
private static final String ASYNC_SCHEDULER_CLASS = "AsyncScheduler";
|
||||
|
||||
private final Plugin plugin;
|
||||
private final Object globalScheduler;
|
||||
private final Object asyncScheduler;
|
||||
private final Method globalRun;
|
||||
private final Method globalRunDelayed;
|
||||
private final Method globalRunAtFixedRate;
|
||||
private final Method asyncRunNow;
|
||||
private final Method taskCancel;
|
||||
|
||||
FoliaBackend(Plugin plugin) throws ReflectiveOperationException {
|
||||
this.plugin = plugin;
|
||||
this.globalScheduler = scheduler("getGlobalRegionScheduler");
|
||||
this.asyncScheduler = scheduler("getAsyncScheduler");
|
||||
this.globalRun = method(GLOBAL_SCHEDULER_CLASS, "run", Plugin.class, Consumer.class);
|
||||
this.globalRunDelayed = method(GLOBAL_SCHEDULER_CLASS, "runDelayed",
|
||||
Plugin.class, Consumer.class, long.class);
|
||||
this.globalRunAtFixedRate = method(GLOBAL_SCHEDULER_CLASS, "runAtFixedRate",
|
||||
Plugin.class, Consumer.class, long.class, long.class);
|
||||
this.asyncRunNow = method(ASYNC_SCHEDULER_CLASS, "runNow", Plugin.class, Consumer.class);
|
||||
this.taskCancel = Class.forName(SCHEDULER_PACKAGE + "ScheduledTask").getMethod("cancel");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask run(Runnable task) {
|
||||
return wrap(invoke(globalRun, globalScheduler, plugin, consumer(task)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask runAsync(Runnable task) {
|
||||
return wrap(invoke(asyncRunNow, asyncScheduler, plugin, consumer(task)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask runLater(Runnable task, long delayTicks) {
|
||||
return wrap(invoke(globalRunDelayed, globalScheduler, plugin, consumer(task), delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask runTimer(Runnable task, long delayTicks, long periodTicks) {
|
||||
return wrap(invoke(globalRunAtFixedRate, globalScheduler, plugin,
|
||||
consumer(task), delayTicks, periodTicks));
|
||||
}
|
||||
|
||||
private static Object scheduler(String bukkitGetter) throws ReflectiveOperationException {
|
||||
return Bukkit.class.getMethod(bukkitGetter).invoke(null);
|
||||
}
|
||||
|
||||
private static Method method(String owner, String name, Class<?>... params)
|
||||
throws ReflectiveOperationException {
|
||||
return Class.forName(SCHEDULER_PACKAGE + owner).getMethod(name, params);
|
||||
}
|
||||
|
||||
private static Consumer<Object> consumer(Runnable task) {
|
||||
return ignored -> task.run();
|
||||
}
|
||||
|
||||
private static Object invoke(Method method, Object target, Object... args) {
|
||||
try {
|
||||
return method.invoke(target, args);
|
||||
} catch (IllegalAccessException | InvocationTargetException cause) {
|
||||
throw new IllegalStateException("Folia scheduler call failed: " + method.getName(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
private ScheduledTask wrap(Object foliaTask) {
|
||||
final Object handle = Objects.requireNonNull(foliaTask, "Folia scheduler returned no task");
|
||||
return () -> invoke(taskCancel, handle);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package dev.loki.loparkour.scheduler;
|
||||
|
||||
/**
|
||||
* Handle for a scheduled task.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ScheduledTask {
|
||||
|
||||
/**
|
||||
* Cancels the task if it has not run yet (repeating tasks stop after the current run).
|
||||
*/
|
||||
void cancel();
|
||||
}
|
||||
122
src/main/java/dev/loki/loparkour/scheduler/Scheduler.java
Normal file
122
src/main/java/dev/loki/loparkour/scheduler/Scheduler.java
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package dev.loki.loparkour.scheduler;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Thin task scheduler facade over the platform scheduler.
|
||||
*
|
||||
* <p>Call sites keep the {@code Scheduler.get(plugin).runLater(...)} shape, so the
|
||||
* backend (Bukkit today, Folia region/async schedulers) can change without touching
|
||||
* game code. The Folia backend is selected automatically when the Folia server
|
||||
* classes are present; the Folia scheduler API itself is resolved reflectively
|
||||
* because it is absent from the Spigot/Paper API artifacts used by the build matrix.
|
||||
*/
|
||||
public final class Scheduler {
|
||||
|
||||
private static final String FOLIA_SERVER_CLASS = "io.papermc.paper.threadedregions.RegionizedServer";
|
||||
|
||||
private static final Map<Plugin, Scheduler> INSTANCES = new ConcurrentHashMap<>();
|
||||
|
||||
private static final boolean FOLIA = hasFoliaServer();
|
||||
|
||||
private final Backend backend;
|
||||
|
||||
private Scheduler(Plugin plugin) {
|
||||
this.backend = createBackend(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param plugin the owning plugin
|
||||
* @return the scheduler bound to that plugin
|
||||
*/
|
||||
public static Scheduler get(Plugin plugin) {
|
||||
return INSTANCES.computeIfAbsent(plugin, Scheduler::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true when the server runs Folia (or a Folia fork)
|
||||
*/
|
||||
public static boolean isFolia() {
|
||||
return FOLIA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the task on the next global tick.
|
||||
*
|
||||
* @param task the task to run
|
||||
* @return a handle used to cancel the task
|
||||
*/
|
||||
public ScheduledTask run(Runnable task) {
|
||||
return backend.run(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the task on a worker thread.
|
||||
*
|
||||
* @param task the task to run
|
||||
* @return a handle used to cancel the task
|
||||
*/
|
||||
public ScheduledTask runAsync(Runnable task) {
|
||||
return backend.runAsync(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the task once after the given delay.
|
||||
*
|
||||
* @param task the task to run
|
||||
* @param delayTicks delay in ticks
|
||||
* @return a handle used to cancel the task
|
||||
*/
|
||||
public ScheduledTask runLater(Runnable task, long delayTicks) {
|
||||
return backend.runLater(task, delayTicks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the task repeatedly after the given delay.
|
||||
*
|
||||
* @param task the task to run
|
||||
* @param delayTicks first run delay in ticks
|
||||
* @param periodTicks period between runs in ticks
|
||||
* @return a handle used to cancel the task
|
||||
*/
|
||||
public ScheduledTask runTimer(Runnable task, long delayTicks, long periodTicks) {
|
||||
return backend.runTimer(task, delayTicks, periodTicks);
|
||||
}
|
||||
|
||||
private static Backend createBackend(Plugin plugin) {
|
||||
if (FOLIA) {
|
||||
try {
|
||||
return new FoliaBackend(plugin);
|
||||
} catch (ReflectiveOperationException expected) {
|
||||
plugin.getLogger().log(Level.WARNING,
|
||||
"Folia scheduler API not found - falling back to the Bukkit scheduler", expected);
|
||||
}
|
||||
}
|
||||
return new BukkitBackend(plugin);
|
||||
}
|
||||
|
||||
private static boolean hasFoliaServer() {
|
||||
try {
|
||||
Class.forName(FOLIA_SERVER_CLASS, false, Scheduler.class.getClassLoader());
|
||||
return true;
|
||||
} catch (ClassNotFoundException expected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** The four task operations shared by the Bukkit and Folia backends. */
|
||||
interface Backend {
|
||||
|
||||
ScheduledTask run(Runnable task);
|
||||
|
||||
ScheduledTask runAsync(Runnable task);
|
||||
|
||||
ScheduledTask runLater(Runnable task, long delayTicks);
|
||||
|
||||
ScheduledTask runTimer(Runnable task, long delayTicks, long periodTicks);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package dev.loki.loparkour.schematic.registry;
|
|||
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.schematic.core.SchematicManager;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import dev.loki.loparkour.adaptive.bootstrap.AdaptiveServices;
|
|||
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||
import dev.loki.loparkour.player.core.ParkourUser;
|
||||
import dev.loki.loparkour.world.core.Divider;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.lolib.scheduler.ScheduledTask;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import dev.loki.loparkour.player.core.ParkourPlayer;
|
|||
import dev.loki.loparkour.storage.sql.repo.SQLPlayerRepository;
|
||||
import dev.loki.loparkour.storage.sql.repo.SQLScoreRepository;
|
||||
import dev.loki.loparkour.storage.sql.schema.SQLSchemaManager;
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.loki.loparkour.scheduler.Scheduler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package dev.loki.loparkour.world.core;
|
||||
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
|
||||
/**
|
||||
* Generates an empty chunk grid — the parkour world must not contain terrain
|
||||
* (vanilla map layers also break flat void worlds: "No keys in MapLayer").
|
||||
*/
|
||||
public final class VoidGenerator extends ChunkGenerator {
|
||||
|
||||
private static final VoidGenerator INSTANCE = new VoidGenerator();
|
||||
|
||||
private VoidGenerator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the shared void generator
|
||||
*/
|
||||
public static VoidGenerator getGenerator() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGenerateNoise() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGenerateSurface() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGenerateCaves() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGenerateDecorations() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGenerateMobs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldGenerateStructures() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package dev.loki.loparkour.world.core;
|
|||
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.core.Config;
|
||||
import dev.lolib.worlds.VoidGenerator;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Difficulty;
|
||||
import org.bukkit.GameRule;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ author: loki
|
|||
version: ${version}
|
||||
api-version: ${apiVersion}
|
||||
main: dev.loki.loparkour.LoParkour
|
||||
softdepend: [floodgate, Vault, PlaceholderAPI, Multiverse-Core, VoidGen]
|
||||
softdepend: [floodgate, Vault, PlaceholderAPI, Multiverse-Core]
|
||||
commands:
|
||||
loparkour:
|
||||
aliases: [parkour, lp]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package dev.loki.loparkour.menu.gui.inventory;
|
||||
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class InventoryGuiTest {
|
||||
|
||||
private static final int ROWS = 2;
|
||||
private static final int SLOTS_PER_ROW = 9;
|
||||
private static final int SLOT_WITH_HANDLER = 5;
|
||||
private static final int PLAIN_SLOT = 7;
|
||||
private static final int OUT_OF_RANGE_SLOT = 40;
|
||||
|
||||
@Test
|
||||
void testRowsBoundsAreEnforced() {
|
||||
final InventoryGui gui = InventoryGui.create();
|
||||
assertThrows(IllegalArgumentException.class, () -> gui.rows(0), "zero rows are rejected");
|
||||
assertThrows(IllegalArgumentException.class, () -> gui.rows(7), "seven rows are rejected");
|
||||
assertSame(gui, gui.rows(6), "rows() is fluent");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFillKeepsItemsPlacedBeforeIt() {
|
||||
final InventoryGui gui = InventoryGui.create().rows(ROWS);
|
||||
gui.setItem(SLOT_WITH_HANDLER, null, event -> {
|
||||
});
|
||||
gui.fill(null);
|
||||
|
||||
final Map<Integer, InventoryGui.PlacedItem> items = gui.items();
|
||||
assertEquals(ROWS * SLOTS_PER_ROW, items.size(), "fill covers every slot exactly once");
|
||||
assertNotNull(items.get(SLOT_WITH_HANDLER).onClick(), "fill must not overwrite placed items");
|
||||
assertNull(items.get(PLAIN_SLOT).onClick(), "a plain filler slot has no handler");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClickRunsHandlerAndCancelsTheEvent() {
|
||||
final AtomicBoolean clicked = new AtomicBoolean();
|
||||
final InventoryGui gui = InventoryGui.create().rows(ROWS);
|
||||
gui.setItem(SLOT_WITH_HANDLER, null, event -> clicked.set(true));
|
||||
|
||||
final InventoryClickEvent event = Mockito.mock(InventoryClickEvent.class);
|
||||
when(event.getRawSlot()).thenReturn(SLOT_WITH_HANDLER);
|
||||
gui.handleClick(event);
|
||||
|
||||
verify(event).setCancelled(true);
|
||||
assertTrue(clicked.get(), "the registered handler must run");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClickOnUnknownSlotOnlyCancels() {
|
||||
final InventoryGui gui = InventoryGui.create().rows(ROWS);
|
||||
|
||||
final InventoryClickEvent event = Mockito.mock(InventoryClickEvent.class);
|
||||
when(event.getRawSlot()).thenReturn(OUT_OF_RANGE_SLOT);
|
||||
gui.handleClick(event);
|
||||
|
||||
verify(event).setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package dev.loki.loparkour.scheduler;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
class SchedulerTest {
|
||||
|
||||
@Test
|
||||
void testNoFoliaOnPlainJvm() {
|
||||
assertFalse(Scheduler.isFolia(), "Folia server classes are absent in the test JVM");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInstancesAreCachedPerPlugin() {
|
||||
final Plugin plugin = Mockito.mock(Plugin.class);
|
||||
assertSame(Scheduler.get(plugin), Scheduler.get(plugin), "the same plugin must reuse one scheduler");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package dev.loki.loparkour.world.core;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
class VoidGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testGeneratorIsShared() {
|
||||
assertSame(VoidGenerator.getGenerator(), VoidGenerator.getGenerator(), "the generator is a singleton");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGeneratesNothing() {
|
||||
final VoidGenerator generator = VoidGenerator.getGenerator();
|
||||
assertFalse(generator.shouldGenerateNoise(), "noise off");
|
||||
assertFalse(generator.shouldGenerateSurface(), "surface off");
|
||||
assertFalse(generator.shouldGenerateCaves(), "caves off");
|
||||
assertFalse(generator.shouldGenerateDecorations(), "decorations off");
|
||||
assertFalse(generator.shouldGenerateMobs(), "mobs off");
|
||||
assertFalse(generator.shouldGenerateStructures(), "structures off");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue