diff --git a/patches/unapplied/api/Add-more-WanderingTrader-API.patch b/patches/api/Add-more-WanderingTrader-API.patch similarity index 100% rename from patches/unapplied/api/Add-more-WanderingTrader-API.patch rename to patches/api/Add-more-WanderingTrader-API.patch diff --git a/patches/unapplied/api/Added-PlayerBedFailEnterEvent.patch b/patches/api/Added-PlayerBedFailEnterEvent.patch similarity index 100% rename from patches/unapplied/api/Added-PlayerBedFailEnterEvent.patch rename to patches/api/Added-PlayerBedFailEnterEvent.patch diff --git a/patches/unapplied/api/Added-PlayerDeepSleepEvent.patch b/patches/api/Added-PlayerDeepSleepEvent.patch similarity index 100% rename from patches/unapplied/api/Added-PlayerDeepSleepEvent.patch rename to patches/api/Added-PlayerDeepSleepEvent.patch diff --git a/patches/unapplied/api/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch b/patches/api/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch similarity index 100% rename from patches/unapplied/api/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch rename to patches/api/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch diff --git a/patches/unapplied/api/Expose-protocol-version.patch b/patches/api/Expose-protocol-version.patch similarity index 100% rename from patches/unapplied/api/Expose-protocol-version.patch rename to patches/api/Expose-protocol-version.patch diff --git a/patches/unapplied/api/Introduce-beacon-activation-deactivation-events.patch b/patches/api/Introduce-beacon-activation-deactivation-events.patch similarity index 100% rename from patches/unapplied/api/Introduce-beacon-activation-deactivation-events.patch rename to patches/api/Introduce-beacon-activation-deactivation-events.patch diff --git a/patches/unapplied/api/More-World-API.patch b/patches/api/More-World-API.patch similarity index 100% rename from patches/unapplied/api/More-World-API.patch rename to patches/api/More-World-API.patch diff --git a/patches/unapplied/api/PlayerMoveEvent-Improvements.patch b/patches/api/PlayerMoveEvent-Improvements.patch similarity index 100% rename from patches/unapplied/api/PlayerMoveEvent-Improvements.patch rename to patches/api/PlayerMoveEvent-Improvements.patch diff --git a/patches/unapplied/api/add-RespawnFlags-to-PlayerRespawnEvent.patch b/patches/api/add-RespawnFlags-to-PlayerRespawnEvent.patch similarity index 100% rename from patches/unapplied/api/add-RespawnFlags-to-PlayerRespawnEvent.patch rename to patches/api/add-RespawnFlags-to-PlayerRespawnEvent.patch diff --git a/patches/unapplied/api/add-consumeFuel-to-FurnaceBurnEvent.patch b/patches/api/add-consumeFuel-to-FurnaceBurnEvent.patch similarity index 100% rename from patches/unapplied/api/add-consumeFuel-to-FurnaceBurnEvent.patch rename to patches/api/add-consumeFuel-to-FurnaceBurnEvent.patch diff --git a/patches/unapplied/api/add-get-set-drop-chance-to-EntityEquipment.patch b/patches/api/add-get-set-drop-chance-to-EntityEquipment.patch similarity index 100% rename from patches/unapplied/api/add-get-set-drop-chance-to-EntityEquipment.patch rename to patches/api/add-get-set-drop-chance-to-EntityEquipment.patch diff --git a/patches/unapplied/api/add-isDeeplySleeping-to-HumanEntity.patch b/patches/api/add-isDeeplySleeping-to-HumanEntity.patch similarity index 100% rename from patches/unapplied/api/add-isDeeplySleeping-to-HumanEntity.patch rename to patches/api/add-isDeeplySleeping-to-HumanEntity.patch diff --git a/patches/unapplied/server/Add-Channel-initialization-listeners.patch b/patches/server/Add-Channel-initialization-listeners.patch similarity index 100% rename from patches/unapplied/server/Add-Channel-initialization-listeners.patch rename to patches/server/Add-Channel-initialization-listeners.patch diff --git a/patches/unapplied/server/Add-bypass-host-check.patch b/patches/server/Add-bypass-host-check.patch similarity index 100% rename from patches/unapplied/server/Add-bypass-host-check.patch rename to patches/server/Add-bypass-host-check.patch diff --git a/patches/unapplied/server/Add-more-WanderingTrader-API.patch b/patches/server/Add-more-WanderingTrader-API.patch similarity index 100% rename from patches/unapplied/server/Add-more-WanderingTrader-API.patch rename to patches/server/Add-more-WanderingTrader-API.patch diff --git a/patches/unapplied/server/Added-PlayerBedFailEnterEvent.patch b/patches/server/Added-PlayerBedFailEnterEvent.patch similarity index 100% rename from patches/unapplied/server/Added-PlayerBedFailEnterEvent.patch rename to patches/server/Added-PlayerBedFailEnterEvent.patch diff --git a/patches/unapplied/server/Added-PlayerDeepSleepEvent.patch b/patches/server/Added-PlayerDeepSleepEvent.patch similarity index 100% rename from patches/unapplied/server/Added-PlayerDeepSleepEvent.patch rename to patches/server/Added-PlayerDeepSleepEvent.patch diff --git a/patches/unapplied/server/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch b/patches/server/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch similarity index 100% rename from patches/unapplied/server/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch rename to patches/server/Allow-for-Component-suggestion-tooltips-in-AsyncTabC.patch diff --git a/patches/unapplied/server/Enhance-console-tab-completions-for-brigadier-comman.patch b/patches/server/Enhance-console-tab-completions-for-brigadier-comman.patch similarity index 98% rename from patches/unapplied/server/Enhance-console-tab-completions-for-brigadier-comman.patch rename to patches/server/Enhance-console-tab-completions-for-brigadier-comman.patch index fab708b772..9929923c33 100644 --- a/patches/unapplied/server/Enhance-console-tab-completions-for-brigadier-comman.patch +++ b/patches/server/Enhance-console-tab-completions-for-brigadier-comman.patch @@ -9,17 +9,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +0,0 @@ public class PaperConfig { - private static void fixEntityPositionDesync() { fixEntityPositionDesync = getBoolean("settings.fix-entity-position-desync", fixEntityPositionDesync); } -+ + + public static boolean enableBrigadierConsoleHighlighting = true; + public static boolean enableBrigadierConsoleCompletions = true; + private static void consoleSettings() { + enableBrigadierConsoleHighlighting = getBoolean("settings.console.enable-brigadier-highlighting", enableBrigadierConsoleHighlighting); + enableBrigadierConsoleCompletions = getBoolean("settings.console.enable-brigadier-completions", enableBrigadierConsoleCompletions); + } - } ++ + public static void registerCommands() { + for (Map.Entry entry : commands.entrySet()) { + MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Paper", entry.getValue()); diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java diff --git a/patches/unapplied/server/Expose-protocol-version.patch b/patches/server/Expose-protocol-version.patch similarity index 100% rename from patches/unapplied/server/Expose-protocol-version.patch rename to patches/server/Expose-protocol-version.patch diff --git a/patches/unapplied/server/Fix-PlayerItemConsumeEvent-cancelling-properly.patch b/patches/server/Fix-PlayerItemConsumeEvent-cancelling-properly.patch similarity index 100% rename from patches/unapplied/server/Fix-PlayerItemConsumeEvent-cancelling-properly.patch rename to patches/server/Fix-PlayerItemConsumeEvent-cancelling-properly.patch diff --git a/patches/unapplied/server/Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch b/patches/server/Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch similarity index 100% rename from patches/unapplied/server/Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch rename to patches/server/Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch diff --git a/patches/unapplied/server/Fix-checkReach-check-for-Shulker-boxes.patch b/patches/server/Fix-checkReach-check-for-Shulker-boxes.patch similarity index 100% rename from patches/unapplied/server/Fix-checkReach-check-for-Shulker-boxes.patch rename to patches/server/Fix-checkReach-check-for-Shulker-boxes.patch diff --git a/patches/unapplied/server/Fix-duplicating-give-items-on-item-drop-cancel.patch b/patches/server/Fix-duplicating-give-items-on-item-drop-cancel.patch similarity index 100% rename from patches/unapplied/server/Fix-duplicating-give-items-on-item-drop-cancel.patch rename to patches/server/Fix-duplicating-give-items-on-item-drop-cancel.patch diff --git a/patches/unapplied/server/Implement-methods-to-convert-between-Component-and-B.patch b/patches/server/Implement-methods-to-convert-between-Component-and-B.patch similarity index 100% rename from patches/unapplied/server/Implement-methods-to-convert-between-Component-and-B.patch rename to patches/server/Implement-methods-to-convert-between-Component-and-B.patch diff --git a/patches/unapplied/server/Introduce-beacon-activation-deactivation-events.patch b/patches/server/Introduce-beacon-activation-deactivation-events.patch similarity index 100% rename from patches/unapplied/server/Introduce-beacon-activation-deactivation-events.patch rename to patches/server/Introduce-beacon-activation-deactivation-events.patch diff --git a/patches/unapplied/server/More-World-API.patch b/patches/server/More-World-API.patch similarity index 100% rename from patches/unapplied/server/More-World-API.patch rename to patches/server/More-World-API.patch diff --git a/patches/unapplied/server/Send-empty-commands-if-tab-completion-is-disabled.patch b/patches/server/Send-empty-commands-if-tab-completion-is-disabled.patch similarity index 100% rename from patches/unapplied/server/Send-empty-commands-if-tab-completion-is-disabled.patch rename to patches/server/Send-empty-commands-if-tab-completion-is-disabled.patch diff --git a/patches/unapplied/server/Set-area-affect-cloud-rotation.patch b/patches/server/Set-area-affect-cloud-rotation.patch similarity index 100% rename from patches/unapplied/server/Set-area-affect-cloud-rotation.patch rename to patches/server/Set-area-affect-cloud-rotation.patch diff --git a/patches/unapplied/server/add-RespawnFlags-to-PlayerRespawnEvent.patch b/patches/server/add-RespawnFlags-to-PlayerRespawnEvent.patch similarity index 77% rename from patches/unapplied/server/add-RespawnFlags-to-PlayerRespawnEvent.patch rename to patches/server/add-RespawnFlags-to-PlayerRespawnEvent.patch index 91ac7038ed..84cd8ae4e1 100644 --- a/patches/unapplied/server/add-RespawnFlags-to-PlayerRespawnEvent.patch +++ b/patches/server/add-RespawnFlags-to-PlayerRespawnEvent.patch @@ -13,7 +13,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 if (this.player.wonGame) { this.player.wonGame = false; - this.player = this.server.getPlayerList().respawn(this.player, true); -+ this.player = this.server.getPlayerList().moveToWorld(this.player, this.server.getLevel(this.player.getRespawnDimension()), true, null, true, org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag.END_PORTAL); // Paper - add isEndCreditsRespawn argument ++ this.player = this.server.getPlayerList().respawn(this.player, this.server.getLevel(this.player.getRespawnDimension()), true, null, true, org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag.END_PORTAL); // Paper - add isEndCreditsRespawn argument CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, Level.END, Level.OVERWORLD); } else { if (this.player.getHealth() > 0.0F) { @@ -24,12 +24,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public abstract class PlayerList { } - public ServerPlayer moveToWorld(ServerPlayer entityplayer, ServerLevel worldserver, boolean flag, Location location, boolean avoidSuffocation) { + public ServerPlayer respawn(ServerPlayer entityplayer, ServerLevel worldserver, boolean flag, Location location, boolean avoidSuffocation) { + // Paper start -+ return moveToWorld(entityplayer, worldserver, flag, location, avoidSuffocation, new org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag[0]); ++ return respawn(entityplayer, worldserver, flag, location, avoidSuffocation, new org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag[0]); + } + -+ public ServerPlayer moveToWorld(ServerPlayer entityplayer, ServerLevel worldserver, boolean flag, Location location, boolean avoidSuffocation, org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag...respawnFlags) { ++ public ServerPlayer respawn(ServerPlayer entityplayer, ServerLevel worldserver, boolean flag, Location location, boolean avoidSuffocation, org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag...respawnFlags) { + // Paper end entityplayer.stopRiding(); // CraftBukkit this.players.remove(entityplayer); diff --git a/patches/unapplied/server/add-consumeFuel-to-FurnaceBurnEvent.patch b/patches/server/add-consumeFuel-to-FurnaceBurnEvent.patch similarity index 100% rename from patches/unapplied/server/add-consumeFuel-to-FurnaceBurnEvent.patch rename to patches/server/add-consumeFuel-to-FurnaceBurnEvent.patch diff --git a/patches/unapplied/server/add-get-set-drop-chance-to-EntityEquipment.patch b/patches/server/add-get-set-drop-chance-to-EntityEquipment.patch similarity index 100% rename from patches/unapplied/server/add-get-set-drop-chance-to-EntityEquipment.patch rename to patches/server/add-get-set-drop-chance-to-EntityEquipment.patch diff --git a/patches/unapplied/server/add-isDeeplySleeping-to-HumanEntity.patch b/patches/server/add-isDeeplySleeping-to-HumanEntity.patch similarity index 100% rename from patches/unapplied/server/add-isDeeplySleeping-to-HumanEntity.patch rename to patches/server/add-isDeeplySleeping-to-HumanEntity.patch diff --git a/patches/unapplied/server/fix-PigZombieAngerEvent-cancellation.patch b/patches/server/fix-PigZombieAngerEvent-cancellation.patch similarity index 89% rename from patches/unapplied/server/fix-PigZombieAngerEvent-cancellation.patch rename to patches/server/fix-PigZombieAngerEvent-cancellation.patch index 930286d797..b01338dfcf 100644 --- a/patches/unapplied/server/fix-PigZombieAngerEvent-cancellation.patch +++ b/patches/server/fix-PigZombieAngerEvent-cancellation.patch @@ -20,8 +20,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 protected void addBehaviourGoals() { this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0D, false)); this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D)); -- this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(new Class[0])); // CraftBukkit - decompile error -+ this.targetSelector.addGoal(1, pathfinderGoalHurtByTarget = new HurtByTargetGoal(this).setAlertOthers(new Class[0])); // CraftBukkit - decompile error // Paper - assign field +- this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); ++ this.targetSelector.addGoal(1, pathfinderGoalHurtByTarget = (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); // Paper - assign field this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); } diff --git a/patches/unapplied/server/fix-PlayerItemHeldEvent-firing-twice.patch b/patches/server/fix-PlayerItemHeldEvent-firing-twice.patch similarity index 100% rename from patches/unapplied/server/fix-PlayerItemHeldEvent-firing-twice.patch rename to patches/server/fix-PlayerItemHeldEvent-firing-twice.patch diff --git a/patches/unapplied/server/Incremental-player-saving.patch b/patches/unapplied/server/Incremental-player-saving.patch deleted file mode 100644 index 7c7ee6c669..0000000000 --- a/patches/unapplied/server/Incremental-player-saving.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sun, 9 Aug 2020 08:59:25 +0300 -Subject: [PATCH] Incremental player saving - - -diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -0,0 +0,0 @@ public class PaperConfig { - set("settings.unsupported-settings.allow-tnt-duplication", null); - } - -+ public static int playerAutoSaveRate = -1; -+ public static int maxPlayerAutoSavePerTick = 10; -+ private static void playerAutoSaveRate() { -+ playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1); -+ maxPlayerAutoSavePerTick = getInt("settings.max-player-auto-save-per-tick", -1); -+ if (maxPlayerAutoSavePerTick == -1) { // -1 Automatic / "Recommended" -+ // 10 should be safe for everyone unless you mass spamming player auto save -+ maxPlayerAutoSavePerTick = (playerAutoSaveRate == -1 || playerAutoSaveRate > 100) ? 10 : 20; -+ } -+ } - } -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 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit // Paper - move down - // MinecraftServer.LOGGER.debug("Autosave started"); // Paper - serverAutoSave = (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0); // Paper -+ // Paper start -+ int playerSaveInterval = com.destroystokyo.paper.PaperConfig.playerAutoSaveRate; -+ if (playerSaveInterval < 0) { -+ playerSaveInterval = autosavePeriod; -+ } -+ // Paper end - this.profiler.push("save"); -- if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // Paper - moved from above -- this.playerList.saveAll(); -+ if (playerSaveInterval > 0) { // Paper -+ this.playerList.savePlayers(playerSaveInterval); // Paper - // this.saveAllChunks(true, false, false); // Paper - saved incrementally below - } // Paper start - for (ServerLevel level : this.getAllLevels()) { -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -0,0 +0,0 @@ public class ServerPlayer extends Player { - public final int getViewDistance() { return this.getLevel().getChunkSource().chunkMap.viewDistance - 1; } // Paper - placeholder - - private static final Logger LOGGER = LogManager.getLogger(); -+ public long lastSave = MinecraftServer.currentTick; // Paper - private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; - private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; - public ServerGamePacketListenerImpl connection; -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -0,0 +0,0 @@ public abstract class PlayerList { - protected void save(ServerPlayer player) { - if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit - if (!player.didPlayerJoinEvent) return; // Paper - If we never fired PJE, we disconnected during login. Data has not changed, and additionally, our saved vehicle is not loaded! If we save now, we will lose our vehicle (CraftBukkit bug) -+ player.lastSave = MinecraftServer.currentTick; // Paper - this.playerIo.save(player); - ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit - -@@ -0,0 +0,0 @@ public abstract class PlayerList { - } - - public void saveAll() { -- net.minecraft.server.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main -+ // Paper start - incremental player saving -+ savePlayers(null); -+ } -+ public void savePlayers(Integer interval) { -+ 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 == null || now - entityplayer.lastSave >= interval) { -+ this.save(entityplayer); -+ if (interval != null && ++numSaved <= com.destroystokyo.paper.PaperConfig.maxPlayerAutoSavePerTick) { break; } -+ } -+ // Paper end - } - MinecraftTimings.savePlayers.stopTiming(); // Paper - return null; }); // Paper - ensure main diff --git a/patches/unapplied/server/incremental-chunk-saving.patch b/patches/unapplied/server/incremental-chunk-saving.patch deleted file mode 100644 index aa7dfb8b32..0000000000 --- a/patches/unapplied/server/incremental-chunk-saving.patch +++ /dev/null @@ -1,333 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shane Freeder -Date: Sun, 9 Jun 2019 03:53:22 +0100 -Subject: [PATCH] incremental chunk saving - - -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 { - log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16)); - } - -+ public int autoSavePeriod = -1; -+ private void autoSavePeriod() { -+ autoSavePeriod = getInt("auto-save-interval", -1); -+ if (autoSavePeriod > 0) { -+ log("Auto Save Interval: " +autoSavePeriod + " (" + (autoSavePeriod / 20) + "s)"); -+ } else if (autoSavePeriod < 0) { -+ autoSavePeriod = net.minecraft.server.MinecraftServer.getServer().autosavePeriod; -+ } -+ } -+ -+ public int maxAutoSaveChunksPerTick = 24; -+ private void maxAutoSaveChunksPerTick() { -+ maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); -+ } -+ - 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 processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); - public int autosavePeriod; -+ public boolean serverAutoSave = false; // Paper - public Commands vanillaCommandDispatcher; - public boolean forceTicks; // Paper - // CraftBukkit end -@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit -- MinecraftServer.LOGGER.debug("Autosave started"); -+ // if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit // Paper - move down -+ // MinecraftServer.LOGGER.debug("Autosave started"); // Paper -+ serverAutoSave = (autosavePeriod > 0 && this.tickCount % autosavePeriod == 0); // Paper - this.profiler.push("save"); -+ if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // Paper - moved from above - this.playerList.saveAll(); -- this.saveAllChunks(true, false, false); -- this.profiler.pop(); -- MinecraftServer.LOGGER.debug("Autosave finished"); -+ // this.saveAllChunks(true, false, false); // Paper - saved incrementally below -+ } // Paper start -+ for (ServerLevel level : this.getAllLevels()) { -+ if (level.paperConfig.autoSavePeriod > 0) { -+ level.saveIncrementally(this.serverAutoSave); -+ } - } -+ // Paper end -+ this.profiler.pop(); -+ // MinecraftServer.LOGGER.debug("Autosave finished"); // Paper -+ //} // Paper - - this.profiler.push("snooper"); - if (((DedicatedServer) this).getProperties().snooperEnabled && !this.snooper.isStarted() && this.tickCount > 100) { // Spigot -diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -0,0 +0,0 @@ public class ChunkHolder { - this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key); - } - // Paper end - optimise isOutsideOfRange -+ long lastAutoSaveTime; // Paper - incremental autosave -+ long inactiveTimeStart; // Paper - incremental autosave - - public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) { - this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); -@@ -0,0 +0,0 @@ public class ChunkHolder { - boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER); - boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER); - -+ boolean prevHasBeenLoaded = this.wasAccessibleSinceLastSave; // Paper - this.wasAccessibleSinceLastSave |= flag3; -+ // Paper start - incremental autosave -+ if (this.wasAccessibleSinceLastSave & !prevHasBeenLoaded) { -+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime; -+ if (timeSinceAutoSave < 0) { -+ // safest bet is to assume autosave is needed here -+ timeSinceAutoSave = this.chunkMap.level.paperConfig.autoSavePeriod; -+ } -+ this.lastAutoSaveTime = this.chunkMap.level.getGameTime() - timeSinceAutoSave; -+ this.chunkMap.autoSaveQueue.add(this); -+ } -+ // Paper end - if (!flag2 && flag3) { - int expectCreateCount = ++this.fullChunkCreateCount; // Paper - this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this); -@@ -0,0 +0,0 @@ public class ChunkHolder { - } - - public void refreshAccessibility() { -+ boolean prev = this.wasAccessibleSinceLastSave; // Paper - this.wasAccessibleSinceLastSave = ChunkHolder.getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); -+ // Paper start - incremental autosave -+ if (prev != this.wasAccessibleSinceLastSave) { -+ if (this.wasAccessibleSinceLastSave) { -+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime; -+ if (timeSinceAutoSave < 0) { -+ // safest bet is to assume autosave is needed here -+ timeSinceAutoSave = this.chunkMap.level.paperConfig.autoSavePeriod; -+ } -+ this.lastAutoSaveTime = this.chunkMap.level.getGameTime() - timeSinceAutoSave; -+ this.chunkMap.autoSaveQueue.add(this); -+ } else { -+ this.inactiveTimeStart = this.chunkMap.level.getGameTime(); -+ this.chunkMap.autoSaveQueue.remove(this); -+ } -+ } -+ // Paper end - } - -+ // Paper start - incremental autosave -+ public boolean setHasBeenLoaded() { -+ this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER); -+ return this.wasAccessibleSinceLastSave; -+ } -+ // Paper end -+ - public void replaceProtoChunk(ImposterProtoChunk chunk) { - for (int i = 0; i < this.futures.length(); ++i) { - CompletableFuture> completablefuture = (CompletableFuture) this.futures.get(i); -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -0,0 +0,0 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; - import net.minecraft.world.phys.Vec3; -+import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper - import org.apache.commons.lang3.mutable.MutableBoolean; - import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - } - -+ // Paper start - incremental autosave -+ final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((playerchunk1, playerchunk2) -> { -+ int timeCompare = Long.compare(playerchunk1.lastAutoSaveTime, playerchunk2.lastAutoSaveTime); -+ if (timeCompare != 0) { -+ return timeCompare; -+ } -+ -+ return Long.compare(MCUtil.getCoordinateKey(playerchunk1.pos), MCUtil.getCoordinateKey(playerchunk2.pos)); -+ }); -+ -+ protected void saveIncrementally() { -+ int savedThisTick = 0; -+ // optimized since we search far less chunks to hit ones that need to be saved -+ List reschedule = new java.util.ArrayList<>(this.level.paperConfig.maxAutoSaveChunksPerTick); -+ long currentTick = this.level.getGameTime(); -+ long maxSaveTime = currentTick - this.level.paperConfig.autoSavePeriod; -+ -+ for (Iterator iterator = this.autoSaveQueue.iterator(); iterator.hasNext();) { -+ ChunkHolder playerchunk = iterator.next(); -+ if (playerchunk.lastAutoSaveTime > maxSaveTime) { -+ break; -+ } -+ -+ iterator.remove(); -+ -+ ChunkAccess ichunkaccess = playerchunk.getChunkToSave().getNow(null); -+ if (ichunkaccess instanceof LevelChunk) { -+ boolean shouldSave = ((LevelChunk)ichunkaccess).lastSaveTime <= maxSaveTime; -+ -+ if (shouldSave && this.save(ichunkaccess) && this.level.entityManager.storeChunkSections(playerchunk.pos.toLong(), entity -> {})) { -+ ++savedThisTick; -+ -+ if (!playerchunk.setHasBeenLoaded()) { -+ // do not fall through to reschedule logic -+ playerchunk.inactiveTimeStart = currentTick; -+ if (savedThisTick >= this.level.paperConfig.maxAutoSaveChunksPerTick) { -+ break; -+ } -+ continue; -+ } -+ } -+ } -+ -+ reschedule.add(playerchunk); -+ -+ if (savedThisTick >= this.level.paperConfig.maxAutoSaveChunksPerTick) { -+ break; -+ } -+ } -+ -+ for (int i = 0, len = reschedule.size(); i < len; ++i) { -+ ChunkHolder playerchunk = reschedule.get(i); -+ playerchunk.lastAutoSaveTime = this.level.getGameTime(); -+ this.autoSaveQueue.add(playerchunk); -+ } -+ } -+ // Paper end -+ - protected void saveAllChunks(boolean flush) { - if (flush) { - List list = (List) this.visibleChunkMap.values().stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - asyncSaveData, chunk); - - chunk.setUnsaved(false); -+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track last saved time - } - // Paper end - -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - - this.level.unload(chunk); - } -+ this.autoSaveQueue.remove(holder); // Paper - - this.lightEngine.updateChunkStatus(ichunkaccess.getPos()); - this.lightEngine.tryScheduleUpdate(); -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - if (!chunk.isUnsaved()) { - return false; - } else { -+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track save time - chunk.setUnsaved(false); - ChunkPos chunkcoordintpair = chunk.getPos(); - -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -0,0 +0,0 @@ public class ServerChunkCache extends ChunkSource { - } // Paper - Timings - } - -+ // Paper start - 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 -+ - @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 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 @@ public class ServerLevel extends Level implements WorldGenLevel { - return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos); - } - -+ // Paper start - derived from below -+ 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.registryHolder, this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); -+ } -+ // CraftBukkit end -+ } -+ } -+ // Paper end -+ - public void save(@Nullable ProgressListener progressListener, boolean flush, boolean flag1) { - ServerChunkCache chunkproviderserver = this.getChunkSource(); - -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -0,0 +0,0 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess { - return GameEventDispatcher.NOOP; - } - -+ default void setLastSaved(long ticks) {} - // Paper start - default boolean generateFlatBedrock() { - if (this.getLevel() != null) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -0,0 +0,0 @@ public class LevelChunk implements ChunkAccess { - private final ShortList[] postProcessing; - private TickList blockTicks; - private TickList liquidTicks; -+ // Paper start - track last save time -+ public long lastSaveTime; -+ @Override -+ public void setLastSaved(long ticks) { -+ this.lastSaveTime = ticks; -+ } -+ // Paper end - private volatile boolean unsaved; - private long inhabitedTime; - @Nullable