From bff8cf24cd3d32ca74eeb5d996dd7c32723db3ae Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 1 Dec 2025 22:28:15 +0100 Subject: [PATCH] added AbstractEvent and basic Scoreboard functionality --- .../eu/mhsl/craftattack/spawn/core/Main.java | 5 +- .../event/appliances/deathrun/Deathrun.java | 37 ++++++- .../appliances/deathrun/DeathrunListener.java | 15 +++ .../eventController/AbstractEvent.java | 30 ++++++ .../appliances/eventController/Event.java | 25 +++-- .../eventController/EventController.java | 7 +- .../Scoreboard/EventScoreEntry.java | 6 ++ .../Scoreboard/EventScoreboardBuilder.java | 97 +++++++++++++++++++ .../commands/EventCommand.java | 7 +- 9 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/DeathrunListener.java create mode 100644 event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/AbstractEvent.java create mode 100644 event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreEntry.java create mode 100644 event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreboardBuilder.java diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/Main.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/Main.java index d7feeb3..a0ace2f 100644 --- a/core/src/main/java/eu/mhsl/craftattack/spawn/core/Main.java +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/Main.java @@ -10,6 +10,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.reflections.Reflections; import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -49,7 +50,7 @@ public final class Main extends JavaPlugin { Main.logger().info(String.format("Loaded %d repositories!", this.repositoryLoader.getRepositories().size())); Main.logger().info("Loading appliances..."); - this.appliances = this.findSubtypesOf(Appliance.class).stream() + this.appliances = new ArrayList<>(this.findSubtypesOf(Appliance.class).stream() .filter(applianceClass -> !disabledAppliances.contains(applianceClass.getSimpleName())) .filter(appliance -> { Appliance.Flags flags = appliance.getAnnotation(Appliance.Flags.class); @@ -63,7 +64,7 @@ public final class Main extends JavaPlugin { throw new RuntimeException(String.format("Failed to create instance of '%s'", applianceClass.getName()), e); } }) - .toList(); + .toList()); Main.logger().info(String.format("Loaded %d appliances!", this.appliances.size())); Main.logger().info("Initializing appliances..."); diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/Deathrun.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/Deathrun.java index 3278d8e..c9b8a50 100644 --- a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/Deathrun.java +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/Deathrun.java @@ -1,22 +1,36 @@ package eu.mhsl.craftattack.spawn.event.appliances.deathrun; +import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; -import eu.mhsl.craftattack.spawn.event.appliances.eventController.Event; +import eu.mhsl.craftattack.spawn.event.appliances.eventController.AbstractEvent; import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; +import net.kyori.adventure.util.Ticks; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; +import java.util.*; + @Appliance.Flags(autoload = false) -public class Deathrun extends Appliance implements Event { +public class Deathrun extends AbstractEvent { + private int scoreboardUpdateTaskId; + @Override public void initialize(@NotNull JavaPlugin plugin) { World world = Bukkit.getWorlds().getFirst(); world.getWorldBorder().setCenter(world.getSpawnLocation()); world.getWorldBorder().setSize(20); + + this.start(); + } + + @Override + public void onDisable() { + if(this.scoreboardUpdateTaskId != -1) Bukkit.getScheduler().cancelTask(this.scoreboardUpdateTaskId); } @Override @@ -26,6 +40,18 @@ public class Deathrun extends Appliance implements Event { World world = Bukkit.getWorlds().getFirst(); world.getWorldBorder().setSize(world.getWorldBorder().getMaxSize()); + + this.updateScoreboards(); + this.scoreboardUpdateTaskId = Bukkit.getScheduler().scheduleSyncRepeatingTask( + Main.instance(), + this::updateScoreboards, + Ticks.TICKS_PER_SECOND, + Ticks.TICKS_PER_SECOND + ); + } + + private void updateScoreboards() { + Bukkit.getOnlinePlayers().forEach(this::updateScoreboard); } @Override @@ -43,4 +69,11 @@ public class Deathrun extends Appliance implements Event { public String displayName() { return "Deathrun"; } + + @Override + protected @NotNull List listeners() { + return List.of( + new DeathrunListener() + ); + } } diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/DeathrunListener.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/DeathrunListener.java new file mode 100644 index 0000000..cd56f80 --- /dev/null +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/deathrun/DeathrunListener.java @@ -0,0 +1,15 @@ +package eu.mhsl.craftattack.spawn.event.appliances.deathrun; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerMoveEvent; + +class DeathrunListener extends ApplianceListener { + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + if(!event.hasChangedPosition()) return; + if(event.getTo().clone().subtract(event.getFrom()).x() == 0) return; + + this.getAppliance().updateScore(event.getPlayer()); + } +} diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/AbstractEvent.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/AbstractEvent.java new file mode 100644 index 0000000..5948a09 --- /dev/null +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/AbstractEvent.java @@ -0,0 +1,30 @@ +package eu.mhsl.craftattack.spawn.event.appliances.eventController; + +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import eu.mhsl.craftattack.spawn.event.appliances.eventController.Scoreboard.EventScoreEntry; +import eu.mhsl.craftattack.spawn.event.appliances.eventController.Scoreboard.EventScoreboardBuilder; +import org.bukkit.scoreboard.Scoreboard; + +import java.util.*; + +@Appliance.Flags(enabled = false, autoload = false) +public abstract class AbstractEvent extends Appliance implements Event { + private final Map playerScoreboards = new HashMap<>(); + private final List playerScores = new ArrayList<>(); + private final EventScoreboardBuilder scoreboardBuilder = new EventScoreboardBuilder(3, 2, 3); + + @Override + public Map getPlayerScoreboards() { + return this.playerScoreboards; + } + + @Override + public List getPlayerScores() { + return this.playerScores; + } + + @Override + public EventScoreboardBuilder getScoreboardBuilder() { + return this.scoreboardBuilder; + } +} diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Event.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Event.java index e05b25b..b3dbb96 100644 --- a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Event.java +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Event.java @@ -1,18 +1,31 @@ package eu.mhsl.craftattack.spawn.event.appliances.eventController; -import org.bukkit.Bukkit; +import eu.mhsl.craftattack.spawn.event.appliances.eventController.Scoreboard.EventScoreEntry; +import eu.mhsl.craftattack.spawn.event.appliances.eventController.Scoreboard.EventScoreboardBuilder; import org.bukkit.entity.Player; import org.bukkit.scoreboard.Scoreboard; -public interface Event { - Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard(); +import java.util.List; +import java.util.Map; +import java.util.UUID; +public interface Event { String displayName(); + Map getPlayerScoreboards(); + List getPlayerScores(); + EventScoreboardBuilder getScoreboardBuilder(); + void start(); void stop(); int getScore(Player p); - - public default void updateSidebar() { - + default void updateScore(Player p) { + System.out.println(this.getPlayerScores().removeIf(entry -> entry.playerUuid().equals(p.getUniqueId()))); + this.getPlayerScores().add(new EventScoreEntry(p.getUniqueId(), p.getName(), this.getScore(p))); + } + default void updateScoreboard(Player p) { + this.updateScore(p); + Scoreboard scoreboard = this.getScoreboardBuilder().buildFor(p, this); + this.getPlayerScoreboards().put(p.getUniqueId(), scoreboard); + p.setScoreboard(scoreboard); } } diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/EventController.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/EventController.java index 1c4986b..0673815 100644 --- a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/EventController.java +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/EventController.java @@ -19,13 +19,14 @@ public class EventController extends Appliance { } public List getEventAppliances() { - if(eventAppliances == null) throw new IllegalStateException("Event appliances were not initialized"); - return eventAppliances; + if(this.eventAppliances == null) throw new IllegalStateException("Event appliances were not initialized"); + return this.eventAppliances; } public void enableEvent(Appliance event) { - getEventAppliances().forEach(appliance -> appliance.disableSequence(Main.instance())); + this.getEventAppliances().forEach(appliance -> appliance.disableSequence(Main.instance())); Main.instance().restartAppliance(event.getClass()); +// Main.instance().getAppliance(event.getClass()) } public void disableEvent(Appliance event) { diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreEntry.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreEntry.java new file mode 100644 index 0000000..96fa63d --- /dev/null +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreEntry.java @@ -0,0 +1,6 @@ +package eu.mhsl.craftattack.spawn.event.appliances.eventController.Scoreboard; + +import java.util.UUID; + +public record EventScoreEntry(UUID playerUuid, String name, int score) { +} diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreboardBuilder.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreboardBuilder.java new file mode 100644 index 0000000..9ff6871 --- /dev/null +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/Scoreboard/EventScoreboardBuilder.java @@ -0,0 +1,97 @@ +package eu.mhsl.craftattack.spawn.event.appliances.eventController.Scoreboard; + +import eu.mhsl.craftattack.spawn.event.appliances.eventController.Event; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Scoreboard; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.IntStream; + +public class EventScoreboardBuilder { + private final int topPlaces; + private final int aroundPlaces; + private final int bottomPlaces; + + public EventScoreboardBuilder(int topPlaces, int aroundPlaces, int bottomPlaces) { + this.topPlaces = topPlaces; + this.aroundPlaces = aroundPlaces; + this.bottomPlaces = bottomPlaces; + } + + public Scoreboard buildFor(Player p, Event event) { + List scoreMap = event.getPlayerScores(); + Scoreboard newScoreboard = Bukkit.getScoreboardManager().getNewScoreboard(); + + Objective objective = newScoreboard.registerNewObjective( + "event", "dummy", + Component.text("Scoreboard %s".formatted(event.displayName()), NamedTextColor.GOLD) + ); + objective.setDisplaySlot(DisplaySlot.SIDEBAR); + + if(scoreMap.isEmpty()) return newScoreboard; + + scoreMap.sort(Comparator.comparingInt(EventScoreEntry::score).reversed()); + System.out.print(scoreMap); + + EventScoreEntry playerScoreEntry = scoreMap.stream() + .filter(entry -> entry.playerUuid().equals(p.getUniqueId())) + .findFirst() + .orElse(null); + if(playerScoreEntry == null) { + playerScoreEntry = new EventScoreEntry(p.getUniqueId(), p.getName(), event.getScore(p)); + scoreMap.add(playerScoreEntry); + } + + int playerIndex = scoreMap.indexOf(playerScoreEntry); + + IntStream topStream = IntStream.range(0, this.topPlaces); + IntStream aroundStream = IntStream.range(playerIndex - this.aroundPlaces, playerIndex + this.aroundPlaces); + IntStream bottomStream = IntStream.range(scoreMap.size() - this.bottomPlaces, scoreMap.size()); + + if(playerIndex <= this.topPlaces + this.aroundPlaces) { + aroundStream = IntStream.empty(); + topStream = IntStream.range(0, playerIndex + this.aroundPlaces); + } + if(playerIndex >= scoreMap.size() - this.bottomPlaces - this.aroundPlaces) { + aroundStream = IntStream.empty(); + bottomStream = IntStream.range(playerIndex - this.aroundPlaces, scoreMap.size()); + } + if(scoreMap.size() <= this.topPlaces + this.aroundPlaces * 2 + this.bottomPlaces + 2) { + topStream = IntStream.range(0, scoreMap.size()); + aroundStream = IntStream.empty(); + bottomStream = IntStream.empty(); + } + + IntStream indices = IntStream.concat( + IntStream.concat(topStream, aroundStream), + bottomStream + ).distinct().sorted(); + + indices.forEach(place -> { + EventScoreEntry entry = scoreMap.get(place); + String line = this.formattedLine(place, entry.name(), entry.score()); + objective.getScore(line).setScore(place); + }); + + return newScoreboard; + } + + private String formattedLine(int place, String name, int score) { + name = this.trimName(name); + return name + ": " + score; + } + + private String trimName(String name) { + if (name == null) return "Unknown"; + if (name.length() > 12) { + return name.substring(0, 12); + } + return name; + } +} diff --git a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/commands/EventCommand.java b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/commands/EventCommand.java index 7052233..a6610ac 100644 --- a/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/commands/EventCommand.java +++ b/event/src/main/java/eu/mhsl/craftattack/spawn/event/appliances/eventController/commands/EventCommand.java @@ -26,12 +26,12 @@ public class EventCommand extends ApplianceCommand { } case "start": { if(args.length == 1) throw new Error("Not enough arguments for start."); - this.getAppliance().enableEvent(findApplianceFromString(args[1])); + this.getAppliance().enableEvent(this.findApplianceFromString(args[1])); break; } case "stop": { if(args.length == 1) throw new Error("Not enough arguments for stop."); - this.getAppliance().disableEvent(findApplianceFromString(args[1])); + this.getAppliance().disableEvent(this.findApplianceFromString(args[1])); break; } default: throw new Error("No such option: '%s' !".formatted(args[0])); @@ -49,6 +49,9 @@ public class EventCommand extends ApplianceCommand { @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if(args.length == 1) return List.of("select", "start", "stop"); + if(args.length == 2) return this.getAppliance().getEventAppliances().stream() + .map(appliance -> appliance.getClass().getSimpleName().toLowerCase()) + .toList(); return null; } }