Skip to content

Commit

Permalink
Merge pull request #59 from Nixuge/main
Browse files Browse the repository at this point in the history
Legacy Packet Adapter
  • Loading branch information
vytskalt authored Oct 13, 2024
2 parents 4f6d1dc + bf0529a commit 97cd00b
Show file tree
Hide file tree
Showing 22 changed files with 363 additions and 169 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
*.iml

.gradle
build/
build/

.classpath
.project
.settings/
bin/
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public interface ObjectiveDisplaySlot {
return BelowName.INSTANCE;
}

/**
* @since Minecraft 1.8
*/
static @NotNull TeamSidebar teamSidebar(@NotNull NamedTextColor teamColor) {
Preconditions.checkNotNull(teamColor);
return new TeamSidebar(teamColor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,15 @@ public interface TeamDisplay {

/**
* @return name tag visibility rule value
* @since Minecraft 1.8
*/
@NotNull NameTagVisibility nameTagVisibility();

/**
* Updates the name tag visibility rule.
*
* @param nameTagVisibility new rule value
* @since Minecraft 1.8
*/
@NotNull TeamDisplay nameTagVisibility(@NotNull NameTagVisibility nameTagVisibility);

Expand All @@ -156,6 +158,7 @@ public interface TeamDisplay {
* It is also used for displaying team specific sidebars with the {@link ObjectiveDisplaySlot.TeamSidebar}} display slot.
*
* @return player color
* @since Minecraft 1.8
*/
@Nullable NamedTextColor playerColor();

Expand All @@ -164,6 +167,7 @@ public interface TeamDisplay {
*
* @param playerColor new player color
* @see #playerColor()
* @since Minecraft 1.8
*/
@NotNull TeamDisplay playerColor(@Nullable NamedTextColor playerColor);
}
1 change: 0 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ devBundle = "1.21.1-R0.1-20240814.125441-13" # find latest here: https://repo.pa

[libraries]
spigotApi = "org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" # do not update
onePointEightPointEightNms = "app.ashcon:sportpaper:1.8.8-R0.1-SNAPSHOT"
packetEvents = "com.github.retrooper:packetevents-spigot:2.4.0"
buildIndra = { module = "net.kyori:indra-common", version = "3.1.3" }
buildNmcp = "com.gradleup.nmcp:com.gradleup.nmcp.gradle.plugin:0.0.9"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

public final class PacketAdapterLoader {
private static final String MODERN = "modern",
V1_8_R3 = "v1_8_R3",
LEGACY = "legacy",
PACKET_EVENTS = "packetevents";

private PacketAdapterLoader() {
Expand Down Expand Up @@ -45,9 +45,29 @@ private PacketAdapterLoader() {
}

private static @Nullable Class<?> tryLoadVersion(@NotNull String serverVersion) {
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-legacy/
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-10-1-15/
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-16/
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-21/
switch (serverVersion) {
case "1.7.10":
case "1.8":
case "1.8.3":
case "1.8.4":
case "1.8.5":
case "1.8.6":
case "1.8.7":
case "1.8.8":
return tryLoadImplementationClass(V1_8_R3);
case "1.9":
case "1.9.2":
case "1.9.4":
case "1.10.2":
case "1.11":
case "1.11.2":
case "1.12":
case "1.12.1":
case "1.12.2":
return tryLoadImplementationClass(LEGACY);
case "1.17":
case "1.17.1":
case "1.18":
Expand Down
4 changes: 4 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ project(":packetevents").projectDir = file("versions/packetevents")
include(":modern")
project(":modern").projectDir = file("versions/modern")

include(":legacy")
project(":legacy").projectDir = file("versions/legacy")

// For backwards compatibility
include(":v1_8_R3")
project(":v1_8_R3").projectDir = file("versions/v1_8_R3")

Expand Down
7 changes: 7 additions & 0 deletions versions/legacy/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
}

dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;

import net.kyori.adventure.text.format.NamedTextColor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import org.jetbrains.annotations.NotNull;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;

public final class ChatColorUtil {
private static final MethodHandle FROM_NAME_METHOD, GET_INDEX_METHOD;

static {
Class<?> enumChatFormatClass = ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("EnumChatFormat"));
MethodHandles.Lookup lookup = MethodHandles.lookup();

try {
Method fromNameMethod = enumChatFormatClass.getMethod("b", String.class);
FROM_NAME_METHOD = lookup.unreflect(fromNameMethod);

Method getIndexMethod = enumChatFormatClass.getMethod("b");
GET_INDEX_METHOD = lookup.unreflect(getIndexMethod);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}

private ChatColorUtil() {
}

public static int getColorIndex(@NotNull NamedTextColor color) {
String name = NamedTextColor.NAMES.key(color);
try {
Object format = FROM_NAME_METHOD.invoke(name);
return (int) GET_INDEX_METHOD.invoke(format);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;

import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;

// TODO: should be moved to MinecraftReflection at some point
public final class LegacyMinecraftClasses {
private static final String NMS_VERSION_STRING = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];

private LegacyMinecraftClasses() {
}

public static @NotNull String server(String path) {
return "net.minecraft.server." + NMS_VERSION_STRING + "." + path;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;

import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MinecraftClasses;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import org.bukkit.entity.Player;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public final class LegacyPacketSender implements PacketSender<Object> {
public static final LegacyPacketSender INSTANCE = new LegacyPacketSender();

private static final MethodHandle GET_HANDLE_METHOD, PLAYER_CONNECTION_FIELD, SEND_PACKET_METHOD;

static {
Class<?> packetClass = ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("Packet"));
Class<?> craftPlayerClass = ReflectUtil.getClassOrThrow(MinecraftClasses.craftBukkit("entity.CraftPlayer"));
MethodHandles.Lookup lookup = MethodHandles.lookup();

try {
Method getHandleMethod = craftPlayerClass.getMethod("getHandle");
GET_HANDLE_METHOD = lookup.unreflect(getHandleMethod);

Field playerConnectionField = getHandleMethod.getReturnType().getField("playerConnection");
PLAYER_CONNECTION_FIELD = lookup.unreflectGetter(playerConnectionField);

Method sendPacketMethod = playerConnectionField.getType().getMethod("sendPacket", packetClass);
SEND_PACKET_METHOD = lookup.unreflect(sendPacketMethod);
} catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException e) {
throw new ExceptionInInitializerError(e);
}
}

private LegacyPacketSender() {
}

@Override
public void sendPacket(Player player, Object packet) {
try {
Object handle = GET_HANDLE_METHOD.invoke(player);
Object playerConnection = PLAYER_CONNECTION_FIELD.invoke(handle);
SEND_PACKET_METHOD.invoke(playerConnection, packet);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,37 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
import net.minecraft.server.v1_8_R3.*;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;

import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection;

public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
private final PacketSender<Packet<?>> sender;
private final String objectiveName;
private PacketPlayOutScoreboardObjective removePacket;
private Object removePacket;

public ObjectivePacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull String objectiveName) {
this.sender = sender;
public ObjectivePacketAdapterImpl(@NotNull String objectiveName) {
this.objectiveName = objectiveName;
}

@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
PacketPlayOutScoreboardDisplayObjective packet = new PacketPlayOutScoreboardDisplayObjective();
PacketAccessors.DISPLAY_OBJECTIVE_POSITION.set(packet, ObjectiveConstants.displaySlotIndex(slot));
Object packet = PacketAccessors.DISPLAY_OBJECTIVE_CONSTRUCTOR.invoke();
PacketAccessors.DISPLAY_OBJECTIVE_POSITION.set(packet, ObjectiveConstants.displaySlotIndex(slot, false));
PacketAccessors.DISPLAY_OBJECTIVE_NAME.set(packet, objectiveName);
sender.sendPacket(players, packet);
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}

@Override
Expand All @@ -46,7 +43,7 @@ public void sendProperties(
@Nullable ScoreFormat scoreFormat
) {
LocalePacketUtil.sendLocalePackets(
sender,
LegacyPacketSender.INSTANCE,
players,
locale -> createPropertiesPacket(packetType, GlobalTranslator.render(value, locale), renderType)
);
Expand All @@ -55,11 +52,12 @@ public void sendProperties(
@Override
public void remove(@NotNull Collection<Player> players) {
if (removePacket == null) {
removePacket = new PacketPlayOutScoreboardObjective();
removePacket = PacketAccessors.OBJECTIVE_CONSTRUCTOR.invoke();
PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName);
PacketAccessors.OBJECTIVE_DISPLAY_NAME_FIELD.set(removePacket, "");
PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE);
}
sender.sendPacket(players, removePacket);
LegacyPacketSender.INSTANCE.sendPacket(players, removePacket);
}

@Override
Expand All @@ -70,45 +68,52 @@ public void sendScore(
@Nullable Component display,
@Nullable ScoreFormat scoreFormat
) {
PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(entry);
Object packet = PacketAccessors.SCORE_CONSTRUCTOR.invoke(entry);
PacketAccessors.SCORE_OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
PacketAccessors.SCORE_VALUE_FIELD.set(packet, value);
PacketAccessors.SCORE_ACTION_FIELD.set(packet, PacketPlayOutScoreboardScore.EnumScoreboardAction.CHANGE);
sender.sendPacket(players, packet);
if (PacketAccessors.SCORE_ACTION_FIELD_1_8 != null) {
PacketAccessors.SCORE_ACTION_FIELD_1_8.set(packet, PacketAccessors.SCORE_ACTION_CHANGE_1_8);
} else {
PacketAccessors.SCORE_ACTION_FIELD_1_7.set(packet, 0);
}

LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}

@Override
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(entry);
Object packet = PacketAccessors.SCORE_CONSTRUCTOR.invoke(entry);
PacketAccessors.SCORE_OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
sender.sendPacket(players, packet);
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}

private @NotNull PacketPlayOutScoreboardObjective createPropertiesPacket(
private @NotNull Object createPropertiesPacket(
@NotNull PropertiesPacketType packetType,
@NotNull Component value,
@NotNull ObjectiveRenderType renderType
) {
PacketPlayOutScoreboardObjective packet = new PacketPlayOutScoreboardObjective();
Object packet = PacketAccessors.OBJECTIVE_CONSTRUCTOR.invoke();
PacketAccessors.OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
PacketAccessors.OBJECTIVE_MODE_FIELD.set(packet, ObjectiveConstants.mode(packetType));

String legacyValue = LegacyFormatUtil.limitLegacyText(LegacyComponentSerializer.legacySection().serialize(value), ObjectiveConstants.LEGACY_VALUE_CHAR_LIMIT);
String legacyValue = LegacyFormatUtil.limitLegacyText(legacySection().serialize(value), ObjectiveConstants.LEGACY_VALUE_CHAR_LIMIT);
PacketAccessors.OBJECTIVE_DISPLAY_NAME_FIELD.set(packet, legacyValue);

IScoreboardCriteria.EnumScoreboardHealthDisplay nmsRenderType;
switch (renderType) {
case INTEGER:
nmsRenderType = IScoreboardCriteria.EnumScoreboardHealthDisplay.INTEGER;
break;
case HEARTS:
nmsRenderType = IScoreboardCriteria.EnumScoreboardHealthDisplay.HEARTS;
break;
default:
throw new IllegalStateException();
if (PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD != null) {
Object nmsRenderType;
switch (renderType) {
case INTEGER:
nmsRenderType = PacketAccessors.HEALTH_DISPLAY_INTEGER;
break;
case HEARTS:
nmsRenderType = PacketAccessors.HEALTH_DISPLAY_HEARTS;
break;
default:
throw new IllegalStateException();
}
PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD.set(packet, nmsRenderType);
}

PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD.set(packet, nmsRenderType);
return packet;
}
}
Loading

0 comments on commit 97cd00b

Please sign in to comment.