diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch new file mode 100644 index 0000000000..c505c77d38 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/item/component/ResolvableProfile.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/item/component/ResolvableProfile.java ++++ b/net/minecraft/world/item/component/ResolvableProfile.java +@@ -49,7 +49,7 @@ + if (this.isResolved()) { + return CompletableFuture.completedFuture(this); + } else { +- return this.id.isPresent() ? SkullBlockEntity.fetchGameProfile(this.id.get()).thenApply(optional -> { ++ return this.id.isPresent() ? SkullBlockEntity.fetchGameProfile(this.id.get(), this.name.orElse(null)).thenApply(optional -> { // Paper - player profile events + GameProfile gameProfile = optional.orElseGet(() -> new GameProfile(this.id.get(), this.name.orElse(""))); + return new ResolvableProfile(gameProfile); + }) : SkullBlockEntity.fetchGameProfile(this.name.orElseThrow()).thenApply(profile -> { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch index 348568625e..a683a1cb1b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch @@ -1,7 +1,54 @@ --- a/net/minecraft/world/level/block/entity/SkullBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SkullBlockEntity.java -@@ -105,7 +105,7 @@ - ProfileResult profileResult = apiServices.sessionService().fetchProfile(uuid, true); +@@ -41,7 +41,7 @@ + @Nullable + private static LoadingCache<String, CompletableFuture<Optional<GameProfile>>> profileCacheByName; + @Nullable +- private static LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> profileCacheById; ++ private static LoadingCache<com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile>, CompletableFuture<Optional<GameProfile>>> profileCacheById; // Paper - player profile events + public static final Executor CHECKED_MAIN_THREAD_EXECUTOR = runnable -> { + Executor executor = mainThreadExecutor; + if (executor != null) { +@@ -76,9 +76,9 @@ + profileCacheById = CacheBuilder.newBuilder() + .expireAfterAccess(Duration.ofMinutes(10L)) + .maximumSize(256L) +- .build(new CacheLoader<UUID, CompletableFuture<Optional<GameProfile>>>() { ++ .build(new CacheLoader<>() { // Paper - player profile events + @Override +- public CompletableFuture<Optional<GameProfile>> load(UUID uUID) { ++ public CompletableFuture<Optional<GameProfile>> load(com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile> uUID) { // Paper - player profile events + return SkullBlockEntity.fetchProfileById(uUID, apiServices, booleanSupplier); + } + }); +@@ -89,23 +89,29 @@ + .getAsync(name) + .thenCompose( + optional -> { +- LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById; ++ LoadingCache<com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile>, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById; // Paper - player profile events + return loadingCache != null && !optional.isEmpty() +- ? loadingCache.getUnchecked(optional.get().getId()).thenApply(optional2 -> optional2.or(() -> optional)) ++ ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(optional.get().getId(), optional.get())).thenApply(optional2 -> optional2.or(() -> optional)) // Paper - player profile events + : CompletableFuture.completedFuture(Optional.empty()); + } + ); + } + +- static CompletableFuture<Optional<GameProfile>> fetchProfileById(UUID uuid, Services apiServices, BooleanSupplier booleanSupplier) { ++ static CompletableFuture<Optional<GameProfile>> fetchProfileById(com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile> pair, Services apiServices, BooleanSupplier booleanSupplier) { // Paper + return CompletableFuture.supplyAsync(() -> { + if (booleanSupplier.getAsBoolean()) { + return Optional.empty(); + } else { +- ProfileResult profileResult = apiServices.sessionService().fetchProfile(uuid, true); ++ // Paper start - fill player profile events ++ if (apiServices.sessionService() instanceof com.destroystokyo.paper.profile.PaperMinecraftSessionService paperService) { ++ final GameProfile profile = pair.getSecond() != null ? pair.getSecond() : new com.mojang.authlib.GameProfile(pair.getFirst(), ""); ++ return Optional.ofNullable(paperService.fetchProfile(profile, true)).map(ProfileResult::profile); ++ } ++ ProfileResult profileResult = apiServices.sessionService().fetchProfile(pair.getFirst(), true); ++ // Paper end - fill player profile events return Optional.ofNullable(profileResult).map(ProfileResult::profile); } - }, Util.backgroundExecutor().forName("fetchProfile")); @@ -9,3 +56,18 @@ } public static void clear() { +@@ -210,9 +216,11 @@ + : CompletableFuture.completedFuture(Optional.empty()); + } + +- public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(UUID uuid) { +- LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById; +- return loadingCache != null ? loadingCache.getUnchecked(uuid) : CompletableFuture.completedFuture(Optional.empty()); ++ // Paper start - player profile events ++ public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(UUID uuid, @Nullable String name) { ++ LoadingCache<com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile>, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById; ++ return loadingCache != null ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(uuid, name != null ? new com.mojang.authlib.GameProfile(uuid, name) : null)) : CompletableFuture.completedFuture(Optional.empty()); ++ // Paper end - player profile events + } + + @Override diff --git a/paper-server/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java b/paper-server/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java index 985e6fc43a..d577384797 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java @@ -1,6 +1,7 @@ package com.destroystokyo.paper.profile; import com.mojang.authlib.Environment; +import com.mojang.authlib.GameProfile; import com.mojang.authlib.yggdrasil.ProfileResult; import com.mojang.authlib.yggdrasil.ServicesKeySet; import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; @@ -15,7 +16,21 @@ public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionServi super(servicesKeySet, proxy, environment); } - @Override + public @Nullable ProfileResult fetchProfile(GameProfile profile, final boolean requireSecure) { + CraftPlayerProfile playerProfile = (CraftPlayerProfile) CraftPlayerProfile.asBukkitMirror(profile); + new com.destroystokyo.paper.event.profile.PreFillProfileEvent(playerProfile).callEvent(); + profile = playerProfile.getGameProfile(); + if (profile.getProperties().containsKey("textures")) { + return new ProfileResult(profile, java.util.Collections.emptySet()); + } + ProfileResult result = super.fetchProfile(profile.getId(), requireSecure); + if (result != null) { + new com.destroystokyo.paper.event.profile.FillProfileEvent(CraftPlayerProfile.asBukkitMirror(result.profile())).callEvent(); + } + return result; + } + + @Override @io.papermc.paper.annotation.DoNotUse @Deprecated public @Nullable ProfileResult fetchProfile(final UUID profileId, final boolean requireSecure) { return super.fetchProfile(profileId, requireSecure); }