3
0
Mirror von https://github.com/ViaVersion/ViaVersion.git synchronisiert 2024-12-27 08:30:09 +01:00

Merge remote-tracking branch 'origin/master' into dev

Dieser Commit ist enthalten in:
Nassim Jahnke 2021-10-01 22:46:37 +02:00
Commit 2afa3fd9de
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 6BE3B555EBC5982B
19 geänderte Dateien mit 647 neuen und 932 gelöschten Zeilen

3
.gitignore vendored
Datei anzeigen

@ -105,3 +105,6 @@ nbdist/
nbactions.xml
nb-configuration.xml
.nb-gradle/
### MacOS ###
.DS_Store

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,264 @@
/*
* 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";
}
/**
* Returns the Vanilla server connection object the channels to be injected should be searched in.
*
* @return server connection object, or null if failed
*/
protected abstract @Nullable Object getServerConnection() throws ReflectiveOperationException;
/**
* Returns a new Via channel initializer wrapping the original one.
*
* @param oldInitializer original channel initializer
* @return wrapped Via channel initializer
*/
protected abstract WrappedChannelInitializer createChannelInitializer(ChannelInitializer<Channel> oldInitializer);
/**
* Should throw a {@link RuntimeException} with information on what/who might have caused an issue.
* Called when injection fails.
*
* @param bootstrapAcceptor head channel handler to be used when blaming
* @throws ReflectiveOperationException during reflective operation
*/
protected abstract void blame(ChannelHandler bootstrapAcceptor) throws ReflectiveOperationException;
}

Datei anzeigen

@ -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();
}

Datei anzeigen

@ -39,6 +39,7 @@ import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.metadata.Metadat
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.packets.EntityPackets;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.packets.WorldPackets;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.storage.InventoryTracker1_16;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import com.viaversion.viaversion.api.minecraft.RegistryType;
import com.viaversion.viaversion.rewriter.SoundRewriter;
@ -275,6 +276,7 @@ public class Protocol1_16To1_15_2 extends AbstractProtocol<ClientboundPackets1_1
@Override
public void init(UserConnection userConnection) {
userConnection.addEntityTracker(this.getClass(), new EntityTrackerBase(userConnection, Entity1_16Types.PLAYER));
userConnection.put(new InventoryTracker1_16());
}
@Override

Datei anzeigen

@ -27,7 +27,6 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.WorldIdentifiers;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_16Types;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
@ -36,7 +35,9 @@ import com.viaversion.viaversion.api.type.types.version.Types1_16;
import com.viaversion.viaversion.protocols.protocol1_15to1_14_4.ClientboundPackets1_15;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.ClientboundPackets1_16;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.Protocol1_16To1_15_2;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.ServerboundPackets1_16;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.metadata.MetadataRewriter1_16To1_15_2;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.storage.InventoryTracker1_16;
import java.util.UUID;
@ -277,5 +278,18 @@ public class EntityPackets {
});
}
});
protocol.registerServerbound(ServerboundPackets1_16.ANIMATION, new PacketRemapper() {
@Override
public void registerMap() {
handler(wrapper -> {
InventoryTracker1_16 inventoryTracker = wrapper.user().get(InventoryTracker1_16.class);
// Don't send an arm swing if the player has an inventory opened.
if (inventoryTracker.getInventory() != -1) {
wrapper.cancel();
}
});
}
});
}
}

Datei anzeigen

