Create raw handlers in plugin listener overloads

This commit is contained in:
Major
2019-07-23 02:03:28 +01:00
parent ed8611be01
commit 12b4bef1f8
10 changed files with 150 additions and 174 deletions
@@ -7,19 +7,6 @@ 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<F : Message, T : ListenableContext>(
world: World,
private val listenable: MessageListenable<F, T>,
private val callback: T.() -> Unit
) : MessageHandler<F>(world) {
override fun handle(player: Player, message: F) {
val context = listenable.createContext(player, message)
context?.callback()
}
}
/** /**
* A handler for [Message]s. * A handler for [Message]s.
*/ */
@@ -20,35 +20,35 @@ abstract class KotlinPluginScript(var world: World, val context: PluginContext)
private var stopListener: (World) -> Unit = { _ -> } private var stopListener: (World) -> Unit = { _ -> }
fun <T : Any, C : ListenableContext> on(listenable: Listenable<T, C>, callback: C.() -> Unit) { fun <T : Any, C : ListenableContext, I : PredicateContext> on(
listenable: Listenable<T, C, I>,
callback: C.() -> Unit
) {
registerListener(listenable, null, callback)
}
internal fun <T : Any, C : ListenableContext, I : PredicateContext> registerListener(
listenable: Listenable<T, C, I>,
predicateContext: I?,
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<Message, C> val l = listenable as MessageListenable<Message, C, I>
val handler = KotlinMessageHandler(world, l, callback) val handler = l.createHandler(world, predicateContext, callback)
context.addMessageHandler(l.type.java, handler) context.addMessageHandler(l.type.java, handler)
} }
is PlayerEventListenable -> {
@Suppress("UNCHECKED_CAST")
val l = listenable as PlayerEventListenable<PlayerEvent, C>
world.listenFor(l.type.java) { event ->
val context = l.createContext(event)
context?.callback()
}
}
is EventListenable -> { is EventListenable -> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val l = listenable as EventListenable<Event, C> val l = listenable as EventListenable<Event, C, I>
world.listenFor(l.type.java) { event -> val handler = l.createHandler(world, predicateContext, callback)
val context = l.createContext(event) world.listenFor(l.type.java, handler)
context?.callback()
}
} }
} }
} }
@@ -1,32 +1,27 @@
package org.apollo.game.plugin.kotlin package org.apollo.game.plugin.kotlin
import org.apollo.game.model.entity.Player import org.apollo.game.message.handler.MessageHandler
import org.apollo.game.model.World
import org.apollo.game.model.event.Event import org.apollo.game.model.event.Event
import org.apollo.game.model.event.PlayerEvent import org.apollo.game.model.event.EventListener
import org.apollo.net.message.Message import org.apollo.net.message.Message
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* A game occurrence that can be listened to. * A game occurrence that can be listened to.
*/ */
sealed class Listenable<T : Any, C : ListenableContext> { sealed class Listenable<T : Any, C : ListenableContext, P : PredicateContext> {
abstract val type: KClass<T> abstract val type: KClass<T>
} }
abstract class EventListenable<T : Event, C : ListenableContext> : Listenable<T, C>() { abstract class EventListenable<T : Event, C : ListenableContext, P : PredicateContext> : Listenable<T, C, P>() {
abstract fun createContext(event: T): C?
}
abstract class MessageListenable<T : Message, C : ListenableContext> : Listenable<T, C>() { abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): EventListener<T>
abstract fun createContext(player: Player, message: T): C?
} }
abstract class PlayerEventListenable<T : PlayerEvent, C : ListenableContext> : EventListenable<T, C>() { abstract class MessageListenable<T : Message, C : ListenableContext, P : PredicateContext> : Listenable<T, C, P>() {
abstract fun createContext(player: Player, event: T): C? abstract fun createHandler(world: World, predicateContext: P?, callback: C.() -> Unit): MessageHandler<T>
final override fun createContext(event: T): C? {
return createContext(event.player, event)
}
} }
@@ -13,3 +13,5 @@ interface ListenableContext
interface PlayerContext : ListenableContext { interface PlayerContext : ListenableContext {
val player: Player val player: Player
} }
interface PredicateContext
@@ -1,10 +1,13 @@
package org.apollo.game.plugin.kotlin.message package org.apollo.game.plugin.kotlin.message
import org.apollo.game.message.handler.MessageHandler
import org.apollo.game.message.impl.ButtonMessage import org.apollo.game.message.impl.ButtonMessage
import org.apollo.game.model.World
import org.apollo.game.model.entity.Player 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.MessageListenable import org.apollo.game.plugin.kotlin.MessageListenable
import org.apollo.game.plugin.kotlin.PlayerContext import org.apollo.game.plugin.kotlin.PlayerContext
import org.apollo.game.plugin.kotlin.PredicateContext
/** /**
* Registers a listener for [ButtonMessage]s that occur on the given [button] id. * Registers a listener for [ButtonMessage]s that occur on the given [button] id.
@@ -20,21 +23,34 @@ fun KotlinPluginScript.on(
button: Int, button: Int,
callback: ButtonClick.() -> Unit callback: ButtonClick.() -> Unit
) { ) {
on(listenable) { registerListener(listenable, ButtonPredicateContext(button), callback)
if (this.button == button) {
callback()
}
}
} }
class ButtonClick(override val player: Player, val button: Int) : PlayerContext { class ButtonClick(override val player: Player, val button: Int) : PlayerContext {
companion object : MessageListenable<ButtonMessage, ButtonClick>() { companion object : MessageListenable<ButtonMessage, ButtonClick, ButtonPredicateContext>() {
override val type = ButtonMessage::class override val type = ButtonMessage::class
override fun createContext(player: Player, message: ButtonMessage): ButtonClick { override fun createHandler(
return ButtonClick(player, message.widgetId) world: World,
predicateContext: ButtonPredicateContext?,
callback: ButtonClick.() -> Unit
): MessageHandler<ButtonMessage> {
return object : MessageHandler<ButtonMessage>(world) {
override fun handle(player: Player, message: ButtonMessage) {
if (predicateContext == null || predicateContext.button == message.widgetId) {
val context = ButtonClick(player, message.widgetId)
context.callback()
}
}
}
} }
} }
} }
class ButtonPredicateContext(val button: Int) : PredicateContext
@@ -3,6 +3,7 @@ package org.apollo.game.plugin.kotlin.message.action
import org.apollo.game.plugin.kotlin.KotlinPluginScript import org.apollo.game.plugin.kotlin.KotlinPluginScript
import org.apollo.game.plugin.kotlin.MessageListenable import org.apollo.game.plugin.kotlin.MessageListenable
import org.apollo.game.plugin.kotlin.PlayerContext import org.apollo.game.plugin.kotlin.PlayerContext
import org.apollo.game.plugin.kotlin.PredicateContext
import org.apollo.net.message.Message import org.apollo.net.message.Message
/** /**
@@ -14,18 +15,16 @@ import org.apollo.net.message.Message
* } * }
* ``` * ```
*/ */
inline fun <T : ActionContext, reified F : Message> KotlinPluginScript.on( fun <T : ActionContext, F : Message> KotlinPluginScript.on(
listenable: MessageListenable<F, T>, listenable: MessageListenable<F, T, ActionPredicateContext>,
option: String, option: String,
crossinline callback: T.() -> Unit callback: T.() -> Unit
) { ) {
on(listenable) { registerListener(listenable, ActionPredicateContext(option), callback)
if (this.option.equals(option, ignoreCase = true)) {
callback()
}
}
} }
interface ActionContext : PlayerContext { interface ActionContext : PlayerContext {
val option: String val option: String
} }
open class ActionPredicateContext(open val option: String) : PredicateContext
@@ -1,12 +1,60 @@
package org.apollo.game.plugin.kotlin.message.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.impl.ObjectActionMessage import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.World
import org.apollo.game.model.entity.EntityType 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.MessageListenable
import org.apollo.game.plugin.kotlin.message.action.ActionContext import org.apollo.game.plugin.kotlin.message.action.ActionContext
/**
* Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the
* given [option] (case-insensitive).
*
* ```
* on(ObjectAction, option = "Open", objects = DOORS.toList()) {
* player.sendMessage("You open the door.")
* }
* ```
*/
fun <T : InteractiveObject> KotlinPluginScript.on(
listenable: ObjectAction.Companion,
option: String,
objects: List<T>,
callback: ObjectAction<T>.() -> Unit
) {
@Suppress("UNCHECKED_CAST") (callback as ObjectAction<*>.() -> Unit)
registerListener(listenable, ObjectActionPredicateContext(option, objects), callback)
}
/**
* Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the
* given [option] (case-insensitive).
*
* ```
* on(ObjectAction, option = "Open", objects = DOORS.toList()) {
* player.sendMessage("You open the door.")
* }
* ```
*/
fun KotlinPluginScript.on(
listenable: ObjectAction.Companion,
option: String,
callback: ObjectAction<*>.() -> Unit
) {
registerListener(listenable, ObjectActionPredicateContext(option, emptyList()), callback)
}
fun KotlinPluginScript.x() {
on(ObjectAction, "walk") {
}
}
/** /**
* An interaction between a [Player] and an [interactive] [GameObject]. * An interaction between a [Player] and an [interactive] [GameObject].
*/ */
@@ -17,44 +65,44 @@ class ObjectAction<T : InteractiveObject?>( // TODO split into two classes, one
val interactive: T val interactive: T
) : ActionContext { ) : ActionContext {
companion object : ObjectActionListenable() { companion object : MessageListenable<ObjectActionMessage, ObjectAction<*>, ObjectActionPredicateContext<*>>() {
override val type = ObjectActionMessage::class override val type = ObjectActionMessage::class
override fun createContext(player: Player, message: ObjectActionMessage): ObjectAction<*>? { override fun createHandler(
return create<InteractiveObject>(player, message, objects = null) world: World,
} predicateContext: ObjectActionPredicateContext<*>?,
callback: ObjectAction<*>.() -> Unit
): MessageHandler<ObjectActionMessage> {
return object : MessageHandler<ObjectActionMessage>(world) {
override fun <T : InteractiveObject> createContext( override fun handle(player: Player, message: ObjectActionMessage) {
player: Player, val def = ObjectDefinition.lookup(message.id)
other: ObjectActionMessage, val option = def.menuActions[message.option]
objects: List<T>
): ObjectAction<T>? {
@Suppress("UNCHECKED_CAST")
return create(player, other, objects) as ObjectAction<T>
}
private fun <T : InteractiveObject> create( val target = world.regionRepository
player: Player, .fromPosition(message.position)
other: ObjectActionMessage, .getEntities<GameObject>(message.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT)
objects: List<T>? .find { it.definition == def }
): ObjectAction<*>? { ?: return // Could happen if object was despawned this tick, before calling this handle function
val def = ObjectDefinition.lookup(other.id)
val selectedAction = def.menuActions[other.option]
val obj = player.world.regionRepository val context = when { // Evaluation-order matters here.
.fromPosition(other.position) predicateContext == null -> ObjectAction<InteractiveObject?>(player, option, target, null)
.getEntities<GameObject>(other.position, EntityType.DYNAMIC_OBJECT, EntityType.STATIC_OBJECT) !predicateContext.option.equals(option, ignoreCase = true) -> return
.find { it.definition == def } predicateContext.objects.isEmpty() -> ObjectAction(player, option, target, null)
?: return null predicateContext.objects.any { it.instanceOf(target) } -> {
val interactive = predicateContext.objects.find { it.instanceOf(target) } ?: return
ObjectAction(player, option, target, interactive)
}
else -> return
}
context.callback()
}
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 +0,0 @@
package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.entity.Player
import org.apollo.game.plugin.kotlin.MessageListenable
abstract class ObjectActionListenable : MessageListenable<ObjectActionMessage, ObjectAction<*>>() {
override val type = ObjectActionMessage::class
abstract fun <T : InteractiveObject> createContext(
player: Player,
other: ObjectActionMessage,
objects: List<T>
): ObjectAction<T>?
}
@@ -1,62 +0,0 @@
package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.cache.def.ObjectDefinition
import org.apollo.game.message.handler.MessageHandler
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.World
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.KotlinPluginScript
/**
* Registers a listener for [ObjectActionMessage]s that occur on any of the given [InteractiveObject]s using the
* given [option] (case-insensitive).
*
* ```
* on(ObjectAction, option = "Open", objects = DOORS.toList()) {
* player.sendMessage("You open the door.")
* }
* ```
*/
fun <T : InteractiveObject> KotlinPluginScript.on(
listenable: ObjectActionListenable,
option: String,
objects: List<T>,
callback: ObjectAction<T>.() -> Unit
) {
if (objects.isEmpty()) {
on(listenable) {
@Suppress("UNCHECKED_CAST") (callback as ObjectAction<*>.() -> Unit)
callback(this)
}
} else {
val handler = ObjectActionMessageHandler(world, listenable, objects, option, callback)
context.addMessageHandler(listenable.type.java, handler)
}
}
class ObjectActionMessageHandler<T : InteractiveObject>(
world: World,
private val listenable: ObjectActionListenable,
private val objects: List<T>,
private val option: String,
private val callback: ObjectAction<T>.() -> Unit
) : MessageHandler<ObjectActionMessage>(world) {
override fun handle(player: Player, message: ObjectActionMessage) {
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 }
if (option.equals(selectedAction, ignoreCase = true) && objects.any { it.instanceOf(obj) }) {
val context = listenable.createContext(player, message, objects)
context?.callback()
}
}
}
@@ -0,0 +1,8 @@
package org.apollo.game.plugin.kotlin.message.action.obj
import org.apollo.game.plugin.kotlin.message.action.ActionPredicateContext
data class ObjectActionPredicateContext<T : InteractiveObject>(
override val option: String,
val objects: List<T>
) : ActionPredicateContext(option)