mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-01 14:39:13 -06:00
Added MapArea interface for writing content, simplified the ClassScanner, added better javadoc
This commit is contained in:
parent
ae5fc443c7
commit
55126ce8a6
13 changed files with 151 additions and 132 deletions
|
|
@ -714,6 +714,15 @@ fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List<NPC>{
|
|||
return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param regionId the ID of the region
|
||||
* @return a [ZoneBorders] encapsulating the entire region indicated by the provided regionId
|
||||
*/
|
||||
fun getRegionBorders(regionId: Int) : ZoneBorders
|
||||
{
|
||||
return ZoneBorders.forRegion(regionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an attribute key from the Entity's attributes store
|
||||
* @param entity the entity to get the attribute from
|
||||
|
|
|
|||
7
Server/src/main/kotlin/api/ContentInterface.kt
Normal file
7
Server/src/main/kotlin/api/ContentInterface.kt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package api
|
||||
|
||||
interface ContentInterface {
|
||||
/**
|
||||
* A dummy/shell interface that allows us to more easily load content via class scanning
|
||||
*/
|
||||
}
|
||||
|
|
@ -2,7 +2,12 @@ package api
|
|||
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
interface LoginListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute some code when a player logs in.
|
||||
*
|
||||
* Login listeners are called *before* [PersistPlayer] data is parsed.
|
||||
*/
|
||||
interface LoginListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference any non-static class-local variables.
|
||||
* If you need to access a player's specific instance, use an attribute.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ package api
|
|||
|
||||
import core.game.node.entity.player.Player
|
||||
|
||||
interface LogoutListener {
|
||||
/**
|
||||
* An interface for writing content that allows code to be executed by the class when a player logs out.
|
||||
*
|
||||
* Logout listeners are called *before* [PersistPlayer] data is saved.
|
||||
*/
|
||||
interface LogoutListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference any non-static class-local variables.
|
||||
* If you need to access a player's specific instance, use an attribute.
|
||||
|
|
|
|||
18
Server/src/main/kotlin/api/MapArea.kt
Normal file
18
Server/src/main/kotlin/api/MapArea.kt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package api
|
||||
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneRestriction
|
||||
|
||||
/**
|
||||
* Interface that allows a class to define a map area.
|
||||
* Optionally-overridable methods include [getRestrictions], [areaEnter], [areaLeave] and [entityStep]
|
||||
*/
|
||||
interface MapArea {
|
||||
fun defineAreaBorders() : Array<ZoneBorders>
|
||||
fun getRestrictions() : Array<ZoneRestriction> {return arrayOf()}
|
||||
fun areaEnter(entity: Entity) {}
|
||||
fun areaLeave(entity: Entity) {}
|
||||
fun entityStep(entity: Entity, location: Location, lastLocation: Location) {}
|
||||
}
|
||||
|
|
@ -3,7 +3,14 @@ package api
|
|||
import core.game.node.entity.player.Player
|
||||
import org.json.simple.JSONObject
|
||||
|
||||
interface PersistPlayer {
|
||||
/**
|
||||
* An interface for writing content that allows data to be saved and loaded from player saves.
|
||||
*
|
||||
* Parsing is called *after* any [LoginListener] is executed.
|
||||
*
|
||||
* Saving is called *after* any [LogoutListener] is executed.
|
||||
*/
|
||||
interface PersistPlayer : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
* You need to fetch a player's specific instance of the data and save from that.
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
package api
|
||||
|
||||
interface PersistWorld {
|
||||
fun saveWorld()
|
||||
fun parseWorld()
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@ package api
|
|||
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
interface ShutdownListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute code as the server is shutting down
|
||||
*/
|
||||
interface ShutdownListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package api
|
|||
|
||||
import rs09.game.system.SystemLogger
|
||||
|
||||
interface StartupListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to execute code when the server is started.
|
||||
*/
|
||||
interface StartupListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package api
|
||||
|
||||
interface TickListener {
|
||||
/**
|
||||
* An interface for writing content that allows the class to be updated each tick.
|
||||
*/
|
||||
interface TickListener : ContentInterface {
|
||||
/**
|
||||
* NOTE: This should NOT reference nonstatic class-local variables.
|
||||
* TickListeners are generally for NON-player, WORLD tick events.
|
||||
|
|
|
|||
|
|
@ -6,75 +6,52 @@ import core.game.node.entity.Entity
|
|||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.zone.MapZone
|
||||
import core.game.world.map.zone.ZoneBorders
|
||||
import core.game.world.map.zone.ZoneBuilder
|
||||
import core.plugin.Initializable
|
||||
import core.plugin.Plugin
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.game.content.dialogue.DialogueFile
|
||||
import rs09.tools.secondsToTicks
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Initializable
|
||||
class FarmingPatchZone : MapZone("farming patch", true), Plugin<Any> {
|
||||
class FarmingPatchZone : MapArea, TickListener {
|
||||
private val playersInZone = hashMapOf<Player,Int>()
|
||||
|
||||
val playersInZone = hashMapOf<Player,Int>()
|
||||
|
||||
override fun configure() {
|
||||
registerRegion(12083)
|
||||
registerRegion(10548)
|
||||
register(ZoneBorders(3594,3521,3608,3532))
|
||||
submitWorldPulse(zonePulse)
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(
|
||||
getRegionBorders(12083),
|
||||
getRegionBorders(10548),
|
||||
ZoneBorders(3594,3521,3608,3532)
|
||||
)
|
||||
}
|
||||
|
||||
override fun newInstance(arg: Any?): Plugin<Any> {
|
||||
ZoneBuilder.configure(this)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun fireEvent(identifier: String?, vararg args: Any?): Any {
|
||||
return Unit
|
||||
}
|
||||
|
||||
override fun enter(e: Entity?): Boolean {
|
||||
if(e is Player && playersInZone[e] == null && getStatLevel(e, Skills.FARMING) <= 15) {
|
||||
playersInZone[e] = 0
|
||||
override fun areaEnter(entity: Entity) {
|
||||
if(entity is Player && playersInZone[entity] == null && getStatLevel(entity, Skills.FARMING) <= 15) {
|
||||
playersInZone[entity] = 0
|
||||
}
|
||||
return super.enter(e)
|
||||
}
|
||||
|
||||
override fun leave(e: Entity?, logout: Boolean): Boolean {
|
||||
if(e is Player){
|
||||
playersInZone.remove(e)
|
||||
}
|
||||
return super.enter(e)
|
||||
override fun areaLeave(entity: Entity) {
|
||||
if(entity is Player)
|
||||
playersInZone.remove(entity)
|
||||
}
|
||||
|
||||
val zonePulse = object : Pulse(){
|
||||
override fun pulse(): Boolean {
|
||||
playersInZone.toList().forEach { (player, ticks) ->
|
||||
if(ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(5).toInt())){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(true), npc)
|
||||
} else if (ticks == secondsToTicks(TimeUnit.MINUTES.toSeconds(10).toInt())){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(false), npc)
|
||||
playersInZone.remove(player)
|
||||
}
|
||||
|
||||
playersInZone[player] = ticks + 1
|
||||
override fun tick() {
|
||||
playersInZone.toList().forEach { (player, ticks) ->
|
||||
if(ticks == 500){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(true), npc)
|
||||
} else if (ticks == 1000){
|
||||
val npc = NPC(NPCs.GITHAN_7122)
|
||||
npc.location = player.location
|
||||
npc.init()
|
||||
npc.moveStep()
|
||||
npc.face(player)
|
||||
openDialogue(player, SpiritDialogue(false), npc)
|
||||
playersInZone.remove(player)
|
||||
}
|
||||
return false
|
||||
|
||||
playersInZone[player] = ticks + 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,5 +71,4 @@ class FarmingPatchZone : MapZone("farming patch", true), Plugin<Any> {
|
|||
poofClear(npc ?: return)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package rs09.game.node.entity.player.diary.seers
|
||||
|
||||
import api.MapArea
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.diary.DiaryType
|
||||
|
|
@ -10,32 +11,16 @@ import core.game.world.map.zone.ZoneBuilder
|
|||
import core.plugin.Initializable
|
||||
import core.plugin.Plugin
|
||||
|
||||
@Initializable
|
||||
class SeersCourthouseZone : MapZone("seers-courthouse", true), Plugin<Any?>{
|
||||
|
||||
override fun newInstance(arg: Any?): SeersCourthouseZone {
|
||||
ZoneBuilder.configure(this)
|
||||
return this
|
||||
class SeersCourthouseZone : MapArea {
|
||||
override fun defineAreaBorders(): Array<ZoneBorders> {
|
||||
return arrayOf(ZoneBorders(2735,3471,2736,3471))
|
||||
}
|
||||
|
||||
override fun configure() {
|
||||
super.register(ZoneBorders(2735,3471,2736,3471))
|
||||
}
|
||||
|
||||
override fun enter(e: Entity?): Boolean {
|
||||
if(e is Player && !e.isArtificial){
|
||||
if(!e.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE,2,3) && e.prayer.active.contains(PrayerType.PIETY)){
|
||||
e.achievementDiaryManager.finishTask(e,DiaryType.SEERS_VILLAGE,2,3)
|
||||
override fun areaEnter(entity: Entity) {
|
||||
if(entity is Player && !entity.isArtificial){
|
||||
if(!entity.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE,2,3) && entity.prayer.active.contains(PrayerType.PIETY)){
|
||||
entity.achievementDiaryManager.finishTask(entity,DiaryType.SEERS_VILLAGE,2,3)
|
||||
}
|
||||
}
|
||||
return super.enter(e)
|
||||
}
|
||||
|
||||
override fun fireEvent(identifier: String?, vararg args: Any?): Any {
|
||||
return Unit
|
||||
}
|
||||
|
||||
override fun leave(e: Entity?, logout: Boolean): Boolean {
|
||||
return super.leave(e, logout)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,14 @@ import api.*
|
|||
import core.game.content.activity.ActivityManager
|
||||
import core.game.content.activity.ActivityPlugin
|
||||
import core.game.content.dialogue.DialoguePlugin
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.player.info.login.LoginConfiguration
|
||||
import core.game.node.entity.player.link.quest.Quest
|
||||
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
|
||||
|
|
@ -15,6 +20,7 @@ import io.github.classgraph.ClassInfo
|
|||
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
|
||||
|
|
@ -68,14 +74,47 @@ 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 {
|
||||
try {
|
||||
definePlugin(p.loadClass().newInstance() as Plugin<Object>)
|
||||
} catch (e: InstantiationException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
val clazz = it.loadClass().newInstance()
|
||||
if(clazz is LoginListener) GameWorld.loginListeners.add(clazz)
|
||||
if(clazz is LogoutListener) GameWorld.logoutListeners.add(clazz)
|
||||
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 PersistPlayer) {
|
||||
PlayerSaver.contentHooks.add(clazz)
|
||||
PlayerSaveParser.contentHooks.add(clazz)
|
||||
}
|
||||
if(clazz is MapArea)
|
||||
{
|
||||
val zone = object : MapZone(clazz.javaClass.simpleName + "MapArea", true, *clazz.getRestrictions()){
|
||||
override fun enter(e: Entity?): Boolean {
|
||||
clazz.areaEnter(e ?: return super.enter(null))
|
||||
return super.enter(e)
|
||||
}
|
||||
|
||||
override fun leave(e: Entity?, logout: Boolean): Boolean {
|
||||
clazz.areaLeave(e ?: return super.leave(null, logout))
|
||||
return super.leave(e, logout)
|
||||
}
|
||||
|
||||
override fun move(e: Entity?, from: Location?, to: Location?): Boolean {
|
||||
if(e != null && from != null && to != null) clazz.entityStep(e, to, from)
|
||||
return super.move(e, from, to)
|
||||
}
|
||||
}
|
||||
for(border in clazz.defineAreaBorders()) zone.register(border)
|
||||
ZoneBuilder.configure(zone)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
SystemLogger.logErr("Error loading content: ${it.simpleName}, ${e.localizedMessage}")
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
result.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()
|
||||
|
|
@ -83,41 +122,6 @@ object ClassScanner {
|
|||
PlayerScripts.identifierMap[identifier] =
|
||||
PlayerScripts.PlayerScript(identifier, description, name, res.loadClass())
|
||||
}
|
||||
result.getClassesImplementing("api.StartupListener").filter { !it.isAbstract }.forEach {
|
||||
try {
|
||||
val clazz = it.loadClass().newInstance() as StartupListener
|
||||
GameWorld.startupListeners.add(clazz)
|
||||
} catch (e: Exception)
|
||||
{
|
||||
SystemLogger.logErr("Error loading startup listener: ${it.simpleName}, ${e.localizedMessage}")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
result.getClassesImplementing("api.ShutdownListener").filter { !it.isAbstract }.forEach {
|
||||
val clazz = it.loadClass().newInstance() as ShutdownListener
|
||||
GameWorld.shutdownListeners.add(clazz)
|
||||
}
|
||||
result.getClassesImplementing("api.LoginListener").filter { !it.isAbstract }.forEach {
|
||||
val clazz = it.loadClass().newInstance() as LoginListener
|
||||
GameWorld.loginListeners.add(clazz)
|
||||
}
|
||||
result.getClassesImplementing("api.LogoutListener").filter { !it.isAbstract }.forEach {
|
||||
val clazz = it.loadClass().newInstance() as LogoutListener
|
||||
GameWorld.logoutListeners.add(clazz)
|
||||
}
|
||||
result.getClassesImplementing("api.TickListener").filter { !it.isAbstract }.forEach {
|
||||
val clazz = it.loadClass().newInstance() as TickListener
|
||||
GameWorld.tickListeners.add(clazz)
|
||||
}
|
||||
result.getClassesImplementing("api.PersistPlayer").filter { !it.isAbstract }.forEach {
|
||||
val clazz = it.loadClass().newInstance() as PersistPlayer
|
||||
PlayerSaver.contentHooks.add(clazz)
|
||||
PlayerSaveParser.contentHooks.add(clazz)
|
||||
}
|
||||
result.getClassesImplementing("api.PersistWorld").filter { !it.isAbstract }.forEach {
|
||||
val clazz = it.loadClass().newInstance() as PersistPlayer
|
||||
PlayerSaver.contentHooks.add(clazz)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue