diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/platform/BukkitViaInjector.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/platform/BukkitViaInjector.java index 17b429241..004cb07ea 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/platform/BukkitViaInjector.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/platform/BukkitViaInjector.java @@ -1,5 +1,7 @@ package us.myles.ViaVersion.bukkit.platform; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; @@ -16,7 +18,6 @@ import us.myles.ViaVersion.util.ReflectionUtil; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.List; public class BukkitViaInjector implements ViaInjector { @@ -275,4 +276,65 @@ public class BukkitViaInjector implements ViaInjector { } 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 pair : injectedLists) { + Object list = pair.getKey().get(pair.getValue()); + // Note down the current value (could be overridden by another plugin) + currentLists.addProperty(pair.getKey().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.getKey().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; + } } diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeChannelInitializer.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeChannelInitializer.java index dce420c08..3902ddf56 100644 --- a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeChannelInitializer.java +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeChannelInitializer.java @@ -3,13 +3,14 @@ package us.myles.ViaVersion.bungee.handlers; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; +import lombok.Getter; import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.protocol.ProtocolPipeline; import java.lang.reflect.Method; -public class BungeeChannelInitializer extends ChannelInitializer { - +public class BungeeChannelInitializer extends ChannelInitializer { + @Getter private final ChannelInitializer original; private Method method; @@ -24,7 +25,7 @@ public class BungeeChannelInitializer extends ChannelInitializer } @Override - protected void initChannel(SocketChannel socketChannel) throws Exception { + protected void initChannel(Channel socketChannel) throws Exception { UserConnection info = new UserConnection(socketChannel); // init protocol new ProtocolPipeline(info); diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/platform/BungeeViaInjector.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/platform/BungeeViaInjector.java index d8efb2627..edc66c086 100644 --- a/bungee/src/main/java/us/myles/ViaVersion/bungee/platform/BungeeViaInjector.java +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/platform/BungeeViaInjector.java @@ -1,5 +1,6 @@ package us.myles.ViaVersion.bungee.platform; +import com.google.gson.JsonObject; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import us.myles.ViaVersion.api.Via; @@ -56,4 +57,31 @@ public class BungeeViaInjector implements ViaInjector { public String getDecoderName() { return "via-decoder"; } + + private ChannelInitializer getChannelInitializer() throws Exception { + Class pipelineUtils = Class.forName("net.md_5.bungee.netty.PipelineUtils"); + Field field = pipelineUtils.getDeclaredField("SERVER_CHILD"); + field.setAccessible(true); + // Remove any final stuff + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + + return (ChannelInitializer) field.get(null); + } + + @Override + public JsonObject getDump() { + JsonObject data = new JsonObject(); + try { + ChannelInitializer initializer = getChannelInitializer(); + data.addProperty("currentInitializer", initializer.getClass().getName()); + if (initializer instanceof BungeeChannelInitializer) { + data.addProperty("originalInitializer", ((BungeeChannelInitializer) initializer).getOriginal().getClass().getName()); + } + } catch (Exception e) { + // Ignored, not printed in the dump + } + return data; + } } diff --git a/common/src/main/java/us/myles/ViaVersion/api/platform/ViaInjector.java b/common/src/main/java/us/myles/ViaVersion/api/platform/ViaInjector.java index 14f853a2b..f075c5054 100644 --- a/common/src/main/java/us/myles/ViaVersion/api/platform/ViaInjector.java +++ b/common/src/main/java/us/myles/ViaVersion/api/platform/ViaInjector.java @@ -1,5 +1,7 @@ package us.myles.ViaVersion.api.platform; +import com.google.gson.JsonObject; + public interface ViaInjector { /** * Inject into the current Platform @@ -36,4 +38,11 @@ public interface ViaInjector { * @return The name */ String getDecoderName(); + + /** + * Get any relevant data for debugging injection issues. + * + * @return JSONObject containing the data + */ + JsonObject getDump(); } diff --git a/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/DumpSubCmd.java b/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/DumpSubCmd.java index c36822a2e..4a1e29fa5 100644 --- a/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/DumpSubCmd.java +++ b/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/DumpSubCmd.java @@ -48,7 +48,7 @@ public class DumpSubCmd extends ViaSubCommand { Map configuration = Via.getPlatform().getConfigurationProvider().getValues(); - final DumpTemplate template = new DumpTemplate(version, configuration, Via.getPlatform().getDump()); + final DumpTemplate template = new DumpTemplate(version, configuration, Via.getPlatform().getDump(), Via.getManager().getInjector().getDump()); Via.getPlatform().runAsync(new Runnable() { @Override diff --git a/common/src/main/java/us/myles/ViaVersion/dump/DumpTemplate.java b/common/src/main/java/us/myles/ViaVersion/dump/DumpTemplate.java index 6c35344a6..ec15a1e40 100644 --- a/common/src/main/java/us/myles/ViaVersion/dump/DumpTemplate.java +++ b/common/src/main/java/us/myles/ViaVersion/dump/DumpTemplate.java @@ -12,4 +12,5 @@ public class DumpTemplate { private VersionInfo versionInfo; private Map configuration; private JsonObject platformDump; + private JsonObject injectionDump; } diff --git a/common/src/main/java/us/myles/ViaVersion/util/ListWrapper.java b/common/src/main/java/us/myles/ViaVersion/util/ListWrapper.java index 00e13278f..4e3837bfb 100644 --- a/common/src/main/java/us/myles/ViaVersion/util/ListWrapper.java +++ b/common/src/main/java/us/myles/ViaVersion/util/ListWrapper.java @@ -20,39 +20,53 @@ public abstract class ListWrapper implements List { @Override public int size() { - return this.list.size(); + synchronized (this) { + return this.list.size(); + } } @Override public boolean isEmpty() { - return this.list.isEmpty(); + synchronized (this) { + return this.list.isEmpty(); + } } @Override public boolean contains(Object o) { - return this.list.contains(o); + synchronized (this) { + return this.list.contains(o); + } } @Override public Iterator iterator() { - return listIterator(); + synchronized (this) { + return listIterator(); + } } @Override public Object[] toArray() { - return this.list.toArray(); + synchronized (this) { + return this.list.toArray(); + } } @Override public boolean add(Object o) { handleAdd(o); - return this.list.add(o); + synchronized (this) { + return this.list.add(o); + } } @Override public boolean remove(Object o) { - return this.list.remove(o); + synchronized (this) { + return this.list.remove(o); + } } @Override @@ -60,7 +74,9 @@ public abstract class ListWrapper implements List { for (Object o : c) { handleAdd(o); } - return this.list.addAll(c); + synchronized (this) { + return this.list.addAll(c); + } } @Override @@ -68,76 +84,106 @@ public abstract class ListWrapper implements List { for (Object o : c) { handleAdd(o); } - return this.list.addAll(index, c); + synchronized (this) { + return this.list.addAll(index, c); + } } @Override public void clear() { - this.list.clear(); + synchronized (this) { + this.list.clear(); + } } @Override public Object get(int index) { - return this.list.get(index); + synchronized (this) { + return this.list.get(index); + } } @Override public Object set(int index, Object element) { - return this.list.set(index, element); + synchronized (this) { + return this.list.set(index, element); + } } @Override public void add(int index, Object element) { - this.list.add(index, element); + synchronized (this) { + this.list.add(index, element); + } } @Override public Object remove(int index) { - return this.list.remove(index); + synchronized (this) { + return this.list.remove(index); + } } @Override public int indexOf(Object o) { - return this.list.indexOf(o); + synchronized (this) { + return this.list.indexOf(o); + } } @Override public int lastIndexOf(Object o) { - return this.list.lastIndexOf(o); + synchronized (this) { + return this.list.lastIndexOf(o); + } } @Override public ListIterator listIterator() { - return this.list.listIterator(); + synchronized (this) { + return this.list.listIterator(); + } } @Override public ListIterator listIterator(int index) { - return this.list.listIterator(index); + synchronized (this) { + return this.list.listIterator(index); + } } @Override public List subList(int fromIndex, int toIndex) { - return this.list.subList(fromIndex, toIndex); + synchronized (this) { + return this.list.subList(fromIndex, toIndex); + } } @Override public boolean retainAll(Collection c) { - return this.list.retainAll(c); + synchronized (this) { + return this.list.retainAll(c); + } } @Override public boolean removeAll(Collection c) { - return this.list.removeAll(c); + synchronized (this) { + return this.list.removeAll(c); + } } @Override public boolean containsAll(Collection c) { - return this.list.containsAll(c); + synchronized (this) { + return this.list.containsAll(c); + } } @Override public Object[] toArray(Object[] a) { - return this.list.toArray(a); + synchronized (this) { + return this.list.toArray(a); + } } } diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/platform/SpongeViaInjector.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/platform/SpongeViaInjector.java index 0b0a00dc1..284ad0d24 100644 --- a/sponge/src/main/java/us/myles/ViaVersion/sponge/platform/SpongeViaInjector.java +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/platform/SpongeViaInjector.java @@ -1,5 +1,7 @@ package us.myles.ViaVersion.sponge.platform; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; @@ -189,4 +191,63 @@ public class SpongeViaInjector implements ViaInjector { return connection; } + @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 pair : injectedLists) { + Object list = pair.getKey().get(pair.getValue()); + // Note down the current value (could be overridden by another plugin) + currentLists.addProperty(pair.getKey().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.getKey().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; + } } diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java index 64c7c4f6e..4448f6b5f 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java @@ -1,5 +1,6 @@ package us.myles.ViaVersion.velocity.platform; +import com.google.gson.JsonObject; import io.netty.channel.ChannelInitializer; import us.myles.ViaVersion.VelocityPlugin; import us.myles.ViaVersion.api.Via; @@ -23,13 +24,19 @@ public class VelocityViaInjector implements ViaInjector { } } + private ChannelInitializer getInitializer() throws Exception { + Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); + Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getServerChannelInitializer"); + return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get"); + } + @Override public void inject() throws Exception { Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getServerChannelInitializer"); - ChannelInitializer originalIntializer = (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get"); + ChannelInitializer originalInitializer = getInitializer(); channelInitializerHolder.getClass().getMethod("set", ChannelInitializer.class) - .invoke(channelInitializerHolder, new VelocityChannelInitializer(originalIntializer)); + .invoke(channelInitializerHolder, new VelocityChannelInitializer(originalInitializer)); } @Override @@ -62,4 +69,15 @@ public class VelocityViaInjector implements ViaInjector { public String getDecoderName() { return "via-decoder"; } + + @Override + public JsonObject getDump() { + JsonObject data = new JsonObject(); + try { + data.addProperty("currentInitializer", getInitializer().getClass().getName()); + } catch (Exception e) { + // Ignored + } + return data; + } }