Add lobby/game worlds, void generator, dbd reload, and fix plugin loading

- Add VoidWorldGenerator for empty void worlds
- Add LobbyManager with auto-creation of lobby world (stone platform 7x7, built once) and game world
- Add /dbd reload command to reload all configs at runtime
- Add plugin.yml for proper command registration, remove paper-plugin.yml
- Fix mdbdBootstrap/mdbdLoader public constructors (Paper requirement)
- Fix command registration (getCommand null on Paper plugin system)
This commit is contained in:
loki5512344 2026-06-01 22:08:37 +02:00
parent 76e0c1e167
commit 34e0aebd5d
27 changed files with 767 additions and 85 deletions

View file

@ -10,7 +10,7 @@ repositories {
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
compileOnly("com.comphenix.protocol:ProtocolLib:5.3.0")
}
@ -20,7 +20,7 @@ java {
tasks {
runServer {
minecraftVersion("1.20.6")
minecraftVersion("1.21.1")
jvmArgs("-Xms2G", "-Xmx2G")
}

View file

@ -0,0 +1,202 @@
package dev.loki.command;
import dev.loki.data.GeneratorDataManager;
import dev.loki.mdbd;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class DBDAdminCommand implements TabExecutor {
private final mdbd plugin;
private final GeneratorDataManager generatorData;
private final Set<UUID> selectingGenerator;
private final MiniMessage mm;
private BukkitTask actionBarTask;
public DBDAdminCommand(mdbd plugin, GeneratorDataManager generatorData) {
this.plugin = plugin;
this.generatorData = generatorData;
this.selectingGenerator = new HashSet<>();
this.mm = MiniMessage.miniMessage();
startActionBarTask();
}
private void startActionBarTask() {
this.actionBarTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
for (UUID uuid : selectingGenerator) {
Player p = Bukkit.getPlayer(uuid);
if (p != null && p.isOnline()) {
p.sendActionBar(mm.deserialize(
"&#FFA500⚒ Режим установки: &#00FF00сломайте блок для генератора &#FFA500⚒"));
}
}
}, 0L, 20L);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("mdbd.admin")) {
sender.sendMessage(Component.text("Нет прав!", NamedTextColor.RED));
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("Только игроки!", NamedTextColor.RED));
return true;
}
if (args.length == 0) {
sendUsage(sender);
return true;
}
return switch (args[0].toLowerCase()) {
case "create" -> handleCreate(player, args);
case "list" -> handleList(player);
case "remove" -> handleRemove(player, args);
case "clear" -> handleClear(player);
default -> {
sendUsage(sender);
yield true;
}
};
}
@SuppressWarnings("UnreachableBranchError")
private boolean handleCreate(Player player, String[] args) {
if (args.length < 2) {
player.sendMessage(Component.text("&#FF0000Использование: /dbdadmin create <generator>"));
return true;
}
String type = args[1].toLowerCase();
switch (type) {
case "generator" -> {
if (selectingGenerator.contains(player.getUniqueId())) {
selectingGenerator.remove(player.getUniqueId());
player.sendMessage(Component.text("&#FFA500Режим выбора генератора отключен"));
} else {
selectingGenerator.add(player.getUniqueId());
player.sendMessage(Component.text("&#00FF00Режим выбора генератора включен!"));
player.sendMessage(Component.text("&#FFD700Сломайте блок, чтобы установить генератор"));
player.sendMessage(Component.text("&#FFD700(блок не сломается, а сохранятся его координаты)"));
player.sendMessage(Component.text("&#FFA500Введите /dbdadmin create generator снова, чтобы отключить режим"));
}
return true;
}
default -> {
player.sendMessage(Component.text("&#FF0000Неизвестный тип: " + type));
return true;
}
}
}
private boolean handleList(Player player) {
var entries = generatorData.getGeneratorEntries();
player.sendMessage(Component.text("&#FFD700=== Генераторы (" + entries.size() + ") ==="));
if (entries.isEmpty()) {
player.sendMessage(Component.text("&#808000Нет установленных генераторов"));
} else {
for (int i = 0; i < entries.size(); i++) {
player.sendMessage(Component.text("&#00CED1[" + i + "] " + entries.get(i)));
}
}
return true;
}
private boolean handleRemove(Player player, String[] args) {
if (args.length < 2) {
player.sendMessage(Component.text("&#FF0000Использование: /dbdadmin remove <id>"));
return true;
}
try {
int id = Integer.parseInt(args[1]);
var entries = generatorData.getGeneratorEntries();
if (id < 0 || id >= entries.size()) {
player.sendMessage(Component.text("&#FF0000Генератор с ID " + id + " не найден"));
return true;
}
String removed = entries.get(id);
generatorData.removeGenerator(id);
player.sendMessage(Component.text("&#00FF00Генератор удален: " + removed));
} catch (NumberFormatException e) {
player.sendMessage(Component.text("&#FF0000ID должен быть числом"));
}
return true;
}
private boolean handleClear(Player player) {
generatorData.clearGenerators();
player.sendMessage(Component.text("&#FFA500Все генераторы удалены"));
return true;
}
private void sendUsage(CommandSender sender) {
sender.sendMessage(Component.text("&#FF6B00=== DBD Admin ==="));
sender.sendMessage(Component.text("&#FFD700/dbdadmin create generator — Режим установки генератора"));
sender.sendMessage(Component.text("&#FFD700/dbdadmin list — Список генераторов"));
sender.sendMessage(Component.text("&#FFD700/dbdadmin remove <id> — Удалить генератор"));
sender.sendMessage(Component.text("&#FFD700/dbdadmin clear — Очистить все генераторы"));
}
public boolean isSelectingGenerator(UUID uuid) {
return selectingGenerator.contains(uuid);
}
public void shutdown() {
if (actionBarTask != null) {
actionBarTask.cancel();
actionBarTask = null;
}
selectingGenerator.clear();
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("mdbd.admin")) return List.of();
if (args.length == 1) {
return Arrays.asList("create", "list", "remove", "clear").stream()
.filter(s -> s.startsWith(args[0].toLowerCase()))
.toList();
}
if (args.length == 2 && args[0].equalsIgnoreCase("create")) {
return List.of("generator").stream()
.filter(s -> s.startsWith(args[1].toLowerCase()))
.toList();
}
if (args.length == 2 && args[0].equalsIgnoreCase("remove")) {
var entries = generatorData.getGeneratorEntries();
List<String> ids = new ArrayList<>();
for (int i = 0; i < entries.size(); i++) {
ids.add(String.valueOf(i));
}
return ids.stream()
.filter(s -> s.startsWith(args[1]))
.toList();
}
return List.of();
}
}

