Skip to content

Commit ab32f93

Browse files
authored
resolve packet instance construction concurrency issue (#2312)
1 parent c7753a9 commit ab32f93

File tree

1 file changed

+46
-39
lines changed

1 file changed

+46
-39
lines changed

src/main/java/com/comphenix/protocol/injector/StructureCache.java

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ public class StructureCache {
5454
private static final Map<PacketType, StructureModifier<Object>> STRUCTURE_MODIFIER_CACHE = new ConcurrentHashMap<>();
5555

5656
// packet data serializer which always returns an empty nbt tag compound
57+
private static final Object TRICK_INIT_LOCK = new Object();
5758
private static boolean TRICK_TRIED = false;
59+
5860
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
5961
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
6062

@@ -164,45 +166,50 @@ public static boolean tryInitTrickDataSerializer() {
164166
return TRICKED_DATA_SERIALIZER_BASE != null;
165167
}
166168

167-
// prevent double init
168-
TRICK_TRIED = true;
169-
170-
try {
171-
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
172-
Object textCompound = WrappedChatComponent.fromText("").getHandle();
173-
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
174-
// base builder which intercepts a few methods
175-
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
176-
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
177-
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
178-
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
179-
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
180-
.intercept(FixedValue.value(compound))
181-
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
182-
.intercept(FixedValue.value(textCompound))
183-
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
184-
.intercept(FixedValue.nullValue());
185-
Class<?> serializerBase = baseBuilder.make()
186-
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
187-
.getLoaded();
188-
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
189-
190-
// extended builder which intercepts the read string method as well
191-
Class<?> withStringIntercept = baseBuilder
192-
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
193-
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
194-
.intercept(FixedValue.value("{}"))
195-
.make()
196-
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
197-
.getLoaded();
198-
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
199-
200-
// worked
201-
return true;
202-
} catch (Exception ignored) {
203-
}
169+
synchronized (TRICK_INIT_LOCK) {
170+
if (TRICK_TRIED) {
171+
return TRICKED_DATA_SERIALIZER_BASE != null;
172+
}
204173

205-
// didn't work
206-
return false;
174+
try {
175+
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
176+
Object textCompound = WrappedChatComponent.fromText("").getHandle();
177+
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
178+
// base builder which intercepts a few methods
179+
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
180+
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
181+
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
182+
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
183+
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
184+
.intercept(FixedValue.value(compound))
185+
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
186+
.intercept(FixedValue.value(textCompound))
187+
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
188+
.intercept(FixedValue.nullValue());
189+
Class<?> serializerBase = baseBuilder.make()
190+
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
191+
.getLoaded();
192+
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
193+
194+
// extended builder which intercepts the read string method as well
195+
Class<?> withStringIntercept = baseBuilder
196+
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
197+
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
198+
.intercept(FixedValue.value("{}"))
199+
.make()
200+
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
201+
.getLoaded();
202+
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
203+
204+
// worked
205+
return true;
206+
} catch (Exception ignored) {
207+
} finally {
208+
TRICK_TRIED = true;
209+
}
210+
211+
// didn't work
212+
return false;
213+
}
207214
}
208215
}

0 commit comments

Comments
 (0)