Basic PlayerProfile API

Establishes base extension of profile systems for future edits too

== AT ==
public org.bukkit.craftbukkit.profile.CraftProfileProperty
public org.bukkit.craftbukkit.profile.CraftPlayerTextures
public org.bukkit.craftbukkit.profile.CraftPlayerTextures copyFrom(Lorg/bukkit/profile/PlayerTextures;)V
public org.bukkit.craftbukkit.profile.CraftPlayerTextures rebuildPropertyIfDirty()V
public org.bukkit.craftbukkit.profile.CraftPlayerProfile toString(Lcom/mojang/authlib/properties/PropertyMap;)Ljava/lang/String;
public org.bukkit.craftbukkit.profile.CraftPlayerProfile getProperty(Ljava/lang/String;)Lcom/mojang/authlib/properties/Property;
public org.bukkit.craftbukkit.profile.CraftPlayerProfile setProperty(Ljava/lang/String;Lcom/mojang/authlib/properties/Property;)V
This commit is contained in:
Aikar 2018-01-15 22:11:48 -05:00
parent 40654111d1
commit bb90110894
10 changed files with 621 additions and 15 deletions

View file

@ -126,7 +126,7 @@
- Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), file);
- String s = (String) Optional.ofNullable((String) optionset.valueOf(optionspec10)).orElse(dedicatedserversettings.getProperties().levelName);
+ File file = (File) optionset.valueOf("universe"); // CraftBukkit
+ Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), file, optionset); // Paper - pass OptionSet to load paper config files
+ Services services = Services.create(new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY), file, optionset); // Paper - pass OptionSet to load paper config files; override authentication service
+ // CraftBukkit start
+ String s = (String) Optional.ofNullable((String) optionset.valueOf("world")).orElse(dedicatedserversettings.getProperties().levelName);
LevelStorageSource convertable = LevelStorageSource.createDefault(file.toPath());

View file

