From 66ea8f6540a8810a74260f257bda2f799b09fe01 Mon Sep 17 00:00:00 2001 From: MobiusDevelopment <8391001+MobiusDevelopment@users.noreply.github.com> Date: Sat, 19 Dec 2020 22:30:07 +0000 Subject: [PATCH] Addition of precautionary restart manager. --- .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/main/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 6 + .../org/l2jmobius/gameserver/Shutdown.java | 11 + .../PrecautionaryRestartManager.java | 198 ++++++++++++++++++ .../dist/game/config/main/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 6 + .../org/l2jmobius/gameserver/Shutdown.java | 11 + .../PrecautionaryRestartManager.java | 198 ++++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ .../dist/game/config/Server.ini | 31 +++ .../java/org/l2jmobius/Config.java | 12 ++ .../org/l2jmobius/gameserver/GameServer.java | 5 + .../org/l2jmobius/gameserver/Shutdown.java | 12 ++ .../PrecautionaryRestartManager.java | 196 +++++++++++++++++ 105 files changed, 5380 insertions(+) create mode 100644 L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java create mode 100644 L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/config/Server.ini b/L2J_Mobius_1.0_Ertheia/dist/game/config/Server.ini index 50f69fca04..b174b854e7 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/config/Server.ini +++ b/L2J_Mobius_1.0_Ertheia/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java index 0d8cba2363..b695cfd5b2 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java @@ -774,6 +774,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1438,6 +1444,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/GameServer.java index 20ae528fa8..e0ee65aba1 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/GameServer.java @@ -130,6 +130,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -427,6 +428,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/dist/game/config/Server.ini b/L2J_Mobius_2.5_Underground/dist/game/config/Server.ini index 908d798510..81047aa753 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/config/Server.ini +++ b/L2J_Mobius_2.5_Underground/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java index 83e40bc7dd..c48fe444f3 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java @@ -784,6 +784,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1449,6 +1455,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/GameServer.java index 0d3b20edaa..85652fbcbf 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/GameServer.java @@ -134,6 +134,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -435,6 +436,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/dist/game/config/Server.ini b/L2J_Mobius_3.0_Helios/dist/game/config/Server.ini index e1aff779ad..f5c23c6087 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/config/Server.ini +++ b/L2J_Mobius_3.0_Helios/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java index 80933e74f7..1d5b0c14e9 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java @@ -785,6 +785,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1462,6 +1468,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/GameServer.java index 0d3b20edaa..85652fbcbf 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/GameServer.java @@ -134,6 +134,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -435,6 +436,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/Server.ini b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/Server.ini index 1aeb02e160..bab2b45d5b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/Server.ini +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java index f7164d8f71..2eb568071b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java @@ -772,6 +772,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1449,6 +1455,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/GameServer.java index 292892d8d2..7187525e28 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/GameServer.java @@ -134,6 +134,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -435,6 +436,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/dist/game/config/Server.ini b/L2J_Mobius_5.0_Salvation/dist/game/config/Server.ini index 02dccf0fbf..d923afe204 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/config/Server.ini +++ b/L2J_Mobius_5.0_Salvation/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java index 9d34c64dda..a7895b4194 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java @@ -767,6 +767,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1444,6 +1450,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/GameServer.java index 6c09849fdf..00302ea498 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/GameServer.java @@ -135,6 +135,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -437,6 +438,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/Shutdown.java index 9209a15157..01f7e56d47 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/config/Server.ini b/L2J_Mobius_5.5_EtinasFate/dist/game/config/Server.ini index a58fc549b9..343dd520b1 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/config/Server.ini +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java index 6e09372fc4..40e852bb72 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java @@ -767,6 +767,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1451,6 +1457,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/GameServer.java index 6c09849fdf..00302ea498 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/GameServer.java @@ -135,6 +135,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -437,6 +438,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/config/Server.ini b/L2J_Mobius_6.0_Fafurion/dist/game/config/Server.ini index 05c426a3a5..4e21050262 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/config/Server.ini +++ b/L2J_Mobius_6.0_Fafurion/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java index 3f1fe16c79..174b28d304 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java @@ -768,6 +768,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1473,6 +1479,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/GameServer.java index 984a20ad63..adcb7009a0 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/GameServer.java @@ -136,6 +136,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -439,6 +440,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/Server.ini b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/Server.ini index 7d7362ed45..b739db22c0 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/Server.ini +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java index 187bc0bd7c..06adcdce1c 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java @@ -773,6 +773,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1484,6 +1490,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/GameServer.java index 021dada889..7dda47cf28 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/GameServer.java @@ -137,6 +137,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -443,6 +444,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/config/Server.ini b/L2J_Mobius_8.0_Homunculus/dist/game/config/Server.ini index f67efa7ed0..d564479ff8 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/config/Server.ini +++ b/L2J_Mobius_8.0_Homunculus/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java index c01de0062d..9586f4504a 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java @@ -770,6 +770,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Hardin (Agent of Chaos) @@ -1481,6 +1487,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/GameServer.java index 719ecb68ba..9b6e917b7f 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/GameServer.java @@ -136,6 +136,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -441,6 +442,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/Shutdown.java index e58b8b6e20..5279bac36d 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/Shutdown.java @@ -34,6 +34,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -309,6 +310,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -324,6 +330,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/Server.ini b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/Server.ini index 0acc4f745b..babee464c8 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/Server.ini +++ b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/Server.ini @@ -158,6 +158,37 @@ ClanNameTemplate = .* AllyNameTemplate = .* +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java index 88c69584eb..ca6aff37ab 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java @@ -1127,6 +1127,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; public static int IP_UPDATE_TIME; public static boolean SHOW_LICENCE; @@ -1228,6 +1234,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverConfig.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverConfig.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverConfig.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverConfig.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverConfig.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverConfig.getInt("PrecautionaryRestartDelay", 60) * 1000; } public static void loadTelnetConfig() diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/GameServer.java index 8c00e90a24..61dac42896 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/GameServer.java @@ -100,6 +100,7 @@ import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; import org.l2jmobius.gameserver.instancemanager.MercTicketManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.RaidBossPointsManager; import org.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager; @@ -471,6 +472,11 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } + System.gc(); Util.printSection("Info"); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/Shutdown.java index 28a1339b71..4d47a1e27f 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/Shutdown.java @@ -29,6 +29,7 @@ import org.l2jmobius.gameserver.instancemanager.CastleManorManager; import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager; import org.l2jmobius.gameserver.model.World; @@ -193,6 +194,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -219,6 +225,11 @@ public class Shutdown extends Thread { _counterInstance._abort(); } + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } } /** diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..ce1beb4a05 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,198 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.enums.ChatType; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers(new CreatureSay(-1, ChatType.ANNOUNCEMENT, "", "Server will restart in 10 minutes.")); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers(new CreatureSay(-1, ChatType.ANNOUNCEMENT, "", "Server will restart in 10 minutes.")); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getAllPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player._inEvent || player.atEvent) + { + return true; + } + + if (player.getInstanceId() > 0) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/dist/game/config/main/Server.ini b/L2J_Mobius_C6_Interlude/dist/game/config/main/Server.ini index 6d540cfb8b..87aeff88ba 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/config/main/Server.ini +++ b/L2J_Mobius_C6_Interlude/dist/game/config/main/Server.ini @@ -158,6 +158,37 @@ ClanNameTemplate = .* AllyNameTemplate = .* +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java index 8836841d47..d2dcddf599 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java @@ -1162,6 +1162,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; public static int IP_UPDATE_TIME; public static boolean SHOW_LICENCE; @@ -1263,6 +1269,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverConfig.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverConfig.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverConfig.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverConfig.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverConfig.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverConfig.getInt("PrecautionaryRestartDelay", 60) * 1000; } public static void loadTelnetConfig() diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/GameServer.java index 98428f42d8..2878aeecb6 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/GameServer.java @@ -104,6 +104,7 @@ import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; import org.l2jmobius.gameserver.instancemanager.MercTicketManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.RaidBossPointsManager; import org.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager; @@ -483,6 +484,11 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } + System.gc(); Util.printSection("Info"); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/Shutdown.java index abb3857153..43ea608cc6 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/Shutdown.java @@ -31,6 +31,7 @@ import org.l2jmobius.gameserver.instancemanager.FishingChampionshipManager; import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager; import org.l2jmobius.gameserver.model.World; @@ -195,6 +196,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -221,6 +227,11 @@ public class Shutdown extends Thread { _counterInstance._abort(); } + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } } /** diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..ce1beb4a05 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,198 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.enums.ChatType; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.network.serverpackets.CreatureSay; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers(new CreatureSay(-1, ChatType.ANNOUNCEMENT, "", "Server will restart in 10 minutes.")); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers(new CreatureSay(-1, ChatType.ANNOUNCEMENT, "", "Server will restart in 10 minutes.")); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getAllPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player._inEvent || player.atEvent) + { + return true; + } + + if (player.getInstanceId() > 0) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/Server.ini b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/Server.ini index 51ef09886b..28f5b33d5f 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/Server.ini +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/Server.ini @@ -184,6 +184,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java index e915810ff8..d1f7626103 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java @@ -897,6 +897,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // MMO Settings @@ -1482,6 +1488,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/GameServer.java index eac9f63fc5..9d8ad0134f 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/GameServer.java @@ -119,6 +119,7 @@ import org.l2jmobius.gameserver.instancemanager.MailManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.MercTicketManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -439,6 +440,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/Shutdown.java index 7145a4974d..8ed80852fa 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager; import org.l2jmobius.gameserver.model.World; @@ -313,6 +314,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -328,6 +334,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..095f632ac2 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.getInstanceId() > 0) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/Server.ini b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/Server.ini index 078e3e3d8f..5f4e106e11 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/Server.ini +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/Server.ini @@ -184,6 +184,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java index 54df061839..2e22d7ab87 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java @@ -901,6 +901,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // MMO Settings @@ -1481,6 +1487,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/GameServer.java index 2f1c160eb6..9be776aec9 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/GameServer.java @@ -120,6 +120,7 @@ import org.l2jmobius.gameserver.instancemanager.MailManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.MercTicketManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -441,6 +442,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/Shutdown.java index 7145a4974d..8ed80852fa 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager; import org.l2jmobius.gameserver.model.World; @@ -313,6 +314,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -328,6 +334,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..095f632ac2 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.getInstanceId() > 0) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/Server.ini b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/Server.ini index 0b6151070d..39b5db82f8 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java index c202c0fdbd..33f9ce5fb3 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java @@ -774,6 +774,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1380,6 +1386,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/GameServer.java index b6f4ab656e..c6f280cb70 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/GameServer.java @@ -131,6 +131,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -432,6 +433,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/Server.ini b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/Server.ini index 4c0548da1d..f31dd638ff 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java index ffac9ebadb..4b04f8b623 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java @@ -774,6 +774,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1384,6 +1390,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/GameServer.java index b6f4ab656e..c6f280cb70 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/GameServer.java @@ -131,6 +131,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -432,6 +433,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/Server.ini b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/Server.ini index 3b9aa1bcd8..fd19902600 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java index ffac9ebadb..4b04f8b623 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java @@ -774,6 +774,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1384,6 +1390,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/GameServer.java index b6f4ab656e..c6f280cb70 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/GameServer.java @@ -131,6 +131,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -432,6 +433,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/Server.ini b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/Server.ini index ef351b585a..a07c6414bb 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java index a9b81c697e..a4202f1f57 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java @@ -774,6 +774,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1384,6 +1390,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/GameServer.java index ed439a7a83..3c4bda433b 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/GameServer.java @@ -132,6 +132,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -434,6 +435,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/Server.ini b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/Server.ini index 4c1f46e67d..29958b5736 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java index 594796ad0c..e4cf4d220a 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java @@ -774,6 +774,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1389,6 +1395,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/GameServer.java index f325caebbe..0089164311 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/GameServer.java @@ -133,6 +133,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -436,6 +437,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/Server.ini b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/Server.ini index 20632ac4bb..4ce8c2859d 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java index a601ccd45a..f86bff6acb 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java @@ -778,6 +778,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1397,6 +1403,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/GameServer.java index a0dd1fee55..d2b8678aca 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/GameServer.java @@ -134,6 +134,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -440,6 +441,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/dist/game/config/Server.ini b/L2J_Mobius_Classic_Interlude/dist/game/config/Server.ini index 190cc64742..3253273fed 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/config/Server.ini +++ b/L2J_Mobius_Classic_Interlude/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java index f2a5549e7a..8043e5f202 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java @@ -783,6 +783,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1398,6 +1404,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/GameServer.java index 8155a37715..e23e81c7af 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/GameServer.java @@ -130,6 +130,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -432,6 +433,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/Server.ini b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/Server.ini index de1144cbad..c520efa6c4 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/Server.ini +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/Server.ini @@ -203,6 +203,37 @@ ClanNameTemplate = .* CharMaxNumber = 7 +# --------------------------------------------------------------------------- +# Precautionary Server Restart +# --------------------------------------------------------------------------- + +# Enable server restart when CPU or memory usage is too high. +# Default: False +PrecautionaryRestartEnabled = False + +# Enable monitoring system CPU usage. +# Default: True +PrecautionaryRestartCpu = True + +# Enable monitoring process memory usage. +# Default: False +PrecautionaryRestartMemory = False + +# Check if sieges are in progress +# or players are in olympiad, events, instances +# or have targeted raidbosses. +# Default: True +PrecautionaryRestartChecks = True + +# Percentage of used resources. +# Default: 95 +PrecautionaryRestartPercentage = 95 + +# Delay in seconds between each check. +# Default: 60 +PrecautionaryRestartDelay = 60 + + # --------------------------------------------------------------------------- # Scheduled Server Restart # --------------------------------------------------------------------------- diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java index 85d2663df9..55611ca976 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java @@ -778,6 +778,12 @@ public class Config public static int SERVER_RESTART_SCHEDULE_COUNTDOWN; public static String[] SERVER_RESTART_SCHEDULE; public static List SERVER_RESTART_DAYS; + public static boolean PRECAUTIONARY_RESTART_ENABLED; + public static boolean PRECAUTIONARY_RESTART_CPU; + public static boolean PRECAUTIONARY_RESTART_MEMORY; + public static boolean PRECAUTIONARY_RESTART_CHECKS; + public static int PRECAUTIONARY_RESTART_PERCENTAGE; + public static int PRECAUTIONARY_RESTART_DELAY; // -------------------------------------------------- // Vitality Settings @@ -1397,6 +1403,12 @@ public class Config SERVER_RESTART_DAYS.add(Integer.parseInt(day)); } } + PRECAUTIONARY_RESTART_ENABLED = serverSettings.getBoolean("PrecautionaryRestartEnabled", false); + PRECAUTIONARY_RESTART_CPU = serverSettings.getBoolean("PrecautionaryRestartCpu", true); + PRECAUTIONARY_RESTART_MEMORY = serverSettings.getBoolean("PrecautionaryRestartMemory", false); + PRECAUTIONARY_RESTART_CHECKS = serverSettings.getBoolean("PrecautionaryRestartChecks", true); + PRECAUTIONARY_RESTART_PERCENTAGE = serverSettings.getInt("PrecautionaryRestartPercentage", 95); + PRECAUTIONARY_RESTART_DELAY = serverSettings.getInt("PrecautionaryRestartDelay", 60) * 1000; // Hosts and Subnets final IPConfigData ipcd = new IPConfigData(); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/GameServer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/GameServer.java index b5a83852a5..88783cec91 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/GameServer.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/GameServer.java @@ -135,6 +135,7 @@ import org.l2jmobius.gameserver.instancemanager.MatchingRoomManager; import org.l2jmobius.gameserver.instancemanager.MentorManager; import org.l2jmobius.gameserver.instancemanager.PcCafePointsManager; import org.l2jmobius.gameserver.instancemanager.PetitionManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.PremiumManager; import org.l2jmobius.gameserver.instancemanager.PunishmentManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -442,6 +443,10 @@ public class GameServer ServerRestartManager.getInstance(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance(); + } if (Config.DEADLOCK_DETECTOR) { _deadDetectThread = new DeadLockDetector(Duration.ofSeconds(Config.DEADLOCK_CHECK_INTERVAL), () -> diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/Shutdown.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/Shutdown.java index 7abc1f3c8d..aed157de11 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/Shutdown.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/Shutdown.java @@ -35,6 +35,7 @@ import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager; import org.l2jmobius.gameserver.instancemanager.GrandBossManager; import org.l2jmobius.gameserver.instancemanager.ItemAuctionManager; import org.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager; +import org.l2jmobius.gameserver.instancemanager.PrecautionaryRestartManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; @@ -310,6 +311,11 @@ public class Shutdown extends Thread _counterInstance._abort(); } + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartEnabled(); + } + // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); @@ -325,6 +331,12 @@ public class Shutdown extends Thread if (_counterInstance != null) { _counterInstance._abort(); + + if (Config.PRECAUTIONARY_RESTART_ENABLED) + { + PrecautionaryRestartManager.getInstance().restartAborted(); + } + Broadcast.toAllOnlinePlayers("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!", false); } } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java new file mode 100644 index 0000000000..68229872bc --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/PrecautionaryRestartManager.java @@ -0,0 +1,196 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.instancemanager; + +import java.lang.management.ManagementFactory; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.gameserver.Shutdown; +import org.l2jmobius.gameserver.model.World; +import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.instance.GrandBossInstance; +import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import org.l2jmobius.gameserver.model.actor.instance.RaidBossInstance; +import org.l2jmobius.gameserver.model.siege.Castle; +import org.l2jmobius.gameserver.model.siege.Fort; +import org.l2jmobius.gameserver.util.Broadcast; + +/** + * @author Mobius + */ +public class PrecautionaryRestartManager +{ + private static final Logger LOGGER = Logger.getLogger(PrecautionaryRestartManager.class.getName()); + + private static final String SYSTEM_CPU_LOAD_VAR = "SystemCpuLoad"; + private static final String PROCESS_CPU_LOAD_VAR = "ProcessCpuLoad"; + + private static boolean _restarting = false; + + protected PrecautionaryRestartManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + if (_restarting) + { + return; + } + + if (Config.PRECAUTIONARY_RESTART_CPU && (getCpuLoad(SYSTEM_CPU_LOAD_VAR) > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: CPU usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + LOGGER.info("PrecautionaryRestartManager: Server is using " + getCpuLoad(PROCESS_CPU_LOAD_VAR) + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + + if (Config.PRECAUTIONARY_RESTART_MEMORY && (getProcessRamLoad() > Config.PRECAUTIONARY_RESTART_PERCENTAGE)) + { + if (serverBizzy()) + { + return; + } + + LOGGER.info("PrecautionaryRestartManager: Memory usage over " + Config.PRECAUTIONARY_RESTART_PERCENTAGE + "%."); + Broadcast.toAllOnlinePlayers("Server will restart in 10 minutes.", false); + Shutdown.getInstance().startShutdown(null, 600, true); + } + }, Config.PRECAUTIONARY_RESTART_DELAY, Config.PRECAUTIONARY_RESTART_DELAY); + } + + private static double getCpuLoad(String var) + { + try + { + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem"); + final AttributeList list = mbs.getAttributes(name, new String[] + { + var + }); + + if (list.isEmpty()) + { + return 0; + } + + final Attribute att = (Attribute) list.get(0); + final Double value = (Double) att.getValue(); + if (value == -1) + { + return 0; + } + + return (value * 1000) / 10d; + } + catch (Exception e) + { + } + + return 0; + } + + private static double getProcessRamLoad() + { + final Runtime runTime = Runtime.getRuntime(); + final long totalMemory = runTime.maxMemory(); + final long usedMemory = totalMemory - ((totalMemory - runTime.totalMemory()) + runTime.freeMemory()); + return (usedMemory * 100) / totalMemory; + } + + private boolean serverBizzy() + { + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if ((castle != null) && castle.getSiege().isInProgress()) + { + return true; + } + } + + for (Fort fort : FortManager.getInstance().getForts()) + { + if ((fort != null) && fort.getSiege().isInProgress()) + { + return true; + } + } + + for (PlayerInstance player : World.getInstance().getPlayers()) + { + if ((player == null) || player.isInOfflineMode()) + { + continue; + } + + if (player.isInOlympiadMode()) + { + return true; + } + + if (player.isOnEvent()) + { + return true; + } + + if (player.isInInstance()) + { + return true; + } + + final WorldObject target = player.getTarget(); + if ((target instanceof RaidBossInstance) || (target instanceof GrandBossInstance)) + { + return true; + } + } + + return false; + } + + public void restartEnabled() + { + _restarting = true; + } + + public void restartAborted() + { + _restarting = false; + } + + public static PrecautionaryRestartManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final PrecautionaryRestartManager INSTANCE = new PrecautionaryRestartManager(); + } +} \ No newline at end of file