Mirror von
https://github.com/ViaVersion/ViaVersion.git
synchronisiert 2024-11-19 14:30:16 +01:00
Cleanup Bukkit/Sponge injectors
Dieser Commit ist enthalten in:
Ursprung
a306c222d4
Commit
427b0a68d0
@ -78,14 +78,18 @@ public interface ViaInjector {
|
|||||||
*
|
*
|
||||||
* @return The name
|
* @return The name
|
||||||
*/
|
*/
|
||||||
String getEncoderName();
|
default String getEncoderName() {
|
||||||
|
return "via-encoder";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the decoder for then netty pipeline for this platform.
|
* Get the name of the decoder for then netty pipeline for this platform.
|
||||||
*
|
*
|
||||||
* @return The name
|
* @return The name
|
||||||
*/
|
*/
|
||||||
String getDecoderName();
|
default String getDecoderName() {
|
||||||
|
return "via-decoder";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get any relevant data for debugging injection issues.
|
* Get any relevant data for debugging injection issues.
|
||||||
|
@ -84,14 +84,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
|
|||||||
|
|
||||||
// Check if we're using protocol support too
|
// Check if we're using protocol support too
|
||||||
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
|
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
|
@Override
|
||||||
@ -124,7 +116,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
|
|||||||
|
|
||||||
// Generate classes needed (only works if it's compat or ps)
|
// Generate classes needed (only works if it's compat or ps)
|
||||||
ClassGenerator.generate();
|
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!"));
|
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
|
||||||
if (!lateBind) {
|
if (!lateBind) {
|
||||||
|
@ -22,6 +22,7 @@ import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
|
|||||||
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
|
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
|
||||||
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
|
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
|
||||||
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
||||||
|
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
|
||||||
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
@ -30,21 +31,25 @@ import io.netty.handler.codec.MessageToByteEncoder;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
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 final ChannelInitializer<Channel> original;
|
||||||
private Method method;
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||||
|
INIT_CHANNEL_METHOD.setAccessible(true);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
|
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
|
||||||
this.original = oldInit;
|
this.original = oldInit;
|
||||||
try {
|
|
||||||
this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
|
||||||
this.method.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated/*(forRemoval = true)*/
|
||||||
public ChannelInitializer<Channel> getOriginal() {
|
public ChannelInitializer<Channel> getOriginal() {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
@ -52,7 +57,7 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> {
|
|||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel channel) throws Exception {
|
protected void initChannel(Channel channel) throws Exception {
|
||||||
// Add originals
|
// Add originals
|
||||||
this.method.invoke(this.original, channel);
|
INIT_CHANNEL_METHOD.invoke(this.original, channel);
|
||||||
afterChannelInitialize(channel);
|
afterChannelInitialize(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,4 +77,9 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> {
|
|||||||
channel.pipeline().replace("encoder", "encoder", encoder);
|
channel.pipeline().replace("encoder", "encoder", encoder);
|
||||||
channel.pipeline().replace("decoder", "decoder", decoder);
|
channel.pipeline().replace("decoder", "decoder", decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelInitializer<Channel> original() {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.viaversion.viaversion.bukkit.platform;
|
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.handlers.BukkitChannelInitializer;
|
||||||
import com.viaversion.viaversion.bukkit.util.NMSUtil;
|
import com.viaversion.viaversion.bukkit.util.NMSUtil;
|
||||||
import com.viaversion.viaversion.util.ConcurrentList;
|
import com.viaversion.viaversion.platform.LegacyViaInjector;
|
||||||
import com.viaversion.viaversion.util.ListWrapper;
|
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
|
||||||
import com.viaversion.viaversion.util.Pair;
|
|
||||||
import com.viaversion.viaversion.util.ReflectionUtil;
|
import com.viaversion.viaversion.util.ReflectionUtil;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
@ -33,177 +28,43 @@ import io.netty.channel.ChannelHandler;
|
|||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
//TODO screams
|
public class BukkitViaInjector extends LegacyViaInjector {
|
||||||
public class BukkitViaInjector implements ViaInjector {
|
|
||||||
private final List<ChannelFuture> injectedFutures = new ArrayList<>();
|
|
||||||
private final List<Pair<Field, Object>> injectedLists = new ArrayList<>();
|
|
||||||
|
|
||||||
private boolean protocolLib;
|
private boolean protocolLib;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inject() throws Exception {
|
public void inject() throws ReflectiveOperationException {
|
||||||
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
|
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
|
||||||
PaperViaInjector.setPaperChannelInitializeListener();
|
PaperViaInjector.setPaperChannelInitializeListener();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
super.inject();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uninject() throws Exception {
|
public void uninject() throws ReflectiveOperationException {
|
||||||
// TODO: Uninject from players currently online to prevent protocol lib issues.
|
|
||||||
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
|
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
|
||||||
PaperViaInjector.removePaperChannelInitializeListener();
|
PaperViaInjector.removePaperChannelInitializeListener();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ChannelFuture future : injectedFutures) {
|
super.uninject();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean lateProtocolVersionSetting() {
|
public int getServerProtocolVersion() throws ReflectiveOperationException {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getServerProtocolVersion() throws Exception {
|
|
||||||
if (PaperViaInjector.PAPER_PROTOCOL_METHOD) {
|
if (PaperViaInjector.PAPER_PROTOCOL_METHOD) {
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
return Bukkit.getUnsafe().getProtocolVersion();
|
return Bukkit.getUnsafe().getProtocolVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Time to go on a journey! The protocol version is hidden inside an int in ServerPing.ServerData
|
||||||
// Grab a static instance of the server
|
// Grab a static instance of the server
|
||||||
Class<?> serverClazz = NMSUtil.nms("MinecraftServer", "net.minecraft.server.MinecraftServer");
|
Class<?> serverClazz = NMSUtil.nms("MinecraftServer", "net.minecraft.server.MinecraftServer");
|
||||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
||||||
@ -214,49 +75,41 @@ public class BukkitViaInjector implements ViaInjector {
|
|||||||
"net.minecraft.network.protocol.status.ServerPing"
|
"net.minecraft.network.protocol.status.ServerPing"
|
||||||
);
|
);
|
||||||
Object ping = null;
|
Object ping = null;
|
||||||
// Search for ping method
|
for (Field field : serverClazz.getDeclaredFields()) {
|
||||||
for (Field f : serverClazz.getDeclaredFields()) {
|
if (field.getType() == pingClazz) {
|
||||||
if (f.getType() != null) {
|
field.setAccessible(true);
|
||||||
if (f.getType().getSimpleName().equals("ServerPing")) {
|
ping = field.get(server);
|
||||||
f.setAccessible(true);
|
break;
|
||||||
ping = f.get(server);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (ping != null) {
|
// Now get the ServerData inside ServerPing
|
||||||
|
Class<?> serverDataClass = NMSUtil.nms(
|
||||||
|
"ServerPing$ServerData",
|
||||||
|
"net.minecraft.network.protocol.status.ServerPing$ServerData"
|
||||||
|
);
|
||||||
Object serverData = null;
|
Object serverData = null;
|
||||||
for (Field f : pingClazz.getDeclaredFields()) {
|
for (Field field : pingClazz.getDeclaredFields()) {
|
||||||
if (f.getType() != null) {
|
if (field.getType() == serverDataClass) {
|
||||||
if (f.getType().getSimpleName().endsWith("ServerData")) {
|
field.setAccessible(true);
|
||||||
f.setAccessible(true);
|
serverData = field.get(ping);
|
||||||
serverData = f.get(ping);
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (protocolVersion != -1) {
|
||||||
return protocolVersion;
|
return protocolVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
throw new RuntimeException("Failed to get server");
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Exception("Failed to get server", e);
|
|
||||||
}
|
|
||||||
throw new Exception("Failed to get server");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEncoderName() {
|
|
||||||
return "encoder";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -264,133 +117,77 @@ public class BukkitViaInjector implements ViaInjector {
|
|||||||
return protocolLib ? "protocol_lib_decoder" : "decoder";
|
return protocolLib ? "protocol_lib_decoder" : "decoder";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object getServerConnection() throws Exception {
|
@Override
|
||||||
Class<?> serverClazz = NMSUtil.nms(
|
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
|
||||||
|
Class<?> serverClass = NMSUtil.nms(
|
||||||
"MinecraftServer",
|
"MinecraftServer",
|
||||||
"net.minecraft.server.MinecraftServer"
|
"net.minecraft.server.MinecraftServer"
|
||||||
);
|
);
|
||||||
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
|
Class<?> connectionClass = NMSUtil.nms(
|
||||||
Object connection = null;
|
"ServerConnection",
|
||||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
"net.minecraft.server.network.ServerConnection"
|
||||||
if (m.getReturnType() != null) {
|
);
|
||||||
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
|
|
||||||
if (m.getParameterTypes().length == 0) {
|
Object server = ReflectionUtil.invokeStatic(serverClass, "getServer");
|
||||||
connection = m.invoke(server);
|
for (Method method : serverClass.getDeclaredMethods()) {
|
||||||
}
|
if (method.getReturnType() != connectionClass || method.getParameterTypes().length != 0) {
|
||||||
}
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBinded() {
|
// We need the method that initiates the connection if not yet set
|
||||||
if (PaperViaInjector.PAPER_INJECTION_METHOD) return true;
|
Object connection = method.invoke(server);
|
||||||
try {
|
if (connection != null) {
|
||||||
Object connection = getServerConnection();
|
return connection;
|
||||||
if (connection == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject getDump() {
|
protected WrappedChannelInitializer createChannelInitializer(ChannelInitializer<Channel> oldInitializer) {
|
||||||
JsonObject data = new JsonObject();
|
return new BukkitChannelInitializer(oldInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate information about current injections
|
@Override
|
||||||
JsonArray injectedChannelInitializers = new JsonArray();
|
protected void blame(ChannelHandler bootstrapAcceptor) throws ReflectiveOperationException {
|
||||||
for (ChannelFuture cf : injectedFutures) {
|
// Let's find who to blame!
|
||||||
JsonObject info = new JsonObject();
|
ClassLoader classLoader = bootstrapAcceptor.getClass().getClassLoader();
|
||||||
info.addProperty("futureClass", cf.getClass().getName());
|
if (classLoader.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
|
||||||
info.addProperty("channelClass", cf.channel().getClass().getName());
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get information about the pipes for this channel future
|
public boolean isBinded() {
|
||||||
JsonArray pipeline = new JsonArray();
|
if (PaperViaInjector.PAPER_INJECTION_METHOD) {
|
||||||
for (String pipeName : cf.channel().pipeline().names()) {
|
return true;
|
||||||
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 {
|
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();
|
Object connection = getServerConnection();
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
Via.getPlatform().getLogger().warning("We failed to find the core component 'ServerConnection', please file an issue on our GitHub.");
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Field field : connection.getClass().getDeclaredFields()) {
|
for (Field field : connection.getClass().getDeclaredFields()) {
|
||||||
field.setAccessible(true);
|
if (!List.class.isAssignableFrom(field.getType())) {
|
||||||
Object value = field.get(connection);
|
continue;
|
||||||
if (!(value instanceof List)) continue;
|
|
||||||
if (value instanceof ConcurrentList) continue;
|
|
||||||
|
|
||||||
ConcurrentList list = new ConcurrentList();
|
|
||||||
list.addAll((Collection) value);
|
|
||||||
field.set(connection, list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
field.setAccessible(true);
|
||||||
|
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 (ReflectiveOperationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProtocolLib(boolean protocolLib) {
|
public void setProtocolLib(boolean protocolLib) {
|
||||||
|
@ -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);
|
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 {
|
private ChannelInitializer<Channel> getChannelInitializer() throws Exception {
|
||||||
Class<?> pipelineUtils = Class.forName("net.md_5.bungee.netty.PipelineUtils");
|
Class<?> pipelineUtils = Class.forName("net.md_5.bungee.netty.PipelineUtils");
|
||||||
Field field = pipelineUtils.getDeclaredField("SERVER_CHILD");
|
Field field = pipelineUtils.getDeclaredField("SERVER_CHILD");
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,28 +17,40 @@
|
|||||||
*/
|
*/
|
||||||
package com.viaversion.viaversion.util;
|
package com.viaversion.viaversion.util;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
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 final class SynchronizedListWrapper<E> implements List<E> {
|
||||||
public abstract class ListWrapper implements List {
|
private final List<E> list;
|
||||||
private final List list;
|
private final Consumer<E> addHandler;
|
||||||
|
|
||||||
public ListWrapper(List inputList) {
|
public SynchronizedListWrapper(final List<E> inputList, final Consumer<E> addHandler) {
|
||||||
this.list = inputList;
|
this.list = inputList;
|
||||||
|
this.addHandler = addHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void handleAdd(Object o);
|
public List<E> originalList() {
|
||||||
|
|
||||||
public List getOriginalList() {
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleAdd(E o) {
|
||||||
|
synchronized (this) {
|
||||||
|
addHandler.accept(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -53,59 +65,57 @@ public abstract class ListWrapper implements List {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object o) {
|
public boolean contains(final Object o) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.contains(o);
|
return this.list.contains(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator iterator() {
|
public @NonNull Iterator<E> iterator() {
|
||||||
synchronized (this) {
|
// Has to be manually synched
|
||||||
return listIterator();
|
return listIterator();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] toArray() {
|
public Object @NonNull [] toArray() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.toArray();
|
return this.list.toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(Object o) {
|
public boolean add(final E o) {
|
||||||
handleAdd(o);
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
handleAdd(o);
|
||||||
return this.list.add(o);
|
return this.list.add(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(Object o) {
|
public boolean remove(final Object o) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.remove(o);
|
return this.list.remove(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(Collection c) {
|
public boolean addAll(final Collection<? extends E> c) {
|
||||||
for (Object o : c) {
|
synchronized (this) {
|
||||||
|
for (final E o : c) {
|
||||||
handleAdd(o);
|
handleAdd(o);
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
|
||||||
return this.list.addAll(c);
|
return this.list.addAll(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(int index, Collection c) {
|
public boolean addAll(final int index, final Collection<? extends E> c) {
|
||||||
for (Object o : c) {
|
synchronized (this) {
|
||||||
|
for (final E o : c) {
|
||||||
handleAdd(o);
|
handleAdd(o);
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
|
||||||
return this.list.addAll(index, c);
|
return this.list.addAll(index, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,93 +128,135 @@ public abstract class ListWrapper implements List {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(int index) {
|
public E get(final int index) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.get(index);
|
return this.list.get(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object set(int index, Object element) {
|
public E set(final int index, final E element) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.set(index, element);
|
return this.list.set(index, element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(int index, Object element) {
|
public void add(final int index, final E element) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
this.list.add(index, element);
|
this.list.add(index, element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object remove(int index) {
|
public E remove(final int index) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.remove(index);
|
return this.list.remove(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int indexOf(Object o) {
|
public int indexOf(final Object o) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.indexOf(o);
|
return this.list.indexOf(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int lastIndexOf(Object o) {
|
public int lastIndexOf(final Object o) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.lastIndexOf(o);
|
return this.list.lastIndexOf(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListIterator listIterator() {
|
public @NonNull ListIterator<E> listIterator() {
|
||||||
synchronized (this) {
|
// Has to be manually synched
|
||||||
return this.list.listIterator();
|
return this.list.listIterator();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListIterator listIterator(int index) {
|
public @NonNull ListIterator<E> listIterator(final int index) {
|
||||||
synchronized (this) {
|
// Has to be manually synched
|
||||||
return this.list.listIterator(index);
|
return this.list.listIterator(index);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List subList(int fromIndex, int toIndex) {
|
public @NonNull List<E> subList(final int fromIndex, final int toIndex) {
|
||||||
|
// Not perfect
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.subList(fromIndex, toIndex);
|
return this.list.subList(fromIndex, toIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean retainAll(Collection c) {
|
public boolean retainAll(@NonNull final Collection<?> c) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.retainAll(c);
|
return this.list.retainAll(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeAll(Collection c) {
|
public boolean removeAll(@NonNull final Collection<?> c) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.removeAll(c);
|
return this.list.removeAll(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsAll(Collection c) {
|
public boolean containsAll(@NonNull final Collection<?> c) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.containsAll(c);
|
return this.list.containsAll(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] toArray(Object[] a) {
|
public <T> T @NonNull [] toArray(final T @NonNull [] a) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
return this.list.toArray(a);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ package com.viaversion.viaversion.sponge.handlers;
|
|||||||
import com.viaversion.viaversion.api.Via;
|
import com.viaversion.viaversion.api.Via;
|
||||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||||
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
||||||
|
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
|
||||||
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
@ -29,21 +30,23 @@ import io.netty.handler.codec.MessageToByteEncoder;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
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 final ChannelInitializer<Channel> original;
|
||||||
private Method method;
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||||
|
INIT_CHANNEL_METHOD.setAccessible(true);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SpongeChannelInitializer(ChannelInitializer<Channel> oldInit) {
|
public SpongeChannelInitializer(ChannelInitializer<Channel> oldInit) {
|
||||||
this.original = oldInit;
|
this.original = oldInit;
|
||||||
try {
|
|
||||||
this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
|
||||||
this.method.setAccessible(true);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel channel) throws Exception {
|
protected void initChannel(Channel channel) throws Exception {
|
||||||
@ -54,7 +57,7 @@ public class SpongeChannelInitializer extends ChannelInitializer<Channel> {
|
|||||||
// init protocol
|
// init protocol
|
||||||
new ProtocolPipelineImpl(info);
|
new ProtocolPipelineImpl(info);
|
||||||
// Add originals
|
// Add originals
|
||||||
this.method.invoke(this.original, channel);
|
INIT_CHANNEL_METHOD.invoke(this.original, channel);
|
||||||
// Add our transformers
|
// Add our transformers
|
||||||
MessageToByteEncoder encoder = new SpongeEncodeHandler(info, (MessageToByteEncoder) channel.pipeline().get("encoder"));
|
MessageToByteEncoder encoder = new SpongeEncodeHandler(info, (MessageToByteEncoder) channel.pipeline().get("encoder"));
|
||||||
ByteToMessageDecoder decoder = new SpongeDecodeHandler(info, (ByteToMessageDecoder) channel.pipeline().get("decoder"));
|
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("encoder", "encoder", encoder);
|
||||||
channel.pipeline().replace("decoder", "decoder", decoder);
|
channel.pipeline().replace("decoder", "decoder", decoder);
|
||||||
} else {
|
} else {
|
||||||
this.method.invoke(this.original, channel);
|
INIT_CHANNEL_METHOD.invoke(this.original, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*@Deprecated(forRemoval = true)*/
|
||||||
public ChannelInitializer<Channel> getOriginal() {
|
public ChannelInitializer<Channel> getOriginal() {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelInitializer<Channel> original() {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,260 +17,47 @@
|
|||||||
*/
|
*/
|
||||||
package com.viaversion.viaversion.sponge.platform;
|
package com.viaversion.viaversion.sponge.platform;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.viaversion.viaversion.platform.LegacyViaInjector;
|
||||||
import com.google.gson.JsonObject;
|
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
|
||||||
import com.viaversion.viaversion.api.Via;
|
|
||||||
import com.viaversion.viaversion.api.platform.ViaInjector;
|
|
||||||
import com.viaversion.viaversion.sponge.handlers.SpongeChannelInitializer;
|
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.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.spongepowered.api.MinecraftVersion;
|
import org.spongepowered.api.MinecraftVersion;
|
||||||
import org.spongepowered.api.Sponge;
|
import org.spongepowered.api.Sponge;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
//TODO screams
|
public class SpongeViaInjector extends LegacyViaInjector {
|
||||||
public class SpongeViaInjector implements ViaInjector {
|
|
||||||
private List<ChannelFuture> injectedFutures = new ArrayList<>();
|
|
||||||
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inject() throws Exception {
|
public int getServerProtocolVersion() throws ReflectiveOperationException {
|
||||||
try {
|
MinecraftVersion version = Sponge.getPlatform().getMinecraftVersion();
|
||||||
Object connection = getServerConnection();
|
return (int) version.getClass().getDeclaredMethod("getProtocol").invoke(version);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean lateProtocolVersionSetting() {
|
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
|
||||||
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 {
|
|
||||||
Class<?> serverClazz = Class.forName("net.minecraft.server.MinecraftServer");
|
Class<?> serverClazz = Class.forName("net.minecraft.server.MinecraftServer");
|
||||||
Object server = getServer();
|
for (Method method : serverClazz.getDeclaredMethods()) {
|
||||||
Object connection = null;
|
if (method.getReturnType().getSimpleName().equals("NetworkSystem") && method.getParameterTypes().length == 0) {
|
||||||
for (Method m : serverClazz.getDeclaredMethods()) {
|
Object connection = method.invoke(Sponge.getServer());
|
||||||
if (m.getReturnType() != null) {
|
if (connection != null) {
|
||||||
if (m.getReturnType().getSimpleName().equals("NetworkSystem")) {
|
|
||||||
if (m.getParameterTypes().length == 0) {
|
|
||||||
connection = m.invoke(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject getDump() {
|
protected WrappedChannelInitializer createChannelInitializer(ChannelInitializer<Channel> oldInitializer) {
|
||||||
JsonObject data = new JsonObject();
|
return new SpongeChannelInitializer(oldInitializer);
|
||||||
|
|
||||||
// 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;
|
@Override
|
||||||
|
protected void blame(ChannelHandler bootstrapAcceptor) {
|
||||||
|
throw new RuntimeException("Unable to find core component 'childHandler', please check your plugins. Issue: " + bootstrapAcceptor.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -106,16 +106,6 @@ public class VelocityViaInjector implements ViaInjector {
|
|||||||
return com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION.getProtocol();
|
return com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION.getProtocol();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEncoderName() {
|
|
||||||
return "via-encoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDecoderName() {
|
|
||||||
return "via-decoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject getDump() {
|
public JsonObject getDump() {
|
||||||
JsonObject data = new JsonObject();
|
JsonObject data = new JsonObject();
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren