package com.bedwars; import org.bukkit.*; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Firework; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; import org.bukkit.event.entity.EntityDamageEvent; import java.util.*; public class GameManager { private final BedwarsPlugin plugin; private GameState state = GameState.LOBBY; private final Map playerTeams = new HashMap<>(); private final Map teamBeds = new HashMap<>(); private final Set spectators = new HashSet<>(); private final Set eliminatedTeams = new HashSet<>(); private final Set eliminatedPlayers = new HashSet<>(); // Stats private final Map kills = new HashMap<>(); private final Map bedsBroken = new HashMap<>(); // Combat Tracking private final Map lastAttacker = new HashMap<>(); private final Map lastAttackTime = new HashMap<>(); private final Map lastDamageType = new HashMap<>(); // Countdowns private boolean countingDown = false; private int countdownTime = 30; private BukkitTask countdownTask; private BukkitTask gameLoopTask; private final List respawnTasks = new ArrayList<>(); private int gameTimeTicks = 0; public GameManager(BedwarsPlugin plugin) { this.plugin = plugin; resetStats(); } public void init() { // Initialize bed status for (BedwarsTeam team : BedwarsTeam.values()) { teamBeds.put(team, true); } // Save pristine bed states from world plugin.getArenaManager().saveBedStates(); } public GameState getState() { return state; } public void setState(GameState state) { this.state = state; plugin.getScoreboardManager().updateAll(); } public boolean isCountingDown() { return countingDown; } public int getCountdownTime() { return countdownTime; } public BedwarsTeam getPlayerTeam(Player player) { return playerTeams.get(player.getUniqueId()); } public void setPlayerTeam(Player player, BedwarsTeam team) { playerTeams.put(player.getUniqueId(), team); updateTabName(player); plugin.getScoreboardManager().updateAll(); } public void updateTabName(Player player) { player.setPlayerListHeaderFooter("", ""); BedwarsTeam team = getPlayerTeam(player); if (team != null) { if (isSpectator(player)) { player.setPlayerListName("§7" + player.getName()); player.setDisplayName("§7" + player.getName() + "§r"); } else { player.setPlayerListName(team.getColor() + player.getName()); player.setDisplayName(team.getColor() + player.getName() + "§r"); } } else { player.setPlayerListName("§7" + player.getName()); player.setDisplayName("§7" + player.getName() + "§r"); } } public boolean hasBed(BedwarsTeam team) { return teamBeds.getOrDefault(team, false); } public void setBedBroken(BedwarsTeam team, boolean broken) { teamBeds.put(team, !broken); plugin.getScoreboardManager().updateAll(); } public boolean isSpectator(Player player) { return spectators.contains(player.getUniqueId()); } public int getKills(Player player) { return kills.getOrDefault(player.getUniqueId(), 0); } public void addKill(Player player) { kills.put(player.getUniqueId(), getKills(player) + 1); plugin.getScoreboardManager().updateScoreboard(player); } public int getBedsBroken(Player player) { return bedsBroken.getOrDefault(player.getUniqueId(), 0); } public void addBedBroken(Player player) { bedsBroken.put(player.getUniqueId(), getBedsBroken(player) + 1); plugin.getScoreboardManager().updateScoreboard(player); } public void resetStats() { playerTeams.clear(); spectators.clear(); eliminatedTeams.clear(); eliminatedPlayers.clear(); kills.clear(); bedsBroken.clear(); lastAttacker.clear(); lastAttackTime.clear(); lastDamageType.clear(); for (BedwarsTeam team : BedwarsTeam.values()) { teamBeds.put(team, false); // Disabled until game starts } if (plugin.getUpgradesManager() != null) { plugin.getUpgradesManager().resetTeamUpgrades(); } if (plugin.getShopManager() != null) { plugin.getShopManager().resetAllShopData(); } gameTimeTicks = 0; // Show all players to all players again for (Player p1 : Bukkit.getOnlinePlayers()) { for (Player p2 : Bukkit.getOnlinePlayers()) { p1.showPlayer(plugin, p2); } } } /** * Attempts to start the lobby countdown. */ public void checkStart() { if (state != GameState.LOBBY || countingDown) return; int minPlayers = plugin.getConfig().getInt("min-players", 2); if (Bukkit.getOnlinePlayers().size() >= minPlayers) { startCountdown(); } } private void startCountdown() { countingDown = true; countdownTime = plugin.getConfig().getInt("countdown-seconds", 30); countdownTask = new BukkitRunnable() { @Override public void run() { if (Bukkit.getOnlinePlayers().size() < plugin.getConfig().getInt("min-players", 2)) { cancelCountdown(); Bukkit.broadcastMessage("§cNot enough players! Countdown cancelled."); return; } if (countdownTime <= 0) { startGame(); cancel(); return; } if (countdownTime == 30 || countdownTime == 15 || countdownTime == 10 || countdownTime <= 5) { Bukkit.broadcastMessage("§eThe match will begin in §a" + countdownTime + " §eseconds!"); for (Player p : Bukkit.getOnlinePlayers()) { p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f); } } countdownTime--; plugin.getScoreboardManager().updateAll(); } }.runTaskTimer(plugin, 0L, 20L); } public void cancelCountdown() { countingDown = false; if (countdownTask != null) { countdownTask.cancel(); } plugin.getScoreboardManager().updateAll(); } /** * Core Game Start logic. */ public void startGame() { setState(GameState.PLAYING); countingDown = false; // Remove waiting area glass cage plugin.getArenaManager().removeWaitingLobby(); // Purge residual mobs from world region files plugin.getArenaManager().clearAllMobs(); // 1. Setup active teams in the game List activeTeams = new ArrayList<>(); ConfigurationSection teamsSec = plugin.getConfig().getConfigurationSection("locations.teams"); if (teamsSec != null) { for (String key : teamsSec.getKeys(false)) { if (teamsSec.getBoolean(key + ".enabled", false)) { try { BedwarsTeam t = BedwarsTeam.valueOf(key.toUpperCase()); activeTeams.add(t); teamBeds.put(t, true); // Bed active! } catch (Exception ignored) {} } } } if (activeTeams.isEmpty()) { Bukkit.broadcastMessage("§cError: No teams have been configured in /bw setup! Force ending game."); endGame(null); return; } // 2. Distribute players into teams List players = new ArrayList<>(Bukkit.getOnlinePlayers()); Collections.shuffle(players); int teamIndex = 0; for (Player player : players) { BedwarsTeam team = activeTeams.get(teamIndex); setPlayerTeam(player, team); teamIndex = (teamIndex + 1) % activeTeams.size(); // Set up player player.setGameMode(GameMode.SURVIVAL); player.setHealth(20.0); player.setFoodLevel(20); player.setSaturation(20.0f); player.getInventory().clear(); player.getEnderChest().clear(); // Clear potion effects for (org.bukkit.potion.PotionEffect effect : player.getActivePotionEffects()) { player.removePotionEffect(effect.getType()); } // Hide spectators from this player hideAllSpectatorsFrom(player); // Apply starting gear giveStartingGear(player); // Teleport to Team Spawn teleportToTeamSpawn(player, team); // Setup tab and display name colors updateTabName(player); player.sendMessage("§eMatch started! You are in team: " + team.getColorizedName()); player.playSound(player.getLocation(), Sound.ENTITY_ENDER_DRAGON_GROWL, 0.8f, 1.0f); } // Mark empty teams as eliminated immediately so they don't block victory for (BedwarsTeam team : activeTeams) { int teamSize = 0; for (Player p : Bukkit.getOnlinePlayers()) { if (getPlayerTeam(p) == team) { teamSize++; } } if (teamSize == 0) { teamBeds.put(team, false); eliminatedTeams.add(team); } } // 3. Spawn Shop Keepers & Upgrades Shop Keepers spawnNPCs(activeTeams); // Place enderchests for active teams asynchronously to prevent synchronous chunk loading locks String worldName = plugin.getConfig().getString("world-name", "bedwars"); World world = Bukkit.getWorld(worldName); if (world != null) { for (BedwarsTeam team : activeTeams) { String teamName = team.getName().toLowerCase(); String path = "locations.teams." + teamName + ".enderchest"; if (plugin.getConfig().contains(path)) { final double x = plugin.getConfig().getDouble(path + ".x"); final double y = plugin.getConfig().getDouble(path + ".y"); final double z = plugin.getConfig().getDouble(path + ".z"); world.getChunkAtAsync((int) x >> 4, (int) z >> 4).thenAccept(chunk -> { chunk.getWorld().getBlockAt((int) x, (int) y, (int) z).setType(Material.ENDER_CHEST); }); } } } // 4. Load generators and start game loop task plugin.getGeneratorManager().loadGenerators(); startGameLoop(); Bukkit.broadcastMessage("§a§lThe game has begun! Protect your bed!"); checkWinner(); } private void giveStartingGear(Player player) { player.getInventory().setItem(0, new ItemStack(Material.WOODEN_SWORD)); // Give leather armor colored with team dye BedwarsTeam team = getPlayerTeam(player); if (team != null) { player.getInventory().setHelmet(createColoredArmor(Material.LEATHER_HELMET, team.getColor())); player.getInventory().setChestplate(createColoredArmor(Material.LEATHER_CHESTPLATE, team.getColor())); // Retrieve and equip permanent bought armor tier, falling back to team colored leather int tier = plugin.getShopManager().getArmorTier(player); if (tier > 0) { plugin.getShopManager().equipArmorTier(player, tier); } else { player.getInventory().setLeggings(createColoredArmor(Material.LEATHER_LEGGINGS, team.getColor())); player.getInventory().setBoots(createColoredArmor(Material.LEATHER_BOOTS, team.getColor())); } // Retrieve and give permanent tools (Shears, Pickaxe, Axe) plugin.getShopManager().giveTools(player); } } private ItemStack createColoredArmor(Material material, ChatColor chatColor) { ItemStack item = new ItemStack(material); org.bukkit.inventory.meta.LeatherArmorMeta meta = (org.bukkit.inventory.meta.LeatherArmorMeta) item.getItemMeta(); if (meta != null) { Color color = Color.WHITE; if (chatColor == ChatColor.RED) color = Color.RED; else if (chatColor == ChatColor.BLUE) color = Color.BLUE; else if (chatColor == ChatColor.DARK_PURPLE) color = Color.PURPLE; else if (chatColor == ChatColor.YELLOW) color = Color.YELLOW; meta.setColor(color); meta.setUnbreakable(true); item.setItemMeta(meta); } return item; } private void teleportToTeamSpawn(Player player, BedwarsTeam team) { String colorName = team.getName().toLowerCase(); String worldName = plugin.getConfig().getString("world-name", "bedwars"); World w = Bukkit.getWorld(worldName); if (w != null && plugin.getConfig().contains("locations.teams." + colorName + ".spawn")) { double x = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.x"); double y = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.y"); double z = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.z"); float yaw = (float) plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.yaw"); float pitch = (float) plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.pitch"); player.teleport(new Location(w, x, y, z, yaw, pitch)); } else { // Fallback player.teleport(w != null ? w.getSpawnLocation() : player.getWorld().getSpawnLocation()); } } private void spawnNPCs(List activeTeams) { String worldName = plugin.getConfig().getString("world-name", "bedwars"); World world = Bukkit.getWorld(worldName); if (world == null) return; // Clear existing villager entities in the bedwars world world.getEntitiesByClass(org.bukkit.entity.Villager.class).forEach(org.bukkit.entity.Entity::remove); for (BedwarsTeam team : activeTeams) { String path = "locations.teams." + team.getName().toLowerCase(); // 1. Spawn Item Shop Villager if (plugin.getConfig().contains(path + ".shop")) { double x = plugin.getConfig().getDouble(path + ".shop.x"); double y = plugin.getConfig().getDouble(path + ".shop.y"); double z = plugin.getConfig().getDouble(path + ".shop.z"); float yaw = (float) plugin.getConfig().getDouble(path + ".shop.yaw"); float pitch = (float) plugin.getConfig().getDouble(path + ".shop.pitch"); org.bukkit.entity.Villager villager = world.spawn(new Location(world, x, y, z, yaw, pitch), org.bukkit.entity.Villager.class); villager.setCustomName("§a§lITEM SHOP\n§7Right Click"); villager.setCustomNameVisible(true); villager.setProfession(org.bukkit.entity.Villager.Profession.ARMORER); villager.setAI(false); villager.setInvulnerable(true); villager.setSilent(true); } // 2. Spawn Upgrades Shop Villager if (plugin.getConfig().contains(path + ".upgrades")) { double x = plugin.getConfig().getDouble(path + ".upgrades.x"); double y = plugin.getConfig().getDouble(path + ".upgrades.y"); double z = plugin.getConfig().getDouble(path + ".upgrades.z"); float yaw = (float) plugin.getConfig().getDouble(path + ".upgrades.yaw"); float pitch = (float) plugin.getConfig().getDouble(path + ".upgrades.pitch"); org.bukkit.entity.Villager villager = world.spawn(new Location(world, x, y, z, yaw, pitch), org.bukkit.entity.Villager.class); villager.setCustomName("§b§lTEAM UPGRADES\n§7Right Click"); villager.setCustomNameVisible(true); villager.setProfession(org.bukkit.entity.Villager.Profession.WEAPONSMITH); villager.setAI(false); villager.setInvulnerable(true); villager.setSilent(true); } } } private void startGameLoop() { gameTimeTicks = 0; gameLoopTask = new BukkitRunnable() { @Override public void run() { if (state != GameState.PLAYING) { cancel(); return; } // Tick generators plugin.getGeneratorManager().tick(); // Increment ticks and check game duration (20 minutes = 24000 ticks) gameTimeTicks++; // Custom regeneration every 5 seconds (100 ticks) if (gameTimeTicks % 100 == 0) { for (Player player : Bukkit.getOnlinePlayers()) { if (!isSpectator(player) && !eliminatedPlayers.contains(player.getUniqueId())) { if (!player.isDead() && player.getHealth() > 0) { double maxHealth = player.getMaxHealth(); double currentHealth = player.getHealth(); if (currentHealth < maxHealth) { player.setHealth(Math.min(maxHealth, currentHealth + 1.0)); } } } } } int secondsLeft = (24000 - gameTimeTicks) / 20; if (gameTimeTicks < 24000 && gameTimeTicks % 20 == 0) { // This runs once every second if (secondsLeft == 300) { // 5 minutes Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a5 minutes§e!"); } else if (secondsLeft == 60) { // 1 minute Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a1 minute§e!"); } else if (secondsLeft == 30) { Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a30 seconds§e!"); } else if (secondsLeft == 10) { Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a10 seconds§e!"); } else if (secondsLeft > 0 && secondsLeft <= 5) { Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §c" + secondsLeft + " §eseconds!"); for (Player p : Bukkit.getOnlinePlayers()) { p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f); } } } if (gameTimeTicks >= 24000) { Bukkit.broadcastMessage("§c§lTIME UP! §eThe game has ended in a draw."); endGame(null); cancel(); } } }.runTaskTimer(plugin, 0L, 1L); } /** * Handles a player's death, respawn loop, or final elimination. */ public void handlePlayerDeath(Player player, Player killer) { BedwarsTeam team = getPlayerTeam(player); if (team == null) return; // Check if player died in the void boolean inVoid = player.getLocation().getY() <= -20.0 || (player.getLastDamageCause() != null && player.getLastDamageCause().getCause() == EntityDamageEvent.DamageCause.VOID); // Handle tool levels and shears on death plugin.getShopManager().handlePlayerDeath(player); // Drop player resources or transfer to killer if in void if (inVoid && killer != null) { giveResourcesToKiller(player, killer); } else { dropPlayerResources(player); } // Determine death message String deathMsg = getDeathMessage(player, killer, inVoid); boolean isFinal = !hasBed(team); String finalSuffix = isFinal ? " §c§lFINAL KILL!" : ""; Bukkit.broadcastMessage("§7" + deathMsg + finalSuffix); // Clear combat tracking clearCombatTag(player); // Record stats if (killer != null) { addKill(killer); killer.sendMessage("§eYou killed " + team.getColor() + player.getName() + "!"); killer.playSound(killer.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.4f); } // Check if player has bed if (hasBed(team)) { // Can respawn! startRespawnSequence(player, team); } else { // Eliminated! eliminatePlayer(player); checkWinner(); } } public void handlePlayerQuit(Player player) { if (state != GameState.PLAYING) return; if (isSpectator(player) || eliminatedPlayers.contains(player.getUniqueId())) return; BedwarsTeam team = getPlayerTeam(player); if (team == null) return; // Eliminate player eliminatedPlayers.add(player.getUniqueId()); spectators.add(player.getUniqueId()); // Broadcast quit final kill/elimination Bukkit.broadcastMessage(team.getColor() + player.getName() + " §c§lquit and was eliminated!"); // Drop player resources at their last location dropPlayerResources(player); // Check if team is eliminated and check winner checkWinner(); } public String getFormattedName(Player player) { BedwarsTeam team = getPlayerTeam(player); if (team != null) { return team.getColor() + player.getName() + "§7"; } return "§7" + player.getName(); } private String getDeathMessage(Player player, Player killer, boolean inVoid) { String victimName = getFormattedName(player); if (killer != null) { String killerName = getFormattedName(killer); LastDamageType type = lastDamageType.getOrDefault(player.getUniqueId(), LastDamageType.OTHER); if (inVoid) { switch (type) { case FIREBALL: return victimName + " was fireballed into the void by " + killerName + "."; case TNT: return victimName + " was blown into the void by " + killerName + "."; case PROJECTILE: return victimName + " was shot into the void by " + killerName + "."; case MELEE: default: return victimName + " was knocked into the void by " + killerName + "."; } } else { switch (type) { case FIREBALL: return victimName + " was fireballed by " + killerName + "."; case TNT: return victimName + " was blown up by " + killerName + "."; case PROJECTILE: return victimName + " was shot by " + killerName + "."; case MELEE: default: return victimName + " was slain by " + killerName + "."; } } } else { if (inVoid) { return victimName + " fell into the void."; } else { EntityDamageEvent lastDamage = player.getLastDamageCause(); if (lastDamage != null) { EntityDamageEvent.DamageCause cause = lastDamage.getCause(); if (cause == EntityDamageEvent.DamageCause.FALL) { return victimName + " fell to their death."; } else if (cause == EntityDamageEvent.DamageCause.BLOCK_EXPLOSION || cause == EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) { return victimName + " was blown up."; } } return victimName + " died."; } } } private void dropPlayerResources(Player player) { Location loc = player.getLocation(); for (ItemStack item : player.getInventory().getContents()) { if (item != null) { Material type = item.getType(); if (type == Material.IRON_INGOT || type == Material.GOLD_INGOT || type == Material.DIAMOND || type == Material.EMERALD) { loc.getWorld().dropItemNaturally(loc, item.clone()); } } } player.getInventory().clear(); } private void giveResourcesToKiller(Player player, Player killer) { for (ItemStack item : player.getInventory().getContents()) { if (item != null) { Material type = item.getType(); if (type == Material.IRON_INGOT || type == Material.GOLD_INGOT || type == Material.DIAMOND || type == Material.EMERALD) { // Give directly to killer's inventory HashMap remaining = killer.getInventory().addItem(item.clone()); if (!remaining.isEmpty()) { // Drop any items that don't fit at killer's feet for (ItemStack overflow : remaining.values()) { killer.getWorld().dropItemNaturally(killer.getLocation(), overflow); } } // Send informative message to killer killer.sendMessage("§aReceived " + item.getAmount() + "x " + formatMaterialName(type) + " §afrom " + player.getName() + "'s void death!"); } } } player.getInventory().clear(); } private String formatMaterialName(Material material) { switch (material) { case IRON_INGOT: return "§fIron"; case GOLD_INGOT: return "§eGold"; case DIAMOND: return "§bDiamond"; case EMERALD: return "§aEmerald"; default: return material.name(); } } public void setLastAttacker(Player victim, Player attacker, LastDamageType type) { lastAttacker.put(victim.getUniqueId(), attacker.getUniqueId()); lastAttackTime.put(victim.getUniqueId(), System.currentTimeMillis()); lastDamageType.put(victim.getUniqueId(), type); } public Player getLastAttacker(Player player) { UUID victimId = player.getUniqueId(); if (lastAttacker.containsKey(victimId)) { long time = lastAttackTime.getOrDefault(victimId, 0L); if (System.currentTimeMillis() - time <= 15000) { // 15 seconds combat window UUID attackerId = lastAttacker.get(victimId); return Bukkit.getPlayer(attackerId); } } return null; } public void clearCombatTag(Player player) { lastAttacker.remove(player.getUniqueId()); lastAttackTime.remove(player.getUniqueId()); lastDamageType.remove(player.getUniqueId()); } private void startRespawnSequence(Player player, BedwarsTeam team) { spectators.add(player.getUniqueId()); player.setGameMode(GameMode.SPECTATOR); player.teleport(player.getLocation().clone().add(0, 5, 0)); updateTabName(player); BukkitTask task = new BukkitRunnable() { int time = 5; @Override public void run() { if (state != GameState.PLAYING || !player.isOnline()) { cancel(); return; } if (time <= 0) { respawnPlayer(player, team); cancel(); return; } player.sendTitle("§cYOU DIED!", "§eRespawning in §a" + time + "s", 0, 22, 0); player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.8f, 1.2f); time--; } }.runTaskTimer(plugin, 0L, 20L); respawnTasks.add(task); } private void respawnPlayer(Player player, BedwarsTeam team) { spectators.remove(player.getUniqueId()); player.setGameMode(GameMode.SURVIVAL); player.setHealth(20.0); player.setFoodLevel(20); player.setSaturation(20.0f); player.getInventory().clear(); // Clear potion effects for (org.bukkit.potion.PotionEffect effect : player.getActivePotionEffects()) { player.removePotionEffect(effect.getType()); } // Hide all active spectators from this player hideAllSpectatorsFrom(player); // Gear giveStartingGear(player); // Apply passive team upgrades (Haste, Sharpness, Protection) plugin.getUpgradesManager().applyTeamUpgrades(player); teleportToTeamSpawn(player, team); updateTabName(player); player.sendTitle("§a§lRESPAWNED!", "§fGo get them!", 5, 20, 5); player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.8f, 1.5f); } private void eliminatePlayer(Player player) { spectators.add(player.getUniqueId()); eliminatedPlayers.add(player.getUniqueId()); player.setGameMode(GameMode.CREATIVE); player.getInventory().clear(); giveSpectatorItems(player); hideSpectator(player); updateTabName(player); player.sendTitle("§c§lELIMINATED!", "§7You have no respawns left!", 10, 40, 10); player.playSound(player.getLocation(), Sound.ENTITY_LIGHTNING_BOLT_THUNDER, 0.8f, 0.8f); } public void addSpectator(Player player) { spectators.add(player.getUniqueId()); player.setGameMode(GameMode.CREATIVE); player.getInventory().clear(); giveSpectatorItems(player); hideSpectator(player); updateTabName(player); plugin.getScoreboardManager().updateAll(); } public void giveSpectatorItems(Player player) { player.getInventory().clear(); ItemStack compass = new ItemStack(Material.COMPASS); org.bukkit.inventory.meta.ItemMeta m1 = compass.getItemMeta(); if (m1 != null) { m1.setDisplayName("§a§lTeleporter §7(Right Click)"); compass.setItemMeta(m1); } player.getInventory().setItem(0, compass); ItemStack bed = new ItemStack(Material.RED_BED); org.bukkit.inventory.meta.ItemMeta m2 = bed.getItemMeta(); if (m2 != null) { m2.setDisplayName("§c§lReturn to Lobby §7(Right Click)"); bed.setItemMeta(m2); } player.getInventory().setItem(8, bed); } public void hideSpectator(Player player) { for (Player p : Bukkit.getOnlinePlayers()) { if (!isSpectator(p)) { p.hidePlayer(plugin, player); } } } public void hideAllSpectatorsFrom(Player alivePlayer) { for (UUID specId : spectators) { Player spec = Bukkit.getPlayer(specId); if (spec != null && spec.isOnline()) { alivePlayer.hidePlayer(plugin, spec); } } } public boolean isTeamEnabled(BedwarsTeam team) { return plugin.getConfig().getBoolean("locations.teams." + team.getName().toLowerCase() + ".enabled", false); } public int getAliveTeamCount(BedwarsTeam team) { int alive = 0; for (Player p : Bukkit.getOnlinePlayers()) { if (getPlayerTeam(p) == team && !eliminatedPlayers.contains(p.getUniqueId())) { alive++; } } return alive; } /** * Checks if only one team is left alive. */ public void checkWinner() { if (state != GameState.PLAYING) return; List activeTeams = new ArrayList<>(); ConfigurationSection teamsSec = plugin.getConfig().getConfigurationSection("locations.teams"); if (teamsSec != null) { for (String key : teamsSec.getKeys(false)) { if (teamsSec.getBoolean(key + ".enabled", false)) { activeTeams.add(BedwarsTeam.valueOf(key.toUpperCase())); } } } // Check for team eliminations (if getAliveTeamCount(team) == 0, even if there is a bed, the team is eliminated!) for (BedwarsTeam team : activeTeams) { if (!eliminatedTeams.contains(team)) { if (getAliveTeamCount(team) == 0) { eliminatedTeams.add(team); teamBeds.put(team, false); // No bed active if team has no alive players! Bukkit.broadcastMessage("§r"); Bukkit.broadcastMessage("§d§lTEAM ELIMINATED! §fTeam " + team.getColor() + team.name() + " §fhas been §c§lELIMINATED§f!"); Bukkit.broadcastMessage("§r"); } } } BedwarsTeam winningTeam = null; int activeTeamCount = 0; for (BedwarsTeam team : activeTeams) { // A team is active if they have at least one alive player if (getAliveTeamCount(team) > 0) { activeTeamCount++; winningTeam = team; } } // If there is only one player online, do not end the game so they can test features alone! if (Bukkit.getOnlinePlayers().size() > 1) { if (activeTeamCount <= 1) { endGame(winningTeam); } } } private BedwarsTeam winnerTeam = null; public BedwarsTeam getWinnerTeam() { return winnerTeam; } /** * Game Over, show winner, celebrate, redirect to proxy lobby server, reset world. */ public void endGame(BedwarsTeam winner) { setState(GameState.ENDING); this.winnerTeam = winner; if (winner != null) { Bukkit.broadcastMessage(" §b§lVICTORY!"); Bukkit.broadcastMessage(" " + winner.getColorizedName() + " §fhas won the match!"); for (Player p : Bukkit.getOnlinePlayers()) { p.sendTitle("§6§lVICTORY!", winner.getColorizedName() + " §fhas won!", 10, 80, 10); p.playSound(p.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 0.8f, 1.0f); } // Spawn fireworks String colorName = winner.getName().toLowerCase(); String worldName = plugin.getConfig().getString("world-name", "bedwars"); World w = Bukkit.getWorld(worldName); if (w != null && plugin.getConfig().contains("locations.teams." + colorName + ".spawn")) { double x = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.x"); double y = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.y"); double z = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.z"); Location spawnLoc = new Location(w, x, y + 2, z); new BukkitRunnable() { int fireworkCount = 10; @Override public void run() { if (fireworkCount <= 0 || state != GameState.ENDING) { cancel(); return; } Firework fw = spawnLoc.getWorld().spawn(spawnLoc.clone().add(Math.random()*4-2, 0, Math.random()*4-2), Firework.class); FireworkMeta meta = fw.getFireworkMeta(); meta.addEffect(FireworkEffect.builder() .withColor(winner.getColor() == ChatColor.RED ? Color.RED : winner.getColor() == ChatColor.BLUE ? Color.BLUE : winner.getColor() == ChatColor.DARK_PURPLE ? Color.PURPLE : Color.YELLOW) .with(FireworkEffect.Type.BALL_LARGE) .flicker(true) .trail(true) .build()); meta.setPower(1); fw.setFireworkMeta(meta); fireworkCount--; } }.runTaskTimer(plugin, 0L, 10L); } } else { Bukkit.broadcastMessage("§cThe game ended in a draw!"); } // Start 10 seconds ending countdown before unloading and resetting world new BukkitRunnable() { int timeLeft = 10; @Override public void run() { if (timeLeft <= 0) { // Kick all players to trigger proxy redirection for (Player p : Bukkit.getOnlinePlayers()) { p.kickPlayer("§a§lGAME OVER!\n\n§fThe game has ended.\n§eWinner: " + (winner != null ? winner.getColorizedName() : "§cDraw") + "\n\n§bThank you for playing!"); } // Schedule world reset shortly after kicking so connection is fully released Bukkit.getScheduler().runTaskLater(plugin, () -> { // Cleanup generators and NPCs plugin.getGeneratorManager().cleanup(); // Reset the world map plugin.getArenaManager().resetWorld(); // Cancel all tasks for (BukkitTask task : respawnTasks) { task.cancel(); } respawnTasks.clear(); if (gameLoopTask != null) { gameLoopTask.cancel(); } // Reinitialize variables resetStats(); winnerTeam = null; eliminatedTeams.clear(); setState(GameState.LOBBY); // Check if players are already inside waiting lobby (in case they joined during reset) checkStart(); }, 40L); cancel(); return; } if (timeLeft <= 5) { Bukkit.broadcastMessage("§eGame closing and resetting in §a" + timeLeft + " §eseconds..."); for (Player p : Bukkit.getOnlinePlayers()) { p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.2f); } } timeLeft--; } }.runTaskTimer(plugin, 0L, 20L); } /** * Sends players back to the configured lobby proxy server. */ public void sendToLobby(Player player) { String serverName = plugin.getConfig().getString("lobby-server", "lobby"); player.sendMessage("§b§lNEXORIA §fsending you back to the lobby..."); try { // 1. Send standard BungeeCord packets com.google.common.io.ByteArrayDataOutput bungeeOut = com.google.common.io.ByteStreams.newDataOutput(); bungeeOut.writeUTF("Connect"); bungeeOut.writeUTF(serverName); player.sendPluginMessage(plugin, "BungeeCord", bungeeOut.toByteArray()); player.sendPluginMessage(plugin, "bungeecord:main", bungeeOut.toByteArray()); // 2. Send custom nexoria direct channel packet com.google.common.io.ByteArrayDataOutput nexoriaOut = com.google.common.io.ByteStreams.newDataOutput(); nexoriaOut.writeUTF("Connect"); nexoriaOut.writeUTF(player.getName()); nexoriaOut.writeUTF(serverName); player.sendPluginMessage(plugin, "nexoria:main", nexoriaOut.toByteArray()); } catch (Exception e) { player.sendMessage("§cFailed to redirect to proxy lobby: " + e.getMessage()); } } public void resetGameImmediately() { // Cleanup generators and NPCs plugin.getGeneratorManager().cleanup(); // Reset the world map plugin.getArenaManager().resetWorld(); // Cancel all tasks for (BukkitTask task : respawnTasks) { task.cancel(); } respawnTasks.clear(); if (gameLoopTask != null) { gameLoopTask.cancel(); } // Reinitialize variables resetStats(); winnerTeam = null; eliminatedTeams.clear(); setState(GameState.LOBBY); countingDown = false; if (countdownTask != null) { countdownTask.cancel(); } } }