Paper/patches/server/0143-Basic-PlayerProfile-API.patch
Jake Potrebic 170382fe35
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#6245)
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
e7b0f8d6 #642: Add Crafting methods to API
9e58831e SPIGOT-6641: Use varargs in sendMessage
e409fe49 SPIGOT-6545: Unable to set Guardian target via API while awareness is disabled
6997c726 SPIGOT-6661: Fix missing radius from GenericGameEvent
02d03f35 SPIGOT-6369: Add ItemStack to HangingPlaceEvent

CraftBukkit Changes:
0abf420c SPIGOT-6665: Shearing a Snowman does not drop a carved pumpkin
e8e3cbcc #893: Add Crafting methods to API
879acfee Fix missing varargs from previous commit
6572b9c3 SPIGOT-6641: Use varargs in sendMessage
9e06bb2a SPIGOT-6663: Chicken Jockeys chickens don't despawn
699f2d36 SPIGOT-6545: Unable to set Guardian target via API while awareness is disabled
8ffa54ba SPIGOT-6369: Add ItemStack to HangingPlaceEvent
c851639c SPIGOT-6645: Call EntityChangeBlockEvent before PlayerHarvestBlockEvent
8d244b0b SPIGOT-3725, SPIGOT-6638, MC-136917: Properly clear tile entities before replacing

Spigot Changes:
18c71bf4 Rebuild patches
2021-07-22 18:11:56 +00:00

550 lines
22 KiB
Diff

