From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Tue, 26 Sep 2023 19:00:41 +0800 Subject: [PATCH] Leaves Protocol Core diff --git a/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java b/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java index 975da2529d18391ed4ecc7359a2d7319129bd872..ae5590b015a2b018188bd7a45b44ec4d4af048a5 100644 --- a/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java +++ b/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java @@ -20,7 +20,12 @@ public record ServerboundCustomPayloadPacket(CustomPacketPayload payload) implem private static CustomPacketPayload readPayload(ResourceLocation id, FriendlyByteBuf buf) { FriendlyByteBuf.Reader packetdataserializer_a = (FriendlyByteBuf.Reader) ServerboundCustomPayloadPacket.KNOWN_TYPES.get(id); - + // Leaves start - protocol + CustomPacketPayload leavesPayload = top.leavesmc.leaves.protocol.core.LeavesProtocolManager.getPayload(id, buf); + if (leavesPayload != null) { + return leavesPayload; + } + // Leaves end - protocol return (CustomPacketPayload) (packetdataserializer_a != null ? (CustomPacketPayload) packetdataserializer_a.apply(buf) : readUnknownPayload(id, buf)); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index c798fbe078e14b0473b2d8bcdccc11238d28025e..68a242f6aaa4aeebff5c2c566ac2aab538eccc87 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1562,6 +1562,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop>> KNOWN_TYPES = new HashMap<>(); + private static final Map> KNOW_RECEIVERS = new HashMap<>(); + + private static final List TICKERS = new ArrayList<>(); + private static final List PLAYER_JOIN = new ArrayList<>(); + private static final List PLAYER_LEAVE = new ArrayList<>(); + private static final List RELOAD_SERVER = new ArrayList<>(); + private static final Map MINECRAFT_REGISTER = new HashMap<>(); + + public static void init() { + for (Class clazz : getClasses("top.leavesmc.leaves.protocol")) { + final LeavesProtocol protocol = clazz.getAnnotation(LeavesProtocol.class); + if (protocol != null) { + Set methods; + try { + Method[] publicMethods = clazz.getMethods(); + Method[] privateMethods = clazz.getDeclaredMethods(); + methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0f); + Collections.addAll(methods, publicMethods); + Collections.addAll(methods, privateMethods); + } catch (NoClassDefFoundError e) { + e.printStackTrace(); + return; + } + + Map> map = new HashMap<>(); + for (final Method method : methods) { + if (method.isBridge() || method.isSynthetic() || !Modifier.isStatic(method.getModifiers())) { + continue; + } + + method.setAccessible(true); + + final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class); + if (init != null) { + try { + method.invoke(null); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + continue; + } + + final ProtocolHandler.PayloadReceiver receiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class); + if (receiver != null) { + try { + map.put(receiver, receiver.payload().getConstructor(ResourceLocation.class, FriendlyByteBuf.class)); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + continue; + } + + if (!KNOW_RECEIVERS.containsKey(protocol)) { + KNOW_RECEIVERS.put(protocol, new HashMap<>()); + } + + KNOW_RECEIVERS.get(protocol).put(receiver, method); + continue; + } + + final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class); + if (ticker != null) { + TICKERS.add(method); + continue; + } + + final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class); + if (playerJoin != null) { + PLAYER_JOIN.add(method); + continue; + } + + final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class); + if (playerLeave != null) { + PLAYER_LEAVE.add(method); + continue; + } + + final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class); + if (reloadServer != null) { + RELOAD_SERVER.add(method); + continue; + } + + final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class); + if (minecraftRegister != null) { + MINECRAFT_REGISTER.put(minecraftRegister, method); + } + } + KNOWN_TYPES.put(protocol, map); + } + } + } + + public static CustomPacketPayload getPayload(ResourceLocation id, FriendlyByteBuf buf) { + for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) { + if (!protocol.namespace().equals(id.getNamespace()) && !ArrayUtils.contains(protocol.namespaces(), id.getNamespace())) { + continue; + } + + Map> map = KNOWN_TYPES.get(protocol); + for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { + if (receiver.ignoreId() || receiver.payloadId().equals(id.getPath()) || ArrayUtils.contains(receiver.payloadIds(), id.getPath())) { + try { + return map.get(receiver).newInstance(id, buf); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + return null; + } + + public static void handlePayload(ServerPlayer player, CustomPacketPayload payload) { + for (LeavesProtocol protocol : KNOW_RECEIVERS.keySet()) { + if (!protocol.namespace().equals(payload.id().getNamespace()) && !ArrayUtils.contains(protocol.namespaces(), payload.id().getNamespace())) { + continue; + } + + Map map = KNOW_RECEIVERS.get(protocol); + for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { + if (payload.getClass() == receiver.payload()) { + if (receiver.ignoreId() || receiver.payloadId().equals(payload.id().getPath()) || + ArrayUtils.contains(receiver.payloadIds(), payload.id().getPath())) { + try { + map.get(receiver).invoke(player, payload); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + } + } + + public static void handleTick() { + if (!TICKERS.isEmpty()) { + try { + for (Method method : TICKERS) { + method.invoke(null); + } + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public static void handlePlayerJoin(ServerPlayer player) { + if (!PLAYER_JOIN.isEmpty()) { + try { + for (Method method : PLAYER_JOIN) { + method.invoke(player); + } + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public static void handlePlayerLeave(ServerPlayer player) { + if (!PLAYER_LEAVE.isEmpty()) { + try { + for (Method method : PLAYER_LEAVE) { + method.invoke(player); + } + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public static void handleServerReload() { + if (!RELOAD_SERVER.isEmpty()) { + try { + for (Method method : RELOAD_SERVER) { + method.invoke(null); + } + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public static void handleMinecraftRegister(String channel, ServerPlayer player) { + if (!MINECRAFT_REGISTER.isEmpty()) { + for (ProtocolHandler.MinecraftRegister register : MINECRAFT_REGISTER.keySet()) { + if (register.ignoreId() || register.channelId().equals(channel) || + ArrayUtils.contains(register.channelIds(), channel)) { + try { + MINECRAFT_REGISTER.get(register).invoke(player); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + } + + private static List> getClasses(String packageName) { + List> classes = new ArrayList<>(); + String packageDirName = packageName.replace('.', '/'); + Enumeration dirs; + try { + dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); + while (dirs.hasMoreElements()) { + URL url = dirs.nextElement(); + findClassInPackageByFile(packageName, URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8), classes); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return classes; + } + + private static void findClassInPackageByFile(String packageName, String filePath, List> classes) { + File dir = new File(filePath); + if (!dir.exists() || !dir.isDirectory()) { + return; + } + + File[] dirFiles = dir.listFiles(file -> file.getName().endsWith("class") || file.isDirectory()); + if (dirFiles != null) { + for (File file : dirFiles) { + if (file.isDirectory()) { + if (!file.getName().equals("core")) { + findClassInPackageByFile(packageName + "." + file.getName(), file.getPath(), classes); + } + continue; + } + String className = file.getName().substring(0, file.getName().length() - 6); + try { + classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + } + + public record EmptyPayload(ResourceLocation id) implements CustomPacketPayload { + + public EmptyPayload(ResourceLocation id, FriendlyByteBuf buf) { + this(id); + } + + @Override + public void write(@NotNull FriendlyByteBuf buf) { + } + } +} diff --git a/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolHandler.java b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..d696f001d2576d1b61cc732c81f22eb52205072b --- /dev/null +++ b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolHandler.java @@ -0,0 +1,65 @@ +package top.leavesmc.leaves.protocol.core; + +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class ProtocolHandler { + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Init { + + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface PayloadReceiver { + + Class payload(); + + String[] payloadIds() default {}; + + String payloadId() default ""; + + boolean ignoreId() default false; + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Ticker { + int delay() default 0; + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface PlayerJoin { + + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface PlayerLeave { + + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface ReloadServer { + + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface MinecraftRegister { + + String channelId() default ""; + + String[] channelIds() default {}; + + boolean ignoreId() default false; + } +} diff --git a/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolUtils.java b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..5282c5ad3d26d06ab685ddaaf6fd9a4d49559717 --- /dev/null +++ b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolUtils.java @@ -0,0 +1,36 @@ +package top.leavesmc.leaves.protocol.core; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +public class ProtocolUtils { + + public static void sendEmptyPayloadPacket(ServerPlayer player, ResourceLocation id) { + player.connection.send(new ClientboundCustomPayloadPacket(new LeavesProtocolManager.EmptyPayload(id))); + } + + public static void sendPayloadPacket(ServerPlayer player, ResourceLocation id, Consumer consumer) { + player.connection.send(new ClientboundCustomPayloadPacket(new CustomPacketPayload() { + @Override + public void write(@NotNull FriendlyByteBuf buf) { + consumer.accept(buf); + } + + @Override + @NotNull + public ResourceLocation id() { + return id; + } + })); + } + + public static void sendPayloadPacket(ServerPlayer player, CustomPacketPayload payload) { + player.connection.send(new ClientboundCustomPayloadPacket(payload)); + } +}