@@ -35,6 +35,8 @@ public class WrappedGameProfile extends AbstractWrapper {
3535 GAME_PROFILE , String .class , String .class );
3636 private static final ConstructorAccessor CREATE_UUID_STRING = Accessors .getConstructorAccessorOrNull (
3737 GAME_PROFILE , UUID .class , String .class );
38+ private static final ConstructorAccessor CREATE_UUID_STRING_PROPERTIES = Accessors .getConstructorAccessorOrNull (
39+ GAME_PROFILE , UUID .class , String .class , MinecraftReflection .getGameProfilePropertyMapClass ());
3840
3941 private static final FieldAccessor GET_UUID_STRING = Accessors .getFieldAccessorOrNull (
4042 GAME_PROFILE , "id" , String .class );
@@ -127,6 +129,51 @@ public WrappedGameProfile(String id, String name) {
127129 this (parseUUID (id ), name );
128130 }
129131
132+ private static Object createHandle (UUID uuid , String name , Multimap <String , WrappedSignedProperty > properties ) {
133+ if (CREATE_STRING_STRING != null ) {
134+ return CREATE_STRING_STRING .invoke (uuid != null ? uuid .toString () : null , name );
135+ }
136+
137+ if (CREATE_UUID_STRING == null ) {
138+ throw new IllegalArgumentException ("Unsupported GameProfile constructor." );
139+ }
140+
141+ if (!MinecraftVersion .CONFIG_PHASE_PROTOCOL_UPDATE .atOrAbove ()) {
142+ return CREATE_UUID_STRING .invoke (uuid , name );
143+ }
144+
145+ // 1.20.2+ requires all fields to have a value: null uuid -> UUID(0,0), null name -> empty name
146+ // it's not allowed to pass null for both, so we need to pre-check that
147+ if (uuid == null && (name == null || name .isEmpty ())) {
148+ throw new IllegalArgumentException ("Name and ID cannot both be blank" );
149+ }
150+
151+ // 1.21.9+ made PropertyMap's underlying map immutable, so we need to override it with a mutable map
152+ if (MinecraftVersion .v1_21_9 .atOrAbove ()) {
153+ return CREATE_UUID_STRING_PROPERTIES .invoke (uuid == null ? MinecraftGenerator .SYS_UUID : uuid , name == null ? "" : name ,
154+ convertPropertyMap (properties ));
155+ }
156+
157+ return CREATE_UUID_STRING .invoke (uuid == null ? MinecraftGenerator .SYS_UUID : uuid , name == null ? "" : name );
158+ }
159+
160+ private static Object convertPropertyMap (Multimap <String , WrappedSignedProperty > properties ) {
161+ com .comphenix .protocol .wrappers .MutablePropertyMap map =
162+ new com .comphenix .protocol .wrappers .MutablePropertyMap ();
163+
164+ if (properties == null || properties .isEmpty ()) {
165+ return map ;
166+ }
167+
168+ for (String key : properties .keySet ()) {
169+ for (WrappedSignedProperty property : properties .get (key )) {
170+ map .put (key , (com .mojang .authlib .properties .Property ) property .getHandle ());
171+ }
172+ }
173+
174+ return map ;
175+ }
176+
130177 /**
131178 * Construct a new game profile with the given properties.
132179 * <p>
@@ -137,24 +184,12 @@ public WrappedGameProfile(String id, String name) {
137184 */
138185 public WrappedGameProfile (UUID uuid , String name ) {
139186 super (GAME_PROFILE );
187+ setHandle (createHandle (uuid , name , null ));
188+ }
140189
141- if (CREATE_STRING_STRING != null ) {
142- setHandle (CREATE_STRING_STRING .invoke (uuid != null ? uuid .toString () : null , name ));
143- } else if (CREATE_UUID_STRING != null ) {
144- if (MinecraftVersion .CONFIG_PHASE_PROTOCOL_UPDATE .atOrAbove ()) {
145- // 1.20.2+ requires all fields to have a value: null uuid -> UUID(0,0), null name -> empty name
146- // it's not allowed to pass null for both, so we need to pre-check that
147- if (uuid == null && (name == null || name .isEmpty ())) {
148- throw new IllegalArgumentException ("Name and ID cannot both be blank" );
149- }
150-
151- setHandle (CREATE_UUID_STRING .invoke (uuid == null ? MinecraftGenerator .SYS_UUID : uuid , name == null ? "" : name ));
152- } else {
153- setHandle (CREATE_UUID_STRING .invoke (uuid , name ));
154- }
155- } else {
156- throw new IllegalArgumentException ("Unsupported GameProfile constructor." );
157- }
190+ public WrappedGameProfile (UUID uuid , String name , Multimap <String , WrappedSignedProperty > properties ) {
191+ super (GAME_PROFILE );
192+ setHandle (createHandle (uuid , name , properties ));
158193 }
159194
160195 /**
@@ -167,7 +202,12 @@ public static WrappedGameProfile fromHandle(Object handle) {
167202 if (handle == null )
168203 return null ;
169204
170- return new WrappedGameProfile (handle );
205+ WrappedGameProfile delegate = new WrappedGameProfile (handle );
206+ if (MinecraftVersion .v1_21_9 .atOrAbove ()) {
207+ return new WrappedGameProfile (delegate .getUUID (), delegate .getName (), delegate .getProperties ());
208+ } else {
209+ return delegate ;
210+ }
171211 }
172212
173213 /**
@@ -279,7 +319,15 @@ public Multimap<String, WrappedSignedProperty> getProperties() {
279319
280320 if (result == null ) {
281321 Multimap properties = (Multimap ) GET_PROPERTIES .invoke (handle );
282- result = new ConvertedMultimap <String , Object , WrappedSignedProperty >(GuavaWrappers .getBukkitMultimap (properties )) {
322+
323+ Multimap inner ;
324+ if (MinecraftVersion .v1_21_9 .atOrAbove ()) {
325+ inner = properties ;
326+ } else {
327+ inner = GuavaWrappers .getBukkitMultimap (properties );
328+ }
329+
330+ result = new ConvertedMultimap <String , Object , WrappedSignedProperty >(inner ) {
283331 @ Override
284332 protected Object toInner (WrappedSignedProperty outer ) {
285333 return outer .handle ;
0 commit comments