Cleanup Bukkit/Sponge injectors

This commit is contained in:
Nassim Jahnke 2021-10-01 11:57:35 +02:00
parent a306c222d4
commit 427b0a68d0
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
12 changed files with 537 additions and 917 deletions

View File

@ -78,14 +78,18 @@ public interface ViaInjector {
*
* @return The name
*/
String getEncoderName();
default String getEncoderName() {
return "via-encoder";
}
/**
* Get the name of the decoder for then netty pipeline for this platform.
*
* @return The name
*/
String getDecoderName();
default String getDecoderName() {
return "via-decoder";
}
/**
* Get any relevant data for debugging injection issues.

View File

@ -84,14 +84,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
// Check if we're using protocol support too
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
if (protocolSupport) {
getLogger().info("Hooking into ProtocolSupport, to prevent issues!");
try {
BukkitViaInjector.patchLists();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
@ -124,7 +116,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
// Generate classes needed (only works if it's compat or ps)
ClassGenerator.generate();
lateBind = !BukkitViaInjector.isBinded();
lateBind = !((BukkitViaInjector) Via.getManager().getInjector()).isBinded();
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
if (!lateBind) {

View File

@ -22,6 +22,7 @@ import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
@ -30,21 +31,25 @@ import io.netty.handler.codec.MessageToByteEncoder;
import java.lang.reflect.Method;
public class BukkitChannelInitializer extends ChannelInitializer<Channel> {
public class BukkitChannelInitializer extends ChannelInitializer<Channel> implements WrappedChannelInitializer {
private static final Method INIT_CHANNEL_METHOD;
private final ChannelInitializer<Channel> original;
private Method method;
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
this.original = oldInit;
static {
try {
this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
this.method.setAccessible(true);
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
INIT_CHANNEL_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
this.original = oldInit;
}
@Deprecated/*(forRemoval = true)*/
public ChannelInitializer<Channel> getOriginal() {
return original;
}
@ -52,7 +57,7 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel channel) throws Exception {
// Add originals
this.method.invoke(this.original, channel);
INIT_CHANNEL_METHOD.invoke(this.original, channel);
afterChannelInitialize(channel);
}
@ -72,4 +77,9 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> {
channel.pipeline().replace("encoder", "encoder", encoder);
channel.pipeline().replace("decoder", "decoder", decoder);
}
@Override
public ChannelInitializer<Channel> original() {
return original;
}
}

View File

