Patches
This commit is contained in:
parent
e9eec78fc7
commit
f6ea3736a7
17 changed files with 267 additions and 275 deletions
167
patches/server/0973-incremental-chunk-and-player-saving.patch
Normal file
167
patches/server/0973-incremental-chunk-and-player-saving.patch
Normal file
|
@ -0,0 +1,167 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Sun, 9 Jun 2019 03:53:22 +0100
|
||||
Subject: [PATCH] incremental chunk and player saving
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index d7699ac1c627f265c403b9e00121f5f55e982341..0f3601f2f1a7ac53425129df6498ed0df302dec8 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -916,7 +916,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
try {
|
||||
this.isSaving = true;
|
||||
- this.getPlayerList().saveAll();
|
||||
+ this.getPlayerList().saveAll(); // Paper - Incremental chunk and player saving; diff on change
|
||||
flag3 = this.saveAllChunks(suppressLogs, flush, force);
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
@@ -1438,16 +1438,28 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
--this.ticksUntilAutosave;
|
||||
- // CraftBukkit start
|
||||
- if (this.autosavePeriod > 0 && this.ticksUntilAutosave <= 0) {
|
||||
- this.ticksUntilAutosave = this.autosavePeriod;
|
||||
- // CraftBukkit end
|
||||
- MinecraftServer.LOGGER.debug("Autosave started");
|
||||
- this.profiler.push("save");
|
||||
- this.saveEverything(true, false, false);
|
||||
- this.profiler.pop();
|
||||
- MinecraftServer.LOGGER.debug("Autosave finished");
|
||||
+ // Paper start - Incremental chunk and player saving
|
||||
+ int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate;
|
||||
+ if (playerSaveInterval < 0) {
|
||||
+ playerSaveInterval = autosavePeriod;
|
||||
}
|
||||
+ this.profiler.push("save");
|
||||
+ final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0;
|
||||
+ try {
|
||||
+ this.isSaving = true;
|
||||
+ if (playerSaveInterval > 0) {
|
||||
+ this.playerList.saveAll(playerSaveInterval);
|
||||
+ }
|
||||
+ for (ServerLevel level : this.getAllLevels()) {
|
||||
+ if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
|
||||
+ level.saveIncrementally(fullSave);
|
||||
+ }
|
||||
+ }
|
||||
+ } finally {
|
||||
+ this.isSaving = false;
|
||||
+ }
|
||||
+ this.profiler.pop();
|
||||
+ // Paper end - Incremental chunk and player saving
|
||||
io.papermc.paper.util.CachedLists.reset(); // Paper
|
||||
// Paper start - move executeAll() into full server tick timing
|
||||
try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index f74efe41cd0da2f9749fc96fb9e0f7cf237ad1c6..d1728e13a7b649f308bde90ab633c79d86c10822 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -430,6 +430,15 @@ public class ServerChunkCache extends ChunkSource {
|
||||
} // Paper - Timings
|
||||
}
|
||||
|
||||
+ // Paper start - Incremental chunk and player saving; duplicate save, but call incremental
|
||||
+ public void saveIncrementally() {
|
||||
+ this.runDistanceManagerUpdates();
|
||||
+ try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
|
||||
+ this.chunkMap.saveIncrementally();
|
||||
+ } // Paper - Timings
|
||||
+ }
|
||||
+ // Paper end - Incremental chunk and player saving
|
||||
+
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// CraftBukkit start
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index e517037047013d0d55771fc08af9e03c4d334823..a0cf3652b4101c559532e9b04ef551c448aab1bc 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1296,6 +1296,37 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos);
|
||||
}
|
||||
|
||||
+ // Paper start - Incremental chunk and player saving
|
||||
+ public void saveIncrementally(boolean doFull) {
|
||||
+ ServerChunkCache chunkproviderserver = this.getChunkSource();
|
||||
+
|
||||
+ if (doFull) {
|
||||
+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
|
||||
+ }
|
||||
+
|
||||
+ try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
|
||||
+ if (doFull) {
|
||||
+ this.saveLevelData();
|
||||
+ }
|
||||
+
|
||||
+ this.timings.worldSaveChunks.startTiming(); // Paper
|
||||
+ if (!this.noSave()) chunkproviderserver.saveIncrementally();
|
||||
+ this.timings.worldSaveChunks.stopTiming(); // Paper
|
||||
+
|
||||
+ // Copied from save()
|
||||
+ // CraftBukkit start - moved from MinecraftServer.saveChunks
|
||||
+ if (doFull) { // Paper
|
||||
+ ServerLevel worldserver1 = this;
|
||||
+
|
||||
+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
|
||||
+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
|
||||
+ this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Incremental chunk and player saving
|
||||
+
|
||||
public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) {
|
||||
// Paper start - rewrite chunk system - add close param
|
||||
this.save(progressListener, flush, savingDisabled, false);
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index 65e6623c2d1a6cf48729f78948fc48e82057114c..fc8e9cd699bdefb6ddadc6aa30e6459417c73194 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -198,6 +198,7 @@ import org.bukkit.inventory.MainHand;
|
||||
public class ServerPlayer extends Player {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
+ public long lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving
|
||||
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
|
||||
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
|
||||
private static final int FLY_STAT_RECORDING_SPEED = 25;
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index 7bf4884940c6414000a8ac661c1ff64427f53789..11f13eb06516aefca926e150b9b66bafdebf4226 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -575,6 +575,7 @@ public abstract class PlayerList {
|
||||
|
||||
protected void save(ServerPlayer player) {
|
||||
if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
|
||||
+ player.lastSave = MinecraftServer.currentTick; // Paper - Incremental chunk and player saving
|
||||
this.playerIo.save(player);
|
||||
ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit
|
||||
|
||||
@@ -1227,10 +1228,22 @@ public abstract class PlayerList {
|
||||
}
|
||||
|
||||
public void saveAll() {
|
||||
+ // Paper start - Incremental chunk and player saving
|
||||
+ this.saveAll(-1);
|
||||
+ }
|
||||
+
|
||||
+ public void saveAll(int interval) {
|
||||
io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
|
||||
MinecraftTimings.savePlayers.startTiming(); // Paper
|
||||
+ int numSaved = 0;
|
||||
+ long now = MinecraftServer.currentTick;
|
||||
for (int i = 0; i < this.players.size(); ++i) {
|
||||
- this.save(this.players.get(i));
|
||||
+ ServerPlayer entityplayer = this.players.get(i);
|
||||
+ if (interval == -1 || now - entityplayer.lastSave >= interval) {
|
||||
+ this.save(entityplayer);
|
||||
+ if (interval != -1 && ++numSaved >= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; }
|
||||
+ }
|
||||
+ // Paper end - Incremental chunk and player saving
|
||||
}
|
||||
MinecraftTimings.savePlayers.stopTiming(); // Paper
|
||||
return null; }); // Paper - ensure main
|
|
@ -0,0 +1,99 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 3 Mar 2016 02:07:55 -0600
|
||||
Subject: [PATCH] Optimize isInWorldBounds and getBlockState for inlining
|
||||
|
||||
Hot methods, so reduce # of instructions for the method.
|
||||
|
||||
Move is valid location test to the BlockPosition class so that it can access local variables.
|
||||
|
||||
Replace all calls to the new place to the unnecessary forward.
|
||||
|
||||
Optimize getType and getBlockData to manually inline and optimize the calls
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java
|
||||
index 02367ef1371dde94ff6c4cd40bd32e800d6ccaaf..7b0fc7135bc107103dcaed6dc0707b1829928fae 100644
|
||||
--- a/src/main/java/net/minecraft/core/Vec3i.java
|
||||
+++ b/src/main/java/net/minecraft/core/Vec3i.java
|
||||
@@ -28,6 +28,12 @@ public class Vec3i implements Comparable<Vec3i> {
|
||||
);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public final boolean isInsideBuildHeightAndWorldBoundsHorizontal(net.minecraft.world.level.LevelHeightAccessor levelHeightAccessor) {
|
||||
+ return getX() >= -30000000 && getZ() >= -30000000 && getX() < 30000000 && getZ() < 30000000 && !levelHeightAccessor.isOutsideBuildHeight(getY());
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public Vec3i(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 143ab00a079c0bb2af8717567f7069e82cddd9a6..5f547c45b8fb943059b982697b28de47d44cff54 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -331,7 +331,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
// Paper end
|
||||
|
||||
public boolean isInWorldBounds(BlockPos pos) {
|
||||
- return !this.isOutsideBuildHeight(pos) && Level.isInWorldBoundsHorizontal(pos);
|
||||
+ return pos.isInsideBuildHeightAndWorldBoundsHorizontal(this); // Paper - use better/optimized check
|
||||
}
|
||||
|
||||
public static boolean isInSpawnableBounds(BlockPos pos) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
index 1f8c72b6c7d8683d67880fa175843c73b3d39b78..383dc47c81b3f34e8166bce180a51a2ccbfaf6ca 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
@@ -175,6 +175,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
|
||||
return GameEventListenerRegistry.NOOP;
|
||||
}
|
||||
|
||||
+ public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
|
||||
@Nullable
|
||||
public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved);
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
|
||||
index aa5dee839d4c0dbc3c2abee9b501ec250c575cb3..6359d2d80f4c1360f8450bf6555ccfea3b69daa5 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
|
||||
@@ -99,6 +99,12 @@ public class ImposterProtoChunk extends ProtoChunk {
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
return this.wrapped.getBlockState(pos);
|
||||
}
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public final BlockState getBlockState(final int x, final int y, final int z) {
|
||||
+ return this.wrapped.getBlockStateFinal(x, y, z);
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
|
||||
index 576ae0cb138b265c8a3995de7b5ebc827d50949d..bfdee41d3095a869408abc89a6085556ff188d8d 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
|
||||
@@ -97,14 +97,18 @@ public class ProtoChunk extends ChunkAccess {
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
- int i = pos.getY();
|
||||
- if (this.isOutsideBuildHeight(i)) {
|
||||
+ // Paper start
|
||||
+ return getBlockState(pos.getX(), pos.getY(), pos.getZ());
|
||||
+ }
|
||||
+ public BlockState getBlockState(final int x, final int y, final int z) {
|
||||
+ if (this.isOutsideBuildHeight(y)) {
|
||||
return Blocks.VOID_AIR.defaultBlockState();
|
||||
} else {
|
||||
- LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndex(i));
|
||||
- return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(pos.getX() & 15, i & 15, pos.getZ() & 15);
|
||||
+ LevelChunkSection levelChunkSection = this.getSections()[this.getSectionIndex(y)];
|
||||
+ return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(x & 15, y & 15, z & 15);
|
||||
}
|
||||
}
|
||||
+ // Paper end
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
|
@ -0,0 +1,128 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 29 Apr 2016 20:02:00 -0400
|
||||
Subject: [PATCH] Improve Maps (in item frames) performance and bug fixes
|
||||
|
||||
Maps used a modified version of rendering to support plugin controlled
|
||||
imaging on maps. The Craft Map Renderer is much slower than Vanilla,
|
||||
causing maps in item frames to cause a noticeable hit on server performance.
|
||||
|
||||
This updates the map system to not use the Craft system if we detect that no
|
||||
custom renderers are in use, defaulting to the much simpler Vanilla system.
|
||||
|
||||
Additionally, numerous issues to player position tracking on maps has been fixed.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index a0cf3652b4101c559532e9b04ef551c448aab1bc..63777efec582b8db9804914d554e8aedafcf08c8 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2582,6 +2582,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
{
|
||||
if ( iter.next().player == entity )
|
||||
{
|
||||
+ map.decorations.remove(entity.getName().getString()); // Paper
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
|
||||
index 70d225dbf5649e6a3fa658291de734f54a6f73dc..72b3e64cf6d184e90a6e2c0d822b83312312d67d 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
|
||||
@@ -789,6 +789,14 @@ public abstract class Player extends LivingEntity {
|
||||
return null;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Paper start - remove player from map on drop
|
||||
+ if (itemstack.getItem() == Items.FILLED_MAP) {
|
||||
+ net.minecraft.world.level.saveddata.maps.MapItemSavedData worldmap = net.minecraft.world.item.MapItem.getSavedData(itemstack, this.level());
|
||||
+ if (worldmap != null) {
|
||||
+ worldmap.tickCarriedBy(this, itemstack);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
return entityitem;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
index ad24a79e190d07c75d8e29e816fc398894771c2c..cf8ae635fce7ea66d4e1ab1dc05575f035fa95ef 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
|
||||
@@ -78,6 +78,7 @@ public class MapItemSavedData extends SavedData {
|
||||
public final Map<String, MapDecoration> decorations = Maps.newLinkedHashMap();
|
||||
private final Map<String, MapFrame> frameMarkers = Maps.newHashMap();
|
||||
private int trackedDecorationCount;
|
||||
+ private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper
|
||||
|
||||
// CraftBukkit start
|
||||
public final CraftMapView mapView;
|
||||
@@ -104,6 +105,7 @@ public class MapItemSavedData extends SavedData {
|
||||
// CraftBukkit start
|
||||
this.mapView = new CraftMapView(this);
|
||||
this.server = (CraftServer) org.bukkit.Bukkit.getServer();
|
||||
+ this.vanillaRender.buffer = colors; // Paper
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
@@ -178,6 +180,7 @@ public class MapItemSavedData extends SavedData {
|
||||
if (abyte.length == 16384) {
|
||||
worldmap.colors = abyte;
|
||||
}
|
||||
+ worldmap.vanillaRender.buffer = abyte; // Paper
|
||||
|
||||
RegistryOps<Tag> registryops = registryLookup.createSerializationContext(NbtOps.INSTANCE);
|
||||
List<MapBanner> list = (List) MapBanner.LIST_CODEC.parse(registryops, nbt.get("banners")).resultOrPartial((s) -> {
|
||||
@@ -583,6 +586,21 @@ public class MapItemSavedData extends SavedData {
|
||||
|
||||
public class HoldingPlayer {
|
||||
|
||||
+ // Paper start
|
||||
+ private void addSeenPlayers(java.util.Collection<MapDecoration> icons) {
|
||||
+ org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.player.getBukkitEntity();
|
||||
+ MapItemSavedData.this.decorations.forEach((name, mapIcon) -> {
|
||||
+ // If this cursor is for a player check visibility with vanish system
|
||||
+ org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot
|
||||
+ if (other == null || player.canSee(other)) {
|
||||
+ icons.add(mapIcon);
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ private boolean shouldUseVanillaMap() {
|
||||
+ return mapView.getRenderers().size() == 1 && mapView.getRenderers().get(0).getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class;
|
||||
+ }
|
||||
+ // Paper end
|
||||
public final Player player;
|
||||
private boolean dirtyData = true;
|
||||
private int minDirtyX;
|
||||
@@ -616,7 +634,9 @@ public class MapItemSavedData extends SavedData {
|
||||
@Nullable
|
||||
Packet<?> nextUpdatePacket(MapId mapId) {
|
||||
MapItemSavedData.MapPatch worldmap_b;
|
||||
- org.bukkit.craftbukkit.map.RenderData render = MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()); // CraftBukkit
|
||||
+ if (!this.dirtyData && this.tick % 5 != 0) { this.tick++; return null; } // Paper - this won't end up sending, so don't render it!
|
||||
+ boolean vanillaMaps = shouldUseVanillaMap(); // Paper
|
||||
+ org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()) : MapItemSavedData.this.vanillaRender; // CraftBukkit // Paper
|
||||
|
||||
if (this.dirtyData) {
|
||||
this.dirtyData = false;
|
||||
@@ -632,6 +652,8 @@ public class MapItemSavedData extends SavedData {
|
||||
// CraftBukkit start
|
||||
java.util.Collection<MapDecoration> icons = new java.util.ArrayList<MapDecoration>();
|
||||
|
||||
+ if (vanillaMaps) addSeenPlayers(icons); // Paper
|
||||
+
|
||||
for (org.bukkit.map.MapCursor cursor : render.cursors) {
|
||||
if (cursor.isVisible()) {
|
||||
icons.add(new MapDecoration(CraftMapCursor.CraftType.bukkitToMinecraftHolder(cursor.getType()), cursor.getX(), cursor.getY(), cursor.getDirection(), Optional.ofNullable(PaperAdventure.asVanilla(cursor.caption()))));
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java
|
||||
index 256a131781721c86dd6cdbc329335964570cbe8c..5768cd512ec166f1e8d1f4a28792015347297c3f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java
|
||||
@@ -5,7 +5,7 @@ import org.bukkit.map.MapCursor;
|
||||
|
||||
public class RenderData {
|
||||
|
||||
- public final byte[] buffer;
|
||||
+ public byte[] buffer; // Paper
|
||||
public final ArrayList<MapCursor> cursors;
|
||||
|
||||
public RenderData() {
|
|
@ -0,0 +1,158 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Paul Sauve <paul@technove.co>
|
||||
Date: Sat, 31 Oct 2020 18:43:02 -0500
|
||||
Subject: [PATCH] Strip raytracing for EntityLiving#hasLineOfSight
|
||||
|
||||
The BlockGetter#clip method is very wasteful in both allocations,
|
||||
and in logic. While EntityLiving#hasLineOfSight provides static
|
||||
parameters for collisions with blocks and fluids, the method still does
|
||||
a lot of dynamic checks for both of these, which result in extra work.
|
||||
As well, since the fluid collision option is set to NONE, the entire
|
||||
fluid collision system is completely unneeded, yet used anyways.
|
||||
|
||||
Copyright (C) 2020 Technove LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index 0416af1342dad6a6789aa743aadf61b32b70f338..5854e58a014b5581fa065d1db256ffc0aa947ce2 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3730,7 +3730,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ());
|
||||
|
||||
// Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists
|
||||
- return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - Perf: Use distance squared
|
||||
+ return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clipDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == HitResult.Type.MISS; // Paper - Perf: Use distance squared & strip raytracing
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
index bb8e962e63c7a2d931f9bd7f7c002aa35cfa5fd3..0fa131a6c98adb498fc8d534e0e39647e80c6923 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java
|
||||
@@ -68,6 +68,18 @@ public interface BlockGetter extends LevelHeightAccessor {
|
||||
});
|
||||
}
|
||||
|
||||
+ // Paper start - Broken down variant of the method below, used by Level#clipDirect
|
||||
+ @Nullable
|
||||
+ default BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, BlockPos pos, BlockState state, net.minecraft.world.phys.shapes.CollisionContext collisionContext) {
|
||||
+ if (state.isAir()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(state, this, pos, collisionContext);
|
||||
+ final BlockHitResult hitResult = this.clipWithInteractionOverride(start, end, pos, voxelshape, state);
|
||||
+ return hitResult == null ? null : hitResult.getType();
|
||||
+ }
|
||||
+ // Paper end
|
||||
// CraftBukkit start - moved block handling into separate method for use by Block#rayTrace
|
||||
default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) {
|
||||
// Paper start - Add predicate for blocks when raytracing
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 5f547c45b8fb943059b982697b28de47d44cff54..acfb87cd9ea3ca52f1ded99f0f7065fe5abd9af4 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -323,10 +323,87 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
return null;
|
||||
}
|
||||
|
||||
- // Paper start
|
||||
+ // Paper start - Broken down method of raytracing for EntityLiving#hasLineOfSight, replaces BlockGetter#clip(CollisionContext)
|
||||
public net.minecraft.world.phys.BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, net.minecraft.world.phys.shapes.CollisionContext context) {
|
||||
- // To be patched over
|
||||
- return this.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, context)).getType();
|
||||
+ // most of this code comes from BlockGetter#clip(CollisionContext, BiFunction, Function), but removes the needless functions
|
||||
+ if (start.equals(end)) {
|
||||
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
||||
+ }
|
||||
+
|
||||
+ final double endX = Mth.lerp(-1.0E-7D, end.x, start.x);
|
||||
+ final double endY = Mth.lerp(-1.0E-7D, end.y, start.y);
|
||||
+ final double endZ = Mth.lerp(-1.0E-7D, end.z, start.z);
|
||||
+
|
||||
+ final double startX = Mth.lerp(-1.0E-7D, start.x, end.x);
|
||||
+ final double startY = Mth.lerp(-1.0E-7D, start.y, end.y);
|
||||
+ final double startZ = Mth.lerp(-1.0E-7D, start.z, end.z);
|
||||
+
|
||||
+ int currentX = Mth.floor(startX);
|
||||
+ int currentY = Mth.floor(startY);
|
||||
+ int currentZ = Mth.floor(startZ);
|
||||
+
|
||||
+ final BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ);
|
||||
+
|
||||
+ LevelChunk chunk = this.getChunkIfLoaded(currentBlock);
|
||||
+ if (chunk == null) {
|
||||
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
||||
+ }
|
||||
+
|
||||
+ final net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.clipDirect(start, end, currentBlock, chunk.getBlockState(currentBlock), context);
|
||||
+ if (initialCheck != null) {
|
||||
+ return initialCheck;
|
||||
+ }
|
||||
+
|
||||
+ final double diffX = endX - startX;
|
||||
+ final double diffY = endY - startY;
|
||||
+ final double diffZ = endZ - startZ;
|
||||
+
|
||||
+ final int xDirection = Mth.sign(diffX);
|
||||
+ final int yDirection = Mth.sign(diffY);
|
||||
+ final int zDirection = Mth.sign(diffZ);
|
||||
+
|
||||
+ final double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX;
|
||||
+ final double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY;
|
||||
+ final double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ;
|
||||
+
|
||||
+ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX));
|
||||
+ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY));
|
||||
+ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ));
|
||||
+
|
||||
+ net.minecraft.world.phys.BlockHitResult.Type result;
|
||||
+
|
||||
+ do {
|
||||
+ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) {
|
||||
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
||||
+ }
|
||||
+
|
||||
+ if (normalizedXDirection < normalizedYDirection) {
|
||||
+ if (normalizedXDirection < normalizedZDirection) {
|
||||
+ currentX += xDirection;
|
||||
+ normalizedXDirection += normalizedX;
|
||||
+ } else {
|
||||
+ currentZ += zDirection;
|
||||
+ normalizedZDirection += normalizedZ;
|
||||
+ }
|
||||
+ } else if (normalizedYDirection < normalizedZDirection) {
|
||||
+ currentY += yDirection;
|
||||
+ normalizedYDirection += normalizedY;
|
||||
+ } else {
|
||||
+ currentZ += zDirection;
|
||||
+ normalizedZDirection += normalizedZ;
|
||||
+ }
|
||||
+
|
||||
+ currentBlock.set(currentX, currentY, currentZ);
|
||||
+ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) {
|
||||
+ chunk = this.getChunkIfLoaded(currentBlock);
|
||||
+ if (chunk == null) {
|
||||
+ return net.minecraft.world.phys.BlockHitResult.Type.MISS;
|
||||
+ }
|
||||
+ }
|
||||
+ result = this.clipDirect(start, end, currentBlock, chunk.getBlockState(currentBlock), context);
|
||||
+ } while (result == null);
|
||||
+
|
||||
+ return result;
|
||||
}
|
||||
// Paper end
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 6 May 2020 04:53:35 -0400
|
||||
Subject: [PATCH] Optimize Network Manager and add advanced packet support
|
||||
|
||||
Adds ability for 1 packet to bundle other packets to follow it
|
||||
Adds ability for a packet to delay sending more packets until a state is ready.
|
||||
|
||||
Removes synchronization from sending packets
|
||||
Removes processing packet queue off of main thread
|
||||
- for the few cases where it is allowed, order is not necessary nor
|
||||
should it even be happening concurrently in first place (handshaking/login/status)
|
||||
|
||||
Ensures packets sent asynchronously are dispatched on main thread
|
||||
|
||||
This helps ensure safety for ProtocolLib as packet listeners
|
||||
are commonly accessing world state. This will allow you to schedule
|
||||
a packet to be sent async, but itll be dispatched sync for packet
|
||||
listeners to process.
|
||||
|
||||
This should solve some deadlock risks
|
||||
|
||||
Also adds Netty Channel Flush Consolidation to reduce the amount of flushing
|
||||
|
||||
Also avoids spamming closed channel exception by rechecking closed state in dispatch
|
||||
and then catch exceptions and close if they fire.
|
||||
|
||||
Part of this commit was authored by: Spottedleaf, sandtechnology
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index f57679d88cd015caa8996d44b486da694df29521..f94458d4270042062b9ae18931cc8179ef1fb420 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -93,7 +93,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
|
||||
private final PacketFlow receiving;
|
||||
private volatile boolean sendLoginDisconnect = true;
|
||||
- private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
|
||||
+ private final Queue<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue(); // Paper
|
||||
public Channel channel;
|
||||
public SocketAddress address;
|
||||
// Spigot Start
|
||||
@@ -125,6 +125,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public java.net.InetSocketAddress virtualHost;
|
||||
private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit network manager flushing
|
||||
// Paper end
|
||||
+ // Paper start - Optimize network
|
||||
+ public boolean isPending = true;
|
||||
+ public boolean queueImmunity;
|
||||
+ // Paper end - Optimize network
|
||||
|
||||
// Paper start - add utility methods
|
||||
public final net.minecraft.server.level.ServerPlayer getPlayer() {
|
||||
@@ -415,15 +419,39 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
|
||||
- if (this.isConnected()) {
|
||||
- this.flushQueue();
|
||||
+ // Paper start - Optimize network: Handle oversized packets better
|
||||
+ final boolean connected = this.isConnected();
|
||||
+ if (!connected && !this.preparing) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ packet.onPacketDispatch(this.getPlayer());
|
||||
+ if (connected && (InnerUtil.canSendImmediate(this, packet)
|
||||
+ || (io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.pendingActions.isEmpty()
|
||||
+ && (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())))) {
|
||||
this.sendPacket(packet, callbacks, flush);
|
||||
} else {
|
||||
- this.pendingActions.add((networkmanager) -> {
|
||||
- networkmanager.sendPacket(packet, callbacks, flush);
|
||||
- });
|
||||
- }
|
||||
+ // Write the packets to the queue, then flush - antixray hooks there already
|
||||
+ final java.util.List<Packet<?>> extraPackets = InnerUtil.buildExtraPackets(packet);
|
||||
+ final boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
|
||||
+ if (!hasExtraPackets) {
|
||||
+ this.pendingActions.add(new PacketSendAction(packet, callbacks, flush));
|
||||
+ } else {
|
||||
+ final java.util.List<PacketSendAction> actions = new java.util.ArrayList<>(1 + extraPackets.size());
|
||||
+ actions.add(new PacketSendAction(packet, null, false)); // Delay the future listener until the end of the extra packets
|
||||
+
|
||||
+ for (int i = 0, len = extraPackets.size(); i < len;) {
|
||||
+ final Packet<?> extraPacket = extraPackets.get(i);
|
||||
+ final boolean end = ++i == len;
|
||||
+ actions.add(new PacketSendAction(extraPacket, end ? callbacks : null, end)); // Append listener to the end
|
||||
+ }
|
||||
+
|
||||
+ this.pendingActions.addAll(actions);
|
||||
+ }
|
||||
|
||||
+ this.flushQueue();
|
||||
+ // Paper end - Optimize network
|
||||
+ }
|
||||
}
|
||||
|
||||
public void runOnceConnected(Consumer<Connection> task) {
|
||||
@@ -431,7 +459,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
this.flushQueue();
|
||||
task.accept(this);
|
||||
} else {
|
||||
- this.pendingActions.add(task);
|
||||
+ this.pendingActions.add(new WrappedConsumer(task)); // Paper - Optimize network
|
||||
}
|
||||
|
||||
}
|
||||
@@ -449,6 +477,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
|
||||
private void doSendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
|
||||
+ // Paper start - Optimize network
|
||||
+ final net.minecraft.server.level.ServerPlayer player = this.getPlayer();
|
||||
+ if (!this.isConnected()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ return;
|
||||
+ }
|
||||
+ try {
|
||||
+ // Paper end - Optimize network
|
||||
ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
|
||||
|
||||
if (callbacks != null) {
|
||||
@@ -468,14 +504,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
});
|
||||
}
|
||||
|
||||
+ // Paper start - Optimize network
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ channelfuture.addListener((ChannelFutureListener) channelFuture -> packet.onPacketDispatchFinish(player, channelFuture));
|
||||
+ }
|
||||
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
+ } catch (final Exception e) {
|
||||
+ LOGGER.error("NetworkException: {}", player, e);
|
||||
+ this.disconnect(Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ // Paper end - Optimize network
|
||||
}
|
||||
|
||||
public void flushChannel() {
|
||||
if (this.isConnected()) {
|
||||
this.flush();
|
||||
} else {
|
||||
- this.pendingActions.add(Connection::flush);
|
||||
+ this.pendingActions.add(new WrappedConsumer(Connection::flush)); // Paper - Optimize network
|
||||
}
|
||||
|
||||
}
|
||||
@@ -491,20 +537,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
}
|
||||
|
||||
- private void flushQueue() {
|
||||
- if (this.channel != null && this.channel.isOpen()) {
|
||||
- Queue queue = this.pendingActions;
|
||||
-
|
||||
+ // Paper start - Optimize network: Rewrite this to be safer if ran off main thread
|
||||
+ private boolean flushQueue() {
|
||||
+ if (!this.isConnected()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (io.papermc.paper.util.MCUtil.isMainThread()) {
|
||||
+ return this.processQueue();
|
||||
+ } else if (this.isPending) {
|
||||
+ // Should only happen during login/status stages
|
||||
synchronized (this.pendingActions) {
|
||||
- Consumer consumer;
|
||||
+ return this.processQueue();
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private boolean processQueue() {
|
||||
+ if (this.pendingActions.isEmpty()) {
|
||||
+ return true;
|
||||
+ }
|
||||
|
||||
- while ((consumer = (Consumer) this.pendingActions.poll()) != null) {
|
||||
- consumer.accept(this);
|
||||
+ // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
|
||||
+ // But if we are not on main due to login/status, the parent is synchronized on packetQueue
|
||||
+ final java.util.Iterator<WrappedConsumer> iterator = this.pendingActions.iterator();
|
||||
+ while (iterator.hasNext()) {
|
||||
+ final WrappedConsumer queued = iterator.next(); // poll -> peek
|
||||
+
|
||||
+ // Fix NPE (Spigot bug caused by handleDisconnection())
|
||||
+ if (queued == null) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ if (queued.isConsumed()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (queued instanceof PacketSendAction packetSendAction) {
|
||||
+ final Packet<?> packet = packetSendAction.packet;
|
||||
+ if (!packet.isReady()) {
|
||||
+ return false;
|
||||
}
|
||||
+ }
|
||||
|
||||
+ iterator.remove();
|
||||
+ if (queued.tryMarkConsumed()) {
|
||||
+ queued.accept(this);
|
||||
}
|
||||
}
|
||||
+ return true;
|
||||
}
|
||||
+ // Paper end - Optimize network
|
||||
|
||||
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
|
||||
private static int joinAttemptsThisTick; // Paper - Buffer joins to world
|
||||
@@ -564,6 +647,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public void disconnect(Component disconnectReason) {
|
||||
// Spigot Start
|
||||
this.preparing = false;
|
||||
+ this.clearPacketQueue(); // Paper - Optimize network
|
||||
// Spigot End
|
||||
if (this.channel == null) {
|
||||
this.delayedDisconnect = disconnectReason;
|
||||
@@ -751,7 +835,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public void handleDisconnection() {
|
||||
if (this.channel != null && !this.channel.isOpen()) {
|
||||
if (this.disconnectionHandled) {
|
||||
- Connection.LOGGER.warn("handleDisconnection() called twice");
|
||||
+ // Connection.LOGGER.warn("handleDisconnection() called twice"); // Paper - Don't log useless message
|
||||
} else {
|
||||
this.disconnectionHandled = true;
|
||||
PacketListener packetlistener = this.getPacketListener();
|
||||
@@ -764,7 +848,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
packetlistener1.onDisconnect(ichatbasecomponent);
|
||||
}
|
||||
- this.pendingActions.clear(); // Free up packet queue.
|
||||
+ this.clearPacketQueue(); // Paper - Optimize network
|
||||
// Paper start - Add PlayerConnectionCloseEvent
|
||||
final PacketListener packetListener = this.getPacketListener();
|
||||
if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
|
||||
@@ -801,4 +885,93 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public void setBandwidthLogger(LocalSampleLogger log) {
|
||||
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(log);
|
||||
}
|
||||
+
|
||||
+ // Paper start - Optimize network
|
||||
+ public void clearPacketQueue() {
|
||||
+ final net.minecraft.server.level.ServerPlayer player = getPlayer();
|
||||
+ for (final Consumer<Connection> queuedAction : this.pendingActions) {
|
||||
+ if (queuedAction instanceof PacketSendAction packetSendAction) {
|
||||
+ final Packet<?> packet = packetSendAction.packet;
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ this.pendingActions.clear();
|
||||
+ }
|
||||
+
|
||||
+ private static class InnerUtil { // Attempt to hide these methods from ProtocolLib, so it doesn't accidently pick them up.
|
||||
+
|
||||
+ @Nullable
|
||||
+ private static java.util.List<Packet<?>> buildExtraPackets(final Packet<?> packet) {
|
||||
+ final java.util.List<Packet<?>> extra = packet.getExtraPackets();
|
||||
+ if (extra == null || extra.isEmpty()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ final java.util.List<Packet<?>> ret = new java.util.ArrayList<>(1 + extra.size());
|
||||
+ buildExtraPackets0(extra, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private static void buildExtraPackets0(final java.util.List<Packet<?>> extraPackets, final java.util.List<Packet<?>> into) {
|
||||
+ for (final Packet<?> extra : extraPackets) {
|
||||
+ into.add(extra);
|
||||
+ final java.util.List<Packet<?>> extraExtra = extra.getExtraPackets();
|
||||
+ if (extraExtra != null && !extraExtra.isEmpty()) {
|
||||
+ buildExtraPackets0(extraExtra, into);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean canSendImmediate(final Connection networkManager, final net.minecraft.network.protocol.Packet<?> packet) {
|
||||
+ return networkManager.isPending || networkManager.packetListener.protocol() != ConnectionProtocol.PLAY ||
|
||||
+ packet instanceof net.minecraft.network.protocol.common.ClientboundKeepAlivePacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundClearTitlesPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSoundPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSoundEntityPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundStopSoundPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundBossEventPacket;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static class WrappedConsumer implements Consumer<Connection> {
|
||||
+ private final Consumer<Connection> delegate;
|
||||
+ private final java.util.concurrent.atomic.AtomicBoolean consumed = new java.util.concurrent.atomic.AtomicBoolean(false);
|
||||
+
|
||||
+ private WrappedConsumer(final Consumer<Connection> delegate) {
|
||||
+ this.delegate = delegate;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void accept(final Connection connection) {
|
||||
+ this.delegate.accept(connection);
|
||||
+ }
|
||||
+
|
||||
+ public boolean tryMarkConsumed() {
|
||||
+ return consumed.compareAndSet(false, true);
|
||||
+ }
|
||||
+
|
||||
+ public boolean isConsumed() {
|
||||
+ return consumed.get();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static final class PacketSendAction extends WrappedConsumer {
|
||||
+ private final Packet<?> packet;
|
||||
+
|
||||
+ private PacketSendAction(final Packet<?> packet, @Nullable final PacketSendListener packetSendListener, final boolean flush) {
|
||||
+ super(connection -> connection.sendPacket(packet, packetSendListener, flush));
|
||||
+ this.packet = packet;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Optimize network
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/Packet.java b/src/main/java/net/minecraft/network/protocol/Packet.java
|
||||
index 4c776c591dd0a7b36945a6487fdfe86d1187b4af..1ffb80e431b88b24c698f364b40b10590c61b656 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/Packet.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/Packet.java
|
||||
@@ -22,4 +22,31 @@ public interface Packet<T extends PacketListener> {
|
||||
static <B extends ByteBuf, T extends Packet<?>> StreamCodec<B, T> codec(StreamMemberEncoder<B, T> encoder, StreamDecoder<B, T> decoder) {
|
||||
return StreamCodec.ofMember(encoder, decoder);
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ /**
|
||||
+ * @param player Null if not at PLAY stage yet
|
||||
+ */
|
||||
+ default void onPacketDispatch(@Nullable net.minecraft.server.level.ServerPlayer player) {
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * @param player Null if not at PLAY stage yet
|
||||
+ * @param future Can be null if packet was cancelled
|
||||
+ */
|
||||
+ default void onPacketDispatchFinish(@Nullable net.minecraft.server.level.ServerPlayer player, @Nullable io.netty.channel.ChannelFuture future) {}
|
||||
+
|
||||
+ default boolean hasFinishListener() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ default boolean isReady() {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ default java.util.List<Packet<?>> getExtraPackets() {
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 96355e1da8feb6687ea0069dda4a82fcd7e25e8a..a08d9aa6e420f691795df9b627a9cd5b5c0112c5 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -63,10 +63,12 @@ public class ServerConnectionListener {
|
||||
final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
|
||||
// Paper start - prevent blocking on adding a new connection while the server is ticking
|
||||
private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
||||
+ private static final boolean disableFlushConsolidation = Boolean.getBoolean("Paper.disableFlushConsolidate"); // Paper - Optimize network
|
||||
private final void addPending() {
|
||||
Connection connection;
|
||||
while ((connection = pending.poll()) != null) {
|
||||
connections.add(connection);
|
||||
+ connection.isPending = false; // Paper - Optimize network
|
||||
}
|
||||
}
|
||||
// Paper end - prevent blocking on adding a new connection while the server is ticking
|
||||
@@ -112,6 +114,7 @@ public class ServerConnectionListener {
|
||||
;
|
||||
}
|
||||
|
||||
+ if (!disableFlushConsolidation) channel.pipeline().addFirst(new io.netty.handler.flush.FlushConsolidationHandler()); // Paper - Optimize network
|
||||
ChannelPipeline channelpipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30));
|
||||
|
||||
if (ServerConnectionListener.this.server.repliesToStatus()) {
|
213
patches/server/0978-Allow-Saving-of-Oversized-Chunks.patch
Normal file
213
patches/server/0978-Allow-Saving-of-Oversized-Chunks.patch
Normal file
|
@ -0,0 +1,213 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 15 Feb 2019 01:08:19 -0500
|
||||
Subject: [PATCH] Allow Saving of Oversized Chunks
|
||||
|
||||
Note 1.17 update: With 1.17, Entities are no longer stored in chunk slices, so this needs updating!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
The Minecraft World Region File format has a hard cap of 1MB per chunk.
|
||||
This is due to the fact that the header of the file format only allocates
|
||||
a single byte for sector count, meaning a maximum of 256 sectors, at 4k per sector.
|
||||
|
||||
This limit can be reached fairly easily with books, resulting in the chunk being unable
|
||||
to save to the world. Worse off, is that nothing printed when this occured, and silently
|
||||
performed a chunk rollback on next load.
|
||||
|
||||
This leads to security risk with duplication and is being actively exploited.
|
||||
|
||||
This patch catches the too large scenario, falls back and moves any large Entity
|
||||
or Tile Entity into a new compound, and this compound is saved into a different file.
|
||||
|
||||
On Chunk Load, we check for oversized status, and if so, we load the extra file and
|
||||
merge the Entities and Tile Entities from the oversized chunk back into the level to
|
||||
then be loaded as normal.
|
||||
|
||||
Once a chunk is returned back to normal size, the oversized flag will clear, and no
|
||||
extra data file will exist.
|
||||
|
||||
This fix maintains compatability with all existing Anvil Region Format tools as it
|
||||
does not alter the save format. They will just not know about the extra entities.
|
||||
|
||||
This fix also maintains compatability if someone switches server jars to one without
|
||||
this fix, as the data will remain in the oversized file. Once the server returns
|
||||
to a jar with this fix, the data will be restored.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 307196b2a58d4f8db3e6e3c3517a8004d4908b13..69d7a7c2dcee40552d229288c52ed7dd6ad0c2a8 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -18,10 +18,13 @@ import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
+import java.util.zip.InflaterInputStream; // Paper
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.profiling.jfr.JvmProfiler;
|
||||
+import net.minecraft.nbt.CompoundTag; // Paper
|
||||
+import net.minecraft.nbt.NbtIo; // Paper
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -49,6 +52,7 @@ public class RegionFile implements AutoCloseable {
|
||||
@VisibleForTesting
|
||||
protected final RegionBitmap usedSectors;
|
||||
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper
|
||||
+ public final Path regionFile; // Paper
|
||||
|
||||
public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException {
|
||||
this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
|
||||
@@ -56,6 +60,8 @@ public class RegionFile implements AutoCloseable {
|
||||
|
||||
public RegionFile(RegionStorageInfo storageKey, Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync) throws IOException {
|
||||
this.header = ByteBuffer.allocateDirect(8192);
|
||||
+ this.regionFile = file; // Paper
|
||||
+ initOversizedState(); // Paper
|
||||
this.usedSectors = new RegionBitmap();
|
||||
this.info = storageKey;
|
||||
this.path = path;
|
||||
@@ -453,6 +459,74 @@ public class RegionFile implements AutoCloseable {
|
||||
}
|
||||
|
||||
public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
|
||||
+ // Paper start
|
||||
+ private final byte[] oversized = new byte[1024];
|
||||
+ private int oversizedCount;
|
||||
+
|
||||
+ private synchronized void initOversizedState() throws IOException {
|
||||
+ Path metaFile = getOversizedMetaFile();
|
||||
+ if (Files.exists(metaFile)) {
|
||||
+ final byte[] read = java.nio.file.Files.readAllBytes(metaFile);
|
||||
+ System.arraycopy(read, 0, oversized, 0, oversized.length);
|
||||
+ for (byte temp : oversized) {
|
||||
+ oversizedCount += temp;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static int getChunkIndex(int x, int z) {
|
||||
+ return (x & 31) + (z & 31) * 32;
|
||||
+ }
|
||||
+ synchronized boolean isOversized(int x, int z) {
|
||||
+ return this.oversized[getChunkIndex(x, z)] == 1;
|
||||
+ }
|
||||
+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
|
||||
+ final int offset = getChunkIndex(x, z);
|
||||
+ boolean previous = this.oversized[offset] == 1;
|
||||
+ this.oversized[offset] = (byte) (oversized ? 1 : 0);
|
||||
+ if (!previous && oversized) {
|
||||
+ oversizedCount++;
|
||||
+ } else if (!oversized && previous) {
|
||||
+ oversizedCount--;
|
||||
+ }
|
||||
+ if (previous && !oversized) {
|
||||
+ Path oversizedFile = getOversizedFile(x, z);
|
||||
+ if (Files.exists(oversizedFile)) {
|
||||
+ Files.delete(oversizedFile);
|
||||
+ }
|
||||
+ }
|
||||
+ if (oversizedCount > 0) {
|
||||
+ if (previous != oversized) {
|
||||
+ writeOversizedMeta();
|
||||
+ }
|
||||
+ } else if (previous) {
|
||||
+ Path oversizedMetaFile = getOversizedMetaFile();
|
||||
+ if (Files.exists(oversizedMetaFile)) {
|
||||
+ Files.delete(oversizedMetaFile);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void writeOversizedMeta() throws IOException {
|
||||
+ java.nio.file.Files.write(getOversizedMetaFile(), oversized);
|
||||
+ }
|
||||
+
|
||||
+ private Path getOversizedMetaFile() {
|
||||
+ return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + ".oversized.nbt");
|
||||
+ }
|
||||
+
|
||||
+ private Path getOversizedFile(int x, int z) {
|
||||
+ return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
|
||||
+ }
|
||||
+
|
||||
+ synchronized CompoundTag getOversizedData(int x, int z) throws IOException {
|
||||
+ Path file = getOversizedFile(x, z);
|
||||
+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) {
|
||||
+ return NbtIo.read((java.io.DataInput) out);
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ // Paper end
|
||||
private class ChunkBuffer extends ByteArrayOutputStream {
|
||||
|
||||
private final ChunkPos pos;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 3f6955be976064eb542b5c50a9d6d74457c1833c..3b5849f51ff56c6582c8bd451c5c3398827df6db 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -123,6 +123,43 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static void printOversizedLog(String msg, Path file, int x, int z) {
|
||||
+ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.");
|
||||
+ }
|
||||
+
|
||||
+ private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
|
||||
+ synchronized (regionfile) {
|
||||
+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
|
||||
+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z);
|
||||
+ CompoundTag chunk = NbtIo.read((DataInput) datainputstream);
|
||||
+ if (oversizedData == null) {
|
||||
+ return chunk;
|
||||
+ }
|
||||
+ CompoundTag oversizedLevel = oversizedData.getCompound("Level");
|
||||
+
|
||||
+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "Entities", "Entities");
|
||||
+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "TileEntities", "TileEntities");
|
||||
+
|
||||
+ return chunk;
|
||||
+ } catch (Throwable throwable) {
|
||||
+ throwable.printStackTrace();
|
||||
+ throw throwable;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static void mergeChunkList(CompoundTag level, CompoundTag oversizedLevel, String key, String oversizedKey) {
|
||||
+ net.minecraft.nbt.ListTag levelList = level.getList(key, net.minecraft.nbt.Tag.TAG_COMPOUND);
|
||||
+ net.minecraft.nbt.ListTag oversizedList = oversizedLevel.getList(oversizedKey, net.minecraft.nbt.Tag.TAG_COMPOUND);
|
||||
+
|
||||
+ if (!oversizedList.isEmpty()) {
|
||||
+ levelList.addAll(oversizedList);
|
||||
+ level.put(key, levelList);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Nullable
|
||||
public CompoundTag read(ChunkPos pos) throws IOException {
|
||||
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
|
||||
@@ -134,6 +171,12 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
try { // Paper
|
||||
DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
|
||||
|
||||
+ // Paper start
|
||||
+ if (regionfile.isOversized(pos.x, pos.z)) {
|
||||
+ printOversizedLog("Loading Oversized Chunk!", regionfile.regionFile, pos.x, pos.z);
|
||||
+ return readOversizedChunk(regionfile, pos);
|
||||
+ }
|
||||
+ // Paper end
|
||||
CompoundTag nbttagcompound;
|
||||
label43:
|
||||
{
|
||||
@@ -225,6 +268,7 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
|
||||
try {
|
||||
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
||||
+ regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
// Paper start - don't write garbage data to disk if writing serialization fails
|
||||
dataoutputstream.close(); // Only write if successful
|
||||
} catch (final RegionFileSizeException e) {
|
243
patches/server/0979-Fix-World-isChunkGenerated-calls.patch
Normal file
243
patches/server/0979-Fix-World-isChunkGenerated-calls.patch
Normal file
|
@ -0,0 +1,243 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Sat, 15 Jun 2019 08:54:33 -0700
|
||||
Subject: [PATCH] Fix World#isChunkGenerated calls
|
||||
|
||||
Optimize World#loadChunk() too
|
||||
This patch also adds a chunk status cache on region files (note that
|
||||
its only purpose is to cache the status on DISK)
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 721b63f7471ace33ae22f4205f554ee3be0e033d..c22b40790a28c9a670538a8cc97821b33443845d 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -735,9 +735,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
// Paper end
|
||||
|
||||
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
|
||||
- return this.read(chunkPos).thenApplyAsync((optional) -> {
|
||||
- return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
|
||||
- }, Util.backgroundExecutor());
|
||||
+ // Paper start - Cache chunk status on disk
|
||||
+ try {
|
||||
+ return CompletableFuture.completedFuture(Optional.ofNullable(this.readConvertChunkSync(chunkPos)));
|
||||
+ } catch (Throwable thr) {
|
||||
+ return CompletableFuture.failedFuture(thr);
|
||||
+ }
|
||||
+ // Paper end - Cache chunk status on disk
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
@@ -746,6 +750,60 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
+ // Paper start - Cache chunk status on disk
|
||||
+ @Nullable
|
||||
+ public CompoundTag readConvertChunkSync(ChunkPos pos) throws IOException {
|
||||
+ CompoundTag nbttagcompound = this.readSync(pos);
|
||||
+ if (nbttagcompound == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ nbttagcompound = this.upgradeChunkTag(nbttagcompound, pos); // CraftBukkit
|
||||
+ if (nbttagcompound == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ this.updateChunkStatusOnDisk(pos, nbttagcompound);
|
||||
+
|
||||
+ return nbttagcompound;
|
||||
+ }
|
||||
+
|
||||
+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) {
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos);
|
||||
+
|
||||
+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
||||
+ }
|
||||
+
|
||||
+ public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException {
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true);
|
||||
+
|
||||
+ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
||||
+
|
||||
+ if (status != null) {
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ this.readChunk(chunkPos);
|
||||
+
|
||||
+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
|
||||
+ }
|
||||
+
|
||||
+ public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false);
|
||||
+
|
||||
+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound));
|
||||
+ }
|
||||
+
|
||||
+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
|
||||
+ ChunkHolder chunkHolder = io.papermc.paper.chunk.system.ChunkSystem.getUnloadingChunkHolder(this.level, chunkX, chunkZ);
|
||||
+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
|
||||
+ }
|
||||
+ // Paper end - Cache chunk status on disk
|
||||
+
|
||||
public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) { // Paper - public
|
||||
// Spigot start
|
||||
return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
index 69d7a7c2dcee40552d229288c52ed7dd6ad0c2a8..36cdf165334c4d7c1ca28ba42b1006b163e45a95 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
||||
@@ -53,6 +53,29 @@ public class RegionFile implements AutoCloseable {
|
||||
protected final RegionBitmap usedSectors;
|
||||
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper
|
||||
public final Path regionFile; // Paper
|
||||
+ // Paper start - Cache chunk status
|
||||
+ private final net.minecraft.world.level.chunk.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.ChunkStatus[32 * 32];
|
||||
+
|
||||
+ private boolean closed;
|
||||
+
|
||||
+ // invoked on write/read
|
||||
+ public void setStatus(int x, int z, net.minecraft.world.level.chunk.ChunkStatus status) {
|
||||
+ if (this.closed) {
|
||||
+ // We've used an invalid region file.
|
||||
+ throw new IllegalStateException("RegionFile is closed");
|
||||
+ }
|
||||
+ this.statuses[getChunkLocation(x, z)] = status;
|
||||
+ }
|
||||
+
|
||||
+ public net.minecraft.world.level.chunk.ChunkStatus getStatusIfCached(int x, int z) {
|
||||
+ if (this.closed) {
|
||||
+ // We've used an invalid region file.
|
||||
+ throw new IllegalStateException("RegionFile is closed");
|
||||
+ }
|
||||
+ final int location = getChunkLocation(x, z);
|
||||
+ return this.statuses[location];
|
||||
+ }
|
||||
+ // Paper end - Cache chunk status
|
||||
|
||||
public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException {
|
||||
this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
|
||||
@@ -419,6 +442,7 @@ public class RegionFile implements AutoCloseable {
|
||||
return this.getOffset(pos) != 0;
|
||||
}
|
||||
|
||||
+ private static int getChunkLocation(int x, int z) { return (x & 31) + (z & 31) * 32; } // Paper - Cache chunk status; OBFHELPER - sort of, mirror of logic below
|
||||
private static int getOffsetIndex(ChunkPos pos) {
|
||||
return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32;
|
||||
}
|
||||
@@ -429,6 +453,7 @@ public class RegionFile implements AutoCloseable {
|
||||
synchronized (this) {
|
||||
try {
|
||||
// Paper end
|
||||
+ this.closed = true; // Paper - Cache chunk status
|
||||
try {
|
||||
this.padToFullSector();
|
||||
} finally {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
index 3b5849f51ff56c6582c8bd451c5c3398827df6db..a73a37320da2c141fc2db9d1d61233a34ce0c906 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
||||
@@ -268,6 +268,7 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
|
||||
try {
|
||||
NbtIo.write(nbt, (DataOutput) dataoutputstream);
|
||||
+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - Cache chunk status
|
||||
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
|
||||
// Paper start - don't write garbage data to disk if writing serialization fails
|
||||
dataoutputstream.close(); // Only write if successful
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
index 4bcfae1ca9c2ba590d800534bb199f947c114c20..67e51df85f967e7f3184a7b21562b26cbd64eae1 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -383,9 +383,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
|
||||
@Override
|
||||
public boolean isChunkGenerated(int x, int z) {
|
||||
+ // Paper start - Fix this method
|
||||
+ if (!Bukkit.isPrimaryThread()) {
|
||||
+ return java.util.concurrent.CompletableFuture.supplyAsync(() -> {
|
||||
+ return CraftWorld.this.isChunkGenerated(x, z);
|
||||
+ }, world.getChunkSource().mainThreadProcessor).join();
|
||||
+ }
|
||||
+ ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z);
|
||||
+ if (chunk == null) {
|
||||
+ chunk = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
|
||||
+ }
|
||||
+ if (chunk != null) {
|
||||
+ return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk;
|
||||
+ }
|
||||
try {
|
||||
- return this.isChunkLoaded(x, z) || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)).get().isPresent();
|
||||
- } catch (InterruptedException | ExecutionException ex) {
|
||||
+ return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL;
|
||||
+ } catch (java.io.IOException ex) {
|
||||
+ // Paper end - Fix this method
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
@@ -539,20 +553,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
|
||||
public boolean loadChunk(int x, int z, boolean generate) {
|
||||
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
|
||||
warnUnsafeChunk("loading a faraway chunk", x, z); // Paper
|
||||
- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
|
||||
-
|
||||
- // If generate = false, but the chunk already exists, we will get this back.
|
||||
- if (chunk instanceof ImposterProtoChunk) {
|
||||
- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
|
||||
- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
|
||||
- }
|
||||
-
|
||||
- if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
|
||||
- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper
|
||||
+ // Paper start - Optimize this method
|
||||
+ ChunkPos chunkPos = new ChunkPos(x, z);
|
||||
+ ChunkAccess immediate = world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
|
||||
+ if (immediate != null) {
|
||||
+ // Plugins should use plugin tickets instead of this method to keep a chunk perpetually loaded
|
||||
return true;
|
||||
}
|
||||
|
||||
- return false;
|
||||
+ if (!generate) {
|
||||
+ immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
|
||||
+ if (immediate != null) {
|
||||
+ if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) {
|
||||
+ return false; // not full status
|
||||
+ }
|
||||
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
|
||||
+ world.getChunk(x, z); // make sure we're at ticket level 32 or lower
|
||||
+ return true;
|
||||
+ }
|
||||
+ net.minecraft.world.level.chunk.storage.RegionFile file;
|
||||
+ try {
|
||||
+ file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
|
||||
+ } catch (java.io.IOException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+
|
||||
+ ChunkStatus status = file.getStatusIfCached(x, z);
|
||||
+ if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true);
|
||||
+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // fall through to load
|
||||
+ // we do this so we do not re-read the chunk data on disk
|
||||
+ }
|
||||
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
|
||||
+ world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
|
||||
+ return true;
|
||||
+ // Paper end - Optimize this method
|
||||
}
|
||||
|
||||
@Override
|
288
patches/server/0980-Flat-bedrock-generator-settings.patch
Normal file
288
patches/server/0980-Flat-bedrock-generator-settings.patch
Normal file
|
@ -0,0 +1,288 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Byteflux <byte@byteflux.net>
|
||||
Date: Wed, 2 Mar 2016 02:17:54 -0600
|
||||
Subject: [PATCH] Flat bedrock generator settings
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Condition
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Context
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockX
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockY
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockZ
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Context context
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$Context randomState
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule
|
||||
|
||||
Co-authored-by: Noah van der Aa <ndvdaa@gmail.com>
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java b/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..02d98ec591b676acf64460d14d608860d32a362a
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java
|
||||
@@ -0,0 +1,76 @@
|
||||
+package io.papermc.paper.world.worldgen;
|
||||
+
|
||||
+import com.mojang.serialization.Codec;
|
||||
+import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
+import net.minecraft.core.Registry;
|
||||
+import net.minecraft.core.registries.BuiltInRegistries;
|
||||
+import net.minecraft.core.registries.Registries;
|
||||
+import net.minecraft.resources.ResourceKey;
|
||||
+import net.minecraft.resources.ResourceLocation;
|
||||
+import net.minecraft.util.KeyDispatchDataCodec;
|
||||
+import net.minecraft.util.Mth;
|
||||
+import net.minecraft.util.RandomSource;
|
||||
+import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
||||
+import net.minecraft.world.level.levelgen.SurfaceRules;
|
||||
+import net.minecraft.world.level.levelgen.VerticalAnchor;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+// Modelled off of SurfaceRules$VerticalGradientConditionSource
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public record OptionallyFlatBedrockConditionSource(ResourceLocation randomName, VerticalAnchor trueAtAndBelow, VerticalAnchor falseAtAndAbove, boolean isRoof) implements SurfaceRules.ConditionSource {
|
||||
+
|
||||
+ private static final ResourceKey<Codec<? extends SurfaceRules.ConditionSource>> CODEC_RESOURCE_KEY = ResourceKey.create(Registries.MATERIAL_CONDITION, new ResourceLocation(ResourceLocation.PAPER_NAMESPACE, "optionally_flat_bedrock_condition_source"));
|
||||
+ private static final KeyDispatchDataCodec<OptionallyFlatBedrockConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec((instance) -> {
|
||||
+ return instance.group(
|
||||
+ ResourceLocation.CODEC.fieldOf("random_name").forGetter(OptionallyFlatBedrockConditionSource::randomName),
|
||||
+ VerticalAnchor.CODEC.fieldOf("true_at_and_below").forGetter(OptionallyFlatBedrockConditionSource::trueAtAndBelow),
|
||||
+ VerticalAnchor.CODEC.fieldOf("false_at_and_above").forGetter(OptionallyFlatBedrockConditionSource::falseAtAndAbove),
|
||||
+ Codec.BOOL.fieldOf("is_roof").forGetter(OptionallyFlatBedrockConditionSource::isRoof)
|
||||
+ ).apply(instance, OptionallyFlatBedrockConditionSource::new);
|
||||
+ }));
|
||||
+
|
||||
+ public static void bootstrap() {
|
||||
+ Registry.register(BuiltInRegistries.MATERIAL_CONDITION, CODEC_RESOURCE_KEY, CODEC.codec());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
|
||||
+ return CODEC;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public SurfaceRules.Condition apply(final SurfaceRules.Context context) {
|
||||
+ boolean hasFlatBedrock = context.context.getWorld().paperConfig().environment.generateFlatBedrock;
|
||||
+ int tempTrueAtAndBelowY = this.trueAtAndBelow().resolveY(context.context);
|
||||
+ int tempFalseAtAndAboveY = this.falseAtAndAbove().resolveY(context.context);
|
||||
+
|
||||
+ int flatYLevel = this.isRoof ? Math.max(tempFalseAtAndAboveY, tempTrueAtAndBelowY) - 1 : Math.min(tempFalseAtAndAboveY, tempTrueAtAndBelowY);
|
||||
+ final int trueAtAndBelowY = hasFlatBedrock ? flatYLevel : tempTrueAtAndBelowY;
|
||||
+ final int falseAtAndAboveY = hasFlatBedrock ? flatYLevel : tempFalseAtAndAboveY;
|
||||
+
|
||||
+ final PositionalRandomFactory positionalRandomFactory = context.randomState.getOrCreateRandomFactory(this.randomName());
|
||||
+
|
||||
+ class VerticalGradientCondition extends SurfaceRules.LazyYCondition {
|
||||
+ VerticalGradientCondition(SurfaceRules.Context context) {
|
||||
+ super(context);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected boolean compute() {
|
||||
+ int blockY = this.context.blockY;
|
||||
+ if (blockY <= trueAtAndBelowY) {
|
||||
+ return true;
|
||||
+ } else if (blockY >= falseAtAndAboveY) {
|
||||
+ return false;
|
||||
+ } else {
|
||||
+ double d = Mth.map(blockY, trueAtAndBelowY, falseAtAndAboveY, 1.0D, 0.0D);
|
||||
+ RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, blockY, this.context.blockZ);
|
||||
+ return (double)randomSource.nextFloat() < d;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return new VerticalGradientCondition(context);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/Bootstrap.java b/src/main/java/net/minecraft/server/Bootstrap.java
|
||||
index 05f4be8dfff37ab6804a2d990b1f8c430d42dc7c..2a49a6a6bccabf588ba67b565d8f16fda04ba0b0 100644
|
||||
--- a/src/main/java/net/minecraft/server/Bootstrap.java
|
||||
+++ b/src/main/java/net/minecraft/server/Bootstrap.java
|
||||
@@ -76,6 +76,7 @@ public class Bootstrap {
|
||||
CauldronInteraction.bootStrap();
|
||||
// Paper start
|
||||
BuiltInRegistries.bootStrap(() -> {
|
||||
+ io.papermc.paper.world.worldgen.OptionallyFlatBedrockConditionSource.bootstrap(); // Paper - Flat bedrock generator settings
|
||||
io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.enterBootstrappers(); // Paper - Entrypoint for bootstrapping
|
||||
});
|
||||
// Paper end
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
|
||||
index dc765b92cc90f5f370254e68bbbdfa5add7935ce..8ce870a5341a61fbbaf42021ef7f7f615a6a3e09 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
|
||||
@@ -207,7 +207,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
|
||||
@Override
|
||||
public void buildSurface(WorldGenRegion region, StructureManager structures, RandomState noiseConfig, ChunkAccess chunk) {
|
||||
if (!SharedConstants.debugVoidTerrain(chunk.getPos())) {
|
||||
- WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region);
|
||||
+ WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region, region.getMinecraftWorld()); // Paper - Flat bedrock generator settings
|
||||
|
||||
this.buildSurface(chunk, worldgenerationcontext, noiseConfig, structures, region.getBiomeManager(), region.registryAccess().registryOrThrow(Registries.BIOME), Blender.of(region));
|
||||
}
|
||||
@@ -235,7 +235,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
|
||||
return this.createNoiseChunk(ichunkaccess1, structureAccessor, Blender.of(chunkRegion), noiseConfig);
|
||||
});
|
||||
Aquifer aquifer = noisechunk.aquifer();
|
||||
- CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule());
|
||||
+ CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule(), chunkRegion.getMinecraftWorld()); // Paper - Flat bedrock generator settings
|
||||
CarvingMask carvingmask = ((ProtoChunk) chunk).getOrCreateCarvingMask(carverStep);
|
||||
|
||||
for (int j = -8; j <= 8; ++j) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
|
||||
index b99283c31193e2110f6e3f39c23dbfc2442bab2b..a34e53249668d917c9d77c6837b91360a2349bbc 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
|
||||
@@ -6,10 +6,13 @@ import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
public class WorldGenerationContext {
|
||||
private final int minY;
|
||||
private final int height;
|
||||
+ private final @javax.annotation.Nullable net.minecraft.world.level.Level level; // Paper - Flat bedrock generator settings
|
||||
|
||||
- public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) {
|
||||
+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) { this(generator, world, null); } // Paper - Flat bedrock generator settings
|
||||
+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world, @org.jetbrains.annotations.Nullable net.minecraft.world.level.Level level) { // Paper - Flat bedrock generator settings
|
||||
this.minY = Math.max(world.getMinBuildHeight(), generator.getMinY());
|
||||
this.height = Math.min(world.getHeight(), generator.getGenDepth());
|
||||
+ this.level = level; // Paper - Flat bedrock generator settings
|
||||
}
|
||||
|
||||
public int getMinGenY() {
|
||||
@@ -19,4 +22,13 @@ public class WorldGenerationContext {
|
||||
public int getGenDepth() {
|
||||
return this.height;
|
||||
}
|
||||
+
|
||||
+ // Paper start - Flat bedrock generator settings
|
||||
+ public net.minecraft.world.level.Level getWorld() {
|
||||
+ if (this.level == null) {
|
||||
+ throw new NullPointerException("WorldGenerationContext was initialized without a Level, but WorldGenerationContext#getWorld was called");
|
||||
+ }
|
||||
+ return this.level;
|
||||
+ }
|
||||
+ // Paper end - Flat bedrock generator settings
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
|
||||
index 390bcf9c302effd9db42d7a0e65b5433cc2eadd6..b9e919d31e442f49300744395af3cf9431e86c57 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
|
||||
@@ -27,9 +27,9 @@ public class CarvingContext extends WorldGenerationContext {
|
||||
LevelHeightAccessor heightLimitView,
|
||||
NoiseChunk chunkNoiseSampler,
|
||||
RandomState noiseConfig,
|
||||
- SurfaceRules.RuleSource materialRule
|
||||
+ SurfaceRules.RuleSource materialRule, @javax.annotation.Nullable net.minecraft.world.level.Level level // Paper - Flat bedrock generator settings
|
||||
) {
|
||||
- super(noiseChunkGenerator, heightLimitView);
|
||||
+ super(noiseChunkGenerator, heightLimitView, level); // Paper - Flat bedrock generator settings
|
||||
this.registryAccess = registryManager;
|
||||
this.noiseChunk = chunkNoiseSampler;
|
||||
this.randomState = noiseConfig;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
|
||||
index 640c2683c842655bbaee8f293f1c2613ef44844e..c7dfd844c7846281ceff0d443c0160054fd36c5c 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
|
||||
@@ -18,7 +18,7 @@ public class PlacementContext extends WorldGenerationContext {
|
||||
private final Optional<PlacedFeature> topFeature;
|
||||
|
||||
public PlacementContext(WorldGenLevel world, ChunkGenerator generator, Optional<PlacedFeature> placedFeature) {
|
||||
- super(generator, world);
|
||||
+ super(generator, world, world.getLevel()); // Paper - Flat bedrock generator settings
|
||||
this.level = world;
|
||||
this.generator = generator;
|
||||
this.topFeature = placedFeature;
|
||||
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json b/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
|
||||
index 3f61ea695aa6a91a0cacf1fa8dc0a9bdc6fa36e6..02d32bbdbc795db271205bef95e6987ac34b0136 100644
|
||||
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
|
||||
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/amplified.json
|
||||
@@ -389,7 +389,8 @@
|
||||
{
|
||||
"type": "minecraft:condition",
|
||||
"if_true": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": false,
|
||||
"false_at_and_above": {
|
||||
"above_bottom": 5
|
||||
},
|
||||
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json b/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
|
||||
index 1fe9ce904cba21ff4e6efb948a0bc274a22bcb0b..2e8c1aad10a2b0adbdee4180cfc1902984b6565b 100644
|
||||
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
|
||||
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/caves.json
|
||||
@@ -110,7 +110,8 @@
|
||||
"if_true": {
|
||||
"type": "minecraft:not",
|
||||
"invert": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": true,
|
||||
"false_at_and_above": {
|
||||
"below_top": 0
|
||||
},
|
||||
@@ -130,7 +131,8 @@
|
||||
{
|
||||
"type": "minecraft:condition",
|
||||
"if_true": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": false,
|
||||
"false_at_and_above": {
|
||||
"above_bottom": 5
|
||||
},
|
||||
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json b/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
|
||||
index f4c34de998d78a80bc026d8e0d423c9bed9bbf8a..fd5bd5474b8f931f2e04706997e71cd5145a5a82 100644
|
||||
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
|
||||
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/large_biomes.json
|
||||
@@ -389,7 +389,8 @@
|
||||
{
|
||||
"type": "minecraft:condition",
|
||||
"if_true": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": false,
|
||||
"false_at_and_above": {
|
||||
"above_bottom": 5
|
||||
},
|
||||
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json b/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
|
||||
index 6219d25fcdbc761debc1d1e357757d1b977dc0b0..1657179b8c3f7e549e3b8774ecb75f292ae79c38 100644
|
||||
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
|
||||
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/nether.json
|
||||
@@ -108,7 +108,8 @@
|
||||
{
|
||||
"type": "minecraft:condition",
|
||||
"if_true": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": false,
|
||||
"false_at_and_above": {
|
||||
"above_bottom": 5
|
||||
},
|
||||
@@ -129,7 +130,8 @@
|
||||
"if_true": {
|
||||
"type": "minecraft:not",
|
||||
"invert": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": true,
|
||||
"false_at_and_above": {
|
||||
"below_top": 0
|
||||
},
|
||||
diff --git a/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json b/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
|
||||
index da3bda6167859f4ddf7d76ec2053f40b2139c13e..70beb7665a43a06b4afcb3c77aa77f923cb444cb 100644
|
||||
--- a/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
|
||||
+++ b/src/main/resources/data/minecraft/worldgen/noise_settings/overworld.json
|
||||
@@ -389,7 +389,8 @@
|
||||
{
|
||||
"type": "minecraft:condition",
|
||||
"if_true": {
|
||||
- "type": "minecraft:vertical_gradient",
|
||||
+ "type": "paper:optionally_flat_bedrock_condition_source",
|
||||
+ "is_roof": false,
|
||||
"false_at_and_above": {
|
||||
"above_bottom": 5
|
||||
},
|
812
patches/server/0981-Entity-Activation-Range-2.0.patch
Normal file
812
patches/server/0981-Entity-Activation-Range-2.0.patch
Normal file
|
@ -0,0 +1,812 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Fri, 13 May 2016 01:38:06 -0400
|
||||
Subject: [PATCH] Entity Activation Range 2.0
|
||||
|
||||
Optimizes performance of Activation Range
|
||||
|
||||
Adds many new configurations and a new wake up inactive system
|
||||
|
||||
Fixes and adds new Immunities to improve gameplay behavior
|
||||
|
||||
Adds water Mobs to activation range config and nerfs fish
|
||||
Adds flying monsters to control ghast and phantoms
|
||||
Adds villagers as separate config
|
||||
|
||||
== AT ==
|
||||
public net.minecraft.world.entity.Entity isInsidePortal
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 63777efec582b8db9804914d554e8aedafcf08c8..d0adc0fab52c0b394d348fbf658eb77a7dfc7049 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2,7 +2,6 @@ package net.minecraft.server.level;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import co.aikar.timings.TimingHistory; // Paper
|
||||
-import co.aikar.timings.Timings; // Paper
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
@@ -1228,17 +1227,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
++TimingHistory.entityTicks; // Paper - timings
|
||||
// Spigot start
|
||||
co.aikar.timings.Timing timer; // Paper
|
||||
- if (!org.spigotmc.ActivationRange.checkIfActive(entity)) {
|
||||
+ /*if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { // Paper - comment out - EAR 2, reimplement below
|
||||
entity.tickCount++;
|
||||
timer = entity.getType().inactiveTickTimer.startTiming(); try { // Paper - timings
|
||||
entity.inactiveTick();
|
||||
} finally { timer.stopTiming(); } // Paper
|
||||
return;
|
||||
- }
|
||||
+ }*/ // Paper - comment out EAR 2
|
||||
// Spigot end
|
||||
// Paper start- timings
|
||||
- TimingHistory.activatedEntityTicks++;
|
||||
- timer = entity.getVehicle() != null ? entity.getType().passengerTickTimer.startTiming() : entity.getType().tickTimer.startTiming();
|
||||
+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity);
|
||||
+ timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper
|
||||
try {
|
||||
// Paper end - timings
|
||||
entity.setOldPosAndRot();
|
||||
@@ -1249,9 +1248,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
return BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString();
|
||||
});
|
||||
gameprofilerfiller.incrementCounter("tickNonPassenger");
|
||||
+ if (isActive) { // Paper - EAR 2
|
||||
+ TimingHistory.activatedEntityTicks++;
|
||||
entity.tick();
|
||||
entity.postTick(); // CraftBukkit
|
||||
+ } else { entity.inactiveTick(); } // Paper - EAR 2
|
||||
this.getProfiler().pop();
|
||||
+ } finally { timer.stopTiming(); } // Paper - timings
|
||||
Iterator iterator = entity.getPassengers().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
@@ -1259,13 +1262,18 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
|
||||
this.tickPassenger(entity, entity1);
|
||||
}
|
||||
- } finally { timer.stopTiming(); } // Paper - timings
|
||||
+ // } finally { timer.stopTiming(); } // Paper - timings - move up
|
||||
|
||||
}
|
||||
|
||||
private void tickPassenger(Entity vehicle, Entity passenger) {
|
||||
if (!passenger.isRemoved() && passenger.getVehicle() == vehicle) {
|
||||
if (passenger instanceof Player || this.entityTickList.contains(passenger)) {
|
||||
+ // Paper - EAR 2
|
||||
+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(passenger);
|
||||
+ co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper
|
||||
+ try {
|
||||
+ // Paper end
|
||||
passenger.setOldPosAndRot();
|
||||
++passenger.tickCount;
|
||||
ProfilerFiller gameprofilerfiller = this.getProfiler();
|
||||
@@ -1274,8 +1282,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
return BuiltInRegistries.ENTITY_TYPE.getKey(passenger.getType()).toString();
|
||||
});
|
||||
gameprofilerfiller.incrementCounter("tickPassenger");
|
||||
+ // Paper start - EAR 2
|
||||
+ if (isActive) {
|
||||
passenger.rideTick();
|
||||
passenger.postTick(); // CraftBukkit
|
||||
+ } else {
|
||||
+ passenger.setDeltaMovement(Vec3.ZERO);
|
||||
+ passenger.inactiveTick();
|
||||
+ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary
|
||||
+ vehicle.positionRider(passenger);
|
||||
+ }
|
||||
+ // Paper end - EAR 2
|
||||
gameprofilerfiller.pop();
|
||||
Iterator iterator = passenger.getPassengers().iterator();
|
||||
|
||||
@@ -1285,6 +1302,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
this.tickPassenger(passenger, entity2);
|
||||
}
|
||||
|
||||
+ } finally { timer.stopTiming(); }// Paper - EAR2 timings
|
||||
}
|
||||
} else {
|
||||
passenger.stopRiding();
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 64ba2b3f8e0d5176dc24432ceb4a51eea1f24098..ab529d8c2c10fffd58fe4754882b558eed239fbd 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -414,6 +414,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
// Spigot end
|
||||
protected int numCollisions = 0; // Paper - Cap entity collisions
|
||||
public boolean fromNetherPortal; // Paper - Add option to nerf pigmen from nether portals
|
||||
+ public long activatedImmunityTick = Integer.MIN_VALUE; // Paper - EAR
|
||||
+ public boolean isTemporarilyActive; // Paper - EAR
|
||||
public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
|
||||
// Paper start - Entity origin API
|
||||
@javax.annotation.Nullable
|
||||
@@ -1045,6 +1047,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
} else {
|
||||
this.wasOnFire = this.isOnFire();
|
||||
if (movementType == MoverType.PISTON) {
|
||||
+ this.activatedTick = Math.max(this.activatedTick, MinecraftServer.currentTick + 20); // Paper
|
||||
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, MinecraftServer.currentTick + 20); // Paper
|
||||
movement = this.limitPistonMovement(movement);
|
||||
if (movement.equals(Vec3.ZERO)) {
|
||||
return;
|
||||
@@ -1057,6 +1061,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.stuckSpeedMultiplier = Vec3.ZERO;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
+ // Paper start - ignore movement changes while inactive.
|
||||
+ if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && movementType == MoverType.SELF) {
|
||||
+ setDeltaMovement(Vec3.ZERO);
|
||||
+ this.level.getProfiler().pop();
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
movement = this.maybeBackOffFromEdge(movement, movementType);
|
||||
Vec3 vec3d1 = this.collide(movement);
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
index 4f2148d13fc26ad2c43460f46c376832c1bb8150..146ec4bb70ae7b1d2b7705daa150e5fcfe6ec223 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
|
||||
@@ -237,6 +237,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti
|
||||
return this.lookControl;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public void inactiveTick() {
|
||||
+ super.inactiveTick();
|
||||
+ if (this.goalSelector.inactiveTick()) {
|
||||
+ this.goalSelector.tick();
|
||||
+ }
|
||||
+ if (this.targetSelector.inactiveTick()) {
|
||||
+ this.targetSelector.tick();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public MoveControl getMoveControl() {
|
||||
Entity entity = this.getControlledVehicle();
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/PathfinderMob.java b/src/main/java/net/minecraft/world/entity/PathfinderMob.java
|
||||
index cdd07093342521ff9944bf7a342bbf142ba3f0b7..645414720db0231a19c88f681608eb08200b8782 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/PathfinderMob.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/PathfinderMob.java
|
||||
@@ -23,6 +23,7 @@ public abstract class PathfinderMob extends Mob {
|
||||
}
|
||||
|
||||
public org.bukkit.craftbukkit.entity.CraftCreature getBukkitCreature() { return (org.bukkit.craftbukkit.entity.CraftCreature) super.getBukkitEntity(); } // Paper
|
||||
+ public BlockPos movingTarget = null; public BlockPos getMovingTarget() { return movingTarget; } // Paper
|
||||
|
||||
public float getWalkTargetValue(BlockPos pos) {
|
||||
return this.getWalkTargetValue(pos, this.level());
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
index 89b8a304fe9fae4b57640afbab04b6764ce9aab8..074ef807258139f818e30494126585262c2f33c0 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
@@ -26,6 +26,7 @@ public class GoalSelector {
|
||||
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
|
||||
private final Supplier<ProfilerFiller> profiler;
|
||||
private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private int curRate;
|
||||
|
||||
public GoalSelector(Supplier<ProfilerFiller> profiler) {
|
||||
this.profiler = profiler;
|
||||
@@ -40,6 +41,20 @@ public class GoalSelector {
|
||||
this.availableGoals.removeIf(goal -> predicate.test(goal.getGoal()));
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public boolean inactiveTick() {
|
||||
+ this.curRate++;
|
||||
+ return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct
|
||||
+ }
|
||||
+ public boolean hasTasks() {
|
||||
+ for (WrappedGoal task : this.availableGoals) {
|
||||
+ if (task.isRunning()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
public void removeGoal(Goal goal) {
|
||||
for (WrappedGoal wrappedGoal : this.availableGoals) {
|
||||
if (wrappedGoal.getGoal() == goal && wrappedGoal.isRunning()) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
index 6d8ea05e5e86e9f6359b560043bb55a10784e952..aee0147649d458b87d92496eda0c1723ebe570d2 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java
|
||||
@@ -23,6 +23,14 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
public MoveToBlockGoal(PathfinderMob mob, double speed, int range) {
|
||||
this(mob, speed, range, 1);
|
||||
}
|
||||
+ // Paper start - activation range improvements
|
||||
+ @Override
|
||||
+ public void stop() {
|
||||
+ super.stop();
|
||||
+ this.blockPos = BlockPos.ZERO;
|
||||
+ this.mob.movingTarget = null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public MoveToBlockGoal(PathfinderMob mob, double speed, int range, int maxYDifference) {
|
||||
this.mob = mob;
|
||||
@@ -115,6 +123,7 @@ public abstract class MoveToBlockGoal extends Goal {
|
||||
mutableBlockPos.setWithOffset(blockPos, m, k - 1, n);
|
||||
if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) {
|
||||
this.blockPos = mutableBlockPos;
|
||||
+ this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper
|
||||
return true;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
||||
index 1e8579c994098fb18d9725e3f1604a582be4162f..a7930f9875aa4aca997caaead46ecdc21e5e11d7 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
|
||||
@@ -228,17 +228,34 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
@Override
|
||||
public void inactiveTick() {
|
||||
// SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
|
||||
- if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) {
|
||||
- this.customServerAiStep();
|
||||
+ // Paper start
|
||||
+ if (this.getUnhappyCounter() > 0) {
|
||||
+ this.setUnhappyCounter(this.getUnhappyCounter() - 1);
|
||||
}
|
||||
+ if (this.isEffectiveAi()) {
|
||||
+ if (this.level().spigotConfig.tickInactiveVillagers) {
|
||||
+ this.customServerAiStep();
|
||||
+ } else {
|
||||
+ this.customServerAiStep(true);
|
||||
+ }
|
||||
+ }
|
||||
+ maybeDecayGossip();
|
||||
+ // Paper end
|
||||
+
|
||||
super.inactiveTick();
|
||||
}
|
||||
// Spigot End
|
||||
|
||||
@Override
|
||||
+ @Deprecated // Paper
|
||||
protected void customServerAiStep() {
|
||||
+ // Paper start
|
||||
+ this.customServerAiStep(false);
|
||||
+ }
|
||||
+ protected void customServerAiStep(final boolean inactive) {
|
||||
+ // Paper end
|
||||
this.level().getProfiler().push("villagerBrain");
|
||||
- this.getBrain().tick((ServerLevel) this.level(), this);
|
||||
+ if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper
|
||||
this.level().getProfiler().pop();
|
||||
if (this.assignProfessionWhenSpawned) {
|
||||
this.assignProfessionWhenSpawned = false;
|
||||
@@ -262,7 +279,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
this.lastTradedPlayer = null;
|
||||
}
|
||||
|
||||
- if (!this.isNoAi() && this.random.nextInt(100) == 0) {
|
||||
+ if (!inactive && !this.isNoAi() && this.random.nextInt(100) == 0) { // Paper
|
||||
Raid raid = ((ServerLevel) this.level()).getRaidAt(this.blockPosition());
|
||||
|
||||
if (raid != null && raid.isActive() && !raid.isOver()) {
|
||||
@@ -273,6 +290,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
|
||||
this.stopTrading();
|
||||
}
|
||||
+ if (inactive) return; // Paper
|
||||
|
||||
super.customServerAiStep();
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
|
||||
index 0b7f52021441d633c37543e8ae485e81c292b747..d7f8464bf3eed0e42a5fc7f14a5b243d171f8b5e 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
|
||||
@@ -52,6 +52,7 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
|
||||
if (bl != this.isEnabled()) {
|
||||
this.setEnabled(bl);
|
||||
}
|
||||
+ this.immunize(); // Paper
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@@ -92,11 +93,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
|
||||
|
||||
public boolean suckInItems() {
|
||||
if (HopperBlockEntity.suckInItems(this.level(), this)) {
|
||||
+ this.immunize(); // Paper
|
||||
return true;
|
||||
} else {
|
||||
for (ItemEntity itemEntity : this.level()
|
||||
.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.25, 0.0, 0.25), EntitySelector.ENTITY_STILL_ALIVE)) {
|
||||
if (HopperBlockEntity.addItem(this, itemEntity)) {
|
||||
+ this.immunize(); // Paper
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -126,4 +129,11 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
|
||||
public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory) {
|
||||
return new HopperMenu(syncId, playerInventory, this);
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public void immunize() {
|
||||
+ this.activatedImmunityTick = Math.max(this.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 20);
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index acfb87cd9ea3ca52f1ded99f0f7065fe5abd9af4..09608b495a460af86dabf34ccd8803cf1c0e3769 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -154,6 +154,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
|
||||
public List<ItemEntity> captureDrops;
|
||||
public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
|
||||
+ // Paper start
|
||||
+ public int wakeupInactiveRemainingAnimals;
|
||||
+ public int wakeupInactiveRemainingFlying;
|
||||
+ public int wakeupInactiveRemainingMonsters;
|
||||
+ public int wakeupInactiveRemainingVillagers;
|
||||
+ // Paper end
|
||||
public boolean populating;
|
||||
public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
|
||||
// Paper start - add paper world config
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
index d7b963571c900f0f68005d6954bcd9ef1d9e0b7c..b35f476e26a020cf75e53a5eb488717d996a6935 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
@@ -148,6 +148,10 @@ public class PistonMovingBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
entity.setDeltaMovement(e, g, h);
|
||||
+ // Paper - EAR items stuck in in slime pushed by a piston
|
||||
+ entity.activatedTick = Math.max(entity.activatedTick, net.minecraft.server.MinecraftServer.currentTick + 10);
|
||||
+ entity.activatedImmunityTick = Math.max(entity.activatedImmunityTick, net.minecraft.server.MinecraftServer.currentTick + 10);
|
||||
+ // Paper end
|
||||
break;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
||||
index 9fb9fa62c32445ac3c3883a6433759c86dcfc428..3283ed99c35ffed6805567705e0518d9f84feedc 100644
|
||||
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
||||
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
||||
@@ -1,33 +1,43 @@
|
||||
package org.spigotmc;
|
||||
|
||||
+import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ExperienceOrb;
|
||||
+import net.minecraft.world.entity.FlyingMob;
|
||||
import net.minecraft.world.entity.LightningBolt;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
+import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.PathfinderMob;
|
||||
+import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraft.world.entity.ambient.AmbientCreature;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
+import net.minecraft.world.entity.animal.Bee;
|
||||
import net.minecraft.world.entity.animal.Sheep;
|
||||
+import net.minecraft.world.entity.animal.WaterAnimal;
|
||||
+import net.minecraft.world.entity.animal.horse.Llama;
|
||||
import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
|
||||
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||
import net.minecraft.world.entity.boss.wither.WitherBoss;
|
||||
import net.minecraft.world.entity.item.PrimedTnt;
|
||||
import net.minecraft.world.entity.monster.Creeper;
|
||||
-import net.minecraft.world.entity.monster.Monster;
|
||||
-import net.minecraft.world.entity.monster.Slime;
|
||||
+import net.minecraft.world.entity.monster.Enemy;
|
||||
+import net.minecraft.world.entity.monster.Pillager;
|
||||
import net.minecraft.world.entity.npc.Villager;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.AbstractArrow;
|
||||
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
|
||||
+import net.minecraft.world.entity.projectile.EyeOfEnder;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
import net.minecraft.world.entity.projectile.ThrowableProjectile;
|
||||
import net.minecraft.world.entity.projectile.ThrownTrident;
|
||||
import net.minecraft.world.entity.raid.Raider;
|
||||
+import co.aikar.timings.MinecraftTimings;
|
||||
+import net.minecraft.world.entity.schedule.Activity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
-import co.aikar.timings.MinecraftTimings;
|
||||
|
||||
public class ActivationRange
|
||||
{
|
||||
@@ -44,6 +54,43 @@ public class ActivationRange
|
||||
|
||||
AABB boundingBox = new AABB( 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
+ // Paper start
|
||||
+
|
||||
+ static Activity[] VILLAGER_PANIC_IMMUNITIES = {
|
||||
+ Activity.HIDE,
|
||||
+ Activity.PRE_RAID,
|
||||
+ Activity.RAID,
|
||||
+ Activity.PANIC
|
||||
+ };
|
||||
+
|
||||
+ private static int checkInactiveWakeup(Entity entity) {
|
||||
+ Level world = entity.level();
|
||||
+ SpigotWorldConfig config = world.spigotConfig;
|
||||
+ long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
|
||||
+ if (entity.activationType == ActivationType.VILLAGER) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) {
|
||||
+ world.wakeupInactiveRemainingVillagers--;
|
||||
+ return config.wakeUpInactiveVillagersFor;
|
||||
+ }
|
||||
+ } else if (entity.activationType == ActivationType.ANIMAL) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) {
|
||||
+ world.wakeupInactiveRemainingAnimals--;
|
||||
+ return config.wakeUpInactiveAnimalsFor;
|
||||
+ }
|
||||
+ } else if (entity.activationType == ActivationType.FLYING_MONSTER) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) {
|
||||
+ world.wakeupInactiveRemainingFlying--;
|
||||
+ return config.wakeUpInactiveFlyingFor;
|
||||
+ }
|
||||
+ } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) {
|
||||
+ if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) {
|
||||
+ world.wakeupInactiveRemainingMonsters--;
|
||||
+ return config.wakeUpInactiveMonstersFor;
|
||||
+ }
|
||||
+ }
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
static AABB maxBB = new AABB( 0, 0, 0, 0, 0, 0 );
|
||||
|
||||
@@ -56,10 +103,13 @@ public class ActivationRange
|
||||
*/
|
||||
public static ActivationType initializeEntityActivationType(Entity entity)
|
||||
{
|
||||
+ if (entity instanceof WaterAnimal) { return ActivationType.WATER; } // Paper
|
||||
+ else if (entity instanceof Villager) { return ActivationType.VILLAGER; } // Paper
|
||||
+ else if (entity instanceof FlyingMob && entity instanceof Enemy) { return ActivationType.FLYING_MONSTER; } // Paper - doing & Monster incase Flying no longer includes monster in future
|
||||
if ( entity instanceof Raider )
|
||||
{
|
||||
return ActivationType.RAIDER;
|
||||
- } else if ( entity instanceof Monster || entity instanceof Slime )
|
||||
+ } else if ( entity instanceof Enemy ) // Paper - correct monster check
|
||||
{
|
||||
return ActivationType.MONSTER;
|
||||
} else if ( entity instanceof PathfinderMob || entity instanceof AmbientCreature )
|
||||
@@ -80,10 +130,14 @@ public class ActivationRange
|
||||
*/
|
||||
public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config)
|
||||
{
|
||||
- if ( ( entity.activationType == ActivationType.MISC && config.miscActivationRange == 0 )
|
||||
- || ( entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0 )
|
||||
- || ( entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0 )
|
||||
- || ( entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0 )
|
||||
+ if ( ( entity.activationType == ActivationType.MISC && config.miscActivationRange <= 0 )
|
||||
+ || ( entity.activationType == ActivationType.RAIDER && config.raiderActivationRange <= 0 )
|
||||
+ || ( entity.activationType == ActivationType.ANIMAL && config.animalActivationRange <= 0 )
|
||||
+ || ( entity.activationType == ActivationType.MONSTER && config.monsterActivationRange <= 0 )
|
||||
+ || ( entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0 ) // Paper
|
||||
+ || ( entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0 ) // Paper
|
||||
+ || ( entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0 ) // Paper
|
||||
+ || entity instanceof EyeOfEnder // Paper
|
||||
|| entity instanceof Player
|
||||
|| entity instanceof ThrowableProjectile
|
||||
|| entity instanceof EnderDragon
|
||||
@@ -118,10 +172,25 @@ public class ActivationRange
|
||||
final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
|
||||
final int animalActivationRange = world.spigotConfig.animalActivationRange;
|
||||
final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
|
||||
+ // Paper start
|
||||
+ final int waterActivationRange = world.spigotConfig.waterActivationRange;
|
||||
+ final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange;
|
||||
+ final int villagerActivationRange = world.spigotConfig.villagerActivationRange;
|
||||
+ world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals);
|
||||
+ world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers);
|
||||
+ world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters);
|
||||
+ world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying);
|
||||
+ final ServerChunkCache chunkProvider = (ServerChunkCache) world.getChunkSource();
|
||||
+ // Paper end
|
||||
|
||||
int maxRange = Math.max( monsterActivationRange, animalActivationRange );
|
||||
maxRange = Math.max( maxRange, raiderActivationRange );
|
||||
maxRange = Math.max( maxRange, miscActivationRange );
|
||||
+ // Paper start
|
||||
+ maxRange = Math.max( maxRange, flyingActivationRange );
|
||||
+ maxRange = Math.max( maxRange, waterActivationRange );
|
||||
+ maxRange = Math.max( maxRange, villagerActivationRange );
|
||||
+ // Paper end
|
||||
maxRange = Math.min( ( world.spigotConfig.simulationDistance << 4 ) - 8, maxRange );
|
||||
|
||||
for ( Player player : world.players() )
|
||||
@@ -132,13 +201,30 @@ public class ActivationRange
|
||||
continue;
|
||||
}
|
||||
|
||||
- ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, 256, maxRange );
|
||||
- ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, 256, miscActivationRange );
|
||||
- ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, 256, raiderActivationRange );
|
||||
- ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, 256, animalActivationRange );
|
||||
- ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, 256, monsterActivationRange );
|
||||
+ // Paper start
|
||||
+ int worldHeight = world.getHeight();
|
||||
+ ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );
|
||||
+ ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, worldHeight, miscActivationRange );
|
||||
+ ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, worldHeight, raiderActivationRange );
|
||||
+ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, worldHeight, animalActivationRange );
|
||||
+ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, worldHeight, monsterActivationRange );
|
||||
+ ActivationType.WATER.boundingBox = player.getBoundingBox().inflate( waterActivationRange, worldHeight, waterActivationRange );
|
||||
+ ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate( flyingActivationRange, worldHeight, flyingActivationRange );
|
||||
+ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate( villagerActivationRange, worldHeight, villagerActivationRange );
|
||||
+ // Paper end
|
||||
|
||||
- world.getEntities().get(ActivationRange.maxBB, ActivationRange::activateEntity);
|
||||
+ // Paper start
|
||||
+ java.util.List<Entity> entities = world.getEntities((Entity)null, ActivationRange.maxBB, null);
|
||||
+ boolean tickMarkers = world.paperConfig().entities.markers.tick; // Paper - Configurable marker ticking
|
||||
+ for (Entity entity : entities) {
|
||||
+ // Paper start - Configurable marker ticking
|
||||
+ if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end - Configurable marker ticking
|
||||
+ ActivationRange.activateEntity(entity);
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
MinecraftTimings.entityActivationCheckTimer.stopTiming();
|
||||
}
|
||||
@@ -171,60 +257,118 @@ public class ActivationRange
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
- public static boolean checkEntityImmunities(Entity entity)
|
||||
+ public static int checkEntityImmunities(Entity entity) // Paper - return # of ticks to get immunity
|
||||
{
|
||||
+ // Paper start
|
||||
+ SpigotWorldConfig config = entity.level().spigotConfig;
|
||||
+ int inactiveWakeUpImmunity = checkInactiveWakeup(entity);
|
||||
+ if (inactiveWakeUpImmunity > -1) {
|
||||
+ return inactiveWakeUpImmunity;
|
||||
+ }
|
||||
+ if (entity.getRemainingFireTicks() > 0) {
|
||||
+ return 2;
|
||||
+ }
|
||||
+ if (entity.activatedImmunityTick >= MinecraftServer.currentTick) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ long inactiveFor = MinecraftServer.currentTick - entity.activatedTick;
|
||||
+ // Paper end
|
||||
// quick checks.
|
||||
- if ( entity.wasTouchingWater || entity.getRemainingFireTicks() > 0 )
|
||||
+ if ( (entity.activationType != ActivationType.WATER && entity.wasTouchingWater && entity.isPushedByFluid()) ) // Paper
|
||||
{
|
||||
- return true;
|
||||
+ return 100; // Paper
|
||||
+ }
|
||||
+ // Paper start
|
||||
+ if ( !entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D )
|
||||
+ {
|
||||
+ return 100;
|
||||
}
|
||||
+ // Paper end
|
||||
if ( !( entity instanceof AbstractArrow ) )
|
||||
{
|
||||
- if ( !entity.onGround() || !entity.passengers.isEmpty() || entity.isPassenger() )
|
||||
+ if ( (!entity.onGround() && !(entity instanceof FlyingMob)) ) // Paper - remove passengers logic
|
||||
{
|
||||
- return true;
|
||||
+ return 10; // Paper
|
||||
}
|
||||
} else if ( !( (AbstractArrow) entity ).inGround )
|
||||
{
|
||||
- return true;
|
||||
+ return 1; // Paper
|
||||
}
|
||||
// special cases.
|
||||
if ( entity instanceof LivingEntity )
|
||||
{
|
||||
LivingEntity living = (LivingEntity) entity;
|
||||
- if ( /*TODO: Missed mapping? living.attackTicks > 0 || */ living.hurtTime > 0 || living.activeEffects.size() > 0 )
|
||||
+ if ( living.onClimbable() || living.jumping || living.hurtTime > 0 || living.activeEffects.size() > 0 || living.isFreezing()) // Paper
|
||||
{
|
||||
- return true;
|
||||
+ return 1; // Paper
|
||||
}
|
||||
- if ( entity instanceof PathfinderMob && ( (PathfinderMob) entity ).getTarget() != null )
|
||||
+ if ( entity instanceof Mob && ((Mob) entity ).getTarget() != null) // Paper
|
||||
{
|
||||
- return true;
|
||||
+ return 20; // Paper
|
||||
+ }
|
||||
+ // Paper start
|
||||
+ if (entity instanceof Bee) {
|
||||
+ Bee bee = (Bee)entity;
|
||||
+ BlockPos movingTarget = bee.getMovingTarget();
|
||||
+ if (bee.isAngry() ||
|
||||
+ (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) ||
|
||||
+ (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget))
|
||||
+ ) {
|
||||
+ return 20;
|
||||
+ }
|
||||
}
|
||||
- if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
|
||||
+ if ( entity instanceof Villager ) {
|
||||
+ Brain<Villager> behaviorController = ((Villager) entity).getBrain();
|
||||
+
|
||||
+ if (config.villagersActiveForPanic) {
|
||||
+ for (Activity activity : VILLAGER_PANIC_IMMUNITIES) {
|
||||
+ if (behaviorController.isActive(activity)) {
|
||||
+ return 20*5;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) {
|
||||
+ if (behaviorController.isActive(Activity.WORK)) {
|
||||
+ return config.villagersWorkImmunityFor;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if ( entity instanceof Llama && ( (Llama) entity ).inCaravan() )
|
||||
{
|
||||
- return true;
|
||||
+ return 1;
|
||||
}
|
||||
+ // Paper end
|
||||
if ( entity instanceof Animal )
|
||||
{
|
||||
Animal animal = (Animal) entity;
|
||||
if ( animal.isBaby() || animal.isInLove() )
|
||||
{
|
||||
- return true;
|
||||
+ return 5; // Paper
|
||||
}
|
||||
if ( entity instanceof Sheep && ( (Sheep) entity ).isSheared() )
|
||||
{
|
||||
- return true;
|
||||
+ return 1; // Paper
|
||||
}
|
||||
}
|
||||
if (entity instanceof Creeper && ((Creeper) entity).isIgnited()) { // isExplosive
|
||||
- return true;
|
||||
+ return 20; // Paper
|
||||
+ }
|
||||
+ // Paper start
|
||||
+ if (entity instanceof Mob && ((Mob) entity).targetSelector.hasTasks() ) {
|
||||
+ return 0;
|
||||
}
|
||||
+ if (entity instanceof Pillager) {
|
||||
+ Pillager pillager = (Pillager) entity;
|
||||
+ // TODO:?
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
// SPIGOT-6644: Otherwise the target refresh tick will be missed
|
||||
if (entity instanceof ExperienceOrb) {
|
||||
- return true;
|
||||
+ return 20; // Paper
|
||||
}
|
||||
- return false;
|
||||
+ return -1; // Paper
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,8 +383,19 @@ public class ActivationRange
|
||||
if ( entity instanceof FireworkRocketEntity ) {
|
||||
return true;
|
||||
}
|
||||
+ // Paper start - special case always immunities
|
||||
+ // immunize brand new entities, dead entities, and portal scenarios
|
||||
+ if (entity.defaultActivationState || entity.tickCount < 20*10 || !entity.isAlive() || entity.isInsidePortal || entity.portalCooldown > 0) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // immunize leashed entities
|
||||
+ if (entity instanceof Mob && ((Mob)entity).getLeashHolder() instanceof Player) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
- boolean isActive = entity.activatedTick >= MinecraftServer.currentTick || entity.defaultActivationState;
|
||||
+ boolean isActive = entity.activatedTick >= MinecraftServer.currentTick;
|
||||
+ entity.isTemporarilyActive = false; // Paper
|
||||
|
||||
// Should this entity tick?
|
||||
if ( !isActive )
|
||||
@@ -248,15 +403,19 @@ public class ActivationRange
|
||||
if ( ( MinecraftServer.currentTick - entity.activatedTick - 1 ) % 20 == 0 )
|
||||
{
|
||||
// Check immunities every 20 ticks.
|
||||
- if ( ActivationRange.checkEntityImmunities( entity ) )
|
||||
- {
|
||||
- // Triggered some sort of immunity, give 20 full ticks before we check again.
|
||||
- entity.activatedTick = MinecraftServer.currentTick + 20;
|
||||
+ // Paper start
|
||||
+ int immunity = checkEntityImmunities(entity);
|
||||
+ if (immunity >= 0) {
|
||||
+ entity.activatedTick = MinecraftServer.currentTick + immunity;
|
||||
+ } else {
|
||||
+ entity.isTemporarilyActive = true;
|
||||
}
|
||||
+ // Paper end
|
||||
isActive = true;
|
||||
+
|
||||
}
|
||||
// Add a little performance juice to active entities. Skip 1/4 if not immune.
|
||||
- } else if ( !entity.defaultActivationState && (entity.tickCount + entity.getId()) % 4 == 0 && !ActivationRange.checkEntityImmunities( entity ) ) // Paper - Ensure checking item movement is offset from Spigot's entity activation range check
|
||||
+ } else if ( (entity.tickCount + entity.getId()) % 4 == 0 && ActivationRange.checkEntityImmunities( entity ) < 0 ) // Paper
|
||||
{
|
||||
isActive = false;
|
||||
}
|
||||
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
index 5485df1a1b59e81f4dcedd21dd972e1fd2759573..1cf6d4f854d89c515e48e1fb365eb95ff9340765 100644
|
||||
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
||||
@@ -211,14 +211,60 @@ public class SpigotWorldConfig
|
||||
public int monsterActivationRange = 32;
|
||||
public int raiderActivationRange = 48;
|
||||
public int miscActivationRange = 16;
|
||||
+ // Paper start
|
||||
+ public int flyingMonsterActivationRange = 32;
|
||||
+ public int waterActivationRange = 16;
|
||||
+ public int villagerActivationRange = 32;
|
||||
+ public int wakeUpInactiveAnimals = 4;
|
||||
+ public int wakeUpInactiveAnimalsEvery = 60*20;
|
||||
+ public int wakeUpInactiveAnimalsFor = 5*20;
|
||||
+ public int wakeUpInactiveMonsters = 8;
|
||||
+ public int wakeUpInactiveMonstersEvery = 20*20;
|
||||
+ public int wakeUpInactiveMonstersFor = 5*20;
|
||||
+ public int wakeUpInactiveVillagers = 4;
|
||||
+ public int wakeUpInactiveVillagersEvery = 30*20;
|
||||
+ public int wakeUpInactiveVillagersFor = 5*20;
|
||||
+ public int wakeUpInactiveFlying = 8;
|
||||
+ public int wakeUpInactiveFlyingEvery = 10*20;
|
||||
+ public int wakeUpInactiveFlyingFor = 5*20;
|
||||
+ public int villagersWorkImmunityAfter = 5*20;
|
||||
+ public int villagersWorkImmunityFor = 20;
|
||||
+ public boolean villagersActiveForPanic = true;
|
||||
+ // Paper end
|
||||
public boolean tickInactiveVillagers = true;
|
||||
public boolean ignoreSpectatorActivation = false;
|
||||
private void activationRange()
|
||||
{
|
||||
+ boolean hasAnimalsConfig = config.getInt("entity-activation-range.animals", this.animalActivationRange) != this.animalActivationRange; // Paper
|
||||
this.animalActivationRange = this.getInt( "entity-activation-range.animals", this.animalActivationRange );
|
||||
this.monsterActivationRange = this.getInt( "entity-activation-range.monsters", this.monsterActivationRange );
|
||||
this.raiderActivationRange = this.getInt( "entity-activation-range.raiders", this.raiderActivationRange );
|
||||
this.miscActivationRange = this.getInt( "entity-activation-range.misc", this.miscActivationRange );
|
||||
+ // Paper start
|
||||
+ this.waterActivationRange = this.getInt( "entity-activation-range.water", this.waterActivationRange );
|
||||
+ this.villagerActivationRange = this.getInt( "entity-activation-range.villagers", hasAnimalsConfig ? this.animalActivationRange : this.villagerActivationRange );
|
||||
+ this.flyingMonsterActivationRange = this.getInt( "entity-activation-range.flying-monsters", this.flyingMonsterActivationRange );
|
||||
+
|
||||
+ this.wakeUpInactiveAnimals = this.getInt("entity-activation-range.wake-up-inactive.animals-max-per-tick", this.wakeUpInactiveAnimals);
|
||||
+ this.wakeUpInactiveAnimalsEvery = this.getInt("entity-activation-range.wake-up-inactive.animals-every", this.wakeUpInactiveAnimalsEvery);
|
||||
+ this.wakeUpInactiveAnimalsFor = this.getInt("entity-activation-range.wake-up-inactive.animals-for", this.wakeUpInactiveAnimalsFor);
|
||||
+
|
||||
+ this.wakeUpInactiveMonsters = this.getInt("entity-activation-range.wake-up-inactive.monsters-max-per-tick", this.wakeUpInactiveMonsters);
|
||||
+ this.wakeUpInactiveMonstersEvery = this.getInt("entity-activation-range.wake-up-inactive.monsters-every", this.wakeUpInactiveMonstersEvery);
|
||||
+ this.wakeUpInactiveMonstersFor = this.getInt("entity-activation-range.wake-up-inactive.monsters-for", this.wakeUpInactiveMonstersFor);
|
||||
+
|
||||
+ this.wakeUpInactiveVillagers = this.getInt("entity-activation-range.wake-up-inactive.villagers-max-per-tick", this.wakeUpInactiveVillagers);
|
||||
+ this.wakeUpInactiveVillagersEvery = this.getInt("entity-activation-range.wake-up-inactive.villagers-every", this.wakeUpInactiveVillagersEvery);
|
||||
+ this.wakeUpInactiveVillagersFor = this.getInt("entity-activation-range.wake-up-inactive.villagers-for", this.wakeUpInactiveVillagersFor);
|
||||
+
|
||||
+ this.wakeUpInactiveFlying = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-max-per-tick", this.wakeUpInactiveFlying);
|
||||
+ this.wakeUpInactiveFlyingEvery = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-every", this.wakeUpInactiveFlyingEvery);
|
||||
+ this.wakeUpInactiveFlyingFor = this.getInt("entity-activation-range.wake-up-inactive.flying-monsters-for", this.wakeUpInactiveFlyingFor);
|
||||
+
|
||||
+ this.villagersWorkImmunityAfter = this.getInt( "entity-activation-range.villagers-work-immunity-after", this.villagersWorkImmunityAfter );
|
||||
+ this.villagersWorkImmunityFor = this.getInt( "entity-activation-range.villagers-work-immunity-for", this.villagersWorkImmunityFor );
|
||||
+ this.villagersActiveForPanic = this.getBoolean( "entity-activation-range.villagers-active-for-panic", this.villagersActiveForPanic );
|
||||
+ // Paper end
|
||||
this.tickInactiveVillagers = this.getBoolean( "entity-activation-range.tick-inactive-villagers", this.tickInactiveVillagers );
|
||||
this.ignoreSpectatorActivation = this.getBoolean( "entity-activation-range.ignore-spectators", this.ignoreSpectatorActivation );
|
||||
this.log( "Entity Activation Range: An " + this.animalActivationRange + " / Mo " + this.monsterActivationRange + " / Ra " + this.raiderActivationRange + " / Mi " + this.miscActivationRange + " / Tiv " + this.tickInactiveVillagers + " / Isa " + this.ignoreSpectatorActivation );
|
255
patches/server/0982-Optional-per-player-mob-spawns.patch
Normal file
255
patches/server/0982-Optional-per-player-mob-spawns.patch
Normal file
|
@ -0,0 +1,255 @@
|
|||
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] Optional per player mob spawns
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index c22b40790a28c9a670538a8cc97821b33443845d..79a3410833cb3f2d3a6429f821b524b89e152629 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -291,9 +291,28 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
});
|
||||
}
|
||||
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ public void updatePlayerMobTypeMap(final Entity entity) {
|
||||
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ return;
|
||||
+ }
|
||||
+ int index = entity.getType().getCategory().ordinal();
|
||||
+
|
||||
+ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> inRange =
|
||||
+ this.getNearbyPlayers().getPlayers(entity.chunkPosition(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
|
||||
+ if (inRange == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+ final Object[] backingSet = inRange.getRawData();
|
||||
+ for (int i = 0, len = inRange.size(); i < len; i++) {
|
||||
+ ++((ServerPlayer)backingSet[i]).mobCounts[index];
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
|
||||
- return -1;
|
||||
+ return player.mobCounts[mobCategory.ordinal()];
|
||||
}
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
|
||||
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
|
||||
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index d1728e13a7b649f308bde90ab633c79d86c10822..002aad174fb1781ee963414037f84e5120488592 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -516,7 +516,19 @@ public class ServerChunkCache extends ChunkSource {
|
||||
gameprofilerfiller.popPush("naturalSpawnCount");
|
||||
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
|
||||
int k = this.distanceManager.getNaturalSpawnChunkCount();
|
||||
- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(k, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ int naturalSpawnChunkCount = k;
|
||||
+ NaturalSpawner.SpawnState spawnercreature_d; // moved down
|
||||
+ if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
|
||||
+ // re-set mob counts
|
||||
+ for (ServerPlayer player : this.level.players) {
|
||||
+ Arrays.fill(player.mobCounts, 0);
|
||||
+ }
|
||||
+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
|
||||
+ } else {
|
||||
+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false);
|
||||
+ }
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
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 fc8e9cd699bdefb6ddadc6aa30e6459417c73194..61e256224c6d7c944bee0e11d0edcf05b8a5ecd3 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -271,6 +271,10 @@ public class ServerPlayer extends Player {
|
||||
public boolean queueHealthUpdatePacket;
|
||||
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
|
||||
// Paper end - cancellable death event
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
|
||||
+ public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
|
||||
// CraftBukkit start
|
||||
public CraftPlayer.TransferCookieConnection transferCookieConnection;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
index ea6533c1ac218aa075da3401807a06fcb7892321..0679636530ca1afd077c43b0fa605a2ac74e0522 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -67,6 +67,12 @@ public final class NaturalSpawner {
|
||||
private NaturalSpawner() {}
|
||||
|
||||
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ return createState(spawningChunkCount, entities, chunkSource, densityCapper, false);
|
||||
+ }
|
||||
+
|
||||
+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) {
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
|
||||
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
|
||||
Iterator iterator = entities.iterator();
|
||||
@@ -99,11 +105,16 @@ public final class NaturalSpawner {
|
||||
spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
|
||||
}
|
||||
|
||||
- if (entity instanceof Mob) {
|
||||
+ if (densityCapper != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
|
||||
densityCapper.addMob(chunk.getPos(), enumcreaturetype);
|
||||
}
|
||||
|
||||
object2intopenhashmap.addTo(enumcreaturetype, 1);
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ if (countMobs) {
|
||||
+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity);
|
||||
+ }
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -138,13 +149,35 @@ public final class NaturalSpawner {
|
||||
continue;
|
||||
}
|
||||
|
||||
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) {
|
||||
+ // Paper start - Optional per player mob spawns; only allow spawns upto the limit per chunk and update count afterwards
|
||||
+ int currEntityCount = info.mobCategoryCounts.getInt(enumcreaturetype);
|
||||
+ int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER;
|
||||
+ int difference = k1 - currEntityCount;
|
||||
+
|
||||
+ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ int minDiff = Integer.MAX_VALUE;
|
||||
+ final com.destroystokyo.paper.util.maplist.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
|
||||
+ world.chunkSource.chunkMap.getNearbyPlayers().getPlayers(chunk.getPos(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
|
||||
+ if (inRange != null) {
|
||||
+ final Object[] backingSet = inRange.getRawData();
|
||||
+ for (int k = 0, len = inRange.size(); k < len; k++) {
|
||||
+ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear((net.minecraft.server.level.ServerPlayer)backingSet[k], enumcreaturetype), minDiff);
|
||||
+ }
|
||||
+ }
|
||||
+ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
|
||||
+ }
|
||||
+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) {
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
// CraftBukkit end
|
||||
Objects.requireNonNull(info);
|
||||
NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
|
||||
|
||||
Objects.requireNonNull(info);
|
||||
- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
|
||||
+ difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
|
||||
+ info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum);
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,11 +196,17 @@ public final class NaturalSpawner {
|
||||
// Paper end - Add mobcaps commands
|
||||
|
||||
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
|
||||
+ }
|
||||
+ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
|
||||
|
||||
if (blockposition.getY() >= world.getMinBuildHeight() + 1) {
|
||||
- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner);
|
||||
+ return NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
|
||||
}
|
||||
+ return 0; // Paper - Optional per player mob spawns
|
||||
}
|
||||
|
||||
@VisibleForDebug
|
||||
@@ -178,15 +217,21 @@ public final class NaturalSpawner {
|
||||
});
|
||||
}
|
||||
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
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, Integer.MAX_VALUE, null);
|
||||
+ }
|
||||
+ 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 - Optional per player mob spawns
|
||||
StructureManager structuremanager = world.structureManager();
|
||||
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
|
||||
int i = pos.getY();
|
||||
BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
|
||||
+ int j = 0; // Paper - Optional per player mob spawns; 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;
|
||||
+ //int j = 0; // Paper - Optional per player mob spawns; moved up
|
||||
int k = 0;
|
||||
|
||||
while (k < 3) {
|
||||
@@ -228,14 +273,14 @@ public final class NaturalSpawner {
|
||||
// Paper start - PreCreatureSpawnEvent
|
||||
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
|
||||
if (doSpawning == PreSpawnStatus.ABORT) {
|
||||
- return;
|
||||
+ return j; // Paper - Optional per player mob spawns
|
||||
}
|
||||
if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
|
||||
// Paper end - PreCreatureSpawnEvent
|
||||
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
|
||||
|
||||
if (entityinsentient == null) {
|
||||
- return;
|
||||
+ return j; // Paper - Optional per player mob spawns
|
||||
}
|
||||
|
||||
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
|
||||
@@ -248,10 +293,15 @@ public final class NaturalSpawner {
|
||||
++j;
|
||||
++k1;
|
||||
runner.run(entityinsentient, chunk);
|
||||
+ // Paper start - Optional per player mob spawns
|
||||
+ if (trackEntity != null) {
|
||||
+ trackEntity.accept(entityinsentient);
|
||||
+ }
|
||||
+ // Paper end - Optional per player mob spawns
|
||||
}
|
||||
// CraftBukkit end
|
||||
- if (j >= entityinsentient.getMaxSpawnClusterSize()) {
|
||||
- return;
|
||||
+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns
|
||||
+ return j; // Paper - Optional per player mob spawns
|
||||
}
|
||||
|
||||
if (entityinsentient.isMaxGroupSizeReached(k1)) {
|
||||
@@ -273,6 +323,7 @@ public final class NaturalSpawner {
|
||||
}
|
||||
|
||||
}
|
||||
+ return j; // Paper - Optional per player mob spawns
|
||||
}
|
||||
|
||||
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
||||
@@ -523,7 +574,7 @@ public final class NaturalSpawner {
|
||||
MobCategory enumcreaturetype = entitytypes.getCategory();
|
||||
|
||||
this.mobCategoryCounts.addTo(enumcreaturetype, 1);
|
||||
- this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype);
|
||||
+ if (this.localMobCapCalculator != null) this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype); // Paper - Optional per player mob spawns
|
||||
}
|
||||
|
||||
public int getSpawnableChunkCount() {
|
||||
@@ -539,6 +590,7 @@ public final class NaturalSpawner {
|
||||
int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
||||
// CraftBukkit end
|
||||
|
||||
+ if (this.localMobCapCalculator == null) return this.mobCategoryCounts.getInt(enumcreaturetype) < i; // Paper - Optional per player mob spawns
|
||||
return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
|
||||
}
|
||||
}
|
1638
patches/server/0983-Anti-Xray.patch
Normal file
1638
patches/server/0983-Anti-Xray.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,89 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kickash32 <kickash32@gmail.com>
|
||||
Date: Mon, 5 Apr 2021 01:42:35 -0400
|
||||
Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
|
||||
spawns
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 79a3410833cb3f2d3a6429f821b524b89e152629..2ae2f36ef3d78ee4859c09096d516eeb3ffb02a0 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -309,8 +309,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - per player mob count backoff
|
||||
+ public void updateFailurePlayerMobTypeMap(int chunkX, int chunkZ, net.minecraft.world.entity.MobCategory mobCategory) {
|
||||
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ return;
|
||||
+ }
|
||||
+ int idx = mobCategory.ordinal();
|
||||
+ final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> inRange =
|
||||
+ this.getNearbyPlayers().getPlayersByChunk(chunkX, chunkZ, io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
|
||||
+ if (inRange == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+ final Object[] backingSet = inRange.getRawData();
|
||||
+ for (int i = 0, len = inRange.size(); i < len; i++) {
|
||||
+ ++((ServerPlayer)backingSet[i]).mobBackoffCounts[idx];
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - per player mob count backoff
|
||||
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
|
||||
- return player.mobCounts[mobCategory.ordinal()];
|
||||
+ return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff
|
||||
}
|
||||
// Paper end - Optional per player mob spawns
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 002aad174fb1781ee963414037f84e5120488592..c7b7f153895a4b95b2071a31db00c9c4b69fa094 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -522,7 +522,17 @@ public class ServerChunkCache extends ChunkSource {
|
||||
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
|
||||
// re-set mob counts
|
||||
for (ServerPlayer player : this.level.players) {
|
||||
- Arrays.fill(player.mobCounts, 0);
|
||||
+ // Paper start - per player mob spawning backoff
|
||||
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
|
||||
+ player.mobCounts[ii] = 0;
|
||||
+
|
||||
+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
|
||||
+ if (newBackoff < 0) {
|
||||
+ newBackoff = 0;
|
||||
+ }
|
||||
+ player.mobBackoffCounts[ii] = newBackoff;
|
||||
+ }
|
||||
+ // Paper end - per player mob spawning backoff
|
||||
}
|
||||
spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true);
|
||||
} else {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index 61e256224c6d7c944bee0e11d0edcf05b8a5ecd3..e06b9b0c7b1c3a200b89819e0e521f1c88dc981b 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -275,6 +275,7 @@ public class ServerPlayer extends Player {
|
||||
public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length;
|
||||
public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper
|
||||
// Paper end - Optional per player mob spawns
|
||||
+ public final int[] mobBackoffCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper - per player mob count backoff
|
||||
|
||||
// CraftBukkit start
|
||||
public CraftPlayer.TransferCookieConnection transferCookieConnection;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 0679636530ca1afd077c43b0fa605a2ac74e0522..ed8032495af9ce9c23419224814b8d27e4a97c17 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -272,6 +272,11 @@ public final class NaturalSpawner {
|
||||
|
||||
// Paper start - PreCreatureSpawnEvent
|
||||
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
|
||||
+ // Paper start - per player mob count backoff
|
||||
+ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
|
||||
+ world.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(blockposition_mutableblockposition.getX() >> 4, blockposition_mutableblockposition.getZ() >> 4, group);
|
||||
+ }
|
||||
+ // Paper end - per player mob count backoff
|
||||
if (doSpawning == PreSpawnStatus.ABORT) {
|
||||
return j; // Paper - Optional per player mob spawns
|
||||
}
|
109
patches/server/0985-Optimize-Collision-to-not-load-chunks.patch
Normal file
109
patches/server/0985-Optimize-Collision-to-not-load-chunks.patch
Normal file
|
@ -0,0 +1,109 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 2 Apr 2020 02:37:57 -0400
|
||||
Subject: [PATCH] Optimize Collision to not load chunks
|
||||
|
||||
The collision code takes an AABB and generates a cuboid of checks rather
|
||||
than a cylinder, so at high velocity this can generate a lot of chunk checks.
|
||||
|
||||
Treat an unloaded chunk as a collision for entities, and also for players if
|
||||
the "prevent moving into unloaded chunks" setting is enabled.
|
||||
|
||||
If that serting is not enabled, collisions will be ignored for players, since
|
||||
movement will load only the chunk the player enters anyways and avoids loading
|
||||
massive amounts of surrounding chunks due to large AABB lookups.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index 5b031c7c13fb2c2e593eec4c8f6a973a3758a60b..65803c0927103e3ae63d8d8616f42090e736508b 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -934,6 +934,7 @@ public abstract class PlayerList {
|
||||
entityplayer1.setShiftKeyDown(false);
|
||||
entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
|
||||
+ worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
|
||||
while (avoidSuffocation && !worldserver1.noCollision((Entity) entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
|
||||
// CraftBukkit end
|
||||
entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index ab529d8c2c10fffd58fe4754882b558eed239fbd..1a8a52a77fa0b7891220bf0f04688f5b7eaa9bd7 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -245,6 +245,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
||||
|
||||
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
|
||||
+ public boolean collisionLoadChunks = false; // Paper
|
||||
private CraftEntity bukkitEntity;
|
||||
|
||||
public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
|
||||
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
|
||||
index 1c10835b59aaefa3a65ff64f784620bdc54ddcdc..cd89623a44f02d7db77f0d0f87545cf80841f403 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
|
||||
@@ -66,18 +66,37 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
while (this.cursor.advance()) {
|
||||
- int i = this.cursor.nextX();
|
||||
- int j = this.cursor.nextY();
|
||||
- int k = this.cursor.nextZ();
|
||||
+ int i = this.cursor.nextX(); final int x = i; // Paper - OBFHELPER
|
||||
+ int j = this.cursor.nextY(); final int y = j; // Paper - OBFHELPER
|
||||
+ int k = this.cursor.nextZ(); final int z = k; // Paper - OBFHELPER
|
||||
int l = this.cursor.getNextType();
|
||||
if (l != 3) {
|
||||
- BlockGetter blockGetter = this.getChunk(i, k);
|
||||
- if (blockGetter != null) {
|
||||
- this.pos.set(i, j, k);
|
||||
- BlockState blockState = blockGetter.getBlockState(this.pos);
|
||||
- if ((!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos))
|
||||
- && (l != 1 || blockState.hasLargeCollisionShape())
|
||||
- && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) {
|
||||
+ // Paper start - ensure we don't load chunks
|
||||
+ // BlockGetter blockGetter = this.getChunk(i, k);
|
||||
+ if (true) {
|
||||
+ final @Nullable Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
|
||||
+ boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
|
||||
+ this.pos.set(x, y, z);
|
||||
+ BlockState blockState;
|
||||
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
|
||||
+ BlockGetter blockGetter = this.getChunk(x, z);
|
||||
+ if (blockGetter == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ blockState = blockGetter.getBlockState(this.pos);
|
||||
+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
|
||||
+ blockState = this.collisionGetter.getBlockState(this.pos);
|
||||
+ } else {
|
||||
+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
|
||||
+ }
|
||||
+ if (blockState == null) {
|
||||
+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
|
||||
+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (/*(!this.onlySuffocatingBlocks || blockState.isSuffocating(blockGetter, this.pos)) &&*/ (l != 1 || blockState.hasLargeCollisionShape()) && (l != 2 || blockState.is(Blocks.MOVING_PISTON))) { // Paper - onlySuffocatingBlocks is only true on the client, so we don't care about it here
|
||||
+ // Paper end
|
||||
VoxelShape voxelShape = blockState.getCollisionShape(this.collisionGetter, this.pos, this.context);
|
||||
if (voxelShape == Shapes.block()) {
|
||||
if (this.box.intersects((double)i, (double)j, (double)k, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0)) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
|
||||
index e57cb7fe53e915d24246e44c7f49971f5b2ab2cf..1ad0c976c6e2d6d31397dff850a9de7c16d16fba 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
|
||||
@@ -44,11 +44,13 @@ public interface CollisionGetter extends BlockGetter {
|
||||
}
|
||||
|
||||
default boolean noCollision(@Nullable Entity entity, AABB box) {
|
||||
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
|
||||
for (VoxelShape voxelShape : this.getBlockCollisions(entity, box)) {
|
||||
if (!voxelShape.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
|
||||
|
||||
if (!this.getEntityCollisions(entity, box).isEmpty()) {
|
||||
return false;
|
|
@ -0,0 +1,160 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Mon, 6 Apr 2020 17:53:29 -0700
|
||||
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
|
||||
|
||||
Optimise the stream.anyMatch statement to move to a bitset
|
||||
where we can replace the call with a single bitwise operation.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
index 16f9a98b8a939e5ca7e2dc04f87134a7ed66736b..dd423302b1baa64ef86ded87a29659342e28c142 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
@@ -4,7 +4,16 @@ import java.util.EnumSet;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public abstract class Goal {
|
||||
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
|
||||
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
|
||||
+
|
||||
+ // Paper start - remove streams from pathfindergoalselector; make sure types are not empty
|
||||
+ public Goal() {
|
||||
+ if (this.goalTypes.size() == 0) {
|
||||
+ this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
|
||||
public abstract boolean canUse();
|
||||
|
||||
@@ -30,8 +39,13 @@ public abstract class Goal {
|
||||
}
|
||||
|
||||
public void setFlags(EnumSet<Goal.Flag> controls) {
|
||||
- this.flags.clear();
|
||||
- this.flags.addAll(controls);
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ this.goalTypes.clear();
|
||||
+ this.goalTypes.addAll(controls);
|
||||
+ if (this.goalTypes.size() == 0) {
|
||||
+ this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,8 +53,10 @@ public abstract class Goal {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
- public EnumSet<Goal.Flag> getFlags() {
|
||||
- return this.flags;
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
|
||||
+ return this.goalTypes;
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
protected int adjustedTickDelay(int ticks) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
index 074ef807258139f818e30494126585262c2f33c0..74d4f653d5c7f1923c59019effd78337402f7025 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
|
||||
@@ -25,7 +25,8 @@ public class GoalSelector {
|
||||
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
|
||||
private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
|
||||
private final Supplier<ProfilerFiller> profiler;
|
||||
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
|
||||
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
|
||||
private int curRate;
|
||||
|
||||
public GoalSelector(Supplier<ProfilerFiller> profiler) {
|
||||
@@ -65,18 +66,18 @@ public class GoalSelector {
|
||||
this.availableGoals.removeIf(wrappedGoalx -> wrappedGoalx.getGoal() == goal);
|
||||
}
|
||||
|
||||
- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
|
||||
- for (Goal.Flag flag : goal.getFlags()) {
|
||||
- if (controls.contains(flag)) {
|
||||
- return true;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
+ // Paper start
|
||||
+ private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
|
||||
+ return goal.getFlags().hasCommonElements(controls);
|
||||
}
|
||||
|
||||
private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) {
|
||||
- for (Goal.Flag flag : goal.getFlags()) {
|
||||
+ long flagIterator = goal.getFlags().getBackingSet();
|
||||
+ int wrappedGoalSize = goal.getFlags().size();
|
||||
+ for (int i = 0; i < wrappedGoalSize; ++i) {
|
||||
+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
|
||||
+ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator);
|
||||
+ // Paper end
|
||||
if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) {
|
||||
return false;
|
||||
}
|
||||
@@ -90,7 +91,7 @@ public class GoalSelector {
|
||||
profilerFiller.push("goalCleanup");
|
||||
|
||||
for (WrappedGoal wrappedGoal : this.availableGoals) {
|
||||
- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) {
|
||||
+ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams
|
||||
wrappedGoal.stop();
|
||||
}
|
||||
}
|
||||
@@ -100,11 +101,14 @@ public class GoalSelector {
|
||||
profilerFiller.push("goalUpdate");
|
||||
|
||||
for (WrappedGoal wrappedGoal2 : this.availableGoals) {
|
||||
- if (!wrappedGoal2.isRunning()
|
||||
- && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags)
|
||||
- && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags)
|
||||
- && wrappedGoal2.canUse()) {
|
||||
- for (Goal.Flag flag : wrappedGoal2.getFlags()) {
|
||||
+ // Paper start
|
||||
+ if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
|
||||
+ long flagIterator = wrappedGoal2.getFlags().getBackingSet();
|
||||
+ int wrappedGoalSize = wrappedGoal2.getFlags().size();
|
||||
+ for (int i = 0; i < wrappedGoalSize; ++i) {
|
||||
+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
|
||||
+ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator);
|
||||
+ // Paper end
|
||||
WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
|
||||
wrappedGoal3.stop();
|
||||
this.lockedFlags.put(flag, wrappedGoal2);
|
||||
@@ -136,11 +140,11 @@ public class GoalSelector {
|
||||
}
|
||||
|
||||
public void disableControlFlag(Goal.Flag control) {
|
||||
- this.disabledFlags.add(control);
|
||||
+ this.goalTypes.add(control); // Paper - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public void enableControlFlag(Goal.Flag control) {
|
||||
- this.disabledFlags.remove(control);
|
||||
+ this.goalTypes.remove(control); // Paper - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public void setControlFlag(Goal.Flag control, boolean enabled) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
index b02d3deb550830245c8945ef17d3073ea930fdda..65ccdbaa5230c02d44a5959bca0f6fc30237a6fd 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
@@ -69,8 +69,10 @@ public class WrappedGoal extends Goal {
|
||||
}
|
||||
|
||||
@Override
|
||||
- public EnumSet<Goal.Flag> getFlags() {
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
|
||||
return this.goal.getFlags();
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
58
patches/server/0987-Entity-load-save-limit-per-chunk.patch
Normal file
58
patches/server/0987-Entity-load-save-limit-per-chunk.patch
Normal file
|
@ -0,0 +1,58 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Wed, 18 Nov 2020 20:52:25 -0800
|
||||
Subject: [PATCH] Entity load/save limit per chunk
|
||||
|
||||
Adds a config option to limit the number of entities saved and loaded
|
||||
to a chunk. The default values of -1 disable the limit. Although
|
||||
defaults are only included for certain entites, this allows setting
|
||||
limits for any entity type.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
|
||||
index d2503224a1697636b3f9c26126d6bb201143076a..9abc274049ccc7b77286676313f7010e76aa199e 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
|
||||
@@ -649,9 +649,20 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
||||
final Spliterator<? extends Tag> spliterator = entityNbtList.spliterator();
|
||||
|
||||
return StreamSupport.stream(new Spliterator<Entity>() {
|
||||
+ final java.util.Map<EntityType<?>, Integer> loadedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
|
||||
public boolean tryAdvance(Consumer<? super Entity> consumer) {
|
||||
return spliterator.tryAdvance((nbtbase) -> {
|
||||
EntityType.loadEntityRecursive((CompoundTag) nbtbase, world, (entity) -> {
|
||||
+ // Paper start - Entity load/save limit per chunk
|
||||
+ final EntityType<?> entityType = entity.getType();
|
||||
+ final int saveLimit = world.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
|
||||
+ if (saveLimit > -1) {
|
||||
+ if (this.loadedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ this.loadedEntityCounts.merge(entityType, 1, Integer::sum);
|
||||
+ }
|
||||
+ // Paper end - Entity load/save limit per chunk
|
||||
consumer.accept(entity);
|
||||
return entity;
|
||||
});
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
||||
index 9fdf8f857a5f9b231c6d0633eaba498244214f74..bee39dee1b96023c907407877aedf3aafaf5e1b8 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
|
||||
@@ -105,7 +105,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
|
||||
}
|
||||
|
||||
ListTag listTag = new ListTag();
|
||||
+ final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper - Entity load/save limit per chunk
|
||||
entities.forEach((entity) -> { // diff here: use entities parameter
|
||||
+ // Paper start - Entity load/save limit per chunk
|
||||
+ final EntityType<?> entityType = entity.getType();
|
||||
+ final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
|
||||
+ if (saveLimit > -1) {
|
||||
+ if (savedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
|
||||
+ return;
|
||||
+ }
|
||||
+ savedEntityCounts.merge(entityType, 1, Integer::sum);
|
||||
+ }
|
||||
+ // Paper end - Entity load/save limit per chunk
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
if (entity.save(compoundTag)) {
|
||||
listTag.add(compoundTag);
|
Loading…
Add table
Add a link
Reference in a new issue