Reorganise kotlin message handlers

This commit is contained in:
Major
2019-07-22 23:55:12 +01:00
parent 0405639ed1
commit 8fb0c8f3df
11 changed files with 125 additions and 116 deletions
@@ -13,17 +13,29 @@ class KotlinCommandHandler(
val world: World, val world: World,
val command: String, val command: String,
privileges: PrivilegeLevel privileges: PrivilegeLevel
) : KotlinPlayerHandlerProxyTrait<Command>, CommandListener(privileges) { ) : CommandListener(privileges) {
override var callback: Command.(Player) -> Unit = {} var callback: Command.(Player) -> Unit = {}
override var predicate: Command.() -> Boolean = { true } var predicate: Command.() -> Boolean = { true }
override fun execute(player: Player, command: Command) { override fun execute(player: Player, command: Command) {
handleProxy(player, command) if (command.predicate()) {
command.callback(player)
}
} }
override fun register() { fun register() {
world.commandDispatcher.register(command, this) world.commandDispatcher.register(command, this)
} }
fun where(predicate: Command.() -> Boolean): KotlinCommandHandler {
this.predicate = predicate
return this
}
fun then(callback: Command.(Player) -> Unit) {
this.callback = callback
this.register()
}
} }
@@ -7,15 +7,15 @@ import org.apollo.game.plugin.PluginContext
import org.apollo.net.message.Message import org.apollo.net.message.Message
import kotlin.reflect.KClass import kotlin.reflect.KClass
class KotlinMessageHandler<T : ListenableContext, F : Message>( class KotlinMessageHandler<F : Message, T : ListenableContext>(
world: World, world: World,
private val listenable: MessageListenable<T, F>, private val listenable: MessageListenable<F, T>,
private val callback: T.() -> Unit private val callback: T.() -> Unit
) : MessageHandler<F>(world) { ) : MessageHandler<F>(world) {
override fun handle(player: Player, message: F) { override fun handle(player: Player, message: F) {
val context = listenable.from(player, message) val context = listenable.createContext(player, message)
context.callback() context?.callback()
} }
} }
@@ -20,34 +20,34 @@ abstract class KotlinPluginScript(var world: World, val context: PluginContext)
private var stopListener: (World) -> Unit = { _ -> } private var stopListener: (World) -> Unit = { _ -> }
fun <T : ListenableContext, F : Any> on(listenable: Listenable<T, F>, callback: T.() -> Unit) { fun <T : Any, C : ListenableContext> on(listenable: Listenable<T, C>, callback: C.() -> Unit) {
// Smart-casting/type-inference is completely broken in this function in intelliJ, so assign to otherwise // Smart-casting/type-inference is completely broken in this function in intelliJ, so assign to otherwise
// pointless `l` values for now. // pointless `l` values for now.
return when (listenable) { return when (listenable) {
is MessageListenable -> { is MessageListenable -> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val l = listenable as MessageListenable<T, Message> val l = listenable as MessageListenable<Message, C>
val handler = KotlinMessageHandler(world, l, callback) val handler = KotlinMessageHandler(world, l, callback)
context.addMessageHandler(l.type.java, handler) context.addMessageHandler(l.type.java, handler)
} }
is PlayerEventListenable -> { is PlayerEventListenable -> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val l = listenable as PlayerEventListenable<T, PlayerEvent> val l = listenable as PlayerEventListenable<PlayerEvent, C>
world.listenFor(l.type.java) { event -> world.listenFor(l.type.java) { event ->
val context = l.from(event) val context = l.createContext(event)
context.callback() context?.callback()
} }
} }
is EventListenable -> { is EventListenable -> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val l = listenable as EventListenable<T, Event> val l = listenable as EventListenable<Event, C>
world.listenFor(l.type.java) { event -> world.listenFor(l.type.java) { event ->
val context = l.from(event) val context = l.createContext(event)
context.callback() context?.callback()
} }
} }
} }
@@ -9,24 +9,24 @@ import kotlin.reflect.KClass
/** /**
* A game occurrence that can be listened to. * A game occurrence that can be listened to.
*/ */
sealed class Listenable<T : ListenableContext, F : Any> { sealed class Listenable<T : Any, C : ListenableContext> {
abstract val type: KClass<F> abstract val type: KClass<T>
} }
abstract class EventListenable<T : ListenableContext, F : Event> : Listenable<T, F>() { abstract class EventListenable<T : Event, C : ListenableContext> : Listenable<T, C>() {
abstract fun from(event: F): T abstract fun createContext(event: T): C?
} }
abstract class MessageListenable<T : ListenableContext, F : Message> : Listenable<T, F>() { abstract class MessageListenable<T : Message, C : ListenableContext> : Listenable<T, C>() {
abstract fun from(player: Player, message: F): T abstract fun createContext(player: Player, message: T): C?
} }
abstract class PlayerEventListenable<T : ListenableContext, F : PlayerEvent> : EventListenable<T, F>() { abstract class PlayerEventListenable<T : PlayerEvent, C : ListenableContext> : EventListenable<T, C>() {
abstract fun from(player: Player, event: F): T abstract fun createContext(player: Player, event: T): C?
override fun from(event: F): T { final override fun createContext(event: T): C? {
return from(event.player, event) return createContext(event.player, event)
} }
} }
@@ -1,6 +1,15 @@
package org.apollo.game.plugin.kotlin package org.apollo.game.plugin.kotlin
import org.apollo.game.model.entity.Player
/** /**
* Contains contextual information for a [Listenable]. * Contextual information for a [Listenable].
*/ */
interface ListenableContext interface ListenableContext
/**
* Contextual information for a [Listenable] involving a specific [Player].
*/
interface PlayerContext : ListenableContext {
val player: Player
}
@@ -1,53 +0,0 @@
package org.apollo.game.plugin.kotlin.action.obj
import org.apollo.cache.def.ObjectDefinition
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.entity.EntityType
import org.apollo.game.model.entity.Player
import org.apollo.game.model.entity.obj.GameObject
import org.apollo.game.plugin.kotlin.action.ActionListenableContext
/**
* An interaction between a [Player] and an [interactive] [GameObject].
*/
class ObjectAction<T : InteractiveObject?>(
override val option: String,
override val player: Player,
val target: GameObject,
val interactive: T
) : ActionListenableContext {
companion object : ObjectActionListenable() {
override fun from(player: Player, message: ObjectActionMessage): ObjectAction<*> {
val def = ObjectDefinition.lookup(message.id)
val selectedAction = def.menuActions[message.option]
val obj = player.world.regionRepository
.fromPosition(message.position)
.getEntities<GameObject>(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
.first { it.definition == def }
return ObjectAction(selectedAction, player, obj, null)
}
override val type = ObjectActionMessage::class
override fun <T : InteractiveObject> from(
player: Player,
other: ObjectActionMessage,
objects: List<T>
): ObjectAction<T> {
val def = ObjectDefinition.lookup(other.id)
val selectedAction = def.menuActions[other.option]
val obj = player.world.regionRepository
.fromPosition(other.position)
.getEntities<GameObject>(other.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
.first { it.definition == def }
return ObjectAction(selectedAction, player, obj, objects.first { it.instanceOf(obj) })
}
}
}
@@ -1,9 +1,8 @@
package org.apollo.game.plugin.kotlin.action package org.apollo.game.plugin.kotlin.message.action
import org.apollo.game.model.entity.Player
import org.apollo.game.plugin.kotlin.KotlinPluginScript import org.apollo.game.plugin.kotlin.KotlinPluginScript
import org.apollo.game.plugin.kotlin.ListenableContext
import org.apollo.game.plugin.kotlin.MessageListenable import org.apollo.game.plugin.kotlin.MessageListenable
import org.apollo.game.plugin.kotlin.PlayerContext
import org.apollo.net.message.Message import org.apollo.net.message.Message
/** /**
@@ -15,8 +14,8 @@ import org.apollo.net.message.Message
* } * }
* ``` * ```
*/ */
inline fun <T : ActionListenableContext, reified F : Message> KotlinPluginScript.on( inline fun <T : ActionContext, reified F : Message> KotlinPluginScript.on(
listenable: MessageListenable<T, F>, listenable: MessageListenable<F, T>,
option: String, option: String,
crossinline callback: T.() -> Unit crossinline callback: T.() -> Unit
) { ) {
@@ -27,7 +26,6 @@ inline fun <T : ActionListenableContext, reified F : Message> KotlinPluginScript
} }
} }
interface ActionListenableContext : ListenableContext { interface ActionContext : PlayerContext {
val option: String val option: String
val player: Player
} }
@@ -1,4 +1,4 @@
package org.apollo.game.plugin.kotlin.action.obj package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.game.model.entity.obj.GameObject import org.apollo.game.model.entity.obj.GameObject
@@ -0,0 +1,60 @@
package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.cache.def.ObjectDefinition
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.entity.EntityType
import org.apollo.game.model.entity.Player
import org.apollo.game.model.entity.obj.GameObject
import org.apollo.game.plugin.kotlin.message.action.ActionContext
/**
* An interaction between a [Player] and an [interactive] [GameObject].
*/
class ObjectAction<T : InteractiveObject?>( // TODO split into two classes, one with T and one without?
override val player: Player,
override val option: String,
val target: GameObject,
val interactive: T
) : ActionContext {
companion object : ObjectActionListenable() {
override val type = ObjectActionMessage::class
override fun createContext(player: Player, message: ObjectActionMessage): ObjectAction<*>? {
return create<InteractiveObject>(player, message, objects = null)
}
override fun <T : InteractiveObject> createContext(
player: Player,
other: ObjectActionMessage,
objects: List<T>
): ObjectAction<T>? {
@Suppress("UNCHECKED_CAST")
return create(player, other, objects) as ObjectAction<T>
}
private fun <T : InteractiveObject> create(
player: Player,
other: ObjectActionMessage,
objects: List<T>?
): ObjectAction<*>? {
val def = ObjectDefinition.lookup(other.id)
val selectedAction = def.menuActions[other.option]
val obj = player.world.regionRepository
.fromPosition(other.position)
.getEntities<GameObject>(other.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
.find { it.definition == def }
?: return null
if (objects == null) {
return ObjectAction(player, selectedAction, obj, null)
} else {
val interactive = objects.find { it.instanceOf(obj) } ?: return null
return ObjectAction(player, selectedAction, obj, interactive)
}
}
}
}
@@ -1,17 +1,17 @@
package org.apollo.game.plugin.kotlin.action.obj package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.game.message.impl.ObjectActionMessage import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.entity.Player import org.apollo.game.model.entity.Player
import org.apollo.game.plugin.kotlin.MessageListenable import org.apollo.game.plugin.kotlin.MessageListenable
abstract class ObjectActionListenable : MessageListenable<ObjectAction<*>, ObjectActionMessage>() { abstract class ObjectActionListenable : MessageListenable<ObjectActionMessage, ObjectAction<*>>() {
override val type = ObjectActionMessage::class override val type = ObjectActionMessage::class
abstract fun <T : InteractiveObject> from( abstract fun <T : InteractiveObject> createContext(
player: Player, player: Player,
other: ObjectActionMessage, other: ObjectActionMessage,
objects: List<T> objects: List<T>
): ObjectAction<T> ): ObjectAction<T>?
} }
@@ -1,4 +1,4 @@
package org.apollo.game.plugin.kotlin.action.obj package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.cache.def.ObjectDefinition import org.apollo.cache.def.ObjectDefinition
import org.apollo.game.message.handler.MessageHandler import org.apollo.game.message.handler.MessageHandler
@@ -8,18 +8,6 @@ import org.apollo.game.model.entity.EntityType
import org.apollo.game.model.entity.Player import org.apollo.game.model.entity.Player
import org.apollo.game.model.entity.obj.GameObject import org.apollo.game.model.entity.obj.GameObject
import org.apollo.game.plugin.kotlin.KotlinPluginScript import org.apollo.game.plugin.kotlin.KotlinPluginScript
import org.apollo.game.plugin.kotlin.action.on
@Deprecated("example function, remove")
fun KotlinPluginScript.x() {
on(ObjectAction, option = "Trade", objects = listOf()) {
}
on(ObjectAction, option = "Trade") {
}
}
/** /**
* Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the * Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the
@@ -27,7 +15,7 @@ fun KotlinPluginScript.x() {
* *
* ``` * ```
* on(ObjectAction, option = "Open", objects = DOORS.toList()) { * on(ObjectAction, option = "Open", objects = DOORS.toList()) {
* player.sendMessage("You open the door") * player.sendMessage("You open the door.")
* } * }
* ``` * ```
*/ */
@@ -39,10 +27,8 @@ fun <T : InteractiveObject> KotlinPluginScript.on(
) { ) {
if (objects.isEmpty()) { if (objects.isEmpty()) {
on(listenable) { on(listenable) {
if (this.option.equals(option, ignoreCase = true)) { @Suppress("UNCHECKED_CAST") (callback as ObjectAction<*>.() -> Unit)
@Suppress("UNCHECKED_CAST") (this as ObjectAction<T>) callback(this)
callback()
}
} }
} else { } else {
val handler = ObjectActionMessageHandler(world, listenable, objects, option, callback) val handler = ObjectActionMessageHandler(world, listenable, objects, option, callback)
@@ -50,9 +36,6 @@ fun <T : InteractiveObject> KotlinPluginScript.on(
} }
} }
/**
* A [MessageHandler]
*/
class ObjectActionMessageHandler<T : InteractiveObject>( class ObjectActionMessageHandler<T : InteractiveObject>(
world: World, world: World,
private val listenable: ObjectActionListenable, private val listenable: ObjectActionListenable,
@@ -71,8 +54,8 @@ class ObjectActionMessageHandler<T : InteractiveObject>(
.first { it.definition == def } .first { it.definition == def }
if (option.equals(selectedAction, ignoreCase = true) && objects.any { it.instanceOf(obj) }) { if (option.equals(selectedAction, ignoreCase = true) && objects.any { it.instanceOf(obj) }) {
val context = listenable.from(player, message, objects) val context = listenable.createContext(player, message, objects)
context.callback() context?.callback()
} }
} }