@ -23,7 +23,7 @@
}
public void add(GameProfile profile) {
@@ -117,7 +119,7 @@
@@ -117,13 +119,24 @@
GameProfileCache.GameProfileInfo usercache_usercacheentry = new GameProfileCache.GameProfileInfo(profile, date);
this.safeAdd(usercache_usercacheentry);
@ -32,7 +32,24 @@
}
private long getNextOperation() {
@@ -142,15 +144,15 @@
return this.operationCount.incrementAndGet();
}
+ // Paper start
+ public @Nullable GameProfile getProfileIfCached(String name) {
+ GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
+ if (entry == null) {
+ return null;
+ }
+ entry.setLastAccess(this.getNextOperation());
+ return entry.getProfile();
+ }
+ // Paper end
+
public Optional<GameProfile> get(String name) {
String s1 = name.toLowerCase(Locale.ROOT);
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
@@ -142,15 +155,15 @@
usercache_usercacheentry.setLastAccess(this.getNextOperation());
optional = Optional.of(usercache_usercacheentry.getProfile());
} else {
@ -51,7 +68,7 @@
}
return optional;
@@ -167,7 +169,7 @@
@@ -167,7 +180,7 @@
} else {
CompletableFuture<Optional<GameProfile>> completablefuture1 = CompletableFuture.supplyAsync(() -> {
return this.get(username);
@ -60,7 +77,7 @@
this.requests.remove(username);
}, this.executor);
@@ -208,7 +210,7 @@
@@ -208,7 +221,7 @@
label54:
{
@ -69,7 +86,7 @@
try {
JsonArray jsonarray = (JsonArray) this.gson.fromJson(bufferedreader, JsonArray.class);
@@ -217,7 +219,7 @@
@@ -217,7 +230,7 @@
DateFormat dateformat = GameProfileCache.createDateFormat();
jsonarray.forEach((jsonelement) -> {
@ -78,7 +95,7 @@
Objects.requireNonNull(list);
optional.ifPresent(list::add);
@@ -250,6 +252,11 @@
@@ -250,6 +263,11 @@
}
} catch (FileNotFoundException filenotfoundexception) {
;
@ -90,7 +107,7 @@
} catch (JsonParseException | IOException ioexception) {
GameProfileCache.LOGGER.warn("Failed to load profile cache {}", this.file, ioexception);
}
@@ -257,14 +264,15 @@
@@ -257,14 +275,15 @@
return list;
}
@ -108,7 +125,7 @@
try {
BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8);
@@ -289,6 +297,14 @@
@@ -289,6 +308,14 @@
} catch (IOException ioexception) {
;
}

View file

@ -0,0 +1,451 @@
package com.destroystokyo.paper.profile;
import com.google.common.base.Preconditions;
import com.mojang.authlib.yggdrasil.ProfileResult;
import io.papermc.paper.configuration.GlobalConfiguration;
import com.google.common.base.Charsets;
import com.google.common.collect.Iterables;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import net.minecraft.Util;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.players.GameProfileCache;
import net.minecraft.util.StringUtil;
import net.minecraft.world.item.component.ResolvableProfile;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.craftbukkit.configuration.ConfigSerializationUtil;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.profile.CraftPlayerTextures;
import org.bukkit.craftbukkit.profile.CraftProfileProperty;
import org.bukkit.profile.PlayerTextures;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@SerializableAs("PlayerProfile")
public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile {
private boolean emptyName;
private boolean emptyUUID;
private GameProfile profile;
private final PropertySet properties = new PropertySet();
public CraftPlayerProfile(CraftPlayer player) {
this.profile = player.getHandle().getGameProfile();
}
public CraftPlayerProfile(UUID id, String name) {
this.profile = createAuthLibProfile(id, name);
this.emptyName = name == null;
this.emptyUUID = id == null;
}
public CraftPlayerProfile(GameProfile profile) {
Validate.notNull(profile, "GameProfile cannot be null!");
this.profile = profile;
}
public CraftPlayerProfile(ResolvableProfile resolvableProfile) {
this(resolvableProfile.id().orElse(null), resolvableProfile.name().orElse(null));
copyProfileProperties(resolvableProfile.gameProfile(), this.profile);
}
@Override
public boolean hasProperty(String property) {
return profile.getProperties().containsKey(property);
}
@Override
public void setProperty(ProfileProperty property) {
String name = property.getName();
PropertyMap properties = profile.getProperties();
properties.removeAll(name);
Preconditions.checkArgument(properties.size() < 16, "Cannot add more than 16 properties to a profile");
properties.put(name, new Property(name, property.getValue(), property.getSignature()));
}
@Override
public CraftPlayerTextures getTextures() {
return new CraftPlayerTextures(this);
}
@Override
public void setTextures(@Nullable PlayerTextures textures) {
if (textures == null) {
this.removeProperty("textures");
} else {
CraftPlayerTextures craftPlayerTextures = new CraftPlayerTextures(this);
craftPlayerTextures.copyFrom(textures);
craftPlayerTextures.rebuildPropertyIfDirty();
}
}
public GameProfile getGameProfile() {
return profile;
}
@Nullable
@Override
public UUID getId() {
return this.emptyUUID ? null : this.profile.getId();
}
@Override
@Deprecated(forRemoval = true)
public UUID setId(@Nullable UUID uuid) {
final GameProfile previousProfile = this.profile;
final UUID previousId = this.getId();
this.profile = createAuthLibProfile(uuid, previousProfile.getName());
copyProfileProperties(previousProfile, this.profile);
this.emptyUUID = uuid == null;
return previousId;
}
@Override
public UUID getUniqueId() {
return getId();
}
@Nullable
@Override
public String getName() {
return this.emptyName ? null : this.profile.getName();
}
@Override
@Deprecated(forRemoval = true)
public String setName(@Nullable String name) {
GameProfile prev = this.profile;
this.profile = createAuthLibProfile(prev.getId(), name);
copyProfileProperties(prev, this.profile);
this.emptyName = name == null;
return prev.getName();
}
@Nonnull
@Override
public Set<ProfileProperty> getProperties() {
return properties;
}
@Override
public void setProperties(Collection<ProfileProperty> properties) {
properties.forEach(this::setProperty);
}
@Override
public void clearProperties() {
profile.getProperties().clear();
}
@Override
public boolean removeProperty(String property) {
return !profile.getProperties().removeAll(property).isEmpty();
}
@Nullable
@Override
public Property getProperty(String property) {
return Iterables.getFirst(this.profile.getProperties().get(property), null);
}
@Nullable
@Override
public void setProperty(@NotNull String propertyName, @Nullable Property property) {
if (property != null) {
this.setProperty(new ProfileProperty(propertyName, property.value(), property.signature()));
} else {
profile.getProperties().removeAll(propertyName);
}
}
@Override
public @NotNull GameProfile buildGameProfile() {
GameProfile profile = new GameProfile(this.profile.getId(), this.profile.getName());
profile.getProperties().putAll(this.profile.getProperties());
return profile;
}
@Override
public @NotNull ResolvableProfile buildResolvableProfile() {
if (this.emptyName || this.emptyUUID) {
return new ResolvableProfile(this.emptyName ? Optional.empty() : Optional.of(this.profile.getName()), this.emptyUUID ? Optional.empty() : Optional.of(this.profile.getId()), this.profile.getProperties());
} else {
return new ResolvableProfile(this.buildGameProfile());
}
}
@Override
public CraftPlayerProfile clone() {
CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName());
clone.setProperties(getProperties());
return clone;
}
@Override
public boolean isComplete() {
return this.getId() != null && StringUtils.isNotBlank(this.getName());
}
@Override
public @NotNull CompletableFuture<PlayerProfile> update() {
return CompletableFuture.supplyAsync(() -> {
final CraftPlayerProfile clone = clone();
clone.complete(true);
return clone;
}, Util.PROFILE_EXECUTOR);
}
@Override
public boolean completeFromCache() {
return completeFromCache(false, GlobalConfiguration.get().proxies.isProxyOnlineMode());
}
public boolean completeFromCache(boolean onlineMode) {
return completeFromCache(false, onlineMode);
}
public boolean completeFromCache(boolean lookupUUID, boolean onlineMode) {
MinecraftServer server = MinecraftServer.getServer();
String name = profile.getName();
GameProfileCache userCache = server.getProfileCache();
if (this.getId() == null) {
final GameProfile profile;
if (onlineMode) {
profile = lookupUUID ? userCache.get(name).orElse(null) : userCache.getProfileIfCached(name);
} else {
// Make an OfflinePlayer using an offline mode UUID since the name has no profile
profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
}
if (profile != null) {
// if old has it, assume its newer, so overwrite, else use cached if it was set and ours wasn't
copyProfileProperties(this.profile, profile);
this.profile = profile;
this.emptyUUID = false; // UUID was just retrieved from user cache and profile isn't null (so a completed profile was found)
}
}
if ((profile.getName().isEmpty() || !hasTextures()) && this.getId() != null) {
Optional<GameProfile> optProfile = userCache.get(this.profile.getId());
if (optProfile.isPresent()) {
GameProfile profile = optProfile.get();
if (this.profile.getName().isEmpty()) {
// if old has it, assume its newer, so overwrite, else use cached if it was set and ours wasn't
copyProfileProperties(this.profile, profile);
this.profile = profile;
this.emptyName = false; // Name was just retrieved via the userCache
} else if (profile != this.profile) {
copyProfileProperties(profile, this.profile);
}
}
}
return this.isComplete();
}
public boolean complete(boolean textures) {
return complete(textures, GlobalConfiguration.get().proxies.isProxyOnlineMode());
}
public boolean complete(boolean textures, boolean onlineMode) {
if (this.isComplete() && (!textures || hasTextures())) { // Don't do lookup if we already have everything
return true;
}
MinecraftServer server = MinecraftServer.getServer();
boolean isCompleteFromCache = this.completeFromCache(true, onlineMode);
if (onlineMode && (!isCompleteFromCache || (textures && !hasTextures()))) {
ProfileResult result = server.getSessionService().fetchProfile(this.profile.getId(), true);
if (result != null && result.profile() != null) {
copyProfileProperties(result.profile(), this.profile, true);
}
if (this.isComplete()) {
server.getProfileCache().add(this.profile);
}
}
return this.isComplete() && (!onlineMode || !textures || hasTextures());
}
private static void copyProfileProperties(GameProfile source, GameProfile target) {
copyProfileProperties(source, target, false);
}
private static void copyProfileProperties(GameProfile source, GameProfile target, boolean clearTarget) {
if (source == target) {
throw new IllegalArgumentException("Source and target profiles are the same (" + source + ")");
}
PropertyMap sourceProperties = source.getProperties();
PropertyMap targetProperties = target.getProperties();
if (clearTarget) targetProperties.clear();
if (sourceProperties.isEmpty()) {
return;
}
for (Property property : sourceProperties.values()) {
targetProperties.removeAll(property.name());
targetProperties.put(property.name(), property);
}
}
private static GameProfile createAuthLibProfile(UUID uniqueId, String name) {
Preconditions.checkArgument(name == null || name.length() <= 16, "Name cannot be longer than 16 characters");
Preconditions.checkArgument(name == null || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name);
return new GameProfile(
uniqueId != null ? uniqueId : Util.NIL_UUID,
name != null ? name : ""
);
}
private static ProfileProperty toBukkit(Property property) {
return new ProfileProperty(property.name(), property.value(), property.signature());
}
public static PlayerProfile asBukkitCopy(GameProfile gameProfile) {
CraftPlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName());
copyProfileProperties(gameProfile, profile.profile);
return profile;
}
public static PlayerProfile asBukkitMirror(GameProfile profile) {
return new CraftPlayerProfile(profile);
}
public static Property asAuthlib(ProfileProperty property) {
return new Property(property.getName(), property.getValue(), property.getSignature());
}
public static GameProfile asAuthlibCopy(PlayerProfile profile) {
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
return asAuthlib(craft.clone());
}
public static GameProfile asAuthlib(PlayerProfile profile) {
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
return craft.getGameProfile();
}
public static ResolvableProfile asResolvableProfileCopy(PlayerProfile profile) {
return ((SharedPlayerProfile) profile).buildResolvableProfile();
}
@Override
public @NotNull Map<String, Object> serialize() {
Map<String, Object> map = new LinkedHashMap<>();
if (!this.emptyUUID) {
map.put("uniqueId", this.getId().toString());
}
if (!this.emptyName) {
map.put("name", getName());
}
if (!this.properties.isEmpty()) {
List<Object> propertiesData = new ArrayList<>();
for (ProfileProperty property : properties) {
propertiesData.add(CraftProfileProperty.serialize(new Property(property.getName(), property.getValue(), property.getSignature())));
}
map.put("properties", propertiesData);
}
return map;
}
public static CraftPlayerProfile deserialize(Map<String, Object> map) {
UUID uniqueId = ConfigSerializationUtil.getUuid(map, "uniqueId", true);
String name = ConfigSerializationUtil.getString(map, "name", true);
// This also validates the deserialized unique id and name (ensures that not both are null):
CraftPlayerProfile profile = new CraftPlayerProfile(uniqueId, name);
if (map.containsKey("properties")) {
for (Object propertyData : (List<?>) map.get("properties")) {
if (!(propertyData instanceof Map)) {
throw new IllegalArgumentException("Property data (" + propertyData + ") is not a valid Map");
}
Property property = CraftProfileProperty.deserialize((Map<?, ?>) propertyData);
profile.profile.getProperties().put(property.name(), property);
}
}
return profile;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
final CraftPlayerProfile that = (CraftPlayerProfile) o;
return this.emptyName == that.emptyName && this.emptyUUID == that.emptyUUID && Objects.equals(this.profile, that.profile);
}
@Override
public int hashCode() {
return Objects.hash(this.emptyName, this.emptyUUID, this.profile);
}
@Override
public String toString() {
return "CraftPlayerProfile [uniqueId=" + getId() +
", name=" + getName() +
", properties=" + org.bukkit.craftbukkit.profile.CraftPlayerProfile.toString(this.profile.getProperties()) +
"]";
}
private class PropertySet extends AbstractSet<ProfileProperty> {
@Override
@Nonnull
public Iterator<ProfileProperty> iterator() {
return new ProfilePropertyIterator(profile.getProperties().values().iterator());
}
@Override
public int size() {
return profile.getProperties().size();
}
@Override
public boolean add(ProfileProperty property) {
setProperty(property);
return true;
}
@Override
public boolean addAll(Collection<? extends ProfileProperty> c) {
//noinspection unchecked
setProperties((Collection<ProfileProperty>) c);
return true;
}
@Override
public boolean contains(Object o) {
return o instanceof ProfileProperty && profile.getProperties().containsKey(((ProfileProperty) o).getName());
}
private class ProfilePropertyIterator implements Iterator<ProfileProperty> {
private final Iterator<Property> iterator;
ProfilePropertyIterator(Iterator<Property> iterator) {
this.iterator = iterator;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public ProfileProperty next() {
return toBukkit(iterator.next());
}
@Override
public void remove() {
iterator.remove();
}
}
}
}

