Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 1.20.5/1.20.6 part 2 #2910

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
java-version: '21'
cache: 'gradle'

- name: Run gradle build lifecycle
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
java-version: '21'
cache: 'gradle'

- name: Initialize CodeQL
Expand Down
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ repositories {
}

dependencies {
implementation 'net.bytebuddy:byte-buddy:1.14.9'
implementation 'net.bytebuddy:byte-buddy:1.14.14'
compileOnly 'org.spigotmc:spigot-api:1.20.5-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT'
compileOnly 'io.netty:netty-all:4.0.23.Final'
Expand Down Expand Up @@ -69,6 +69,9 @@ shadowJar {

test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
}
}

processResources {
Expand Down
410 changes: 234 additions & 176 deletions src/main/java/com/comphenix/protocol/PacketType.java

Large diffs are not rendered by default.

31 changes: 12 additions & 19 deletions src/main/java/com/comphenix/protocol/PacketTypeLookup.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* @author Kristian
*/
class PacketTypeLookup {

public static class ProtocolSenderLookup {
// Unroll lookup for performance reasons
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = new IntegerMap<>();
Expand All @@ -27,6 +28,8 @@ public static class ProtocolSenderLookup {
public final IntegerMap<PacketType> STATUS_SERVER = new IntegerMap<>();
public final IntegerMap<PacketType> LOGIN_CLIENT = new IntegerMap<>();
public final IntegerMap<PacketType> LOGIN_SERVER = new IntegerMap<>();
public final IntegerMap<PacketType> CONFIGURATION_CLIENT = new IntegerMap<>();
public final IntegerMap<PacketType> CONFIGURATION_SERVER = new IntegerMap<>();

/**
* Retrieve the correct integer map for a specific protocol and sender.
Expand All @@ -44,6 +47,8 @@ public IntegerMap<PacketType> getMap(Protocol protocol, Sender sender) {
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
case LOGIN:
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
case CONFIGURATION:
return sender == Sender.CLIENT ? CONFIGURATION_CLIENT : CONFIGURATION_SERVER;
default:
throw new IllegalArgumentException("Unable to find protocol " + protocol);
}
Expand Down Expand Up @@ -87,11 +92,6 @@ public Map<String, PacketType> getMap(Protocol protocol, Sender sender) {
}
}

// Packet IDs from 1.6.4 and below
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<>();
private final IntegerMap<PacketType> serverLookup = new IntegerMap<>();
private final IntegerMap<PacketType> clientLookup = new IntegerMap<>();

// Packets for 1.7.2
private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup();

Expand Down Expand Up @@ -123,9 +123,11 @@ public PacketTypeLookup addPacketTypes(Iterable<? extends PacketType> types) {
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
* @param packetId - the legacy packet ID.
* @return The corresponding packet type, or NULL if not found.
* @deprecated no longer works and will always return null
*/
@Deprecated
public PacketType getFromLegacy(int packetId) {
return legacyLookup.get(packetId);
return null;
}

/**
Expand All @@ -142,20 +144,11 @@ public Collection<PacketType> getFromName(String name) {
* @param packetId - the legacy packet ID.
* @param preference - which packet type to look for first.
* @return The corresponding packet type, or NULL if not found.
* @deprecated no longer works and will always return null
*/
public PacketType getFromLegacy(int packetId, Sender preference) {
if (preference == Sender.CLIENT)
return getFirst(packetId, clientLookup, serverLookup);
else
return getFirst(packetId, serverLookup, clientLookup);
}

// Helper method for looking up in two sets
private <T> T getFirst(int packetId, IntegerMap<T> first, IntegerMap<T> second) {
if (first.containsKey(packetId))
return first.get(packetId);
else
return second.get(packetId);
@Deprecated
public PacketType getFromLegacy(int packetId, Sender preference) {
return null;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/com/comphenix/protocol/events/PacketContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
Expand All @@ -56,6 +57,7 @@
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
import com.google.common.collect.Sets;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
Expand Down Expand Up @@ -345,6 +347,11 @@ public static Object deserializeFromBuffer(PacketType packetType, Object buffer)
}

Function<Object, Object> deserializer = PACKET_DESERIALIZER_METHODS.computeIfAbsent(packetType, type -> {
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass());
if (streamCodec != null) {
return streamCodec::decode;
}

if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
// best guess - a constructor which takes a buffer as the only argument
ConstructorAccessor bufferConstructor = Accessors.getConstructorAccessorOrNull(
Expand Down Expand Up @@ -392,7 +399,14 @@ public Object serializeToBuffer() {
}

Object targetBuffer = MinecraftReflection.createPacketDataSerializer(0);
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);

WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass());
if (streamCodec != null) {
streamCodec.encode(targetBuffer, handle);
} else {
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);
}

return targetBuffer;
}

Expand Down
82 changes: 68 additions & 14 deletions src/main/java/com/comphenix/protocol/injector/StructureCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,28 @@
import java.security.PublicKey;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.ZeroBuffer;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
import com.google.common.base.Preconditions;

import io.netty.buffer.ByteBuf;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
Expand All @@ -57,30 +63,55 @@ public class StructureCache {
private static final Object TRICK_INIT_LOCK = new Object();
private static boolean TRICK_TRIED = false;

private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
private static Supplier<Object> TRICKED_DATA_SERIALIZER_BASE;
private static Supplier<Object> TRICKED_DATA_SERIALIZER_JSON;

public static Object newPacket(Class<?> packetClass) {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, packetClassKey -> {
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(packetClassKey);

// use the new stream codec for versions above 1.20.5 if possible
if (streamCodec != null && tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
streamCodec.decode(serializer); // throwaway instance, for testing

// method is working
return () -> streamCodec.decode(serializer);
} catch (Exception exception) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
streamCodec.decode(serializer); // throwaway instance, for testing

// method is working
return () -> streamCodec.decode(serializer);
} catch (Exception ignored) {
// shrug, fall back to default behaviour
}
}
}

// prefer construction via PacketDataSerializer constructor on 1.17 and above
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
clazz,
packetClassKey,
MinecraftReflection.getPacketDataSerializerClass());
if (serializerAccessor != null) {
// check if the method is possible
if (tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
serializerAccessor.invoke(serializer); // throwaway instance, for testing

// method is working
return () -> serializerAccessor.invoke(serializer);
} catch (Exception exception) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.invoke(new ZeroBuffer());
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
serializerAccessor.invoke(serializer); // throwaway instance, for testing

// method is working
Expand All @@ -95,8 +126,8 @@ public static Object newPacket(Class<?> packetClass) {

// try via DefaultInstances as fallback
return () -> {
Object packetInstance = DefaultInstances.DEFAULT.create(clazz);
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + clazz);
Object packetInstance = DefaultInstances.DEFAULT.create(packetClassKey);
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + packetClassKey + " - " + tryInitTrickDataSerializer() + " - " + streamCodec);
return packetInstance;
};
});
Expand Down Expand Up @@ -156,16 +187,19 @@ public static StructureModifier<Object> getStructure(final PacketType packetType
*/
public static Object newNullDataSerializer() {
tryInitTrickDataSerializer();
return TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
return TRICKED_DATA_SERIALIZER_BASE.get();
}

static void initTrickDataSerializer() {
Optional<Class<?>> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass();

// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();

// base builder which intercepts a few methods
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.createSubclass(registryByteBuf.orElse(MinecraftReflection.getPacketDataSerializerClass()))
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())
.and(ElementMatchers.returns(ElementMatchers.isSubTypeOf(MinecraftReflection.getNBTBaseClass()))))
Expand All @@ -177,17 +211,37 @@ static void initTrickDataSerializer() {
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);

// extended builder which intercepts the read string method as well
if (registryByteBuf.isPresent()) {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(serializerBase, true).getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
} else {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer());
}

//xtended builder which intercepts the read string method as well
Class<?> withStringIntercept = baseBuilder
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
.intercept(FixedValue.value("{}"))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);

if (registryByteBuf.isPresent()) {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(withStringIntercept).getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
} else {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer());
}
}

/**
Expand Down
Loading
Loading