diff --git a/Server/src/main/java/core/game/system/SystemTermination.java b/Server/src/main/java/core/game/system/SystemTermination.java index c59e468d6..73a38f450 100644 --- a/Server/src/main/java/core/game/system/SystemTermination.java +++ b/Server/src/main/java/core/game/system/SystemTermination.java @@ -1,5 +1,6 @@ package core.game.system; +import api.PersistWorld; import api.ShutdownListener; import core.game.ge.GrandExchangeDatabase; import core.game.interaction.object.dmc.DMCHandler; @@ -52,6 +53,7 @@ public final class SystemTermination { } } GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown); + GameWorld.getWorldPersists().forEach(PersistWorld::save); if(ServerConstants.DATA_PATH != null) save(ServerConstants.DATA_PATH); } catch (Throwable e) { diff --git a/Server/src/main/kotlin/api/PersistWorld.kt b/Server/src/main/kotlin/api/PersistWorld.kt new file mode 100644 index 000000000..ac905eafd --- /dev/null +++ b/Server/src/main/kotlin/api/PersistWorld.kt @@ -0,0 +1,6 @@ +package api + +interface PersistWorld : ContentInterface { + fun save() + fun parse() +} diff --git a/Server/src/main/kotlin/rs09/Server.kt b/Server/src/main/kotlin/rs09/Server.kt index bfb0bc416..a295b5ce1 100644 --- a/Server/src/main/kotlin/rs09/Server.kt +++ b/Server/src/main/kotlin/rs09/Server.kt @@ -67,8 +67,6 @@ object Server { } startTime = System.currentTimeMillis() val t = TimeStamp() - SystemLogger.logInfo("Initializing Server Store...") - SystemLogger.logInfo("Initialized ${ServerStore.counter} store files.") GameWorld.prompt(true) SQLManager.init() Runtime.getRuntime().addShutdownHook(ServerConstants.SHUTDOWN_HOOK) diff --git a/Server/src/main/kotlin/rs09/ServerStore.kt b/Server/src/main/kotlin/rs09/ServerStore.kt index c40037bdb..317fb9e6b 100644 --- a/Server/src/main/kotlin/rs09/ServerStore.kt +++ b/Server/src/main/kotlin/rs09/ServerStore.kt @@ -1,5 +1,6 @@ package rs09 +import api.PersistWorld import api.ShutdownListener import api.StartupListener import org.json.simple.JSONArray @@ -13,8 +14,8 @@ import java.io.FileReader import java.io.FileWriter import javax.script.ScriptEngineManager -class ServerStore : StartupListener, ShutdownListener { - override fun startup() { +class ServerStore : PersistWorld { + override fun parse() { logStartup("Parsing server store...") val dir = File(ServerConstants.STORE_PATH!!) if(!dir.exists()){ @@ -41,9 +42,11 @@ class ServerStore : StartupListener, ShutdownListener { return@forEach } } + + logStartup("Initialized $counter store files.") } - override fun shutdown() { + override fun save() { logShutdown("Saving server store...") val dir = File(ServerConstants.DATA_PATH + File.separator + "serverstore") if(!dir.exists()){ diff --git a/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt b/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt index 99b15d2e1..5f9b0b910 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt +++ b/Server/src/main/kotlin/rs09/game/system/config/ConfigParser.kt @@ -4,20 +4,18 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch class ConfigParser { - fun prePlugin() { - NPCConfigParser().load() - ItemConfigParser().load() - ObjectConfigParser().load() - XteaParser().load() - InterfaceConfigParser().load() - } - fun postPlugin() { - ShopParser().load() - DropTableParser().load() - NPCSpawner().load() - DoorConfigLoader().load() - GroundSpawnLoader().load() - MusicConfigLoader().load() - RangedConfigLoader().load() + fun parseConfigs() { + NPCConfigParser().load() + ItemConfigParser().load() + ObjectConfigParser().load() + XteaParser().load() + InterfaceConfigParser().load() + ShopParser().load() + DropTableParser().load() + NPCSpawner().load() + DoorConfigLoader().load() + GroundSpawnLoader().load() + MusicConfigLoader().load() + RangedConfigLoader().load() } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/world/GameWorld.kt b/Server/src/main/kotlin/rs09/game/world/GameWorld.kt index 1508846e3..4ef4db688 100644 --- a/Server/src/main/kotlin/rs09/game/world/GameWorld.kt +++ b/Server/src/main/kotlin/rs09/game/world/GameWorld.kt @@ -13,9 +13,7 @@ import core.game.world.map.RegionManager import core.plugin.CorePluginTypes.StartupPlugin import core.tools.RandomFunction import core.tools.mysql.DatabaseManager -import rs09.game.ai.general.scriptrepository.PlayerScripts import rs09.ServerConstants -import rs09.game.node.entity.state.newsys.StateRepository import rs09.game.system.SystemLogger import rs09.game.system.SystemLogger.logInfo import rs09.game.system.config.ConfigParser @@ -32,6 +30,8 @@ import kotlin.collections.ArrayList * @author Ceikry */ object GameWorld { + @JvmStatic + val worldPersists = ArrayList() /** * The major update worker. @@ -160,9 +160,11 @@ object GameWorld { Cache.init(ServerConstants.CACHE_PATH) databaseManager = DatabaseManager(ServerConstants.DATABASE) databaseManager!!.connect() - configParser.prePlugin() - ClassScanner.scanAndLoad() - configParser.postPlugin() + ConfigParser().parseConfigs() + ClassScanner.scanClasspath() + ClassScanner.loadPureInterfaces() + worldPersists.forEach { it.parse() } + ClassScanner.loadSideEffectfulPlugins() startupListeners.forEach { it.startup() } if (run) { SystemManager.flag(if (settings?.isDevMode == true) SystemState.PRIVATE else SystemState.ACTIVE) diff --git a/Server/src/main/kotlin/rs09/plugin/ClassScanner.kt b/Server/src/main/kotlin/rs09/plugin/ClassScanner.kt index 8ec560e9f..28dea9198 100644 --- a/Server/src/main/kotlin/rs09/plugin/ClassScanner.kt +++ b/Server/src/main/kotlin/rs09/plugin/ClassScanner.kt @@ -11,21 +11,19 @@ import core.game.node.entity.player.link.quest.QuestRepository import core.game.world.map.Location import core.game.world.map.zone.MapZone import core.game.world.map.zone.ZoneBuilder -import core.game.world.map.zone.ZoneMonitor import core.plugin.Plugin import core.plugin.PluginManifest import core.plugin.PluginType import io.github.classgraph.ClassGraph import io.github.classgraph.ClassInfo +import io.github.classgraph.ScanResult import rs09.game.ai.general.scriptrepository.PlayerScripts import rs09.game.interaction.InteractionListener import rs09.game.interaction.InterfaceListener -import rs09.game.interaction.Listener import rs09.game.node.entity.player.info.login.PlayerSaveParser import rs09.game.node.entity.player.info.login.PlayerSaver -import rs09.game.node.entity.skill.magic.SpellListener import rs09.game.system.SystemLogger -import rs09.game.system.command.Command +import rs09.game.system.SystemLogger.logStartup import rs09.game.world.GameWorld import java.util.* import java.util.function.Consumer @@ -37,11 +35,14 @@ import java.util.function.Consumer object ClassScanner { var disabledPlugins = HashMap() /** - * The amount of plugins loaded. + * The amount of content interfaces loaded. */ var amountLoaded = 0 private set + var numPlugins = 0 + private set + /** * The currently loaded plugin names. */ @@ -52,16 +53,20 @@ object ClassScanner { */ private val lastLoaded: String? = null + lateinit var scanResults: ScanResult + + @JvmStatic fun scanClasspath() { + scanResults = ClassGraph().enableClassInfo().enableAnnotationInfo().scan() + } + /** * Scan the classpath for reflection-loaded content classes such as listeners, "plugins", etc */ @JvmStatic - fun scanAndLoad() { + fun loadPureInterfaces() { try { - load() - loadedPlugins!!.clear() - loadedPlugins = null - SystemLogger.logInfo("Initialized $amountLoaded plugins...") + loadContentInterfacesFrom(scanResults) + logStartup("Loaded $amountLoaded content interfaces.") } catch (t: Throwable) { SystemLogger.logErr("Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) t.printStackTrace() @@ -71,13 +76,8 @@ object ClassScanner { } } - fun load() { - val result = ClassGraph().enableClassInfo().enableAnnotationInfo().scan() - result.getClassesWithAnnotation("core.plugin.Initializable").forEach(Consumer { p: ClassInfo -> - val clazz = p.loadClass().newInstance() - if(clazz is Plugin<*>) definePlugin(clazz) - }) - result.getClassesImplementing("api.ContentInterface").filter { !it.isAbstract }.forEach { + private fun loadContentInterfacesFrom(scanResults: ScanResult) { + scanResults.getClassesImplementing("api.ContentInterface").filter { !it.isAbstract }.forEach { try { val clazz = it.loadClass().newInstance() if(clazz is LoginListener) GameWorld.loginListeners.add(clazz) @@ -85,6 +85,7 @@ object ClassScanner { if(clazz is TickListener) GameWorld.tickListeners.add(clazz) if(clazz is StartupListener) GameWorld.startupListeners.add(clazz) if(clazz is ShutdownListener) GameWorld.shutdownListeners.add(clazz) + if(clazz is PersistWorld) GameWorld.worldPersists.add(clazz) if(clazz is InteractionListener) clazz.defineListeners().also { clazz.defineDestinationOverrides() } if(clazz is InterfaceListener) clazz.defineInterfaceListeners() if(clazz is Commands) clazz.defineCommands() @@ -115,12 +116,35 @@ object ClassScanner { SystemLogger.logInfo("Configured zone: ${clazz.javaClass.simpleName + "MapArea"}") MapArea.zoneMaps[clazz.javaClass.simpleName + "MapArea"] = zone } + amountLoaded++ } catch (e: Exception) { SystemLogger.logErr("Error loading content: ${it.simpleName}, ${e.localizedMessage}") e.printStackTrace() } } - result.getClassesWithAnnotation("rs09.game.ai.general.scriptrepository.PlayerCompatible").forEach { res -> + } + + @JvmStatic + fun loadSideEffectfulPlugins() { + try { + loadedPlugins!!.clear() + loadPluginsFrom(scanResults) + logStartup("We still have $numPlugins legacy plugins being loaded.") + } catch (t: Throwable) { + SystemLogger.logErr("Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) + t.printStackTrace() + } catch (e: Exception) { + SystemLogger.logErr("Error initializing Plugins -> " + e.localizedMessage + " for file -> " + lastLoaded) + e.printStackTrace() + } + } + + private fun loadPluginsFrom(scanResults: ScanResult) { + scanResults.getClassesWithAnnotation("core.plugin.Initializable").forEach(Consumer { p: ClassInfo -> + val clazz = p.loadClass().newInstance() + if(clazz is Plugin<*>) definePlugin(clazz) + }) + scanResults.getClassesWithAnnotation("rs09.game.ai.general.scriptrepository.PlayerCompatible").forEach { res -> val description = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptDescription").parameterValues[0].value as Array val identifier = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptIdentifier").parameterValues[0].value.toString() val name = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptName").parameterValues[0].value.toString() @@ -171,7 +195,7 @@ object ClassScanner { else -> SystemLogger.logWarn("Unknown Manifest: " + manifest.type) } } - amountLoaded++ + numPlugins++ } catch (e: Throwable) { e.printStackTrace() } diff --git a/Server/src/test/kotlin/TestUtils.kt b/Server/src/test/kotlin/TestUtils.kt index 002d115c0..2f1e8bea8 100644 --- a/Server/src/test/kotlin/TestUtils.kt +++ b/Server/src/test/kotlin/TestUtils.kt @@ -47,8 +47,7 @@ object TestUtils { if(ServerConstants.DATA_PATH == null) { ServerConfigParser.parse(this::class.java.getResource("test.conf")) Cache.init(this::class.java.getResource("cache").path.toString()) - ConfigParser().prePlugin() - ConfigParser().postPlugin() + ConfigParser().parseConfigs() } }