View file

@ -0,0 +1,30 @@
package com.destroystokyo.paper.profile;
import com.mojang.authlib.Environment;
import com.mojang.authlib.EnvironmentParser;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilEnvironment;
import java.net.Proxy;
public class PaperAuthenticationService extends YggdrasilAuthenticationService {
private final Environment environment;
public PaperAuthenticationService(Proxy proxy) {
super(proxy);
this.environment = EnvironmentParser.getEnvironmentFromProperties().orElse(YggdrasilEnvironment.PROD.getEnvironment());
}
@Override
public MinecraftSessionService createMinecraftSessionService() {
return new PaperMinecraftSessionService(this.getServicesKeySet(), this.getProxy(), this.environment);
}
@Override
public GameProfileRepository createProfileRepository() {
return new PaperGameProfileRepository(this.getProxy(), this.environment);
}
}

View file

@ -0,0 +1,17 @@
package com.destroystokyo.paper.profile;
import com.mojang.authlib.Environment;
import com.mojang.authlib.ProfileLookupCallback;
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
import java.net.Proxy;
public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
public PaperGameProfileRepository(Proxy proxy, Environment environment) {
super(proxy, environment);
}
@Override
public void findProfilesByNames(String[] names, ProfileLookupCallback callback) {
super.findProfilesByNames(names, callback);
}
}

