Skip to content

Commit cb0c870

Browse files
committed
fix: game profile construction on bukkit with ProtocolLib
1 parent 0748778 commit cb0c870

File tree

2 files changed

+120
-9
lines changed

2 files changed

+120
-9
lines changed

bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/ProtocolLibPacketAdapter.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,11 @@
4040
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
4141
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
4242
import com.comphenix.protocol.wrappers.WrappedGameProfile;
43-
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
4443
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
4544
import com.github.juliarn.npclib.api.Npc;
4645
import com.github.juliarn.npclib.api.Platform;
4746
import com.github.juliarn.npclib.api.PlatformVersionAccessor;
4847
import com.github.juliarn.npclib.api.event.InteractNpcEvent;
49-
import com.github.juliarn.npclib.api.profile.ProfileProperty;
5048
import com.github.juliarn.npclib.api.protocol.OutboundPacket;
5149
import com.github.juliarn.npclib.api.protocol.PlatformPacketAdapter;
5250
import com.github.juliarn.npclib.api.protocol.chat.Component;
@@ -364,14 +362,8 @@ final class ProtocolLibPacketAdapter implements PlatformPacketAdapter<World, Pla
364362
container.getPlayerInfoAction().write(0, playerInfoAction);
365363
}
366364

367-
// convert to a protocol lib profile
368-
WrappedGameProfile wrappedGameProfile = new WrappedGameProfile(profile.uniqueId(), profile.name());
369-
for (ProfileProperty prop : profile.properties()) {
370-
WrappedSignedProperty wrapped = new WrappedSignedProperty(prop.name(), prop.value(), prop.signature());
371-
wrappedGameProfile.getProperties().put(prop.name(), wrapped);
372-
}
373-
374365
// add the player info data
366+
WrappedGameProfile wrappedGameProfile = ProtocolLibProfileFactory.wrapProfile(profile);
375367
PlayerInfoData playerInfoData = new PlayerInfoData(
376368
profile.uniqueId(),
377369
20,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* This file is part of npc-lib, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) 2022-2025 Julian M., Pasqual K. and contributors
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package com.github.juliarn.npclib.bukkit.protocol;
26+
27+
import com.comphenix.protocol.wrappers.WrappedGameProfile;
28+
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
29+
import com.github.juliarn.npclib.api.profile.Profile;
30+
import com.github.juliarn.npclib.api.profile.ProfileProperty;
31+
import com.google.common.collect.HashMultimap;
32+
import com.google.common.collect.Multimap;
33+
import java.lang.invoke.MethodHandle;
34+
import java.lang.invoke.MethodHandles;
35+
import java.lang.invoke.MethodType;
36+
import java.util.UUID;
37+
import org.jetbrains.annotations.NotNull;
38+
39+
/**
40+
* Factory for GameProfiles when working with ProtocolLib, as the new authlib v7 types are not properly supported.
41+
*/
42+
final class ProtocolLibProfileFactory {
43+
44+
private static final MethodHandle CONSTRUCT_PROFILE_MODERN; // (UUID, String, Multimap<String, Property>): Object
45+
private static final MethodHandle CONSTRUCT_PROFILE_PROPERTY_MODERN; // (String, String, String): Object
46+
47+
static {
48+
MethodHandle constructGameProfileModern;
49+
MethodHandle constructGameProfilePropertyModern;
50+
try {
51+
Class<?> gameProfileClass = Class.forName("com.mojang.authlib.GameProfile");
52+
Class<?> gameProfilePropertyClass = Class.forName("com.mojang.authlib.properties.Property");
53+
Class<?> gameProfilePropertyMapClass = Class.forName("com.mojang.authlib.properties.PropertyMap");
54+
55+
// resolve constructor for constructing a profile property
56+
MethodHandles.Lookup lookup = MethodHandles.publicLookup(); // everything should be public api
57+
MethodHandle constructProfileProperty = lookup.findConstructor(
58+
gameProfilePropertyClass,
59+
MethodType.methodType(void.class, String.class, String.class, String.class));
60+
MethodType propertyAsObject = constructProfileProperty.type().changeReturnType(Object.class);
61+
constructGameProfilePropertyModern = constructProfileProperty.asType(propertyAsObject);
62+
63+
// resolve constructor for constructing a game profile
64+
MethodHandle constructGameProfile = lookup.findConstructor(
65+
gameProfileClass,
66+
MethodType.methodType(void.class, UUID.class, String.class, gameProfilePropertyMapClass));
67+
MethodHandle constructGamePropertiesMap = lookup.findConstructor(
68+
gameProfilePropertyMapClass,
69+
MethodType.methodType(void.class, Multimap.class));
70+
MethodHandle constructFull = MethodHandles.filterArguments(constructGameProfile, 2, constructGamePropertiesMap);
71+
MethodType constructFullAsObject = constructFull.type().changeReturnType(Object.class);
72+
constructGameProfileModern = constructFull.asType(constructFullAsObject);
73+
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException ignored) {
74+
constructGameProfileModern = null;
75+
constructGameProfilePropertyModern = null;
76+
}
77+
78+
CONSTRUCT_PROFILE_MODERN = constructGameProfileModern;
79+
CONSTRUCT_PROFILE_PROPERTY_MODERN = constructGameProfilePropertyModern;
80+
}
81+
82+
private ProtocolLibProfileFactory() {
83+
throw new UnsupportedOperationException();
84+
}
85+
86+
/**
87+
* Wraps the given resolved profile into a ProtocolLib {@link WrappedGameProfile}.
88+
*
89+
* @param profile the profile to wrap.
90+
* @return the given profile wrapped into a {@link WrappedGameProfile}.
91+
* @throws NullPointerException if the given profile is null.
92+
* @throws IllegalArgumentException if the given profile is invalid.
93+
* @throws IllegalStateException if the profile construction failed for some reason.
94+
*/
95+
public static @NotNull WrappedGameProfile wrapProfile(@NotNull Profile.Resolved profile) {
96+
if (CONSTRUCT_PROFILE_MODERN != null) {
97+
// constructs a modern (authlib >= v7) profile using the authlib.GameProfile constructor
98+
try {
99+
Multimap<String, Object> properties = HashMultimap.create();
100+
for (ProfileProperty prop : profile.properties()) {
101+
Object property = CONSTRUCT_PROFILE_PROPERTY_MODERN.invokeExact(prop.name(), prop.value(), prop.signature());
102+
properties.put(prop.name(), property);
103+
}
104+
Object gameProfile = CONSTRUCT_PROFILE_MODERN.invokeExact(profile.uniqueId(), profile.name(), properties);
105+
return WrappedGameProfile.fromHandle(gameProfile);
106+
} catch (Throwable thrown) {
107+
throw new IllegalStateException("Unable to wrap resolved profile into game profile", thrown);
108+
}
109+
} else {
110+
// use the wrapped profile offered by ProtocolLib
111+
WrappedGameProfile wrappedGameProfile = new WrappedGameProfile(profile.uniqueId(), profile.name());
112+
for (ProfileProperty prop : profile.properties()) {
113+
WrappedSignedProperty wrapped = new WrappedSignedProperty(prop.name(), prop.value(), prop.signature());
114+
wrappedGameProfile.getProperties().put(prop.name(), wrapped);
115+
}
116+
return wrappedGameProfile;
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)