Update player actor

Make stats observable hot
Remove concurrentsubject
This commit is contained in:
Rohan Sircar 2021-02-28 23:45:43 +05:30
parent 92aae68254
commit 1422d91b14
5 changed files with 23 additions and 120 deletions

View File

@ -377,6 +377,7 @@ class MainAppDelegate(
playerActor playerActor
.askL(PlayerActorSupervisor.GetStatsObservable(_)) .askL(PlayerActorSupervisor.GetStatsObservable(_))
.onErrorHandleWith(TimeoutError.from) .onErrorHandleWith(TimeoutError.from)
.flatten
playerHud <- playerHud <-
UIO UIO
.deferAction(implicit s => .deferAction(implicit s =>
@ -391,7 +392,6 @@ class MainAppDelegate(
children = List( children = List(
new Label("Health") { textFill = Color.White }, new Label("Health") { textFill = Color.White },
new Label("100") { new Label("100") {
text == "hello"
text <-- statsObs text <-- statsObs
.doOnNextF(i => loggerL.debug(show"Received stats: $i")) .doOnNextF(i => loggerL.debug(show"Received stats: $i"))
.map(_.hp.toInt.toString) .map(_.hp.toInt.toString)
@ -504,6 +504,7 @@ class MainAppDelegate(
.hideErrors .hideErrors
.startAndForget .startAndForget
sched <- UIO.pure(schedulers.async) sched <- UIO.pure(schedulers.async)
fxSched <- UIO.pure(schedulers.fx)
playerActor <- wire[PlayerController.Props].create playerActor <- wire[PlayerController.Props].create
} yield playerActor } yield playerActor
} }

View File

@ -6,28 +6,15 @@ import scala.util.Success
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout import akka.util.Timeout
import cats.syntax.eq._
import cats.syntax.show._ import cats.syntax.show._
import monix.execution.CancelableFuture import monix.execution.CancelableFuture
import monix.execution.CancelablePromise import monix.execution.CancelablePromise
import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedDown
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedLeft
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedRight
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedUp
import wow.doge.mygame.subsystems.events.Event
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.GenericTimerActor import wow.doge.mygame.utils.GenericTimerActor
object NpcActorSupervisor { object NpcActorSupervisor {
@ -236,76 +223,3 @@ class NpcMovementActor[T](
Behaviors.same Behaviors.same
} }
} }
object NpcMovementActorNotUsed {
sealed trait Command
class Props(
imMovementActorBehavior: Behavior[ImMovementActor.Command],
npcName: String,
// movementActor: ActorRef[ImMovementActor.Command],
mainEventBus: ActorRef[
EventBus.Command[Event]
],
tickEventBus: GameEventBus[TickEvent]
) {
def behavior: Behavior[Command] =
Behaviors.setup { ctx =>
val movementActor = ctx.spawn(
Behaviors
.supervise(imMovementActorBehavior)
.onFailure[Exception](
SupervisorStrategy.restart.withLimit(2, 100.millis)
),
s"npc-${npcName}-MovementActorChild"
)
val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] {
case event: EntityMovementEvent =>
event match {
case MovedLeft(name, pressed) =>
if (name === npcName)
movementActor ! ImMovementActor.MoveLeft(pressed)
Behaviors.same
case MovedUp(name, pressed) =>
if (name === npcName)
movementActor ! ImMovementActor.MoveUp(pressed)
Behaviors.same
case MovedRight(name, pressed) =>
if (name === npcName)
movementActor ! ImMovementActor.MoveRight(pressed)
Behaviors.same
case MovedDown(name, pressed) =>
if (name === npcName)
movementActor ! ImMovementActor.MoveDown(pressed)
Behaviors.same
}
}
val npcMovementEl = ctx.spawn(
Behaviors
.supervise(npcMovementElBehavior)
.onFailure[Exception](
SupervisorStrategy.restart.withLimit(2, 100.millis)
),
s"npc-${npcName}-MovementEventHandler"
)
val renderTickElBehavior =
Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
Behaviors.same
}
val renderTickEl =
ctx.spawn(
renderTickElBehavior,
s"npc-${npcName}-MovementTickListener"
)
mainEventBus ! EventBus.Subscribe(npcMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
Behaviors.receiveMessage(msg => Behaviors.same)
}
}
}

View File

@ -14,11 +14,8 @@ import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout import akka.util.Timeout
import cats.syntax.show._ import cats.syntax.show._
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import monix.eval.Coeval
import monix.execution.AsyncQueue import monix.execution.AsyncQueue
import monix.reactive.Observable import monix.reactive.Observable
import monix.reactive.OverflowStrategy
import monix.reactive.subjects.ConcurrentSubject
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.Dispatchers import wow.doge.mygame.Dispatchers
import wow.doge.mygame.executors.Schedulers.AsyncScheduler import wow.doge.mygame.executors.Schedulers.AsyncScheduler
@ -30,6 +27,8 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.executors.Schedulers
import monix.bio.UIO
object PlayerActorSupervisor { object PlayerActorSupervisor {
type Ref = ActorRef[PlayerActorSupervisor.Command] type Ref = ActorRef[PlayerActorSupervisor.Command]
@ -54,7 +53,7 @@ object PlayerActorSupervisor {
extends Command extends Command
final case class GetStatus(replyTo: ActorRef[Status]) extends Command final case class GetStatus(replyTo: ActorRef[Status]) extends Command
final case class GetStatsObservable( final case class GetStatsObservable(
replyTo: ActorRef[Observable[CharacterStats]] replyTo: ActorRef[UIO[Observable[CharacterStats]]]
) extends Command ) extends Command
private case object Die extends Command private case object Die extends Command
@ -62,13 +61,13 @@ object PlayerActorSupervisor {
response: (Boolean, CharacterStats), response: (Boolean, CharacterStats),
replyTo: ActorRef[Unit] replyTo: ActorRef[Unit]
) extends Command ) extends Command
// private final case class InternalTakeDamage(old: Int, value: Int) extends Command
private final case class LogError(ex: Throwable) extends Command private final case class LogError(ex: Throwable) extends Command
class Props( class Props(
val playerEventBus: GameEventBus[PlayerEvent], val playerEventBus: GameEventBus[PlayerEvent],
val tickEventBus: GameEventBus[TickEvent], val tickEventBus: GameEventBus[TickEvent],
val imMovementActorBehavior: Behavior[ImMovementActor.Command], val imMovementActorBehavior: Behavior[ImMovementActor.Command],
val scheduler: AsyncScheduler val scheduler: AsyncScheduler,
val fxScheduler: Schedulers.FxScheduler
) { ) {
def behavior = def behavior =
Behaviors.logMessages( Behaviors.logMessages(
@ -142,12 +141,6 @@ object PlayerActorSupervisor {
ctx, ctx,
this, this,
Children(playerMovementActor, playerStatsActor), Children(playerMovementActor, playerStatsActor),
ConcurrentSubject.publish(
OverflowStrategy.DropOldAndSignal(
50,
dropped => Coeval.pure(None)
)
)(scheduler.value),
AsyncQueue.bounded(10)(scheduler.value) AsyncQueue.bounded(10)(scheduler.value)
).aliveState ).aliveState
} }
@ -164,7 +157,6 @@ class PlayerActorSupervisor(
ctx: ActorContext[PlayerActorSupervisor.Command], ctx: ActorContext[PlayerActorSupervisor.Command],
props: PlayerActorSupervisor.Props, props: PlayerActorSupervisor.Props,
children: PlayerActorSupervisor.Children, children: PlayerActorSupervisor.Children,
statsSubject: ConcurrentSubject[CharacterStats, CharacterStats],
statsQueue: AsyncQueue[CharacterStats] statsQueue: AsyncQueue[CharacterStats]
) { ) {
import PlayerActorSupervisor._ import PlayerActorSupervisor._
@ -173,11 +165,6 @@ class PlayerActorSupervisor(
Behaviors Behaviors
.receiveMessage[Command] { .receiveMessage[Command] {
case TakeDamage(value, replyTo) => case TakeDamage(value, replyTo) =>
// children.movementActor ! ImMovementActor.MovedDown(true)
// ctx.ask(children.statsActor, StatsActor.CurrentStats(_)) {
// case Success(status) => InternalTakeDamage(status.hp, value)
// case Failure(ex) => LogError(ex)
// }
ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) { ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) {
case Success(response) => DamageResponse(response, replyTo) case Success(response) => DamageResponse(response, replyTo)
case Failure(ex) => LogError(ex) case Failure(ex) => LogError(ex)
@ -193,7 +180,6 @@ class PlayerActorSupervisor(
} }
Behaviors.same Behaviors.same
case CurrentStats(replyTo) => case CurrentStats(replyTo) =>
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
children.statsActor ! StatsActor.CurrentStats(replyTo) children.statsActor ! StatsActor.CurrentStats(replyTo)
Behaviors.same Behaviors.same
case Heal(value) => case Heal(value) =>
@ -202,18 +188,18 @@ class PlayerActorSupervisor(
case GetStatus(replyTo) => case GetStatus(replyTo) =>
replyTo ! Status.Alive replyTo ! Status.Alive
Behaviors.same Behaviors.same
// case _ => Behaviors.unhandled
// case InternalTakeDamage(hp, damage) =>
// if (hp - damage <= 0) dead
// else {
// children.statsActor ! StatsActor.TakeDamage(damage)
// Behaviors.same
// }
case GetStatsObservable(replyTo) => case GetStatsObservable(replyTo) =>
import monix.{eval => me} import monix.{eval => me}
replyTo ! Observable.repeatEvalF( replyTo !
me.Task.deferFuture(statsQueue.poll()) UIO(
) Observable
.repeatEvalF(
me.Task.deferFuture(statsQueue.poll())
)
.publish(props.fxScheduler.value)
.refCount
)
Behaviors.same Behaviors.same
case DamageResponse(response, replyTo) => case DamageResponse(response, replyTo) =>
response match { response match {
@ -235,7 +221,6 @@ class PlayerActorSupervisor(
.receiveSignal { .receiveSignal {
case (_, PostStop) => case (_, PostStop) =>
ctx.log.infoP("stopped") ctx.log.infoP("stopped")
statsSubject.onComplete()
Behaviors.same Behaviors.same
} }
val deadState = Behaviors val deadState = Behaviors
@ -262,7 +247,6 @@ class PlayerActorSupervisor(
.receiveSignal { .receiveSignal {
case (_, PostStop) => case (_, PostStop) =>
ctx.log.infoP("stopped") ctx.log.infoP("stopped")
statsSubject.onComplete()
Behaviors.same Behaviors.same
} }
} }

View File

@ -24,6 +24,7 @@ import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.types._ import wow.doge.mygame.types._
import wow.doge.mygame.utils.wrappers.jme._ import wow.doge.mygame.utils.wrappers.jme._
import wow.doge.mygame.executors.Schedulers
object PlayerController { object PlayerController {
sealed trait Error sealed trait Error
@ -50,8 +51,8 @@ object PlayerController {
initialPlayerPos: ImVector3f, initialPlayerPos: ImVector3f,
playerEventBus: GameEventBus[PlayerEvent], playerEventBus: GameEventBus[PlayerEvent],
playerPhysicsControl: BetterCharacterControl, playerPhysicsControl: BetterCharacterControl,
// appScheduler: monix.execution.Scheduler,
scheduler: AsyncScheduler, scheduler: AsyncScheduler,
fxScheduler: Schedulers.FxScheduler,
playerNode: PlayerNode, playerNode: PlayerNode,
cameraNode: PlayerCameraNode, cameraNode: PlayerCameraNode,
cameraPivotNode: PlayerCameraPivotNode, cameraPivotNode: PlayerCameraPivotNode,
@ -67,7 +68,8 @@ object PlayerController {
playerEventBus, playerEventBus,
tickEventBus, tickEventBus,
movementActorBeh, movementActorBeh,
scheduler scheduler,
fxScheduler
).behavior ).behavior
} }
val playerActor = val playerActor =

View File

@ -12,7 +12,9 @@ import com.jme3.scene.Spatial.DFSMode
import monix.execution.Scheduler.Implicits.global import monix.execution.Scheduler.Implicits.global
import org.scalatest.funsuite.AnyFunSuite import org.scalatest.funsuite.AnyFunSuite
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
import scala.annotation.nowarn
@nowarn("msg=method get in class LeftProjection is deprecated")
class CollisionShapeFactoryTest extends AnyFunSuite { class CollisionShapeFactoryTest extends AnyFunSuite {
test("Test for WrongArgumentError") { test("Test for WrongArgumentError") {
val res = CollisionShapeFactory val res = CollisionShapeFactory