Better support for ServerConnection detection, and also adding injectors. (Basic late-bind support is now available, but does not work with ProtocolLib due to it injecting first.)

This commit is contained in:
Myles 2016-03-09 00:51:50 +00:00
parent 3a217503fc
commit f65666b7a6
2 changed files with 210 additions and 40 deletions

View File

@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture; 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 io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -25,9 +24,11 @@ import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.listeners.CommandBlockListener; import us.myles.ViaVersion.listeners.CommandBlockListener;
import us.myles.ViaVersion.update.UpdateListener; import us.myles.ViaVersion.update.UpdateListener;
import us.myles.ViaVersion.update.UpdateUtil; import us.myles.ViaVersion.update.UpdateUtil;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ReflectionUtil; import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -54,8 +55,8 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
}).get(10, TimeUnit.SECONDS); }).get(10, TimeUnit.SECONDS);
} catch (Exception e) { } catch (Exception e) {
System.out.println("Error fetching hand item: " + e.getClass().getName()); System.out.println("Error fetching hand item: " + e.getClass().getName());
if(ViaVersion.getInstance().isDebug()) if (ViaVersion.getInstance().isDebug())
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
} }
@ -71,13 +72,8 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
} }
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
try { injectPacketHandler();
injectPacketHandler();
System.setProperty("ViaVersion", getDescription().getVersion());
} catch (Exception e) {
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
e.printStackTrace();
}
if (getConfig().getBoolean("checkforupdates")) if (getConfig().getBoolean("checkforupdates"))
UpdateUtil.sendUpdateMessage(this); UpdateUtil.sendUpdateMessage(this);
@ -95,39 +91,75 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
getCommand("viaversion").setExecutor(new ViaVersionCommand(this)); getCommand("viaversion").setExecutor(new ViaVersionCommand(this));
} }
public void injectPacketHandler() throws Exception { public void injectPacketHandler() {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer"); try {
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer"); Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object connection = serverClazz.getDeclaredMethod("getServerConnection").invoke(server); Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
// loop through all fields checking if list Object connection = serverClazz.getDeclaredMethod("getServerConnection").invoke(server);
boolean injected = false; if (connection == null) {
for (Field field : connection.getClass().getDeclaredFields()) { System.out.println("connection is null!!");
field.setAccessible(true); //try others
Object value = field.get(connection); for (Method m : serverClazz.getDeclaredMethods()) {
if (value instanceof List) { if (m.getReturnType() != null && !m.getName().equals("getServerConnection")) {
for (Object o : (List) value) { if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
if (o instanceof ChannelFuture) { if (m.getParameterTypes().length == 0) {
ChannelFuture future = (ChannelFuture) o; connection = m.invoke(server);
ChannelPipeline pipeline = future.channel().pipeline(); }
ChannelHandler bootstrapAcceptor = pipeline.first(); }
try { }
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class); }
ChannelInitializer newInit = new ViaVersionInitializer(oldInit); if (connection == null) {
getLogger().warning("We failed to find the ServerConnection? :(");
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit); return;
} catch (NoSuchFieldException e) { }
// field not found }
throw new Exception("Unable to find childHandler, blame " + bootstrapAcceptor.getClass().getName()); if (connection != null) {
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
field.set(connection, new ListWrapper((List) value) {
@Override
public void handleAdd(Object o) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
}
}
});
// Iterate through current list
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
} else {
break; // not the right list.
}
} }
injected = true;
} else {
break; // not the right list.
} }
} }
} }
System.setProperty("ViaVersion", getDescription().getVersion());
} catch (Exception e) {
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
e.printStackTrace();
} }
if (!injected) { }
throw new Exception("Could not find server to inject (Please ensure late-bind in your spigot.yml is false)");
private void inject(ChannelFuture future) {
try {
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
try {
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
} catch (NoSuchFieldException e) {
// field not found
throw new Exception("Unable to find childHandler, blame " + bootstrapAcceptor.getClass().getName());
}
} catch (Exception e) {
getLogger().severe("Have you got late-bind enabled with something else? (ProtocolLib?)");
e.printStackTrace();
} }
} }
@ -217,8 +249,8 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
} }
} catch (Exception e) { } catch (Exception e) {
System.out.println("Failed to run task: " + e.getClass().getName()); System.out.println("Failed to run task: " + e.getClass().getName());
if(ViaVersion.getInstance().isDebug()) if (ViaVersion.getInstance().isDebug())
e.printStackTrace(); e.printStackTrace();
} }
} }
} }

View File

@ -0,0 +1,138 @@
package us.myles.ViaVersion.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public abstract class ListWrapper implements List {
private final List list;
public ListWrapper(List inputList) {
this.list = inputList;
}
public abstract void handleAdd(Object o);
@Override
public int size() {
return this.list.size();
}
@Override
public boolean isEmpty() {
return this.list.isEmpty();
}
@Override
public boolean contains(Object o) {
return this.list.contains(o);
}
@Override
public Iterator iterator() {
return this.list.iterator();
}
@Override
public Object[] toArray() {
return this.list.toArray();
}
@Override
public boolean add(Object o) {
handleAdd(o);
return this.list.add(o);
}
@Override
public boolean remove(Object o) {
return this.list.remove(o);
}
@Override
public boolean addAll(Collection c) {
for (Object o : c) {
handleAdd(o);
}
return this.list.addAll(c);
}
@Override
public boolean addAll(int index, Collection c) {
for (Object o : c) {
handleAdd(o);
}
return this.list.addAll(index, c);
}
@Override
public void clear() {
this.list.clear();
}
@Override
public Object get(int index) {
return this.list.get(index);
}
@Override
public Object set(int index, Object element) {
return this.list.set(index, element);
}
@Override
public void add(int index, Object element) {
this.list.add(index, element);
}
@Override
public Object remove(int index) {
return this.list.remove(index);
}
@Override
public int indexOf(Object o) {
return this.list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return this.list.lastIndexOf(o);
}
@Override
public ListIterator listIterator() {
return this.list.listIterator();
}
@Override
public ListIterator listIterator(int index) {
return this.listIterator(index);
}
@Override
public List subList(int fromIndex, int toIndex) {
return this.list.subList(fromIndex, toIndex);
}
@Override
public boolean retainAll(Collection c) {
return this.list.retainAll(c);
}
@Override
public boolean removeAll(Collection c) {
return this.list.removeAll(c);
}
@Override
public boolean containsAll(Collection c) {
return this.list.containsAll(c);
}
@Override
public Object[] toArray(Object[] a) {
return this.list.toArray(a);
}
}