13
0
geforkt von Mirrors/Paper

Merge pull request #3636 from Spottedleaf/ver/1.16

Dieser Commit ist enthalten in:
Daniel Ennis 2020-06-27 01:42:12 -04:00
Commit def98e8d33
19 geänderte Dateien mit 910 neuen und 83 gelöschten Zeilen

Datei anzeigen

@ -21,9 +21,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
Bukkit.getPluginManager().callEvent(ev);
Location loc = ev.getSpawnLocation();
worldserver = ((CraftWorld) loc.getWorld()).getHandle();
@@ -0,0 +0,0 @@ public abstract class PlayerList {
entityplayer.spawnIn(worldserver);
entityplayer.spawnIn(worldserver1);
entityplayer.playerInteractManager.a((WorldServer) entityplayer.world);
- entityplayer.setPosition(loc.getX(), loc.getY(), loc.getZ());
+ entityplayer.setPositionRaw(loc.getX(), loc.getY(), loc.getZ()); // Paper - set raw so we aren't fully joined to the world (not added to chunk or world)
entityplayer.setYawPitch(loc.getYaw(), loc.getPitch());

Datei anzeigen

@ -46,21 +46,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
this.world = worldserver;
if (nbttagcompound.hasKeyOfType("DragonKilled", 99)) {
if (nbttagcompound.b("Dragon")) {
@@ -0,0 +0,0 @@ public class EnderDragonBattle {
private void a(BlockPosition blockposition) {
this.world.triggerEffect(3000, blockposition, 0);
- WorldGenerator.END_GATEWAY.b((WorldGenFeatureConfiguration) WorldGenEndGatewayConfiguration.a()).a(this.world, this.world.getStructureManager(), this.world.getChunkProvider().getChunkGenerator(), new Random(), blockposition);
+ WorldGenerator.END_GATEWAY.b(WorldGenEndGatewayConfiguration.a()).a(this.world, this.world.getStructureManager(), this.world.getChunkProvider().getChunkGenerator(), new Random(), blockposition); // Paper - decompile error
}
private void a(boolean flag) {
@@ -0,0 +0,0 @@ public class EnderDragonBattle {
}
}
- worldgenendtrophy.b((WorldGenFeatureConfiguration) WorldGenFeatureConfiguration.k).a(this.world, this.world.getStructureManager(), this.world.getChunkProvider().getChunkGenerator(), new Random(), this.exitPortalLocation);
+ worldgenendtrophy.b(WorldGenFeatureConfiguration.k).a(this.world, this.world.getStructureManager(), this.world.getChunkProvider().getChunkGenerator(), new Random(), this.exitPortalLocation); // Paper - decompile error
}
private EntityEnderDragon o() {

Datei anzeigen

@ -891,19 +891,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+
+ public int read() {
+ int value = (int) (current >>> bitInLongIndex) & mask;
+ bitInLongIndex += bitsPerObject;
+
+ if (bitInLongIndex > 63) {
+ bitInLongIndex -= 64;
+ if (bitInLongIndex + bitsPerObject > 64) {
+ bitInLongIndex = 0;
+ longInDataBitsIndex += 8;
+ init();
+
+ if (bitInLongIndex > 0) {
+ value |= current << bitsPerObject - bitInLongIndex & mask;
+ }
+ }
+
+ int value = (int) (current >>> bitInLongIndex) & mask;
+ bitInLongIndex += bitsPerObject;
+ return value;
+ }
+}
@ -969,29 +964,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+
+ public void write(int value) {
+ if (bitInLongIndex + bitsPerObject > 64) {
+ finish();
+ bitInLongIndex = 0;
+ longInDataBitsIndex += 8;
+ init();
+ }
+
+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
+ dirty = true;
+ bitInLongIndex += bitsPerObject;
+
+ if (bitInLongIndex > 63) {
+ finish();
+ bitInLongIndex -= 64;
+ longInDataBitsIndex += 8;
+ init();
+
+ if (bitInLongIndex > 0) {
+ current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
+ dirty = true;
+ }
+ }
+ }
+
+ public void skip() {
+ bitInLongIndex += bitsPerObject;
+
+ if (bitInLongIndex > 63) {
+ if (bitInLongIndex > 64) {
+ finish();
+ bitInLongIndex -= 64;
+ bitInLongIndex = bitsPerObject;
+ longInDataBitsIndex += 8;
+ init();
+ }

Datei anzeigen

@ -3241,6 +3241,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ if (ioThrowable != null) {
+ com.destroystokyo.paper.util.SneakyThrow.sneaky(ioThrowable);
+ }
+ chunkHolder.tasks.forEach(Runnable::run);
+ // Paper end
- if (nbttagcompound != null) {try (Timing ignored2 = this.world.timings.chunkLoadLevelTimer.startTimingIfSync()) { // Paper start - timings

Datei anzeigen

@ -15,8 +15,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// CraftBukkit start
+ if (!(generatoraccess instanceof WorldServer)) return this.canPlace(iblockdata, generatoraccess, blockposition) ? (IBlockData) this.a(generatoraccess, blockposition, (Integer) iblockdata.get(BlockFire.AGE)) : Blocks.AIR.getBlockData(); // Paper - don't fire events in world generation
if (!this.canPlace(iblockdata, generatoraccess, blockposition)) {
CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition);
blockState.setData(Blocks.AIR.getBlockData());
// Suppress during worldgen
if (!(generatoraccess instanceof World)) {
@@ -0,0 +0,0 @@ public class BlockFire extends BlockFireAbstract {
return blockState.getHandle();
}

Datei anzeigen

@ -63,12 +63,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
blockposition3 = blockposition3.shift(enumdirection1);
map.remove(blockposition3);
world.setTypeAndData(blockposition3, (IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPiston.FACING, enumdirection), 68);
- world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false));
+ // Paper start - fix a variety of piston desync dupes
+ if (!allowDesync) {
+ iblockdata1 = world.getType(oldPos);
+ map.replace(oldPos, iblockdata1);
+ }
world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false));
+ world.setTileEntity(blockposition3, BlockPistonMoving.a(allowDesync ? list1.get(k) : iblockdata1, enumdirection, flag, false));
+ if (!allowDesync) {
+ world.setTypeAndData(oldPos, Blocks.AIR.getBlockData(), 2 | 4 | 16 | 1024); // set air to prevent later physics updates from seeing this block
+ }

Datei anzeigen

@ -9,8 +9,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ public class PaperWorldConfig {
}
}
private void perPlayerMobSpawns() {
perPlayerMobSpawns = getBoolean("per-player-mob-spawns", false);
}
+
+ public boolean generateFlatBedrock;

Datei anzeigen

@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Fri, 26 Jun 2020 22:35:08 -0700
Subject: [PATCH] Hide sync chunk writes behind flag
Syncing writes on each write call has terrible performance
on harddrives.
-DPaper.enable-sync-chunk-writes=true to enable
diff --git a/src/main/java/net/minecraft/server/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/DedicatedServerProperties.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/DedicatedServerProperties.java
+++ b/src/main/java/net/minecraft/server/DedicatedServerProperties.java
@@ -0,0 +0,0 @@ public class DedicatedServerProperties extends PropertyManager<DedicatedServerPr
this.maxWorldSize = this.a("max-world-size", (integer) -> {
return MathHelper.clamp(integer, 1, 29999984);
}, 29999984);
- this.syncChunkWrites = this.getBoolean("sync-chunk-writes", true);
+ this.syncChunkWrites = this.getBoolean("sync-chunk-writes", true) && Boolean.getBoolean("Paper.enable-sync-chunk-writes"); // Paper - hide behind flag
this.enableJmxMonitoring = this.getBoolean("enable-jmx-monitoring", false);
this.enableStatus = this.getBoolean("enable-status", true);
this.entityBroadcastRangePercentage = this.a("entity-broadcast-range-percentage", (integer) -> {

Datei anzeigen

@ -587,8 +587,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private int lastFoodSent = -99999999;
private boolean lastSentSaturationZero = true;
@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.canPickUpLoot = true;
this.maxHealthCache = this.getMaxHealth();
this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
}
+ // Paper start
+ public BlockPosition getPointInFront(double inFront) {
@ -1069,8 +1069,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end
private static double getDistanceSquaredFromChunk(ChunkCoordIntPair chunkPos, Entity entity) { return a(chunkPos, entity); } // Paper - OBFHELPER
private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) {
public void updatePlayerMobTypeMap(Entity entity) {
if (!this.world.paperConfig.perPlayerMobSpawns) {
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
List<CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>>> list = Lists.newArrayList();
int j = chunkcoordintpair.x;

Datei anzeigen

@ -13,9 +13,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (flag5) {
if (i > 0) {
if (entity instanceof EntityLiving) {
+ ((EntityLiving) entity).knockingBackEntity = this; // Paper
((EntityLiving) entity).a((float) i * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)));
+ ((EntityLiving) entity).knockingBackEntity = null; // Paper
- ((EntityLiving) entity).a((float) i * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)));
+ ((EntityLiving) entity).doKnockback((float) i * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)), this); // Paper
} else {
entity.h((double) (-MathHelper.sin(this.yaw * 0.017453292F) * (float) i * 0.5F), 0.1D, (double) (MathHelper.cos(this.yaw * 0.017453292F) * (float) i * 0.5F));
}
@ -23,9 +22,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (entityliving != this && entityliving != entity && !this.r(entityliving) && (!(entityliving instanceof EntityArmorStand) || !((EntityArmorStand) entityliving).isMarker()) && this.h((Entity) entityliving) < 9.0D) {
// CraftBukkit start - Only apply knockback if the damage hits
if (entityliving.damageEntity(DamageSource.playerAttack(this).sweep(), f4)) {
+ ((EntityLiving) entity).knockingBackEntity = this; // Paper
entityliving.a(0.4F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)));
+ ((EntityLiving) entity).knockingBackEntity = null; // Paper
- entityliving.a(0.4F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)));
+ entityliving.doKnockback(0.4F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)), this);
}
// CraftBukkit end
}
@ -37,9 +35,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (flag) {
if (f1 > 0.0F && entity instanceof EntityLiving) {
+ ((EntityLiving) entity).knockingBackEntity = this; // Paper
((EntityLiving) entity).a(f1 * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)));
+ ((EntityLiving) entity).knockingBackEntity = null; // Paper
- ((EntityLiving) entity).a(f1 * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)));
+ ((EntityLiving) entity).doKnockback(f1 * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F)), this);
this.setMot(this.getMot().d(0.6D, 1.0D, 0.6D));
}
@ -51,9 +48,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
this.aw = (float) (MathHelper.d(d1, d0) * 57.2957763671875D - (double) this.yaw);
+ this.knockingBackEntity = entity1 instanceof EntityLiving ? ((EntityLiving) entity1) : null; // Paper
this.a(0.4F, d0, d1);
+ this.knockingBackEntity = null; // Paper
- this.a(0.4F, d0, d1);
+ this.doKnockback(0.4F, d0, d1, entity1); // Paper
} else {
this.aw = (float) ((int) (Math.random() * 2.0D) * 180);
}
@ -61,12 +57,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
protected void f(EntityLiving entityliving) {
+ ((EntityLiving) entityliving).knockingBackEntity = this; // Paper
entityliving.a(0.5F, entityliving.locX() - this.locX(), entityliving.locZ() - this.locZ());
+ ((EntityLiving) entityliving).knockingBackEntity = null; // Paper
- entityliving.a(0.5F, entityliving.locX() - this.locX(), entityliving.locZ() - this.locZ());
+ entityliving.doKnockback(0.5F, entityliving.locX() - this.locX(), entityliving.locZ() - this.locZ(), this);
}
private boolean f(DamageSource damagesource) {
@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity {
}
public void a(float f, double d0, double d1) {
+ // Paper start - add knockbacking entity parameter
+ this.doKnockback(f, d0, d1, null);
+ }
+ public void doKnockback(float f, double d0, double d1, Entity knockingBackEntity) {
+ // Paper end - add knockbacking entity parameter
f = (float) ((double) f * (1.0D - this.b(GenericAttributes.KNOCKBACK_RESISTANCE)));
if (f > 0.0F) {
this.impulse = true;
@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity {
Vec3D vec3d1 = (new Vec3D(d0, 0.0D, d1)).d().a((double) f);
@ -83,7 +90,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end
}
}
+ EntityLiving knockingBackEntity; // Paper
@Nullable
protected SoundEffect getSoundHurt(DamageSource damagesource) {

Datei anzeigen

@ -353,7 +353,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - no-tick view distance
}
private static double getDistanceSquaredFromChunk(ChunkCoordIntPair chunkPos, Entity entity) { return a(chunkPos, entity); } // Paper - OBFHELPER
public void updatePlayerMobTypeMap(Entity entity) {
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
completablefuture1.thenAcceptAsync((either) -> {
either.mapLeft((chunk) -> {

Datei anzeigen

@ -157,7 +157,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * Not guaranteed to only return hard colliding entites
+ */
+ default List<Entity> getHardCollidingEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
+ return this.getEntities(entity, axisalignedbb);
+ return this.getEntities(entity, axisalignedbb, predicate);
+ }
+ // Paper end - optimise hard collision
+

Datei anzeigen

@ -290,6 +290,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - optimise PlayerChunkMap#isOutsideRange
}
public void updatePlayerMobTypeMap(Entity entity) {
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
return entityPlayer.mobCounts[enumCreatureType.ordinal()];
}
+ private static double getDistanceSquaredFromChunk(ChunkCoordIntPair chunkPos, Entity entity) { return a(chunkPos, entity); } // Paper - OBFHELPER
private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) {
double d0 = (double) (chunkcoordintpair.x * 16 + 8);

Datei anzeigen

@ -29,7 +29,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start
+ @Override
+ public boolean canLeash(EntityHuman entityhuman) {
+ public boolean a(EntityHuman entityhuman) {
+ return world.paperConfig.allowLeashingUndeadHorse ? super.a(entityhuman) : super.a(entityhuman) && this.getMonsterType() != EnumMonsterType.UNDEAD; // Paper
+ }
+ // Paper end
@ -42,14 +42,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/EntityInsentient.java
+++ b/src/main/java/net/minecraft/server/EntityInsentient.java
@@ -0,0 +0,0 @@ public abstract class EntityInsentient extends EntityLiving {
}
public boolean a(EntityHuman entityhuman) {
+ // Paper start - allow overriding
+ return this.canLeash(entityhuman);
+ }
+ public boolean canLeash(EntityHuman entityhuman) {
+ // Paper end - allow overriding
- public boolean a(EntityHuman entityhuman) {
+ public boolean a(EntityHuman entityhuman) { // Paper - overriden in EntityHorseAbstract
return !this.isLeashed() && !(this instanceof IMonster);
}

Datei anzeigen

@ -117,9 +117,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Paper end
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.l = supplier;
this.m = new VillagePlace(new File(this.w, "poi"), datafixer, flag, this.world); // Paper
this.setViewDistance(i);
this.playerMobDistanceMap = this.world.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper
+ // Paper start - use distance map to optimise entity tracker
+ this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length];
+ this.entityTrackerTrackRanges = new int[TRACKING_RANGE_TYPES.length];
@ -161,7 +161,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - use distance map to optimise entity tracker
}
private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) {
public void updatePlayerMobTypeMap(Entity entity) {
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
}

Datei anzeigen

@ -0,0 +1,823 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 19 Aug 2019 01:27:58 +0500
Subject: [PATCH] implement optional per player mob spawns
diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java
+++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
@@ -0,0 +0,0 @@ public class WorldTimingsHandler {
public final Timing miscMobSpawning;
+ public final Timing playerMobDistanceMapUpdate;
public final Timing poiUnload;
public final Timing chunkUnload;
@@ -0,0 +0,0 @@ public class WorldTimingsHandler {
miscMobSpawning = Timings.ofSafe(name + "Mob spawning - Misc");
+ playerMobDistanceMapUpdate = Timings.ofSafe(name + "Per Player Mob Spawning - Distance Map Update");
poiUnload = Timings.ofSafe(name + "Chunk unload - POI");
chunkUnload = Timings.ofSafe(name + "Chunk unload - Chunk");
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ public class PaperWorldConfig {
}
}
}
+
+ public boolean perPlayerMobSpawns = false;
+ private void perPlayerMobSpawns() {
+ perPlayerMobSpawns = getBoolean("per-player-mob-spawns", false);
+ }
}
diff --git a/src/main/java/com/destroystokyo/paper/util/PlayerMobDistanceMap.java b/src/main/java/com/destroystokyo/paper/util/PlayerMobDistanceMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/PlayerMobDistanceMap.java
@@ -0,0 +0,0 @@
+package com.destroystokyo.paper.util;
+
+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
+import net.minecraft.server.ChunkCoordIntPair;
+import net.minecraft.server.EntityPlayer;
+import net.minecraft.server.SectionPosition;
+import org.spigotmc.AsyncCatcher;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** @author Spottedleaf */
+public final class PlayerMobDistanceMap {
+
+ private static final PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> EMPTY_SET = new PooledHashSets.PooledObjectLinkedOpenHashSet<>();
+
+ private final Map<EntityPlayer, SectionPosition> players = new HashMap<>();
+ // we use linked for better iteration.
+ private final Long2ObjectOpenHashMap<PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer>> playerMap = new Long2ObjectOpenHashMap<>(32, 0.5f);
+ private int viewDistance;
+
+ private final PooledHashSets<EntityPlayer> pooledHashSets = new PooledHashSets<>();
+
+ public PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> getPlayersInRange(final ChunkCoordIntPair chunkPos) {
+ return this.getPlayersInRange(chunkPos.x, chunkPos.z);
+ }
+
+ public PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> getPlayersInRange(final int chunkX, final int chunkZ) {
+ return this.playerMap.getOrDefault(ChunkCoordIntPair.pair(chunkX, chunkZ), EMPTY_SET);
+ }
+
+ public void update(final List<EntityPlayer> currentPlayers, final int newViewDistance) {
+ AsyncCatcher.catchOp("Distance map update");
+ final ObjectLinkedOpenHashSet<EntityPlayer> gone = new ObjectLinkedOpenHashSet<>(this.players.keySet());
+
+ final int oldViewDistance = this.viewDistance;
+ this.viewDistance = newViewDistance;
+
+ for (final EntityPlayer player : currentPlayers) {
+ if (player.isSpectator() || !player.affectsSpawning) {
+ continue; // will be left in 'gone' (or not added at all)
+ }
+
+ gone.remove(player);
+
+ final SectionPosition newPosition = player.getPlayerMapSection();
+ final SectionPosition oldPosition = this.players.put(player, newPosition);
+
+ if (oldPosition == null) {
+ this.addNewPlayer(player, newPosition, newViewDistance);
+ } else {
+ this.updatePlayer(player, oldPosition, newPosition, oldViewDistance, newViewDistance);
+ }
+ //this.validatePlayer(player, newViewDistance); // debug only
+ }
+
+ for (final EntityPlayer player : gone) {
+ final SectionPosition oldPosition = this.players.remove(player);
+ if (oldPosition != null) {
+ this.removePlayer(player, oldPosition, oldViewDistance);
+ }
+ }
+ }
+
+ // expensive op, only for debug
+ private void validatePlayer(final EntityPlayer player, final int viewDistance) {
+ int entiesGot = 0;
+ int expectedEntries = (2 * viewDistance + 1);
+ expectedEntries *= expectedEntries;
+
+ final SectionPosition currPosition = player.getPlayerMapSection();
+
+ final int centerX = currPosition.getX();
+ final int centerZ = currPosition.getZ();
+
+ for (final Long2ObjectLinkedOpenHashMap.Entry<PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer>> entry : this.playerMap.long2ObjectEntrySet()) {
+ final long key = entry.getLongKey();
+ final PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> map = entry.getValue();
+
+ if (map.referenceCount == 0) {
+ throw new IllegalStateException("Invalid map");
+ }
+
+ if (map.set.contains(player)) {
+ ++entiesGot;
+
+ final int chunkX = ChunkCoordIntPair.getX(key);
+ final int chunkZ = ChunkCoordIntPair.getZ(key);
+
+ final int dist = Math.max(Math.abs(chunkX - centerX), Math.abs(chunkZ - centerZ));
+
+ if (dist > viewDistance) {
+ throw new IllegalStateException("Expected view distance " + viewDistance + ", got " + dist);
+ }
+ }
+ }
+
+ if (entiesGot != expectedEntries) {
+ throw new IllegalStateException("Expected " + expectedEntries + ", got " + entiesGot);
+ }
+ }
+
+ private void addPlayerTo(final EntityPlayer player, final int chunkX, final int chunkZ) {
+ this.playerMap.compute(ChunkCoordIntPair.pair(chunkX, chunkZ), (final Long key, final PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> players) -> {
+ if (players == null) {
+ return player.cachedSingleMobDistanceMap;
+ } else {
+ return PlayerMobDistanceMap.this.pooledHashSets.findMapWith(players, player);
+ }
+ });
+ }
+
+ private void removePlayerFrom(final EntityPlayer player, final int chunkX, final int chunkZ) {
+ this.playerMap.compute(ChunkCoordIntPair.pair(chunkX, chunkZ), (final Long keyInMap, final PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> players) -> {
+ return PlayerMobDistanceMap.this.pooledHashSets.findMapWithout(players, player); // rets null instead of an empty map
+ });
+ }
+
+ private void updatePlayer(final EntityPlayer player, final SectionPosition oldPosition, final SectionPosition newPosition, final int oldViewDistance, final int newViewDistance) {
+ final int toX = newPosition.getX();
+ final int toZ = newPosition.getZ();
+ final int fromX = oldPosition.getX();
+ final int fromZ = oldPosition.getZ();
+
+ final int dx = toX - fromX;
+ final int dz = toZ - fromZ;
+
+ final int totalX = Math.abs(fromX - toX);
+ final int totalZ = Math.abs(fromZ - toZ);
+
+ if (Math.max(totalX, totalZ) > (2 * oldViewDistance)) {
+ // teleported?
+ this.removePlayer(player, oldPosition, oldViewDistance);
+ this.addNewPlayer(player, newPosition, newViewDistance);
+ return;
+ }
+
+ // x axis is width
+ // z axis is height
+ // right refers to the x axis of where we moved
+ // top refers to the z axis of where we moved
+
+ if (oldViewDistance == newViewDistance) {
+ // same view distance
+
+ // used for relative positioning
+ final int up = 1 | (dz >> (Integer.SIZE - 1)); // 1 if dz >= 0, -1 otherwise
+ final int right = 1 | (dx >> (Integer.SIZE - 1)); // 1 if dx >= 0, -1 otherwise
+
+ // The area excluded by overlapping the two view distance squares creates four rectangles:
+ // Two on the left, and two on the right. The ones on the left we consider the "removed" section
+ // and on the right the "added" section.
+ // https://i.imgur.com/MrnOBgI.png is a reference image. Note that the outside border is not actually
+ // exclusive to the regions they surround.
+
+ // 4 points of the rectangle
+ int maxX; // exclusive
+ int minX; // inclusive
+ int maxZ; // exclusive
+ int minZ; // inclusive
+
+ if (dx != 0) {
+ // handle right addition
+
+ maxX = toX + (oldViewDistance * right) + right; // exclusive
+ minX = fromX + (oldViewDistance * right) + right; // inclusive
+ maxZ = fromZ + (oldViewDistance * up) + up; // exclusive
+ minZ = toZ - (oldViewDistance * up); // inclusive
+
+ for (int currX = minX; currX != maxX; currX += right) {
+ for (int currZ = minZ; currZ != maxZ; currZ += up) {
+ this.addPlayerTo(player, currX, currZ);
+ }
+ }
+ }
+
+ if (dz != 0) {
+ // handle up addition
+
+ maxX = toX + (oldViewDistance * right) + right; // exclusive
+ minX = toX - (oldViewDistance * right); // inclusive
+ maxZ = toZ + (oldViewDistance * up) + up; // exclusive
+ minZ = fromZ + (oldViewDistance * up) + up; // inclusive
+
+ for (int currX = minX; currX != maxX; currX += right) {
+ for (int currZ = minZ; currZ != maxZ; currZ += up) {
+ this.addPlayerTo(player, currX, currZ);
+ }
+ }
+ }
+
+ if (dx != 0) {
+ // handle left removal
+
+ maxX = toX - (oldViewDistance * right); // exclusive
+ minX = fromX - (oldViewDistance * right); // inclusive
+ maxZ = fromZ + (oldViewDistance * up) + up; // exclusive
+ minZ = toZ - (oldViewDistance * up); // inclusive
+
+ for (int currX = minX; currX != maxX; currX += right) {
+ for (int currZ = minZ; currZ != maxZ; currZ += up) {
+ this.removePlayerFrom(player, currX, currZ);
+ }
+ }
+ }
+
+ if (dz != 0) {
+ // handle down removal
+
+ maxX = fromX + (oldViewDistance * right) + right; // exclusive
+ minX = fromX - (oldViewDistance * right); // inclusive
+ maxZ = toZ - (oldViewDistance * up); // exclusive
+ minZ = fromZ - (oldViewDistance * up); // inclusive
+
+ for (int currX = minX; currX != maxX; currX += right) {
+ for (int currZ = minZ; currZ != maxZ; currZ += up) {
+ this.removePlayerFrom(player, currX, currZ);
+ }
+ }
+ }
+ } else {
+ // different view distance
+ // for now :)
+ this.removePlayer(player, oldPosition, oldViewDistance);
+ this.addNewPlayer(player, newPosition, newViewDistance);
+ }
+ }
+
+ private void removePlayer(final EntityPlayer player, final SectionPosition position, final int viewDistance) {
+ final int x = position.getX();
+ final int z = position.getZ();
+
+ for (int xoff = -viewDistance; xoff <= viewDistance; ++xoff) {
+ for (int zoff = -viewDistance; zoff <= viewDistance; ++zoff) {
+ this.removePlayerFrom(player, x + xoff, z + zoff);
+ }
+ }
+ }
+
+ private void addNewPlayer(final EntityPlayer player, final SectionPosition position, final int viewDistance) {
+ final int x = position.getX();
+ final int z = position.getZ();
+
+ for (int xoff = -viewDistance; xoff <= viewDistance; ++xoff) {
+ for (int zoff = -viewDistance; zoff <= viewDistance; ++zoff) {
+ this.addPlayerTo(player, x + xoff, z + zoff);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java b/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/PooledHashSets.java
@@ -0,0 +0,0 @@
+package com.destroystokyo.paper.util;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+
+/** @author Spottedleaf */
+public class PooledHashSets<E> {
+
+ // we really want to avoid that equals() check as much as possible...
+ protected final Object2ObjectOpenHashMap<PooledObjectLinkedOpenHashSet<E>, PooledObjectLinkedOpenHashSet<E>> mapPool = new Object2ObjectOpenHashMap<>(64, 0.25f);
+
+ protected void decrementReferenceCount(final PooledObjectLinkedOpenHashSet<E> current) {
+ if (current.referenceCount == 0) {
+ throw new IllegalStateException("Cannot decrement reference count for " + current);
+ }
+ if (current.referenceCount == -1 || --current.referenceCount > 0) {
+ return;
+ }
+
+ this.mapPool.remove(current);
+ return;
+ }
+
+ public PooledObjectLinkedOpenHashSet<E> findMapWith(final PooledObjectLinkedOpenHashSet<E> current, final E object) {
+ final PooledObjectLinkedOpenHashSet<E> cached = current.getAddCache(object);
+
+ if (cached != null) {
+ if (cached.referenceCount != -1) {
+ ++cached.referenceCount;
+ }
+
+ decrementReferenceCount(current);
+
+ return cached;
+ }
+
+ if (!current.add(object)) {
+ return current;
+ }
+
+ // we use get/put since we use a different key on put
+ PooledObjectLinkedOpenHashSet<E> ret = this.mapPool.get(current);
+
+ if (ret == null) {
+ ret = new PooledObjectLinkedOpenHashSet<>(current);
+ current.remove(object);
+ this.mapPool.put(ret, ret);
+ ret.referenceCount = 1;
+ } else {
+ if (ret.referenceCount != -1) {
+ ++ret.referenceCount;
+ }
+ current.remove(object);
+ }
+
+ current.updateAddCache(object, ret);
+
+ decrementReferenceCount(current);
+ return ret;
+ }
+
+ // rets null if current.size() == 1
+ public PooledObjectLinkedOpenHashSet<E> findMapWithout(final PooledObjectLinkedOpenHashSet<E> current, final E object) {
+ if (current.set.size() == 1) {
+ decrementReferenceCount(current);
+ return null;
+ }
+
+ final PooledObjectLinkedOpenHashSet<E> cached = current.getRemoveCache(object);
+
+ if (cached != null) {
+ if (cached.referenceCount != -1) {
+ ++cached.referenceCount;
+ }
+
+ decrementReferenceCount(current);
+
+ return cached;
+ }
+
+ if (!current.remove(object)) {
+ return current;
+ }
+
+ // we use get/put since we use a different key on put
+ PooledObjectLinkedOpenHashSet<E> ret = this.mapPool.get(current);
+
+ if (ret == null) {
+ ret = new PooledObjectLinkedOpenHashSet<>(current);
+ current.add(object);
+ this.mapPool.put(ret, ret);
+ ret.referenceCount = 1;
+ } else {
+ if (ret.referenceCount != -1) {
+ ++ret.referenceCount;
+ }
+ current.add(object);
+ }
+
+ current.updateRemoveCache(object, ret);
+
+ decrementReferenceCount(current);
+ return ret;
+ }
+
+ public static final class PooledObjectLinkedOpenHashSet<E> implements Iterable<E> {
+
+ private static final WeakReference NULL_REFERENCE = new WeakReference(null);
+
+ final ObjectLinkedOpenHashSet<E> set;
+ int referenceCount; // -1 if special
+ int hash; // optimize hashcode
+
+ // add cache
+ WeakReference<E> lastAddObject = NULL_REFERENCE;
+ WeakReference<PooledObjectLinkedOpenHashSet<E>> lastAddMap = NULL_REFERENCE;
+
+ // remove cache
+ WeakReference<E> lastRemoveObject = NULL_REFERENCE;
+ WeakReference<PooledObjectLinkedOpenHashSet<E>> lastRemoveMap = NULL_REFERENCE;
+
+ public PooledObjectLinkedOpenHashSet() {
+ this.set = new ObjectLinkedOpenHashSet<>(2, 0.6f);
+ }
+
+ public PooledObjectLinkedOpenHashSet(final E single) {
+ this();
+ this.referenceCount = -1;
+ this.add(single);
+ }
+
+ public PooledObjectLinkedOpenHashSet(final PooledObjectLinkedOpenHashSet<E> other) {
+ this.set = other.set.clone();
+ this.hash = other.hash;
+ }
+
+ // from https://github.com/Spottedleaf/ConcurrentUtil/blob/master/src/main/java/ca/spottedleaf/concurrentutil/util/IntegerUtil.java
+ // generated by https://github.com/skeeto/hash-prospector
+ static int hash0(int x) {
+ x *= 0x36935555;
+ x ^= x >>> 16;
+ return x;
+ }
+
+ public PooledObjectLinkedOpenHashSet<E> getAddCache(final E element) {
+ final E currentAdd = this.lastAddObject.get();
+
+ if (currentAdd == null || !(currentAdd == element || currentAdd.equals(element))) {
+ return null;
+ }
+
+ final PooledObjectLinkedOpenHashSet<E> map = this.lastAddMap.get();
+ if (map == null || map.referenceCount == 0) {
+ // we need to ret null if ref count is zero as calling code will assume the map is in use
+ return null;
+ }
+
+ return map;
+ }
+
+ public PooledObjectLinkedOpenHashSet<E> getRemoveCache(final E element) {
+ final E currentRemove = this.lastRemoveObject.get();
+
+ if (currentRemove == null || !(currentRemove == element || currentRemove.equals(element))) {
+ return null;
+ }
+
+ final PooledObjectLinkedOpenHashSet<E> map = this.lastRemoveMap.get();
+ if (map == null || map.referenceCount == 0) {
+ // we need to ret null if ref count is zero as calling code will assume the map is in use
+ return null;
+ }
+
+ return map;
+ }
+
+ public void updateAddCache(final E element, final PooledObjectLinkedOpenHashSet<E> map) {
+ this.lastAddObject = new WeakReference<>(element);
+ this.lastAddMap = new WeakReference<>(map);
+ }
+
+ public void updateRemoveCache(final E element, final PooledObjectLinkedOpenHashSet<E> map) {
+ this.lastRemoveObject = new WeakReference<>(element);
+ this.lastRemoveMap = new WeakReference<>(map);
+ }
+
+ boolean add(final E element) {
+ boolean added = this.set.add(element);
+
+ if (added) {
+ this.hash += hash0(element.hashCode());
+ }
+
+ return added;
+ }
+
+ boolean remove(Object element) {
+ boolean removed = this.set.remove(element);
+
+ if (removed) {
+ this.hash -= hash0(element.hashCode());
+ }
+
+ return removed;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return this.set.iterator();
+ }
+
+ @Override
+ public int hashCode() {
+ return this.hash;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (!(other instanceof PooledObjectLinkedOpenHashSet)) {
+ return false;
+ }
+ if (this.referenceCount == 0) {
+ return other == this;
+ } else {
+ if (other == this) {
+ // Unfortunately we are never equal to our own instance while in use!
+ return false;
+ }
+ return this.hash == ((PooledObjectLinkedOpenHashSet)other).hash && this.set.equals(((PooledObjectLinkedOpenHashSet)other).set);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "PooledHashSet: size: " + this.set.size() + ", reference count: " + this.referenceCount + ", hash: " +
+ this.hashCode() + ", identity: " + System.identityHashCode(this) + " map: " + this.set.toString();
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
this.world.getMethodProfiler().enter("naturalSpawnCount");
this.world.timings.countNaturalMobs.startTiming(); // Paper - timings
int l = this.chunkMapDistance.b();
- SpawnerCreature.d spawnercreature_d = SpawnerCreature.a(l, this.world.z(), this::a);
+ // Paper start - per player mob spawning
+ SpawnerCreature.d spawnercreature_d; // moved down
+ if (this.playerChunkMap.playerMobDistanceMap != null) {
+ // update distance map
+ this.world.timings.playerMobDistanceMapUpdate.startTiming();
+ this.playerChunkMap.playerMobDistanceMap.update(this.world.players, this.playerChunkMap.viewDistance);
+ this.world.timings.playerMobDistanceMapUpdate.stopTiming();
+ // re-set mob counts
+ for (EntityPlayer player : this.world.players) {
+ Arrays.fill(player.mobCounts, 0);
+ }
+ spawnercreature_d = SpawnerCreature.countMobs(l, this.world.z(), this::a, true);
+ } else {
+ spawnercreature_d = SpawnerCreature.countMobs(l, this.world.z(), this::a, false);
+ }
+ // Paper end
this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings
this.p = spawnercreature_d;
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public boolean queueHealthUpdatePacket = false;
public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket;
// Paper end
+ // Paper start - mob spawning rework
+ public static final int ENUMCREATURETYPE_TOTAL_ENUMS = EnumCreatureType.values().length;
+ public final int[] mobCounts = new int[ENUMCREATURETYPE_TOTAL_ENUMS]; // Paper
+ public final com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> cachedSingleMobDistanceMap;
+ // Paper end
// CraftBukkit start
public String displayName;
@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.displayName = this.getName();
this.canPickUpLoot = true;
this.maxHealthCache = this.getMaxHealth();
+ this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
}
// Yes, this doesn't match Vanilla, but it's the best we can do for now.
@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
+ public final SectionPosition getPlayerMapSection() { return this.N(); } // Paper - OBFHELPER
public SectionPosition N() {
return this.cq;
}
diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/EntityTypes.java
+++ b/src/main/java/net/minecraft/server/EntityTypes.java
@@ -0,0 +0,0 @@ public class EntityTypes<T extends Entity> {
return this.bk;
}
+ public final EnumCreatureType getEnumCreatureType() { return this.e(); } // Paper - OBFHELPER
public EnumCreatureType e() {
return this.bf;
}
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
public final Int2ObjectMap<PlayerChunkMap.EntityTracker> trackedEntities;
private final Long2ByteMap z;
private final Queue<Runnable> A; private final Queue<Runnable> getUnloadQueueTasks() { return this.A; } // Paper - OBFHELPER
- private int viewDistance;
+ int viewDistance; // Paper - private -> package private
+ public final com.destroystokyo.paper.util.PlayerMobDistanceMap playerMobDistanceMap; // Paper
// CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
public final CallbackExecutor callbackExecutor = new CallbackExecutor();
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
this.l = supplier;
this.m = new VillagePlace(new File(this.w, "poi"), datafixer, flag, this.world); // Paper
this.setViewDistance(i);
+ this.playerMobDistanceMap = this.world.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper
+ }
+
+ public void updatePlayerMobTypeMap(Entity entity) {
+ if (!this.world.paperConfig.perPlayerMobSpawns) {
+ return;
+ }
+ int chunkX = (int)Math.floor(entity.locX()) >> 4;
+ int chunkZ = (int)Math.floor(entity.locZ()) >> 4;
+ int index = entity.getEntityType().getEnumCreatureType().ordinal();
+
+ for (EntityPlayer player : this.playerMobDistanceMap.getPlayersInRange(chunkX, chunkZ)) {
+ ++player.mobCounts[index];
+ }
+ }
+
+ public int getMobCountNear(EntityPlayer entityPlayer, EnumCreatureType enumCreatureType) {
+ return entityPlayer.mobCounts[enumCreatureType.ordinal()];
}
private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) {
diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/server/SpawnerCreature.java
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
});
public static SpawnerCreature.d a(int i, Iterable<Entity> iterable, SpawnerCreature.b spawnercreature_b) {
+ // Paper start - add countMobs parameter
+ return countMobs(i, iterable, spawnercreature_b, false);
+ }
+ public static SpawnerCreature.d countMobs(int i, Iterable<Entity> iterable, SpawnerCreature.b spawnercreature_b, boolean countMobs) {
+ // Paper end - add countMobs parameter
SpawnerCreatureProbabilities spawnercreatureprobabilities = new SpawnerCreatureProbabilities();
Object2IntOpenHashMap<EnumCreatureType> object2intopenhashmap = new Object2IntOpenHashMap();
Iterator iterator = iterable.iterator();
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
}
object2intopenhashmap.addTo(enumcreaturetype, 1);
+ // Paper start
+ if (countMobs) {
+ ((WorldServer)chunk.world).getChunkProvider().playerChunkMap.updatePlayerMobTypeMap(entity);
+ }
+ // Paper end
});
}
}
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
continue;
}
+ // Paper start - only allow spawns upto the limit per chunk and update count afterwards
+ int currEntityCount = spawnercreature_d.getEntityCountsByType().getInt(enumcreaturetype);
+ int k1 = limit * spawnercreature_d.getSpawnerChunks() / SpawnerCreature.b;
+ int difference = k1 - currEntityCount;
+
+ if (worldserver.paperConfig.perPlayerMobSpawns) {
+ int minDiff = Integer.MAX_VALUE;
+ for (EntityPlayer entityplayer : worldserver.getChunkProvider().playerChunkMap.playerMobDistanceMap.getPlayersInRange(chunk.getPos())) {
+ minDiff = Math.min(limit - worldserver.getChunkProvider().playerChunkMap.getMobCountNear(entityplayer, enumcreaturetype), minDiff);
+ }
+ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
+ }
+ // Paper end
+
+ if (difference > 0) { // Paper
if ((flag || !enumcreaturetype.d()) && (flag1 || enumcreaturetype.d()) && (flag2 || !enumcreaturetype.e()) && spawnercreature_d.a(enumcreaturetype, limit)) {
// CraftBukkit end
- a(enumcreaturetype, worldserver, chunk, (entitytypes, blockposition, ichunkaccess) -> {
+ int spawnCount = spawnMobs(enumcreaturetype, worldserver, chunk, (entitytypes, blockposition, ichunkaccess) -> {
return spawnercreature_d.a(entitytypes, blockposition, ichunkaccess);
}, (entityinsentient, ichunkaccess) -> {
spawnercreature_d.a(entityinsentient, ichunkaccess);
+ },
+ limit, worldserver.paperConfig.perPlayerMobSpawns ? worldserver.getChunkProvider().playerChunkMap::updatePlayerMobTypeMap : null);
+ spawnercreature_d.getEntityCountsByType().mergeInt(enumcreaturetype, 0, (keyInMap, valueInMap) -> {
+ return Integer.valueOf(spawnCount + valueInMap.intValue());
});
+ } // Paper
}
}
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
}
public static void a(EnumCreatureType enumcreaturetype, WorldServer worldserver, Chunk chunk, SpawnerCreature.c spawnercreature_c, SpawnerCreature.a spawnercreature_a) {
+ // Paper start - add parameters and int ret type
+ spawnMobs(enumcreaturetype, worldserver, chunk, spawnercreature_c, spawnercreature_a, Integer.MAX_VALUE, null);
+ }
+ public static int spawnMobs(EnumCreatureType enumcreaturetype, WorldServer worldserver, Chunk chunk, SpawnerCreature.c spawnercreature_c, SpawnerCreature.a spawnercreature_a, int maxSpawns, Consumer<Entity> trackEntity) {
+ // Paper end - add parameters and int ret type
BlockPosition blockposition = getRandomPosition(worldserver, chunk);
if (blockposition.getY() >= 1) {
- a(enumcreaturetype, worldserver, (IChunkAccess) chunk, blockposition, spawnercreature_c, spawnercreature_a);
+ return spawnMobsInternal(enumcreaturetype, worldserver, (IChunkAccess) chunk, blockposition, spawnercreature_c, spawnercreature_a, maxSpawns, trackEntity);
}
+ return 0; // Paper
}
public static void a(EnumCreatureType enumcreaturetype, WorldServer worldserver, IChunkAccess ichunkaccess, BlockPosition blockposition, SpawnerCreature.c spawnercreature_c, SpawnerCreature.a spawnercreature_a) {
+ // Paper start - add maxSpawns parameter and return spawned mobs
+ spawnMobsInternal(enumcreaturetype, worldserver, ichunkaccess, blockposition, spawnercreature_c, spawnercreature_a, Integer.MAX_VALUE, null);
+ }
+ public static int spawnMobsInternal(EnumCreatureType enumcreaturetype, WorldServer worldserver, IChunkAccess ichunkaccess, BlockPosition blockposition, SpawnerCreature.c spawnercreature_c, SpawnerCreature.a spawnercreature_a, int maxSpawns, Consumer<Entity> trackEntity) {
+ // Paper end - add maxSpawns parameter and return spawned mobs
StructureManager structuremanager = worldserver.getStructureManager();
ChunkGenerator chunkgenerator = worldserver.getChunkProvider().getChunkGenerator();
int i = blockposition.getY();
IBlockData iblockdata = worldserver.getTypeIfLoadedAndInBounds(blockposition); // Paper - don't load chunks for mob spawn
+ int j = 0; // Paper - moved up
if (iblockdata != null && !iblockdata.isOccluding(ichunkaccess, blockposition)) { // Paper - don't load chunks for mob spawn
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
- int j = 0;
+ // Paper - moved up
int k = 0;
while (k < 3) {
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
// Paper start
Boolean doSpawning = a(worldserver, enumcreaturetype, structuremanager, chunkgenerator, biomebase_biomemeta, blockposition_mutableblockposition, d2);
if (doSpawning == null) {
- return;
+ return j; // Paper
}
if (doSpawning.booleanValue() && spawnercreature_c.test(biomebase_biomemeta.c, blockposition_mutableblockposition, ichunkaccess)) { // Paper end
EntityInsentient entityinsentient = a(worldserver, biomebase_biomemeta.c);
if (entityinsentient == null) {
- return;
+ return j; // Paper
}
entityinsentient.setPositionRotation(d0, (double) i, d1, worldserver.random.nextFloat() * 360.0F, 0.0F);
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
groupdataentity = entityinsentient.prepare(worldserver, worldserver.getDamageScaler(entityinsentient.getChunkCoordinates()), EnumMobSpawn.NATURAL, groupdataentity, (NBTTagCompound) null);
// CraftBukkit start
if (worldserver.addEntity(entityinsentient, SpawnReason.NATURAL)) {
- ++j;
+ ++j; // Paper - force diff on name change - we expect this to be the total amount spawned
++k1;
spawnercreature_a.run(entityinsentient, ichunkaccess);
+ // Paper start
+ if (trackEntity != null) {
+ trackEntity.accept(entityinsentient);
+ }
+ // Paper end
}
// CraftBukkit end
- if (j >= entityinsentient.getMaxSpawnGroup()) {
- return;
+ if (j >= entityinsentient.getMaxSpawnGroup() || j >= maxSpawns) { // Paper
+ return j; // Paper
}
if (entityinsentient.c(k1)) {
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
}
}
+ return j; // Paper
}
private static boolean a(WorldServer worldserver, IChunkAccess ichunkaccess, BlockPosition.MutableBlockPosition blockposition_mutableblockposition, double d0) {
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
public static class d {
- private final int a;
- private final Object2IntOpenHashMap<EnumCreatureType> b;
+ private final int a; final int getSpawnerChunks() { return this.a; } // Paper - OBFHELPER
+ private final Object2IntOpenHashMap<EnumCreatureType> b; final Object2IntMap<EnumCreatureType> getEntityCountsByType() { return this.b; } // Paper - OBFHELPER
private final SpawnerCreatureProbabilities c;
private final Object2IntMap<EnumCreatureType> d;
@Nullable
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
// CraftBukkit start
private boolean a(EnumCreatureType enumcreaturetype, int limit) {
- int i = limit * this.a / SpawnerCreature.b;
+ int i = limit * this.a / SpawnerCreature.b; // Paper - diff on change, needed in the spawn method
// CraftBukkit end
return this.b.getInt(enumcreaturetype) < i;

@ -1 +1 @@
Subproject commit edc7a378c9258ef533f90de135e250ca91b1ae02
Subproject commit 3284612a10e704b4658aa087213a3fa9670b6926

@ -1 +1 @@
Subproject commit 4ff609e60c191f107682681b5e3a8262f8a4c844
Subproject commit d4243510102530684b860dbe927311bf47c488db

@ -1 +1 @@
Subproject commit 16d78990fe27633e0ec129216f96d3b50f770025
Subproject commit 6f4ff1b691828e538ae534eac66e80a1bf2c1adf