From 169a4bcde7aa5596794b15d147b9c331d8b01fb0 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 15 Jan 2018 22:11:48 -0500
Subject: [PATCH] Basic PlayerProfile API


diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
new file mode 100644
index 000000000..e673726c5
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
@@ -0,0 +1,191 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import com.mojang.authlib.properties.PropertyMap;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.UUID;
+
+public class CraftPlayerProfile implements PlayerProfile {
+
+    private final GameProfile profile;
+    private final PropertySet properties;
+
+    /**
+     * Constructs a new Game Profile with the specified ID and name.
+     * <p/>
+     * Either ID or name may be null/empty, but at least one must be filled.
+     *
+     * @param id   Unique ID of the profile
+     * @param name Display name of the profile
+     * @throws IllegalArgumentException Both ID and name are either null or empty
+     */
+    public CraftPlayerProfile(UUID id, String name) {
+        this(new GameProfile(id, name));
+    }
+
+    public CraftPlayerProfile(GameProfile profile) {
+        this.profile = profile;
+        this.properties = new PropertySet();
+    }
+
+    public GameProfile getGameProfile() {
+        return profile;
+    }
+
+    @Nullable
+    @Override
+    public UUID getId() {
+        return profile.getId();
+    }
+
+    @Nonnull
+    @Override
+    public Set<ProfileProperty> getProperties() {
+        return properties;
+    }
+
+    @Nullable
+    @Override
+    public String getName() {
+        return profile.getName();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return profile.equals(o);
+    }
+
+    @Override
+    public int hashCode() {
+        return profile.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return profile.toString();
+    }
+
+    @Override
+    public CraftPlayerProfile clone() {
+        CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName());
+        clone.setProperties(getProperties());
+        return clone;
+    }
+
+    @Override
+    public void setProperty(ProfileProperty property) {
+        String name = property.getName();
+        PropertyMap properties = profile.getProperties();
+        properties.removeAll(name);
+        properties.put(name, new Property(name, property.getValue(), property.getSignature()));
+    }
+
+    @Override
+    public void setProperties(Collection<ProfileProperty> properties) {
+        properties.forEach(this::setProperty);
+    }
+
+    @Override
+    public boolean removeProperty(String property) {
+        return !profile.getProperties().removeAll(property).isEmpty();
+    }
+
+    @Override
+    public void clearProperties() {
+        profile.getProperties().clear();
+    }
+
+    @Override
+    public boolean isComplete() {
+        return profile.isComplete();
+    }
+
+    private static ProfileProperty toBukkit(Property property) {
+        return new ProfileProperty(property.getName(), property.getValue(), property.getSignature());
+    }
+
+    public static PlayerProfile asBukkitCopy(GameProfile gameProfile) {
+        PlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName());
+        gameProfile.getProperties().values().forEach(property -> profile.setProperty(toBukkit(property)));
+        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();
+    }
+
+    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();
+            }
+        }
+    }
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 02940d697..4539b5601 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -1,6 +1,9 @@
 package net.minecraft.server;
 
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
+import com.destroystokyo.paper.profile.PlayerProfile;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.mojang.authlib.GameProfile;
 import org.apache.commons.lang.exception.ExceptionUtils;
 import org.bukkit.Location;
 import org.bukkit.craftbukkit.CraftWorld;
@@ -66,6 +69,10 @@ public final class MCUtil {
         return run.get();
     }
 
+    public static PlayerProfile toBukkit(GameProfile profile) {
+        return CraftPlayerProfile.asBukkitMirror(profile);
+    }
+
     /**
      * Calculates distance between 2 entities
      * @param e1
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 77c16fe2c..aca5ea7c0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -135,6 +135,10 @@ import org.bukkit.craftbukkit.util.CraftNamespacedKey;
 import org.bukkit.event.server.TabCompleteEvent;
 import net.md_5.bungee.api.chat.BaseComponent;
 
+import javax.annotation.Nullable; // Paper
+import javax.annotation.Nonnull; // Paper
+
+
 public final class CraftServer implements Server {
     private final String serverName = "Paper";
     private final String serverVersion;
@@ -1923,5 +1927,17 @@ public final class CraftServer implements Server {
     public boolean suggestPlayerNamesWhenNullTabCompletions() {
         return com.destroystokyo.paper.PaperConfig.suggestPlayersWhenNullTabCompletions;
     }
+
+    public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull UUID uuid) {
+        return createProfile(uuid, null);
+    }
+
+    public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull String name) {
+        return createProfile(null, name);
+    }
+
+    public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) {
+        return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name);
+    }
     // Paper end
 }
-- 
2.16.2