package wow.doge.mygame.game.entities import scala.concurrent.duration._ import scala.util.Random import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.typed.LogOptions import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.TimerScheduler import com.typesafe.scalalogging.Logger import org.slf4j.event.Level import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent.RenderTick import wow.doge.mygame.subsystems.movement.ImMovementActor object PlayerActorSupervisor { sealed trait Command final case class Props( playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] ], playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], tickEventBus: GameEventBus[TickEvent], imMovementActorBehavior: Behavior[ImMovementActor.Command], playerCameraActorBehavior: Behavior[PlayerCameraActor.Command] ) { def create[T: CanMove](movable: T) = Behaviors.logMessages( LogOptions() .withLevel(Level.TRACE) .withLogger( Logger[PlayerActorSupervisor[T]].underlying ), Behaviors.setup[Command] { ctx => ctx.log.info("Hello from PlayerActor") // spawn children actors lazy val movementActor = ctx.spawn( Behaviors .supervise(imMovementActorBehavior) .onFailure[Exception](SupervisorStrategy.restart), "playerMovementActorChild" ) val playerCameraActor = ctx.spawn(playerCameraActorBehavior, "playerCameraActor") val playerCameraEl = ctx.spawn( PlayerCameraEventListener(playerCameraActor), "playerCameraActorEl" ) ctx.spawn( PlayerMovementActor .Props( movementActor, playerCameraActor, playerMovementEventBus, tickEventBus ) .create, "playerMovementAcor" ) //init actors playerCameraEventBus ! EventBus.Subscribe(playerCameraEl) new PlayerActorSupervisor( ctx, this, Children(movementActor) ).receive } ) } case class Children( movementActor: ActorRef[ImMovementActor.Command] ) } class PlayerActorSupervisor[T: CanMove]( ctx: ActorContext[PlayerActorSupervisor.Command], props: PlayerActorSupervisor.Props, children: PlayerActorSupervisor.Children ) { import PlayerActorSupervisor._ def receive = Behaviors.receiveMessage[Command] { case _ => // children.movementActor ! ImMovementActor.MovedDown(true) Behaviors.same } } object PlayerMovementActor { sealed trait Command final case class Props( movementActor: ActorRef[ImMovementActor.Command], playerCameraActor: ActorRef[PlayerCameraActor.Command], playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] ], tickEventBus: GameEventBus[TickEvent] ) { def create: Behavior[Command] = Behaviors.setup { ctx => val playerMovementEl = ctx.spawn( Behaviors .supervise(PlayerMovementEventListener(movementActor)) .onFailure[Exception](SupervisorStrategy.restart), "playerMovementEventHandler" ) val renderTickElBehavior = Behaviors.receiveMessage[RenderTick.type] { case RenderTick => movementActor ! ImMovementActor.Tick // playerCameraActor ! PlayerCameraActor.Tick Behaviors.same } val renderTickEl = ctx.spawn(renderTickElBehavior, "playerMovementTickListener") playerMovementEventBus ! EventBus.Subscribe(playerMovementEl) tickEventBus ! EventBus.Subscribe(renderTickEl) Behaviors.receiveMessage { msg => Behaviors.same } } } } object GenericTimerActor { sealed trait Command final case object Start extends Command final case object Stop extends Command private case object Send extends Command case class TimerKey(seed: Long) case class Props[T]( target: ActorRef[T], messageToSend: T, timeInterval: FiniteDuration ) { val create = Behaviors.withTimers[Command] { timers => new GenericTimerActor(timers, TimerKey(Random.nextLong()), this).idle } } } class GenericTimerActor[T]( timers: TimerScheduler[GenericTimerActor.Command], timerKey: GenericTimerActor.TimerKey, props: GenericTimerActor.Props[T] ) { import GenericTimerActor._ val idle: Behavior[Command] = Behaviors.receiveMessage { case Start => timers.startTimerWithFixedDelay(timerKey, Send, props.timeInterval) active case _ => Behaviors.unhandled } val active: Behavior[Command] = Behaviors.receiveMessagePartial { case Send => props.target ! props.messageToSend Behaviors.same case Stop => timers.cancel(timerKey) idle } }