View file

@ -0,0 +1,22 @@
package com.destroystokyo.paper.profile;
import com.mojang.authlib.Environment;
import com.mojang.authlib.yggdrasil.ProfileResult;
import com.mojang.authlib.yggdrasil.ServicesKeySet;
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
import java.net.Proxy;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService {
protected PaperMinecraftSessionService(ServicesKeySet servicesKeySet, Proxy proxy, Environment environment) {
super(servicesKeySet, proxy, environment);
}
@Override
public @Nullable ProfileResult fetchProfile(final UUID profileId, final boolean requireSecure) {
return super.fetchProfile(profileId, requireSecure);
}
}

View file

@ -0,0 +1,26 @@
package com.destroystokyo.paper.profile;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.minecraft.world.item.component.ResolvableProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public interface SharedPlayerProfile {
@Nullable UUID getUniqueId();
@Nullable String getName();
boolean removeProperty(@NotNull String property);
@Nullable Property getProperty(@NotNull String propertyName);
@Nullable void setProperty(@NotNull String propertyName, @Nullable Property property);
@NotNull GameProfile buildGameProfile();
@NotNull ResolvableProfile buildResolvableProfile();
}

View file

@ -265,6 +265,9 @@ import org.yaml.snakeyaml.error.MarkedYAMLException;
import net.md_5.bungee.api.chat.BaseComponent; // Spigot
import javax.annotation.Nullable; // Paper
import javax.annotation.Nonnull; // Paper
public final class CraftServer implements Server {
private final String serverName = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); // Paper
private final String serverVersion;
@ -310,6 +313,7 @@ public final class CraftServer implements Server {
static {
ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
ConfigurationSerialization.registerClass(CraftPlayerProfile.class);
ConfigurationSerialization.registerClass(com.destroystokyo.paper.profile.CraftPlayerProfile.class); // Paper
CraftItemFactory.instance();
CraftEntityFactory.instance();
}
@ -2868,5 +2872,39 @@ public final class CraftServer implements Server {
public boolean suggestPlayerNamesWhenNullTabCompletions() {
return io.papermc.paper.configuration.GlobalConfiguration.get().commands.suggestPlayerNamesWhenNullTabCompletions;
}
@Override
public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull UUID uuid) {
return createProfile(uuid, null);
}
@Override
public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull String name) {
return createProfile(null, name);
}
@Override
public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) {
Player player = uuid != null ? Bukkit.getPlayer(uuid) : (name != null ? Bukkit.getPlayerExact(name) : null);
if (player != null) return new com.destroystokyo.paper.profile.CraftPlayerProfile((CraftPlayer) player);
return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
}
@Override
public com.destroystokyo.paper.profile.PlayerProfile createProfileExact(@Nullable UUID uuid, @Nullable String name) {
Player player = uuid != null ? Bukkit.getPlayer(uuid) : (name != null ? Bukkit.getPlayerExact(name) : null);
if (player == null) {
return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
}
if (java.util.Objects.equals(uuid, player.getUniqueId()) && java.util.Objects.equals(name, player.getName())) {
return new com.destroystokyo.paper.profile.CraftPlayerProfile((CraftPlayer) player);
}
final com.destroystokyo.paper.profile.CraftPlayerProfile profile = new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
profile.getGameProfile().getProperties().putAll(((CraftPlayer) player).getHandle().getGameProfile().getProperties());
return profile;
}
// Paper end
}

