diff --git a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch
index 00b906f1b6..2a727b7405 100644
--- a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch
+++ b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch
@@ -484,12 +484,10 @@
  
          if (!iworlddataserver.isInitialized()) {
              try {
-@@ -425,32 +686,10 @@
-             }
- 
+@@ -427,30 +688,8 @@
              iworlddataserver.setInitialized(true);
--        }
--
+         }
+ 
 -        this.getPlayerList().addWorldborderListener(worldserver);
 -        if (this.worldData.getCustomBossEvents() != null) {
 -            this.getCustomBossEvents().load(this.worldData.getCustomBossEvents(), this.registryAccess());
@@ -510,8 +508,8 @@
 -                worldborder.addListener(new BorderChangeListener.DelegateBorderChangeListener(worldserver1.getWorldBorder()));
 -                this.levels.put(resourcekey1, worldserver1);
 -            }
-         }
- 
+-        }
+-
 -        worldborder.applySettings(iworlddataserver.getWorldBorder());
      }
 +    // CraftBukkit end
@@ -670,7 +668,20 @@
          }
  
          MinecraftServer.LOGGER.info("Saving worlds");
-@@ -720,6 +1012,13 @@
+@@ -693,6 +985,12 @@
+         } catch (IOException ioexception1) {
+             MinecraftServer.LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), ioexception1);
+         }
++        // Spigot start
++        if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
++            MinecraftServer.LOGGER.info("Saving usercache.json");
++            this.getProfileCache().save();
++        }
++        // Spigot end
+ 
+     }
+ 
+@@ -720,6 +1018,13 @@
  
      }
  
@@ -684,7 +695,7 @@
      protected void runServer() {
          try {
              if (!this.initServer()) {
-@@ -727,9 +1026,12 @@
+@@ -727,9 +1032,12 @@
              }
  
              this.nextTickTimeNanos = Util.getNanos();
@@ -698,7 +709,7 @@
              while (this.running) {
                  long i;
  
-@@ -744,11 +1046,23 @@
+@@ -744,11 +1052,23 @@
                      if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) {
                          long k = j / i;
  
@@ -722,7 +733,7 @@
  
                  boolean flag = i == 0L;
  
-@@ -757,6 +1071,7 @@
+@@ -757,6 +1077,7 @@
                      this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
                  }
  
@@ -730,7 +741,7 @@
                  this.nextTickTimeNanos += i;
  
                  try {
-@@ -830,6 +1145,13 @@
+@@ -830,6 +1151,13 @@
                      this.services.profileCache().clearExecutor();
                  }
  
@@ -744,7 +755,7 @@
                  this.onServerExit();
              }
  
-@@ -889,9 +1211,16 @@
+@@ -889,9 +1217,16 @@
      }
  
      private boolean haveTime() {
@@ -762,7 +773,7 @@
      public static boolean throwIfFatalException() {
          RuntimeException runtimeexception = (RuntimeException) MinecraftServer.fatalException.get();
  
-@@ -903,7 +1232,7 @@
+@@ -903,7 +1238,7 @@
      }
  
      public static void setFatalException(RuntimeException exception) {
@@ -771,7 +782,7 @@
      }
  
      @Override
-@@ -977,7 +1306,7 @@
+@@ -977,7 +1312,7 @@
          }
      }
  
@@ -780,7 +791,7 @@
          Profiler.get().incrementCounter("runTask");
          super.doRunTask(ticktask);
      }
-@@ -1025,6 +1354,7 @@
+@@ -1025,6 +1360,7 @@
      }
  
      public void tickServer(BooleanSupplier shouldKeepTicking) {
@@ -788,7 +799,7 @@
          long i = Util.getNanos();
          int j = this.pauseWhileEmptySeconds() * 20;
  
-@@ -1041,11 +1371,13 @@
+@@ -1041,11 +1377,13 @@
                      this.autoSave();
                  }
  
@@ -802,7 +813,7 @@
          ++this.tickCount;
          this.tickRateManager.tick();
          this.tickChildren(shouldKeepTicking);
-@@ -1055,7 +1387,7 @@
+@@ -1055,7 +1393,7 @@
          }
  
          --this.ticksUntilAutosave;
@@ -811,7 +822,7 @@
              this.autoSave();
          }
  
