resolve packet instance construction concurrency issue (#2312)

This commit is contained in:
Pasqual Koschmieder 2023-04-08 03:57:49 +02:00 committed by GitHub
parent c7753a9d5b
commit ab32f938d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 43 additions and 36 deletions

View File

@ -54,7 +54,9 @@ public class StructureCache {
private static final Map<PacketType, StructureModifier<Object>> STRUCTURE_MODIFIER_CACHE = new ConcurrentHashMap<>();
// packet data serializer which always returns an empty nbt tag compound
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;
@ -164,45 +166,50 @@ public class StructureCache {
return TRICKED_DATA_SERIALIZER_BASE != null;
}
// prevent double init
TRICK_TRIED = true;
synchronized (TRICK_INIT_LOCK) {
if (TRICK_TRIED) {
return TRICKED_DATA_SERIALIZER_BASE != null;
}
try {
// 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())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
try {
// 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())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
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
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);
// extended 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);
// worked
return true;
} catch (Exception ignored) {
// worked
return true;
} catch (Exception ignored) {
} finally {
TRICK_TRIED = true;
}
// didn't work
return false;
}
// didn't work
return false;
}
}