Class scanning now distinguishes side effectful plugins from our pure content interfaces

Server store is now loaded and parsed ***before*** side effectful plugins
This commit is contained in:
Ceikry
2022-05-15 11:20:27 +00:00
committed by Ryan
parent ebc540c7c7
commit 24cea604fc
8 changed files with 78 additions and 46 deletions
@@ -1,5 +1,6 @@
package core.game.system; package core.game.system;
import api.PersistWorld;
import api.ShutdownListener; import api.ShutdownListener;
import core.game.ge.GrandExchangeDatabase; import core.game.ge.GrandExchangeDatabase;
import core.game.interaction.object.dmc.DMCHandler; import core.game.interaction.object.dmc.DMCHandler;
@@ -52,6 +53,7 @@ public final class SystemTermination {
} }
} }
GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown); GameWorld.getShutdownListeners().forEach(ShutdownListener::shutdown);
GameWorld.getWorldPersists().forEach(PersistWorld::save);
if(ServerConstants.DATA_PATH != null) if(ServerConstants.DATA_PATH != null)
save(ServerConstants.DATA_PATH); save(ServerConstants.DATA_PATH);
} catch (Throwable e) { } catch (Throwable e) {
@@ -0,0 +1,6 @@
package api
interface PersistWorld : ContentInterface {
fun save()
fun parse()
}
-2
View File
@@ -67,8 +67,6 @@ object Server {
} }
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
val t = TimeStamp() val t = TimeStamp()
SystemLogger.logInfo("Initializing Server Store...")
SystemLogger.logInfo("Initialized ${ServerStore.counter} store files.")
GameWorld.prompt(true) GameWorld.prompt(true)
SQLManager.init() SQLManager.init()
Runtime.getRuntime().addShutdownHook(ServerConstants.SHUTDOWN_HOOK) Runtime.getRuntime().addShutdownHook(ServerConstants.SHUTDOWN_HOOK)
+6 -3
View File
@@ -1,5 +1,6 @@
package rs09 package rs09
import api.PersistWorld
import api.ShutdownListener import api.ShutdownListener
import api.StartupListener import api.StartupListener
import org.json.simple.JSONArray import org.json.simple.JSONArray
@@ -13,8 +14,8 @@ import java.io.FileReader
import java.io.FileWriter import java.io.FileWriter
import javax.script.ScriptEngineManager import javax.script.ScriptEngineManager
class ServerStore : StartupListener, ShutdownListener { class ServerStore : PersistWorld {
override fun startup() { override fun parse() {
logStartup("Parsing server store...") logStartup("Parsing server store...")
val dir = File(ServerConstants.STORE_PATH!!) val dir = File(ServerConstants.STORE_PATH!!)
if(!dir.exists()){ if(!dir.exists()){
@@ -41,9 +42,11 @@ class ServerStore : StartupListener, ShutdownListener {
return@forEach return@forEach
} }
} }
logStartup("Initialized $counter store files.")
} }
override fun shutdown() { override fun save() {
logShutdown("Saving server store...") logShutdown("Saving server store...")
val dir = File(ServerConstants.DATA_PATH + File.separator + "serverstore") val dir = File(ServerConstants.DATA_PATH + File.separator + "serverstore")
if(!dir.exists()){ if(!dir.exists()){
@@ -4,14 +4,12 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ConfigParser { class ConfigParser {
fun prePlugin() { fun parseConfigs() {
NPCConfigParser().load() NPCConfigParser().load()
ItemConfigParser().load() ItemConfigParser().load()
ObjectConfigParser().load() ObjectConfigParser().load()
XteaParser().load() XteaParser().load()
InterfaceConfigParser().load() InterfaceConfigParser().load()
}
fun postPlugin() {
ShopParser().load() ShopParser().load()
DropTableParser().load() DropTableParser().load()
NPCSpawner().load() NPCSpawner().load()
@@ -13,9 +13,7 @@ import core.game.world.map.RegionManager
import core.plugin.CorePluginTypes.StartupPlugin import core.plugin.CorePluginTypes.StartupPlugin
import core.tools.RandomFunction import core.tools.RandomFunction
import core.tools.mysql.DatabaseManager import core.tools.mysql.DatabaseManager
import rs09.game.ai.general.scriptrepository.PlayerScripts
import rs09.ServerConstants import rs09.ServerConstants
import rs09.game.node.entity.state.newsys.StateRepository
import rs09.game.system.SystemLogger import rs09.game.system.SystemLogger
import rs09.game.system.SystemLogger.logInfo import rs09.game.system.SystemLogger.logInfo
import rs09.game.system.config.ConfigParser import rs09.game.system.config.ConfigParser
@@ -32,6 +30,8 @@ import kotlin.collections.ArrayList
* @author Ceikry * @author Ceikry
*/ */
object GameWorld { object GameWorld {
@JvmStatic
val worldPersists = ArrayList<PersistWorld>()
/** /**
* The major update worker. * The major update worker.
@@ -160,9 +160,11 @@ object GameWorld {
Cache.init(ServerConstants.CACHE_PATH) Cache.init(ServerConstants.CACHE_PATH)
databaseManager = DatabaseManager(ServerConstants.DATABASE) databaseManager = DatabaseManager(ServerConstants.DATABASE)
databaseManager!!.connect() databaseManager!!.connect()
configParser.prePlugin() ConfigParser().parseConfigs()
ClassScanner.scanAndLoad() ClassScanner.scanClasspath()
configParser.postPlugin() ClassScanner.loadPureInterfaces()
worldPersists.forEach { it.parse() }
ClassScanner.loadSideEffectfulPlugins()
startupListeners.forEach { it.startup() } startupListeners.forEach { it.startup() }
if (run) { if (run) {
SystemManager.flag(if (settings?.isDevMode == true) SystemState.PRIVATE else SystemState.ACTIVE) SystemManager.flag(if (settings?.isDevMode == true) SystemState.PRIVATE else SystemState.ACTIVE)
@@ -11,21 +11,19 @@ import core.game.node.entity.player.link.quest.QuestRepository
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.map.zone.MapZone import core.game.world.map.zone.MapZone
import core.game.world.map.zone.ZoneBuilder import core.game.world.map.zone.ZoneBuilder
import core.game.world.map.zone.ZoneMonitor
import core.plugin.Plugin import core.plugin.Plugin
import core.plugin.PluginManifest import core.plugin.PluginManifest
import core.plugin.PluginType import core.plugin.PluginType
import io.github.classgraph.ClassGraph import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo import io.github.classgraph.ClassInfo
import io.github.classgraph.ScanResult
import rs09.game.ai.general.scriptrepository.PlayerScripts import rs09.game.ai.general.scriptrepository.PlayerScripts
import rs09.game.interaction.InteractionListener import rs09.game.interaction.InteractionListener
import rs09.game.interaction.InterfaceListener 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.PlayerSaveParser
import rs09.game.node.entity.player.info.login.PlayerSaver 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.SystemLogger
import rs09.game.system.command.Command import rs09.game.system.SystemLogger.logStartup
import rs09.game.world.GameWorld import rs09.game.world.GameWorld
import java.util.* import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
@@ -37,11 +35,14 @@ import java.util.function.Consumer
object ClassScanner { object ClassScanner {
var disabledPlugins = HashMap<String, Boolean>() var disabledPlugins = HashMap<String, Boolean>()
/** /**
* The amount of plugins loaded. * The amount of content interfaces loaded.
*/ */
var amountLoaded = 0 var amountLoaded = 0
private set private set
var numPlugins = 0
private set
/** /**
* The currently loaded plugin names. * The currently loaded plugin names.
*/ */
@@ -52,16 +53,20 @@ object ClassScanner {
*/ */
private val lastLoaded: String? = null 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 * Scan the classpath for reflection-loaded content classes such as listeners, "plugins", etc
*/ */
@JvmStatic @JvmStatic
fun scanAndLoad() { fun loadPureInterfaces() {
try { try {
load() loadContentInterfacesFrom(scanResults)
loadedPlugins!!.clear() logStartup("Loaded $amountLoaded content interfaces.")
loadedPlugins = null
SystemLogger.logInfo("Initialized $amountLoaded plugins...")
} catch (t: Throwable) { } catch (t: Throwable) {
SystemLogger.logErr("Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) SystemLogger.logErr("Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded)
t.printStackTrace() t.printStackTrace()
@@ -71,13 +76,8 @@ object ClassScanner {
} }
} }
fun load() { private fun loadContentInterfacesFrom(scanResults: ScanResult) {
val result = ClassGraph().enableClassInfo().enableAnnotationInfo().scan() scanResults.getClassesImplementing("api.ContentInterface").filter { !it.isAbstract }.forEach {
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 {
try { try {
val clazz = it.loadClass().newInstance() val clazz = it.loadClass().newInstance()
if(clazz is LoginListener) GameWorld.loginListeners.add(clazz) 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 TickListener) GameWorld.tickListeners.add(clazz)
if(clazz is StartupListener) GameWorld.startupListeners.add(clazz) if(clazz is StartupListener) GameWorld.startupListeners.add(clazz)
if(clazz is ShutdownListener) GameWorld.shutdownListeners.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 InteractionListener) clazz.defineListeners().also { clazz.defineDestinationOverrides() }
if(clazz is InterfaceListener) clazz.defineInterfaceListeners() if(clazz is InterfaceListener) clazz.defineInterfaceListeners()
if(clazz is Commands) clazz.defineCommands() if(clazz is Commands) clazz.defineCommands()
@@ -115,12 +116,35 @@ object ClassScanner {
SystemLogger.logInfo("Configured zone: ${clazz.javaClass.simpleName + "MapArea"}") SystemLogger.logInfo("Configured zone: ${clazz.javaClass.simpleName + "MapArea"}")
MapArea.zoneMaps[clazz.javaClass.simpleName + "MapArea"] = zone MapArea.zoneMaps[clazz.javaClass.simpleName + "MapArea"] = zone
} }
amountLoaded++
} catch (e: Exception) { } catch (e: Exception) {
SystemLogger.logErr("Error loading content: ${it.simpleName}, ${e.localizedMessage}") SystemLogger.logErr("Error loading content: ${it.simpleName}, ${e.localizedMessage}")
e.printStackTrace() 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<String> val description = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptDescription").parameterValues[0].value as Array<String>
val identifier = res.getAnnotationInfo("rs09.game.ai.general.scriptrepository.ScriptIdentifier").parameterValues[0].value.toString() 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() 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) else -> SystemLogger.logWarn("Unknown Manifest: " + manifest.type)
} }
} }
amountLoaded++ numPlugins++
} catch (e: Throwable) { } catch (e: Throwable) {
e.printStackTrace() e.printStackTrace()
} }
+1 -2
View File
@@ -47,8 +47,7 @@ object TestUtils {
if(ServerConstants.DATA_PATH == null) { if(ServerConstants.DATA_PATH == null) {
ServerConfigParser.parse(this::class.java.getResource("test.conf")) ServerConfigParser.parse(this::class.java.getResource("test.conf"))
Cache.init(this::class.java.getResource("cache").path.toString()) Cache.init(this::class.java.getResource("cache").path.toString())
ConfigParser().prePlugin() ConfigParser().parseConfigs()
ConfigParser().postPlugin()
} }
} }