Initial Support for 1.20.5 (#2894)
This commit is contained in:
parent
d0c7382d7f
commit
70e4812fde
|
@ -1,18 +1,22 @@
|
||||||
package com.comphenix.protocol.injector.netty.channel;
|
package com.comphenix.protocol.injector.netty.channel;
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType;
|
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
||||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|
||||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.util.AttributeKey;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.PacketType;
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.util.AttributeKey;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final class ChannelProtocolUtil {
|
final class ChannelProtocolUtil {
|
||||||
|
@ -28,7 +32,10 @@ final class ChannelProtocolUtil {
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
BiFunction<Channel, PacketType.Sender, Object> baseResolver = null;
|
BiFunction<Channel, PacketType.Sender, Object> baseResolver = null;
|
||||||
if (attributeKeys.size() == 1) {
|
if (attributeKeys.isEmpty()) {
|
||||||
|
// since 1.20.5 the protocol is stored as final field in de-/encoder
|
||||||
|
baseResolver = new Post1_20_5WrappedResolver();
|
||||||
|
} else if (attributeKeys.size() == 1) {
|
||||||
// if there is only one attribute key we can assume it's the correct one (1.8 - 1.20.1)
|
// if there is only one attribute key we can assume it's the correct one (1.8 - 1.20.1)
|
||||||
Object protocolKey = Accessors.getFieldAccessor(attributeKeys.get(0)).get(null);
|
Object protocolKey = Accessors.getFieldAccessor(attributeKeys.get(0)).get(null);
|
||||||
baseResolver = new Pre1_20_2DirectResolver((AttributeKey<Object>) protocolKey);
|
baseResolver = new Pre1_20_2DirectResolver((AttributeKey<Object>) protocolKey);
|
||||||
|
@ -130,4 +137,70 @@ final class ChannelProtocolUtil {
|
||||||
return this.protocolAccessor;
|
return this.protocolAccessor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since 1.20.5 the protocol is stored as final field in de-/encoder
|
||||||
|
*/
|
||||||
|
private static final class Post1_20_5WrappedResolver implements BiFunction<Channel, PacketType.Sender, Object> {
|
||||||
|
|
||||||
|
// lazy initialized when needed
|
||||||
|
private Function<Object, Object> serverProtocolAccessor;
|
||||||
|
private Function<Object, Object> clientProtocolAccessor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(Channel channel, PacketType.Sender sender) {
|
||||||
|
String key = this.getKeyForSender(sender);
|
||||||
|
Object codecHandler = channel.pipeline().get(key);
|
||||||
|
if (codecHandler == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Function<Object, Object> protocolAccessor = this.getProtocolAccessor(codecHandler.getClass(), sender);
|
||||||
|
return protocolAccessor.apply(codecHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler, PacketType.Sender sender) {
|
||||||
|
switch (sender) {
|
||||||
|
case SERVER:
|
||||||
|
if (this.serverProtocolAccessor == null) {
|
||||||
|
this.serverProtocolAccessor = getProtocolAccessor(codecHandler);
|
||||||
|
}
|
||||||
|
return this.serverProtocolAccessor;
|
||||||
|
case CLIENT:
|
||||||
|
if (this.clientProtocolAccessor == null) {
|
||||||
|
this.clientProtocolAccessor = getProtocolAccessor(codecHandler);
|
||||||
|
}
|
||||||
|
return this.clientProtocolAccessor;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getKeyForSender(PacketType.Sender sender) {
|
||||||
|
switch (sender) {
|
||||||
|
case SERVER:
|
||||||
|
return "encoder";
|
||||||
|
case CLIENT:
|
||||||
|
return "decoder";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler) {
|
||||||
|
Class<?> protocolInfoClass = MinecraftReflection.getProtocolInfoClass();
|
||||||
|
|
||||||
|
MethodAccessor protocolAccessor = Accessors.getMethodAccessor(FuzzyReflection
|
||||||
|
.fromClass(protocolInfoClass)
|
||||||
|
.getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0]));
|
||||||
|
|
||||||
|
FieldAccessor protocolInfoAccessor = Accessors.getFieldAccessor(codecHandler, protocolInfoClass, true);
|
||||||
|
|
||||||
|
// get ProtocolInfo from handler and get EnumProtocol of ProtocolInfo
|
||||||
|
return (handler) -> {
|
||||||
|
Object protocolInfo = protocolInfoAccessor.get(handler);
|
||||||
|
return protocolAccessor.invoke(protocolInfo);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
|
@ -202,9 +203,18 @@ public class NettyChannelInjector implements Injector {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChannelPipeline pipeline = this.wrappedChannel.pipeline();
|
||||||
|
|
||||||
|
// since 1.20.5 the encoder is renamed to outbound_config only in the handshake phase
|
||||||
|
String encoderName = pipeline.get("outbound_config") != null
|
||||||
|
? "outbound_config" : "encoder";
|
||||||
|
|
||||||
// inject our handlers
|
// inject our handlers
|
||||||
this.wrappedChannel.pipeline().addAfter("encoder", WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER);
|
pipeline.addAfter(
|
||||||
this.wrappedChannel.pipeline().addAfter(
|
encoderName,
|
||||||
|
WIRE_PACKET_ENCODER_NAME,
|
||||||
|
WIRE_PACKET_ENCODER);
|
||||||
|
pipeline.addAfter(
|
||||||
"decoder",
|
"decoder",
|
||||||
INTERCEPTOR_NAME,
|
INTERCEPTOR_NAME,
|
||||||
new InboundPacketInterceptor(this, this.channelListener));
|
new InboundPacketInterceptor(this, this.channelListener));
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -62,7 +62,7 @@ public final class ChatExtensions {
|
||||||
// since 1.19 system chat is extracted into a separate packet
|
// since 1.19 system chat is extracted into a separate packet
|
||||||
packet = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
|
packet = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
|
||||||
|
|
||||||
packet.getStrings().write(0, component.getJson());
|
packet.getChatComponents().write(0, component);
|
||||||
packet.getBooleans().write(0, false);
|
packet.getBooleans().write(0, false);
|
||||||
} else {
|
} else {
|
||||||
packet = new PacketContainer(PacketType.Play.Server.CHAT);
|
packet = new PacketContainer(PacketType.Play.Server.CHAT);
|
||||||
|
|
|
@ -638,7 +638,8 @@ public final class MinecraftReflection {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBundleDelimiter(Class<?> packetClass) {
|
public static boolean isBundleDelimiter(Class<?> packetClass) {
|
||||||
return Optionals.Equals(getBundleDelimiterClass(), packetClass);
|
Class<?> bundleDelimiterClass = getBundleDelimiterClass().orElse(null);
|
||||||
|
return bundleDelimiterClass != null && (packetClass.equals(bundleDelimiterClass) || bundleDelimiterClass.isAssignableFrom(packetClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<Class<?>> getBundleDelimiterClass() {
|
public static Optional<Class<?>> getBundleDelimiterClass() {
|
||||||
|
@ -1660,7 +1661,7 @@ public final class MinecraftReflection {
|
||||||
*/
|
*/
|
||||||
private static void useFallbackServer() {
|
private static void useFallbackServer() {
|
||||||
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
|
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
|
||||||
Constructor<?> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
|
Constructor<?> selected = FuzzyReflection.fromClass(getCraftServer())
|
||||||
.getConstructor(FuzzyMethodContract.newBuilder()
|
.getConstructor(FuzzyMethodContract.newBuilder()
|
||||||
.parameterMatches(getMinecraftObjectMatcher(), 0)
|
.parameterMatches(getMinecraftObjectMatcher(), 0)
|
||||||
.parameterCount(2)
|
.parameterCount(2)
|
||||||
|
@ -1716,4 +1717,16 @@ public final class MinecraftReflection {
|
||||||
public static Class<?> getHolderClass() {
|
public static Class<?> getHolderClass() {
|
||||||
return getMinecraftClass("core.Holder");
|
return getMinecraftClass("core.Holder");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Class<?> getCraftServer() {
|
||||||
|
return getCraftBukkitClass("CraftServer");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> getHolderLookupProviderClass() {
|
||||||
|
return getMinecraftClass("core.HolderLookup$a" /* Spigot Mappings */, "core.HolderLookup$Provider" /* Mojang Mappings */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> getProtocolInfoClass() {
|
||||||
|
return getMinecraftClass("network.ProtocolInfo");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static getter for the registry access accessor which is need for most of the methods
|
||||||
|
* since 1.20.5 that access the registry and in form.
|
||||||
|
*/
|
||||||
|
public class MinecraftRegistryAccess {
|
||||||
|
|
||||||
|
private static MethodAccessor GET_SERVER = null;
|
||||||
|
private static MethodAccessor REGISTRY_ACCESS = null;
|
||||||
|
|
||||||
|
// lazy initialized
|
||||||
|
private static Object registryAccess = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||||
|
GET_SERVER = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getCraftServer(), false)
|
||||||
|
.getMethod(FuzzyMethodContract.newBuilder()
|
||||||
|
.banModifier(Modifier.STATIC)
|
||||||
|
.returnDerivedOf(MinecraftReflection.getMinecraftServerClass())
|
||||||
|
.build()));
|
||||||
|
|
||||||
|
REGISTRY_ACCESS = Accessors.getMethodAccessor(
|
||||||
|
FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), false)
|
||||||
|
.getMethod(FuzzyMethodContract.newBuilder()
|
||||||
|
.banModifier(Modifier.STATIC)
|
||||||
|
.returnDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
|
||||||
|
.build()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the composite global registry access. Equiv. of
|
||||||
|
* <pre>((CraftServer) Bukkit.getServer()).getServer().registryAccess()</pre>
|
||||||
|
*
|
||||||
|
* @return composite registy acesss
|
||||||
|
*/
|
||||||
|
public static Object get() {
|
||||||
|
if (GET_SERVER == null || REGISTRY_ACCESS == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registryAccess == null) {
|
||||||
|
Object server = GET_SERVER.invoke(Bukkit.getServer());
|
||||||
|
registryAccess = REGISTRY_ACCESS.invoke(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return registryAccess;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,11 @@ import org.bukkit.Server;
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
|
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
|
||||||
|
/**
|
||||||
|
* Version 1.20.5 - the cookie and transfer packet update
|
||||||
|
*/
|
||||||
|
public static final MinecraftVersion v1_20_5 = new MinecraftVersion("1.20.5");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Version 1.20.4 - the decorated pot update
|
* Version 1.20.4 - the decorated pot update
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.comphenix.protocol.wrappers;
|
package com.comphenix.protocol.wrappers;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -11,10 +10,10 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||||
|
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import net.minecraft.network.chat.IChatBaseComponent;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a chat component added in Minecraft 1.7.2
|
* Represents a chat component added in Minecraft 1.7.2
|
||||||
|
@ -39,14 +38,22 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
||||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
|
||||||
|
|
||||||
// Retrieve the correct methods
|
// Retrieve the correct methods
|
||||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||||
String.class, new Class<?>[] { COMPONENT }));
|
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||||
|
String.class, new Class<?>[] { COMPONENT, MinecraftReflection.getHolderLookupProviderClass() }));
|
||||||
|
} else {
|
||||||
|
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||||
|
String.class, new Class<?>[] { COMPONENT }));
|
||||||
|
}
|
||||||
|
|
||||||
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
|
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
|
||||||
|
|
||||||
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false)
|
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false)
|
||||||
.getMethodByReturnTypeAndParameters("fromJsonLenient", MUTABLE_COMPONENT_CLASS.get(), String.class));
|
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class, MinecraftReflection.getHolderLookupProviderClass() }));
|
||||||
|
} else if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||||
|
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false)
|
||||||
|
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class }));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
|
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
|
||||||
|
@ -68,7 +75,19 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object serialize(Object handle) {
|
||||||
|
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||||
|
return SERIALIZE_COMPONENT.invoke(null, handle, MinecraftRegistryAccess.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return SERIALIZE_COMPONENT.invoke(null, handle);
|
||||||
|
}
|
||||||
|
|
||||||
private static Object deserialize(String json) {
|
private static Object deserialize(String json) {
|
||||||
|
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||||
|
return DESERIALIZE.invoke(null, json, MinecraftRegistryAccess.get());
|
||||||
|
}
|
||||||
|
|
||||||
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||||
return DESERIALIZE.invoke(null, json);
|
return DESERIALIZE.invoke(null, json);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +186,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
||||||
*/
|
*/
|
||||||
public String getJson() {
|
public String getJson() {
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
cache = (String) SERIALIZE_COMPONENT.invoke(null, handle);
|
cache = (String) serialize(handle);
|
||||||
}
|
}
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue