mirror of
https://github.com/PaperMC/Paper.git
synced 2024-09-12 15:08:27 +02:00
Upstream has released updates that appears 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: f009c3dd SPIGOT-5810, SPIGOT-5835: 'Better' handling of Player.isOnGround e677c370 Update ECJ version 5058a35d SPIGOT-5860: Item.setItemStack should be NotNull CraftBukkit Changes:d77f4d9b
SPIGOT-5810, SPIGOT-5835: 'Better' handling of Player.isOnGround53c95627
SPIGOT-5865: Piglin does not trigger EntityPickupItemEvent2ab04d24
Update ECJ version7884e079
SPIGOT-5868: Blocks do not tick in custom nether / end2a848286
SPIGOT-5863: Don't check colour in scoreboard length validationf2cbce30
SPIGOT-5866: Beehive unknown TargetReason Spigot Changes: ad703da0 SPIGOT-5870: /plugins "website" field shows "version" 1a27cfd8 #98: Improve output of /plugins command using text components 732d5bab Disable checkstyle in Spigot blocks 0199a9a6 #97: Add Memory Usage to Ticks Per Second Command. 33ea98fc SPIGOT-5858: NPE: Joining the server with an invalid dimension
654 lines
37 KiB
Diff
654 lines
37 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Tue, 5 May 2020 21:23:34 -0700
|
|
Subject: [PATCH] No-Tick view distance implementation
|
|
|
|
Implements world view distance getters/setters
|
|
|
|
Per-Player is absent due to difficulty of maintaining
|
|
the diff required to make it happen.
|
|
|
|
diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java
|
|
index c9164dfdb27ddf3709129c8aec54903a1df121ff..e33e889c291d37a821a4fbd40d9aac7bb079de0d 100644
|
|
--- a/src/main/java/co/aikar/timings/TimingsExport.java
|
|
+++ b/src/main/java/co/aikar/timings/TimingsExport.java
|
|
@@ -153,7 +153,8 @@ public class TimingsExport extends Thread {
|
|
pair("gamerules", toObjectMapper(world.getWorld().getGameRules(), rule -> {
|
|
return pair(rule, world.getWorld().getGameRuleValue(rule));
|
|
})),
|
|
- pair("ticking-distance", world.getChunkProvider().playerChunkMap.getEffectiveViewDistance())
|
|
+ pair("ticking-distance", world.getChunkProvider().playerChunkMap.getEffectiveViewDistance()),
|
|
+ pair("notick-viewdistance", world.getChunkProvider().playerChunkMap.getEffectiveNoTickViewDistance())
|
|
));
|
|
}));
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
index 5785f24b0646f8dbf3fba0bbc3b3e5b93e02f162..0746932a3191669052f15270f5c94efbce0bd0c2 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
|
@@ -634,4 +634,9 @@ public class PaperWorldConfig {
|
|
phantomIgnoreCreative = getBoolean("phantoms-do-not-spawn-on-creative-players", phantomIgnoreCreative);
|
|
phantomOnlyAttackInsomniacs = getBoolean("phantoms-only-attack-insomniacs", phantomOnlyAttackInsomniacs);
|
|
}
|
|
+
|
|
+ public int noTickViewDistance;
|
|
+ private void viewDistance() {
|
|
+ this.noTickViewDistance = this.getInt("viewdistances.no-tick-view-distance", -1);
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
|
index b65ae2d6919a67498d0646c5522735086fec00c1..7f508b9ef616071b1adeef7c00da7f4565ef4ddd 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -246,7 +246,51 @@ public class Chunk implements IChunkAccess {
|
|
}
|
|
|
|
protected void onNeighbourChange(final long bitsetBefore, final long bitsetAfter) {
|
|
+ // Paper start - no-tick view distance
|
|
+ ChunkProviderServer chunkProviderServer = ((WorldServer)this.world).getChunkProvider();
|
|
+ PlayerChunkMap chunkMap = chunkProviderServer.playerChunkMap;
|
|
+ // this code handles the addition of ticking tickets - the distance map handles the removal
|
|
+ if (!areNeighboursLoaded(bitsetBefore, 2) && areNeighboursLoaded(bitsetAfter, 2)) {
|
|
+ if (chunkMap.playerViewDistanceTickMap.getObjectsInRange(this.coordinateKey) != null) {
|
|
+ // now we're ready for entity ticking
|
|
+ chunkProviderServer.serverThreadQueue.execute(() -> {
|
|
+ // double check that this condition still holds.
|
|
+ if (Chunk.this.areNeighboursLoaded(2) && chunkMap.playerViewDistanceTickMap.getObjectsInRange(Chunk.this.coordinateKey) != null) {
|
|
+ chunkProviderServer.addTicketAtLevel(TicketType.PLAYER, Chunk.this.loc, 31, Chunk.this.loc); // 31 -> entity ticking, TODO check on update
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ }
|
|
|
|
+ // this code handles the chunk sending
|
|
+ if (!areNeighboursLoaded(bitsetBefore, 1) && areNeighboursLoaded(bitsetAfter, 1)) {
|
|
+ if (chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(this.coordinateKey) != null) {
|
|
+ // now we're ready to send
|
|
+ chunkMap.mailboxMain.a(ChunkTaskQueueSorter.a(chunkMap.getUpdatingChunk(this.coordinateKey), (() -> { // Copied frm PlayerChunkMap
|
|
+ // double check that this condition still holds.
|
|
+ if (!Chunk.this.areNeighboursLoaded(1)) {
|
|
+ return;
|
|
+ }
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> inRange = chunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(Chunk.this.coordinateKey);
|
|
+ if (inRange == null) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // broadcast
|
|
+ Object[] backingSet = inRange.getBackingSet();
|
|
+ Packet[] chunkPackets = new Packet[2];
|
|
+ for (int index = 0, len = backingSet.length; index < len; ++index) {
|
|
+ Object temp = backingSet[index];
|
|
+ if (!(temp instanceof EntityPlayer)) {
|
|
+ continue;
|
|
+ }
|
|
+ EntityPlayer player = (EntityPlayer)temp;
|
|
+ chunkMap.sendChunk(player, chunkPackets, Chunk.this);
|
|
+ }
|
|
+ })));
|
|
+ }
|
|
+ }
|
|
+ // Paper end - no-tick view distance
|
|
}
|
|
|
|
public final boolean isAnyNeighborsLoaded() {
|
|
diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
index 7e57a53ec614a2f7d2672edff9d7c0e0dca42377..c072f61e8c88eac8335acd660d8ff0e2f9db819e 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
|
|
@@ -262,7 +262,7 @@ public abstract class ChunkMapDistance {
|
|
return s;
|
|
}
|
|
|
|
- protected void a(int i) {
|
|
+ protected void setNoTickViewDistance(int i) { // Paper - force abi breakage on usage change
|
|
this.g.a(i);
|
|
}
|
|
|
|
@@ -381,7 +381,7 @@ public abstract class ChunkMapDistance {
|
|
|
|
private void a(long i, int j, boolean flag, boolean flag1) {
|
|
if (flag != flag1) {
|
|
- Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, ChunkMapDistance.b, new ChunkCoordIntPair(i));
|
|
+ Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, new ChunkCoordIntPair(i)); // Paper - no-tick view distance
|
|
|
|
if (flag1) {
|
|
ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> {
|
|
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
index fb2b64628b974758cace770ce6debe5e88318ae8..0e3ceb60e503c74fc65b1d08371645ffbb26ef5c 100644
|
|
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
|
|
@@ -115,6 +115,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|
|
|
double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks
|
|
|
|
+ boolean needsChunkCenterUpdate; // Paper - no-tick view distance
|
|
+
|
|
public EntityPlayer(MinecraftServer minecraftserver, WorldServer worldserver, GameProfile gameprofile, PlayerInteractManager playerinteractmanager) {
|
|
super(worldserver, worldserver.getSpawn(), gameprofile);
|
|
this.spawnDimension = World.OVERWORLD;
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
index 7f660d3c528f5fb4150e4ee8b29913436f125b06..40347212ad1bcf857d5b8ddb0ee6a698e2568201 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -55,6 +55,18 @@ public class PlayerChunk {
|
|
}
|
|
// Paper end - optimise isOutsideOfRange
|
|
|
|
+ // Paper start - no-tick view distance
|
|
+ public final Chunk getSendingChunk() {
|
|
+ // it's important that we use getChunkAtIfLoadedImmediately to mirror the chunk sending logic used
|
|
+ // in Chunk's neighbour callback
|
|
+ Chunk ret = this.chunkMap.world.getChunkProvider().getChunkAtIfLoadedImmediately(this.location.x, this.location.z);
|
|
+ if (ret != null && ret.areNeighboursLoaded(1)) {
|
|
+ return ret;
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end - no-tick view distance
|
|
+
|
|
public PlayerChunk(ChunkCoordIntPair chunkcoordintpair, int i, LightEngine lightengine, PlayerChunk.c playerchunk_c, PlayerChunk.d playerchunk_d) {
|
|
this.statusFutures = new AtomicReferenceArray(PlayerChunk.CHUNK_STATUSES.size());
|
|
this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
|
|
@@ -210,7 +222,7 @@ public class PlayerChunk {
|
|
}
|
|
|
|
public void a(int i, int j, int k) {
|
|
- Chunk chunk = this.getChunk();
|
|
+ Chunk chunk = this.getSendingChunk(); // Paper - no-tick view distance
|
|
|
|
if (chunk != null) {
|
|
this.r |= 1 << (j >> 4);
|
|
@@ -230,7 +242,7 @@ public class PlayerChunk {
|
|
}
|
|
|
|
public void a(EnumSkyBlock enumskyblock, int i) {
|
|
- Chunk chunk = this.getChunk();
|
|
+ Chunk chunk = this.getSendingChunk(); // Paper - no-tick view distance
|
|
|
|
if (chunk != null) {
|
|
chunk.setNeedsSaving(true);
|
|
@@ -303,9 +315,48 @@ public class PlayerChunk {
|
|
}
|
|
|
|
private void a(Packet<?> packet, boolean flag) {
|
|
- this.players.a(this.location, flag).forEach((entityplayer) -> {
|
|
- entityplayer.playerConnection.sendPacket(packet);
|
|
- });
|
|
+ // Paper start - per player view distance
|
|
+ // there can be potential desync with player's last mapped section and the view distance map, so use the
|
|
+ // view distance map here.
|
|
+ com.destroystokyo.paper.util.misc.PlayerAreaMap viewDistanceMap = this.chunkMap.playerViewDistanceBroadcastMap;
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> players = viewDistanceMap.getObjectsInRange(this.location);
|
|
+ if (players == null) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (flag) { // flag -> border only
|
|
+ Object[] backingSet = players.getBackingSet();
|
|
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
+ Object temp = backingSet[i];
|
|
+ if (!(temp instanceof EntityPlayer)) {
|
|
+ continue;
|
|
+ }
|
|
+ EntityPlayer player = (EntityPlayer)temp;
|
|
+
|
|
+ int viewDistance = viewDistanceMap.getLastViewDistance(player);
|
|
+ long lastPosition = viewDistanceMap.getLastCoordinate(player);
|
|
+
|
|
+ int distX = Math.abs(MCUtil.getCoordinateX(lastPosition) - this.location.x);
|
|
+ int distZ = Math.abs(MCUtil.getCoordinateZ(lastPosition) - this.location.z);
|
|
+
|
|
+ if (Math.max(distX, distZ) == viewDistance) {
|
|
+ player.playerConnection.sendPacket(packet);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ Object[] backingSet = players.getBackingSet();
|
|
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
+ Object temp = backingSet[i];
|
|
+ if (!(temp instanceof EntityPlayer)) {
|
|
+ continue;
|
|
+ }
|
|
+ EntityPlayer player = (EntityPlayer)temp;
|
|
+ player.playerConnection.sendPacket(packet);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ // Paper end - per player view distance
|
|
}
|
|
|
|
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> a(ChunkStatus chunkstatus, PlayerChunkMap playerchunkmap) {
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
index 026562c72d7e95345d9369c6d6331cf6cedb8f17..fe343f70ce8024c86363637fda8e5c09cc6e60a9 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -95,7 +95,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
private boolean updatingChunksModified;
|
|
private final ChunkTaskQueueSorter p;
|
|
private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailboxWorldGen;
|
|
- private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailboxMain;
|
|
+ final Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailboxMain; // Paper - private -> package private
|
|
public final WorldLoadListener worldLoadListener;
|
|
public final PlayerChunkMap.a chunkDistanceManager; public final PlayerChunkMap.a getChunkMapDistanceManager() { return this.chunkDistanceManager; } // Paper - OBFHELPER
|
|
private final AtomicInteger u;
|
|
@@ -170,6 +170,22 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
|
|
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap;
|
|
// Paper end - optimise PlayerChunkMap#isOutsideRange
|
|
+ // Paper start - no-tick view distance
|
|
+ int noTickViewDistance;
|
|
+ public final int getRawNoTickViewDistance() {
|
|
+ return this.noTickViewDistance;
|
|
+ }
|
|
+ public final int getEffectiveNoTickViewDistance() {
|
|
+ return this.noTickViewDistance == -1 ? this.getEffectiveViewDistance() : this.noTickViewDistance;
|
|
+ }
|
|
+ public final int getLoadViewDistance() {
|
|
+ return Math.max(this.getEffectiveViewDistance(), this.getEffectiveNoTickViewDistance());
|
|
+ }
|
|
+
|
|
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceBroadcastMap;
|
|
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceTickMap;
|
|
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerViewDistanceNoTickMap;
|
|
+ // Paper end - no-tick view distance
|
|
|
|
void addPlayerToDistanceMaps(EntityPlayer player) {
|
|
int chunkX = MCUtil.getChunkCoordinate(player.locX());
|
|
@@ -186,6 +202,19 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
// Paper start - optimise PlayerChunkMap#isOutsideRange
|
|
this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, ChunkMapDistance.MOB_SPAWN_RANGE);
|
|
// Paper end - optimise PlayerChunkMap#isOutsideRange
|
|
+ // Paper start - no-tick view distance
|
|
+ int effectiveTickViewDistance = this.getEffectiveViewDistance();
|
|
+ int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance);
|
|
+
|
|
+ if (!this.cannotLoadChunks(player)) {
|
|
+ this.playerViewDistanceTickMap.add(player, chunkX, chunkZ, effectiveTickViewDistance);
|
|
+ this.playerViewDistanceNoTickMap.add(player, chunkX, chunkZ, effectiveNoTickViewDistance + 2); // clients need chunk 1 neighbour, and we need another 1 for sending those extra neighbours (as we require neighbours to send)
|
|
+ }
|
|
+
|
|
+ player.needsChunkCenterUpdate = true;
|
|
+ this.playerViewDistanceBroadcastMap.add(player, chunkX, chunkZ, effectiveNoTickViewDistance + 1); // clients need an extra neighbour to render the full view distance configured
|
|
+ player.needsChunkCenterUpdate = false;
|
|
+ // Paper end - no-tick view distance
|
|
}
|
|
|
|
void removePlayerFromDistanceMaps(EntityPlayer player) {
|
|
@@ -198,6 +227,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
this.playerMobSpawnMap.remove(player);
|
|
this.playerChunkTickRangeMap.remove(player);
|
|
// Paper end - optimise PlayerChunkMap#isOutsideRange
|
|
+ // Paper start - no-tick view distance
|
|
+ this.playerViewDistanceBroadcastMap.remove(player);
|
|
+ this.playerViewDistanceTickMap.remove(player);
|
|
+ this.playerViewDistanceNoTickMap.remove(player);
|
|
+ // Paper end - no-tick view distance
|
|
}
|
|
|
|
void updateMaps(EntityPlayer player) {
|
|
@@ -215,6 +249,19 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
// Paper start - optimise PlayerChunkMap#isOutsideRange
|
|
this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, ChunkMapDistance.MOB_SPAWN_RANGE);
|
|
// Paper end - optimise PlayerChunkMap#isOutsideRange
|
|
+ // Paper start - no-tick view distance
|
|
+ int effectiveTickViewDistance = this.getEffectiveViewDistance();
|
|
+ int effectiveNoTickViewDistance = Math.max(this.getEffectiveNoTickViewDistance(), effectiveTickViewDistance);
|
|
+
|
|
+ if (!this.cannotLoadChunks(player)) {
|
|
+ this.playerViewDistanceTickMap.update(player, chunkX, chunkZ, effectiveTickViewDistance);
|
|
+ this.playerViewDistanceNoTickMap.update(player, chunkX, chunkZ, effectiveNoTickViewDistance + 2); // clients need chunk 1 neighbour, and we need another 1 for sending those extra neighbours (as we require neighbours to send)
|
|
+ }
|
|
+
|
|
+ player.needsChunkCenterUpdate = true;
|
|
+ this.playerViewDistanceBroadcastMap.update(player, chunkX, chunkZ, effectiveNoTickViewDistance + 1); // clients need an extra neighbour to render the full view distance configured
|
|
+ player.needsChunkCenterUpdate = false;
|
|
+ // Paper end - no-tick view distance
|
|
}
|
|
// Paper end
|
|
|
|
@@ -322,6 +369,45 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
});
|
|
// Paper end - optimise PlayerChunkMap#isOutsideRange
|
|
+ // Paper start - no-tick view distance
|
|
+ this.setNoTickViewDistance(this.world.paperConfig.noTickViewDistance);
|
|
+ this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
|
+ (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
|
|
+ if (newState.size() != 1) {
|
|
+ return;
|
|
+ }
|
|
+ Chunk chunk = PlayerChunkMap.this.world.getChunkProvider().getChunkAtIfLoadedMainThreadNoCache(rangeX, rangeZ);
|
|
+ if (chunk == null || !chunk.areNeighboursLoaded(2)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(rangeX, rangeZ);
|
|
+ PlayerChunkMap.this.world.getChunkProvider().addTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update
|
|
+ },
|
|
+ (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
|
|
+ if (newState != null) {
|
|
+ return;
|
|
+ }
|
|
+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(rangeX, rangeZ);
|
|
+ PlayerChunkMap.this.world.getChunkProvider().removeTicketAtLevel(TicketType.PLAYER, chunkPos, 31, chunkPos); // entity ticking level, TODO check on update
|
|
+ });
|
|
+ this.playerViewDistanceNoTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
|
|
+ this.playerViewDistanceBroadcastMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
|
+ (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
|
|
+ if (player.needsChunkCenterUpdate) {
|
|
+ player.needsChunkCenterUpdate = false;
|
|
+ player.playerConnection.sendPacket(new PacketPlayOutViewCentre(currPosX, currPosZ));
|
|
+ }
|
|
+ PlayerChunkMap.this.sendChunk(player, new ChunkCoordIntPair(rangeX, rangeZ), new Packet[2], false, true); // unloaded, loaded
|
|
+ },
|
|
+ (EntityPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newState) -> {
|
|
+ PlayerChunkMap.this.sendChunk(player, new ChunkCoordIntPair(rangeX, rangeZ), null, true, false); // unloaded, loaded
|
|
+ });
|
|
+ // Paper end - no-tick view distance
|
|
}
|
|
|
|
public void updatePlayerMobTypeMap(Entity entity) {
|
|
@@ -1140,15 +1226,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
completablefuture1.thenAcceptAsync((either) -> {
|
|
either.mapLeft((chunk) -> {
|
|
this.u.getAndIncrement();
|
|
- Packet<?>[] apacket = new Packet[2];
|
|
-
|
|
- this.a(chunkcoordintpair, false).forEach((entityplayer) -> {
|
|
- this.a(entityplayer, apacket, chunk);
|
|
- });
|
|
+ // Paper - no-tick view distance - moved to Chunk neighbour update
|
|
return Either.left(chunk);
|
|
});
|
|
}, (runnable) -> {
|
|
- this.mailboxMain.a(ChunkTaskQueueSorter.a(playerchunk, runnable));
|
|
+ this.mailboxMain.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // Paper - diff on change, this is the scheduling method copied in Chunk used to schedule chunk broadcasts (on change it needs to be copied again)
|
|
});
|
|
return completablefuture1;
|
|
}
|
|
@@ -1243,32 +1325,38 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
}
|
|
|
|
- protected void setViewDistance(int i) {
|
|
- int j = MathHelper.clamp(i + 1, 3, 33);
|
|
+ public void setViewDistance(int i) { // Paper - public
|
|
+ int j = MathHelper.clamp(i + 1, 3, 33); // Paper - diff on change, these make the lower view distance limit 2 and the upper 32
|
|
|
|
if (j != this.viewDistance) {
|
|
int k = this.viewDistance;
|
|
|
|
this.viewDistance = j;
|
|
- this.chunkDistanceManager.a(this.viewDistance);
|
|
- ObjectIterator objectiterator = this.updatingChunks.values().iterator();
|
|
+ this.setNoTickViewDistance(this.getRawNoTickViewDistance()); //Paper - no-tick view distance - propagate changes to no-tick, which does the actual chunk loading/sending
|
|
+ }
|
|
|
|
- while (objectiterator.hasNext()) {
|
|
- PlayerChunk playerchunk = (PlayerChunk) objectiterator.next();
|
|
- ChunkCoordIntPair chunkcoordintpair = playerchunk.i();
|
|
- Packet<?>[] apacket = new Packet[2];
|
|
+ }
|
|
|
|
- this.a(chunkcoordintpair, false).forEach((entityplayer) -> {
|
|
- int l = b(chunkcoordintpair, entityplayer, true);
|
|
- boolean flag = l <= k;
|
|
- boolean flag1 = l <= this.viewDistance;
|
|
+ // Paper start - no-tick view distance
|
|
+ public final void setNoTickViewDistance(int viewDistance) {
|
|
+ viewDistance = viewDistance == -1 ? -1 : MathHelper.clamp(viewDistance, 2, 32);
|
|
|
|
- this.sendChunk(entityplayer, chunkcoordintpair, apacket, flag, flag1);
|
|
- });
|
|
+ this.noTickViewDistance = viewDistance;
|
|
+ int loadViewDistance = this.getLoadViewDistance();
|
|
+ this.chunkDistanceManager.setNoTickViewDistance(loadViewDistance + 2 + 2); // add 2 to account for the change to 31 -> 33 tickets // see notes in the distance map updating for the other + 2
|
|
+
|
|
+ if (this.world != null && this.world.players != null) { // this can be called from constructor, where these aren't set
|
|
+ for (EntityPlayer player : this.world.players) {
|
|
+ PlayerConnection connection = player.playerConnection;
|
|
+ if (connection != null) {
|
|
+ // moved in from PlayerList
|
|
+ connection.sendPacket(new PacketPlayOutViewDistance(loadViewDistance));
|
|
+ }
|
|
+ this.updateMaps(player);
|
|
}
|
|
}
|
|
-
|
|
}
|
|
+ // Paper end - no-tick view distance
|
|
|
|
protected void sendChunk(EntityPlayer entityplayer, ChunkCoordIntPair chunkcoordintpair, Packet<?>[] apacket, boolean flag, boolean flag1) {
|
|
if (entityplayer.world == this.world) {
|
|
@@ -1276,7 +1364,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
PlayerChunk playerchunk = this.getVisibleChunk(chunkcoordintpair.pair());
|
|
|
|
if (playerchunk != null) {
|
|
- Chunk chunk = playerchunk.getChunk();
|
|
+ Chunk chunk = playerchunk.getSendingChunk(); // Paper - no-tick view distance
|
|
|
|
if (chunk != null) {
|
|
this.a(entityplayer, apacket, chunk);
|
|
@@ -1537,6 +1625,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
// Paper end - optimise isOutsideOfRange
|
|
|
|
+ private boolean cannotLoadChunks(EntityPlayer entityplayer) { return this.b(entityplayer); } // Paper - OBFHELPER
|
|
private boolean b(EntityPlayer entityplayer) {
|
|
return entityplayer.isSpectator() && !this.world.getGameRules().getBoolean(GameRules.SPECTATORS_GENERATE_CHUNKS);
|
|
}
|
|
@@ -1564,13 +1653,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
this.removePlayerFromDistanceMaps(entityplayer); // Paper - distance maps
|
|
}
|
|
|
|
- for (int k = i - this.viewDistance; k <= i + this.viewDistance; ++k) {
|
|
- for (int l = j - this.viewDistance; l <= j + this.viewDistance; ++l) {
|
|
- ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(k, l);
|
|
-
|
|
- this.sendChunk(entityplayer, chunkcoordintpair, new Packet[2], !flag, flag);
|
|
- }
|
|
- }
|
|
+ // Paper - broadcast view distance map handles this (see remove/add calls above)
|
|
|
|
}
|
|
|
|
@@ -1578,7 +1661,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
SectionPosition sectionposition = SectionPosition.a((Entity) entityplayer);
|
|
|
|
entityplayer.a(sectionposition);
|
|
- entityplayer.playerConnection.sendPacket(new PacketPlayOutViewCentre(sectionposition.a(), sectionposition.c()));
|
|
+ // Paper - distance map handles this now
|
|
return sectionposition;
|
|
}
|
|
|
|
@@ -1623,6 +1706,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
int k1;
|
|
int l1;
|
|
|
|
+ /* // Paper start - replaced by distance map
|
|
if (Math.abs(i1 - i) <= this.viewDistance * 2 && Math.abs(j1 - j) <= this.viewDistance * 2) {
|
|
k1 = Math.min(i, i1) - this.viewDistance;
|
|
l1 = Math.min(j, j1) - this.viewDistance;
|
|
@@ -1660,7 +1744,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
this.sendChunk(entityplayer, chunkcoordintpair1, new Packet[2], false, true);
|
|
}
|
|
}
|
|
- }
|
|
+ }*/ // Paper end - replaced by distance map
|
|
|
|
this.updateMaps(entityplayer); // Paper - distance maps
|
|
|
|
@@ -1668,11 +1752,46 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
@Override
|
|
public Stream<EntityPlayer> a(ChunkCoordIntPair chunkcoordintpair, boolean flag) {
|
|
- return this.playerMap.a(chunkcoordintpair.pair()).filter((entityplayer) -> {
|
|
- int i = b(chunkcoordintpair, entityplayer, true);
|
|
+ // Paper start - per player view distance
|
|
+ // there can be potential desync with player's last mapped section and the view distance map, so use the
|
|
+ // view distance map here.
|
|
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> inRange = this.playerViewDistanceBroadcastMap.getObjectsInRange(chunkcoordintpair);
|
|
|
|
- return i > this.viewDistance ? false : !flag || i == this.viewDistance;
|
|
- });
|
|
+ if (inRange == null) {
|
|
+ return Stream.empty();
|
|
+ }
|
|
+ // all current cases are inlined so we wont hit this code, it's just in case plugins or future updates use it
|
|
+ List<EntityPlayer> players = new java.util.ArrayList<>();
|
|
+ Object[] backingSet = inRange.getBackingSet();
|
|
+
|
|
+ if (flag) { // flag -> border only
|
|
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
+ Object temp = backingSet[i];
|
|
+ if (!(temp instanceof EntityPlayer)) {
|
|
+ continue;
|
|
+ }
|
|
+ EntityPlayer player = (EntityPlayer)temp;
|
|
+ int viewDistance = this.playerViewDistanceBroadcastMap.getLastViewDistance(player);
|
|
+ long lastPosition = this.playerViewDistanceBroadcastMap.getLastCoordinate(player);
|
|
+
|
|
+ int distX = Math.abs(MCUtil.getCoordinateX(lastPosition) - chunkcoordintpair.x);
|
|
+ int distZ = Math.abs(MCUtil.getCoordinateZ(lastPosition) - chunkcoordintpair.z);
|
|
+ if (Math.max(distX, distZ) == viewDistance) {
|
|
+ players.add(player);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
|
+ Object temp = backingSet[i];
|
|
+ if (!(temp instanceof EntityPlayer)) {
|
|
+ continue;
|
|
+ }
|
|
+ EntityPlayer player = (EntityPlayer)temp;
|
|
+ players.add(player);
|
|
+ }
|
|
+ }
|
|
+ return players.stream();
|
|
+ // Paper end - per player view distance
|
|
}
|
|
|
|
protected void addEntity(Entity entity) {
|
|
@@ -1830,6 +1949,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
|
|
}
|
|
|
|
+ final void sendChunk(EntityPlayer entityplayer, Packet<?>[] apacket, Chunk chunk) { this.a(entityplayer, apacket, chunk); } // Paper - OBFHELPER
|
|
private void a(EntityPlayer entityplayer, Packet<?>[] apacket, Chunk chunk) {
|
|
if (apacket[0] == null) {
|
|
apacket[0] = new PacketPlayOutMapChunk(chunk, 65535, true);
|
|
@@ -2015,7 +2135,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(this.tracker.chunkX, this.tracker.chunkZ);
|
|
PlayerChunk playerchunk = PlayerChunkMap.this.getVisibleChunk(chunkcoordintpair.pair());
|
|
|
|
- if (playerchunk != null && playerchunk.getChunk() != null) {
|
|
+ if (playerchunk != null && playerchunk.getSendingChunk() != null) { // Paper - no-tick view distance
|
|
flag1 = PlayerChunkMap.b(chunkcoordintpair, entityplayer, false) <= PlayerChunkMap.this.viewDistance;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
|
|
index f7c23ec053b261a5e7e069fb61dd803c385d6758..e1ed059fd347465929b89c1d5a324f494104d602 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerList.java
|
|
@@ -819,7 +819,7 @@ public abstract class PlayerList {
|
|
// CraftBukkit start
|
|
WorldData worlddata = worldserver1.getWorldData();
|
|
entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(worldserver1.getTypeKey(), worldserver1.getDimensionKey(), BiomeManager.a(worldserver1.getSeed()), entityplayer1.playerInteractManager.getGameMode(), entityplayer1.playerInteractManager.c(), worldserver1.isDebugWorld(), worldserver1.isFlatWorld(), flag));
|
|
- entityplayer1.playerConnection.sendPacket(new PacketPlayOutViewDistance(worldserver1.spigotConfig.viewDistance)); // Spigot
|
|
+ entityplayer1.playerConnection.sendPacket(new PacketPlayOutViewDistance(worldserver1.getChunkProvider().playerChunkMap.getLoadViewDistance())); // Spigot // Paper - no-tick view distance
|
|
entityplayer1.spawnIn(worldserver1);
|
|
entityplayer1.dead = false;
|
|
entityplayer1.playerConnection.teleport(new Location(worldserver1.getWorld(), entityplayer1.locX(), entityplayer1.locY(), entityplayer1.locZ(), entityplayer1.yaw, entityplayer1.pitch));
|
|
@@ -1286,7 +1286,7 @@ public abstract class PlayerList {
|
|
|
|
public void a(int i) {
|
|
this.viewDistance = i;
|
|
- this.sendAll(new PacketPlayOutViewDistance(i));
|
|
+ //this.sendAll(new PacketPlayOutViewDistance(i)); // Paper - move into setViewDistance
|
|
Iterator iterator = this.server.getWorlds().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index 365539e53efdb7e729b580f52fb23659cbde4f0a..ba50189b10b54f5b35448c898f8c3495ddf8e112 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -508,8 +508,13 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
|
|
this.b(blockposition, iblockdata1, iblockdata2);
|
|
}
|
|
|
|
- if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || (chunk.getState() != null && chunk.getState().isAtLeast(PlayerChunk.State.TICKING)))) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement
|
|
+ if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || (chunk.getState() != null && chunk.getState().isAtLeast(PlayerChunk.State.TICKING)))) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement // Paper - diff on change, see below
|
|
this.notify(blockposition, iblockdata1, iblockdata, i);
|
|
+ // Paper start - per player view distance - allow block updates for non-ticking chunks in player view distance
|
|
+ // if copied from above
|
|
+ } else if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || ((WorldServer)this).getChunkProvider().playerChunkMap.playerViewDistanceBroadcastMap.getObjectsInRange(MCUtil.getCoordinateKey(blockposition)) != null)) {
|
|
+ ((WorldServer)this).getChunkProvider().flagDirty(blockposition);
|
|
+ // Paper end - per player view distance
|
|
}
|
|
|
|
if (!this.isClientSide && (i & 1) != 0) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index 5fb4f31693f1214617e43228450d2435d26202f9..8a37871bea36bd34b514428acea1acad2ba5897e 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -2521,10 +2521,39 @@ public class CraftWorld implements World {
|
|
// Spigot start
|
|
@Override
|
|
public int getViewDistance() {
|
|
- return world.spigotConfig.viewDistance;
|
|
+ return getHandle().getChunkProvider().playerChunkMap.getEffectiveViewDistance(); // Paper - no-tick view distance
|
|
}
|
|
// Spigot end
|
|
|
|
+ // Paper start - per player view distance
|
|
+ @Override
|
|
+ public void setViewDistance(int viewDistance) {
|
|
+ if (viewDistance < 2 || viewDistance > 32) {
|
|
+ throw new IllegalArgumentException("View distance " + viewDistance + " is out of range of [2, 32]");
|
|
+ }
|
|
+ net.minecraft.server.PlayerChunkMap chunkMap = getHandle().getChunkProvider().playerChunkMap;
|
|
+ if (viewDistance != chunkMap.getEffectiveViewDistance()) {
|
|
+ chunkMap.setViewDistance(viewDistance);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getNoTickViewDistance() {
|
|
+ return getHandle().getChunkProvider().playerChunkMap.getEffectiveNoTickViewDistance();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setNoTickViewDistance(int viewDistance) {
|
|
+ if ((viewDistance < 2 || viewDistance > 32) && viewDistance != -1) {
|
|
+ throw new IllegalArgumentException("View distance " + viewDistance + " is out of range of [2, 32]");
|
|
+ }
|
|
+ net.minecraft.server.PlayerChunkMap chunkMap = getHandle().getChunkProvider().playerChunkMap;
|
|
+ if (viewDistance != chunkMap.getRawNoTickViewDistance()) {
|
|
+ chunkMap.setNoTickViewDistance(viewDistance);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - per player view distance
|
|
+
|
|
// Spigot start
|
|
private final Spigot spigot = new Spigot()
|
|
{
|
|
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
|
index 5dcc1ba547db7cc53a08426a7ad119ae88690136..f78e44e05f7a97a7da190aa4b5aa6fb8340728ea 100644
|
|
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
|
@@ -201,7 +201,7 @@ public class ActivationRange
|
|
maxRange = Math.max( maxRange, waterActivationRange );
|
|
maxRange = Math.max( maxRange, villagerActivationRange );
|
|
// Paper end
|
|
- maxRange = Math.min( ( world.spigotConfig.viewDistance << 4 ) - 8, maxRange );
|
|
+ maxRange = Math.min( ( ((net.minecraft.server.WorldServer)world).getChunkProvider().playerChunkMap.getEffectiveViewDistance() << 4 ) - 8, maxRange ); // Paper - no-tick view distance
|
|
|
|
for ( EntityHuman player : world.getPlayers() )
|
|
{
|