2019-03-20 01:28:15 +01:00
|
|
|
From 95829d064a601441f7c7f13d845608e5d00bb1f3 Mon Sep 17 00:00:00 2001
|
2018-09-18 07:33:40 +02:00
|
|
|
From: Aikar <aikar@aikar.co>
|
|
|
|
Date: Mon, 17 Sep 2018 23:37:31 -0400
|
|
|
|
Subject: [PATCH] Optimize Server World Map
|
|
|
|
|
|
|
|
Minecraft moved worlds to a hashmap in 1.13.1.
|
|
|
|
This creates inconsistent order for iteration of the map.
|
|
|
|
|
|
|
|
This patch restores World management to be back as an Array.
|
|
|
|
|
|
|
|
.values() will allow us to iterate as it was pre 1.13.1 by
|
|
|
|
ArrayList, giving consistent ordering and effecient iteration performance.
|
|
|
|
|
|
|
|
KeySet and EntrySet iteration is proxied to the List iterator,
|
|
|
|
and should retain manipulation behavior but nothing should be doing that.
|
|
|
|
|
|
|
|
Getting a World by dimension ID is now back a constant time operation.
|
|
|
|
|
|
|
|
Hopefully no other plugins try to mess with this map, as we are only handling
|
|
|
|
known NMS used methods, but we can add more if naughty plugins are found later.
|
|
|
|
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldMap.java b/src/main/java/com/destroystokyo/paper/PaperWorldMap.java
|
|
|
|
new file mode 100644
|
2019-03-20 01:28:15 +01:00
|
|
|
index 000000000..af9e4455c
|
2018-09-18 07:33:40 +02:00
|
|
|
--- /dev/null
|
|
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldMap.java
|
|
|
|
@@ -0,0 +1,191 @@
|
|
|
|
+package com.destroystokyo.paper;
|
|
|
|
+
|
|
|
|
+import net.minecraft.server.DimensionManager;
|
|
|
|
+import net.minecraft.server.WorldServer;
|
|
|
|
+
|
|
|
|
+import javax.annotation.Nonnull;
|
|
|
|
+import java.util.AbstractSet;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Collection;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.Iterator;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.Set;
|
|
|
|
+
|
|
|
|
+public class PaperWorldMap extends HashMap<DimensionManager, WorldServer> {
|
|
|
|
+ private final List<WorldServer> worlds = new ArrayList<>();
|
|
|
|
+ private final List<WorldServer> worldsIterable = new ArrayList<WorldServer>() {
|
|
|
|
+ @Override
|
|
|
|
+ public Iterator<WorldServer> iterator() {
|
|
|
|
+ Iterator<WorldServer> iterator = super.iterator();
|
|
|
|
+ return new Iterator<WorldServer>() {
|
|
|
|
+ private WorldServer last;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean hasNext() {
|
|
|
|
+ return iterator.hasNext();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public WorldServer next() {
|
|
|
|
+ this.last = iterator.next();
|
|
|
|
+ return last;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void remove() {
|
|
|
|
+ worlds.set(last.dimension.getDimensionID()+1, null);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ @Override
|
|
|
|
+ public int size() {
|
|
|
|
+ return worldsIterable.size();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean isEmpty() {
|
|
|
|
+ return worldsIterable.isEmpty();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public WorldServer get(Object key) {
|
|
|
|
+ // Will hit the below method
|
|
|
|
+ return key instanceof DimensionManager ? get((DimensionManager) key) : null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public WorldServer get(DimensionManager key) {
|
|
|
|
+ int id = key.getDimensionID()+1;
|
|
|
|
+ return worlds.size() > id ? worlds.get(id) : null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean containsKey(Object key) {
|
|
|
|
+ // will hit below method
|
|
|
|
+ return key instanceof DimensionManager && containsKey((DimensionManager) key);
|
|
|
|
+ }
|
|
|
|
+ public boolean containsKey(DimensionManager key) {
|
|
|
|
+ return get(key) != null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public WorldServer put(DimensionManager key, WorldServer value) {
|
|
|
|
+ while (worlds.size() <= key.getDimensionID()+1) {
|
|
|
|
+ worlds.add(null);
|
|
|
|
+ }
|
|
|
|
+ WorldServer old = worlds.set(key.getDimensionID()+1, value);
|
|
|
|
+ if (old != null) {
|
|
|
|
+ worldsIterable.remove(old);
|
|
|
|
+ }
|
|
|
|
+ worldsIterable.add(value);
|
|
|
|
+ return old;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void putAll(Map<? extends DimensionManager, ? extends WorldServer> m) {
|
|
|
|
+ for (Entry<? extends DimensionManager, ? extends WorldServer> e : m.entrySet()) {
|
|
|
|
+ put(e.getKey(), e.getValue());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public WorldServer remove(Object key) {
|
|
|
|
+ return key instanceof DimensionManager ? remove((DimensionManager) key) : null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public WorldServer remove(DimensionManager key) {
|
|
|
|
+ WorldServer old;
|
|
|
|
+ if (key.getDimensionID()+1 == worlds.size() - 1) {
|
|
|
|
+ old = worlds.remove(key.getDimensionID()+1);
|
|
|
|
+ } else {
|
|
|
|
+ old = worlds.set(key.getDimensionID() + 1, null);
|
|
|
|
+ }
|
|
|
|
+ if (old != null) {
|
|
|
|
+ worldsIterable.remove(old);
|
|
|
|
+ }
|
|
|
|
+ return old;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void clear() {
|
|
|
|
+ throw new RuntimeException("What the hell are you doing?");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean containsValue(Object value) {
|
|
|
|
+ return value instanceof WorldServer && get(((WorldServer) value).dimension) != null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Nonnull
|
|
|
|
+ @Override
|
|
|
|
+ public Set<DimensionManager> keySet() {
|
|
|
|
+ return new AbstractSet<DimensionManager>() {
|
|
|
|
+ @Override
|
|
|
|
+ public Iterator<DimensionManager> iterator() {
|
|
|
|
+ Iterator<WorldServer> iterator = worldsIterable.iterator();
|
|
|
|
+ return new Iterator<DimensionManager>() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean hasNext() {
|
|
|
|
+ return iterator.hasNext();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public DimensionManager next() {
|
|
|
|
+ return iterator.next().dimension;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void remove() {
|
|
|
|
+ iterator.remove();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int size() {
|
|
|
|
+ return worlds.size();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Collection<WorldServer> values() {
|
|
|
|
+ return worldsIterable;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Set<Entry<DimensionManager, WorldServer>> entrySet() {
|
|
|
|
+ return new AbstractSet<Entry<DimensionManager, WorldServer>>() {
|
|
|
|
+ @Override
|
|
|
|
+ public Iterator<Entry<DimensionManager, WorldServer>> iterator() {
|
|
|
|
+ Iterator<WorldServer> iterator = worldsIterable.iterator();
|
|
|
|
+ return new Iterator<Entry<DimensionManager, WorldServer>>() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean hasNext() {
|
|
|
|
+ return iterator.hasNext();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Entry<DimensionManager, WorldServer> next() {
|
|
|
|
+ WorldServer entry = iterator.next();
|
|
|
|
+ return new SimpleEntry<>(entry.dimension, entry);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void remove() {
|
|
|
|
+ iterator.remove();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int size() {
|
|
|
|
+ return worldsIterable.size();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
2019-03-20 01:28:15 +01:00
|
|
|
index 4543ac8f1..d2ee4e578 100644
|
2018-09-18 07:33:40 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
2019-01-01 04:15:55 +01:00
|
|
|
@@ -82,7 +82,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
|
2018-09-18 07:33:40 +02:00
|
|
|
public final DataFixer dataConverterManager;
|
|
|
|
private String serverIp;
|
|
|
|
private int q = -1;
|
2019-01-06 18:15:21 +01:00
|
|
|
- public final Map<DimensionManager, WorldServer> worldServer = Maps.newLinkedHashMap(); // CraftBukkit - keep order, k+v already use identity methods
|
|
|
|
+ public final Map<DimensionManager, WorldServer> worldServer = new com.destroystokyo.paper.PaperWorldMap(); // Paper;
|
2018-12-17 06:18:06 +01:00
|
|
|
private PlayerList playerList;
|
2018-09-18 07:33:40 +02:00
|
|
|
private boolean isRunning = true;
|
|
|
|
private boolean isRestarting = false; // Paper - flag to signify we're attempting to restart
|
2019-01-06 18:15:21 +01:00
|
|
|
@@ -546,7 +546,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
|
2018-09-19 01:30:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- for (WorldServer world : this.getWorlds()) {
|
|
|
|
+ for (WorldServer world : com.google.common.collect.Lists.newArrayList(this.getWorlds())) { // Paper - avoid como if 1 world triggers another world
|
|
|
|
this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld()));
|
|
|
|
}
|
|
|
|
// CraftBukkit end
|
2018-09-18 07:33:40 +02:00
|
|
|
--
|
2019-03-20 01:28:15 +01:00
|
|
|
2.21.0
|
2018-09-18 07:33:40 +02:00
|
|
|
|