View file

@ -31,7 +31,7 @@ import org.bukkit.profile.PlayerTextures;
import org.jetbrains.annotations.ApiStatus;
@SerializableAs("PlayerProfile")
public final class CraftPlayerProfile implements PlayerProfile {
public final class CraftPlayerProfile implements PlayerProfile, com.destroystokyo.paper.profile.SharedPlayerProfile { // Paper
@Nonnull
public static GameProfile validateSkullProfile(@Nonnull GameProfile gameProfile) {
@ -132,8 +132,10 @@ public final class CraftPlayerProfile implements PlayerProfile {
}
}
void removeProperty(String propertyName) {
this.properties.removeAll(propertyName);
// Paper start - change return value for shared interface
public boolean removeProperty(String propertyName) {
return !this.properties.removeAll(propertyName).isEmpty();
// Paper end
}
void rebuildDirtyProperties() {
@ -283,6 +285,7 @@ public final class CraftPlayerProfile implements PlayerProfile {
@Override
public Map<String, Object> serialize() {
// Paper - diff on change
Map<String, Object> map = new LinkedHashMap<>();
if (this.uniqueId != null) {
map.put("uniqueId", this.uniqueId.toString());
@ -296,10 +299,12 @@ public final class CraftPlayerProfile implements PlayerProfile {
this.properties.forEach((propertyName, property) -> propertiesData.add(CraftProfileProperty.serialize(property)));
map.put("properties", propertiesData);
}
// Paper - diff on change
return map;
}
public static CraftPlayerProfile deserialize(Map<String, Object> map) {
// Paper - diff on change
UUID uniqueId = ConfigSerializationUtil.getUuid(map, "uniqueId", true);
String name = ConfigSerializationUtil.getString(map, "name", true);
@ -313,7 +318,7 @@ public final class CraftPlayerProfile implements PlayerProfile {
profile.properties.put(property.name(), property);
}
}
// Paper - diff on change
return profile;
}
}

View file

@ -48,7 +48,7 @@ public final class CraftPlayerTextures implements PlayerTextures {
}
}
private final CraftPlayerProfile profile;
private final com.destroystokyo.paper.profile.SharedPlayerProfile profile; // Paper
// The textures data is loaded lazily:
private boolean loaded = false;
@ -67,7 +67,7 @@ public final class CraftPlayerTextures implements PlayerTextures {
// GameProfiles (even if these modifications are later reverted).
private boolean dirty = false;
CraftPlayerTextures(@Nonnull CraftPlayerProfile profile) {
public CraftPlayerTextures(@Nonnull com.destroystokyo.paper.profile.SharedPlayerProfile profile) { // Paper
this.profile = profile;
}