/* * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion * Copyright (C) 2016-2024 ViaVersion and contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.viaversion.viaversion.velocity.platform; import com.google.gson.JsonObject; import com.viaversion.viaversion.VelocityPlugin; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.platform.ViaInjector; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.util.ReflectionUtil; import com.viaversion.viaversion.velocity.handlers.VelocityChannelInitializer; import io.netty.channel.ChannelInitializer; import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.logging.Level; import org.jetbrains.annotations.Nullable; public class VelocityViaInjector implements ViaInjector { public static final Method GET_PLAYER_INFO_FORWARDING_MODE = getPlayerInfoForwardingModeMethod(); private static @Nullable Method getPlayerInfoForwardingModeMethod() { try { return Class.forName("com.velocitypowered.proxy.config.VelocityConfiguration").getMethod("getPlayerInfoForwardingMode"); } catch (NoSuchMethodException | ClassNotFoundException e) { Via.getPlatform().getLogger().log(Level.SEVERE, "Failed to get getPlayerInfoForwardingMode method from Velocity, please report this issue on our GitHub.", e); return null; } } private ChannelInitializer getInitializer() throws Exception { Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getServerChannelInitializer"); return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get"); } private ChannelInitializer getBackendInitializer() throws Exception { Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getBackendChannelInitializer"); return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get"); } @Override public void inject() throws Exception { Via.getPlatform().getLogger().info("Replacing channel initializers; you can safely ignore the following two warnings."); Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getServerChannelInitializer"); ChannelInitializer originalInitializer = getInitializer(); channelInitializerHolder.getClass().getMethod("set", ChannelInitializer.class) .invoke(channelInitializerHolder, new VelocityChannelInitializer(originalInitializer, false)); Object backendInitializerHolder = ReflectionUtil.invoke(connectionManager, "getBackendChannelInitializer"); ChannelInitializer backendInitializer = getBackendInitializer(); backendInitializerHolder.getClass().getMethod("set", ChannelInitializer.class) .invoke(backendInitializerHolder, new VelocityChannelInitializer(backendInitializer, true)); } @Override public void uninject() { Via.getPlatform().getLogger().severe("ViaVersion cannot remove itself from Velocity without a reboot!"); } @Override public int getServerProtocolVersion() throws Exception { return getLowestSupportedProtocolVersion(); } @Override public IntSortedSet getServerProtocolVersions() throws Exception { int lowestSupportedProtocolVersion = getLowestSupportedProtocolVersion(); IntSortedSet set = new IntLinkedOpenHashSet(); for (com.velocitypowered.api.network.ProtocolVersion version : com.velocitypowered.api.network.ProtocolVersion.SUPPORTED_VERSIONS) { if (version.getProtocol() >= lowestSupportedProtocolVersion) { set.add(version.getProtocol()); } } return set; } public static int getLowestSupportedProtocolVersion() { try { if (GET_PLAYER_INFO_FORWARDING_MODE != null && ((Enum) GET_PLAYER_INFO_FORWARDING_MODE.invoke(VelocityPlugin.PROXY.getConfiguration())) .name().equals("MODERN")) { return ProtocolVersion.v1_13.getVersion(); } } catch (IllegalAccessException | InvocationTargetException ignored) { } return com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION.getProtocol(); } @Override public JsonObject getDump() { JsonObject data = new JsonObject(); try { data.addProperty("currentInitializer", getInitializer().getClass().getName()); } catch (Exception e) { // Ignored } return data; } }