Halt tick processing if the server isn't able to reach the internet when watchdog is enabled (configurable via server config options connectivity_check_url and connectivity_timeout)

This commit is contained in:
Ceikry 2024-07-18 08:07:35 +00:00 committed by Ryan
parent 53357d20f3
commit 806680517b
6 changed files with 71 additions and 13 deletions

View file

@ -3,20 +3,21 @@ package core
import core.api.log import core.api.log
import core.game.system.SystemManager import core.game.system.SystemManager
import core.game.system.SystemState import core.game.system.SystemState
import core.net.NioReactor
import core.tools.TimeStamp
import kotlinx.coroutines.*
import core.tools.SystemLogger
import core.game.system.config.ServerConfigParser import core.game.system.config.ServerConfigParser
import core.game.world.GameWorld import core.game.world.GameWorld
import core.game.world.repository.Repository import core.net.NioReactor
import core.tools.Log import core.tools.Log
import core.tools.NetworkReachability
import core.tools.TimeStamp
import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.io.FileWriter import java.io.FileWriter
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.lang.management.ThreadMXBean import java.lang.management.ThreadMXBean
import java.net.BindException import java.net.BindException
import java.net.URL
import java.util.* import java.util.*
import kotlin.math.max
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -43,6 +44,8 @@ object Server {
@JvmStatic @JvmStatic
var reactor: NioReactor? = null var reactor: NioReactor? = null
var networkReachability = NetworkReachability.Reachable
/** /**
* The main method, in this method we load background utilities such as * The main method, in this method we load background utilities such as
* cache and our world, then end with starting networking. * cache and our world, then end with starting networking.
@ -95,6 +98,11 @@ object Server {
GlobalScope.launch { GlobalScope.launch {
delay(20000) delay(20000)
while (running) { while (running) {
val timeStart = System.currentTimeMillis()
if (!checkConnectivity())
networkReachability = NetworkReachability.Unreachable
else
networkReachability = NetworkReachability.Reachable
if (System.currentTimeMillis() - lastHeartbeat > 7200 && running) { if (System.currentTimeMillis() - lastHeartbeat > 7200 && running) {
log(this::class.java, Log.ERR, "Triggering reboot due to heartbeat timeout") log(this::class.java, Log.ERR, "Triggering reboot due to heartbeat timeout")
log(this::class.java, Log.ERR, "Creating thread dump...") log(this::class.java, Log.ERR, "Creating thread dump...")
@ -115,12 +123,36 @@ object Server {
if (!SystemManager.isTerminated()) if (!SystemManager.isTerminated())
exitProcess(0) exitProcess(0)
} }
delay(625) val timeNow = System.currentTimeMillis()
delay(max(0L, 625 - (timeNow - timeStart)))
} }
} }
} }
} }
private fun checkConnectivity(): Boolean
{
//Has to be done this way because you can't actually ping in Java unless you run the whole thing as root
val urls = ServerConstants.CONNECTIVITY_CHECK_URL.split(",")
var timeout = ServerConstants.CONNECTIVITY_TIMEOUT
if (timeout * urls.size > 5000) //Limit timeout down to 5000ms so other watchdog functions continue as expected.
timeout = 5000 / urls.size
for (targetUrl in urls) {
try {
val url = URL(targetUrl)
val conn = url.openConnection()
conn.connectTimeout = timeout
conn.connect()
conn.getInputStream().close()
return true
} catch (e: Exception) {
log(this::class.java, Log.WARN, "${targetUrl} failed to respond. Are we offline?")
continue
}
}
return false
}
@JvmStatic @JvmStatic
fun heartbeat() { fun heartbeat() {
lastHeartbeat = System.currentTimeMillis() lastHeartbeat = System.currentTimeMillis()

View file

@ -338,5 +338,11 @@ class ServerConstants {
@JvmField @JvmField
var STARTUP_MOMENT = Calendar.getInstance() var STARTUP_MOMENT = Calendar.getInstance()
@JvmField
var CONNECTIVITY_CHECK_URL = "https://google.com,https://2009scape.org"
@JvmField
var CONNECTIVITY_TIMEOUT = 500
} }
} }

View file

@ -166,6 +166,8 @@ object ServerConfigParser {
ServerConstants.FORCE_EASTER_EVENTS = data.getBoolean("world.force_easter_randoms", false) ServerConstants.FORCE_EASTER_EVENTS = data.getBoolean("world.force_easter_randoms", false)
ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 581).toInt() ServerConstants.RUNECRAFTING_FORMULA_REVISION = data.getLong("world.runecrafting_formula_revision", 581).toInt()
ServerConstants.ENHANCED_DEEP_WILDERNESS = data.getBoolean("world.enhanced_deep_wilderness", false) ServerConstants.ENHANCED_DEEP_WILDERNESS = data.getBoolean("world.enhanced_deep_wilderness", false)
ServerConstants.CONNECTIVITY_CHECK_URL = data.getString("server.connectivity_check_url", "https://google.com,https://2009scape.org")
ServerConstants.CONNECTIVITY_TIMEOUT = data.getLong("server.connectivity_timeout", 500L).toInt()
val logLevel = data.getString("server.log_level", "VERBOSE").uppercase() val logLevel = data.getString("server.log_level", "VERBOSE").uppercase()
ServerConstants.LOG_LEVEL = parseEnumEntry<LogLevel>(logLevel) ?: LogLevel.VERBOSE ServerConstants.LOG_LEVEL = parseEnumEntry<LogLevel>(logLevel) ?: LogLevel.VERBOSE

View file

@ -0,0 +1,6 @@
package core.tools
enum class NetworkReachability {
Reachable,
Unreachable
}

View file

@ -1,25 +1,25 @@
package core.worker package core.worker
import core.api.submitWorldPulse
import core.game.system.task.Pulse
import core.plugin.CorePluginTypes.Managers
import core.Server import core.Server
import core.ServerConstants import core.ServerConstants
import core.ServerStore import core.ServerStore
import core.api.log import core.api.log
import core.tools.SystemLogger import core.api.submitWorldPulse
import core.game.system.task.Pulse
import core.game.world.GameWorld import core.game.world.GameWorld
import core.game.world.repository.Repository import core.game.world.repository.Repository
import core.game.world.update.UpdateSequence import core.game.world.update.UpdateSequence
import core.integrations.grafana.Grafana
import core.net.packet.PacketProcessor import core.net.packet.PacketProcessor
import core.net.packet.PacketWriteQueue import core.net.packet.PacketWriteQueue
import core.plugin.CorePluginTypes.Managers
import core.tools.Log import core.tools.Log
import core.tools.NetworkReachability
import core.tools.colorize import core.tools.colorize
import java.lang.Long.max import java.lang.Long.max
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.system.exitProcess import kotlin.system.exitProcess
import core.integrations.grafana.*
/** /**
* Handles the running of pulses and writing of masks, etc * Handles the running of pulses and writing of masks, etc
@ -38,7 +38,11 @@ class MajorUpdateWorker {
Grafana.startTick() Grafana.startTick()
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
Server.heartbeat() Server.heartbeat()
if (Server.networkReachability == NetworkReachability.Reachable)
handleTickActions() handleTickActions()
else
tickOffline()
for (player in Repository.players.filter { !it.isArtificial }) { for (player in Repository.players.filter { !it.isArtificial }) {
if (System.currentTimeMillis() - player.session.lastPing > 20000L) { if (System.currentTimeMillis() - player.session.lastPing > 20000L) {
@ -89,6 +93,12 @@ class MajorUpdateWorker {
log(this::class.java, Log.FINE, "Update worker stopped.") log(this::class.java, Log.FINE, "Update worker stopped.")
} }
fun tickOffline()
{
Repository.disconnectionQueue.update() //continue processing disconnection queue
GameWorld.pulse() //continue incrementing the global tick count
}
fun handleTickActions(skipPulseUpdate: Boolean = false) { fun handleTickActions(skipPulseUpdate: Boolean = false) {
try { try {
var packetStart = System.currentTimeMillis() var packetStart = System.currentTimeMillis()

View file

@ -22,7 +22,9 @@ noauth_default_admin = true #NOTE: If we are not using auth, this determines whe
#------------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------------
#The limit on how many different accounts a player can log into per day. #The limit on how many different accounts a player can log into per day.
daily_accounts_per_ip = 3 daily_accounts_per_ip = 3
watchdog_enabled = false watchdog_enabled = true
connectivity_check_url = "https://google.com,https://2009scape.org"
connectivity_timeout = 500
[database] [database]
database_name = "global" database_name = "global"