From 0000000000000000000000000000000000000000 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
Establishes base extension of profile systems for future edits too
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 0000000000000000000000000000000000000000..84551164b76bc8f064a3a0c030c3a1b47f567b6f
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
@@ -0,0 +1,302 @@
+package com.destroystokyo.paper.profile;
+
+import com.destroystokyo.paper.PaperConfig;
+import com.google.common.base.Charsets;
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import com.mojang.authlib.properties.PropertyMap;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.players.GameProfileCache;
+import org.apache.commons.lang3.Validate;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+public class CraftPlayerProfile implements PlayerProfile {
+
+ 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 = new GameProfile(id, name);
+ }
+
+ public CraftPlayerProfile(GameProfile profile) {
+ Validate.notNull(profile, "GameProfile cannot be null!");
+ this.profile = 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);
+ properties.put(name, new Property(name, property.getValue(), property.getSignature()));
+ }
+
+ public GameProfile getGameProfile() {
+ return profile;
+ }
+
+ @Nullable
+ @Override
+ public UUID getId() {
+ return profile.getId();
+ }
+
+ @Override
+ public UUID setId(@Nullable UUID uuid) {
+ GameProfile prev = this.profile;
+ this.profile = new GameProfile(uuid, prev.getName());
+ copyProfileProperties(prev, this.profile);
+ return prev.getId();
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return profile.getName();
+ }
+
+ @Override
+ public String setName(@Nullable String name) {
+ GameProfile prev = this.profile;
+ this.profile = new GameProfile(prev.getId(), name);
+ copyProfileProperties(prev, this.profile);
+ 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();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CraftPlayerProfile that = (CraftPlayerProfile) o;
+ return Objects.equals(profile, that.profile);
+ }
+
+ @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 boolean isComplete() {
+ return profile.isComplete();
+ }
+
+ @Override
+ public boolean completeFromCache() {
+ MinecraftServer server = MinecraftServer.getServer();
+ return completeFromCache(false, PaperConfig.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 (profile.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;
+ }
+ }
+
+ if ((profile.getName() == null || !hasTextures()) && profile.getId() != null) {
+ Optional<GameProfile> optProfile = userCache.get(this.profile.getId());
+ if (optProfile.isPresent()) {
+ GameProfile profile = optProfile.get();
+ if (this.profile.getName() == 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;
+ } else {
+ copyProfileProperties(profile, this.profile);
+ }
+ }
+ }
+ return this.profile.isComplete();
+ }
+
+ public boolean complete(boolean textures) {
+ MinecraftServer server = MinecraftServer.getServer();
+ return complete(textures, PaperConfig.isProxyOnlineMode());
+ }
+ public boolean complete(boolean textures, boolean onlineMode) {
+ MinecraftServer server = MinecraftServer.getServer();
+
+ boolean isCompleteFromCache = this.completeFromCache(true, onlineMode);
+ if (onlineMode && (!isCompleteFromCache || textures && !hasTextures())) {
+ GameProfile result = server.getSessionService().fillProfileProperties(profile, true);
+ if (result != null) {
+ copyProfileProperties(result, this.profile, true);
+ }
+ if (this.profile.isComplete()) {
+ server.getProfileCache().add(this.profile);
+ }
+ }
+ return profile.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) {
+ PropertyMap sourceProperties = source.getProperties();
+ PropertyMap targetProperties = target.getProperties();
+ if (clearTarget) targetProperties.clear();
+ if (sourceProperties.isEmpty()) {
+ return;
+ }
+
+ for (Property property : sourceProperties.values()) {
+ targetProperties.removeAll(property.getName());
+ targetProperties.put(property.getName(), property);
+ }
+ }
+
+ private static ProfileProperty toBukkit(Property property) {
+ return new ProfileProperty(property.getName(), property.getValue(), property.getSignature());
+ }
+
+ 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();
+ }
+
+ 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/com/destroystokyo/paper/profile/PaperAuthenticationService.java b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..d64d45eb01c65864fca1077982d89bc05e0f811b
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java
@@ -0,0 +1,31 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.*;
+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 = (Environment)EnvironmentParser.getEnvironmentFromProperties().orElse(YggdrasilEnvironment.PROD);;
+ }
+
+ @Override
+ public UserAuthentication createUserAuthentication(Agent agent) {
+ return new PaperUserAuthentication(this, agent);
+ }
+
+ @Override
+ public MinecraftSessionService createMinecraftSessionService() {
+ return new PaperMinecraftSessionService(this, this.environment);
+ }
+
+ @Override
+ public GameProfileRepository createProfileRepository() {
+ return new PaperGameProfileRepository(this, this.environment);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..582c169c85ac66f1f9430f79042e4655f776c157
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java
@@ -0,0 +1,18 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.Agent;
+import com.mojang.authlib.Environment;
+import com.mojang.authlib.ProfileLookupCallback;
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
+import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
+
+public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
+ public PaperGameProfileRepository(YggdrasilAuthenticationService authenticationService, Environment environment) {
+ super(authenticationService, environment);
+ }
+
+ @Override
+ public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) {
+ super.findProfilesByNames(names, agent, callback);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
new file mode 100644
index 0000000000000000000000000000000000000000..93d73c27340645c7502acafdc0b2cfbc1a759dd8
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java
@@ -0,0 +1,30 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.Environment;
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.minecraft.MinecraftProfileTexture;
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
+import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
+
+import java.util.Map;
+
+public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService {
+ protected PaperMinecraftSessionService(YggdrasilAuthenticationService authenticationService, Environment environment) {
+ super(authenticationService, environment);
+ }
+
+ @Override
+ public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
+ return super.getTextures(profile, requireSecure);
+ }
+
+ @Override
+ public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
+ return super.fillProfileProperties(profile, requireSecure);
+ }
+
+ @Override
+ protected GameProfile fillGameProfile(GameProfile profile, boolean requireSecure) {
+ return super.fillGameProfile(profile, requireSecure);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java b/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java
new file mode 100644
index 0000000000000000000000000000000000000000..3cdd06d3af7ff94f1fe1a11b9a9275e17c695a38
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java
@@ -0,0 +1,12 @@
+package com.destroystokyo.paper.profile;
+
+import com.mojang.authlib.Agent;
+import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
+import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
+import java.util.UUID;
+
+public class PaperUserAuthentication extends YggdrasilUserAuthentication {
+ public PaperUserAuthentication(YggdrasilAuthenticationService authenticationService, Agent agent) {
+ super(authenticationService, UUID.randomUUID().toString(), agent);
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 80f8d6ce6dd717d4b37b78539c65b6ac814ec93d..966f0951832040c38fdd2caa58f7eae372aa0f59 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -1,5 +1,7 @@
package net.minecraft.server;
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
+import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
import net.minecraft.core.BlockPos;
@@ -10,6 +12,7 @@ import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import org.apache.commons.lang.exception.ExceptionUtils;
+import com.mojang.authlib.GameProfile;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.CraftWorld;
@@ -344,6 +347,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/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
index c8385460701395cb5c65fba41335469ffb2d9b9a..fb0b3c5770f66cc3590f5ac4e690a33cb6179be3 100644
--- a/src/main/java/net/minecraft/server/Main.java
+++ b/src/main/java/net/minecraft/server/Main.java
@@ -130,7 +130,7 @@ public class Main {
}
File file = (File) optionset.valueOf("universe"); // CraftBukkit
- YggdrasilAuthenticationService yggdrasilauthenticationservice = new YggdrasilAuthenticationService(Proxy.NO_PROXY);
+ YggdrasilAuthenticationService yggdrasilauthenticationservice = new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY); // Paper
MinecraftSessionService minecraftsessionservice = yggdrasilauthenticationservice.createMinecraftSessionService();
GameProfileRepository gameprofilerepository = yggdrasilauthenticationservice.createProfileRepository();
GameProfileCache usercache = new GameProfileCache(gameprofilerepository, new File(file, MinecraftServer.USERID_CACHE_FILE.getName()));
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
index 6e1b7d5b20e9f6ed1b650eb9d6ac9f8c4867b4b7..61405c2b53e03a4b83e2c70c6e4d3739ca9676cb 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -135,6 +135,13 @@ public class GameProfileCache {
return this.operationCount.incrementAndGet();
}
+ // Paper start
+ @Nullable public GameProfile getProfileIfCached(String name) {
+ GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
+ return entry == null ? null : entry.getProfile();
+ }
+ // Paper end
+
public synchronized Optional<GameProfile> get(String name) { // Paper - synchronize
String s1 = name.toLowerCase(Locale.ROOT);
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 01c10ee7f4a1568a35101dd73c0049e24bfb3e00..472c466c8257bcd696184ac71019acd491eca714 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -235,6 +235,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 = "Paper"; // Paper
private final String serverVersion;
@@ -2398,5 +2401,24 @@ public final class CraftServer implements Server {
public boolean suggestPlayerNamesWhenNullTabCompletions() {
return com.destroystokyo.paper.PaperConfig.suggestPlayersWhenNullTabCompletions;
}
+
+ @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);
+ }
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
index 9405812b8c308d70de1e26ba55500301b24ecc3c..490df0dcfd0e1e0ab05943410493522f86444ef8 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
@@ -80,6 +80,13 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
}
private void setProfile(GameProfile profile) {
+ // Paper start
+ if (profile != null) {
+ com.destroystokyo.paper.profile.CraftPlayerProfile paperProfile = new com.destroystokyo.paper.profile.CraftPlayerProfile(profile);
+ paperProfile.completeFromCache(false, true);
+ profile = paperProfile.getGameProfile();
+ }
+ // Paper end
this.profile = profile;
this.serializedProfile = (profile == null) ? null : NbtUtils.writeGameProfile(new CompoundTag(), profile);
}