Merge pull request #121 from MegavexNetwork/modern-reflect-v2

Remove paperweight
This commit is contained in:
vytskalt authored and GitHub committed 2026-02-06 22:01:21 +02:00
commit 97e027c2d3
42 files changed
+1190 -458

No files matched your search

+1 -1
View File
@@ -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"
+3
View File
@@ -8,3 +8,6 @@ build/
.project
.settings/
bin/
test-env/servers
test-env/cache
-1
View File
@@ -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 {
Generated
+4 -4
View File
@@ -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"
}
+22 -4
View File
@@ -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
];
};
});
-2
View File
@@ -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" ]
@@ -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) {
+1
View File
@@ -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")
+51
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
settings:
allow-end: false
spawn-limits:
monsters: 0
animals: 0
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -x
rm -r servers
rm -r cache
Executable
+147
View File
@@ -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"
+13
View File
@@ -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;
}
Executable
+30
View File
@@ -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"
+16
View File
@@ -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)
}
@@ -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<Module> 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;
}
}
@@ -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;
}
}
@@ -0,0 +1,9 @@
package net.megavex.scoreboardlibrary.testplugin.module;
public interface Module {
default void onEnable() {
}
default void onDisable() {
}
}
@@ -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());
}
}
@@ -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());
}
}
@@ -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<NamedTextColor> 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()));
}
}
@@ -0,0 +1,4 @@
name: scoreboard-library-test
version: 0.1.0
main: net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin
api-version: 1.13
+6 -19
View File
@@ -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<MavenPublication>("maven") {
// Kept for backwards compatibility
artifact(tasks.jar) {
classifier = "mojmap"
}
artifact(tasks.reobfJar)
artifact(tasks.jar)
artifact(tasks.javadocJar)
artifact(tasks.sourcesJar)
}
@@ -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);
}
@@ -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);
}
}
}
@@ -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<ClientboundSetObjectivePacket> OBJECTIVE_PACKET_CONSTRUCTOR =
ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, String.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, net.minecraft.network.chat.Component> OBJECTIVE_VALUE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, ObjectiveCriteria.RenderType> 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<Object> 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<Object, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, String.class);
public static final FieldAccessor<Object, Object> OBJECTIVE_VALUE_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, COMPONENT_CLASS);
public static final FieldAccessor<Object, Object> OBJECTIVE_RENDER_TYPE_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS);
// Optional<NumberFormat> for 1.20.5+, NumberFormat for below
public static final FieldAccessor<ClientboundSetObjectivePacket, Object> OBJECTIVE_NUMBER_FORMAT_FIELD;
public static final FieldAccessor<ClientboundSetObjectivePacket, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, int.class);
public static final FieldAccessor<Object, Object> OBJECTIVE_NUMBER_FORMAT_FIELD;
public static final FieldAccessor<Object, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, int.class);
public static final ConstructorAccessor<ClientboundSetDisplayObjectivePacket> DISPLAY_1_20_1_CONSTRUCTOR =
ReflectUtil.findOptionalConstructor(ClientboundSetDisplayObjectivePacket.class, int.class, Objective.class);
public static final FieldAccessor<ClientboundSetDisplayObjectivePacket, String> 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<Object, String> DISPLAY_OBJECTIVE_NAME =
ReflectUtil.findFieldUnchecked(SET_DISPLAY_OBJECTIVE_PKT_CLASS, 0, String.class);
public static final ConstructorAccessor<ClientboundSetScorePacket> 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<ClientboundSetScorePacket> SCORE_1_20_2_CONSTRUCTOR;
public static final ConstructorAccessor<?> SCORE_1_20_2_CONSTRUCTOR;
public static final ConstructorAccessor<ClientboundSetPlayerTeamPacket> TEAM_PACKET_CONSTRUCTOR =
ReflectUtil.findConstructor(ClientboundSetPlayerTeamPacket.class, String.class, int.class, Optional.class, Collection.class);
public static final PacketConstructor<ClientboundSetPlayerTeamPacket.Parameters> PARAMETERS_CONSTRUCTOR =
ReflectUtil.getEmptyConstructor(ClientboundSetPlayerTeamPacket.Parameters.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Component> DISPLAY_NAME_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, net.minecraft.network.chat.Component> PREFIX_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, net.minecraft.network.chat.Component> 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<Object, Object> DISPLAY_NAME_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, COMPONENT_CLASS);
public static final FieldAccessor<Object, Object> PREFIX_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 1, COMPONENT_CLASS);
public static final FieldAccessor<Object, Object> SUFFIX_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 2, COMPONENT_CLASS);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> NAME_TAG_VISIBILITY_FIELD_1_21_4;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> COLLISION_RULE_FIELD_1_21_4;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Team.Visibility> NAME_TAG_VISIBILITY_FIELD_1_21_5;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Team.CollisionRule> COLLISION_RULE_FIELD_1_21_5;
public static final FieldAccessor<Object, String> NAME_TAG_VISIBILITY_FIELD_1_21_4;
public static final FieldAccessor<Object, String> COLLISION_RULE_FIELD_1_21_4;
public static final FieldAccessor<Object, Object> NAME_TAG_VISIBILITY_FIELD_1_21_5;
public static final FieldAccessor<Object, Object> COLLISION_RULE_FIELD_1_21_5;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, ChatFormatting> COLOR_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, ChatFormatting.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Integer> OPTIONS_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, int.class);
public static final FieldAccessor<Object, Object> COLOR_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, CHAT_FORMATTING_CLASS);
public static final FieldAccessor<Object, Integer> 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);
}
}
@@ -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<Packet<?>> {
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);
}
}
@@ -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<Packet<?>> sender;
protected final ComponentProvider componentProvider;
protected final String objectiveName;
private ClientboundSetObjectivePacket removePacket;
private Object removePacket;
public AbstractObjectivePacketAdapter(@NotNull PacketSender<Packet<?>> 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<Player> players, @NotNull ObjectiveDisplaySlot slot) {
sender.sendPacket(players, createDisplayPacket(slot));
ModernPacketSender.INSTANCE.sendPacket(players, createDisplayPacket(slot));
}
@Override
public void remove(@NotNull Collection<Player> 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<Player> 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);
@@ -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)];
}
}
@@ -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<Player> 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);
}
}
@@ -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);
}
@@ -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<Player> 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);
}
);
}
@@ -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<Packet<?>> sender;
protected final ComponentProvider componentProvider;
protected final String teamName;
private ClientboundSetPlayerTeamPacket removePacket;
private Object removePacket;
public AbstractTeamsPacketAdapterImpl(@NotNull PacketSender<Packet<?>> 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<String> 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<Player> players, @NotNull Collection<String> 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);
@@ -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<Packet<?>> 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<Component> 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();
}
@@ -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<Packet<?>> 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<Player> players) {
Collection<String> 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));
}
}
}
@@ -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);
}
}
}
@@ -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<Object> {
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);
}
@@ -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);
}
}
@@ -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() {
}
}
@@ -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<T, V> {
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) {
@@ -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);
}
}
}
@@ -77,27 +77,28 @@ public class ReflectUtil {
try {
//noinspection unchecked,rawtypes
return Enum.valueOf((Class<? extends Enum>) 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 <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, int index, @NotNull Class<V> valueClass) {
return findFieldUnchecked(clazz, index, valueClass);
public static <T, V> @NotNull FieldAccessor<T, V> findFieldUnchecked(@NotNull Class<?> clazz, int index, @NotNull Class<?> valueClass) {
return findFieldUnchecked(clazz, index, valueClass, false);
}
public static <T, V> @NotNull FieldAccessor<T, V> findFieldUnchecked(@NotNull Class<?> clazz, int index, @NotNull Class<?> valueClass) {
public static <T, V> @NotNull FieldAccessor<T, V> 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 <T> @Nullable MethodAccessor findOptionalMethod(@NotNull Class<T> 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);