diff --git a/SpigotCore/SpigotCore_21/build.gradle.kts b/SpigotCore/SpigotCore_21/build.gradle.kts new file mode 100644 index 00000000..b702534b --- /dev/null +++ b/SpigotCore/SpigotCore_21/build.gradle.kts @@ -0,0 +1,33 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +plugins { + steamwar.java +} + +dependencies { + compileOnly(project(":SpigotCore:SpigotCore_Main", "default")) + + compileOnly(libs.paperapi21) { + attributes { + // Very Hacky, but it works + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21) + } + } +} diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/scoreboard/SWScoreboard21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/scoreboard/SWScoreboard21.java new file mode 100644 index 00000000..0834e166 --- /dev/null +++ b/SpigotCore/SpigotCore_21/src/de/steamwar/scoreboard/SWScoreboard21.java @@ -0,0 +1,74 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.scoreboard; + +import de.steamwar.core.Core; +import net.kyori.adventure.text.Component; +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.HashMap; +import java.util.Map; + +public class SWScoreboard21 implements SWScoreboard.ISWScoreboard { + + private static final HashMap playerBoards = new HashMap<>(); + private static final String SIDEBAR = "sw-sidebar"; + + static { + Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> { + for(Map.Entry scoreboard : playerBoards.entrySet()) { + render(scoreboard.getKey(), scoreboard.getValue()); + } + }, 5, 10); + } + + private static void render(Player player, ScoreboardCallback callback) { + if (player.getScoreboard().getObjective(SIDEBAR) != null) { + player.getScoreboard().getObjective(SIDEBAR).unregister(); + } + + Objective objective = player.getScoreboard().registerNewObjective(SIDEBAR, "dummy", Component.text(callback.getTitle())); + objective.setAutoUpdateDisplay(true); + objective.setDisplaySlot(DisplaySlot.SIDEBAR); + + callback.getData().forEach((text, score) -> objective.getScore(text).setScore(score)); + } + + @Override + public boolean createScoreboard(Player player, ScoreboardCallback callback) { + Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard(); + player.setScoreboard(scoreboard); + + render(player, callback); + + playerBoards.put(player, callback); + return true; + } + + @Override + public void removeScoreboard(Player player) { + player.getScoreboard().getObjective(SIDEBAR).unregister(); + playerBoards.remove(player); + } +} diff --git a/SpigotCore/SpigotCore_8/src/de/steamwar/scoreboard/SWScoreboard8.java b/SpigotCore/SpigotCore_8/src/de/steamwar/scoreboard/SWScoreboard8.java new file mode 100644 index 00000000..3af0fc93 --- /dev/null +++ b/SpigotCore/SpigotCore_8/src/de/steamwar/scoreboard/SWScoreboard8.java @@ -0,0 +1,115 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.scoreboard; + +import com.comphenix.tinyprotocol.Reflection; +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.core.Core; +import de.steamwar.core.FlatteningWrapper; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class SWScoreboard8 implements SWScoreboard.ISWScoreboard { + private static final Reflection.FieldAccessor scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, String.class, 0); + private static final Reflection.FieldAccessor scoreboardAction = Reflection.getField(FlatteningWrapper.scoreboardObjective, int.class, Core.getVersion() > 15 ? 3 : 0); + private static final Class scoreboardDisplayEnum = Reflection.getClass("{nms.world.scores.criteria}.IScoreboardCriteria$EnumScoreboardHealthDisplay"); + private static final Reflection.FieldAccessor scoreboardDisplayType = Reflection.getField(FlatteningWrapper.scoreboardObjective, scoreboardDisplayEnum, 0); + private static final Object displayTypeIntegers = scoreboardDisplayEnum.getEnumConstants()[0]; + + private static final Reflection.FieldAccessor scoreName = Reflection.getField(FlatteningWrapper.scoreboardScore, String.class, 0); + private static final Reflection.FieldAccessor scoreScoreboardName = Reflection.getField(FlatteningWrapper.scoreboardScore, String.class, 1); + private static final Reflection.FieldAccessor scoreValue = Reflection.getField(FlatteningWrapper.scoreboardScore, int.class, 0); + + private static final HashMap playerBoards = new HashMap<>(); //Object -> Scoreboard | Alle Versionen in der Map! + private static int toggle = 0; // Scoreboard 0 updates while scoreboard 1 is presenting. toggle marks the current active scoreboard + + private static final String SIDEBAR = "Sidebar"; + private static final Object[] DELETE_SCOREBOARD = new Object[2]; + private static final Object[] DISPLAY_SIDEBAR = new Object[2]; + + static { + Class scoreboardDisplayObjective = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutScoreboardDisplayObjective"); + Reflection.FieldAccessor scoreboardDisplayName = Reflection.getField(scoreboardDisplayObjective, String.class, 0); + Reflection.FieldAccessor scoreboardDisplaySlot = Reflection.getField(scoreboardDisplayObjective, int.class, 0); + for(int id = 0; id < 2; id++) { + DELETE_SCOREBOARD[id] = Reflection.newInstance(FlatteningWrapper.scoreboardObjective); + scoreboardName.set(DELETE_SCOREBOARD[id], SIDEBAR + id); + scoreboardAction.set(DELETE_SCOREBOARD[id], 1); //1 to remove + + DISPLAY_SIDEBAR[id] = Reflection.newInstance(scoreboardDisplayObjective); + scoreboardDisplayName.set(DISPLAY_SIDEBAR[id], SIDEBAR + id); + scoreboardDisplaySlot.set(DISPLAY_SIDEBAR[id], 1); // 1 = Sidebar + } + + Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> { + toggle ^= 1; // Toggle between 0 and 1 + + for(Map.Entry scoreboard : playerBoards.entrySet()) { + Player player = scoreboard.getKey(); + ScoreboardCallback callback = scoreboard.getValue(); + + TinyProtocol.instance.sendPacket(player, DELETE_SCOREBOARD[toggle]); + TinyProtocol.instance.sendPacket(player, createSidebarPacket(callback.getTitle())); + for(Map.Entry score : callback.getData().entrySet()){ + TinyProtocol.instance.sendPacket(player, createScorePacket(score.getKey(), score.getValue())); + } + + Bukkit.getScheduler().runTaskLater(Core.getInstance(), () -> { + if(!player.isOnline()) + return; + TinyProtocol.instance.sendPacket(player, DISPLAY_SIDEBAR[toggle]); + }, 2); + } + }, 10, 5); + } + + public boolean createScoreboard(Player player, ScoreboardCallback callback) { + playerBoards.put(player, callback); + return true; + } + + public void removeScoreboard(Player player) { + if(playerBoards.remove(player) == null || !player.isOnline()) + return; + + TinyProtocol.instance.sendPacket(player, DELETE_SCOREBOARD[toggle]); + } + + private static Object createSidebarPacket(String name){ + Object packet = Reflection.newInstance(FlatteningWrapper.scoreboardObjective); + scoreboardName.set(packet, SIDEBAR + toggle); + scoreboardAction.set(packet, 0); //0 to create + FlatteningWrapper.impl.setScoreboardTitle(packet, name); + scoreboardDisplayType.set(packet, displayTypeIntegers); + return packet; + } + + private static Object createScorePacket(String name, int value){ + Object packet = Reflection.newInstance(FlatteningWrapper.scoreboardScore); + scoreName.set(packet, name); + scoreScoreboardName.set(packet, SIDEBAR + toggle); + scoreValue.set(packet, value); + FlatteningWrapper.impl.setScoreAction(packet); + return packet; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/scoreboard/SWScoreboard.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/scoreboard/SWScoreboard.java index 440e29a0..5fd8808e 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/scoreboard/SWScoreboard.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/scoreboard/SWScoreboard.java @@ -19,99 +19,25 @@ package de.steamwar.scoreboard; -import com.comphenix.tinyprotocol.Reflection; -import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.core.Core; -import de.steamwar.core.FlatteningWrapper; -import org.bukkit.Bukkit; +import de.steamwar.core.VersionDependent; import org.bukkit.entity.Player; -import java.util.HashMap; -import java.util.Map; - public class SWScoreboard { private SWScoreboard() {} - private static final Reflection.FieldAccessor scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, String.class, 0); - private static final Reflection.FieldAccessor scoreboardAction = Reflection.getField(FlatteningWrapper.scoreboardObjective, int.class, Core.getVersion() > 15 ? 3 : 0); - private static final Class scoreboardDisplayEnum = Reflection.getClass("{nms.world.scores.criteria}.IScoreboardCriteria$EnumScoreboardHealthDisplay"); - private static final Reflection.FieldAccessor scoreboardDisplayType = Reflection.getField(FlatteningWrapper.scoreboardObjective, scoreboardDisplayEnum, 0); - private static final Object displayTypeIntegers = scoreboardDisplayEnum.getEnumConstants()[0]; - - private static final Reflection.FieldAccessor scoreName = Reflection.getField(FlatteningWrapper.scoreboardScore, String.class, 0); - private static final Reflection.FieldAccessor scoreScoreboardName = Reflection.getField(FlatteningWrapper.scoreboardScore, String.class, 1); - private static final Reflection.FieldAccessor scoreValue = Reflection.getField(FlatteningWrapper.scoreboardScore, int.class, 0); - - private static final HashMap playerBoards = new HashMap<>(); //Object -> Scoreboard | Alle Versionen in der Map! - private static int toggle = 0; // Scoreboard 0 updates while scoreboard 1 is presenting. toggle marks the current active scoreboard - - private static final String SIDEBAR = "Sidebar"; - private static final Object[] DELETE_SCOREBOARD = new Object[2]; - private static final Object[] DISPLAY_SIDEBAR = new Object[2]; - - static { - Class scoreboardDisplayObjective = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutScoreboardDisplayObjective"); - Reflection.FieldAccessor scoreboardDisplayName = Reflection.getField(scoreboardDisplayObjective, String.class, 0); - Reflection.FieldAccessor scoreboardDisplaySlot = Reflection.getField(scoreboardDisplayObjective, int.class, 0); - for(int id = 0; id < 2; id++) { - DELETE_SCOREBOARD[id] = Reflection.newInstance(FlatteningWrapper.scoreboardObjective); - scoreboardName.set(DELETE_SCOREBOARD[id], SIDEBAR + id); - scoreboardAction.set(DELETE_SCOREBOARD[id], 1); //1 to remove - - DISPLAY_SIDEBAR[id] = Reflection.newInstance(scoreboardDisplayObjective); - scoreboardDisplayName.set(DISPLAY_SIDEBAR[id], SIDEBAR + id); - scoreboardDisplaySlot.set(DISPLAY_SIDEBAR[id], 1); // 1 = Sidebar - } - - Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> { - toggle ^= 1; // Toggle between 0 and 1 - - for(Map.Entry scoreboard : playerBoards.entrySet()) { - Player player = scoreboard.getKey(); - ScoreboardCallback callback = scoreboard.getValue(); - - TinyProtocol.instance.sendPacket(player, DELETE_SCOREBOARD[toggle]); - TinyProtocol.instance.sendPacket(player, createSidebarPacket(callback.getTitle())); - for(Map.Entry score : callback.getData().entrySet()){ - TinyProtocol.instance.sendPacket(player, createScorePacket(score.getKey(), score.getValue())); - } - - Bukkit.getScheduler().runTaskLater(Core.getInstance(), () -> { - if(!player.isOnline()) - return; - TinyProtocol.instance.sendPacket(player, DISPLAY_SIDEBAR[toggle]); - }, 2); - } - }, 10, 5); - } + private static final ISWScoreboard impl = VersionDependent.getVersionImpl(Core.getInstance()); public static boolean createScoreboard(Player player, ScoreboardCallback callback) { - playerBoards.put(player, callback); - return true; + return impl.createScoreboard(player, callback); } public static void removeScoreboard(Player player) { - if(playerBoards.remove(player) == null || !player.isOnline()) - return; - - TinyProtocol.instance.sendPacket(player, DELETE_SCOREBOARD[toggle]); + impl.removeScoreboard(player); } - private static Object createSidebarPacket(String name){ - Object packet = Reflection.newInstance(FlatteningWrapper.scoreboardObjective); - scoreboardName.set(packet, SIDEBAR + toggle); - scoreboardAction.set(packet, 0); //0 to create - FlatteningWrapper.impl.setScoreboardTitle(packet, name); - scoreboardDisplayType.set(packet, displayTypeIntegers); - return packet; - } - - private static Object createScorePacket(String name, int value){ - Object packet = Reflection.newInstance(FlatteningWrapper.scoreboardScore); - scoreName.set(packet, name); - scoreScoreboardName.set(packet, SIDEBAR + toggle); - scoreValue.set(packet, value); - FlatteningWrapper.impl.setScoreAction(packet); - return packet; + public interface ISWScoreboard { + boolean createScoreboard(Player player, ScoreboardCallback callback); + void removeScoreboard(Player player); } } diff --git a/SpigotCore/build.gradle.kts b/SpigotCore/build.gradle.kts index 3838522b..b7ff647e 100644 --- a/SpigotCore/build.gradle.kts +++ b/SpigotCore/build.gradle.kts @@ -40,4 +40,5 @@ dependencies { implementation(project(":SpigotCore:SpigotCore_18")) implementation(project(":SpigotCore:SpigotCore_19")) implementation(project(":SpigotCore:SpigotCore_20")) + implementation(project(":SpigotCore:SpigotCore_21")) } diff --git a/settings.gradle.kts b/settings.gradle.kts index a13a1974..2271e124 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -204,6 +204,7 @@ include( "SpigotCore:SpigotCore_18", "SpigotCore:SpigotCore_19", "SpigotCore:SpigotCore_20", + "SpigotCore:SpigotCore_21", "SpigotCore:SpigotCore_Main" )