-@@ -1071,10 +1403,13 @@
+@@ -1071,10 +1409,13 @@
          this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float) k / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
          this.logTickMethodTime(i);
          gameprofilerfiller.pop();
@@ -826,7 +837,7 @@
          MinecraftServer.LOGGER.debug("Autosave started");
          ProfilerFiller gameprofilerfiller = Profiler.get();
  
-@@ -1082,6 +1417,7 @@
+@@ -1082,6 +1423,7 @@
          this.saveEverything(true, false, false);
          gameprofilerfiller.pop();
          MinecraftServer.LOGGER.debug("Autosave finished");
@@ -834,7 +845,7 @@
      }
  
      private void logTickMethodTime(long tickStartTime) {
-@@ -1154,11 +1490,34 @@
+@@ -1154,11 +1496,34 @@
          this.getPlayerList().getPlayers().forEach((entityplayer) -> {
              entityplayer.connection.suspendFlushing();
          });
@@ -847,7 +858,7 @@
 +        SpigotTimings.commandFunctionsTimer.stopTiming(); // Spigot
          gameprofilerfiller.popPush("levels");
          Iterator iterator = this.getAllLevels().iterator();
-+
+ 
 +        // CraftBukkit start
 +        // Run tasks that are waiting on processing
 +        SpigotTimings.processQueueTimer.startTiming(); // Spigot
@@ -855,7 +866,7 @@
 +            this.processQueue.remove().run();
 +        }
 +        SpigotTimings.processQueueTimer.stopTiming(); // Spigot
- 
++
 +        SpigotTimings.timeUpdateTimer.startTiming(); // Spigot
 +        // Send time updates to everyone, it will get the right time from the world the player is in.
 +        if (this.tickCount % 20 == 0) {
@@ -869,7 +880,7 @@
          while (iterator.hasNext()) {
              ServerLevel worldserver = (ServerLevel) iterator.next();
  
-@@ -1167,16 +1526,20 @@
+@@ -1167,16 +1532,20 @@
  
                  return s + " " + String.valueOf(worldserver.dimension().location());
              });
@@ -890,7 +901,7 @@
              } catch (Throwable throwable) {
                  CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
  
-@@ -1189,18 +1552,24 @@
+@@ -1189,18 +1558,24 @@
          }
  
          gameprofilerfiller.popPush("connection");
@@ -915,12 +926,10 @@
  
          gameprofilerfiller.popPush("send chunks");
          iterator = this.playerList.getPlayers().iterator();
-@@ -1265,7 +1634,23 @@
-     @Nullable
-     public ServerLevel getLevel(ResourceKey<Level> key) {
+@@ -1267,6 +1642,22 @@
          return (ServerLevel) this.levels.get(key);
-+    }
-+
+     }
+ 
 +    // CraftBukkit start
 +    public void addLevel(ServerLevel level) {
 +        Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
@@ -934,12 +943,13 @@
 +        Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
 +        newLevels.remove(level.dimension());
 +        this.levels = Collections.unmodifiableMap(newLevels);
-     }
++    }
 +    // CraftBukkit end
- 
++
      public Set<ResourceKey<Level>> levelKeys() {
          return this.levels.keySet();
-@@ -1296,7 +1681,7 @@
+     }
+@@ -1296,7 +1687,7 @@
  
      @DontObfuscate
      public String getServerModName() {
@@ -948,7 +958,7 @@
      }
  
      public SystemReport fillSystemReport(SystemReport details) {
-@@ -1507,7 +1892,7 @@
+@@ -1507,7 +1898,7 @@
      }
  
      public ServerConnectionListener getConnection() {
@@ -957,7 +967,7 @@
      }
  
      public boolean isReady() {
-@@ -1634,11 +2019,11 @@
+@@ -1634,11 +2025,11 @@
  
      public CompletableFuture<Void> reloadResources(Collection<String> dataPacks) {
          CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> {
@@ -971,7 +981,7 @@
          }, this).thenCompose((immutablelist) -> {
              MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist);
              List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess());
-@@ -1654,6 +2039,7 @@
+@@ -1654,6 +2045,7 @@
          }).thenAcceptAsync((minecraftserver_reloadableresources) -> {
              this.resources.close();
              this.resources = minecraftserver_reloadableresources;
@@ -979,7 +989,7 @@
              this.packRepository.setSelected(dataPacks);
              WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures());
  
-@@ -1952,7 +2338,7 @@
+@@ -1952,7 +2344,7 @@
              final List<String> list = Lists.newArrayList();
              final GameRules gamerules = this.getGameRules();
  
@@ -988,7 +998,7 @@
                  @Override
                  public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
                      list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gamerules.getRule(key)));
