diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2649f29..b07c32c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,4 +21,4 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: ./gradlew build - run: nix develop -i -c sh -c "./gradlew build" + run: nix develop .#jdk25 -i -c sh -c "./gradlew build" diff --git a/.gitignore b/.gitignore index 0860bda..6041644 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ build/ .project .settings/ bin/ + +test-env/servers +test-env/cache diff --git a/extra-kotlin/build.gradle.kts b/extra-kotlin/build.gradle.kts index a51f92b..25f9874 100644 --- a/extra-kotlin/build.gradle.kts +++ b/extra-kotlin/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile plugins { diff --git a/flake.lock b/flake.lock index 9ed2b35..d5de671 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1722987190, - "narHash": "sha256-68hmex5efCiM2aZlAAEcQgmFI4ZwWt8a80vOeB/5w3A=", + "lastModified": 1770056022, + "narHash": "sha256-yvCz+Qmci1bVucXEyac3TdoSPMtjqVJmVy5wro6j/70=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "21cc704b5e918c5fbf4f9fff22b4ac2681706d90", + "rev": "d04d8548aed39902419f14a8537006426dc1e4fa", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-24.05", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index e3d2997..7f2e049 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; }; outputs = { nixpkgs, ... }: let @@ -16,9 +16,27 @@ })); in { devShells = forAllSystems (pkgs: { - default = pkgs.mkShell { - buildInputs = [ - pkgs.temurin-bin-21 + jdk25 = pkgs.mkShellNoCC { + buildInputs = with pkgs; [ + jdk25_headless + ]; + }; + + jdk21 = pkgs.mkShellNoCC { + buildInputs = with pkgs; [ + jdk21_headless + ]; + }; + + jdk17 = pkgs.mkShellNoCC { + buildInputs = with pkgs; [ + jdk17_headless + ]; + }; + + jdk8 = pkgs.mkShellNoCC { + buildInputs = with pkgs; [ + jdk8_headless ]; }; }); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f90fb4f..cf59a40 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,5 @@ [versions] adventure = "4.25.0" -devBundle = "1.21.10-R0.1-20251007.183616-3" # find latest here: https://repo.papermc.io/repository/maven-snapshots/io/papermc/paper/dev-bundle/1.21.9-R0.1-SNAPSHOT/maven-metadata.xml [libraries] spigotApi = "org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" # do not update @@ -18,7 +17,6 @@ junitPlatformLauncher = "org.junit.platform:junit-platform-launcher:1.14.1" [plugins] kotlin = "org.jetbrains.kotlin.jvm:2.2.21" -paperweight = "io.papermc.paperweight.userdev:2.0.0-beta.19" [bundles] adventure = [ "adventureApi", "adventureTextSerializerGson", "adventureTextSerializerLegacy" ] diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/SidebarLineHandler.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/SidebarLineHandler.java index e8a50c6..edf116e 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/SidebarLineHandler.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/SidebarLineHandler.java @@ -53,7 +53,9 @@ public class SidebarLineHandler { public void updateScore(int line) { LocaleLine localeLine = lines[line]; - localeLine.sendScore(players); + if (localeLine != null) { + localeLine.sendScore(players); + } } public void setLine(int line, Component renderedLine) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 86d006c..05c6cff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,6 +4,7 @@ include(":api") include(":commons") include(":implementation") include(":extra-kotlin") +include(":test-plugin") include(":packet-adapter-base") project(":packet-adapter-base").projectDir = file("versions/packet-adapter-base") diff --git a/test-env/README.md b/test-env/README.md new file mode 100644 index 0000000..6415033 --- /dev/null +++ b/test-env/README.md @@ -0,0 +1,51 @@ +# test-servers + +## Target server versions + +- [ ] 1.7.10 +- [?] 1.8 +- [?] 1.8.3 +- [?] 1.8.4 +- [?] 1.8.5 +- [?] 1.8.6 +- [?] 1.8.7 +- [ ] 1.8.8 +- [?] 1.9 +- [?] 1.9.2 +- [?] 1.9.4 +- [?] 1.10.2 +- [?] 1.11 +- [?] 1.11.2 +- [?] 1.12 +- [?] 1.12.1 +- [ ] 1.12.2 +- [ ] 1.17 +- [?] 1.17.1 +- [?] 1.18 +- [?] 1.18.1 +- [?] 1.18.2 +- [?] 1.19 +- [?] 1.19.1 +- [?] 1.19.2 +- [?] 1.19.3 +- [?] 1.19.4 +- [?] 1.20 +- [?] 1.20.1 +- [ ] 1.20.2 +- [-] 1.20.3 +- [ ] 1.20.4 +- [ ] 1.20.5 +- [-] 1.20.6 +- [-] 1.21 +- [-] 1.21.1 +- [-] 1.21.2 +- [ ] 1.21.3 +- [-] 1.21.4 +- [ ] 1.21.5 +- [ ] 1.21.6 +- [-] 1.21.7 +- [-] 1.21.8 +- [ ] 1.21.9 +- [-] 1.21.10 +- [ ] 1.21.11 + diff --git a/test-env/bukkit.yml b/test-env/bukkit.yml new file mode 100644 index 0000000..bf93b02 --- /dev/null +++ b/test-env/bukkit.yml @@ -0,0 +1,5 @@ +settings: + allow-end: false +spawn-limits: + monsters: 0 + animals: 0 diff --git a/test-env/clean b/test-env/clean new file mode 100755 index 0000000..c387bdc --- /dev/null +++ b/test-env/clean @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -x +rm -r servers +rm -r cache diff --git a/test-env/init b/test-env/init new file mode 100755 index 0000000..b09a146 --- /dev/null +++ b/test-env/init @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +set -e + +create_server() { + local name=$1 + local port=$2 + local nixshell=$3 + + local root=$(pwd) + + local plugin_path=$(readlink -f ../test-plugin/build/libs/scoreboard-library-test-plugin-*-all.jar) + local jar_path="$root/cache/$name.jar" + + local server_dir="servers/$name" + mkdir -p "$server_dir/plugins" + pushd "$server_dir" &> /dev/null + + echo "eula=true" > eula.txt + printf "server-port=$port\ngamemode=creative\ndifficulty=peaceful\ngenerate-structures=false\nlevel-type=minecraft:flat" > server.properties + cp "$root/bukkit.yml" . + ln -sf "$plugin_path" plugins/test.jar + ln -sf "$jar_path" server.jar + + local flags="-DPaper.IgnoreJavaVersion=true -Xms512M -Xmx1024M -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20" + + printf "#!/bin/sh\nnix develop .#$nixshell -c sh -c 'java $flags -jar server.jar nogui'" > start + chmod +x start + + popd &> /dev/null +} + +create_spigot_server() { + local version=$1 + local port=$2 + local nixshell=$3 + + if [ ! -f "cache/spigot-$version.jar" ]; then + echo Running BuildTools for Spigot $version + + local root=$(pwd) + + pushd cache &> /dev/null + + mkdir -p "spigot-$version" + pushd "spigot-$version" &> /dev/null + nix develop ".#$nixshell" -c java -jar ../buildtools.jar --disable-java-check --nogui --rev "$version" + + if [ -f "$root/patches/spigot-$version-server.patch" ]; then + echo Applying spigot-$version-server.patch + pushd Spigot/Spigot-Server &> /dev/null + git apply "$root/patches/spigot-$version-server.patch" + popd &> /dev/null + + pushd Spigot &> /dev/null + bash ../apache-maven-*/bin/mvn package + mv Spigot-Server/target/spigot-*.jar "../spigot-$version.jar" + popd &> /dev/null + fi + + mv "spigot-$version.jar" .. + popd &> /dev/null + rm -rf "spigot-$version" + + popd &> /dev/null + fi + + create_server "spigot-$version" "$port" "$nixshell" +} + +download_file() { + local name=$1 + local url=$2 + local expected_sha256_hash=$3 + + local path="cache/$name" + mkdir -p cache + + if [ ! -f "$path" ]; then + echo "Downloading $name..." + curl --silent --location --output "$path" "$url" + fi + + local actual_hash=$(sha256sum "$path" | cut -f 1 -d ' ') + if [ "$expected_sha256_hash" != "$actual_hash" ]; then + echo "ERROR: hashes for $name do not match! expected: $expected_sha256_hash, but got $actual_hash" + rm "$path" + exit 1 + fi +} + +download_file "buildtools.jar" "https://hub.spigotmc.org/jenkins/job/BuildTools/197/artifact/target/BuildTools.jar" "9a5b059ace22eaef28fb62f96970f0abe1490f06c860229b94627cd72eaf6ea5" + +# --- 1.21.11 --- +download_file "paper-1.21.11.jar" "https://fill-data.papermc.io/v1/objects/4a558a00005d33dafa4c4d5f9e47b3bd47d92311fceccd9c9754ee6b913f8649/paper-1.21.11-100.jar" "4a558a00005d33dafa4c4d5f9e47b3bd47d92311fceccd9c9754ee6b913f8649" +create_server "paper-1.21.11" "25587" "jdk25" + +create_spigot_server "1.21.11" "25586" "jdk25" + +# --- 1.21.9 --- +download_file "paper-1.21.9.jar" "https://fill-data.papermc.io/v1/objects/aec002e77c7566e49494fdf05430b96078ffd1d7430e652d4f338fef951e7a10/paper-1.21.9-59.jar" "aec002e77c7566e49494fdf05430b96078ffd1d7430e652d4f338fef951e7a10" +create_server "paper-1.21.9" "25585" "jdk25" + +# spigot doesnt have 1.21.9 +create_spigot_server "1.21.10" "25584" "jdk25" + +# --- 1.21.6 --- +download_file "paper-1.21.6.jar" "https://fill-data.papermc.io/v1/objects/35e2dfa66b3491b9d2f0bb033679fa5aca1e1fdf097e7a06a80ce8afeda5c214/paper-1.21.6-48.jar" "35e2dfa66b3491b9d2f0bb033679fa5aca1e1fdf097e7a06a80ce8afeda5c214" +create_server "paper-1.21.6" "25583" "jdk25" + +create_spigot_server "1.21.6" "25582" "jdk21" + +# --- 1.21.5 --- +download_file "paper-1.21.5.jar" "https://fill-data.papermc.io/v1/objects/2ae6ae22adf417699746e0f89fc2ef6cb6ee050a5f6608cee58f0535d60b509e/paper-1.21.5-114.jar" "2ae6ae22adf417699746e0f89fc2ef6cb6ee050a5f6608cee58f0535d60b509e" +create_server "paper-1.21.5" "25581" "jdk25" + +create_spigot_server "1.21.5" "25580" "jdk21" + +# --- 1.21.3 --- +download_file "paper-1.21.3.jar" "https://fill-data.papermc.io/v1/objects/87e973e1d338e869e7fdbc4b8fadc1579d7bb0246a0e0cf6e5700ace6c8bc17e/paper-1.21.3-83.jar" "87e973e1d338e869e7fdbc4b8fadc1579d7bb0246a0e0cf6e5700ace6c8bc17e" +create_server "paper-1.21.3" "25579" "jdk25" + +create_spigot_server "1.21.3" "25578" "jdk21" + +# --- 1.20.5 --- +download_file "paper-1.20.5.jar" "https://fill-data.papermc.io/v1/objects/3cd7da2f8df92e082a501a39c674aab3c0343edd179b86f5baccaebfc9974132/paper-1.20.5-22.jar" "3cd7da2f8df92e082a501a39c674aab3c0343edd179b86f5baccaebfc9974132" +create_server "paper-1.20.5" "25577" "jdk25" + +# spigot doesn't have 1.20.5 +create_spigot_server "1.20.6" "25576" "jdk21" + +# --- 1.20.4 --- +download_file "paper-1.20.4.jar" "https://fill-data.papermc.io/v1/objects/e84aa4943cc51d7545b1c9b669bb1e0b143323d248ebb89012182f5554bc13d7/paper-1.20.4-499-mojang.jar" "e84aa4943cc51d7545b1c9b669bb1e0b143323d248ebb89012182f5554bc13d7" +create_server "paper-1.20.4" "25575" "jdk25" + +create_spigot_server "1.20.4" "25574" "jdk21" + +# --- 1.20.2 --- +download_file "paper-1.20.2.jar" "https://fill-data.papermc.io/v1/objects/07f57cdc5948dcf3cb3358e43b9a46fbbe8b595fa30c8749cfc102bf44897b69/paper-1.20.2-318-mojang.jar" "07f57cdc5948dcf3cb3358e43b9a46fbbe8b595fa30c8749cfc102bf44897b69" +create_server "paper-1.20.2" "25573" "jdk25" + +create_spigot_server "1.20.2" "25572" "jdk21" + +# --- 1.17 --- +download_file "paper-1.17.jar" "https://fill-data.papermc.io/v1/objects/760a93b94a58d619bd647d71af84688617d0444d22b716500bc6b343858dc871/paper-1.17-79.jar" "760a93b94a58d619bd647d71af84688617d0444d22b716500bc6b343858dc871" +create_server "paper-1.17" "25571" "jdk25" + +create_spigot_server "1.17" "25570" "jdk17" diff --git a/test-env/patches/spigot-1.17-server.patch b/test-env/patches/spigot-1.17-server.patch new file mode 100644 index 0000000..0c9cad8 --- /dev/null +++ b/test-env/patches/spigot-1.17-server.patch @@ -0,0 +1,13 @@ +diff --git i/src/main/java/org/bukkit/craftbukkit/Main.java w/src/main/java/org/bukkit/craftbukkit/Main.java +index 02e5090db..5f4215b35 100644 +--- i/src/main/java/org/bukkit/craftbukkit/Main.java ++++ w/src/main/java/org/bukkit/craftbukkit/Main.java +@@ -161,7 +161,7 @@ public class Main { + System.err.println("Unsupported Java detected (" + javaVersion + "). This version of Minecraft requires at least Java 16. Check your Java version with the command 'java -version'."); + return; + } +- if (javaVersion > 60.0) { ++ if (javaVersion > 60.0 && false) { + System.err.println("Unsupported Java detected (" + javaVersion + "). Only up to Java 16 is supported."); + return; + } diff --git a/test-env/start b/test-env/start new file mode 100755 index 0000000..b493021 --- /dev/null +++ b/test-env/start @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +if ! command -v tmux >/dev/null 2>&1; then + echo ERROR: tmux not found, install it >&2 + exit 67 +fi + +sesh="sl" + +if tmux has-session -t "$sesh" 2>/dev/null; then + echo already exists + #tmux a -t "$sesh" + exit 0 +fi + +tmux new-session -d -s "$sesh" -n "shell" + +for dir in servers/*; do + window=$(basename $dir | tr '.' '-') + + pushd "$dir" &> /dev/null + tmux new-window -t "$sesh" -n "$window" + popd &> /dev/null + + flags="-Xms512M -Xmx512M --add-modules=jdk.incubator.vector -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20" + + tmux send-keys -t "$sesh:$window" "./start" C-m +done + +tmux a -t "$sesh" diff --git a/test-plugin/build.gradle.kts b/test-plugin/build.gradle.kts new file mode 100644 index 0000000..1c76b99 --- /dev/null +++ b/test-plugin/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("net.megavex.scoreboardlibrary.base-conventions") + id("com.gradleup.shadow") version "9.3.1" +} + +dependencies { + compileOnly(libs.spigotApi) + + implementation(project(":scoreboard-library-api")) + runtimeOnly(project(":scoreboard-library-implementation")) + runtimeOnly(project(":scoreboard-library-modern")) + + implementation(libs.adventureApi) + implementation(libs.adventureTextSerializerGson) + implementation(libs.adventureTextSerializerLegacy) +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/ScoreboardPlugin.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/ScoreboardPlugin.java new file mode 100644 index 0000000..6c87c6e --- /dev/null +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/ScoreboardPlugin.java @@ -0,0 +1,68 @@ +package net.megavex.scoreboardlibrary.testplugin; + +import net.kyori.adventure.translation.GlobalTranslator; +import net.megavex.scoreboardlibrary.api.ScoreboardLibrary; +import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException; +import net.megavex.scoreboardlibrary.testplugin.module.Module; +import net.megavex.scoreboardlibrary.testplugin.module.ObjectivesModule; +import net.megavex.scoreboardlibrary.testplugin.module.SidebarModule; +import net.megavex.scoreboardlibrary.testplugin.module.TeamsModule; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Arrays; +import java.util.List; + +public final class ScoreboardPlugin extends JavaPlugin implements Listener { + private final TestTranslator translator = new TestTranslator(); + private ScoreboardLibrary lib; + private List modules; + + @Override + public void onEnable() { + this.getServer().getPluginManager().registerEvents(this, this); + GlobalTranslator.get().addSource(this.translator); + + try { + lib = ScoreboardLibrary.loadScoreboardLibrary(this); + } catch (NoPacketAdapterAvailableException e) { + this.getLogger().warning("No packet adapter found, disabling plugin"); + this.getServer().getPluginManager().disablePlugin(this); + return; + } + + this.modules = Arrays.asList( + new SidebarModule(this), + new TeamsModule(this), + new ObjectivesModule(this) + ); + for (final Module module : this.modules) { + this.getLogger().info("Enabling " + module.getClass().getSimpleName()); + module.onEnable(); + } + } + + @Override + public void onDisable() { + for (final Module module : this.modules) { + module.onDisable(); + } + this.lib.close(); + GlobalTranslator.get().removeSource(this.translator); + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + final Player player = event.getPlayer(); + player.setOp(true); + player.sendMessage("Server software: " + this.getServer().getName()); + player.sendMessage("Server version: " + this.getServer().getVersion()); + } + + public ScoreboardLibrary scoreboardLibrary() { + return this.lib; + } +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/TestTranslator.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/TestTranslator.java new file mode 100644 index 0000000..07fbd85 --- /dev/null +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/TestTranslator.java @@ -0,0 +1,26 @@ +package net.megavex.scoreboardlibrary.testplugin; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.translation.Translator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.Locale; + +public final class TestTranslator implements Translator { + public static final String KEY = "test.translatable.key"; + + @Override + public @NotNull Key name() { + return Key.key("scoreboard-library", "test"); + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + if (key.equals(KEY)) { + return new MessageFormat(locale.toString(), locale); + } + return null; + } +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/Module.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/Module.java new file mode 100644 index 0000000..68e974a --- /dev/null +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/Module.java @@ -0,0 +1,9 @@ +package net.megavex.scoreboardlibrary.testplugin.module; + +public interface Module { + default void onEnable() { + } + + default void onDisable() { + } +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/ObjectivesModule.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/ObjectivesModule.java new file mode 100644 index 0000000..f6b3c4e --- /dev/null +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/ObjectivesModule.java @@ -0,0 +1,57 @@ +package net.megavex.scoreboardlibrary.testplugin.module; + +import net.megavex.scoreboardlibrary.api.objective.*; +import net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin; +import net.megavex.scoreboardlibrary.testplugin.TestTranslator; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; + +public final class ObjectivesModule implements Module, Listener { + private final ScoreboardPlugin plugin; + private ObjectiveManager manager; + private ScoreboardObjective objective; + + public ObjectivesModule(final ScoreboardPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void onEnable() { + this.manager = plugin.scoreboardLibrary().createObjectiveManager(); + + this.objective = this.manager.create("test"); + this.objective.value(text("List Objective")); + this.objective.renderType(ObjectiveRenderType.INTEGER); + this.objective.defaultScoreFormat(ScoreFormat.fixed(text("obj ").append(translatable(TestTranslator.KEY)))); + this.manager.display(ObjectiveDisplaySlot.playerList(), this.objective); + + this.plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void onDisable() { + HandlerList.unregisterAll(this); + this.manager.close(); + } + + @EventHandler + public void onJoin(final PlayerJoinEvent event) { + final Player player = event.getPlayer(); + this.manager.addPlayer(player); + this.objective.score(player.getName(), 5); + } + + @EventHandler + public void onQuit(final PlayerQuitEvent event) { + final Player player = event.getPlayer(); + this.manager.removePlayer(player); + this.objective.removeScore(player.getName()); + } +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java new file mode 100644 index 0000000..6f65cc1 --- /dev/null +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java @@ -0,0 +1,99 @@ +package net.megavex.scoreboardlibrary.testplugin.module; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; +import net.megavex.scoreboardlibrary.api.sidebar.Sidebar; +import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout; +import net.megavex.scoreboardlibrary.api.sidebar.component.LineDrawable; +import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent; +import net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin; +import net.megavex.scoreboardlibrary.testplugin.TestTranslator; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +import static net.kyori.adventure.text.Component.*; +import static net.kyori.adventure.text.format.NamedTextColor.*; +import static net.kyori.adventure.text.format.Style.style; +import static net.kyori.adventure.text.format.TextColor.color; +import static net.kyori.adventure.text.format.TextDecoration.BOLD; + +public final class SidebarModule implements Module, Listener, SidebarComponent { + private final ScoreboardPlugin plugin; + private Sidebar sidebar; + private int tickCounter; + private BukkitTask timer; + + public SidebarModule(final ScoreboardPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void onEnable() { + this.sidebar = plugin.scoreboardLibrary().createSidebar(); + + final SidebarComponent title = SidebarComponent.dynamicLine(() -> text("Sidebar Test", hueColor())); + + final ComponentSidebarLayout layout = new ComponentSidebarLayout(title, this); + this.timer = this.plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { + this.tickCounter++; + layout.apply(sidebar); + + if ((this.tickCounter / 20) % 10 == 0) { + for (final Player player : this.plugin.getServer().getOnlinePlayers()) { + this.sidebar.removePlayer(player); + } + } else { + for (final Player player : this.plugin.getServer().getOnlinePlayers()) { + this.sidebar.addPlayer(player); + } + } + }, 1, 1); + + this.plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void onDisable() { + HandlerList.unregisterAll(this); + this.timer.cancel(); + this.sidebar.close(); + this.sidebar = null; + } + + @EventHandler + public void onQuit(final PlayerQuitEvent event) { + sidebar.removePlayer(event.getPlayer()); + } + + @Override + public void draw(final @NotNull LineDrawable drawable) { + drawable.drawLine(empty(), ScoreFormat.styled(style(this.hueColor()))); + drawable.drawLine(text("Static line")); + drawable.drawLine(text("Tick counter: ").append(text(this.tickCounter, color(0x00FF00)))); + + if ((this.tickCounter / 20) % 2 == 0) { + drawable.drawLine(text("Disappearing line"), ScoreFormat.styled(style(DARK_RED))); + } + + drawable.drawLine(text("Your locale x2: ").append(Component.translatable(TestTranslator.KEY)), ScoreFormat.fixed(translatable(TestTranslator.KEY))); + + drawable.drawLine(text("VeryLongLineThatShouldBeCutOffFor1.12.2AndBelowPlayersButNotOnNewerVersions", AQUA, BOLD)); + drawable.drawLine(empty()); + + drawable.drawLine(text("github.com/megavexnetwork/scoreboard-library", YELLOW)); + } + + private TextColor hueColor() { + final float hue = ((this.tickCounter * 5) % 360) / 360f; + final Color awtColor = Color.getHSBColor(hue, 0.8f, 0.9f); + return color(awtColor.getRGB()); + } +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/TeamsModule.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/TeamsModule.java new file mode 100644 index 0000000..8e9e2b0 --- /dev/null +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/TeamsModule.java @@ -0,0 +1,88 @@ +package net.megavex.scoreboardlibrary.testplugin.module; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration.State; +import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam; +import net.megavex.scoreboardlibrary.api.team.TeamDisplay; +import net.megavex.scoreboardlibrary.api.team.TeamManager; +import net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin; +import net.megavex.scoreboardlibrary.testplugin.TestTranslator; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static net.kyori.adventure.text.Component.*; +import static net.kyori.adventure.text.format.NamedTextColor.AQUA; +import static net.kyori.adventure.text.format.NamedTextColor.RED; +import static net.kyori.adventure.text.format.TextDecoration.BOLD; +import static net.kyori.adventure.text.format.TextDecoration.State.FALSE; +import static net.kyori.adventure.text.format.TextDecoration.State.TRUE; + +public final class TeamsModule implements Module, Listener { + private final static List NAMED_COLORS = new ArrayList<>(NamedTextColor.NAMES.values()); + + private final Random random = new Random(); + private final ScoreboardPlugin plugin; + private TeamManager manager; + private ScoreboardTeam team; + private BukkitTask task; + + public TeamsModule(final ScoreboardPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void onEnable() { + this.manager = plugin.scoreboardLibrary().createTeamManager(); + + this.team = this.manager.createIfAbsent("epic"); + final TeamDisplay defaultDisplay = this.team.defaultDisplay(); + defaultDisplay.displayName(text("Epic Team")); + defaultDisplay.prefix(text("[Epic Prefix] ").color(AQUA)); + defaultDisplay.suffix(space().append(Component.translatable(TestTranslator.KEY))); + defaultDisplay.playerColor(RED); + + this.task = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, () -> { + final State state = defaultDisplay.prefix().decoration(BOLD); + final Component newPrefix = defaultDisplay.prefix().decoration(BOLD, state == TRUE ? FALSE : TRUE); + defaultDisplay.prefix(newPrefix); + defaultDisplay.suffix(defaultDisplay.suffix().color(this.randomNamedColor())); + }, 20, 20); + + this.plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void onDisable() { + this.task.cancel(); + HandlerList.unregisterAll(this); + this.manager.close(); + } + + @EventHandler + public void onJoin(final PlayerJoinEvent event) { + final Player player = event.getPlayer(); + this.manager.addPlayer(player); + this.team.defaultDisplay().addEntry(player.getName()); + } + + @EventHandler + public void onQuit(final PlayerQuitEvent event) { + final Player player = event.getPlayer(); + this.manager.removePlayer(player); + this.team.defaultDisplay().removeEntry(player.getName()); + } + + private NamedTextColor randomNamedColor() { + return NAMED_COLORS.get(random.nextInt(NAMED_COLORS.size())); + } +} diff --git a/test-plugin/src/main/resources/plugin.yml b/test-plugin/src/main/resources/plugin.yml new file mode 100644 index 0000000..71a7848 --- /dev/null +++ b/test-plugin/src/main/resources/plugin.yml @@ -0,0 +1,4 @@ +name: scoreboard-library-test +version: 0.1.0 +main: net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin +api-version: 1.13 diff --git a/versions/modern/build.gradle.kts b/versions/modern/build.gradle.kts index f6246ca..7a5426e 100644 --- a/versions/modern/build.gradle.kts +++ b/versions/modern/build.gradle.kts @@ -1,31 +1,17 @@ plugins { id("net.megavex.scoreboardlibrary.base-conventions") - alias(libs.plugins.paperweight) + //id("io.papermc.paperweight.userdev") version "2.0.0-beta.19" } dependencies { compileOnly(project(":scoreboard-library-packet-adapter-base")) { - exclude(group = "org.spigotmc", module = "spigot-api") + //exclude(group = "org.spigotmc", module = "spigot-api") } - paperweight.paperDevBundle(libs.versions.devBundle.get()) + compileOnly(libs.spigotApi) + //paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT") } tasks { - compileJava { - // Workaround to get it to compile targeting Java 8 while using NMS which targets 17 - // Why does this work? I have no idea - options.release = null - } - - // https://github.com/unnamed/hephaestus-engine/blob/db6deabe1ddb0a549306b0c4b519c3e79e6f1ea8/runtime-bukkit/adapt-v1_20_R3/build.gradle.kts#L25 - reobfJar { - outputJar = file("build/libs/scoreboard-library-modern-$version-reobf.jar") - } - - assemble { - dependsOn(reobfJar) - } - javadoc { exclude("**") } @@ -41,11 +27,12 @@ indra { publishing { publications.getByName("maven") { + // Kept for backwards compatibility artifact(tasks.jar) { classifier = "mojmap" } - artifact(tasks.reobfJar) + artifact(tasks.jar) artifact(tasks.javadocJar) artifact(tasks.sourcesJar) } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/ComponentProvider.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/ComponentProvider.java deleted file mode 100644 index 1572a33..0000000 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/ComponentProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern; - -import net.kyori.adventure.text.Component; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Locale; - -public interface ComponentProvider { - @NotNull net.minecraft.network.chat.Component fromAdventure(@NotNull Component adventure, @Nullable Locale locale); -} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/ComponentProviderImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/ComponentProviderImpl.java deleted file mode 100644 index 317a22a..0000000 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/ComponentProviderImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern; - -import com.google.gson.JsonElement; -import com.mojang.serialization.JsonOps; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.translation.GlobalTranslator; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.RegistryUtil; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; -import net.minecraft.network.chat.ComponentSerialization; -import net.minecraft.network.chat.MutableComponent; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.util.Locale; - -import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson; - -public class ComponentProviderImpl implements ComponentProvider { - private static final MethodHandle FROM_JSON_METHOD; - - static { - if (!PacketAccessors.IS_1_21_6_OR_ABOVE) { - Class serializerClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Component$Serializer", "net.minecraft.network.chat.IChatBaseComponent$ChatSerializer"); - MethodHandle handle = null; - for (Method method : serializerClass.getMethods()) { - if (method.getReturnType() == MutableComponent.class && - method.getParameterCount() >= 1 && - method.getParameterCount() <= 2 && - method.getParameterTypes()[0] == JsonElement.class - ) { - try { - handle = MethodHandles.lookup().unreflect(method); - break; - } catch (IllegalAccessException e) { - throw new ExceptionInInitializerError(e); - } - } - } - - if (handle == null) { - throw new ExceptionInInitializerError("failed to find chat component fromJson method"); - } - - FROM_JSON_METHOD = handle; - } else { - FROM_JSON_METHOD = null; - } - } - - private final boolean isNativeAdventure; - - public ComponentProviderImpl(boolean isNativeAdventure) { - this.isNativeAdventure = isNativeAdventure; - } - - @Override - public net.minecraft.network.chat.@NotNull Component fromAdventure(@NotNull Component adventure, @Nullable Locale locale) { - if (isNativeAdventure) { - return NativeAdventureUtil.fromAdventureComponent(adventure); - } - - Component translated = adventure; - if (locale != null) { - translated = GlobalTranslator.render(adventure, locale); - } - JsonElement json = gson().serializeToTree(translated); - - if (FROM_JSON_METHOD == null) { - // 1.21.6+ - return ComponentSerialization.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); - } - - Object[] args; - if (PacketAccessors.IS_1_20_5_OR_ABOVE) { - args = new Object[]{json, RegistryUtil.MINECRAFT_REGISTRY}; - } else { - args = new Object[]{json}; - } - - try { - return (net.minecraft.network.chat.Component) FROM_JSON_METHOD.invokeWithArguments(args); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } -} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java index a0f644d..bc4fbc3 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java @@ -1,24 +1,72 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ConstructorAccessor; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.FieldAccessor; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.PacketConstructor; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.numbers.NumberFormat; -import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; -import net.minecraft.network.protocol.game.ClientboundSetScorePacket; -import net.minecraft.world.scores.Objective; -import net.minecraft.world.scores.Team; -import net.minecraft.world.scores.criteria.ObjectiveCriteria; +import net.kyori.adventure.text.Component; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.*; +import org.jetbrains.annotations.NotNull; -import java.util.Collection; -import java.util.Optional; +import java.lang.invoke.MethodType; +import java.util.*; public final class PacketAccessors { + public static final Class PKT_CLASS, + SET_OBJECTIVE_PKT_CLASS, + SET_DISPLAY_OBJECTIVE_PKT_CLASS, + SET_SCORE_PKT_CLASS, + RESET_SCORE_PKT_CLASS, + SET_PLAYER_TEAM_PKT_CLASS, + TEAM_PARAMETERS_PKT_CLASS, + COMPONENT_CLASS, + MUTABLE_COMPONENT_CLASS, + COMPONENT_SERIALIZATION_CLASS, + STYLE_CLASS, + STYLE_SERIALIZER_CLASS, + NUMBER_FORMAT_CLASS, + DISPLAY_SLOT_CLASS, // 1.20.2 + OBJECTIVE_CLASS, + TEAM_VISIBILITY_CLASS, + TEAM_COLLISION_RULE_CLASS, + CHAT_FORMATTING_CLASS, + OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS, + DATA_RESULT_CLASS, + DYNAMIC_OPS_CLASS, + JSON_OPS_CLASS, + CODEC_CLASS, + SERVER_PLAYER_CLASS, + PLAYER_CONNECTION_CLASS, + ADVENTURE_COMPONENT_CLASS; + + static { + // TODO: spigot mapping names + PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.Packet"); + SET_OBJECTIVE_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective"); + SET_DISPLAY_OBJECTIVE_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective"); + SET_SCORE_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetScorePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore"); + RESET_SCORE_PKT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.protocol.game.ClientboundResetScorePacket", "net.minecraft.network.protocol.game.ClientboundResetScorePacket"); + SET_PLAYER_TEAM_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam"); + TEAM_PARAMETERS_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Parameters", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$b"); + COMPONENT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Component", "net.minecraft.network.chat.IChatBaseComponent"); + MUTABLE_COMPONENT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.MutableComponent", "net.minecraft.network.chat.IChatMutableComponent"); + COMPONENT_SERIALIZATION_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.chat.ComponentSerialization"); + STYLE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Style", "net.minecraft.network.chat.ChatModifier"); + STYLE_SERIALIZER_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Style$Serializer", "net.minecraft.network.chat.ChatModifier$ChatModifierSerializer"); + NUMBER_FORMAT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.chat.numbers.NumberFormat"); + DISPLAY_SLOT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.world.scores.DisplaySlot"); + OBJECTIVE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.Objective", "net.minecraft.world.scores.ScoreboardObjective"); + TEAM_VISIBILITY_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.Team$Visibility", "net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility"); + TEAM_COLLISION_RULE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.Team$CollisionRule", "net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush"); + CHAT_FORMATTING_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.ChatFormatting", "net.minecraft.EnumChatFormat"); + OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType", "net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay"); + DATA_RESULT_CLASS = ReflectUtil.getClassOrThrow("com.mojang.serialization.DataResult"); + DYNAMIC_OPS_CLASS = ReflectUtil.getClassOrThrow("com.mojang.serialization.DynamicOps"); + JSON_OPS_CLASS = ReflectUtil.getClassOrThrow("com.mojang.serialization.JsonOps"); + CODEC_CLASS = ReflectUtil.getClassOrThrow("com.mojang.serialization.Codec"); + SERVER_PLAYER_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.server.level.ServerPlayer", "net.minecraft.server.level.EntityPlayer"); + PLAYER_CONNECTION_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.server.network.ServerGamePacketListenerImpl", "net.minecraft.server.network.PlayerConnection"); + ADVENTURE_COMPONENT_CLASS = ReflectUtil.getOptionalClass("io.papermc.paper.adventure.AdventureComponent"); + } + + public static final boolean IS_1_20_2_OR_ABOVE, IS_1_20_3_OR_ABOVE, IS_1_20_5_OR_ABOVE, IS_1_21_5_OR_ABOVE, IS_1_21_6_OR_ABOVE; + static { boolean is1_20_2OrAbove = false; try { @@ -61,86 +109,137 @@ public final class PacketAccessors { IS_1_21_6_OR_ABOVE = is1_21_6OrAbove; if (is1_21_5OrAbove) { - NAME_TAG_VISIBILITY_FIELD_1_21_5 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, Team.Visibility.class); - COLLISION_RULE_FIELD_1_21_5 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, Team.CollisionRule.class); + NAME_TAG_VISIBILITY_FIELD_1_21_5 = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, TEAM_VISIBILITY_CLASS); + COLLISION_RULE_FIELD_1_21_5 = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, TEAM_COLLISION_RULE_CLASS); NAME_TAG_VISIBILITY_FIELD_1_21_4 = null; COLLISION_RULE_FIELD_1_21_4 = null; } else { - NAME_TAG_VISIBILITY_FIELD_1_21_4 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, String.class); - COLLISION_RULE_FIELD_1_21_4 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, String.class); + NAME_TAG_VISIBILITY_FIELD_1_21_4 = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, String.class); + COLLISION_RULE_FIELD_1_21_4 = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 1, String.class); NAME_TAG_VISIBILITY_FIELD_1_21_5 = null; COLLISION_RULE_FIELD_1_21_5 = null; } + } - if (is1_20_5OrAbove) { - OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(ClientboundSetObjectivePacket.class, 0, Optional.class); - SCORE_1_20_2_CONSTRUCTOR = null; + static { + if (IS_1_20_3_OR_ABOVE) { + RESET_SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(Objects.requireNonNull(RESET_SCORE_PKT_CLASS), String.class, String.class); + } else { + RESET_SCORE_CONSTRUCTOR = null; + } + + if (IS_1_20_5_OR_ABOVE) { + OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, Optional.class); + SCORE_1_20_5_CONSTRUCTOR = ReflectUtil.findConstructor(SET_SCORE_PKT_CLASS, String.class, String.class, int.class, Optional.class, Optional.class); SCORE_1_20_3_CONSTRUCTOR = null; + SCORE_1_20_2_CONSTRUCTOR = null; SCORE_1_20_2_METHOD_CHANGE = null; SCORE_1_20_2_METHOD_REMOVE = null; - } else if (is1_20_3OrAbove) { - OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(ClientboundSetObjectivePacket.class, 0, NumberFormat.class); - SCORE_1_20_3_CONSTRUCTOR = ReflectUtil.findConstructor(ClientboundSetScorePacket.class, String.class, String.class, int.class, Component.class, NumberFormat.class); + } else if (IS_1_20_3_OR_ABOVE) { + OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, NUMBER_FORMAT_CLASS); + SCORE_1_20_5_CONSTRUCTOR = null; + SCORE_1_20_3_CONSTRUCTOR = ReflectUtil.findConstructor(SET_SCORE_PKT_CLASS, String.class, String.class, int.class, COMPONENT_CLASS, NUMBER_FORMAT_CLASS); SCORE_1_20_2_METHOD_CHANGE = null; SCORE_1_20_2_METHOD_REMOVE = null; SCORE_1_20_2_CONSTRUCTOR = null; } else { OBJECTIVE_NUMBER_FORMAT_FIELD = null; + SCORE_1_20_5_CONSTRUCTOR = null; SCORE_1_20_3_CONSTRUCTOR = null; Class methodClass = ReflectUtil.getClassOrThrow("net.minecraft.server.ServerScoreboard$Method", "net.minecraft.server.ScoreboardServer$Action"); SCORE_1_20_2_METHOD_CHANGE = ReflectUtil.getEnumInstance(methodClass, "CHANGE", "a"); SCORE_1_20_2_METHOD_REMOVE = ReflectUtil.getEnumInstance(methodClass, "REMOVE", "b"); - SCORE_1_20_2_CONSTRUCTOR = ReflectUtil.findConstructor(ClientboundSetScorePacket.class, methodClass, String.class, String.class, int.class); + SCORE_1_20_2_CONSTRUCTOR = ReflectUtil.findConstructor(SET_SCORE_PKT_CLASS, methodClass, String.class, String.class, int.class); } + + RESULT_UNWRAP_METHOD = ReflectUtil.findMethod(PacketAccessors.DATA_RESULT_CLASS, false, MethodType.methodType(Optional.class), "result"); + try { + JSON_OPS = PacketAccessors.JSON_OPS_CLASS.getField("INSTANCE").get(null); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + CODEC_PARSE = ReflectUtil.findMethod(PacketAccessors.CODEC_CLASS, false, MethodType.methodType(PacketAccessors.DATA_RESULT_CLASS, PacketAccessors.DYNAMIC_OPS_CLASS, Object.class), "parse"); } - public static final boolean IS_1_20_2_OR_ABOVE, IS_1_20_3_OR_ABOVE, IS_1_20_5_OR_ABOVE, IS_1_21_5_OR_ABOVE, IS_1_21_6_OR_ABOVE; + public static final MethodAccessor RESULT_UNWRAP_METHOD; + public static final MethodAccessor CODEC_PARSE; + public static final Object JSON_OPS; - public static final PacketConstructor OBJECTIVE_PACKET_CONSTRUCTOR = - ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class); - public static final FieldAccessor OBJECTIVE_NAME_FIELD = - ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, String.class); - public static final FieldAccessor OBJECTIVE_VALUE_FIELD = - ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, net.minecraft.network.chat.Component.class); - public static final FieldAccessor OBJECTIVE_RENDER_TYPE_FIELD = - ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, ObjectiveCriteria.RenderType.class); + public static final MethodAccessor CHAT_FORMATTING_GET_BY_CODE = + ReflectUtil.findMethod(CHAT_FORMATTING_CLASS, true, MethodType.methodType(CHAT_FORMATTING_CLASS, char.class), "getByCode", "a"); + + public static final Object NAME_TAG_VISIBILITY_ALWAYS = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "ALWAYS"); + public static final Object NAME_TAG_VISIBILITY_NEVER = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "NEVER"); + public static final Object NAME_TAG_VISIBILITY_HIDE_FOR_OTHER_TEAMS = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "HIDE_FOR_OTHER_TEAMS"); + public static final Object NAME_TAG_VISIBILITY_HIDE_FOR_OWN_TEAM = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "HIDE_FOR_OWN_TEAM"); + + public static final Object COLLISION_RULE_ALWAYS = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "ALWAYS"); + public static final Object COLLISION_RULE_NEVER = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "NEVER"); + public static final Object COLLISION_RULE_PUSH_OTHER_TEAMS = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "PUSH_OTHER_TEAMS"); + public static final Object COLLISION_RULE_PUSH_OWN_TEAM = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "PUSH_OWN_TEAM"); + + public static final Object RENDER_TYPE_INTEGER = ReflectUtil.getEnumInstance(OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS, "INTEGER"); + public static final Object RENDER_TYPE_HEARTS = ReflectUtil.getEnumInstance(OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS, "HEARTS"); + + public static final List DISPLAY_SLOT_VALUES = DISPLAY_SLOT_CLASS == null ? null : Arrays.asList(DISPLAY_SLOT_CLASS.getEnumConstants()); + + public static final ConstructorAccessor ADVENTURE_COMPONENT_CONSTRUCTOR = + ADVENTURE_COMPONENT_CLASS != null ? ReflectUtil.findOptionalConstructor(ADVENTURE_COMPONENT_CLASS, Component.class) : null; + + public static final PacketConstructor OBJECTIVE_PACKET_CONSTRUCTOR = + ReflectUtil.getEmptyConstructor(SET_OBJECTIVE_PKT_CLASS); + public static final FieldAccessor OBJECTIVE_NAME_FIELD = + ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, String.class); + public static final FieldAccessor OBJECTIVE_VALUE_FIELD = + ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, COMPONENT_CLASS); + public static final FieldAccessor OBJECTIVE_RENDER_TYPE_FIELD = + ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS); // Optional for 1.20.5+, NumberFormat for below - public static final FieldAccessor OBJECTIVE_NUMBER_FORMAT_FIELD; - public static final FieldAccessor OBJECTIVE_MODE_FIELD = - ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, int.class); + public static final FieldAccessor OBJECTIVE_NUMBER_FORMAT_FIELD; + public static final FieldAccessor OBJECTIVE_MODE_FIELD = + ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, int.class); - public static final ConstructorAccessor DISPLAY_1_20_1_CONSTRUCTOR = - ReflectUtil.findOptionalConstructor(ClientboundSetDisplayObjectivePacket.class, int.class, Objective.class); - public static final FieldAccessor DISPLAY_OBJECTIVE_NAME = - ReflectUtil.findField(ClientboundSetDisplayObjectivePacket.class, 0, String.class); + public static final ConstructorAccessor DISPLAY_1_20_2_CONSTRUCTOR = + DISPLAY_SLOT_CLASS != null ? ReflectUtil.findOptionalConstructor(SET_DISPLAY_OBJECTIVE_PKT_CLASS, DISPLAY_SLOT_CLASS, OBJECTIVE_CLASS) : null; + public static final ConstructorAccessor DISPLAY_1_20_1_CONSTRUCTOR = + ReflectUtil.findOptionalConstructor(SET_DISPLAY_OBJECTIVE_PKT_CLASS, int.class, OBJECTIVE_CLASS); + public static final FieldAccessor DISPLAY_OBJECTIVE_NAME = + ReflectUtil.findFieldUnchecked(SET_DISPLAY_OBJECTIVE_PKT_CLASS, 0, String.class); - public static final ConstructorAccessor SCORE_1_20_3_CONSTRUCTOR; + public static final ConstructorAccessor RESET_SCORE_CONSTRUCTOR; // 1.20.3+ + + public static final ConstructorAccessor SCORE_1_20_5_CONSTRUCTOR; + public static final ConstructorAccessor SCORE_1_20_3_CONSTRUCTOR; public static final Object SCORE_1_20_2_METHOD_CHANGE, SCORE_1_20_2_METHOD_REMOVE; - public static final ConstructorAccessor SCORE_1_20_2_CONSTRUCTOR; + public static final ConstructorAccessor SCORE_1_20_2_CONSTRUCTOR; - public static final ConstructorAccessor TEAM_PACKET_CONSTRUCTOR = - ReflectUtil.findConstructor(ClientboundSetPlayerTeamPacket.class, String.class, int.class, Optional.class, Collection.class); - public static final PacketConstructor PARAMETERS_CONSTRUCTOR = - ReflectUtil.getEmptyConstructor(ClientboundSetPlayerTeamPacket.Parameters.class); - public static final FieldAccessor DISPLAY_NAME_FIELD = - ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, net.minecraft.network.chat.Component.class); - public static final FieldAccessor PREFIX_FIELD = - ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, net.minecraft.network.chat.Component.class); - public static final FieldAccessor SUFFIX_FIELD = - ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 2, net.minecraft.network.chat.Component.class); + public static final ConstructorAccessor TEAM_PACKET_CONSTRUCTOR = + ReflectUtil.findConstructor(SET_PLAYER_TEAM_PKT_CLASS, String.class, int.class, Optional.class, Collection.class); + public static final PacketConstructor PARAMETERS_CONSTRUCTOR = + ReflectUtil.getEmptyConstructor(TEAM_PARAMETERS_PKT_CLASS); + public static final FieldAccessor DISPLAY_NAME_FIELD = + ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, COMPONENT_CLASS); + public static final FieldAccessor PREFIX_FIELD = + ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 1, COMPONENT_CLASS); + public static final FieldAccessor SUFFIX_FIELD = + ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 2, COMPONENT_CLASS); - public static final FieldAccessor NAME_TAG_VISIBILITY_FIELD_1_21_4; - public static final FieldAccessor COLLISION_RULE_FIELD_1_21_4; - public static final FieldAccessor NAME_TAG_VISIBILITY_FIELD_1_21_5; - public static final FieldAccessor COLLISION_RULE_FIELD_1_21_5; + public static final FieldAccessor NAME_TAG_VISIBILITY_FIELD_1_21_4; + public static final FieldAccessor COLLISION_RULE_FIELD_1_21_4; + public static final FieldAccessor NAME_TAG_VISIBILITY_FIELD_1_21_5; + public static final FieldAccessor COLLISION_RULE_FIELD_1_21_5; - public static final FieldAccessor COLOR_FIELD = - ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, ChatFormatting.class); - public static final FieldAccessor OPTIONS_FIELD = - ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, int.class); + public static final FieldAccessor COLOR_FIELD = + ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, CHAT_FORMATTING_CLASS); + public static final FieldAccessor OPTIONS_FIELD = + ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, int.class); private PacketAccessors() { } + + public static @NotNull Object fromAdventureComponent(@NotNull Component component) { + return Objects.requireNonNull(PacketAccessors.ADVENTURE_COMPONENT_CONSTRUCTOR).invoke(component); + } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java index 998ba39..1f590df 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java @@ -1,62 +1,38 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern; -import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.PaperObjectivePacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.SpigotObjectivePacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team.PaperTeamsPacketAdapterImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team.SpigotTeamsPacketAdapter; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.PacketUtil; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter; -import net.minecraft.network.protocol.Packet; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender> { - private boolean isNativeAdventure; - private final ComponentProvider componentProvider; - +@SuppressWarnings("unused") +public class PacketAdapterProviderImpl implements PacketAdapterProvider { public PacketAdapterProviderImpl() { - try { - Class.forName("io.papermc.paper.adventure.PaperAdventure"); - - // Hide from relocation checkers - String notRelocatedPackage = "net.ky".concat("ori.adventure.text"); - - // The native adventure optimisations only work when the adventure library isn't relocated - if (Component.class.getPackage().getName().equals(notRelocatedPackage)) { - isNativeAdventure = true; - } - } catch (ClassNotFoundException ignored) { - } - - this.componentProvider = new ComponentProviderImpl(isNativeAdventure); } @Override public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) { - return isNativeAdventure - ? new PaperObjectivePacketAdapter(this, componentProvider, objectiveName) - : new SpigotObjectivePacketAdapter(this, componentProvider, objectiveName); + return ModernComponentProvider.IS_NATIVE_ADVENTURE + ? new PaperObjectivePacketAdapter(objectiveName) + : new SpigotObjectivePacketAdapter(objectiveName); } @Override public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) { - return isNativeAdventure - ? new PaperTeamsPacketAdapterImpl(this, componentProvider, teamName) - : new SpigotTeamsPacketAdapter(this, componentProvider, teamName); + return ModernComponentProvider.IS_NATIVE_ADVENTURE + ? new PaperTeamsPacketAdapterImpl(teamName) + : new SpigotTeamsPacketAdapter(teamName); } @Override public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) { return LineRenderingStrategy.MODERN; } - - @Override - public void sendPacket(@NotNull Player player, @NotNull Packet packet) { - PacketUtil.sendPacket(player, packet); - } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java index 61ff7be..26ba669 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java @@ -2,20 +2,11 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.object import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot; import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; -import net.minecraft.network.chat.numbers.NumberFormat; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundResetScorePacket; -import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetScorePacket; -import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,14 +16,10 @@ import java.util.Objects; import java.util.Optional; public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketAdapter { - protected final PacketSender> sender; - protected final ComponentProvider componentProvider; protected final String objectiveName; - private ClientboundSetObjectivePacket removePacket; + private Object removePacket; - public AbstractObjectivePacketAdapter(@NotNull PacketSender> sender, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) { - this.sender = sender; - this.componentProvider = componentProvider; + public AbstractObjectivePacketAdapter(@NotNull String objectiveName) { this.objectiveName = objectiveName; } @@ -43,35 +30,37 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA @Override public void display(@NotNull Collection players, @NotNull ObjectiveDisplaySlot slot) { - sender.sendPacket(players, createDisplayPacket(slot)); + ModernPacketSender.INSTANCE.sendPacket(players, createDisplayPacket(slot)); } @Override public void remove(@NotNull Collection players) { if (removePacket == null) { - this.removePacket = ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class).invoke(); + this.removePacket = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke(); PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName); PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE); } - sender.sendPacket(players, removePacket); + ModernPacketSender.INSTANCE.sendPacket(players, removePacket); } @Override public void removeScore(@NotNull Collection players, @NotNull String entry) { - Packet packet; + Object packet; if (PacketAccessors.IS_1_20_3_OR_ABOVE) { - packet = new ClientboundResetScorePacket(entry, objectiveName); + packet = Objects.requireNonNull(PacketAccessors.RESET_SCORE_CONSTRUCTOR) + .invoke(entry, objectiveName); } else { packet = Objects.requireNonNull(PacketAccessors.SCORE_1_20_2_CONSTRUCTOR) .invoke(PacketAccessors.SCORE_1_20_2_METHOD_REMOVE, objectiveName, entry, 0); } - sender.sendPacket(players, packet); + ModernPacketSender.INSTANCE.sendPacket(players, packet); } - protected @NotNull ClientboundSetDisplayObjectivePacket createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) { - ClientboundSetDisplayObjectivePacket packet; + protected @NotNull Object createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) { + Object packet; if (PacketAccessors.IS_1_20_2_OR_ABOVE) { - packet = new ClientboundSetDisplayObjectivePacket(DisplaySlotProvider.toNms(displaySlot), null); + packet = Objects.requireNonNull(PacketAccessors.DISPLAY_1_20_2_CONSTRUCTOR) + .invoke(PacketAccessors.DISPLAY_SLOT_VALUES.get(ObjectiveConstants.displaySlotIndex(displaySlot)), null); } else { packet = Objects.requireNonNull(PacketAccessors.DISPLAY_1_20_1_CONSTRUCTOR) .invoke(ObjectiveConstants.displaySlotIndex(displaySlot), null); @@ -80,14 +69,15 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA return packet; } - protected @NotNull ClientboundSetScorePacket createScorePacket( + protected @NotNull Object createScorePacket( @NotNull String entry, int value, - @Nullable net.minecraft.network.chat.Component nmsDisplay, + @Nullable Object nmsDisplay, @Nullable Object numberFormat ) { if (PacketAccessors.IS_1_20_5_OR_ABOVE) { - return new ClientboundSetScorePacket(entry, objectiveName, value, Optional.ofNullable(nmsDisplay), Optional.ofNullable((NumberFormat) numberFormat)); + return Objects.requireNonNull(PacketAccessors.SCORE_1_20_5_CONSTRUCTOR) + .invoke(entry, objectiveName, value, Optional.ofNullable(nmsDisplay), Optional.ofNullable(numberFormat)); } else if (PacketAccessors.IS_1_20_3_OR_ABOVE) { return Objects.requireNonNull(PacketAccessors.SCORE_1_20_3_CONSTRUCTOR) .invoke(entry, objectiveName, value, nmsDisplay, numberFormat); @@ -97,13 +87,13 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA } } - protected @NotNull ClientboundSetObjectivePacket createObjectivePacket( + protected @NotNull Object createObjectivePacket( @NotNull PropertiesPacketType packetType, - @NotNull net.minecraft.network.chat.Component nmsValue, + @NotNull Object nmsValue, @NotNull ObjectiveRenderType renderType, @Nullable Object numberFormat ) { - ClientboundSetObjectivePacket packet = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke(); + Object packet = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke(); PacketAccessors.OBJECTIVE_MODE_FIELD.set(packet, ObjectiveConstants.mode(packetType)); PacketAccessors.OBJECTIVE_NAME_FIELD.set(packet, objectiveName); PacketAccessors.OBJECTIVE_VALUE_FIELD.set(packet, nmsValue); @@ -119,16 +109,16 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD.set(packet, value); } - ObjectiveCriteria.RenderType nmsRenderType; + Object nmsRenderType; switch (renderType) { case INTEGER: - nmsRenderType = ObjectiveCriteria.RenderType.INTEGER; + nmsRenderType = PacketAccessors.RENDER_TYPE_INTEGER; break; case HEARTS: - nmsRenderType = ObjectiveCriteria.RenderType.HEARTS; + nmsRenderType = PacketAccessors.RENDER_TYPE_HEARTS; break; default: - throw new IllegalStateException(); + throw new IllegalStateException("unknown render type " + renderType); } PacketAccessors.OBJECTIVE_RENDER_TYPE_FIELD.set(packet, nmsRenderType); diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/DisplaySlotProvider.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/DisplaySlotProvider.java deleted file mode 100644 index a3be9e9..0000000 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/DisplaySlotProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective; - -import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants; -import net.minecraft.world.scores.DisplaySlot; -import org.jetbrains.annotations.NotNull; - -// DisplaySlot was added in 1.20.2 so this has to be a separate class -public final class DisplaySlotProvider { - private static final DisplaySlot[] VALUES = DisplaySlot.values(); - - private DisplaySlotProvider() { - } - - public static @NotNull DisplaySlot toNms(@NotNull ObjectiveDisplaySlot slot) { - return VALUES[ObjectiveConstants.displaySlotIndex(slot)]; - } -} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java index e867a2b..1e7fbc1 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java @@ -1,15 +1,12 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective; -import io.papermc.paper.adventure.AdventureComponent; import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType; import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil; -import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket; -import net.minecraft.network.protocol.game.ClientboundSetScorePacket; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,16 +14,16 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter { - public PaperObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl packetAdapter, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) { - super(packetAdapter, componentProvider, objectiveName); + public PaperObjectivePacketAdapter(@NotNull String objectiveName) { + super(objectiveName); } @Override public void sendScore(@NotNull Collection players, @NotNull String entry, int value, @Nullable Component display, @Nullable ScoreFormat scoreFormat) { - net.minecraft.network.chat.Component nmsDisplay = display == null ? null : componentProvider.fromAdventure(display, null); - Object numberFormat = ScoreFormatConverter.convert(componentProvider, null, scoreFormat); - ClientboundSetScorePacket packet = createScorePacket(entry, value, nmsDisplay, numberFormat); - sender.sendPacket(players, packet); + Object nmsDisplay = display == null ? null : ModernComponentProvider.fromAdventure(display, null); + Object numberFormat = ScoreFormatConverter.convert(null, scoreFormat); + Object packet = createScorePacket(entry, value, nmsDisplay, numberFormat); + ModernPacketSender.INSTANCE.sendPacket(players, packet); } @Override @@ -37,9 +34,9 @@ public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter @NotNull ObjectiveRenderType renderType, @Nullable ScoreFormat scoreFormat ) { - AdventureComponent nmsValue = NativeAdventureUtil.fromAdventureComponent(value); - Object numberFormat = ScoreFormatConverter.convert(componentProvider, null, scoreFormat); - ClientboundSetObjectivePacket packet = createObjectivePacket(packetType, nmsValue, renderType, numberFormat); - sender.sendPacket(players, packet); + Object nmsValue = PacketAccessors.fromAdventureComponent(value); + Object numberFormat = ScoreFormatConverter.convert(null, scoreFormat); + Object packet = createObjectivePacket(packetType, nmsValue, renderType, numberFormat); + ModernPacketSender.INSTANCE.sendPacket(players, packet); } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java index 4162967..a19327d 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java @@ -1,60 +1,62 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective; import com.google.gson.JsonElement; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.JsonOps; import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; -import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.numbers.BlankFormat; -import net.minecraft.network.chat.numbers.FixedFormat; -import net.minecraft.network.chat.numbers.StyledFormat; -import org.jetbrains.annotations.NotNull; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ConstructorAccessor; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; import org.jetbrains.annotations.Nullable; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.util.Locale; import java.util.Optional; import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson; public final class ScoreFormatConverter { - private static final MethodHandle RESULT_UNWRAP_METHOD; + private static final Object STYLE_CODEC; + private static final Object BLANK; + private static final ConstructorAccessor STYLED_CONSTRUCTOR; + private static final ConstructorAccessor FIXED_CONSTRUCTOR; static { - try { - RESULT_UNWRAP_METHOD = MethodHandles.lookup().findVirtual(DataResult.class, "result", MethodType.methodType(Optional.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - throw new RuntimeException(e); + if (PacketAccessors.IS_1_20_3_OR_ABOVE) { + STYLE_CODEC = ReflectUtil.findFieldUnchecked(PacketAccessors.STYLE_SERIALIZER_CLASS, 0, PacketAccessors.CODEC_CLASS, true).get(null); + + Class blankFormatClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.numbers.BlankFormat"); + BLANK = ReflectUtil.findFieldUnchecked(blankFormatClass, 0, blankFormatClass, true).get(null); + + Class styledFormatClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.numbers.StyledFormat"); + STYLED_CONSTRUCTOR = ReflectUtil.findConstructor(styledFormatClass, PacketAccessors.STYLE_CLASS); + + Class fixedFormatClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.numbers.FixedFormat"); + FIXED_CONSTRUCTOR = ReflectUtil.findConstructor(fixedFormatClass, PacketAccessors.COMPONENT_CLASS); + } else { + STYLE_CODEC = null; + BLANK = null; + STYLED_CONSTRUCTOR = null; + FIXED_CONSTRUCTOR = null; } } private ScoreFormatConverter() { } - public static @Nullable Object convert(@NotNull ComponentProvider componentProvider, @Nullable Locale locale, @Nullable ScoreFormat format) { + public static @Nullable Object convert(@Nullable Locale locale, @Nullable ScoreFormat format) { if (format == null || !PacketAccessors.IS_1_20_3_OR_ABOVE) { return null; } if (format == ScoreFormat.blank()) { - return BlankFormat.INSTANCE; + return BLANK; } else if (format instanceof ScoreFormat.Styled) { JsonElement json = gson().serializer().toJsonTree(((ScoreFormat.Styled) format).style()); - Object result = Style.Serializer.CODEC.parse(JsonOps.INSTANCE, json); - Style style; - try { - //noinspection unchecked,rawtypes - style = (Style) ((Optional) RESULT_UNWRAP_METHOD.invokeExact((DataResult) result)).orElseThrow(RuntimeException::new); - } catch (Throwable e) { - throw new RuntimeException(e); - } - return new StyledFormat(style); + Object result = PacketAccessors.CODEC_PARSE.invoke(STYLE_CODEC, PacketAccessors.JSON_OPS, json); + //noinspection OptionalGetWithoutIsPresent + Object style = ((Optional) PacketAccessors.RESULT_UNWRAP_METHOD.invoke(result)).get(); + return STYLED_CONSTRUCTOR.invoke(style); } else if (format instanceof ScoreFormat.Fixed) { - return new FixedFormat(componentProvider.fromAdventure(((ScoreFormat.Fixed) format).content(), locale)); + return FIXED_CONSTRUCTOR.invoke(ModernComponentProvider.fromAdventure(((ScoreFormat.Fixed) format).content(), locale)); } else { throw new IllegalArgumentException("Invalid score format: " + format); } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java index 1e6f804..b88fd1f 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java @@ -4,8 +4,8 @@ import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType; import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -14,15 +14,15 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; public class SpigotObjectivePacketAdapter extends AbstractObjectivePacketAdapter { - public SpigotObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl packetAdapter, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) { - super(packetAdapter, componentProvider, objectiveName); + public SpigotObjectivePacketAdapter(@NotNull String objectiveName) { + super(objectiveName); } @Override public void sendScore(@NotNull Collection players, @NotNull String entry, int value, @Nullable Component display, @Nullable ScoreFormat scoreFormat) { - LocalePacketUtil.sendLocalePackets(sender, players, locale -> { - net.minecraft.network.chat.Component nmsDisplay = display == null ? null : componentProvider.fromAdventure(display, locale); - Object numberFormat = ScoreFormatConverter.convert(componentProvider, locale, scoreFormat); + LocalePacketUtil.sendLocalePackets(ModernPacketSender.INSTANCE, players, locale -> { + Object nmsDisplay = display == null ? null : ModernComponentProvider.fromAdventure(display, locale); + Object numberFormat = ScoreFormatConverter.convert(locale, scoreFormat); return createScorePacket(entry, value, nmsDisplay, numberFormat); }); } @@ -36,11 +36,11 @@ public class SpigotObjectivePacketAdapter extends AbstractObjectivePacketAdapter @Nullable ScoreFormat scoreFormat ) { LocalePacketUtil.sendLocalePackets( - sender, + ModernPacketSender.INSTANCE, players, locale -> { - Object numberFormat = ScoreFormatConverter.convert(componentProvider, locale, scoreFormat); - return createObjectivePacket(packetType, componentProvider.fromAdventure(value, locale), renderType, numberFormat); + Object numberFormat = ScoreFormatConverter.convert(locale, scoreFormat); + return createObjectivePacket(packetType, ModernComponentProvider.fromAdventure(value, locale), renderType, numberFormat); } ); } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java index 281a16a..cc42cd3 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java @@ -3,18 +3,12 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team; import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil; import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter; -import net.minecraft.ChatFormatting; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; -import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters; -import net.minecraft.world.scores.Team; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,21 +17,17 @@ import org.jetbrains.annotations.UnknownNullability; import java.util.*; public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapter { - protected final PacketSender> sender; - protected final ComponentProvider componentProvider; protected final String teamName; - private ClientboundSetPlayerTeamPacket removePacket; + private Object removePacket; - public AbstractTeamsPacketAdapterImpl(@NotNull PacketSender> sender, @NotNull ComponentProvider componentProvider, @NotNull String teamName) { - this.sender = sender; - this.componentProvider = componentProvider; + public AbstractTeamsPacketAdapterImpl(@NotNull String teamName) { this.teamName = teamName; } - public static ClientboundSetPlayerTeamPacket createTeamsPacket( + public static Object createTeamsPacket( int method, @NotNull String name, - @Nullable Parameters parameters, + @Nullable Object parameters, @Nullable Collection entries ) { return PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke( @@ -53,7 +43,7 @@ public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapt if (removePacket == null) { removePacket = createTeamsPacket(TeamConstants.MODE_REMOVE, teamName, null, null); } - sender.sendPacket(players, removePacket); + ModernPacketSender.INSTANCE.sendPacket(players, removePacket); } public abstract class TeamDisplayPacketAdapterImpl implements TeamDisplayPacketAdapter { @@ -65,16 +55,52 @@ public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapt @Override public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection players, @NotNull Collection entries) { - sender.sendPacket(players, createTeamsPacket(TeamConstants.mode(packetType), teamName, null, entries)); + ModernPacketSender.INSTANCE.sendPacket(players, createTeamsPacket(TeamConstants.mode(packetType), teamName, null, entries)); } - protected void fillParameters(@NotNull Parameters parameters, @UnknownNullability Locale locale) { + protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) { if (PacketAccessors.IS_1_21_5_OR_ABOVE) { + Object nameTagVisibility; + switch (properties.nameTagVisibility()){ + case NEVER: + nameTagVisibility = PacketAccessors.NAME_TAG_VISIBILITY_NEVER; + break; + case ALWAYS: + nameTagVisibility = PacketAccessors.NAME_TAG_VISIBILITY_ALWAYS; + break; + case HIDE_FOR_OTHER_TEAMS: + nameTagVisibility = PacketAccessors.NAME_TAG_VISIBILITY_HIDE_FOR_OTHER_TEAMS; + break; + case HIDE_FOR_OWN_TEAM: + nameTagVisibility = PacketAccessors.NAME_TAG_VISIBILITY_HIDE_FOR_OWN_TEAM; + break; + default: + throw new IllegalStateException("unknown name tag visibility " + properties.nameTagVisibility().name()); + } + Objects.requireNonNull(PacketAccessors.NAME_TAG_VISIBILITY_FIELD_1_21_5) - .set(parameters, Team.Visibility.valueOf(properties.nameTagVisibility().name())); + .set(parameters, nameTagVisibility); + + Object collisionRule; + switch (properties.collisionRule()){ + case NEVER: + collisionRule = PacketAccessors.COLLISION_RULE_NEVER; + break; + case ALWAYS: + collisionRule = PacketAccessors.COLLISION_RULE_ALWAYS; + break; + case PUSH_OTHER_TEAMS: + collisionRule = PacketAccessors.COLLISION_RULE_PUSH_OTHER_TEAMS; + break; + case PUSH_OWN_TEAM: + collisionRule = PacketAccessors.COLLISION_RULE_PUSH_OWN_TEAM; + break; + default: + throw new IllegalStateException("unknown collision rule " + properties.collisionRule().name()); + } Objects.requireNonNull(PacketAccessors.COLLISION_RULE_FIELD_1_21_5) - .set(parameters, Team.CollisionRule.valueOf(properties.collisionRule().name())); + .set(parameters, collisionRule); } else { Objects.requireNonNull(PacketAccessors.NAME_TAG_VISIBILITY_FIELD_1_21_4) .set(parameters, properties.nameTagVisibility().key()); @@ -84,7 +110,7 @@ public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapt } char legacyChar = LegacyFormatUtil.getChar(properties.playerColor()); - PacketAccessors.COLOR_FIELD.set(parameters, ChatFormatting.getByCode(legacyChar)); + PacketAccessors.COLOR_FIELD.set(parameters, PacketAccessors.CHAT_FORMATTING_GET_BY_CODE.invoke(legacyChar)); int options = properties.packOptions(); PacketAccessors.OPTIONS_FIELD.set(parameters, options); diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java index 79fb3b7..511d0d7 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java @@ -3,15 +3,11 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team; import com.google.common.collect.ImmutableList; import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnknownNullability; @@ -20,8 +16,8 @@ import java.util.Collection; import java.util.Locale; public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl { - public PaperTeamsPacketAdapterImpl(@NotNull PacketSender> sender, @NotNull ComponentProvider componentProvider, @NotNull String teamName) { - super(sender, componentProvider, teamName); + public PaperTeamsPacketAdapterImpl(@NotNull String teamName) { + super(teamName); } @Override @@ -30,9 +26,9 @@ public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl } private class TeamDisplayPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl.TeamDisplayPacketAdapterImpl { - private final ClientboundSetPlayerTeamPacket.Parameters parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke(); - private ClientboundSetPlayerTeamPacket createPacket = null; - private ClientboundSetPlayerTeamPacket updatePacket = null; + private final Object parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke(); + private Object createPacket = null; + private Object updatePacket = null; private Component displayName, prefix, suffix; public TeamDisplayPacketAdapterImpl(@NotNull ImmutableTeamProperties properties) { @@ -56,32 +52,32 @@ public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl switch (packetType) { case CREATE: - sender.sendPacket(players, createPacket); + ModernPacketSender.INSTANCE.sendPacket(players, createPacket); break; case UPDATE: - sender.sendPacket(players, updatePacket); + ModernPacketSender.INSTANCE.sendPacket(players, updatePacket); break; } } @Override - protected void fillParameters(ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters, @UnknownNullability Locale locale) { + protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) { super.fillParameters(parameters, locale); if (properties.displayName() != displayName) { - net.minecraft.network.chat.Component vanilla = NativeAdventureUtil.fromAdventureComponent(properties.displayName()); + Object vanilla = PacketAccessors.fromAdventureComponent(properties.displayName()); PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, vanilla); displayName = properties.displayName(); } if (properties.prefix() != prefix) { - net.minecraft.network.chat.Component vanilla = NativeAdventureUtil.fromAdventureComponent(properties.prefix()); + Object vanilla = PacketAccessors.fromAdventureComponent(properties.prefix()); PacketAccessors.PREFIX_FIELD.set(parameters, vanilla); prefix = properties.prefix(); } if (properties.suffix() != suffix) { - net.minecraft.network.chat.Component vanilla = NativeAdventureUtil.fromAdventureComponent(properties.suffix()); + Object vanilla = PacketAccessors.fromAdventureComponent(properties.suffix()); PacketAccessors.SUFFIX_FIELD.set(parameters, vanilla); suffix = properties.suffix(); } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java index 146ba40..18b65a3 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java @@ -3,15 +3,13 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team; import com.google.common.collect.ImmutableList; import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnknownNullability; @@ -20,8 +18,8 @@ import java.util.Collection; import java.util.Locale; public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl { - public SpigotTeamsPacketAdapter(@NotNull PacketSender> sender, @NotNull ComponentProvider componentProvider, @NotNull String teamName) { - super(sender, componentProvider, teamName); + public SpigotTeamsPacketAdapter(@NotNull String teamName) { + super(teamName); } @Override @@ -38,10 +36,10 @@ public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl { public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection players) { Collection entries = ImmutableList.copyOf(properties.syncedEntries()); LocalePacketUtil.sendLocalePackets( - sender, + ModernPacketSender.INSTANCE, players, locale -> { - ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke(); + Object parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke(); fillParameters(parameters, locale); return createTeamsPacket(TeamConstants.mode(packetType), teamName, parameters, entries); } @@ -49,12 +47,12 @@ public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl { } @Override - protected void fillParameters(ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters, @UnknownNullability Locale locale) { + protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) { super.fillParameters(parameters, locale); - PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, componentProvider.fromAdventure(properties.displayName(), locale)); - PacketAccessors.PREFIX_FIELD.set(parameters, componentProvider.fromAdventure(properties.prefix(), locale)); - PacketAccessors.SUFFIX_FIELD.set(parameters, componentProvider.fromAdventure(properties.suffix(), locale)); + PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, ModernComponentProvider.fromAdventure(properties.displayName(), locale)); + PacketAccessors.PREFIX_FIELD.set(parameters, ModernComponentProvider.fromAdventure(properties.prefix(), locale)); + PacketAccessors.SUFFIX_FIELD.set(parameters, ModernComponentProvider.fromAdventure(properties.suffix(), locale)); } } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java new file mode 100644 index 0000000..be030e3 --- /dev/null +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java @@ -0,0 +1,118 @@ +package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util; + +import com.google.gson.JsonElement; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.translation.GlobalTranslator; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MinecraftClasses; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Locale; +import java.util.Optional; + +import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson; + +public final class ModernComponentProvider { + public static final boolean IS_NATIVE_ADVENTURE; + + private static final Object MINECRAFT_REGISTRY; + private static final Object CODEC; + + private static final MethodHandle FROM_JSON_METHOD; + + private ModernComponentProvider() { + } + + static { + // Hide from relocation checkers + String notRelocatedPackage = "net.ky".concat("ori.adventure.text"); + + // The native adventure optimisations only work when the adventure library isn't relocated + IS_NATIVE_ADVENTURE = PacketAccessors.ADVENTURE_COMPONENT_CLASS != null && Component.class.getPackage().getName().equals(notRelocatedPackage); + + if (PacketAccessors.IS_1_20_5_OR_ABOVE) { + Class craftRegistry; + try { + craftRegistry = Class.forName(MinecraftClasses.craftBukkit("CraftRegistry")); + } catch (ClassNotFoundException e) { + throw new ExceptionInInitializerError(e); + } + + try { + Method method = craftRegistry.getMethod("getMinecraftRegistry"); + MINECRAFT_REGISTRY = method.invoke(null); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } else { + MINECRAFT_REGISTRY = null; + } + + if (PacketAccessors.IS_1_21_6_OR_ABOVE) { + CODEC = ReflectUtil.findFieldUnchecked(PacketAccessors.COMPONENT_SERIALIZATION_CLASS, 0, PacketAccessors.CODEC_CLASS, true).get(null); + FROM_JSON_METHOD = null; + } else { + Class serializerClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Component$Serializer", "net.minecraft.network.chat.IChatBaseComponent$ChatSerializer"); + MethodHandle handle = null; + for (Method method : serializerClass.getMethods()) { + if (method.getReturnType() == PacketAccessors.MUTABLE_COMPONENT_CLASS && + method.getParameterCount() >= 1 && + method.getParameterCount() <= 2 && + method.getParameterTypes()[0] == JsonElement.class + ) { + try { + handle = MethodHandles.lookup().unreflect(method); + break; + } catch (IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + } + + if (handle == null) { + throw new ExceptionInInitializerError("failed to find chat component fromJson method"); + } + + FROM_JSON_METHOD = handle; + CODEC = null; + } + } + + public static @NotNull Object fromAdventure(@NotNull Component adventure, @Nullable Locale locale) { + if (IS_NATIVE_ADVENTURE) { + return PacketAccessors.fromAdventureComponent(adventure); + } + + Component translated = adventure; + if (locale != null) { + translated = GlobalTranslator.render(adventure, locale); + } + JsonElement json = gson().serializeToTree(translated); + + if (FROM_JSON_METHOD == null) { + // 1.21.6+ + Object result = PacketAccessors.CODEC_PARSE.invoke(CODEC, PacketAccessors.JSON_OPS, json); + //noinspection OptionalGetWithoutIsPresent + return ((Optional) PacketAccessors.RESULT_UNWRAP_METHOD.invoke(result)).get(); + } + + Object[] args; + if (MINECRAFT_REGISTRY != null) { + args = new Object[]{json, MINECRAFT_REGISTRY}; + } else { + args = new Object[]{json}; + } + + try { + return FROM_JSON_METHOD.invokeWithArguments(args); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/PacketUtil.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java similarity index 62% rename from versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/PacketUtil.java rename to versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java index dd68d19..1320063 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/PacketUtil.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java @@ -1,9 +1,8 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util; -import net.minecraft.network.protocol.Packet; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import org.bukkit.Bukkit; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MinecraftClasses; import org.bukkit.entity.Player; import java.lang.invoke.MethodHandle; @@ -11,26 +10,26 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; -public final class PacketUtil { +public final class ModernPacketSender implements PacketSender { + public static final ModernPacketSender INSTANCE = new ModernPacketSender(); + private static final MethodHandle GET_HANDLE; private static final MethodHandle PLAYER_CONNECTION; private static final MethodHandle SEND_PACKET; - private PacketUtil() { + private ModernPacketSender() { } static { - String cbPackage = Bukkit.getServer().getClass().getPackage().getName(); - Class craftPlayer; try { - craftPlayer = Class.forName(cbPackage + ".entity.CraftPlayer"); + craftPlayer = Class.forName(MinecraftClasses.craftBukkit("entity.CraftPlayer")); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - MethodType methodType = MethodType.methodType(ServerPlayer.class); + MethodType methodType = MethodType.methodType(PacketAccessors.SERVER_PLAYER_CLASS); try { GET_HANDLE = lookup.findVirtual(craftPlayer, "getHandle", methodType); } catch (NoSuchMethodException | IllegalAccessException e) { @@ -38,8 +37,8 @@ public final class PacketUtil { } MethodHandle playerConnection = null; - for (Field field : ServerPlayer.class.getFields()) { - if (field.getType() == ServerGamePacketListenerImpl.class) { + for (Field field : PacketAccessors.SERVER_PLAYER_CLASS.getFields()) { + if (field.getType() == PacketAccessors.PLAYER_CONNECTION_CLASS) { try { playerConnection = lookup.unreflectGetter(field); } catch (IllegalAccessException e) { @@ -53,13 +52,13 @@ public final class PacketUtil { } PLAYER_CONNECTION = playerConnection; - MethodType sendMethodType = MethodType.methodType(void.class, Packet.class); + MethodType sendMethodType = MethodType.methodType(void.class, PacketAccessors.PKT_CLASS); MethodHandle sendPacket = null; String[] sendPacketNames = {"a", "sendPacket", "b", "send"}; for (String name : sendPacketNames) { try { - sendPacket = lookup.findVirtual(ServerGamePacketListenerImpl.class, name, sendMethodType); + sendPacket = lookup.findVirtual(PacketAccessors.PLAYER_CONNECTION_CLASS, name, sendMethodType); } catch (NoSuchMethodException ignored) { } catch (IllegalAccessException e) { throw new ExceptionInInitializerError(e); @@ -73,11 +72,12 @@ public final class PacketUtil { SEND_PACKET = sendPacket; } - public static void sendPacket(Player player, Packet packet) { + @Override + public void sendPacket(Player player, Object packet) { try { - ServerPlayer handle = (ServerPlayer) GET_HANDLE.invoke(player); - ServerGamePacketListenerImpl connection = (ServerGamePacketListenerImpl) PLAYER_CONNECTION.invoke(handle); - SEND_PACKET.invoke(connection, packet); + Object handle = GET_HANDLE.invoke(player); + Object connection = PLAYER_CONNECTION.invoke(handle); + SEND_PACKET.invoke(connection, packet); } catch (Throwable e) { throw new IllegalStateException("couldn't send packet to player", e); } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/NativeAdventureUtil.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/NativeAdventureUtil.java deleted file mode 100644 index 39857b2..0000000 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/NativeAdventureUtil.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util; - -import io.papermc.paper.adventure.AdventureComponent; -import net.kyori.adventure.text.Component; -import org.jetbrains.annotations.NotNull; - -public final class NativeAdventureUtil { - private NativeAdventureUtil() { - } - - public static @NotNull AdventureComponent fromAdventureComponent(@NotNull Component component) { - return new AdventureComponent(component); - } -} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/RegistryUtil.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/RegistryUtil.java deleted file mode 100644 index 62df57f..0000000 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/RegistryUtil.java +++ /dev/null @@ -1,31 +0,0 @@ -package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util; - -import net.minecraft.core.RegistryAccess; -import org.bukkit.Bukkit; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public final class RegistryUtil { - public static final RegistryAccess MINECRAFT_REGISTRY; - - static { - String cbPackage = Bukkit.getServer().getClass().getPackage().getName(); - Class craftRegistry; - try { - craftRegistry = Class.forName(cbPackage + ".CraftRegistry"); - } catch (ClassNotFoundException e) { - throw new ExceptionInInitializerError(e); - } - - try { - Method method = craftRegistry.getDeclaredMethod("getMinecraftRegistry"); - MINECRAFT_REGISTRY = (RegistryAccess) method.invoke(null); - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { - throw new ExceptionInInitializerError(e); - } - } - - private RegistryUtil() { - } -} diff --git a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/FieldAccessor.java b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/FieldAccessor.java index 84a563b..6f49fa0 100644 --- a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/FieldAccessor.java +++ b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/FieldAccessor.java @@ -1,18 +1,35 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; import java.lang.invoke.MethodHandle; public class FieldAccessor { - private final MethodHandle setter; + private final MethodHandle getter, setter; - public FieldAccessor(@NotNull MethodHandle setter) { + public FieldAccessor(@NotNull MethodHandle getter, @Nullable MethodHandle setter) { + this.getter = getter; this.setter = setter; } - public void set(@NotNull T instance, @UnknownNullability V value) { + public Object get(@Nullable T instance) { + try { + if (instance == null) { + return getter.invoke(); + } else { + return getter.invoke(instance); + } + } catch (Throwable e) { + throw new IllegalStateException("couldn't set value of field", e); + } + } + + public void set(T instance, @UnknownNullability V value) { + if (this.setter == null) { + throw new IllegalStateException("cannot set static final field"); + } try { setter.invokeExact(instance, value); } catch (Throwable e) { diff --git a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/MethodAccessor.java b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/MethodAccessor.java new file mode 100644 index 0000000..343f682 --- /dev/null +++ b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/MethodAccessor.java @@ -0,0 +1,22 @@ +package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnknownNullability; + +import java.lang.invoke.MethodHandle; + +public class MethodAccessor { + private final MethodHandle handle; + + public MethodAccessor(@NotNull MethodHandle handle) { + this.handle = handle; + } + + public Object invoke(@UnknownNullability Object... args) { + try { + return handle.invokeExact(args); + } catch (Throwable e) { + throw new IllegalStateException("couldn't set value of field", e); + } + } +} diff --git a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/ReflectUtil.java b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/ReflectUtil.java index 34e8700..d94dba3 100644 --- a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/ReflectUtil.java +++ b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/util/reflect/ReflectUtil.java @@ -77,27 +77,28 @@ public class ReflectUtil { try { //noinspection unchecked,rawtypes return Enum.valueOf((Class) clazz, name); - } catch (IllegalArgumentException ignored){ + } catch (IllegalArgumentException ignored) { } } throw new IllegalStateException("Enum " + clazz.getName() + " instance with names either of " + String.join(",", names) + " not found"); } - public static @NotNull FieldAccessor findField(@NotNull Class clazz, int index, @NotNull Class valueClass) { - return findFieldUnchecked(clazz, index, valueClass); + public static @NotNull FieldAccessor findFieldUnchecked(@NotNull Class clazz, int index, @NotNull Class valueClass) { + return findFieldUnchecked(clazz, index, valueClass, false); } - public static @NotNull FieldAccessor findFieldUnchecked(@NotNull Class clazz, int index, @NotNull Class valueClass) { + public static @NotNull FieldAccessor findFieldUnchecked(@NotNull Class clazz, int index, @NotNull Class valueClass, boolean isStatic) { int i = 0; for (Field field : clazz.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != valueClass) { + if (Modifier.isStatic(field.getModifiers()) != isStatic || field.getType() != valueClass) { continue; } if (i == index) { try { - MethodHandle setter = LOOKUP.unreflectSetter(field); - return new FieldAccessor<>(setter.asType(VIRTUAL_FIELD_SETTER)); + MethodHandle getter = LOOKUP.unreflectGetter(field); + MethodHandle setter = !isStatic || !Modifier.isFinal(field.getModifiers()) ? LOOKUP.unreflectSetter(field).asType(VIRTUAL_FIELD_SETTER) : null; + return new FieldAccessor<>(getter, setter); } catch (IllegalAccessException e) { throw new RuntimeException("failed to unreflect field setter", e); } @@ -152,6 +153,35 @@ public class ReflectUtil { }; } + public static @NotNull MethodAccessor findMethod(@NotNull Class clazz, boolean isStatic, @NotNull MethodType methodType, @NotNull String... names) { + MethodAccessor accessor = findOptionalMethod(clazz, isStatic, methodType, names); + if (accessor == null) { + throw new RuntimeException("Method " + String.join(",", names) + " for class " + clazz.getName() + " not found"); + } + return accessor; + } + + public static @Nullable MethodAccessor findOptionalMethod(@NotNull Class clazz, boolean isStatic, @NotNull MethodType methodType, @NotNull String... names) { + if (names.length == 0) { + throw new IllegalArgumentException("no method names passed"); + } + for (String name : names) { + MethodHandle handle; + try { + if (isStatic) { + handle = LOOKUP.findStatic(clazz, name, methodType); + } else { + handle = LOOKUP.findVirtual(clazz, name, methodType); + } + return new MethodAccessor(convertToGeneric(handle)); + } catch (NoSuchMethodException ignored) { + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + return null; + } + private static @NotNull MethodHandle convertToGeneric(@NotNull MethodHandle handle) { MethodHandle target = handle.asFixedArity(); MethodType methodType = MethodType.genericMethodType(0, true);