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
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# 1.19.2 / 1.20.1 — Java 21 (plugin requires Java 21+; lolib jar is 21 bytecode)
|
# 1.19.2 / 1.20.1 — Java 21 (plugin requires Java 21+)
|
||||||
- { mc: "1.19.2", coordinate: "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT", java: 21, release: 21, api: "1.19" }
|
- { mc: "1.19.2", coordinate: "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT", java: 21, release: 21, api: "1.19" }
|
||||||
- { mc: "1.20.1", coordinate: "io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT", java: 21, release: 21, api: "1.20" }
|
- { mc: "1.20.1", coordinate: "io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT", java: 21, release: 21, api: "1.20" }
|
||||||
# 1.21 — Java 21
|
# 1.21 — Java 21
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
# Package Files
|
# Package Files
|
||||||
*.jar
|
*.jar
|
||||||
!libs/*.jar
|
|
||||||
*.war
|
*.war
|
||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
|
|
@ -64,9 +63,6 @@ test-output/
|
||||||
# Modrinth files (local only)
|
# Modrinth files (local only)
|
||||||
modrinth/
|
modrinth/
|
||||||
|
|
||||||
# LoLib API Documentation (не нужно в репозитории)
|
|
||||||
api_docs/
|
|
||||||
|
|
||||||
# Project documentation (local only)
|
# Project documentation (local only)
|
||||||
TODO.md
|
TODO.md
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,9 +182,6 @@ dependencies {
|
||||||
compileOnly("net.kyori:adventure-api:4.14.0")
|
compileOnly("net.kyori:adventure-api:4.14.0")
|
||||||
compileOnly("net.kyori:adventure-text-minimessage:4.14.0")
|
compileOnly("net.kyori:adventure-text-minimessage:4.14.0")
|
||||||
|
|
||||||
// LoLib Core
|
|
||||||
implementation(files("libs/lolib-3.0.0.jar"))
|
|
||||||
|
|
||||||
// Shaded dependencies
|
// Shaded dependencies
|
||||||
implementation("io.papermc:paperlib:1.0.7")
|
implementation("io.papermc:paperlib:1.0.7")
|
||||||
implementation("com.google.code.gson:gson:2.11.0")
|
implementation("com.google.code.gson:gson:2.11.0")
|
||||||
|
|
@ -222,18 +219,10 @@ tasks.shadowJar {
|
||||||
archiveFileName.set("LoParkour-${project.version}-mc${mcVersion}.jar")
|
archiveFileName.set("LoParkour-${project.version}-mc${mcVersion}.jar")
|
||||||
|
|
||||||
// Relocate shaded dependencies
|
// Relocate shaded dependencies
|
||||||
relocate("dev.lolib", "dev.loki.loparkour.lib.lolib")
|
|
||||||
relocate("io.papermc.lib", "dev.loki.loparkour.lib.paperlib")
|
relocate("io.papermc.lib", "dev.loki.loparkour.lib.paperlib")
|
||||||
relocate("com.google.gson", "dev.loki.loparkour.lib.gson")
|
relocate("com.google.gson", "dev.loki.loparkour.lib.gson")
|
||||||
relocate("org.bstats", "dev.loki.loparkour.lib.bstats")
|
relocate("org.bstats", "dev.loki.loparkour.lib.bstats")
|
||||||
relocate("org.apache.commons.math3", "dev.loki.loparkour.lib.commonsmath3")
|
relocate("org.apache.commons.math3", "dev.loki.loparkour.lib.commonsmath3")
|
||||||
|
|
||||||
// Don't minimize - causes issues with Caffeine's dynamically generated classes
|
|
||||||
// minimize {
|
|
||||||
// exclude(dependency("com.zaxxer:HikariCP:.*"))
|
|
||||||
// exclude(dependency("com.mysql:mysql-connector-j:.*"))
|
|
||||||
// exclude(dependency(files("libs/lolib-3.0.0.jar")))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("build") {
|
tasks.named("build") {
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -11,9 +11,9 @@ import dev.loki.loparkour.schematic.core.SchematicManager;
|
||||||
import dev.loki.loparkour.storage.Storage;
|
import dev.loki.loparkour.storage.Storage;
|
||||||
import dev.loki.loparkour.world.core.Divider;
|
import dev.loki.loparkour.world.core.Divider;
|
||||||
import dev.loki.loparkour.world.core.World;
|
import dev.loki.loparkour.world.core.World;
|
||||||
import dev.lolib.core.LoPlugin;
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ import java.util.logging.Level;
|
||||||
*
|
*
|
||||||
* @author loki
|
* @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 NAME = "&#FF6464&lLoParkour&r";
|
||||||
public static final String PREFIX = NAME + " 񢩈» &#A0A0A0";
|
public static final String PREFIX = NAME + " 񢩈» &#A0A0A0";
|
||||||
|
|
@ -87,13 +87,13 @@ public final class LoParkour extends LoPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enable() {
|
public void onEnable() {
|
||||||
instance = this;
|
instance = this;
|
||||||
PluginBootstrap.enable(this);
|
PluginBootstrap.enable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void onDisable() {
|
||||||
PluginBootstrap.disable(this);
|
PluginBootstrap.disable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,10 @@ import org.bstats.charts.SimplePie;
|
||||||
import org.bstats.charts.SingleLineChart;
|
import org.bstats.charts.SingleLineChart;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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 {
|
public final class PluginBootstrap {
|
||||||
|
|
||||||
|
|
@ -117,16 +119,16 @@ public final class PluginBootstrap {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
plugin.loLogger().info("Creating parkour world...");
|
plugin.getLogger().info("Creating parkour world...");
|
||||||
World.create();
|
World.create();
|
||||||
plugin.loLogger().info("Parkour world created successfully!");
|
plugin.getLogger().info("Parkour world created successfully!");
|
||||||
} catch (Exception ex) {
|
} 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) {
|
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();
|
final var pm = plugin.getServer().getPluginManager();
|
||||||
pm.registerEvents(new PlayerConnectionListener(), plugin);
|
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.GhostFrame;
|
||||||
import dev.loki.loparkour.ghost.model.GhostData;
|
import dev.loki.loparkour.ghost.model.GhostData;
|
||||||
|
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
@ -17,9 +17,9 @@ import org.jetbrains.annotations.NotNull;
|
||||||
*
|
*
|
||||||
* <p>Bugs fixed vs original:
|
* <p>Bugs fixed vs original:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Duplicate import of {@code dev.lolib.scheduler.Scheduler} — removed.</li>
|
* <li>Duplicate scheduler import — removed.</li>
|
||||||
* <li>Duplicate field declaration {@code dev.lolib.scheduler.ScheduledTask task}
|
* <li>Duplicate {@code ScheduledTask task} field shadowed the already-imported
|
||||||
* shadowed the already-imported type — removed the fully-qualified duplicate.</li>
|
* type — removed the fully-qualified duplicate.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class GhostPlayer {
|
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.model.GhostFrame;
|
||||||
import dev.loki.loparkour.ghost.replay.PlayerGhost;
|
import dev.loki.loparkour.ghost.replay.PlayerGhost;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import dev.loki.loparkour.leaderboard.core.LeaderboardSorter;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
import dev.loki.loparkour.storage.Storage;
|
import dev.loki.loparkour.storage.Storage;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ public class ParkourRestrictionListener implements Listener {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.getInventory().getType() == InventoryType.CHEST) {
|
if (event.getInventory().getType() == InventoryType.CHEST) {
|
||||||
return; // LoLib GUI
|
return; // menu GUI
|
||||||
}
|
}
|
||||||
if (event.getInventory().getType() == InventoryType.CRAFTING) {
|
if (event.getInventory().getType() == InventoryType.CRAFTING) {
|
||||||
return; // own inv
|
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.Menus;
|
||||||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||||
import dev.loki.loparkour.mode.base.Mode;
|
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.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -58,7 +58,7 @@ public class LeaderboardsMenu extends LPMenu {
|
||||||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
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
|
// Add items for current page
|
||||||
for (int i = startIndex; i < endIndex; i++) {
|
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.menu.core.screen.ParkourOption;
|
||||||
import dev.loki.loparkour.mode.base.Mode;
|
import dev.loki.loparkour.mode.base.Mode;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
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.Material;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
@ -49,7 +49,7 @@ public class SingleLeaderboardMenu extends LPMenu {
|
||||||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
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++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
final int slot = FIRST_SLOT + (i - startIndex); // Slots 10-16
|
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.locale.Locales;
|
||||||
import dev.loki.loparkour.config.options.Option;
|
import dev.loki.loparkour.config.options.Option;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -14,13 +14,11 @@ import org.jetbrains.annotations.NotNull;
|
||||||
/**
|
/**
|
||||||
* Base class for all LoParkour menus.
|
* Base class for all LoParkour menus.
|
||||||
*
|
*
|
||||||
* Real LoLib API:
|
* Menu API ({@link InventoryGui}):
|
||||||
* InventoryGUI.create().title(String).rows(n).fill(item).setItem(slot, item, Consumer<InventoryClickEvent>).open(player)
|
* InventoryGui.create().title(String).rows(n).fill(item)
|
||||||
* ScrollableGUI.create().title(String).rows(n).addItem(item, Consumer<InventoryClickEvent>)
|
* .setItem(slot, item, Consumer<InventoryClickEvent>).open(player)
|
||||||
* .scrollUpButton(item,slot).scrollDownButton(item,slot).open(player)
|
|
||||||
*
|
*
|
||||||
* cancelAllClicks = true by default — items cannot be taken out automatically.
|
* Every click is cancelled before the handler runs — items cannot be taken out.
|
||||||
* Handlers receive Consumer<InventoryClickEvent>, event is already cancelled before handler runs.
|
|
||||||
*/
|
*/
|
||||||
public abstract class LPMenu {
|
public abstract class LPMenu {
|
||||||
|
|
||||||
|
|
@ -74,8 +72,8 @@ public abstract class LPMenu {
|
||||||
* Create a base GUI filled with filler items.
|
* Create a base GUI filled with filler items.
|
||||||
* Uses .fill() — only fills empty slots, so setItem calls after this override correctly.
|
* Uses .fill() — only fills empty slots, so setItem calls after this override correctly.
|
||||||
*/
|
*/
|
||||||
protected InventoryGUI baseGui(String title, int rows) {
|
protected InventoryGui baseGui(String title, int rows) {
|
||||||
return InventoryGUI.create()
|
return InventoryGui.create()
|
||||||
.title(ColorUtil.color(title))
|
.title(ColorUtil.color(title))
|
||||||
.rows(rows)
|
.rows(rows)
|
||||||
.fill(filler());
|
.fill(filler());
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.loki.loparkour.menu.core;
|
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 {
|
public final class MenuStub {
|
||||||
private MenuStub() {}
|
private MenuStub() {}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public abstract class DynamicMenu {
|
||||||
* @param item The item
|
* @param item The item
|
||||||
* @param shouldDisplay Whether this item should be displayed right now.
|
* @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(
|
public void registerMainItem(
|
||||||
int row, int id,
|
int row, int id,
|
||||||
|
|
@ -55,7 +55,7 @@ public abstract class DynamicMenu {
|
||||||
* @param player The player
|
* @param player The player
|
||||||
* @param menu The menu
|
* @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) {
|
public void display(Player player, Menu menu) {
|
||||||
ParkourUser user = ParkourUser.getUser(player);
|
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.LPMenu;
|
||||||
import dev.loki.loparkour.menu.core.Menus;
|
import dev.loki.loparkour.menu.core.Menus;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
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.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -51,7 +51,7 @@ public class MainMenu extends LPMenu {
|
||||||
|
|
||||||
final int[] slots = distributeEvenly(count, SLOT_START, SLOT_END);
|
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++) {
|
for (int i = 0; i < count; i++) {
|
||||||
final int idx = 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;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy paged menu stub — real menus use LoLib GUI.
|
* Legacy paged menu stub — real menus use InventoryGui.
|
||||||
*/
|
*/
|
||||||
public class PagedMenu {
|
public class PagedMenu {
|
||||||
private final int rows;
|
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.session.core.Session;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
import dev.loki.loparkour.util.text.ColorUtil;
|
||||||
import dev.loki.loparkour.world.core.Divider;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -50,7 +50,7 @@ public class DuelsMenu extends LPMenu {
|
||||||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, sessions.size());
|
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, sessions.size());
|
||||||
|
|
||||||
InventoryGUI gui = baseGui(title, ROWS);
|
InventoryGui gui = baseGui(title, ROWS);
|
||||||
|
|
||||||
if (sessions.isEmpty()) {
|
if (sessions.isEmpty()) {
|
||||||
gui = gui.setItem(SLOT_EMPTY, localeItem(player, "play.multi.duels.menu_empty"));
|
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.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -36,7 +36,7 @@ public class LobbyMenu extends LPMenu {
|
||||||
final boolean isHost = !user.session.getPlayers().isEmpty()
|
final boolean isHost = !user.session.getPlayers().isEmpty()
|
||||||
&& user.session.getPlayers().get(0) == user;
|
&& user.session.getPlayers().get(0) == user;
|
||||||
|
|
||||||
InventoryGUI gui = baseGui(title, ROWS);
|
InventoryGui gui = baseGui(title, ROWS);
|
||||||
|
|
||||||
if (ParkourOption.PLAYER_MANAGEMENT.mayPerform(player) && isHost) {
|
if (ParkourOption.PLAYER_MANAGEMENT.mayPerform(player) && isHost) {
|
||||||
gui = gui.setItem(SLOT_PLAYER_MANAGEMENT, localeItem(player, "lobby.player_management.item"),
|
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.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.inventory.ClickType;
|
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.getPlayers().stream().filter(p -> p != viewer).toList());
|
||||||
others.addAll(session.getSpectators());
|
others.addAll(session.getSpectators());
|
||||||
|
|
||||||
InventoryGUI gui = baseGui(title, ROWS)
|
InventoryGui gui = baseGui(title, ROWS)
|
||||||
.setItem(SLOT_CLOSE, closeButton(player), e -> Menus.LOBBY.open(player));
|
.setItem(SLOT_CLOSE, closeButton(player), e -> Menus.LOBBY.open(player));
|
||||||
|
|
||||||
for (int i = 0; i < Math.min(others.size(), MAX_PLAYERS); i++) {
|
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.menu.core.LPMenu;
|
||||||
import dev.loki.loparkour.mode.base.Mode;
|
import dev.loki.loparkour.mode.base.Mode;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
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.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -46,7 +46,7 @@ public class SingleMenu extends LPMenu {
|
||||||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
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++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
final Mode mode = availableModes.get(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.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.world.core.Divider;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -43,7 +43,7 @@ public class SpectatorMenu extends LPMenu {
|
||||||
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
final int startIndex = currentPage * ITEMS_PER_PAGE;
|
||||||
final int endIndex = Math.min(startIndex + ITEMS_PER_PAGE, items.size());
|
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++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
final Session session = sessions.get(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.menu.core.Menus;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -40,7 +40,7 @@ public class LangMenu extends LPMenu {
|
||||||
|
|
||||||
final List<String> langs = new ArrayList<>(Locales.getLocaleKeys());
|
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++) {
|
for (int i = 0; i < Math.min(langs.size(), MAX_LANGS); i++) {
|
||||||
final String lang = langs.get(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.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.style.core.Style;
|
import dev.loki.loparkour.style.core.Style;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
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.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -168,7 +168,7 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
final String locale = pp.locale;
|
final String locale = pp.locale;
|
||||||
final String title = Locales.getString(locale, "settings.parkour_settings.styles.name");
|
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;
|
int slot = FIRST_STYLE_SLOT;
|
||||||
for (final Style style : Registry.getStyles()) {
|
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.menu.core.screen.ParkourOption;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
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.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ public class SettingsMenu extends LPMenu {
|
||||||
final String locale = locale(player);
|
final String locale = locale(player);
|
||||||
final String title = Locales.getString(locale, "settings.name");
|
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)) {
|
if (ParkourOption.PARKOUR_SETTINGS.mayPerform(player) && ParkourPlayer.isPlayer(player)) {
|
||||||
gui = gui.setItem(SLOT_PARKOUR_SETTINGS, localeItem(player, "settings.parkour_settings.item"), e -> {
|
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);
|
registerScore(time, "1.0", targetScore);
|
||||||
|
|
||||||
// Return to lobby after 5 seconds — guarded against null/closed session
|
// 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(() -> {
|
.runLater(() -> {
|
||||||
if (session != null && !state.stopped) {
|
if (session != null && !state.stopped) {
|
||||||
reset(false);
|
reset(false);
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import dev.loki.loparkour.mode.base.Mode;
|
||||||
import dev.loki.loparkour.mode.base.Modes;
|
import dev.loki.loparkour.mode.base.Modes;
|
||||||
|
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
import dev.loki.loparkour.config.locale.Locales;
|
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.LoParkour;
|
||||||
import dev.loki.loparkour.storage.Storage;
|
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.
|
* 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.LoParkour;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
import dev.loki.loparkour.player.service.ui.ParkourHotbar;
|
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.GameMode;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package dev.loki.loparkour.player.data;
|
||||||
|
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
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.Bukkit;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.entity.Player;
|
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.player.data.PreviousData;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
import dev.loki.loparkour.util.text.ColorUtil;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||||
import net.md_5.bungee.api.ChatMessageType;
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import org.bukkit.GameMode;
|
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.LoParkour;
|
||||||
import dev.loki.loparkour.schematic.core.SchematicManager;
|
import dev.loki.loparkour.schematic.core.SchematicManager;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
|
|
||||||
import java.io.File;
|
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.generator.core.coordinator.ParkourGenerator;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.world.core.Divider;
|
import dev.loki.loparkour.world.core.Divider;
|
||||||
import dev.lolib.scheduler.Scheduler;
|
import dev.loki.loparkour.scheduler.Scheduler;
|
||||||
import dev.lolib.scheduler.ScheduledTask;
|
import dev.loki.loparkour.scheduler.ScheduledTask;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.SQLPlayerRepository;
|
||||||
import dev.loki.loparkour.storage.sql.repo.SQLScoreRepository;
|
import dev.loki.loparkour.storage.sql.repo.SQLScoreRepository;
|
||||||
import dev.loki.loparkour.storage.sql.schema.SQLSchemaManager;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
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.LoParkour;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
import dev.lolib.worlds.VoidGenerator;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Difficulty;
|
import org.bukkit.Difficulty;
|
||||||
import org.bukkit.GameRule;
|
import org.bukkit.GameRule;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ author: loki
|
||||||
version: ${version}
|
version: ${version}
|
||||||
api-version: ${apiVersion}
|
api-version: ${apiVersion}
|
||||||
main: dev.loki.loparkour.LoParkour
|
main: dev.loki.loparkour.LoParkour
|
||||||
softdepend: [floodgate, Vault, PlaceholderAPI, Multiverse-Core, VoidGen]
|
softdepend: [floodgate, Vault, PlaceholderAPI, Multiverse-Core]
|
||||||
commands:
|
commands:
|
||||||
loparkour:
|
loparkour:
|
||||||
aliases: [parkour, lp]
|
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