diff --git a/src/main/java/com/alttd/hunger_games/Main.java b/src/main/java/com/alttd/hunger_games/Main.java index b68c894..f830fe0 100644 --- a/src/main/java/com/alttd/hunger_games/Main.java +++ b/src/main/java/com/alttd/hunger_games/Main.java @@ -20,6 +20,7 @@ public final class Main extends JavaPlugin { private LootService lootService; private StatService statService; private BossBarService bossBarService; + private TabListService tabListService; @Override public void onEnable() { @@ -42,6 +43,8 @@ public final class Main extends JavaPlugin { bossBarService = BossBarService.createSingletonInstance(round, roundService); round.setBossBarService(bossBarService); roundService.setBossBarService(bossBarService); + + tabListService = new TabListService(round, roundService); } @Override @@ -50,13 +53,13 @@ public final class Main extends JavaPlugin { } private void registerCommands() { - BaseCommand command = new BaseCommand(this, roundService, playerService, round, statService); + BaseCommand command = new BaseCommand(this, roundService, playerService, round, statService, tabListService); } private void registerEvents() { PluginManager pluginManager = getServer().getPluginManager(); pluginManager.registerEvents(new PlayerDisconnectListener(playerService), this); - pluginManager.registerEvents(new PlayerJoinListener(playerService), this); + pluginManager.registerEvents(new PlayerJoinListener(playerService, tabListService), this); pluginManager.registerEvents(new PlayerDamageListener(roundService, playerService, statService), this); pluginManager.registerEvents(new ChestListener(roundService, lootService), this); pluginManager.registerEvents(new ItemSpawnListener(roundService), this); diff --git a/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java b/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java index 2b47341..eef0038 100644 --- a/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java +++ b/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java @@ -3,10 +3,7 @@ package com.alttd.hunger_games.commands; import com.alttd.hunger_games.Main; import com.alttd.hunger_games.commands.subcommands.*; import com.alttd.hunger_games.config.Messages; -import com.alttd.hunger_games.services.PlayerService; -import com.alttd.hunger_games.services.Round; -import com.alttd.hunger_games.services.RoundService; -import com.alttd.hunger_games.services.StatService; +import com.alttd.hunger_games.services.*; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; @@ -23,7 +20,7 @@ import java.util.stream.Collectors; public class BaseCommand implements CommandExecutor, TabExecutor { private final List subCommands; - public BaseCommand(Main main, RoundService roundService, PlayerService playerService, Round round, StatService statService) { + public BaseCommand(Main main, RoundService roundService, PlayerService playerService, Round round, StatService statService, TabListService tablistService) { PluginCommand command = main.getCommand("hungergames"); if (command == null) { subCommands = null; @@ -37,7 +34,7 @@ public class BaseCommand implements CommandExecutor, TabExecutor { subCommands = new ArrayList<>(List.of( new Reload(main), new RoundState(roundService), - new Register(playerService), + new Register(playerService, tablistService), new StartRound(round, roundService, playerService), new NextPhase(round), new EndRound(round), diff --git a/src/main/java/com/alttd/hunger_games/commands/subcommands/Register.java b/src/main/java/com/alttd/hunger_games/commands/subcommands/Register.java index a24439f..4847917 100644 --- a/src/main/java/com/alttd/hunger_games/commands/subcommands/Register.java +++ b/src/main/java/com/alttd/hunger_games/commands/subcommands/Register.java @@ -5,6 +5,8 @@ import com.alttd.hunger_games.commands.shared_parsers.OnlinePlayerParser; import com.alttd.hunger_games.config.Messages; import com.alttd.hunger_games.data_objects.PLAYER_STATE; import com.alttd.hunger_games.services.PlayerService; +import com.alttd.hunger_games.services.TabListService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; @@ -15,16 +17,14 @@ import javax.annotation.Nullable; import java.util.List; import java.util.Optional; +@RequiredArgsConstructor @Slf4j public class Register extends SubCommand { private final static OnlinePlayerParser ONLINE_PLAYER_PARSER = new OnlinePlayerParser(); private final static int PLAYER_ARG = 1; private final PlayerService playerService; - - public Register(PlayerService playerService) { - this.playerService = playerService; - } + private final TabListService tabListService; @Override public boolean onCommand(CommandSender commandSender, String[] args) { @@ -44,6 +44,7 @@ public class Register extends SubCommand { playerToRegister.sendRichMessage(Messages.REGISTER.FAILED_TO_REGISTER_YOU); } else { notifyRegisteredPlayer(playerToRegister, nullablePlayerState); + tabListService.updatePlayerColor(playerToRegister); } if (commandSender instanceof Player commandPlayer) { diff --git a/src/main/java/com/alttd/hunger_games/config/Config.java b/src/main/java/com/alttd/hunger_games/config/Config.java index 21ae5da..597d3dd 100644 --- a/src/main/java/com/alttd/hunger_games/config/Config.java +++ b/src/main/java/com/alttd/hunger_games/config/Config.java @@ -40,6 +40,7 @@ public class Config extends AbstractConfig { public static class ROUND { private static final String prefix = "round."; + public static String HG_WORLD = "world"; public static Duration COUNTDOWN = Duration.ofSeconds(10); public static Duration SAFE_PHASE = Duration.ofSeconds(30); public static Duration TIME_TO_FINALE = Duration.ofMinutes(5); @@ -54,6 +55,7 @@ public class Config extends AbstractConfig { @SuppressWarnings("unused") private static void load() { + HG_WORLD = config.getString(prefix, "hg-world", HG_WORLD); int countdownSeconds = config.getInt(prefix, "countdown-seconds", Math.toIntExact(COUNTDOWN.toSeconds())); COUNTDOWN = Duration.ofSeconds(countdownSeconds); int safePhaseSeconds = config.getInt(prefix, "safe-phase-seconds", Math.toIntExact(SAFE_PHASE.toSeconds())); diff --git a/src/main/java/com/alttd/hunger_games/config/Messages.java b/src/main/java/com/alttd/hunger_games/config/Messages.java index 6dac853..78392da 100644 --- a/src/main/java/com/alttd/hunger_games/config/Messages.java +++ b/src/main/java/com/alttd/hunger_games/config/Messages.java @@ -89,6 +89,7 @@ public class Messages extends AbstractConfig { public static String CHEST_EMPTY = "This chest is empty!"; public static String WINNER = " has won the Hunger Games!"; public static String PLAYER_DEATH = " has died! players remaining."; + public static String PLAYER_KILLED = " was killed by ! players remaining."; public static String BOSSBAR_COUNTDOWN = "Loot phase in "; public static String BOSSBAR_PVP_COUNTDOWN = "PVP starts in "; @@ -106,6 +107,7 @@ public class Messages extends AbstractConfig { CHEST_EMPTY = config.getString(prefix, "chest-empty", CHEST_EMPTY); WINNER = config.getString(prefix, "winner", WINNER); PLAYER_DEATH = config.getString(prefix, "player-death", PLAYER_DEATH); + PLAYER_KILLED = config.getString(prefix, "player-killed", PLAYER_KILLED); BOSSBAR_COUNTDOWN = config.getString(prefix, "bossbar-countdown", BOSSBAR_COUNTDOWN); BOSSBAR_PVP_COUNTDOWN = config.getString(prefix, "bossbar-pvp-countdown", BOSSBAR_PVP_COUNTDOWN); diff --git a/src/main/java/com/alttd/hunger_games/event_listeners/ItemSpawnListener.java b/src/main/java/com/alttd/hunger_games/event_listeners/ItemSpawnListener.java index 8af23b1..b9db2d3 100644 --- a/src/main/java/com/alttd/hunger_games/event_listeners/ItemSpawnListener.java +++ b/src/main/java/com/alttd/hunger_games/event_listeners/ItemSpawnListener.java @@ -4,22 +4,47 @@ import com.alttd.hunger_games.data_objects.ROUND_STATE; import com.alttd.hunger_games.services.RoundService; import lombok.RequiredArgsConstructor; import org.bukkit.entity.Item; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.player.PlayerDropItemEvent; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + @RequiredArgsConstructor public class ItemSpawnListener implements Listener { private final RoundService roundService; + private final Set deathDrops = new HashSet<>(); @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onItemSpawn(ItemSpawnEvent event) { + if (deathDrops.remove(event.getEntity().getUniqueId())) { + event.getEntity().setTicksLived(5400); // 300 seconds total - 5400 ticks = 30 seconds left + return; + } setDespawnDelay(event.getEntity()); } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerDeath(EntityDeathEvent event) { + if (!(event.getEntity() instanceof Player)) { + return; + } + ROUND_STATE roundState = roundService.getRoundState(); + if (roundState == ROUND_STATE.SAFE_PHASE || roundState == ROUND_STATE.KILL_PHASE || roundState == ROUND_STATE.FINALE) { + event.getDrops().forEach(itemStack -> { + Item item = event.getEntity().getWorld().dropItemNaturally(event.getEntity().getLocation(), itemStack); + deathDrops.add(item.getUniqueId()); + }); + } + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerDropItem(PlayerDropItemEvent event) { setDespawnDelay(event.getItemDrop()); diff --git a/src/main/java/com/alttd/hunger_games/event_listeners/PlayerDamageListener.java b/src/main/java/com/alttd/hunger_games/event_listeners/PlayerDamageListener.java index 1f1800f..d569300 100644 --- a/src/main/java/com/alttd/hunger_games/event_listeners/PlayerDamageListener.java +++ b/src/main/java/com/alttd/hunger_games/event_listeners/PlayerDamageListener.java @@ -45,8 +45,6 @@ public class PlayerDamageListener implements Listener { Player player = event.getPlayer(); event.setShowDeathMessages(false); event.deathMessage(null); - event.getDrops().clear(); - player.getInventory().clear(); playerService.handlePlayerDeath(player); statService.addDeath(player.getUniqueId()); diff --git a/src/main/java/com/alttd/hunger_games/event_listeners/PlayerJoinListener.java b/src/main/java/com/alttd/hunger_games/event_listeners/PlayerJoinListener.java index 9cd526c..15437d7 100644 --- a/src/main/java/com/alttd/hunger_games/event_listeners/PlayerJoinListener.java +++ b/src/main/java/com/alttd/hunger_games/event_listeners/PlayerJoinListener.java @@ -1,18 +1,33 @@ package com.alttd.hunger_games.event_listeners; import com.alttd.hunger_games.services.PlayerService; +import com.alttd.hunger_games.services.TabListService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandException; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +@Slf4j @RequiredArgsConstructor public class PlayerJoinListener implements Listener { private final PlayerService playerService; + private final TabListService tabListService; @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { - playerService.handleJoin(event.getPlayer()); + Player player = event.getPlayer(); + playerService.handleJoin(player); + tabListService.updatePlayerColor(player); + try { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), + String.format("lp user %s permission settemp cmi.kit.hunger-games true 7d", player.getName())); + } catch (CommandException e) { + log.error("Failed to set participant permission for {}", player.getName(), e); + } } } diff --git a/src/main/java/com/alttd/hunger_games/services/BossBarService.java b/src/main/java/com/alttd/hunger_games/services/BossBarService.java index fb47ed7..384bc30 100644 --- a/src/main/java/com/alttd/hunger_games/services/BossBarService.java +++ b/src/main/java/com/alttd/hunger_games/services/BossBarService.java @@ -66,6 +66,10 @@ public class BossBarService implements RoundListener { } private void updateBossBarColor(ROUND_STATE state) { + if (state == ROUND_STATE.ENDED || state == ROUND_STATE.PLAYER_REGISTRATION) { + hideAll(); + return; + } switch (state) { case COUNTDOWN -> bossBar.color(BossBar.Color.YELLOW); case SAFE_PHASE -> bossBar.color(BossBar.Color.GREEN); @@ -92,27 +96,20 @@ public class BossBarService implements RoundListener { } private void updateViewers() { - Set registeredPlayers = roundService.getPlayers(PLAYER_STATE.REGISTERED); - - // Add new viewers - registeredPlayers.stream() - .filter(viewers::add) - .map(Bukkit::getPlayer) - .filter(Objects::nonNull) - .forEach(player -> player.showBossBar(bossBar)); - - // Remove old viewers - Set toRemove = viewers.stream() - .filter(uuid -> !registeredPlayers.contains(uuid)) - .collect(Collectors.toSet()); - - for (UUID uuid : toRemove) { - viewers.remove(uuid); - Player player = Bukkit.getPlayer(uuid); - if (player != null) { - player.hideBossBar(bossBar); - } + if (roundService.getRoundState() == ROUND_STATE.ENDED || roundService.getRoundState() == ROUND_STATE.PLAYER_REGISTRATION) { + hideAll(); + return; } + Bukkit.getOnlinePlayers().forEach(player -> { + if (viewers.add(player.getUniqueId())) { + player.showBossBar(bossBar); + } + }); + + viewers.removeIf(uuid -> { + Player player = Bukkit.getPlayer(uuid); + return player == null || !player.isOnline(); + }); } private void hideAll() { diff --git a/src/main/java/com/alttd/hunger_games/services/PlayerService.java b/src/main/java/com/alttd/hunger_games/services/PlayerService.java index 8b71f05..a925ad4 100644 --- a/src/main/java/com/alttd/hunger_games/services/PlayerService.java +++ b/src/main/java/com/alttd/hunger_games/services/PlayerService.java @@ -91,6 +91,7 @@ public class PlayerService implements RoundListener { } public void handleDisconnect(Player player) { + player.playerListName(null); UUID uuid = player.getUniqueId(); roundService.setPlayerState(uuid, PLAYER_STATE.DISCONNECTED); } @@ -113,9 +114,17 @@ public class PlayerService implements RoundListener { roundService.setPlayerState(uuid, PLAYER_STATE.SPECTATING); int remaining = roundService.getPlayers(PLAYER_STATE.REGISTERED).size(); - Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.PLAYER_DEATH, - Placeholder.component("player", player.name()), - Placeholder.unparsed("remaining", String.valueOf(remaining)))); + Player killer = player.getKiller(); + if (killer != null) { + Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.PLAYER_KILLED, + Placeholder.component("victim", player.name()), + Placeholder.component("killer", killer.name()), + Placeholder.unparsed("remaining", String.valueOf(remaining)))); + } else { + Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.PLAYER_DEATH, + Placeholder.component("player", player.name()), + Placeholder.unparsed("remaining", String.valueOf(remaining)))); + } } public void handlePlayerRespawn(PlayerRespawnEvent event) { diff --git a/src/main/java/com/alttd/hunger_games/services/RoundService.java b/src/main/java/com/alttd/hunger_games/services/RoundService.java index 64dbac4..70a7ef8 100644 --- a/src/main/java/com/alttd/hunger_games/services/RoundService.java +++ b/src/main/java/com/alttd/hunger_games/services/RoundService.java @@ -15,6 +15,8 @@ import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; import org.bukkit.GameMode; +import org.bukkit.World; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; @@ -118,6 +120,9 @@ public class RoundService implements RoundListener { this.roundState = roundState; stopFreeze(); if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION) || roundState.equals(ROUND_STATE.ENDED)) { + if (roundState.equals(ROUND_STATE.ENDED)) { + cleanupItems(); + } clear(); lootService.clear(); } @@ -159,4 +164,13 @@ public class RoundService implements RoundListener { main.getServer().getPluginManager().registerEvents(playerMovementListener, main); } + private void cleanupItems() { + World world = Bukkit.getWorld(Config.ROUND.HG_WORLD); + if (world == null) { + log.error("Unable to cleanup items, world {} not found", Config.ROUND.HG_WORLD); + return; + } + world.getEntitiesByClass(Item.class).forEach(Item::remove); + } + } diff --git a/src/main/java/com/alttd/hunger_games/services/TabListService.java b/src/main/java/com/alttd/hunger_games/services/TabListService.java new file mode 100644 index 0000000..2893775 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/services/TabListService.java @@ -0,0 +1,46 @@ +package com.alttd.hunger_games.services; + +import com.alttd.hunger_games.data_objects.PLAYER_STATE; +import com.alttd.hunger_games.data_objects.ROUND_STATE; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public class TabListService implements RoundListener { + + private final RoundService roundService; + private ROUND_STATE roundState; + + public TabListService(Round round, RoundService roundService) { + this.roundService = roundService; + this.roundState = round.register(this); + } + + public void updatePlayerColor(Player player) { + if (!player.isOnline()) { + return; + } + if (roundState == null || roundState == ROUND_STATE.ENDED || roundState == ROUND_STATE.PLAYER_REGISTRATION) { + player.playerListName(null); + return; + } + + PLAYER_STATE state = roundService.getPlayerState(player.getUniqueId()).orElse(PLAYER_STATE.SPECTATING); + switch (state) { + case REGISTERED -> player.playerListName(player.name().color(NamedTextColor.GREEN)); + case SPECTATING, DISCONNECTED -> player.playerListName(player.name().color(NamedTextColor.RED)); + } + } + + public void updateAllPlayers() { + Bukkit.getOnlinePlayers().forEach(this::updatePlayerColor); + } + + @Override + public void stateChange(ROUND_STATE roundState) { + this.roundState = roundState; + updateAllPlayers(); + } +}