Add support for asynchronous Mob actions

Adds asynchronous implementations of the Action and DistancedAction
classes, allowing plugins to make use of suspendable functions in Kotlin
instead of creating mini state machines to keep track of state.

The asynchronicity works by creating coroutine backed Channels for each
asynchronous action and having them listen on "pulse" events from the
action scheduler.  The action can then suspend execution until a pulse is
received or until some expensive operation has completed (i.e.,
pathfinding).

If an asynchronous action is still busy when a pulse arrives then the
number of missed pulses will be accumulated and sent to the action at
the next possible time.

The training dummy plugin has been updated to use asycnrhonous actions
as an example.
This commit is contained in:
Gary Tierney
2017-06-20 06:53:00 +01:00
parent 182de0330f
commit 97e85868ff
10 changed files with 156 additions and 187 deletions
+15 -18
View File
@@ -1,9 +1,12 @@
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.selects.select
import org.apollo.game.action.AsyncDistancedAction
import org.apollo.game.action.DistancedAction
import org.apollo.game.message.impl.ObjectActionMessage
import org.apollo.game.model.Animation
import org.apollo.game.model.Position
import org.apollo.game.model.entity.Player
import org.apollo.game.model.entity.Skill
import org.apollo.game.model.entity.*
import org.apollo.net.message.Message
/**
@@ -15,7 +18,7 @@ on { ObjectActionMessage::class }
.where { option == 2 && id in DUMMY_IDS }
.then { DummyAction.start(this, it, position) }
class DummyAction(val player: Player, position: Position) : DistancedAction<Player>(0, true, player, position, DISTANCE) {
class DummyAction(val player: Player, position: Position) : AsyncDistancedAction<Player>(0, true, player, position, DISTANCE) {
companion object {
@@ -49,25 +52,19 @@ class DummyAction(val player: Player, position: Position) : DistancedAction<Play
}
var started = false
override fun executeAction() {
if (started) {
val skills = player.skillSet
override suspend fun executeActionAsync() {
mob.sendMessage("You hit the dummy.")
mob.turnTo(this.position)
mob.playAnimation(PUNCH_ANIMATION)
wait()
if (skills.getSkill(Skill.ATTACK).maximumLevel >= LEVEL_THRESHOLD) {
player.sendMessage("There is nothing more you can learn from hitting a dummy.")
} else {
skills.addExperience(Skill.ATTACK, EXP_PER_HIT)
}
val skills = player.skillSet
stop()
if (skills.getSkill(Skill.ATTACK).maximumLevel >= LEVEL_THRESHOLD) {
player.sendMessage("There is nothing more you can learn from hitting a dummy.")
} else {
mob.sendMessage("You hit the dummy.")
mob.turnTo(this.position)
mob.playAnimation(PUNCH_ANIMATION)
started = true
skills.addExperience(Skill.ATTACK, EXP_PER_HIT)
}
}