View file

@ -1,5 +1,6 @@
package dev.loki.command;
import dev.loki.core.GameState;
import dev.loki.core.GameManager;
import dev.loki.mdbd;
import net.kyori.adventure.text.Component;
@ -39,6 +40,7 @@ public class DBDCommand implements TabExecutor {
case "forcestart" -> handleForceStart(sender);
case "create" -> handleCreate(sender);
case "stop" -> handleStop(sender);
case "reload" -> handleReload(sender);
default -> {
sendUsage(sender);
yield true;
@ -82,9 +84,8 @@ public class DBDCommand implements TabExecutor {
sender.sendMessage(Component.text("Нет активной игры!", NamedTextColor.RED));
return true;
}
var phase = session.getCurrentPhase();
if (phase instanceof dev.loki.phase.LobbyPhase lobby) {
lobby.forceStart();
if (session.getState() == GameState.LOBBY) {
session.forceStart();
sender.sendMessage(Component.text("[DBD] Игра форсирована!", NamedTextColor.GREEN));
} else {
sender.sendMessage(Component.text("Нельзя форсировать сейчас", NamedTextColor.RED));
@ -105,6 +106,16 @@ public class DBDCommand implements TabExecutor {
return true;
}
private boolean handleReload(CommandSender sender) {
if (!sender.hasPermission("mdbd.admin")) {
sender.sendMessage(Component.text("Нет прав!", NamedTextColor.RED));
return true;
}
plugin.reloadConfigs();
sender.sendMessage(Component.text("[DBD] Конфиги перезагружены!", NamedTextColor.GREEN));
return true;
}
private boolean handleStop(CommandSender sender) {
if (!sender.hasPermission("mdbd.admin")) {
sender.sendMessage(Component.text("Нет прав!", NamedTextColor.RED));
@ -123,6 +134,7 @@ public class DBDCommand implements TabExecutor {
sender.sendMessage(Component.text("/dbd create - создать игру", NamedTextColor.GRAY));
sender.sendMessage(Component.text("/dbd forcestart - форс старт", NamedTextColor.GRAY));
sender.sendMessage(Component.text("/dbd stop - остановить", NamedTextColor.GRAY));
sender.sendMessage(Component.text("/dbd reload - перезагрузить конфиги", NamedTextColor.GRAY));
}
}
@ -132,7 +144,7 @@ public class DBDCommand implements TabExecutor {
if (args.length == 1) {
List<String> completions = new ArrayList<>(Arrays.asList("join", "leave"));
if (sender.hasPermission("mdbd.admin")) {
completions.addAll(Arrays.asList("create", "forcestart", "stop"));
completions.addAll(Arrays.asList("create", "forcestart", "stop", "reload"));
}
return completions.stream()
.filter(s -> s.startsWith(args[0].toLowerCase()))

View file

@ -115,6 +115,12 @@ public class GameSession {
return plugin.getConfigManager().getGame();
}
public void forceStart() {
if (currentPhase instanceof dev.loki.phase.LobbyPhase lobby) {
lobby.forceStart();
}
}
public int getTicksElapsed() {
return ticksElapsed;
}

View file

@ -0,0 +1,148 @@
package dev.loki.data;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class GeneratorDataManager {
private static final double ZONE_RADIUS = 3.0;
private final JavaPlugin plugin;
private final File dataFile;
private FileConfiguration data;
public GeneratorDataManager(JavaPlugin plugin) {
this.plugin = plugin;
this.dataFile = new File(plugin.getDataFolder(), "generators.yml");
load();
}
public void load() {
if (!dataFile.exists()) {
try {
dataFile.createNewFile();
} catch (IOException e) {
plugin.getLogger().warning("Could not create generators.yml");
}
}
this.data = YamlConfiguration.loadConfiguration(dataFile);
}
public void save() {
try {
data.save(dataFile);
} catch (IOException e) {
plugin.getLogger().warning("Could not save generators.yml");
}
}
public void addGenerator(Location location) {
List<String> list = data.getStringList("generators");
String entry = formatLocation(location);
if (!list.contains(entry)) {
list.add(entry);
data.set("generators", list);
save();
}
}
public void removeGenerator(int index) {
List<String> list = data.getStringList("generators");
if (index >= 0 && index < list.size()) {
list.remove(index);
data.set("generators", list);
save();
}
}
public void clearGenerators() {
data.set("generators", null);
save();
}
public List<Location> getGenerators() {
List<Location> result = new ArrayList<>();
List<String> list = data.getStringList("generators");
for (String entry : list) {
Location loc = parseLocation(entry);
if (loc != null) {
result.add(loc);
}
}
return result;
}
public List<String> getGeneratorEntries() {
return data.getStringList("generators");
}
public int getCount() {
return data.getStringList("generators").size();
}
public static String formatLocation(Location loc) {
return String.format("%s:%.0f,%.0f,%.0f",
loc.getWorld().getName(),
loc.getX(),
loc.getY(),
loc.getZ());
}
private Location parseLocation(String str) {
String[] parts = str.split(":");
if (parts.length < 2) return null;
World world = Bukkit.getWorld(parts[0].trim());
if (world == null) return null;
String[] coords = parts[1].split(",");
if (coords.length < 3) return null;
try {
double x = Double.parseDouble(coords[0].trim());
double y = Double.parseDouble(coords[1].trim());
double z = Double.parseDouble(coords[2].trim());
return new Location(world, x, y, z);
} catch (NumberFormatException e) {
return null;
}
}
public static double getZoneRadius() {
return ZONE_RADIUS;
}
public boolean isInZone(Location location, List<Location> generators) {
for (Location genLoc : generators) {
if (!genLoc.getWorld().equals(location.getWorld())) continue;
if (genLoc.distance(location) <= ZONE_RADIUS) {
return true;
}
}
return false;
}
public Location getNearestGenerator(Location location, List<Location> generators) {
Location nearest = null;
double nearestDist = ZONE_RADIUS;
for (Location genLoc : generators) {
if (!genLoc.getWorld().equals(location.getWorld())) continue;
double dist = genLoc.distance(location);
if (dist <= nearestDist) {
nearestDist = dist;
nearest = genLoc;
}
}
return nearest;
}
}

View file

@ -24,6 +24,7 @@ public class PlayerStats {
public UUID getPlayerId() { return playerId; }
public int getTotalMatches() { return totalMatches; }
public void incrementMatches() { this.totalMatches++; }
public void setTotalMatches(int matches) { this.totalMatches = matches; }
public int getEscapes() { return escapes; }
public void incrementEscapes() { this.escapes++; }
public int getDeaths() { return deaths; }

View file

@ -44,7 +44,7 @@ public class YamlStorage implements DataStorage {
}
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
PlayerStats stats = new PlayerStats(playerId);
stats.incrementMatches(config.getInt("matches", 0));
stats.setTotalMatches(config.getInt("matches", 0));
for (int i = 0; i < config.getInt("escapes", 0); i++) stats.incrementEscapes();
for (int i = 0; i < config.getInt("deaths", 0); i++) stats.incrementDeaths();
for (int i = 0; i < config.getInt("kills", 0); i++) stats.incrementKills();

View file

@ -67,7 +67,7 @@ public class KillerAttack {
}
private void applyCooldown(Killer killer) {
cooldowns.put(killer.getUuid(), Bukkit.getCurrentTick());
cooldowns.put(killer.getUuid(), (long) Bukkit.getCurrentTick());
}
public double getCooldownProgress(Killer killer) {

View file

@ -0,0 +1,46 @@
package dev.loki.listener;
import dev.loki.command.DBDAdminCommand;
import dev.loki.data.GeneratorDataManager;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
public class GeneratorSelectListener implements Listener {
private final DBDAdminCommand adminCommand;
private final GeneratorDataManager generatorData;
public GeneratorSelectListener(DBDAdminCommand adminCommand, GeneratorDataManager generatorData) {
this.adminCommand = adminCommand;
this.generatorData = generatorData;
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
if (!adminCommand.isSelectingGenerator(player.getUniqueId())) {
return;
}
event.setCancelled(true);
Location loc = event.getBlock().getLocation();
Location genLoc = new Location(loc.getWorld(),
loc.getX() + 0.5,
loc.getY(),
loc.getZ() + 0.5);
generatorData.addGenerator(genLoc);
player.sendMessage(Component.text("&#00FF00Генератор установлен!"));
player.sendMessage(Component.text("&#00CED1Координаты: " + GeneratorDataManager.formatLocation(genLoc)));
player.sendMessage(Component.text("&#FFD700Всего генераторов: " + generatorData.getCount()));
player.sendMessage(Component.text("&#FFA500Режим выбора остается активным. Ломайте следующий блок или"));
player.sendMessage(Component.text("&#FFA500введите /dbdadmin create generator для отключения"));
}
}

View file

@ -1,6 +1,7 @@
package dev.loki.listener;
import dev.loki.core.GameManager;
import dev.loki.objective.GeneratorManager;
import dev.loki.player.BasePlayer;
import dev.loki.player.PlayerState;
import org.bukkit.entity.Player;
@ -19,6 +20,7 @@ import java.util.function.BiConsumer;
public class PlayerInteractListener implements Listener {
private final Plugin plugin;
private final GameManager gameManager;
private final GeneratorManager generatorManager;
private BiConsumer<Player, Action> onGeneratorInteract;
private BiConsumer<Player, Boolean> onAttack;
@ -30,9 +32,10 @@ public class PlayerInteractListener implements Listener {
private final Map<UUID, Long> lastClick = new ConcurrentHashMap<>();
private static final long COOLDOWN_MS = 200;
public PlayerInteractListener(Plugin plugin, GameManager gameManager) {
public PlayerInteractListener(Plugin plugin, GameManager gameManager, GeneratorManager generatorManager) {
this.plugin = plugin;
this.gameManager = gameManager;
this.generatorManager = generatorManager;
}
public void setOnGeneratorInteract(BiConsumer<Player, Action> h) { this.onGeneratorInteract = h; }
@ -72,11 +75,20 @@ public class PlayerInteractListener implements Listener {
private void handleRightClick(PlayerInteractEvent event, Player player, BasePlayer bp) {
if (event.getClickedBlock() == null) {
if (!bp.isKiller() && onGeneratorInteract != null && generatorManager.isInAnyZone(player)) {
onGeneratorInteract.accept(player, event.getAction());
return;
}
if (onAttack != null && bp.isKiller()) onAttack.accept(player, true);
return;
}
if (!bp.isKiller() && onGeneratorInteract != null && generatorManager.isInAnyZone(player)) {
onGeneratorInteract.accept(player, event.getAction());
return;
}
String type = event.getClickedBlock().getType().toString();
if (type.contains("TRAPDOOR") && onGeneratorInteract != null) onGeneratorInteract.accept(player, event.getAction());
if ((type.contains("FENCE") || type.contains("PLANKS")) && onPalletInteract != null && !bp.isKiller()) onPalletInteract.accept(player, true);
if (!bp.isKiller() && onRescueAttempt != null) onRescueAttempt.accept(player, true);
if (type.contains("CHEST") && onChestInteract != null) onChestInteract.accept(player, true);

View file

@ -1,5 +1,6 @@
package dev.loki.map;
import dev.loki.data.GeneratorDataManager;
import dev.loki.mdbd;
import dev.loki.config.LocationsConfig;
import org.bukkit.Bukkit;
@ -76,6 +77,23 @@ public class MapBuilder {
plugin.getLogger().info("Map built successfully!");
}
public void buildGenerators(GeneratorDataManager dataManager) {
List<Location> generators = dataManager.getGenerators();
for (int i = 0; i < generators.size(); i++) {
Location loc = generators.get(i);
if (loc == null) continue;
placeGeneratorMarker(loc);
}
}
public void placeGeneratorMarker(Location location) {
World world = location.getWorld();
if (world == null) return;
world.getBlockAt(location).setType(org.bukkit.Material.SEA_LANTERN);
world.getBlockAt(location.clone().add(0, 1, 0)).setType(org.bukkit.Material.REDSTONE_LAMP);
}
public void clearMap(LocationsConfig config) {
// Clear all objects (set to air)
clearLocations(config.getGenerators());
@ -169,8 +187,8 @@ public class MapBuilder {
world.getBlockAt(loc.clone().add(0, 1, 1)).setType(org.bukkit.Material.REDSTONE_LAMP);
world.getBlockAt(loc.clone().add(0, 1, -1)).setType(org.bukkit.Material.REDSTONE_LAMP);
// Control panel: Lever
world.getBlockAt(loc.clone().add(1, 0, 1)).setType(org.bukkit Material.OAK_PLANKS);
world.getBlockAt(loc.clone().add(1, 1, 1)).setType(org.bukkit Material.LEVER);
world.getBlockAt(loc.clone().add(1, 0, 1)).setType(org.bukkit.Material.OAK_PLANKS);
world.getBlockAt(loc.clone().add(1, 1, 1)).setType(org.bukkit.Material.LEVER);
}
private void buildDefaultHook(Location loc) {
@ -181,7 +199,7 @@ public class MapBuilder {
world.getBlockAt(loc.clone().add(0, y, 0)).setType(org.bukkit.Material.IRON_BARS);
}
// Hook at top: Tripwire hook
world.getBlockAt(loc.clone().add(0, 3, 0)).setType(org.bukkit Material.TRIPWIRE_HOOK);
world.getBlockAt(loc.clone().add(0, 3, 0)).setType(org.bukkit.Material.TRIPWIRE_HOOK);
}
private void buildDefaultPallet(Location loc) {

View file

@ -11,8 +11,7 @@ import org.bukkit.structure.StructureManager;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Random;
import java.util.logging.Level;
public class MapTemplate {
@ -27,7 +26,7 @@ public class MapTemplate {
public boolean loadFromFile(File file) {
try {
this.structure = structureManager.loadStructure(file.toPath());
this.structure = structureManager.loadStructure(file);
return true;
} catch (IOException e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to load template: " + name, e);
@ -41,7 +40,7 @@ public class MapTemplate {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
structureManager.saveStructure(file.toPath(), structure);
structureManager.saveStructure(file, structure);
} catch (IOException e) {
Bukkit.getLogger().log(Level.WARNING, "Failed to save template: " + name, e);
}
@ -59,23 +58,24 @@ public class MapTemplate {
int maxZ = Math.max(corner1.getBlockZ(), corner2.getBlockZ());
Location origin = new Location(world, minX, minY, minZ);
Location farCorner = new Location(world, maxX, maxY, maxZ);
int sizeX = maxX - minX + 1;
int sizeY = maxY - minY + 1;
int sizeZ = maxZ - minZ + 1;
this.structure = structureManager.createStructure();
structure.fill(origin, sizeX, sizeY, sizeZ, true);
structure.fill(origin, farCorner, true);
}
public void place(Location location, boolean includeEntities) {
if (structure == null) return;
structure.place(location, true, StructureRotation.NONE, Mirror.NONE,
0, 1.0f, includeEntities ? 1.0f : 0.0f);
0, 1.0f, new Random());
}
public void placeWithRotation(Location location, StructureRotation rotation, Mirror mirror) {
if (structure == null) return;
structure.place(location, true, rotation, mirror, 0, 1.0f, 0.0f);
structure.place(location, true, rotation, mirror, 0, 1.0f, new Random());
}
public void remove(Location location) {
@ -83,15 +83,11 @@ public class MapTemplate {
World world = location.getWorld();
if (world == null) return;
Location rel = structure.getRelativePosition();
for (int x = 0; x < structure.getSize().getBlockX(); x++) {
for (int y = 0; y < structure.getSize().getBlockY(); y++) {
for (int z = 0; z < structure.getSize().getBlockZ(); z++) {
Location blockLoc = location.clone().add(
x - rel.getBlockX(),
y - rel.getBlockY(),
z - rel.getBlockZ()
);
org.bukkit.util.Vector size = structure.getSize();
for (int x = 0; x < size.getBlockX(); x++) {
for (int y = 0; y < size.getBlockY(); y++) {
for (int z = 0; z < size.getBlockZ(); z++) {
Location blockLoc = location.clone().add(x, y, z);
world.getBlockAt(blockLoc).setType(org.bukkit.Material.AIR);
}
}

View file

@ -1,11 +1,15 @@
package dev.loki;
import dev.loki.command.DBDAdminCommand;
import dev.loki.command.DBDCommand;
import dev.loki.command.SetupCommand;
import dev.loki.config.ConfigManager;
import dev.loki.core.GameManager;
import dev.loki.data.GeneratorDataManager;
import dev.loki.listener.GeneratorSelectListener;
import dev.loki.map.MapBuilder;
import dev.loki.map.TemplateManager;
import dev.loki.world.LobbyManager;
import org.bukkit.plugin.java.JavaPlugin;
public final class mdbd extends JavaPlugin {
@ -14,6 +18,9 @@ public final class mdbd extends JavaPlugin {
private GameManager gameManager;
private TemplateManager templateManager;
private MapBuilder mapBuilder;
private GeneratorDataManager generatorData;
private DBDAdminCommand dbdAdminCommand;
private LobbyManager lobbyManager;
@Override
public void onEnable() {
@ -29,6 +36,12 @@ public final class mdbd extends JavaPlugin {
this.mapBuilder = new MapBuilder(this, templateManager);
this.generatorData = new GeneratorDataManager(this);
this.lobbyManager = new LobbyManager(this);
this.lobbyManager.createLobbyWorld();
this.lobbyManager.createGameWorld();
this.gameManager = new GameManager(this);
getCommand("dbd").setExecutor(new DBDCommand(this, gameManager));
@ -36,8 +49,16 @@ public final class mdbd extends JavaPlugin {
getCommand("dbdsetup").setExecutor(new SetupCommand(this));
getCommand("dbdsetup").setTabCompleter(new SetupCommand(this));
this.dbdAdminCommand = new DBDAdminCommand(this, generatorData);
getCommand("dbdadmin").setExecutor(dbdAdminCommand);
getCommand("dbdadmin").setTabCompleter(dbdAdminCommand);
getServer().getPluginManager().registerEvents(
new GeneratorSelectListener(dbdAdminCommand, generatorData), this);
getLogger().info("Mdbd Plugin enabled successfully!");
getLogger().info("Loaded " + templateManager.getAllTemplates().size() + " templates");
getLogger().info("Loaded " + generatorData.getCount() + " generators from data");
}
@Override
@ -49,6 +70,9 @@ public final class mdbd extends JavaPlugin {
if (configManager != null) {
configManager.saveAllConfigs();
}
if (generatorData != null) {
generatorData.save();
}
instance = null;
}
@ -71,4 +95,23 @@ public final class mdbd extends JavaPlugin {
public MapBuilder getMapBuilder() {
return mapBuilder;
}
public GeneratorDataManager getGeneratorData() {
return generatorData;
}
public DBDAdminCommand getDbdAdminCommand() {
return dbdAdminCommand;
}
public LobbyManager getLobbyManager() {
return lobbyManager;
}
public void reloadConfigs() {
configManager.loadConfigs();
templateManager.loadAllTemplates();
generatorData.load();
getLogger().info("All configs reloaded!");
}
}

View file

@ -3,7 +3,9 @@ package dev.loki;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
class mdbdBootstrap implements PluginBootstrap {
public class mdbdBootstrap implements PluginBootstrap {
public mdbdBootstrap() {}
@Override
public void bootstrap(final BootstrapContext context) {

View file

@ -3,7 +3,9 @@ package dev.loki;
import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
import io.papermc.paper.plugin.loader.PluginLoader;
class mdbdLoader implements PluginLoader {
public class mdbdLoader implements PluginLoader {
public mdbdLoader() {}
@Override
public void classloader(final PluginClasspathBuilder builder) {

View file

@ -1,12 +1,16 @@
package dev.loki.objective;
import dev.loki.data.GeneratorDataManager;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import java.util.UUID;
public class Generator {
private final int id;
private final Location location;
private final double zoneRadius;
private double progress;
private boolean isCompleted;
private boolean isBlocked;
@ -15,8 +19,13 @@ public class Generator {
private boolean wasKicked;
public Generator(int id, Location location) {
this(id, location, GeneratorDataManager.getZoneRadius());
}
public Generator(int id, Location location, double zoneRadius) {
this.id = id;
this.location = location;
this.zoneRadius = zoneRadius;
this.progress = 0.0;
this.isCompleted = false;
this.isBlocked = false;
@ -33,6 +42,20 @@ public class Generator {
return location;
}
public double getZoneRadius() {
return zoneRadius;
}
public boolean isPlayerInZone(Player player) {
if (!player.getWorld().equals(location.getWorld())) return false;
return player.getLocation().distance(location) <= zoneRadius;
}
public boolean isLocationInZone(Location loc) {
if (!loc.getWorld().equals(location.getWorld())) return false;
return loc.distance(location) <= zoneRadius;
}
public double getProgress() {
return progress;
}
@ -107,6 +130,7 @@ public class Generator {
return !isCompleted && !isBlocked;
}
@Deprecated
public Block getBlock() {
return location.getBlock();
}

View file

@ -1,7 +1,8 @@
package dev.loki.objective;
import dev.loki.config.LocationsConfig;
import dev.loki.data.GeneratorDataManager;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashMap;
@ -13,19 +14,17 @@ public class GeneratorManager {
private final int requiredCount;
private int completedCount;
public GeneratorManager(LocationsConfig locationsConfig, int requiredCount) {
public GeneratorManager(GeneratorDataManager dataManager, int requiredCount) {
this.generators = new HashMap<>();
this.requiredCount = requiredCount;
this.completedCount = 0;
loadGenerators(locationsConfig);
loadGenerators(dataManager);
}
private void loadGenerators(LocationsConfig config) {
List<Location> locations = config.getGenerators();
int id = 0;
for (Location loc : locations) {
generators.put(id, new Generator(id, loc));
id++;
private void loadGenerators(GeneratorDataManager dataManager) {
List<Location> locations = dataManager.getGenerators();
for (int i = 0; i < locations.size(); i++) {
generators.put(i, new Generator(i, locations.get(i)));
}
}
@ -35,13 +34,32 @@ public class GeneratorManager {
public Generator getGeneratorAt(Location location) {
for (Generator gen : generators.values()) {
if (gen.getLocation().distance(location) < 2.0) {
if (gen.isLocationInZone(location)) {
return gen;
}
}
return null;
}
public Generator getNearestGenerator(Location location, double maxDistance) {
Generator nearest = null;
double nearestDist = maxDistance;
for (Generator gen : generators.values()) {
if (!gen.getLocation().getWorld().equals(location.getWorld())) continue;
double dist = gen.getDistance(location);
if (dist < nearestDist) {
nearestDist = dist;
nearest = gen;
}
}
return nearest;
}
public boolean isInAnyZone(Player player) {
return getGeneratorAt(player.getLocation()) != null;
}
public List<Generator> getAllGenerators() {
return new ArrayList<>(generators.values());
}

View file

@ -25,7 +25,7 @@ public class GeneratorRepair {
this.plugin = plugin;
this.manager = manager;
this.sessions = new HashMap<>();
this.skillCheck = new SkillCheck(plugin);
this.skillCheck = new SkillCheck(plugin, this);
}
public void startRepair(Player player, Generator generator) {

View file

@ -9,13 +9,15 @@ import java.util.UUID;
public class SkillCheck {
private final mdbd plugin;
private final GeneratorRepair generatorRepair;
private final Map<UUID, CheckState> activeChecks;
private static final double ZONE_SIZE = 0.15;
private static final double ROTATION_SPEED = 3.0;
public SkillCheck(mdbd plugin) {
public SkillCheck(mdbd plugin, GeneratorRepair generatorRepair) {
this.plugin = plugin;
this.generatorRepair = generatorRepair;
this.activeChecks = new HashMap<>();
}
@ -58,27 +60,17 @@ public class SkillCheck {
private void onSuccess(CheckState state) {
activeChecks.remove(state.playerId);
GeneratorRepair repair = getGeneratorRepair();
if (repair != null) {
Bukkit.getScheduler().runTask(plugin, () ->
repair.onSkillCheckResult(state.playerId, true, state.generator)
generatorRepair.onSkillCheckResult(state.playerId, true, state.generator)
);
}
}
private void onFail(CheckState state) {
activeChecks.remove(state.playerId);
GeneratorRepair repair = getGeneratorRepair();
if (repair != null) {
Bukkit.getScheduler().runTask(plugin, () ->
repair.onSkillCheckResult(state.playerId, false, state.generator)
generatorRepair.onSkillCheckResult(state.playerId, false, state.generator)
);
}
}
private GeneratorRepair getGeneratorRepair() {
return null;
}
public boolean isActive(UUID playerId) {
return activeChecks.containsKey(playerId);

View file

@ -53,7 +53,7 @@ public class CorruptInterventionPerk extends BasePerk {
private void spawnBlockEffect(Player player) {
Location loc = player.getLocation();
player.getWorld().spawnParticle(Particle.BARRIER, loc.add(0, 1, 0), 5, 0.5, 0.5, 0.5, 0);
player.getWorld().spawnParticle(Particle.WITCH, loc.add(0, 1, 0), 5, 0.5, 0.5, 0.5, 0);
}
private void startEffectTimer() {

View file

@ -38,7 +38,7 @@ public class BorrowedTimePerk extends BasePerk {
if (bukkitPlayer == null) return;
bukkitPlayer.addPotionEffect(new PotionEffect(
PotionEffectType.DAMAGE_RESISTANCE,
PotionEffectType.RESISTANCE,
ENDURANCE_DURATION_SECONDS * 20,
4,
false, false, true));
@ -65,7 +65,7 @@ public class BorrowedTimePerk extends BasePerk {
public void onRemove(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer != null) {
bukkitPlayer.removePotionEffect(PotionEffectType.DAMAGE_RESISTANCE);
bukkitPlayer.removePotionEffect(PotionEffectType.RESISTANCE);
}
sendPerkMessage(player, "Перк деактивирован");
}

View file

@ -57,7 +57,7 @@ public final class LocationUtil {
public static boolean hasEntityLineOfSight(Entity from, Entity to, double maxDistance) {
if (from == null || to == null) return false;
return from.hasLineOfSight(to) && distanceSquared(from.getLocation(), to.getLocation()) <= maxDistance * maxDistance;
return hasLineOfSight(from.getLocation(), to.getLocation(), maxDistance);
}
public static boolean isLookingAt(Entity entity, Location target, double angleThreshold) {

View file

@ -80,7 +80,7 @@ public final class ParticleUtil {
public static void blood(Location loc) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.BLOCK_CRUMBLE, loc, 20, 0.2, 0.2, 0.2, 0,
w.spawnParticle(Particle.BLOCK, loc, 20, 0.2, 0.2, 0.2, 0,
org.bukkit.Material.REDSTONE_BLOCK.createBlockData());
}

View file

@ -0,0 +1,149 @@
package dev.loki.world;
import dev.loki.mdbd;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import java.io.File;
public class LobbyManager {
private final mdbd plugin;
private World lobbyWorld;
private World gameWorld;
public LobbyManager(mdbd plugin) {
this.plugin = plugin;
}
public void createLobbyWorld() {
World existing = Bukkit.getWorld("lobby");
if (existing != null) {
lobbyWorld = existing;
plugin.getLogger().info("Lobby world already loaded, using existing");
return;
}
boolean isNew = !worldFolderExists("lobby");
WorldCreator creator = new WorldCreator("lobby");
creator.generator(new VoidWorldGenerator());
creator.type(WorldType.FLAT);
creator.environment(World.Environment.NORMAL);
creator.generateStructures(false);
lobbyWorld = creator.createWorld();
if (lobbyWorld == null) {
plugin.getLogger().severe("Failed to create lobby world!");
return;
}
lobbyWorld.setAutoSave(false);
lobbyWorld.setMonsterSpawnLimit(0);
lobbyWorld.setAnimalSpawnLimit(0);
lobbyWorld.setGameRule(GameRule.DO_MOB_SPAWNING, false);
lobbyWorld.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
lobbyWorld.setGameRule(GameRule.DO_WEATHER_CYCLE, false);
lobbyWorld.setGameRule(GameRule.KEEP_INVENTORY, true);
lobbyWorld.setGameRule(GameRule.DO_FIRE_TICK, false);
lobbyWorld.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false);
lobbyWorld.setTime(6000);
if (isNew) {
buildPlatform();
}
lobbyWorld.setSpawnLocation(0, 65, 0);
if (isNew) {
plugin.getLogger().info("Lobby world created with stone platform!");
} else {
plugin.getLogger().info("Lobby world loaded (platform already exists)");
}
}
public void createGameWorld() {
World existing = Bukkit.getWorld("game");
if (existing != null) {
gameWorld = existing;
plugin.getLogger().info("Game world already loaded, using existing");
return;
}
WorldCreator creator = new WorldCreator("game");
creator.environment(World.Environment.NORMAL);
creator.generateStructures(false);
gameWorld = creator.createWorld();
if (gameWorld == null) {
plugin.getLogger().severe("Failed to create game world!");
return;
}
gameWorld.setAutoSave(false);
gameWorld.setMonsterSpawnLimit(0);
gameWorld.setAnimalSpawnLimit(0);
gameWorld.setGameRule(GameRule.DO_MOB_SPAWNING, false);
gameWorld.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
gameWorld.setGameRule(GameRule.DO_WEATHER_CYCLE, false);
gameWorld.setGameRule(GameRule.KEEP_INVENTORY, true);
gameWorld.setGameRule(GameRule.DO_FIRE_TICK, false);
gameWorld.setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, false);
gameWorld.setTime(6000);
if (worldFolderExists("game")) {
plugin.getLogger().info("Game world loaded");
} else {
plugin.getLogger().info("Game world created!");
}
}
private boolean worldFolderExists(String worldName) {
File worldDir = new File(Bukkit.getWorldContainer(), worldName);
return worldDir.isDirectory();
}
private void buildPlatform() {
int y = 64;
for (int x = -3; x <= 3; x++) {
for (int z = -3; z <= 3; z++) {
lobbyWorld.getBlockAt(x, y, z).setType(Material.STONE);
}
}
for (int x = -3; x <= 3; x++) {
for (int z = -3; z <= 3; z++) {
lobbyWorld.getBlockAt(x, y - 1, z).setType(Material.BLACK_STAINED_GLASS);
}
}
for (int x = -4; x <= 4; x++) {
lobbyWorld.getBlockAt(x, y, -4).setType(Material.OAK_FENCE);
lobbyWorld.getBlockAt(x, y, 4).setType(Material.OAK_FENCE);
}
for (int z = -4; z <= 4; z++) {
lobbyWorld.getBlockAt(-4, y, z).setType(Material.OAK_FENCE);
lobbyWorld.getBlockAt(4, y, z).setType(Material.OAK_FENCE);
}
lobbyWorld.getBlockAt(0, y + 1, 0).setType(Material.GLOWSTONE);
}
public World getLobbyWorld() {
return lobbyWorld;
}
public World getGameWorld() {
return gameWorld;
}
public void teleportToLobby(org.bukkit.entity.Player player) {
if (lobbyWorld != null) {
player.teleport(lobbyWorld.getSpawnLocation());
}
}
}

View file

@ -0,0 +1,14 @@
package dev.loki.world;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
public class VoidWorldGenerator extends ChunkGenerator {
@Override
public void generateNoise(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) {
}
}

View file

@ -12,12 +12,8 @@ lobby:
yaw: 0.0
pitch: 0.0
# Генераторы (7 штук)
# Формат: "world,x,y,yaw,pitch"
generators: []
# Пример:
# - "world,100.5,64.0,200.5,0.0,0.0"
# - "world,150.5,64.0,250.5,90.0,0.0"
# Генераторы — настраиваются через /dbdadmin create generator
# и сохраняются в data/generators.yml
# Крюки (обычно 4-6)
hooks: []
@ -49,7 +45,10 @@ survivor-spawns: []
# ============================================
# Команды для настройки:
# /dbdsetup set generator - Установить генератор
# /dbdadmin create generator - Войти в режим выбора генератора (сломайте блок)
# /dbdadmin list - Список всех генераторов
# /dbdadmin remove <id> - Удалить генератор по ID
# /dbdadmin clear - Очистить все генераторы
# /dbdsetup set hook - Установить крюк
# /dbdsetup set pallet - Установить паллет
# /dbdsetup set chest - Установить сундук

View file

@ -1,23 +1,21 @@
name: Mdbd
version: '${version}'
main: dev.loki.mdbd
bootstrapper: dev.loki.mdbdBootstrap
loader: dev.loki.mdbdLoader
api-version: '1.20.6'
load: POSTWORLD
authors: [ loki ]
api-version: '1.21'
commands:
dbd:
description: "Главная команда DBD плагина"
usage: "/dbd <join|leave|create|forcestart|stop>"
usage: "/dbd <join|leave|create|forcestart|stop|reload>"
permission: mdbd.player
dbdsetup:
description: "Команды настройки карты и объектов"
usage: "/dbdsetup <wand|pos1|pos2|save|load|set|build|clear|lobby>"
permission: mdbd.admin
dbdadmin:
description: "Админ-команды для управления генераторами и объектами"
usage: "/dbdadmin <create|list|remove|clear>"
permission: mdbd.admin
permissions:
mdbd.admin: