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;
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) {
@@ -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()
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)
+6 -3
View File
@@ -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()){
@@ -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()
}
}
@@ -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<PersistWorld>()
/**
* 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)
@@ -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<String, Boolean>()
/**
* 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<String>
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()
}
+1 -2
View File
@@ -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()
}
}