RecoveryState class

Пакет: com.hypixel.hytale.server.core.universe

Файл: com/hypixel/hytale/server/core/universe/WorldCrashRecoveryHandler.java

Поля (3)

МодификаторыТипИмя
private int attempts
private long lastCrashNanos
private boolean loading

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.server.core.universe;

class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.server.core.Constants;
class="kw">import com.hypixel.hytale.server.core.HytaleServer;
class="kw">import com.hypixel.hytale.server.core.HytaleServerConfig;
class="kw">import com.hypixel.hytale.server.core.Message;
class="kw">import com.hypixel.hytale.server.core.ShutdownReason;
class="kw">import com.hypixel.hytale.server.core.config.CrashRecoveryConfig;
class="kw">import com.hypixel.hytale.server.core.universe.world.World;
class="kw">import com.hypixel.hytale.server.core.universe.world.WorldCrashRecovery;
class="kw">import com.hypixel.hytale.server.core.universe.world.events.RemoveWorldEvent;
class="kw">import java.time.Duration;
class="kw">import java.util.Locale;
class="kw">import java.util.concurrent.ConcurrentHashMap;
class="kw">import java.util.concurrent.ScheduledFuture;
class="kw">import java.util.concurrent.TimeUnit;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;

class="kw">final class WorldCrashRecoveryHandler {
   class="kw">private class="kw">static class="kw">final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
   class="kw">static class="kw">final long CRASH_WINDOW_NANOS = Duration.ofMinutes(5L).toNanos();
   class="kw">private class="kw">final Universe universe;
   class="kw">private class="kw">final ConcurrentHashMap<String, WorldCrashRecoveryHandler.RecoveryState> states = new ConcurrentHashMap<>();

   WorldCrashRecoveryHandler(@Nonnull Universe var1) {
      this.universe = var1;
   }

   void onRemoveWorld(@Nonnull RemoveWorldEvent var1) {
      World var2 = var1.getWorld();
      String var3 = var2.getName().toLowerCase(Locale.ROOT);
      if (var1.getRemovalReason() != RemoveWorldEvent.RemovalReason.EXCEPTIONAL) {
         this.clearState(var3);
      } else {
         WorldCrashRecoveryHandler.ResolvedPolicy var4 = this.resolve(var2);
         if (var4 != null && var4.mode() != WorldCrashRecovery.None) {
            if (var4.mode() == WorldCrashRecovery.Shutdown) {
               LOGGER.at(Level.WARNING).log("World '%s' crashed, shutting down the server", var2.getName());
               HytaleServer.get()
                  .shutdownServer(
                     ShutdownReason.CRASH.withMessage(Message.translation("server.general.disconnect.worldCrashedShutdown").param("world", var2.getName()))
                  );
            } else if (var2.getWorldConfig().isDeleteOnRemove()) {
               LOGGER.at(Level.INFO).log("Skipping reload of crashed world '%s' because it is set to delete on remove", var2.getName());
               this.clearState(var3);
            } else {
               WorldCrashRecoveryHandler.RecoveryState var5 = this.states.computeIfAbsent(var3, var0 -> new WorldCrashRecoveryHandler.RecoveryState());
               class="kw">synchronized (var5) {
                  if (this.states.get(var3) == var5) {
                     if (!var5.loading) {
                        this.scheduleReload(var2.getName(), var3, var5, var4);
                     }
                  }
               }
            }
         }
      }
   }

   @Nullable
   class="kw">private WorldCrashRecoveryHandler.ResolvedPolicy resolve(@Nonnull World var1) {
      HytaleServerConfig var2 = HytaleServer.get().getConfig();
      if (var2 == null) {
         class="kw">return null;
      }

      String var3 = var2.getDefaults().getWorld();
      boolean var4 = var3 != null && var3.equalsIgnoreCase(var1.getName());
      CrashRecoveryConfig var5 = var1.getWorldConfig().getCrashRecovery();
      if (var5 != null) {
         WorldCrashRecovery var6 = var5.getMode();
         if (var6 == null) {
            var6 = var4 ? defaultWorldMode(var2) : WorldCrashRecovery.Reload;
         }

         class="kw">return WorldCrashRecoveryHandler.ResolvedPolicy.of(var5, var6);
      } else {
         class="kw">return var4 ? WorldCrashRecoveryHandler.ResolvedPolicy.of(var2.getDefaults().getCrashRecovery(), defaultWorldMode(var2)) : null;
      }
   }

   @Nonnull
   class="kw">private class="kw">static WorldCrashRecovery defaultWorldMode(@Nonnull HytaleServerConfig var0) {
      WorldCrashRecovery var1 = var0.getDefaults().getCrashRecovery().getMode();
      if (var1 != null) {
         class="kw">return var1;
      } else {
         class="kw">return Constants.SINGLEPLAYER ? WorldCrashRecovery.None : WorldCrashRecovery.Reload;
      }
   }

