From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar <aikar@aikar.co> Date: Sat, 13 Sep 2014 23:14:43 -0400 Subject: [PATCH] Configurable Keep Spawn Loaded range per world This lets you disable it for some worlds and lower it for others. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { } } + public short keepLoadedRange; + private void keepLoadedRange() { + keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 10)) * 16); + log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16)); + } + private boolean getBoolean(String path, boolean def) { config.addDefault("world-settings.default." + path, def); return config.getBoolean("world-settings." + worldName + "." + path, config.getBoolean("world-settings.default." + path)); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa // CraftBukkit start public void prepareLevels(ChunkProgressListener worldloadlistener, ServerLevel worldserver) { - if (!worldserver.getWorld().getKeepSpawnInMemory()) { - return; - } + ServerChunkCache chunkproviderserver = worldserver.getChunkSource(); // Paper // WorldServer worldserver = this.overworld(); this.forceTicks = true; // CraftBukkit end + if (worldserver.getWorld().getKeepSpawnInMemory()) { // Paper MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.dimension().location()); BlockPos blockposition = worldserver.getSharedSpawnPos(); worldloadlistener.updateSpawnPos(new ChunkPos(blockposition)); - ServerChunkCache chunkproviderserver = worldserver.getChunkSource(); + //ChunkProviderServer chunkproviderserver = worldserver.getChunkProvider(); // Paper - move up chunkproviderserver.getLightEngine().setTaskPerBatch(500); this.nextTickTime = Util.getMillis(); - chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(blockposition), 11, Unit.INSTANCE); - - while (chunkproviderserver.getTickingGenerated() != 441) { - // CraftBukkit start - // this.nextTickTime = SystemUtils.getMillis() + 10L; - this.executeModerately(); - // CraftBukkit end - } + // Paper start - configurable spawn reason + int radiusBlocks = worldserver.paperConfig.keepLoadedRange; + int radiusChunks = radiusBlocks / 16 + ((radiusBlocks & 15) != 0 ? 1 : 0); + int totalChunks = ((radiusChunks) * 2 + 1); + totalChunks *= totalChunks; + worldloadlistener.setChunkRadius(radiusBlocks / 16); + + worldserver.addTicketsForSpawn(radiusBlocks, blockposition); + // Paper end // CraftBukkit start // this.nextTickTime = SystemUtils.getMillis() + 10L; this.executeModerately(); // Iterator iterator = this.levels.values().iterator(); + } if (true) { ServerLevel worldserver1 = worldserver; @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa // this.nextTickTime = SystemUtils.getMillis() + 10L; this.executeModerately(); // CraftBukkit end - worldloadlistener.stop(); + if (worldserver.getWorld().getKeepSpawnInMemory()) worldloadlistener.stop(); // Paper chunkproviderserver.getLightEngine().setTaskPerBatch(5); // CraftBukkit start // this.updateMobSpawningFlags(); diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -0,0 +0,0 @@ import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MCUtil; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerScoreboard; import net.minecraft.server.level.progress.ChunkProgressListener; @@ -0,0 +0,0 @@ public class ServerLevel extends Level implements WorldGenLevel { return ((MapIndex) this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex::load, MapIndex::new, "idcounts")).getFreeAuxValueForMap(); } + // Paper start - helper function for configurable spawn radius + public void addTicketsForSpawn(int radiusInBlocks, BlockPos spawn) { + // In order to respect vanilla behavior, which is ensuring everything but the spawn border can tick, we add tickets + // with level 31 for the non-border spawn chunks + ServerChunkCache chunkproviderserver = this.getChunkSource(); + int tickRadius = radiusInBlocks - 16; + + // add ticking chunks + for (int x = -tickRadius; x <= tickRadius; x += 16) { + for (int z = -tickRadius; z <= tickRadius; z += 16) { + // radius of 2 will have the current chunk be level 31 + chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, z)), 2, Unit.INSTANCE); + } + } + + // add border chunks + + // add border along x axis (including corner chunks) + for (int x = -radiusInBlocks; x <= radiusInBlocks; x += 16) { + // top + chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, radiusInBlocks)), 1, Unit.INSTANCE); // level 32 + // bottom + chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, -radiusInBlocks)), 1, Unit.INSTANCE); // level 32 + } + + // add border along z axis (excluding corner chunks) + for (int z = -radiusInBlocks + 16; z < radiusInBlocks; z += 16) { + // right + chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32 + // left + chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(-radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32 + } + } + public void removeTicketsForSpawn(int radiusInBlocks, BlockPos spawn) { + // In order to respect vanilla behavior, which is ensuring everything but the spawn border can tick, we added tickets + // with level 31 for the non-border spawn chunks + ServerChunkCache chunkproviderserver = this.getChunkSource(); + int tickRadius = radiusInBlocks - 16; + + // remove ticking chunks + for (int x = -tickRadius; x <= tickRadius; x += 16) { + for (int z = -tickRadius; z <= tickRadius; z += 16) { + // radius of 2 will have the current chunk be level 31 + chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, z)), 2, Unit.INSTANCE); + } + } + + // remove border chunks + + // remove border along x axis (including corner chunks) + for (int x = -radiusInBlocks; x <= radiusInBlocks; x += 16) { + // top + chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, radiusInBlocks)), 1, Unit.INSTANCE); // level 32 + // bottom + chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, -radiusInBlocks)), 1, Unit.INSTANCE); // level 32 + } + + // remove border along z axis (excluding corner chunks) + for (int z = -radiusInBlocks + 16; z < radiusInBlocks; z += 16) { + // right + chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32 + // left + chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(-radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32 + } + } + // Paper end + public void setDefaultSpawnPos(BlockPos pos, float angle) { - ChunkPos chunkcoordintpair = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn())); + // Paper - configurable spawn radius + BlockPos prevSpawn = this.getSharedSpawnPos(); + //ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(new BlockPosition(this.worldData.a(), 0, this.worldData.c())); this.levelData.setSpawn(pos, angle); - this.getChunkSource().removeRegionTicket(TicketType.START, chunkcoordintpair, 11, Unit.INSTANCE); - this.getChunkSource().addRegionTicket(TicketType.START, new ChunkPos(pos), 11, Unit.INSTANCE); + if (this.keepSpawnInMemory) { + // if this keepSpawnInMemory is false a plugin has already removed our tickets, do not re-add + this.removeTicketsForSpawn(this.paperConfig.keepLoadedRange, prevSpawn); + this.addTicketsForSpawn(this.paperConfig.keepLoadedRange, pos); + } this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(pos, angle)); } diff --git a/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java b/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java +++ b/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java @@ -0,0 +0,0 @@ public interface ChunkProgressListener { void start(); void stop(); + + void setChunkRadius(int radius); // Paper - allow changing chunk radius } diff --git a/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java b/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java +++ b/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java @@ -0,0 +0,0 @@ import org.apache.logging.log4j.Logger; public class LoggerChunkProgressListener implements ChunkProgressListener { private static final Logger LOGGER = LogManager.getLogger(); - private final int maxCount; + private int maxCount; // Paper - remove final private int count; private long startTime; private long nextTickTime = Long.MAX_VALUE; public LoggerChunkProgressListener(int radius) { + // Paper start - Allow changing radius later for configurable spawn patch + this.setChunkRadius(radius); // Move to method + } + + @Override + public void setChunkRadius(int radius) { + // Paper end int i = radius * 2 + 1; this.maxCount = i * i; } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World { @Override public void setKeepSpawnInMemory(boolean keepLoaded) { - world.keepSpawnInMemory = keepLoaded; + // Paper start - Configurable spawn radius + if (keepLoaded == world.keepSpawnInMemory) { + // do nothing, nothing has changed + return; + } + this.world.keepSpawnInMemory = keepLoaded; // Grab the worlds spawn chunk BlockPos chunkcoordinates = this.world.getSharedSpawnPos(); if (keepLoaded) { - this.world.getChunkSource().addRegionTicket(TicketType.START, new ChunkPos(chunkcoordinates), 11, Unit.INSTANCE); + this.world.addTicketsForSpawn(this.world.paperConfig.keepLoadedRange, chunkcoordinates); } else { - // TODO: doesn't work well if spawn changed.... - this.world.getChunkSource().removeRegionTicket(TicketType.START, new ChunkPos(chunkcoordinates), 11, Unit.INSTANCE); + // TODO: doesn't work well if spawn changed.... // Paper - resolved + this.world.removeTicketsForSpawn(this.world.paperConfig.keepLoadedRange, chunkcoordinates); } + // Paper end } @Override