148 lines
7.1 KiB
Diff
148 lines
7.1 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: egg82 <phantom_zero@ymail.com>
|
||
|
Date: Tue, 7 Aug 2018 01:24:23 -0600
|
||
|
Subject: [PATCH] Use ConcurrentHashMap in JsonList
|
||
|
|
||
|
This is specifically aimed at fixing #471
|
||
|
|
||
|
Using a ConcurrentHashMap because thread safety
|
||
|
The performance benefit of Map over ConcurrentMap is negligabe at best in this scenaio, as most operations will be get and not add or remove
|
||
|
Even without considering the use-case the benefits are still negligable
|
||
|
|
||
|
Original ideas for the system included an expiration policy and/or handler
|
||
|
The simpler solution was to use a computeIfPresent in the get method
|
||
|
This will simultaneously have an O(1) lookup time and automatically expire any values
|
||
|
Since the get method (nor other similar methods) don't seem to have a critical need to flush the map to disk at any of these points further processing is simply wasteful
|
||
|
Meaning the original function expired values unrelated to the current value without actually having any explicit need to
|
||
|
|
||
|
The h method was heavily modified to be much more efficient in its processing
|
||
|
Also instead of being called on every get, it's now called just before a save
|
||
|
This will eliminate stale values being flushed to disk
|
||
|
|
||
|
Modified isEmpty to use the isEmpty() method instead of the slightly confusing size() < 1
|
||
|
The point of this is readability, but does have a side-benefit of a small microptimization
|
||
|
|
||
|
Finally, added a couple obfhelpers for the modified code
|
||
|
|
||
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||
|
index 0bb397407b55bd1c464ac603ec4c189045aabbb2..7c307a16ca3962db65be09a0ddd058a4ce81c7be 100644
|
||
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||
|
@@ -614,7 +614,7 @@ public abstract class PlayerList {
|
||
|
} else if (!this.isWhitelisted(gameprofile, event)) { // Paper
|
||
|
//chatmessage = new ChatMessage("multiplayer.disconnect.not_whitelisted"); // Paper
|
||
|
//event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, org.spigotmc.SpigotConfig.whitelistMessage); // Spigot // Paper - moved to isWhitelisted
|
||
|
- } else if (getIpBans().isBanned(socketaddress) && !getIpBans().get(socketaddress).hasExpired()) {
|
||
|
+ } else if (getIpBans().isBanned(socketaddress) && getIpBans().get(socketaddress) != null && !getIpBans().get(socketaddress).hasExpired()) { // Paper - fix NPE with temp ip bans
|
||
|
IpBanListEntry ipbanentry = this.ipBans.get(socketaddress);
|
||
|
|
||
|
chatmessage = new TranslatableComponent("multiplayer.disconnect.banned_ip.reason", new Object[]{ipbanentry.getReason()});
|
||
|
diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java
|
||
|
index 2cb235d695c244863a37454df22d5d94a291524d..e2982a8ac5448110378bc92247952332bdffe12c 100644
|
||
|
--- a/src/main/java/net/minecraft/server/players/StoredUserList.java
|
||
|
+++ b/src/main/java/net/minecraft/server/players/StoredUserList.java
|
||
|
@@ -12,6 +12,8 @@ import java.io.BufferedReader;
|
||
|
import java.io.BufferedWriter;
|
||
|
import java.io.File;
|
||
|
import java.io.IOException;
|
||
|
+import java.lang.reflect.ParameterizedType; // Paper
|
||
|
+import java.lang.reflect.Type; // Paper
|
||
|
import java.nio.charset.StandardCharsets;
|
||
|
import java.util.Collection;
|
||
|
import java.util.Iterator;
|
||
|
@@ -28,7 +30,22 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||
|
protected static final Logger LOGGER = LogManager.getLogger();
|
||
|
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
|
||
|
private final File file;
|
||
|
- private final Map<String, V> map = Maps.newHashMap();
|
||
|
+ // Paper - replace HashMap is ConcurrentHashMap
|
||
|
+ private final Map<String, V> map = Maps.newConcurrentMap(); private final Map<String, V> getBackingMap() { return this.map; } // Paper - OBFHELPER
|
||
|
+ private boolean e = true;
|
||
|
+ private static final ParameterizedType f = new ParameterizedType() {
|
||
|
+ public Type[] getActualTypeArguments() {
|
||
|
+ return new Type[]{StoredUserEntry.class};
|
||
|
+ }
|
||
|
+
|
||
|
+ public Type getRawType() {
|
||
|
+ return List.class;
|
||
|
+ }
|
||
|
+
|
||
|
+ public Type getOwnerType() {
|
||
|
+ return null;
|
||
|
+ }
|
||
|
+ };
|
||
|
|
||
|
public StoredUserList(File file) {
|
||
|
this.file = file;
|
||
|
@@ -51,8 +68,13 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||
|
|
||
|
@Nullable
|
||
|
public V get(K key) {
|
||
|
- this.removeExpired();
|
||
|
- return (V) this.map.get(this.getKeyForUser(key)); // CraftBukkit - fix decompile error
|
||
|
+ // Paper start
|
||
|
+ // this.g();
|
||
|
+ // return (V) this.d.get(this.a(k0)); // CraftBukkit - fix decompile error
|
||
|
+ return (V) this.getBackingMap().computeIfPresent(this.getMappingKey(key), (k, v) -> {
|
||
|
+ return v.hasExpired() ? null : v;
|
||
|
+ });
|
||
|
+ // Paper end
|
||
|
}
|
||
|
|
||
|
public void remove(K key) {
|
||
|
@@ -81,9 +103,11 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||
|
// CraftBukkit end
|
||
|
|
||
|
public boolean isEmpty() {
|
||
|
- return this.map.size() < 1;
|
||
|
+ // return this.d.size() < 1; // Paper
|
||
|
+ return this.getBackingMap().isEmpty(); // Paper - readability is the goal. As an aside, isEmpty() uses only sumCount() and a comparison. size() uses sumCount(), casts, and boolean logic
|
||
|
}
|
||
|
|
||
|
+ protected final String getMappingKey(K k0) { return getKeyForUser(k0); } // Paper - OBFHELPER
|
||
|
protected String getKeyForUser(K profile) {
|
||
|
return profile.toString();
|
||
|
}
|
||
|
@@ -92,15 +116,16 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||
|
return this.map.containsKey(this.getKeyForUser(k0));
|
||
|
}
|
||
|
|
||
|
+ private void removeStaleEntries() { removeExpired(); } // Paper - OBFHELPER
|
||
|
private void removeExpired() {
|
||
|
- List<K> list = Lists.newArrayList();
|
||
|
- Iterator iterator = this.map.values().iterator();
|
||
|
+ /*List<K> list = Lists.newArrayList();
|
||
|
+ Iterator iterator = this.d.values().iterator();
|
||
|
|
||
|
while (iterator.hasNext()) {
|
||
|
V v0 = (V) iterator.next(); // CraftBukkit - decompile error
|
||
|
|
||
|
if (v0.hasExpired()) {
|
||
|
- list.add(v0.getUser());
|
||
|
+ list.add(v0.getKey());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -109,9 +134,11 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||
|
while (iterator.hasNext()) {
|
||
|
K k0 = (K) iterator.next(); // CraftBukkit - decompile error
|
||
|
|
||
|
- this.map.remove(this.getKeyForUser(k0));
|
||
|
- }
|
||
|
+ this.d.remove(this.a(k0));
|
||
|
+ }*/
|
||
|
|
||
|
+ this.getBackingMap().values().removeIf(StoredUserEntry::hasExpired);
|
||
|
+ // Paper end
|
||
|
}
|
||
|
|
||
|
protected abstract StoredUserEntry<K> createEntry(JsonObject json);
|
||
|
@@ -121,6 +148,7 @@ public abstract class StoredUserList<K, V extends StoredUserEntry<K>> {
|
||
|
}
|
||
|
|
||
|
public void save() throws IOException {
|
||
|
+ this.removeStaleEntries(); // Paper - remove expired values before saving
|
||
|
JsonArray jsonarray = new JsonArray();
|
||
|
|
||
|
this.map.values().stream().map((jsonlistentry) -> {
|