PaperMC/paper-server/patches/sources/net/minecraft/server/players/GameProfileCache.java.patch
2024-12-15 11:42:42 -08:00

193 lines
9.2 KiB
Diff

--- a/net/minecraft/server/players/GameProfileCache.java
+++ b/net/minecraft/server/players/GameProfileCache.java
@@ -56,6 +_,10 @@
private final AtomicLong operationCount = new AtomicLong();
@Nullable
private Executor executor;
+ // Paper start - Fix GameProfileCache concurrency
+ protected final java.util.concurrent.locks.ReentrantLock stateLock = new java.util.concurrent.locks.ReentrantLock();
+ protected final java.util.concurrent.locks.ReentrantLock lookupLock = new java.util.concurrent.locks.ReentrantLock();
+ // Paper end - Fix GameProfileCache concurrency
public GameProfileCache(GameProfileRepository profileRepository, File file) {
this.profileRepository = profileRepository;
@@ -64,10 +_,12 @@
}
private void safeAdd(GameProfileCache.GameProfileInfo profile) {
+ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
GameProfile profile1 = profile.getProfile();
profile.setLastAccess(this.getNextOperation());
this.profilesByName.put(profile1.getName().toLowerCase(Locale.ROOT), profile);
this.profilesByUUID.put(profile1.getId(), profile);
+ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency
}
private static Optional<GameProfile> lookupGameProfile(GameProfileRepository profileRepo, String name) {
@@ -86,6 +_,8 @@
atomicReference.set(null);
}
};
+ if (!org.apache.commons.lang3.StringUtils.isBlank(name) // Paper - Don't lookup a profile with a blank name
+ && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()) // Paper - Add setting for proxy online mode status
profileRepo.findProfilesByNames(new String[]{name}, profileLookupCallback);
GameProfile gameProfile = atomicReference.get();
return gameProfile != null ? Optional.of(gameProfile) : createUnknownProfile(name);
@@ -101,7 +_,7 @@
}
private static boolean usesAuthentication() {
- return usesAuthentication;
+ return io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode(); // Paper - Add setting for proxy online mode status
}
public void add(GameProfile gameProfile) {
@@ -111,15 +_,29 @@
Date time = instance.getTime();
GameProfileCache.GameProfileInfo gameProfileInfo = new GameProfileCache.GameProfileInfo(gameProfile, time);
this.safeAdd(gameProfileInfo);
- this.save();
+ if(!org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) this.save(true); // Spigot - skip saving if disabled // Paper - Perf: Async GameProfileCache saving
}
private long getNextOperation() {
return this.operationCount.incrementAndGet();
}
+ // Paper start
+ public @Nullable GameProfile getProfileIfCached(String name) {
+ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
+ GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
+ if (entry == null) {
+ return null;
+ }
+ entry.setLastAccess(this.getNextOperation());
+ return entry.getProfile();
+ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency
+ }
+ // Paper end
+
public Optional<GameProfile> get(String name) {
String string = name.toLowerCase(Locale.ROOT);
+ boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
GameProfileCache.GameProfileInfo gameProfileInfo = this.profilesByName.get(string);
boolean flag = false;
if (gameProfileInfo != null && new Date().getTime() >= gameProfileInfo.expirationDate.getTime()) {
@@ -133,19 +_,24 @@
if (gameProfileInfo != null) {
gameProfileInfo.setLastAccess(this.getNextOperation());
optional = Optional.of(gameProfileInfo.getProfile());
+ stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency
} else {
- optional = lookupGameProfile(this.profileRepository, string);
+ stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency
+ try { this.lookupLock.lock(); // Paper - Fix GameProfileCache concurrency
+ optional = lookupGameProfile(this.profileRepository, name); // CraftBukkit - use correct case for offline players
+ } finally { this.lookupLock.unlock(); } // Paper - Fix GameProfileCache concurrency
if (optional.isPresent()) {
this.add(optional.get());
flag = false;
}
}
- if (flag) {
- this.save();
+ if (flag && !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { // Spigot - skip saving if disabled
+ this.save(true); // Paper - Perf: Async GameProfileCache saving
}
return optional;
+ } finally { if (stateLocked) { this.stateLock.unlock(); } } // Paper - Fix GameProfileCache concurrency
}
public CompletableFuture<Optional<GameProfile>> getAsync(String name) {
@@ -157,7 +_,7 @@
return completableFuture;
} else {
CompletableFuture<Optional<GameProfile>> completableFuture1 = CompletableFuture.<Optional<GameProfile>>supplyAsync(
- () -> this.get(name), Util.backgroundExecutor().forName("getProfile")
+ () -> this.get(name), Util.PROFILE_EXECUTOR // Paper - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
)
.whenCompleteAsync((gameProfile, exception) -> this.requests.remove(name), this.executor);
this.requests.put(name, completableFuture1);
@@ -167,6 +_,7 @@
}
public Optional<GameProfile> get(UUID uuid) {
+ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
GameProfileCache.GameProfileInfo gameProfileInfo = this.profilesByUUID.get(uuid);
if (gameProfileInfo == null) {
return Optional.empty();
@@ -174,6 +_,7 @@
gameProfileInfo.setLastAccess(this.getNextOperation());
return Optional.of(gameProfileInfo.getProfile());
}
+ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency
}
public void setExecutor(Executor exectutor) {
@@ -206,6 +_,11 @@
return (List<GameProfileCache.GameProfileInfo>)var9;
} catch (FileNotFoundException var7) {
+ // Spigot Start
+ } catch (com.google.gson.JsonSyntaxException | NullPointerException ex) {
+ GameProfileCache.LOGGER.warn( "Usercache.json is corrupted or has bad formatting. Deleting it to prevent further issues." );
+ this.file.delete();
+ // Spigot End
} catch (JsonParseException | IOException var8) {
LOGGER.warn("Failed to load profile cache {}", this.file, var8);
}
@@ -213,24 +_,45 @@
return list;
}
- public void save() {
+ public void save(boolean asyncSave) { // Paper - Perf: Async GameProfileCache saving
JsonArray jsonArray = new JsonArray();
DateFormat dateFormat = createDateFormat();
- this.getTopMRUProfiles(1000).forEach(info -> jsonArray.add(writeGameProfile(info, dateFormat)));
+ this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((info) -> jsonArray.add(writeGameProfile(info, dateFormat))); // Spigot // Paper - Fix GameProfileCache concurrency
String string = this.gson.toJson((JsonElement)jsonArray);
+ Runnable save = () -> { // Paper - Perf: Async GameProfileCache saving
try (Writer writer = Files.newWriter(this.file, StandardCharsets.UTF_8)) {
writer.write(string);
} catch (IOException var9) {
}
+ // Paper start - Perf: Async GameProfileCache saving
+ };
+ if (asyncSave) {
+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(save);
+ } else {
+ save.run();
+ }
+ // Paper end - Perf: Async GameProfileCache saving
}
private Stream<GameProfileCache.GameProfileInfo> getTopMRUProfiles(int limit) {
- return ImmutableList.copyOf(this.profilesByUUID.values())
- .stream()
- .sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed())
- .limit(limit);
- }
+ // Paper start - Fix GameProfileCache concurrency
+ return this.listTopMRUProfiles(limit).stream();
+ }
+
+ private List<GameProfileCache.GameProfileInfo> listTopMRUProfiles(int limit) {
+ try {
+ this.stateLock.lock();
+ return this.profilesByUUID.values()
+ .stream()
+ .sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed())
+ .limit(limit)
+ .toList();
+ } finally {
+ this.stateLock.unlock();
+ }
+ }
+ // Paper end - Fix GameProfileCache concurrency
private static JsonElement writeGameProfile(GameProfileCache.GameProfileInfo profileInfo, DateFormat dateFormat) {
JsonObject jsonObject = new JsonObject();