-@@ -2058,7 +2444,7 @@
+@@ -2058,7 +2450,7 @@
              try {
                  label51:
                  {
@@ -997,10 +1007,13 @@
  
                      try {
                          arraylist = Lists.newArrayList(NativeModuleLister.listModules());
-@@ -2108,6 +2494,22 @@
- 
-     }
- 
+@@ -2105,8 +2497,24 @@
+         if (bufferedwriter != null) {
+             bufferedwriter.close();
+         }
++
++    }
++
 +    // CraftBukkit start
 +    public boolean isDebugging() {
 +        return false;
@@ -1010,25 +1023,24 @@
 +    public static MinecraftServer getServer() {
 +        return (Bukkit.getServer() instanceof CraftServer) ? ((CraftServer) Bukkit.getServer()).getServer() : null;
 +    }
-+
+ 
 +    @Deprecated
 +    public static RegistryAccess getDefaultRegistryAccess() {
 +        return CraftRegistry.getMinecraftRegistry();
-+    }
+     }
 +    // CraftBukkit end
-+
+ 
      private ProfilerFiller createProfiler() {
          if (this.willStartRecordingMetrics) {
-             this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
-@@ -2234,6 +2636,11 @@
-         }
+@@ -2235,6 +2643,11 @@
  
      }
-+
+ 
 +    // CraftBukkit start
 +    public final java.util.concurrent.ExecutorService chatExecutor = java.util.concurrent.Executors.newCachedThreadPool(
 +            new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Chat Thread - #%d").build());
 +    // CraftBukkit end
- 
++
      public ChatDecorator getChatDecorator() {
          return ChatDecorator.PLAIN;
+     }
diff --git a/paper-server/patches/sources/net/minecraft/server/players/GameProfileCache.java.patch b/paper-server/patches/sources/net/minecraft/server/players/GameProfileCache.java.patch
index 93ab1dd1ce..9dc80133ec 100644
--- a/paper-server/patches/sources/net/minecraft/server/players/GameProfileCache.java.patch
+++ b/paper-server/patches/sources/net/minecraft/server/players/GameProfileCache.java.patch
@@ -9,7 +9,16 @@
                  }
              };
  
-@@ -142,7 +142,7 @@
+@@ -117,7 +117,7 @@
+         GameProfileCache.GameProfileInfo usercache_usercacheentry = new GameProfileCache.GameProfileInfo(profile, date);
+ 
+         this.safeAdd(usercache_usercacheentry);
+-        this.save();
++        if( !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly ) this.save(); // Spigot - skip saving if disabled
+     }
+ 
+     private long getNextOperation() {
+@@ -142,14 +142,14 @@
              usercache_usercacheentry.setLastAccess(this.getNextOperation());
              optional = Optional.of(usercache_usercacheentry.getProfile());
          } else {
@@ -18,6 +27,14 @@
              if (optional.isPresent()) {
                  this.add((GameProfile) optional.get());
                  flag = false;
+             }
+         }
+ 
+-        if (flag) {
++        if (flag && !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { // Spigot - skip saving if disabled
+             this.save();
+         }
+ 
 @@ -208,7 +208,7 @@
  
              label54:
diff --git a/paper-server/src/main/java/org/spigotmc/SpigotConfig.java b/paper-server/src/main/java/org/spigotmc/SpigotConfig.java
index 59647d169d..92d5ddf69c 100644
--- a/paper-server/src/main/java/org/spigotmc/SpigotConfig.java
+++ b/paper-server/src/main/java/org/spigotmc/SpigotConfig.java
@@ -320,4 +320,10 @@ public class SpigotConfig
     {
         SpigotConfig.userCacheCap = SpigotConfig.getInt( "settings.user-cache-size", 1000 );
     }
+
+    public static boolean saveUserCacheOnStopOnly;
+    private static void saveUserCacheOnStopOnly()
+    {
+        SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean( "settings.save-user-cache-on-stop-only", false );
+    }
 }