@ -17,15 +17,10 @@
*/
package com.viaversion.viaversion.bukkit.platform;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.util.ConcurrentList;
import com.viaversion.viaversion.util.ListWrapper;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.platform.LegacyViaInjector;
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
import com.viaversion.viaversion.util.ReflectionUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@ -33,230 +28,88 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import org.bukkit.Bukkit;
import org.bukkit.plugin.PluginDescriptionFile;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
//TODO screams
public class BukkitViaInjector implements ViaInjector {
private final List<ChannelFuture> injectedFutures = new ArrayList<>();
private final List<Pair<Field, Object>> injectedLists = new ArrayList<>();
public class BukkitViaInjector extends LegacyViaInjector {
private boolean protocolLib;
@Override
public void inject() throws Exception {
public void inject() throws ReflectiveOperationException {
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
PaperViaInjector.setPaperChannelInitializeListener();
return;
}
try {
Object connection = getServerConnection();
if (connection == null) {
throw new Exception("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
List wrapper = new ListWrapper((List) value) {
@Override
public void handleAdd(Object o) {
if (o instanceof ChannelFuture) {
try {
injectChannelFuture((ChannelFuture) o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
injectedLists.add(new Pair<>(field, connection));
field.set(connection, wrapper);
// Iterate through current list
synchronized (wrapper) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
injectChannelFuture((ChannelFuture) o);
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version.");
throw e;
}
}
private void injectChannelFuture(ChannelFuture future) throws Exception {
try {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
ChannelHandler handler = future.channel().pipeline().get(name);
try {
ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class);
bootstrapAcceptor = handler;
} catch (Exception e) {
// Not this one
}
}
// Default to first (Also allows blame to work)
if (bootstrapAcceptor == null) {
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
ChannelInitializer newInit = new BukkitChannelInitializer(oldInit);
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
injectedFutures.add(future);
} catch (NoSuchFieldException e) {
// let's find who to blame!
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile yaml = ReflectionUtil.get(cl, "description", PluginDescriptionFile.class);
throw new Exception("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + yaml.getName() + "?");
} else {
throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("We failed to inject ViaVersion, have you got late-bind enabled with something else?");
throw e;
}
super.inject();
}
@Override
public void uninject() throws Exception {
// TODO: Uninject from players currently online to prevent protocol lib issues.
public void uninject() throws ReflectiveOperationException {
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
PaperViaInjector.removePaperChannelInitializeListener();
return;
}
for (ChannelFuture future : injectedFutures) {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
ChannelHandler handler = future.channel().pipeline().get(name);
try {
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class);
if (oldInit instanceof BukkitChannelInitializer) {
bootstrapAcceptor = handler;
}
} catch (Exception e) {
// Not this one
}
}
// Default to first
if (bootstrapAcceptor == null) {
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
if (oldInit instanceof BukkitChannelInitializer) {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((BukkitChannelInitializer) oldInit).getOriginal());
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("Failed to remove injection handler, reload won't work with connections, please reboot!");
}
}
injectedFutures.clear();
for (Pair<Field, Object> pair : injectedLists) {
try {
Object o = pair.key().get(pair.value());
if (o instanceof ListWrapper) {
pair.key().set(pair.value(), ((ListWrapper) o).getOriginalList());
}
} catch (IllegalAccessException e) {
Via.getPlatform().getLogger().severe("Failed to remove injection, reload won't work with connections, please reboot!");
}
}
injectedLists.clear();
super.uninject();
}
@Override
public boolean lateProtocolVersionSetting() {
return true;
}
@Override
public int getServerProtocolVersion() throws Exception {
public int getServerProtocolVersion() throws ReflectiveOperationException {
if (PaperViaInjector.PAPER_PROTOCOL_METHOD) {
//noinspection deprecation
return Bukkit.getUnsafe().getProtocolVersion();
}
try {
// Grab a static instance of the server
Class<?> serverClazz = NMSUtil.nms("MinecraftServer", "net.minecraft.server.MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
// Time to go on a journey! The protocol version is hidden inside an int in ServerPing.ServerData
// Grab a static instance of the server
Class<?> serverClazz = NMSUtil.nms("MinecraftServer", "net.minecraft.server.MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
// Grab the ping class and find the field to access it
Class<?> pingClazz = NMSUtil.nms(
"ServerPing",
"net.minecraft.network.protocol.status.ServerPing"
);
Object ping = null;
// Search for ping method
for (Field f : serverClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().equals("ServerPing")) {
f.setAccessible(true);
ping = f.get(server);
}
}
// Grab the ping class and find the field to access it
Class<?> pingClazz = NMSUtil.nms(
"ServerPing",
"net.minecraft.network.protocol.status.ServerPing"
);
Object ping = null;
for (Field field : serverClazz.getDeclaredFields()) {
if (field.getType() == pingClazz) {
field.setAccessible(true);
ping = field.get(server);
break;
}
if (ping != null) {
Object serverData = null;
for (Field f : pingClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().endsWith("ServerData")) {
f.setAccessible(true);
serverData = f.get(ping);
}
}
}
if (serverData != null) {
int protocolVersion = -1;
for (Field f : serverData.getClass().getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType() == int.class) {
f.setAccessible(true);
protocolVersion = (int) f.get(serverData);
}
}
}
if (protocolVersion != -1) {
return protocolVersion;
}
}
}
} catch (Exception e) {
throw new Exception("Failed to get server", e);
}
throw new Exception("Failed to get server");
}
@Override
public String getEncoderName() {
return "encoder";
// Now get the ServerData inside ServerPing
Class<?> serverDataClass = NMSUtil.nms(
"ServerPing$ServerData",
"net.minecraft.network.protocol.status.ServerPing$ServerData"
);
Object serverData = null;
for (Field field : pingClazz.getDeclaredFields()) {
if (field.getType() == serverDataClass) {
field.setAccessible(true);
serverData = field.get(ping);
break;
}
}
// Get protocol version field
for (Field field : serverDataClass.getDeclaredFields()) {
if (field.getType() != int.class) {
continue;
}
field.setAccessible(true);
int protocolVersion = (int) field.get(serverData);
if (protocolVersion != -1) {
return protocolVersion;
}
}
throw new RuntimeException("Failed to get server");
}
@Override
@ -264,135 +117,79 @@ public class BukkitViaInjector implements ViaInjector {
return protocolLib ? "protocol_lib_decoder" : "decoder";
}
public static Object getServerConnection() throws Exception {
Class<?> serverClazz = NMSUtil.nms(
@Override
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
Class<?> serverClass = NMSUtil.nms(
"MinecraftServer",
"net.minecraft.server.MinecraftServer"
);
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Object connection = null;
for (Method m : serverClazz.getDeclaredMethods()) {
if (m.getReturnType() != null) {
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
if (m.getParameterTypes().length == 0) {
connection = m.invoke(server);
}
}
Class<?> connectionClass = NMSUtil.nms(
"ServerConnection",
"net.minecraft.server.network.ServerConnection"
);
Object server = ReflectionUtil.invokeStatic(serverClass, "getServer");
for (Method method : serverClass.getDeclaredMethods()) {
if (method.getReturnType() != connectionClass || method.getParameterTypes().length != 0) {
continue;
}
// We need the method that initiates the connection if not yet set
Object connection = method.invoke(server);
if (connection != null) {
return connection;
}
}
return connection;
return null;
}
public static boolean isBinded() {
if (PaperViaInjector.PAPER_INJECTION_METHOD) return true;
@Override
protected WrappedChannelInitializer createChannelInitializer(ChannelInitializer<Channel> oldInitializer) {
return new BukkitChannelInitializer(oldInitializer);
}
@Override
protected void blame(ChannelHandler bootstrapAcceptor) throws ReflectiveOperationException {
// Let's find who to blame!
ClassLoader classLoader = bootstrapAcceptor.getClass().getClassLoader();
if (classLoader.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile description = ReflectionUtil.get(classLoader, "description", PluginDescriptionFile.class);
throw new RuntimeException("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + description.getName() + "?");
} else {
throw new RuntimeException("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
}
public boolean isBinded() {
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
return true;
}
try {
Object connection = getServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
if (!List.class.isAssignableFrom(field.getType())) {
continue;
}
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
List<?> value = (List<?>) field.get(connection);
// Check if the list has at least one element
synchronized (value) {
if (!value.isEmpty() && value.get(0) instanceof ChannelFuture) {
return true;
}
}
}
} catch (Exception e) {
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
return false;
}
@Override
public JsonObject getDump() {
JsonObject data = new JsonObject();
// Generate information about current injections
JsonArray injectedChannelInitializers = new JsonArray();
for (ChannelFuture cf : injectedFutures) {
JsonObject info = new JsonObject();
info.addProperty("futureClass", cf.getClass().getName());
info.addProperty("channelClass", cf.channel().getClass().getName());
// Get information about the pipes for this channel future
JsonArray pipeline = new JsonArray();
for (String pipeName : cf.channel().pipeline().names()) {
JsonObject pipe = new JsonObject();
pipe.addProperty("name", pipeName);
if (cf.channel().pipeline().get(pipeName) != null) {
pipe.addProperty("class", cf.channel().pipeline().get(pipeName).getClass().getName());
try {
Object child = ReflectionUtil.get(cf.channel().pipeline().get(pipeName), "childHandler", ChannelInitializer.class);
pipe.addProperty("childClass", child.getClass().getName());
if (child instanceof BukkitChannelInitializer) {
pipe.addProperty("oldInit", ((BukkitChannelInitializer) child).getOriginal().getClass().getName());
}
} catch (Exception e) {
// Don't display
}
}
// Add to the pipeline array
pipeline.add(pipe);
}
info.add("pipeline", pipeline);
// Add to the list
injectedChannelInitializers.add(info);
}
data.add("injectedChannelInitializers", injectedChannelInitializers);
// Generate information about lists we've injected into
JsonObject wrappedLists = new JsonObject();
JsonObject currentLists = new JsonObject();
try {
for (Pair<Field, Object> pair : injectedLists) {
Object list = pair.key().get(pair.value());
// Note down the current value (could be overridden by another plugin)
currentLists.addProperty(pair.key().getName(), list.getClass().getName());
// Also if it's not overridden we can display what's inside our list (possibly another plugin)
if (list instanceof ListWrapper) {
wrappedLists.addProperty(pair.key().getName(), ((ListWrapper) list).getOriginalList().getClass().getName());
}
}
data.add("wrappedLists", wrappedLists);
data.add("currentLists", currentLists);
} catch (Exception e) {
// Ignored, fields won't be present
}
data.addProperty("binded", isBinded());
return data;
}
public static void patchLists() throws Exception {
if (PaperViaInjector.PAPER_INJECTION_METHOD) return;
Object connection = getServerConnection();
if (connection == null) {
Via.getPlatform().getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(connection);
if (!(value instanceof List)) continue;
if (value instanceof ConcurrentList) continue;
ConcurrentList list = new ConcurrentList();
list.addAll((Collection) value);
field.set(connection, list);
}
}
public void setProtocolLib(boolean protocolLib) {
this.protocolLib = protocolLib;
}

View File

@ -80,16 +80,6 @@ public class BungeeViaInjector implements ViaInjector {
return ReflectionUtil.getStatic(Class.forName("net.md_5.bungee.protocol.ProtocolConstants"), "SUPPORTED_VERSION_IDS", List.class);
}
@Override
public String getEncoderName() {
return "via-encoder";
}
@Override
public String getDecoderName() {
return "via-decoder";
}
private ChannelInitializer<Channel> getChannelInitializer() throws Exception {
Class<?> pipelineUtils = Class.forName("net.md_5.bungee.netty.PipelineUtils");
Field field = pipelineUtils.getDeclaredField("SERVER_CHILD");

View File

@ -0,0 +1,246 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.platform;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.util.ReflectionUtil;
import com.viaversion.viaversion.util.SynchronizedListWrapper;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public abstract class LegacyViaInjector implements ViaInjector {
protected final List<ChannelFuture> injectedFutures = new ArrayList<>();
protected final List<Pair<Field, Object>> injectedLists = new ArrayList<>();
@Override
public void inject() throws ReflectiveOperationException {
Object connection = getServerConnection();
if (connection == null) {
throw new RuntimeException("Failed to find the core component 'ServerConnection'");
}
// Inject into channels list
for (Field field : connection.getClass().getDeclaredFields()) {
// Check for list with the correct generic type
if (!List.class.isAssignableFrom(field.getType()) || !field.getGenericType().getTypeName().contains(ChannelFuture.class.getName())) {
continue;
}
field.setAccessible(true);
List<ChannelFuture> list = (List<ChannelFuture>) field.get(connection);
List<ChannelFuture> wrappedList = new SynchronizedListWrapper(list, o -> {
// Inject newly added entries
try {
injectChannelFuture((ChannelFuture) o);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
});
// Synchronize over original list before setting the field
synchronized (list) {
// Iterate through current list
for (ChannelFuture future : list) {
injectChannelFuture(future);
}
field.set(connection, wrappedList);
}
injectedLists.add(new Pair<>(field, connection));
}
}
private void injectChannelFuture(ChannelFuture future) throws ReflectiveOperationException {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Find the right channelhandler
for (String name : names) {
ChannelHandler handler = future.channel().pipeline().get(name);
try {
ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class);
bootstrapAcceptor = handler;
break;
} catch (ReflectiveOperationException ignored) {
// Not this one
}
}
if (bootstrapAcceptor == null) {
// Default to first (also allows blame to work)
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
ChannelInitializer<Channel> oldInitializer = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
ReflectionUtil.set(bootstrapAcceptor, "childHandler", createChannelInitializer(oldInitializer));
injectedFutures.add(future);
} catch (NoSuchFieldException ignored) {
blame(bootstrapAcceptor);
}
}
@Override
public void uninject() throws ReflectiveOperationException {
//TODO uninject connections
for (ChannelFuture future : injectedFutures) {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
ChannelHandler handler = future.channel().pipeline().get(name);
try {
if (ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class) instanceof WrappedChannelInitializer) {
bootstrapAcceptor = handler;
break;
}
} catch (ReflectiveOperationException ignored) {
}
}
if (bootstrapAcceptor == null) {
// Default to first
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
ChannelInitializer<Channel> initializer = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
if (initializer instanceof WrappedChannelInitializer) {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((WrappedChannelInitializer) initializer).original());
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("Failed to remove injection handler, reload won't work with connections, please reboot!");
e.printStackTrace();
}
}
injectedFutures.clear();
for (Pair<Field, Object> pair : injectedLists) {
try {
Field field = pair.key();
Object o = field.get(pair.value());
if (o instanceof SynchronizedListWrapper) {
List<ChannelFuture> originalList = ((SynchronizedListWrapper) o).originalList();
synchronized (originalList) {
field.set(pair.value(), originalList);
}
}
} catch (ReflectiveOperationException e) {
Via.getPlatform().getLogger().severe("Failed to remove injection, reload won't work with connections, please reboot!");
}
}
injectedLists.clear();
}
@Override
public boolean lateProtocolVersionSetting() {
return true;
}
@Override
public JsonObject getDump() {
JsonObject data = new JsonObject();
// Generate information about current injections
JsonArray injectedChannelInitializers = new JsonArray();
data.add("injectedChannelInitializers", injectedChannelInitializers);
for (ChannelFuture future : injectedFutures) {
JsonObject futureInfo = new JsonObject();
injectedChannelInitializers.add(futureInfo);
futureInfo.addProperty("futureClass", future.getClass().getName());
futureInfo.addProperty("channelClass", future.channel().getClass().getName());
// Get information about the pipes for this channel future
JsonArray pipeline = new JsonArray();
futureInfo.add("pipeline", pipeline);
for (String pipeName : future.channel().pipeline().names()) {
JsonObject handlerInfo = new JsonObject();
pipeline.add(handlerInfo);
handlerInfo.addProperty("name", pipeName);
ChannelHandler channelHandler = future.channel().pipeline().get(pipeName);
if (channelHandler == null) {
handlerInfo.addProperty("status", "INVALID");
continue;
}
handlerInfo.addProperty("class", channelHandler.getClass().getName());
try {
Object child = ReflectionUtil.get(channelHandler, "childHandler", ChannelInitializer.class);
handlerInfo.addProperty("childClass", child.getClass().getName());
if (child instanceof WrappedChannelInitializer) {
handlerInfo.addProperty("oldInit", ((WrappedChannelInitializer) child).original().getClass().getName());
}
} catch (ReflectiveOperationException ignored) {
// Don't display
}
}
}
// Generate information about lists we've injected into
JsonObject wrappedLists = new JsonObject();
JsonObject currentLists = new JsonObject();
try {
for (Pair<Field, Object> pair : injectedLists) {
Field field = pair.key();
Object list = field.get(pair.value());
// Note down the current value (could be overridden by another plugin)
currentLists.addProperty(field.getName(), list.getClass().getName());
// Also, if it's not overridden we can display what's inside our list (possibly another plugin)
if (list instanceof SynchronizedListWrapper) {
wrappedLists.addProperty(field.getName(), ((SynchronizedListWrapper) list).originalList().getClass().getName());
}
}
data.add("wrappedLists", wrappedLists);
data.add("currentLists", currentLists);
} catch (ReflectiveOperationException ignored) {
// Ignored, fields won't be present
}
return data;
}
@Override
public String getEncoderName() {
return "encoder";
}
@Override
public String getDecoderName() {
return "decoder";
}
protected abstract @Nullable Object getServerConnection() throws ReflectiveOperationException;
protected abstract WrappedChannelInitializer createChannelInitializer(ChannelInitializer<Channel> oldInitializer);
protected abstract void blame(ChannelHandler bootstrapAcceptor) throws ReflectiveOperationException;
}

View File

@ -0,0 +1,26 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.platform;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
public interface WrappedChannelInitializer {
ChannelInitializer<Channel> original();
}

View File

@ -1,283 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* Created by wea_ondara licensed under MIT
* Same license as in LICENSE
* <p>
* Taken from:
* https://github.com/weaondara/BungeePerms/blob/master/src/main/java/net/alpenblock/bungeeperms/util/ConcurrentList.java
*
* @param <E> List Type
* @deprecated get rid of this at some point
*/
@Deprecated/*(forRemoval = true)*/
public class ConcurrentList<E> extends ArrayList<E> {
private final Object lock = new Object();
@Override
public boolean add(E e) {
synchronized (lock) {
return super.add(e);
}
}
@Override
public void add(int index, E element) {
synchronized (lock) {
super.add(index, element);
}
}
@Override
public boolean addAll(Collection<? extends E> c) {
synchronized (lock) {
return super.addAll(c);
}
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
synchronized (lock) {
return super.addAll(index, c);
}
}
@Override
public void clear() {
synchronized (lock) {
super.clear();
}
}
@Override
public Object clone() {
synchronized (lock) {
return super.clone();
}
}
@Override
public boolean contains(Object o) {
synchronized (lock) {
return super.contains(o);
}
}
@Override
public void ensureCapacity(int minCapacity) {
synchronized (lock) {
super.ensureCapacity(minCapacity);
}
}
@Override
public E get(int index) {
synchronized (lock) {
return super.get(index);
}
}
@Override
public int indexOf(Object o) {
synchronized (lock) {
return super.indexOf(o);
}
}
@Override
public int lastIndexOf(Object o) {
synchronized (lock) {
return super.lastIndexOf(o);
}
}
@Override
public E remove(int index) {
synchronized (lock) {
return super.remove(index);
}
}
@Override
public boolean remove(Object o) {
synchronized (lock) {
return super.remove(o);
}
}
@Override
public boolean removeAll(Collection<?> c) {
synchronized (lock) {
return super.removeAll(c);
}
}
@Override
public boolean retainAll(Collection<?> c) {
synchronized (lock) {
return super.retainAll(c);
}
}
@Override
public E set(int index, E element) {
synchronized (lock) {
return super.set(index, element);
}
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
synchronized (lock) {
return super.subList(fromIndex, toIndex);
}
}
@Override
public Object[] toArray() {
synchronized (lock) {
return super.toArray();
}
}
@Override
public <T> T[] toArray(T[] a) {
synchronized (lock) {
return super.toArray(a);
}
}
@Override
public void trimToSize() {
synchronized (lock) {
super.trimToSize();
}
}
@Override
public ListIterator<E> listIterator() {
return new ListItr(0);
}
@Override
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
protected int cursor;
protected int lastRet;
final ConcurrentList l;
public Itr() {
cursor = 0;
lastRet = -1;
l = (ConcurrentList) ConcurrentList.this.clone();
}
@Override
public boolean hasNext() {
return cursor < l.size();
}
@Override
public E next() {
int i = cursor;
if (i >= l.size()) {
throw new NoSuchElementException();
}
cursor = i + 1;
return (E) l.get(lastRet = i);
}
@Override
public void remove() {
if (lastRet < 0) {
throw new IllegalStateException();
}
l.remove(lastRet);
ConcurrentList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
}
}
public class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
@Override
public boolean hasPrevious() {
return cursor > 0;
}
@Override
public int nextIndex() {
return cursor;
}
@Override
public int previousIndex() {
return cursor - 1;
}
@Override
public E previous() {
int i = cursor - 1;
if (i < 0) {
throw new NoSuchElementException();
}
cursor = i;
return (E) l.get(lastRet = i);
}
@Override
public void set(E e) {
if (lastRet < 0) {
throw new IllegalStateException();
}
l.set(lastRet, e);
ConcurrentList.this.set(lastRet, e);
}
@Override
public void add(E e) {
int i = cursor;
l.add(i, e);
ConcurrentList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
}
}
}

View File

@ -17,28 +17,40 @@
*/
package com.viaversion.viaversion.util;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @deprecated scary
* Synchronized list wrapper with the addition of an add handler called when an element is added to the list.
*
* @param <E> list type
*/
@Deprecated/*(forRemoval = true)*/
public abstract class ListWrapper implements List {
private final List list;
public final class SynchronizedListWrapper<E> implements List<E> {
private final List<E> list;
private final Consumer<E> addHandler;
public ListWrapper(List inputList) {
public SynchronizedListWrapper(final List<E> inputList, final Consumer<E> addHandler) {
this.list = inputList;
this.addHandler = addHandler;
}
public abstract void handleAdd(Object o);
public List getOriginalList() {
public List<E> originalList() {
return list;
}
private void handleAdd(E o) {
synchronized (this) {
addHandler.accept(o);
}
}
@Override
public int size() {
synchronized (this) {
@ -53,59 +65,57 @@ public abstract class ListWrapper implements List {
}
}
@Override
public boolean contains(Object o) {
public boolean contains(final Object o) {
synchronized (this) {
return this.list.contains(o);
}
}
@Override
public Iterator iterator() {
synchronized (this) {
return listIterator();
}
public @NonNull Iterator<E> iterator() {
// Has to be manually synched
return listIterator();
}
@Override
public Object[] toArray() {
public Object @NonNull [] toArray() {
synchronized (this) {
return this.list.toArray();
}
}
@Override
public boolean add(Object o) {
handleAdd(o);
public boolean add(final E o) {
synchronized (this) {
handleAdd(o);
return this.list.add(o);
}
}
@Override
public boolean remove(Object o) {
public boolean remove(final Object o) {
synchronized (this) {
return this.list.remove(o);
}
}
@Override
public boolean addAll(Collection c) {
for (Object o : c) {
handleAdd(o);
}
public boolean addAll(final Collection<? extends E> c) {
synchronized (this) {
for (final E o : c) {
handleAdd(o);
}
return this.list.addAll(c);
}
}
@Override
public boolean addAll(int index, Collection c) {
for (Object o : c) {
handleAdd(o);
}
public boolean addAll(final int index, final Collection<? extends E> c) {
synchronized (this) {
for (final E o : c) {
handleAdd(o);
}
return this.list.addAll(index, c);
}
}
@ -118,93 +128,135 @@ public abstract class ListWrapper implements List {
}
@Override
public Object get(int index) {
public E get(final int index) {
synchronized (this) {
return this.list.get(index);
}
}
@Override
public Object set(int index, Object element) {
public E set(final int index, final E element) {
synchronized (this) {
return this.list.set(index, element);
}
}
@Override
public void add(int index, Object element) {
public void add(final int index, final E element) {
synchronized (this) {
this.list.add(index, element);
}
}
@Override
public Object remove(int index) {
public E remove(final int index) {
synchronized (this) {
return this.list.remove(index);
}
}
@Override
public int indexOf(Object o) {
public int indexOf(final Object o) {
synchronized (this) {
return this.list.indexOf(o);
}
}
@Override
public int lastIndexOf(Object o) {
public int lastIndexOf(final Object o) {
synchronized (this) {
return this.list.lastIndexOf(o);
}
}
@Override
public ListIterator listIterator() {
synchronized (this) {
return this.list.listIterator();
}
public @NonNull ListIterator<E> listIterator() {
// Has to be manually synched
return this.list.listIterator();
}
@Override
public ListIterator listIterator(int index) {
synchronized (this) {
return this.list.listIterator(index);
}
public @NonNull ListIterator<E> listIterator(final int index) {
// Has to be manually synched
return this.list.listIterator(index);
}
@Override
public List subList(int fromIndex, int toIndex) {
public @NonNull List<E> subList(final int fromIndex, final int toIndex) {
// Not perfect
synchronized (this) {
return this.list.subList(fromIndex, toIndex);
}
}
@Override
public boolean retainAll(Collection c) {
public boolean retainAll(@NonNull final Collection<?> c) {
synchronized (this) {
return this.list.retainAll(c);
}
}
@Override
public boolean removeAll(Collection c) {
public boolean removeAll(@NonNull final Collection<?> c) {
synchronized (this) {
return this.list.removeAll(c);
}
}
@Override
public boolean containsAll(Collection c) {
public boolean containsAll(@NonNull final Collection<?> c) {
synchronized (this) {
return this.list.containsAll(c);
}
}
@Override
public Object[] toArray(Object[] a) {
public <T> T @NonNull [] toArray(final T @NonNull [] a) {
synchronized (this) {
return this.list.toArray(a);
}
}
@Override
public void sort(final Comparator<? super E> c) {
synchronized (this) {
list.sort(c);
}
}
@Override
public void forEach(Consumer<? super E> consumer) {
synchronized (this) {
list.forEach(consumer);
}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
synchronized (this) {
return list.removeIf(filter);
}
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
synchronized (this) {
return list.equals(o);
}
}
@Override
public int hashCode() {
synchronized (this) {
return list.hashCode();
}
}
@Override
public String toString() {
synchronized (this) {
return list.toString();
}
}
}

View File

@ -20,6 +20,7 @@ package com.viaversion.viaversion.sponge.handlers;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.connection.UserConnectionImpl;
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
@ -29,21 +30,23 @@ import io.netty.handler.codec.MessageToByteEncoder;
import java.lang.reflect.Method;
public class SpongeChannelInitializer extends ChannelInitializer<Channel> {
public class SpongeChannelInitializer extends ChannelInitializer<Channel> implements WrappedChannelInitializer {
private static final Method INIT_CHANNEL_METHOD;
private final ChannelInitializer<Channel> original;
private Method method;
public SpongeChannelInitializer(ChannelInitializer<Channel> oldInit) {
this.original = oldInit;
static {
try {
this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
this.method.setAccessible(true);
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
INIT_CHANNEL_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public SpongeChannelInitializer(ChannelInitializer<Channel> oldInit) {
this.original = oldInit;
}
@Override
protected void initChannel(Channel channel) throws Exception {
@ -54,7 +57,7 @@ public class SpongeChannelInitializer extends ChannelInitializer<Channel> {
// init protocol
new ProtocolPipelineImpl(info);
// Add originals
this.method.invoke(this.original, channel);
INIT_CHANNEL_METHOD.invoke(this.original, channel);
// Add our transformers
MessageToByteEncoder encoder = new SpongeEncodeHandler(info, (MessageToByteEncoder) channel.pipeline().get("encoder"));
ByteToMessageDecoder decoder = new SpongeDecodeHandler(info, (ByteToMessageDecoder) channel.pipeline().get("decoder"));
@ -62,11 +65,17 @@ public class SpongeChannelInitializer extends ChannelInitializer<Channel> {
channel.pipeline().replace("encoder", "encoder", encoder);
channel.pipeline().replace("decoder", "decoder", decoder);
} else {
this.method.invoke(this.original, channel);
INIT_CHANNEL_METHOD.invoke(this.original, channel);
}
}
/*@Deprecated(forRemoval = true)*/
public ChannelInitializer<Channel> getOriginal() {
return original;
}
@Override
public ChannelInitializer<Channel> original() {
return original;
}
}

View File

@ -17,260 +17,47 @@
*/
package com.viaversion.viaversion.sponge.platform;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.platform.LegacyViaInjector;
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
import com.viaversion.viaversion.sponge.handlers.SpongeChannelInitializer;
import com.viaversion.viaversion.util.ListWrapper;
import com.viaversion.viaversion.util.Pair;
import com.viaversion.viaversion.util.ReflectionUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.Sponge;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
//TODO screams
public class SpongeViaInjector implements ViaInjector {
private List<ChannelFuture> injectedFutures = new ArrayList<>();
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
public class SpongeViaInjector extends LegacyViaInjector {
@Override
public void inject() throws Exception {
try {
Object connection = getServerConnection();
if (connection == null) {
throw new Exception("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
List wrapper = new ListWrapper((List) value) {
@Override
public void handleAdd(Object o) {
if (o instanceof ChannelFuture) {
try {
injectChannelFuture((ChannelFuture) o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
injectedLists.add(new Pair<>(field, connection));
field.set(connection, wrapper);
// Iterate through current list
synchronized (wrapper) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
injectChannelFuture((ChannelFuture) o);
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version.");
throw e;
}
}
private void injectChannelFuture(ChannelFuture future) throws Exception {
try {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
ChannelHandler handler = future.channel().pipeline().get(name);
try {
ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class);
bootstrapAcceptor = handler;
} catch (Exception e) {
// Not this one
}
}
// Default to first (Also allows blame to work)
if (bootstrapAcceptor == null) {
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
ChannelInitializer newInit = new SpongeChannelInitializer(oldInit);
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
injectedFutures.add(future);
} catch (NoSuchFieldException e) {
throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("We failed to inject ViaVersion, have you got late-bind enabled with something else?");
throw e;
}
public int getServerProtocolVersion() throws ReflectiveOperationException {
MinecraftVersion version = Sponge.getPlatform().getMinecraftVersion();
return (int) version.getClass().getDeclaredMethod("getProtocol").invoke(version);
}
@Override
public boolean lateProtocolVersionSetting() {
return true;
}
@Override
public void uninject() {
// TODO: Uninject from players currently online
for (ChannelFuture future : injectedFutures) {
List<String> names = future.channel().pipeline().names();
ChannelHandler bootstrapAcceptor = null;
// Pick best
for (String name : names) {
ChannelHandler handler = future.channel().pipeline().get(name);
try {
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class);
if (oldInit instanceof SpongeChannelInitializer) {
bootstrapAcceptor = handler;
}
} catch (Exception e) {
// Not this one
}
}
// Default to first
if (bootstrapAcceptor == null) {
bootstrapAcceptor = future.channel().pipeline().first();
}
try {
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
if (oldInit instanceof SpongeChannelInitializer) {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((SpongeChannelInitializer) oldInit).getOriginal());
}
} catch (Exception e) {
Via.getPlatform().getLogger().severe("Failed to remove injection handler, reload won't work with connections, please reboot!");
}
}
injectedFutures.clear();
for (Pair<Field, Object> pair : injectedLists) {
try {
Object o = pair.key().get(pair.value());
if (o instanceof ListWrapper) {
pair.key().set(pair.value(), ((ListWrapper) o).getOriginalList());
}
} catch (IllegalAccessException e) {
Via.getPlatform().getLogger().severe("Failed to remove injection, reload won't work with connections, please reboot!");
}
}
injectedLists.clear();
}
public static Object getServer() throws Exception {
return Sponge.getServer();
}
@Override
public int getServerProtocolVersion() throws Exception {
MinecraftVersion mcv = Sponge.getPlatform().getMinecraftVersion();
try {
return (int) mcv.getClass().getDeclaredMethod("getProtocol").invoke(mcv);
} catch (Exception e) {
throw new Exception("Failed to get server protocol", e);
}
}
@Override
public String getEncoderName() {
return "encoder";
}
@Override
public String getDecoderName() {
return "decoder";
}
public static Object getServerConnection() throws Exception {
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
Class<?> serverClazz = Class.forName("net.minecraft.server.MinecraftServer");
Object server = getServer();
Object connection = null;
for (Method m : serverClazz.getDeclaredMethods()) {
if (m.getReturnType() != null) {
if (m.getReturnType().getSimpleName().equals("NetworkSystem")) {
if (m.getParameterTypes().length == 0) {
connection = m.invoke(server);
}
for (Method method : serverClazz.getDeclaredMethods()) {
if (method.getReturnType().getSimpleName().equals("NetworkSystem") && method.getParameterTypes().length == 0) {
Object connection = method.invoke(Sponge.getServer());
if (connection != null) {
return connection;
}
}
}
return connection;
return null;
}
@Override
public JsonObject getDump() {
JsonObject data = new JsonObject();
// Generate information about current injections
JsonArray injectedChannelInitializers = new JsonArray();
for (ChannelFuture cf : injectedFutures) {
JsonObject info = new JsonObject();
info.addProperty("futureClass", cf.getClass().getName());
info.addProperty("channelClass", cf.channel().getClass().getName());
// Get information about the pipes for this channel future
JsonArray pipeline = new JsonArray();
for (String pipeName : cf.channel().pipeline().names()) {
JsonObject pipe = new JsonObject();
pipe.addProperty("name", pipeName);
if (cf.channel().pipeline().get(pipeName) != null) {
pipe.addProperty("class", cf.channel().pipeline().get(pipeName).getClass().getName());
try {
Object child = ReflectionUtil.get(cf.channel().pipeline().get(pipeName), "childHandler", ChannelInitializer.class);
pipe.addProperty("childClass", child.getClass().getName());
if (child instanceof SpongeChannelInitializer) {
pipe.addProperty("oldInit", ((SpongeChannelInitializer) child).getOriginal().getClass().getName());
}
} catch (Exception e) {
// Don't display
}
}
// Add to the pipeline array
pipeline.add(pipe);
}
info.add("pipeline", pipeline);
// Add to the list
injectedChannelInitializers.add(info);
}
data.add("injectedChannelInitializers", injectedChannelInitializers);
// Generate information about lists we've injected into
JsonObject wrappedLists = new JsonObject();
JsonObject currentLists = new JsonObject();
try {
for (Pair<Field, Object> pair : injectedLists) {
Object list = pair.key().get(pair.value());
// Note down the current value (could be overridden by another plugin)
currentLists.addProperty(pair.key().getName(), list.getClass().getName());
// Also if it's not overridden we can display what's inside our list (possibly another plugin)
if (list instanceof ListWrapper) {
wrappedLists.addProperty(pair.key().getName(), ((ListWrapper) list).getOriginalList().getClass().getName());
}
}
data.add("wrappedLists", wrappedLists);
data.add("currentLists", currentLists);
} catch (Exception e) {
// Ignored, fields won't be present
}
return data;
protected WrappedChannelInitializer createChannelInitializer(ChannelInitializer<Channel> oldInitializer) {
return new SpongeChannelInitializer(oldInitializer);
}
}
@Override
protected void blame(ChannelHandler bootstrapAcceptor) {
throw new RuntimeException("Unable to find core component 'childHandler', please check your plugins. Issue: " + bootstrapAcceptor.getClass().getName());
}
}

View File

@ -106,16 +106,6 @@ public class VelocityViaInjector implements ViaInjector {
return com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION.getProtocol();
}
@Override
public String getEncoderName() {
return "via-encoder";
}
@Override
public String getDecoderName() {
return "via-decoder";
}
@Override
public JsonObject getDump() {
JsonObject data = new JsonObject();