@ -35,6 +35,7 @@ import com.viaversion.viaversion.protocols.protocol1_15to1_14_4.ClientboundPacke
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.ClientboundPackets1_16;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.Protocol1_16To1_15_2;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.ServerboundPackets1_16;
import com.viaversion.viaversion.protocols.protocol1_16to1_15_2.storage.InventoryTracker1_16;
import com.viaversion.viaversion.rewriter.ItemRewriter;
import java.util.UUID;
@ -63,13 +64,16 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16To1_15_2> {
map(Type.VAR_INT); // Window Type
map(Type.COMPONENT); // Window Title
handler(cursorRemapper);
handler(wrapper -> {
InventoryTracker1_16 inventoryTracker = wrapper.user().get(InventoryTracker1_16.class);
int windowId = wrapper.get(Type.VAR_INT, 0);
int windowType = wrapper.get(Type.VAR_INT, 1);
if (windowType >= 20) { // smithing added with id 20
wrapper.set(Type.VAR_INT, 1, ++windowType);
}
inventoryTracker.setInventory((short) windowId);
});
handler(cursorRemapper);
}
});
@ -77,6 +81,10 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16To1_15_2> {
@Override
public void registerMap() {
handler(cursorRemapper);
handler(wrapper -> {
InventoryTracker1_16 inventoryTracker = wrapper.user().get(InventoryTracker1_16.class);
inventoryTracker.setInventory((short) -1);
});
}
});
@ -123,6 +131,16 @@ public class InventoryPackets extends ItemRewriter<Protocol1_16To1_15_2> {
registerClickWindow(ServerboundPackets1_16.CLICK_WINDOW, Type.FLAT_VAR_INT_ITEM);
registerCreativeInvAction(ServerboundPackets1_16.CREATIVE_INVENTORY_ACTION, Type.FLAT_VAR_INT_ITEM);
protocol.registerServerbound(ServerboundPackets1_16.CLOSE_WINDOW, new PacketRemapper() {
@Override
public void registerMap() {
handler(wrapper -> {
InventoryTracker1_16 inventoryTracker = wrapper.user().get(InventoryTracker1_16.class);
inventoryTracker.setInventory((short) -1);
});
}
});
protocol.registerServerbound(ServerboundPackets1_16.EDIT_BOOK, new PacketRemapper() {
@Override
public void registerMap() {

Datei anzeigen

@ -0,0 +1,32 @@
/*
* 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.protocols.protocol1_16to1_15_2.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public class InventoryTracker1_16 implements StorableObject {
private short inventory = -1;
public short getInventory() {
return this.inventory;
}
public void setInventory(short inventory) {
this.inventory = inventory;
}
}

Datei anzeigen

@ -77,8 +77,9 @@ public final class InventoryPackets extends ItemRewriter<Protocol1_17To1_16_4> {
// 1.17 clients send the then carried item, but 1.16 expects the clicked one
Item item = wrapper.read(Type.FLAT_VAR_INT_ITEM);
int action = wrapper.get(Type.VAR_INT, 0);
if (action == 5) {
if (action == 5 || action == 1) {
// Quick craft (= dragging / mouse movement while clicking on an empty slot)
// OR Quick move (= shift click to move a whole stack to the other inventory)
// The server always expects an empty item here
item = null;
} else {

Datei anzeigen

@ -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;
}
}
}

Datei anzeigen

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

Datei anzeigen

@ -17,28 +17,37 @@
*/
package com.viaversion.viaversion.common.nbt;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO.readString;
public class NBTTagTest {
@Test
void test() throws IOException {
BinaryTagIO.readString("{id:test,test:1}");
BinaryTagIO.readString("{id:test,test:1,}");
readString("{id:5}");
readString("{id:5b}");
readString("{id:test,test:1,}");
readString("{id:[3.2,64.5,129.5]}");
readString("{id:[I;1,2, 3, 4,5]}"); // >=1.11
readString("{id:1b,b:true}");
readString("{id:[L;1l,2L,3L]}"); // >=1.11
readString("{id:'minecraft:stone'}"); // >=1.13
readString("{id:1,id:2}");
readString("{id:-20b,test:3.19f}");
readString("{id:[I;1,2,3,]}");
readString("{id:[1,2,3,]}");
BinaryTagIO.readString("{id:[1,2,3,]}");
Assertions.assertEquals("2147483649", readString("{id:9000b,thisisastring:2147483649}").get("thisisastring").getValue());
Assertions.assertEquals((byte) 1, readString("{thisisabyte:true}").get("thisisabyte").getValue());
Assertions.assertEquals((byte) 0, readString("{thisisabyte:false}").get("thisisabyte").getValue());
BinaryTagIO.readString("{id:[I;1,2,3]}");
BinaryTagIO.readString("{id:[I;1,2,3,]}");
Assertions.assertTrue(BinaryTagIO.readString("{id:9000b,num:2147483649}").get("num") instanceof StringTag);
//TODO fix legacy
// BinaryTagIO.readString("{id:minecraft:stone}");
//TODO fix legacy < 1.12
// readString("{id:minecraft:stone}");
// readString("{id:[I;1i,2I,3I]}");
// readString("{id:[1,2, 3, 4,5]}");
}
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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