refactor: drop lolib, add own scheduler, inventory GUI and void generator

This commit is contained in:
loki5512344 2026-10-06 13:41:25 +02:00
parent cc521dc284
commit 7e227e39a4
Signed by: boba
GPG key ID: 253067914055423B
47 changed files with 708 additions and 113 deletions

View file

@ -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++) {

View file

@ -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

View file

@ -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&lt;InventoryClickEvent&gt;).open(player)
* ScrollableGUI.create().title(String).rows(n).addItem(item, Consumer&lt;InventoryClickEvent&gt;)
* .scrollUpButton(item,slot).scrollDownButton(item,slot).open(player)
* Menu API ({@link InventoryGui}):
* InventoryGui.create().title(String).rows(n).fill(item)
* .setItem(slot, item, Consumer&lt;InventoryClickEvent&gt;).open(player)
*
* cancelAllClicks = true by default — items cannot be taken out automatically.
* Handlers receive Consumer&lt;InventoryClickEvent&gt;, 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());

View file

@ -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() {}

View file

@ -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);

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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) {
}
}

View file

@ -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) {
}
}

View file

@ -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"));

View file

@ -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"),

View file

@ -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++) {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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()) {

View file

@ -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 -> {