mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-28 20:07:41 +01:00
work work (#5821)
This commit is contained in:
parent
24cd075f4e
commit
c946bcff39
118
patches/server/Implement-alternative-item-despawn-rate.patch
Normal file
118
patches/server/Implement-alternative-item-despawn-rate.patch
Normal file
@ -0,0 +1,118 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kickash32 <kickash32@gmail.com>
|
||||
Date: Mon, 3 Jun 2019 02:02:39 -0400
|
||||
Subject: [PATCH] Implement alternative item-despawn-rate
|
||||
|
||||
|
||||
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 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
+import java.util.EnumMap;
|
||||
+import java.util.HashMap;
|
||||
import java.util.List;
|
||||
+import java.util.Map;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
+import org.bukkit.Material;
|
||||
+import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
@@ -0,0 +0,0 @@ public class PaperWorldConfig {
|
||||
private void viewDistance() {
|
||||
this.noTickViewDistance = this.getInt("viewdistances.no-tick-view-distance", -1);
|
||||
}
|
||||
+
|
||||
+ public boolean altItemDespawnRateEnabled;
|
||||
+ public Map<Material, Integer> altItemDespawnRateMap;
|
||||
+ private void altItemDespawnRate() {
|
||||
+ String path = "alt-item-despawn-rate";
|
||||
+
|
||||
+ altItemDespawnRateEnabled = getBoolean(path + ".enabled", false);
|
||||
+
|
||||
+ Map<Material, Integer> altItemDespawnRateMapDefault = new EnumMap<>(Material.class);
|
||||
+ altItemDespawnRateMapDefault.put(Material.COBBLESTONE, 300);
|
||||
+ for (Material key : altItemDespawnRateMapDefault.keySet()) {
|
||||
+ config.addDefault("world-settings.default." + path + ".items." + key, altItemDespawnRateMapDefault.get(key));
|
||||
+ }
|
||||
+
|
||||
+ Map<String, Integer> rawMap = new HashMap<>();
|
||||
+ try {
|
||||
+ ConfigurationSection mapSection = config.getConfigurationSection("world-settings." + worldName + "." + path + ".items");
|
||||
+ if (mapSection == null) {
|
||||
+ mapSection = config.getConfigurationSection("world-settings.default." + path + ".items");
|
||||
+ }
|
||||
+ for (String key : mapSection.getKeys(false)) {
|
||||
+ int val = mapSection.getInt(key);
|
||||
+ rawMap.put(key, val);
|
||||
+ }
|
||||
+ }
|
||||
+ catch (Exception e) {
|
||||
+ logError("alt-item-despawn-rate was malformatted");
|
||||
+ altItemDespawnRateEnabled = false;
|
||||
+ }
|
||||
+
|
||||
+ altItemDespawnRateMap = new EnumMap<>(Material.class);
|
||||
+ if (!altItemDespawnRateEnabled) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for(String key : rawMap.keySet()) {
|
||||
+ try {
|
||||
+ altItemDespawnRateMap.put(Material.valueOf(key), rawMap.get(key));
|
||||
+ } catch (Exception e) {
|
||||
+ logError("Could not add item " + key + " to altItemDespawnRateMap: " + e.getMessage());
|
||||
+ }
|
||||
+ }
|
||||
+ if(altItemDespawnRateEnabled) {
|
||||
+ for(Material key : altItemDespawnRateMap.keySet()) {
|
||||
+ log("Alternative item despawn rate of " + key + ": " + altItemDespawnRateMap.get(key));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
|
||||
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!this.level.isClientSide && this.age >= level.spigotConfig.itemDespawnRate) { // Spigot
|
||||
+ if (!this.level.isClientSide && this.age >= this.getDespawnRate()) { // Spigot // Paper
|
||||
// CraftBukkit start - fire ItemDespawnEvent
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
|
||||
this.age = 0;
|
||||
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity {
|
||||
this.lastTick = MinecraftServer.currentTick;
|
||||
// CraftBukkit end
|
||||
|
||||
- if (!this.level.isClientSide && this.age >= level.spigotConfig.itemDespawnRate) { // Spigot
|
||||
+ if (!this.level.isClientSide && this.age >= this.getDespawnRate()) { // Spigot // Paper
|
||||
// CraftBukkit start - fire ItemDespawnEvent
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
|
||||
this.age = 0;
|
||||
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity {
|
||||
|
||||
public void makeFakeItem() {
|
||||
this.setNeverPickUp();
|
||||
- this.age = level.spigotConfig.itemDespawnRate - 1; // Spigot
|
||||
+ this.age = this.getDespawnRate() - 1; // Spigot
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public int getDespawnRate(){
|
||||
+ org.bukkit.Material material = this.getItem().getBukkitStack().getType();
|
||||
+ return level.paperConfig.altItemDespawnRateMap.getOrDefault(material, level.spigotConfig.itemDespawnRate);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public float getSpin(float tickDelta) {
|
||||
return ((float) this.getAge() + tickDelta) / 20.0F + this.bobOffs;
|
||||
}
|
816
patches/server/implement-optional-per-player-mob-spawns.patch
Normal file
816
patches/server/implement-optional-per-player-mob-spawns.patch
Normal file
@ -0,0 +1,816 @@
|
||||
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 java.util.List;
|
||||
+import java.util.Map;
|
||||
+import net.minecraft.core.SectionPos;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
+import net.minecraft.world.level.ChunkPos;
|
||||
+import org.spigotmc.AsyncCatcher;
|
||||
+import java.util.HashMap;
|
||||
+
|
||||
+/** @author Spottedleaf */
|
||||
+public final class PlayerMobDistanceMap {
|
||||
+
|
||||
+ private static final PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> EMPTY_SET = new PooledHashSets.PooledObjectLinkedOpenHashSet<>();
|
||||
+
|
||||
+ private final Map<ServerPlayer, SectionPos> players = new HashMap<>();
|
||||
+ // we use linked for better iteration.
|
||||
+ private final Long2ObjectOpenHashMap<PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer>> playerMap = new Long2ObjectOpenHashMap<>(32, 0.5f);
|
||||
+ private int viewDistance;
|
||||
+
|
||||
+ private final PooledHashSets<ServerPlayer> pooledHashSets = new PooledHashSets<>();
|
||||
+
|
||||
+ public PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> getPlayersInRange(final ChunkPos chunkPos) {
|
||||
+ return this.getPlayersInRange(chunkPos.x, chunkPos.z);
|
||||
+ }
|
||||
+
|
||||
+ public PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> getPlayersInRange(final int chunkX, final int chunkZ) {
|
||||
+ return this.playerMap.getOrDefault(ChunkPos.asLong(chunkX, chunkZ), EMPTY_SET);
|
||||
+ }
|
||||
+
|
||||
+ public void update(final List<ServerPlayer> currentPlayers, final int newViewDistance) {
|
||||
+ AsyncCatcher.catchOp("Distance map update");
|
||||
+ final ObjectLinkedOpenHashSet<ServerPlayer> gone = new ObjectLinkedOpenHashSet<>(this.players.keySet());
|
||||
+
|
||||
+ final int oldViewDistance = this.viewDistance;
|
||||
+ this.viewDistance = newViewDistance;
|
||||
+
|
||||
+ for (final ServerPlayer player : currentPlayers) {
|
||||
+ if (player.isSpectator() || !player.affectsSpawning) {
|
||||
+ continue; // will be left in 'gone' (or not added at all)
|
||||
+ }
|
||||
+
|
||||
+ gone.remove(player);
|
||||
+
|
||||
+ final SectionPos newPosition = player.getPlayerMapSection();
|
||||
+ final SectionPos 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 ServerPlayer player : gone) {
|
||||
+ final SectionPos oldPosition = this.players.remove(player);
|
||||
+ if (oldPosition != null) {
|
||||
+ this.removePlayer(player, oldPosition, oldViewDistance);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // expensive op, only for debug
|
||||
+ private void validatePlayer(final ServerPlayer player, final int viewDistance) {
|
||||
+ int entiesGot = 0;
|
||||
+ int expectedEntries = (2 * viewDistance + 1);
|
||||
+ expectedEntries *= expectedEntries;
|
||||
+
|
||||
+ final SectionPos currPosition = player.getPlayerMapSection();
|
||||
+
|
||||
+ final int centerX = currPosition.getX();
|
||||
+ final int centerZ = currPosition.getZ();
|
||||
+
|
||||
+ for (final Long2ObjectLinkedOpenHashMap.Entry<PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer>> entry : this.playerMap.long2ObjectEntrySet()) {
|
||||
+ final long key = entry.getLongKey();
|
||||
+ final PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> map = entry.getValue();
|
||||
+
|
||||
+ if (map.referenceCount == 0) {
|
||||
+ throw new IllegalStateException("Invalid map");
|
||||
+ }
|
||||
+
|
||||
+ if (map.set.contains(player)) {
|
||||
+ ++entiesGot;
|
||||
+
|
||||
+ final int chunkX = ChunkPos.getX(key);
|
||||
+ final int chunkZ = ChunkPos.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 ServerPlayer player, final int chunkX, final int chunkZ) {
|
||||
+ this.playerMap.compute(ChunkPos.asLong(chunkX, chunkZ), (final Long key, final PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players) -> {
|
||||
+ if (players == null) {
|
||||
+ return player.cachedSingleMobDistanceMap;
|
||||
+ } else {
|
||||
+ return PlayerMobDistanceMap.this.pooledHashSets.findMapWith(players, player);
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ private void removePlayerFrom(final ServerPlayer player, final int chunkX, final int chunkZ) {
|
||||
+ this.playerMap.compute(ChunkPos.asLong(chunkX, chunkZ), (final Long keyInMap, final PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players) -> {
|
||||
+ return PlayerMobDistanceMap.this.pooledHashSets.findMapWithout(players, player); // rets null instead of an empty map
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ private void updatePlayer(final ServerPlayer player, final SectionPos oldPosition, final SectionPos 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 ServerPlayer player, final SectionPos 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 ServerPlayer player, final SectionPos 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/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
+import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager;
|
||||
import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
private final Long2ByteMap chunkTypeCache;
|
||||
private final Queue<Runnable> unloadQueue;
|
||||
int viewDistance;
|
||||
+ 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 ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.overworldDataStorage = persistentStateManagerFactory;
|
||||
this.poiManager = new PoiManager(new File(this.storageFolder, "poi"), dataFixer, dsync, world);
|
||||
this.setViewDistance(viewDistance);
|
||||
+ this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper
|
||||
// Paper start - no-tick view distance
|
||||
this.setNoTickViewDistance(this.level.paperConfig.noTickViewDistance);
|
||||
this.playerViewDistanceTickMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
||||
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
// Paper end - no-tick view distance
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public void updatePlayerMobTypeMap(Entity entity) {
|
||||
+ if (!this.level.paperConfig.perPlayerMobSpawns) {
|
||||
+ return;
|
||||
+ }
|
||||
+ int chunkX = (int)Math.floor(entity.getX()) >> 4;
|
||||
+ int chunkZ = (int)Math.floor(entity.getZ()) >> 4;
|
||||
+ int index = entity.getType().getCategory().ordinal();
|
||||
+
|
||||
+ for (ServerPlayer player : this.playerMobDistanceMap.getPlayersInRange(chunkX, chunkZ)) {
|
||||
+ ++player.mobCounts[index];
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public int getMobCountNear(ServerPlayer entityPlayer, MobCategory enumCreatureType) {
|
||||
+ return entityPlayer.mobCounts[enumCreatureType.ordinal()];
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
|
||||
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
|
||||
double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource {
|
||||
this.level.getProfiler().push("naturalSpawnCount");
|
||||
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
|
||||
int l = this.distanceManager.getNaturalSpawnChunkCount();
|
||||
- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk);
|
||||
+ // Paper start - per player mob spawning
|
||||
+ NaturalSpawner.SpawnState spawnercreature_d; // moved down
|
||||
+ if (this.chunkMap.playerMobDistanceMap != null) {
|
||||
+ // update distance map
|
||||
+ this.level.timings.playerMobDistanceMapUpdate.startTiming();
|
||||
+ this.chunkMap.playerMobDistanceMap.update(this.level.players, this.chunkMap.viewDistance);
|
||||
+ this.level.timings.playerMobDistanceMapUpdate.stopTiming();
|
||||
+ // re-set mob counts
|
||||
+ for (ServerPlayer player : this.level.players) {
|
||||
+ Arrays.fill(player.mobCounts, 0);
|
||||
+ }
|
||||
+ spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, true);
|
||||
+ } else {
|
||||
+ spawnercreature_d = NaturalSpawner.createState(l, this.level.getAllEntities(), this::getFullChunk, false);
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
|
||||
|
||||
this.lastSpawnState = spawnercreature_d;
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.damagesource.EntityDamageSource;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
-import net.minecraft.world.entity.Entity;
|
||||
-import net.minecraft.world.entity.EntitySelector;
|
||||
-import net.minecraft.world.entity.HumanoidArm;
|
||||
-import net.minecraft.world.entity.LivingEntity;
|
||||
-import net.minecraft.world.entity.Mob;
|
||||
-import net.minecraft.world.entity.NeutralMob;
|
||||
+import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
|
||||
public boolean queueHealthUpdatePacket = false;
|
||||
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
|
||||
// Paper end
|
||||
+ // Paper start - mob spawning rework
|
||||
+ public static final int ENUMCREATURETYPE_TOTAL_ENUMS = MobCategory.values().length;
|
||||
+ public final int[] mobCounts = new int[ENUMCREATURETYPE_TOTAL_ENUMS]; // Paper
|
||||
+ public final com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleMobDistanceMap;
|
||||
+ // Paper end
|
||||
|
||||
// CraftBukkit start
|
||||
public String displayName;
|
||||
@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
|
||||
this.adventure$displayName = net.kyori.adventure.text.Component.text(this.getScoreboardName()); // Paper
|
||||
this.bukkitPickUpLoot = 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.
|
||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.tags.Tag;
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
|
||||
private NaturalSpawner() {}
|
||||
|
||||
+ // Paper start - add countMobs parameter
|
||||
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource) {
|
||||
+ return createState(spawningChunkCount, entities, chunkSource, false);
|
||||
+ }
|
||||
+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, boolean countMobs) {
|
||||
+ // Paper end - add countMobs parameter
|
||||
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
|
||||
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
|
||||
Iterator iterator = entities.iterator();
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
}
|
||||
|
||||
object2intopenhashmap.addTo(enumcreaturetype, 1);
|
||||
+ // Paper start
|
||||
+ if (countMobs) {
|
||||
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
|
||||
+ }
|
||||
+ // Paper end
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
continue;
|
||||
}
|
||||
|
||||
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.a(enumcreaturetype, limit)) {
|
||||
+ // Paper start - only allow spawns upto the limit per chunk and update count afterwards
|
||||
+ int currEntityCount = info.getMobCategoryCounts().getInt(enumcreaturetype);
|
||||
+ int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER;
|
||||
+ int difference = k1 - currEntityCount;
|
||||
+
|
||||
+ if (world.paperConfig.perPlayerMobSpawns) {
|
||||
+ int minDiff = Integer.MAX_VALUE;
|
||||
+ for (ServerPlayer entityplayer : world.getChunkSource().chunkMap.playerMobDistanceMap.getPlayersInRange(chunk.getPos())) {
|
||||
+ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear(entityplayer, enumcreaturetype), minDiff);
|
||||
+ }
|
||||
+ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
+ // Paper start - per player mob spawning
|
||||
+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (spawnAnimals || !enumcreaturetype.isPersistent()) && info.a(enumcreaturetype, limit) && difference > 0) {
|
||||
// CraftBukkit end
|
||||
Objects.requireNonNull(info);
|
||||
NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
|
||||
|
||||
Objects.requireNonNull(info);
|
||||
- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
|
||||
+ int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
|
||||
+ difference, world.paperConfig.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
|
||||
+ info.getMobCategoryCounts().mergeInt(enumcreaturetype, spawnCount, Integer::sum);
|
||||
+ // Paper end - per player mob spawning
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
world.getProfiler().pop();
|
||||
}
|
||||
|
||||
+ // Paper start - add parameters and int ret type
|
||||
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
||||
+ spawnCategoryForChunk(group, world, chunk, checker, runner);
|
||||
+ }
|
||||
+ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
|
||||
+ // Paper end - add parameters and int ret type
|
||||
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
|
||||
|
||||
if (blockposition.getY() >= world.getMinBuildHeight() + 1) {
|
||||
- NaturalSpawner.spawnCategoryForPosition(group, world, (ChunkAccess) chunk, blockposition, checker, runner);
|
||||
+ return NaturalSpawner.spawnCategoryForPosition(group, world, (ChunkAccess) chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper
|
||||
}
|
||||
+ return 0; // Paper
|
||||
}
|
||||
|
||||
@VisibleForDebug
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
});
|
||||
}
|
||||
|
||||
+ // Paper start - add maxSpawns parameter and return spawned mobs
|
||||
public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
||||
+ spawnCategoryForPosition(group, world,chunk, pos, checker, runner);
|
||||
+ }
|
||||
+ public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
|
||||
+ // Paper end - add maxSpawns parameter and return spawned mobs
|
||||
StructureFeatureManager structuremanager = world.structureFeatureManager();
|
||||
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
|
||||
int i = pos.getY();
|
||||
BlockState iblockdata = world.getTypeIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
|
||||
+ int j = 0; // Paper - moved up
|
||||
|
||||
if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
|
||||
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
|
||||
- int j = 0;
|
||||
+ // Paper - moved up
|
||||
int k = 0;
|
||||
|
||||
while (k < 3) {
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
|
||||
|
||||
if (entityinsentient == null) {
|
||||
- return;
|
||||
+ return j; // Paper
|
||||
}
|
||||
|
||||
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
++j;
|
||||
++k1;
|
||||
runner.run(entityinsentient, chunk);
|
||||
+ // Paper start
|
||||
+ if (trackEntity != null) {
|
||||
+ trackEntity.accept(entityinsentient);
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
// CraftBukkit end
|
||||
- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
|
||||
- return;
|
||||
+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper
|
||||
+ return j; // Paper
|
||||
}
|
||||
|
||||
if (entityinsentient.isMaxGroupSizeReached(k1)) {
|
||||
@@ -0,0 +0,0 @@ public final class NaturalSpawner {
|
||||
}
|
||||
|
||||
}
|
||||
+ return j; // Paper
|
||||
}
|
||||
|
||||
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
Loading…
Reference in New Issue
Block a user