   class="kw">private void scheduleReload(
      @Nonnull String var1, @Nonnull String var2, @Nonnull WorldCrashRecoveryHandler.RecoveryState var3, @Nonnull WorldCrashRecoveryHandler.ResolvedPolicy var4
   ) {
      if (var3.pending != null) {
         var3.pending.cancel(false);
         var3.pending = null;
      }

      long var5 = System.nanoTime();
      long var7 = CRASH_WINDOW_NANOS + TimeUnit.SECONDS.toNanos(var4.retryDelaySeconds());
      var3.attempts = var5 - var3.lastCrashNanos > var7 ? 1 : var3.attempts + 1;
      var3.lastCrashNanos = var5;
      if (var3.attempts > var4.maxAttempts()) {
         LOGGER.at(Level.WARNING).log("Gave up recovering world '%s' after %d reload attempt(s)", var1, var4.maxAttempts());
         this.states.remove(var2);
         this.applyFallback(var1, var4.fallback());
      } else {
         LOGGER.at(Level.INFO)
            .log("Reloading crashed world '%s' in %d second(s) (attempt %d of %d)", var1, var4.retryDelaySeconds(), var3.attempts, var4.maxAttempts());
         var3.pending = HytaleServer.SCHEDULED_EXECUTOR.schedule(() -> this.reloadNow(var1, var2, var4), var4.retryDelaySeconds(), TimeUnit.SECONDS);
      }
   }

   class="kw">private void reloadNow(@Nonnull String var1, @Nonnull String var2, @Nonnull WorldCrashRecoveryHandler.ResolvedPolicy var3) {
      WorldCrashRecoveryHandler.RecoveryState var4 = this.states.get(var2);
      if (var4 != null) {
         class="kw">synchronized (var4) {
            if (this.states.get(var2) == var4) {
               var4.pending = null;
               if (HytaleServer.get().isShuttingDown()) {
                  this.states.remove(var2);
               } else if (this.universe.getWorld(var1) != null) {
                  LOGGER.at(Level.INFO).log("World '%s' is already loaded, crash recovery superseded", var1);
                  this.states.remove(var2);
               } else {
                  var4.loading = true;

                  try {
                     this.universe.loadWorld(var1).whenComplete((var4x, var5) -> this.onReloadComplete(var1, var2, var3, var5));
                  } catch (Throwable var8) {
                     var4.loading = false;
                     ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var8)).log("Failed to start reload of crashed world '%s'", var1);
                     this.scheduleReload(var1, var2, var4, var3);
                  }
               }
            }
         }
      }
   }

   class="kw">private void onReloadComplete(@Nonnull String var1, @Nonnull String var2, @Nonnull WorldCrashRecoveryHandler.ResolvedPolicy var3, @Nullable Throwable var4) {
      WorldCrashRecoveryHandler.RecoveryState var5 = this.states.get(var2);
      if (var5 != null) {
         class="kw">synchronized (var5) {
            if (this.states.get(var2) == var5) {
               var5.loading = false;
               if (HytaleServer.get().isShuttingDown()) {
                  this.states.remove(var2);
               } else {
                  if (var4 != null) {
                     ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var4)).log("Failed to reload crashed world '%s'", var1);
                     this.scheduleReload(var1, var2, var5, var3);
                  } else {
                     LOGGER.at(Level.INFO).log("Reloaded crashed world '%s'", var1);
                  }
               }
            }
         }
      }
   }

   class="kw">private void clearState(@Nonnull String var1) {
      WorldCrashRecoveryHandler.RecoveryState var2 = this.states.remove(var1);
      if (var2 != null) {
         class="kw">synchronized (var2) {
            if (var2.pending != null) {
               var2.pending.cancel(false);
               var2.pending = null;
            }
         }
      }
   }

   class="kw">private void applyFallback(@Nonnull String var1, @Nonnull WorldCrashRecovery var2) {
      if (var2 == WorldCrashRecovery.Shutdown) {
         LOGGER.at(Level.WARNING).log("World '%s' did not recover, shutting down the server", var1);
         HytaleServer.get()
            .shutdownServer(ShutdownReason.CRASH.withMessage(Message.translation("server.general.disconnect.worldNotRecovered").param("world", var1)));
      } else {
         LOGGER.at(Level.WARNING).log("World '%s' did not recover, the server is left without it", var1);
      }
   }

   class="kw">private class="kw">static class="kw">final class RecoveryState {
      class="kw">private int attempts;
      class="kw">private long lastCrashNanos;
      class="kw">private boolean loading;
      @Nullable
      class="kw">private ScheduledFuture<?> pending;

      class="kw">private RecoveryState() {
      }
   }

   class="kw">private record ResolvedPolicy(@Nonnull WorldCrashRecovery mode, int maxAttempts, int retryDelaySeconds, @Nonnull WorldCrashRecovery fallback) {
      class="kw">private ResolvedPolicy {
      }

      @Nonnull
      class="kw">private class="kw">static WorldCrashRecoveryHandler.ResolvedPolicy of(@Nonnull CrashRecoveryConfig var0, @Nonnull WorldCrashRecovery var1) {
         class="kw">return new WorldCrashRecoveryHandler.ResolvedPolicy(var1, var0.getMaxAttempts(), var0.getRetryDelaySeconds(), var0.getFallback());
      }
   }
}