From a2a328d078b7951a69c6649cd1d210e64fc71e65 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Sat, 21 Nov 2020 20:04:25 +0530 Subject: [PATCH] dunno lol --- build.sbt | 4 +- src/main/scala/wow/doge/mygame/Main.scala | 81 +++++- .../scala/wow/doge/mygame/game/GameApp.scala | 4 +- .../wow/doge/mygame/game/GameAppActor.scala | 24 +- .../wow/doge/mygame/game/GameModule.scala | 18 +- .../mygame/game/GameSystemsInitializer.scala | 94 +++--- .../game/appstates/PlayerMovementState.scala | 2 +- .../game/nodes/PlayerActorSupervisor.scala | 201 +++++++++++++ .../mygame/game/nodes/PlayerCameraActor.scala | 30 ++ .../mygame/game/nodes/PlayerController.scala | 212 ++++++++++---- .../game/nodes/PlayerEventListeners.scala | 42 ++- .../subsystems/input/GameInputHandler.scala | 102 ++++--- .../game/subsystems/input/InputEnums.scala | 19 ++ .../game/subsystems/movement/CanMove.scala | 70 +++++ .../subsystems/movement/MovementActor.scala | 69 ++--- .../wow/doge/mygame/implicits/TestEnum.scala | 43 +++ .../wow/doge/mygame/implicits/package.scala | 271 +++++++++--------- .../mygame/subsystems/events/EventBus.scala | 2 +- .../subsystems/events/EventsModule.scala | 49 +--- .../subsystems/events/EventsModule2.scala | 123 ++++---- .../subsystems/events/MovementEvents.scala | 16 +- .../events/PlayerCameraEvents.scala | 8 + .../scriptsystem/ScriptSystemModule.scala | 23 ++ .../doge/mygame/utils/JFXConsoleStream.scala | 19 +- 24 files changed, 1053 insertions(+), 473 deletions(-) create mode 100644 src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala create mode 100644 src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala create mode 100644 src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala create mode 100644 src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala create mode 100644 src/main/scala/wow/doge/mygame/implicits/TestEnum.scala create mode 100644 src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala create mode 100644 src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala diff --git a/build.sbt b/build.sbt index 2f4d59c..ad4bdaa 100644 --- a/build.sbt +++ b/build.sbt @@ -89,7 +89,9 @@ lazy val root = (project in file(".")).settings( "com.github.valskalla" %% "odin-slf4j" % "0.8.1", "com.softwaremill.quicklens" %% "quicklens" % "1.6.1", "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1", - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2" + "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2", + "io.circe" %% "circe-config" % "0.8.0", + "com.beachape" %% "enumeratum-circe" % "1.6.1" ), // Determine OS version of JavaFX binaries diff --git a/src/main/scala/wow/doge/mygame/Main.scala b/src/main/scala/wow/doge/mygame/Main.scala index a6f6a5b..f4c66a2 100644 --- a/src/main/scala/wow/doge/mygame/Main.scala +++ b/src/main/scala/wow/doge/mygame/Main.scala @@ -1,6 +1,5 @@ package wow.doge.mygame -import monix.bio.Task import cats.effect.Resource import io.odin.syntax._ @@ -10,15 +9,20 @@ import com.softwaremill.macwire._ import scala.concurrent.duration._ import monix.bio.BIOApp import monix.bio.UIO -import monix.bio.IO import io.odin._ -import wow.doge.mygame.implicits._ import wow.doge.mygame.game.GameAppResource import io.odin.json.Formatter - +import wow.doge.mygame.game.GameSystemsInitializer +import wow.doge.mygame.subsystems.events.EventsModule2 +import wow.doge.mygame.implicits._ +import com.jme3.bullet.BulletAppState +import akka.util.Timeout +import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource +import ammonite.runtime.tools.time object Main extends BIOApp with MainModule { import java.util.logging.{Logger => JLogger, Level} JLogger.getLogger("").setLevel(Level.SEVERE) + implicit val timeout = Timeout(1.second) def appResource = for { @@ -29,23 +33,70 @@ object Main extends BIOApp with MainModule { Formatter.json ).withAsync(timeWindow = 1.milliseconds) jmeScheduler <- jMESchedulerResource + actorSystem <- actorSystemResource2(logger) + scriptCacheActor <- new ScriptSystemResource(os.pwd, actorSystem)( + timeout, + actorSystem.scheduler + ).make + // akkaScheduler = actorSystemResource2.scheduler // consoleTextArea <- Resource.liftF(Task(new TextArea())) // consoleStream <- wireWith(JFXConsoleStream.textAreaStream _) (gameApp, gameAppFib) <- { // new BulletAppState() // bas.setThreadingType(Thr) // gameAppResource(new StatsAppState()) - wire[GameAppResource].make + wire[GameAppResource].get } + app = gameApp + inputManager = gameApp.inputManager + assetManager = gameApp.assetManager + bulletAppState = new BulletAppState() + (playerMovementEventBus, playerCameraEventBus) <- new EventsModule2( + actorSystem + ).resource + // b1 = playerMovementEventBus + // b2 = playerCameraEventBus + + // playerPos = ImVector3f.ZERO + // playerNode = None.taggedWith[Player] + // modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o".taggedWith[Player] + // playerController <- Resource.liftF { + // implicit val s = actorSystem.scheduler + // wire[PlayerController.Props].create.onErrorHandle(err => + // logger.error(err.toString()) + // ) + // } // _ <- Resource.liftF(IO(JMERunner.runner = gameApp)) // _ <- Resource.liftF(IO { // new ActorSystemModule {} // }) - actorSystem <- wireWith(actorSystemResource _) - _ <- Resource.liftF( - gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler)) - ) + // actorSystem <- wireWith(actorSystemResource _) + + // rootActor <- rootActorResource(logger, gameApp, schedulers, as2) + // _ <- Resource.liftF( + // gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler)) + // ) + // gameSystemsInitializer = new GameSystemsInitializer( + // actorSystem, + // logger, + // playerMovementEventBus, + // playerCameraEventBus + // )(gameApp) + + gameSystemsInitializerFib <- Resource.make( + logger.info("creating game systems initializer") >> + gameApp + .enqueueL(() => wire[GameSystemsInitializer]) + .start + )(c => logger.info("destroying game systems initializer") >> c.cancel) + _ <- Resource.liftF(gameSystemsInitializerFib.join.flatMap(_.init)) + + // .runAsync { + // case Left(err) => println(err) + // case _ => + // }(schedulers.async) + // _ <- Resource.liftF { // Task { // implicit val sched = actorSystem.scheduler @@ -73,6 +124,18 @@ object Main extends BIOApp with MainModule { // (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop))) } yield (gameAppFib) + // def createPlayerController( + // playerMovementEventBus: ActorRef[ + // EventBus.Command[PlayerMovementEvent] + // ], + // playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] + // ): IO[PlayerController.Error, Unit] = { + // val playerPos = ImVector3f.ZERO + // val playerNode = None.taggedWith[Player] + // val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" + // wire[PlayerController.Props].create + // } + def run(args: List[String]): UIO[ExitCode] = { // Console.withOut( diff --git a/src/main/scala/wow/doge/mygame/game/GameApp.scala b/src/main/scala/wow/doge/mygame/game/GameApp.scala index 880174f..2a2d939 100644 --- a/src/main/scala/wow/doge/mygame/game/GameApp.scala +++ b/src/main/scala/wow/doge/mygame/game/GameApp.scala @@ -13,9 +13,11 @@ import monix.reactive.MulticastStrategy import monix.reactive.Observable import monix.execution.atomic.Atomic import scala.collection.immutable.Queue +import wow.doge.mygame.executors.Schedulers class GameApp( // actorSystem: ActorSystem[SpawnProtocol.Command], + schedulers: Schedulers, appStates: AppState* ) extends SimpleApplication(appStates: _*) { import GameApp._ @@ -27,7 +29,7 @@ class GameApp( private val tickSubject = ConcurrentSubject[Float](multicast = MulticastStrategy.publish)( - monix.execution.Scheduler.Implicits.global + schedulers.async ) // (scheduler) diff --git a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala index 90ad67d..340aca4 100644 --- a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala +++ b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala @@ -39,21 +39,21 @@ object GameAppActor { Behaviors.setup[Command] { ctx => ctx.log.info("Hello from GameAppActor") - { - implicit val s = schedulers.async + // { + // implicit val s = schedulers.async - val initializer: GameSystemsInitializer = wire[GameSystemsInitializer] + // val initializer: GameSystemsInitializer = wire[GameSystemsInitializer] - schedulers.async.execute(() => initializer.init.runAsyncAndForget) + // schedulers.async.execute(() => initializer.init.runAsyncAndForget) - // ctx.pipeToSelf(application.timed.runToFuture) { - // case Failure(exception) => - // ApplicationStartFailed(exception.getMessage()) - // case Success(value) => - // println("here applications started") - // ApplicationStarted - // } - } + // // ctx.pipeToSelf(application.timed.runToFuture) { + // // case Failure(exception) => + // // ApplicationStartFailed(exception.getMessage()) + // // case Success(value) => + // // println("here applications started") + // // ApplicationStarted + // // } + // } Behaviors.receiveMessage { msg => msg match { diff --git a/src/main/scala/wow/doge/mygame/game/GameModule.scala b/src/main/scala/wow/doge/mygame/game/GameModule.scala index 515c5bc..bb74081 100644 --- a/src/main/scala/wow/doge/mygame/game/GameModule.scala +++ b/src/main/scala/wow/doge/mygame/game/GameModule.scala @@ -11,14 +11,17 @@ import monix.bio.IO import monix.bio.Fiber import monix.execution.Scheduler import com.jme3.app.StatsAppState -import com.jme3.app.FlyCamAppState -// import wow.doge.mygame.executors.JMERunner -class GameAppResource(logger: Logger[Task], jmeScheduler: Scheduler) { - def make: Resource[Task, (GameApp, Fiber[Throwable, Unit])] = +import wow.doge.mygame.executors.Schedulers +class GameAppResource( + logger: Logger[Task], + jmeScheduler: Scheduler, + schedulers: Schedulers +) { + def get: Resource[Task, (GameApp, Fiber[Throwable, Unit])] = Resource.make( for { _ <- logger.info("Creating game app") - app <- Task(new GameApp(new StatsAppState())) + app <- Task(new GameApp(schedulers, new StatsAppState())) _ <- Task { val settings = new AppSettings(true) settings.setVSync(true) @@ -38,12 +41,13 @@ trait GameModule { def gameAppResource( logger: Logger[Task], - jmeScheduler: Scheduler + jmeScheduler: Scheduler, + schedulers: Schedulers ): Resource[Task, (GameApp, Fiber[Throwable, Unit])] = Resource.make( (for { _ <- logger.info("Creating game app") - app <- Task(new GameApp()) + app <- Task(new GameApp(schedulers)) _ <- Task { val settings = new AppSettings(true) settings.setVSync(true) diff --git a/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala b/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala index 8ad4b86..11de59b 100644 --- a/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala +++ b/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala @@ -3,44 +3,54 @@ package wow.doge.mygame.game import scala.concurrent.duration._ import akka.actor.typed.ActorRef -import akka.actor.typed.Props import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout import com.jme3.asset.plugins.ZipLocator import com.jme3.bullet.BulletAppState -import com.jme3.bullet.control.BetterCharacterControl import com.softwaremill.macwire._ import com.softwaremill.tagging._ import io.odin.Logger import monix.bio.Task import monix.reactive.Consumer import wow.doge.mygame.events.EventBus -import wow.doge.mygame.events.EventsModule import wow.doge.mygame.game.nodes.Player import wow.doge.mygame.game.nodes.PlayerController import wow.doge.mygame.game.subsystems.input.GameInputHandler import wow.doge.mygame.game.subsystems.level.DefaultGameLevel import wow.doge.mygame.implicits._ -import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.utils.IOUtils +import wow.doge.mygame.math.ImVector3f +import monix.bio.IO +import wow.doge.mygame.subsystems.events.EntityMovementEvent +import akka.actor.typed.ActorSystem +import wow.doge.mygame.subsystems.events.PlayerCameraEvent -class GameSystemsInitializer()( - override val spawnProtocol: ActorRef[SpawnProtocol.Command], - override implicit val akkaScheduler: Scheduler, - app: GameApp, - loggerL: Logger[Task] -) extends EventsModule { - override implicit val timeout: Timeout = Timeout(1.second) +class GameSystemsInitializer( + spawnProtocol: ActorSystem[SpawnProtocol.Command], + loggerL: Logger[Task], + // eventBuses: EventsModule2 + playerMovementEventBus: ActorRef[ + EventBus.Command[EntityMovementEvent.PlayerMovementEvent] + ], + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] +)(app: GameApp) { + implicit val timeout: Timeout = Timeout(1.second) import GameSystemsInitializer._ - def init = + implicit val akkaScheduler: Scheduler = spawnProtocol.scheduler + lazy val inputManager = app.inputManager + lazy val assetManager = app.assetManager + lazy val bulletAppState = new BulletAppState() + // lazy val playerMovementEventBus = eventBuses.playerMovementEventBusTask + + val init = for { - playerMovementEventBus <- playerMovementEventBusTask - inputManager = app.inputManager - bulletAppState = new BulletAppState() + _ <- loggerL.info("Initializing Systems") + // playerMovementEventBus <- playerMovementEventBusTask _ <- Task(app.stateManager.attach(bulletAppState)) _ <- Task( app.assetManager.registerLocator( @@ -50,58 +60,32 @@ class GameSystemsInitializer()( ) ) _ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState)) - playerController <- app.enqueueL(() => - PlayerController( - app, - modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o", - cam = app.camera - )(app.assetManager, bulletAppState) - .taggedWith[Player] - ) - // _ <- loggerL.debug(playerNode.getName()) - // _ <- Task(app.rootNode.attachChild(playerNode)) - // playerMovementActor <- wireWith(spawnMovementActor _) - // _ <- - // IOUtils - // .toIO( - // app.tickObservable - // .doOnNext { tpf => - // IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf)) - // } - // .completedL - // .startAndForget - // .onErrorRestart(3) - // ) + _ <- wireWith(createPlayerController _).startAndForget _ <- wire[GameInputHandler.Props].begin } yield () + + def createPlayerController( + // playerMovementEventBus: ActorRef[ + // EventBus.Command[PlayerMovementEvent] + // ], + // playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] + ): IO[PlayerController.Error, Unit] = { + val playerPos = ImVector3f.ZERO + val playerNode = None.taggedWith[Player] + val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" + wire[PlayerController.Props].create + } } object GameSystemsInitializer { - def spawnMovementActor( - app: GameApp, - spawnProtocol: ActorRef[SpawnProtocol.Command], - playerNode: BetterCharacterControl @@ Player, - playerMovementEventBus: ActorRef[ - EventBus.Command[PlayerMovementEvent] - ], - loggerL: Logger[Task] - )(implicit timeout: Timeout, scheduler: Scheduler) = - spawnProtocol.askL[ActorRef[ImMovementActor.Command]]( - SpawnProtocol.Spawn( - ImMovementActor.Props(app, playerNode, playerMovementEventBus).create, - "imMovementActor", - Props.empty, - _ - ) - ) def playerMovementActorTickConsumer( playerMovementActor: ActorRef[ImMovementActor.Command] ) = Consumer .foreachTask[Float](tpf => - IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf)) + IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick) ) // .mapTask() } diff --git a/src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala b/src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala index 88c8769..862a4cb 100644 --- a/src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala +++ b/src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala @@ -62,7 +62,7 @@ class PlayerMovementState( override def update(tpf: Float) = { // movementActor ! MovementActor.Tick(tpf, geom, cam) - imMovementActor ! ImMovementActor.Tick(tpf) + // imMovementActor ! ImMovementActor.Tick(tpf) // movementActorTimer ! MovementActorTimer.Update(tpf) } diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala new file mode 100644 index 0000000..f8d94e6 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala @@ -0,0 +1,201 @@ +package wow.doge.mygame.game.nodes + +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.ActorContext +import wow.doge.mygame.subsystems.movement.ImMovementActor +import wow.doge.mygame.game.GameApp +import akka.actor.typed.ActorRef +import wow.doge.mygame.events.EventBus +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EntityMovementEvent +import akka.actor.typed.scaladsl.TimerScheduler +import akka.actor.typed.Behavior +import scala.concurrent.duration._ +import akka.actor.typed.LogOptions +import org.slf4j.event.Level +import com.typesafe.scalalogging.Logger +import akka.actor.typed.SupervisorStrategy +import com.jme3.scene.CameraNode +import wow.doge.mygame.subsystems.events.PlayerCameraEvent +import wow.doge.mygame.game.subsystems.movement.CanMove +import wow.doge.mygame.implicits._ + +object PlayerActorSupervisor { + sealed trait Command + + final case class Props( + app: GameApp, + camNode: CameraNode, + playerMovementEventBus: ActorRef[ + EventBus.Command[PlayerMovementEvent] + ], + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] + ) { + 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( + ImMovementActor + .Props(app, movable, playerMovementEventBus) + .create + ) + .onFailure[Exception](SupervisorStrategy.restart), + "playerMovementActor" + ) + lazy val playerMovementEventHandler = ctx.spawn( + Behaviors + .supervise(PlayerMovementEventListener(movementActor)) + .onFailure[Exception](SupervisorStrategy.restart), + "playerMovementEventHandler" + ) + lazy val movementActorTimer = ctx.spawn( + Behaviors + .supervise(MovementActorTimer(movementActor)) + .onFailure[Exception](SupervisorStrategy.restart), + "playerMovementActorTimer" + ) + + lazy val playerCameraHandler = { + ctx.spawn( + Behaviors + .supervise( + PlayerCameraEventListener(camNode, app.enqueueR) + ) + .onFailure[Exception](SupervisorStrategy.restart), + "playerCameraHandler" + ) + } + + //init actors + movementActorTimer ! MovementActorTimer.Start + playerMovementEventBus ! EventBus.Subscribe( + playerMovementEventHandler + ) + playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler) + + new PlayerActorSupervisor( + ctx, + this, + Children(movementActor, playerMovementEventHandler) + ).receive + } + ) + } + + case class Children( + movementActor: ActorRef[ImMovementActor.Command], + playerMovementEventHandler: ActorRef[ + EntityMovementEvent.PlayerMovementEvent + ] + ) +} +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 MovementActorTimer { + sealed trait Command + final case object Start extends Command + final case object Stop extends Command + private case object Send extends Command + case object TimerKey + + def apply(target: ActorRef[ImMovementActor.Command]) = + Behaviors.withTimers[Command] { timers => + new MovementActorTimer(timers, target).idle + } +} +class MovementActorTimer( + timers: TimerScheduler[MovementActorTimer.Command], + target: ActorRef[ImMovementActor.Command] +) { + import MovementActorTimer._ + + val idle: Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case Start => + timers.startTimerWithFixedDelay(TimerKey, Send, (60f / 144).millis) + active + case _ => Behaviors.unhandled + } + } + + val active: Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case Send => + target ! ImMovementActor.Tick + Behaviors.same + case Stop => + timers.cancel(TimerKey) + idle + case _ => Behaviors.unhandled + + } + } +} + +object GenericTimerActor { + sealed trait Command + final case object Start extends Command + final case object Stop extends Command + private case object Send extends Command + case object TimerKey + + case class Props[T]( + target: ActorRef[T], + messageToSend: T, + timeInterval: FiniteDuration + ) { + val create = Behaviors.withTimers[Command] { timers => + new GenericTimerActor(timers, this).idle + } + } +} +class GenericTimerActor[T]( + timers: TimerScheduler[GenericTimerActor.Command], + 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 + } +} diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala b/src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala new file mode 100644 index 0000000..8047e75 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala @@ -0,0 +1,30 @@ +package wow.doge.mygame.game.nodes + +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.ActorContext + +object PlayerCameraActor { + sealed trait Command + + class Props() { + def create = + Behaviors.setup[Command] { ctx => + new PlayerCameraActor(ctx, this).receive(State.empty) + } + } + + case class State() + object State { + val empty = State() + } +} +class PlayerCameraActor( + ctx: ActorContext[PlayerCameraActor.Command], + props: PlayerCameraActor.Props +) { + import PlayerCameraActor._ + def receive(state: State) = + Behaviors.receiveMessage[Command] { + case _ => Behaviors.same + } +} diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala b/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala index 42d9968..a6d5f60 100644 --- a/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala +++ b/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala @@ -6,9 +6,7 @@ import com.jme3.scene.Geometry import com.jme3.renderer.Camera import com.jme3.asset.AssetManager import wow.doge.mygame.state.MyMaterial -import com.jme3.math.Vector3f import com.jme3.scene.control.CameraControl.ControlDirection -import com.jme3.syntax._ import com.jme3.scene.shape.Box import com.jme3.bullet.control.BetterCharacterControl import com.jme3.bullet.BulletAppState @@ -16,20 +14,90 @@ import wow.doge.mygame.game.GameApp import wow.doge.mygame.implicits._ import wow.doge.mygame.math.ImVector3f import com.jme3.math.FastMath +import monix.bio.IO +import cats.implicits._ +import akka.actor.typed.ActorRef +import wow.doge.mygame.events.EventBus +import akka.actor.typed.SpawnProtocol +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.movement.ImMovementActor +import io.odin.Logger +import akka.util.Timeout +import monix.bio.Task +import akka.actor.typed.Scheduler +import akka.actor.typed.Props +import com.softwaremill.tagging._ +import wow.doge.mygame.subsystems.events.PlayerCameraEvent +import wow.doge.mygame.utils.AkkaUtils // class PlayerNode(val name: String) extends Node(name) {} -trait Player +sealed trait Player +sealed trait PlayerCameraNode + object PlayerController { - def defaultMesh = { - lazy val b = new Box(1, 1, 1) - lazy val geom = new Geometry("playerMesh", b) - geom + sealed trait Error + case class GenericError(reason: String) extends Error + + class Props( + app: GameApp, + loggerL: Logger[Task], + assetManager: AssetManager, + bulletAppState: BulletAppState, + initialPlayerPos: ImVector3f = ImVector3f.ZERO, + modelPath: os.RelPath, + spawnProtocol: ActorRef[SpawnProtocol.Command], + playerMovementEventBus: ActorRef[ + EventBus.Command[PlayerMovementEvent] + ], + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], + _playerPhysicsControl: Option[BetterCharacterControl], + _playerNode: Option[Node with Player] = None, + _cameraNode: Option[CameraNode with PlayerCameraNode] = None + )(implicit timeout: Timeout, scheduler: Scheduler) { + import Defaults._ + import Methods._ + val create: IO[Error, Unit] = + // IO.raiseError(GenericError("not implemented yet")) + (for { + camNode <- IO( + _cameraNode.getOrElse(defaultCamerNode(app.camera, initialPlayerPos)) + ) + playerPhysicsControl <- IO( + _playerPhysicsControl + .getOrElse(defaultPlayerPhysicsControl) + .taggedWith[Player] + ) + playerNode <- IO.fromEither( + _playerNode.fold( + defaultPlayerNode( + assetManager, + modelPath, + initialPlayerPos, + camNode, + playerPhysicsControl + ) + )(_.asRight) + ) + playerActor <- AkkaUtils.spawnActorL( + spawnProtocol, + "playerActorSupervisor", + new PlayerActorSupervisor.Props( + app, + camNode, + playerMovementEventBus, + playerCameraEventBus + ).create(playerPhysicsControl) + ) + _ <- IO { + bulletAppState.physicsSpace += playerNode + bulletAppState.physicsSpace += playerPhysicsControl + } + _ <- IO(app.rootNode += playerNode) + } yield ()) + .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage()))) + .executeOn(app.scheduler) + } - def defaultTexture(assetManager: AssetManager) = - MyMaterial( - assetManager = assetManager, - path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" - ) def apply( app: GameApp, @@ -41,10 +109,6 @@ object PlayerController { .withJumpForce(ImVector3f(0, 5f, 0)) lazy val playerNode = new Node("PlayerNode") .withChildren( - new CameraNode("CameraNode", cam) - .withControlDir(ControlDirection.SpatialToCamera) - .withLocalTranslation(ImVector3f(0, 1.5f, 10)) - .withLookAt(playerPos, ImVector3f.UNIT_Y), assetManager .loadModel(modelPath) .asInstanceOf[Node] @@ -64,41 +128,93 @@ object PlayerController { playerPhysicsControl } - def apply( - mesh: Geometry = defaultMesh, - texturePath: os.RelPath = - os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md", - cam: Camera - )(assetManager: AssetManager): Node = { +} - lazy val playerNode = new Node("PlayerNode") - lazy val camNode = new CameraNode("CameraNode", cam) +object Defaults { + lazy val defaultMesh = { + val b = Box(1, 1, 1) + val geom = Geometry("playerMesh", b) + geom + } + def defaultTexture(assetManager: AssetManager) = + MyMaterial( + assetManager = assetManager, + path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" + ) - { - val mat = MyMaterial( - assetManager = assetManager, - path = texturePath - ) - mesh.setMaterial(mat) - } + def defaultCamerNode(cam: Camera, playerPos: ImVector3f) = + new CameraNode("CameraNode", cam) + .withControlDir(ControlDirection.SpatialToCamera) + .withLocalTranslation(ImVector3f(0, 1.5f, 10)) + .withLookAt(playerPos, ImVector3f.UNIT_Y) - // lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera()) - // camNode.setCamera(simpleApp.getCamera()) - discard { - playerNode - .withChild(camNode) - .withChild(mesh) - // playerNode.children(Seq(camNode, geom)) - } + def defaultPlayerNode( + assetManager: AssetManager, + modelPath: os.RelPath, + playerPos: ImVector3f, + camNode: CameraNode, + playerPhysicsControl: BetterCharacterControl + ) = + Either.catchNonFatal( + Node("PlayerNode") + .withChildren( + camNode, + assetManager + .loadModel(modelPath) + .asInstanceOf[Node] + .withRotate(0, FastMath.PI, 0) + ) + .withLocalTranslation(playerPos) + .withControl(playerPhysicsControl) + ) - { - camNode.setControlDir(ControlDirection.SpatialToCamera) - camNode.setLocalTranslation( - new Vector3f(0, 1.5f, 10) - ) - camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y) - } + lazy val defaultPlayerPhysicsControl = + new BetterCharacterControl(1.5f, 6f, 1f) + .withJumpForce(ImVector3f(0, 5f, 0)) +} - playerNode - } +object Methods { + def spawnMovementActor( + app: GameApp, + spawnProtocol: ActorRef[SpawnProtocol.Command], + movable: BetterCharacterControl @@ Player, + playerMovementEventBus: ActorRef[ + EventBus.Command[PlayerMovementEvent] + ], + loggerL: Logger[Task] + )(implicit timeout: Timeout, scheduler: Scheduler) = + spawnProtocol.askL[ActorRef[ImMovementActor.Command]]( + SpawnProtocol.Spawn( + ImMovementActor.Props(app, movable, playerMovementEventBus).create, + "imMovementActor", + Props.empty, + _ + ) + ) + // def spawnPlayerActor( + // app: GameApp, + // spawnProtocol: ActorRef[SpawnProtocol.Command], + // movable: BetterCharacterControl @@ Player, + // playerMovementEventBus: ActorRef[ + // EventBus.Command[PlayerMovementEvent] + // ] + // )(implicit timeout: Timeout, scheduler: Scheduler) = + // spawnProtocol.askL[ActorRef[PlayerActorSupervisor.Command]]( + // SpawnProtocol.Spawn( + // new PlayerActorSupervisor.Props( + // app, + // movable, + // playerMovementEventBus + // ).create, + // "playerActor", + // Props.empty, + // _ + // ) + // ) } +// spawnPlayerActor( +// app, +// spawnProtocol, +// playerPhysicsControl, +// playerMovementEventBus +// ) diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala b/src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala index b3f28e1..72ef5b9 100644 --- a/src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala +++ b/src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala @@ -5,17 +5,23 @@ import akka.actor.typed.scaladsl.Behaviors import wow.doge.mygame.subsystems.movement.ImMovementActor import org.slf4j.event.Level import akka.actor.typed.LogOptions -import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import com.typesafe.scalalogging.Logger +import wow.doge.mygame.subsystems.events.PlayerCameraEvent +import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp +import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedDown +import com.jme3.scene.CameraNode +import wow.doge.mygame.game.GameApp +import wow.doge.mygame.implicits._ -object PlayerMovementEventHandler { +object PlayerMovementEventListener { import PlayerMovementEvent._ def apply(movementActor: ActorRef[ImMovementActor.Command]) = Behaviors.logMessages( LogOptions() .withLevel(Level.TRACE) .withLogger( - Logger[PlayerMovementEventHandler.type].underlying + Logger[PlayerMovementEventListener.type].underlying ), Behaviors.setup[PlayerMovementEvent](ctx => Behaviors.receiveMessage { @@ -52,3 +58,33 @@ object PlayerMovementEventHandler { ) ) } + +object PlayerCameraEventListener { + def apply( + camNode: CameraNode, + enqueueR: Function1[() => Unit, Unit] + ) = + Behaviors.logMessages( + LogOptions() + .withLevel(Level.TRACE) + .withLogger( + Logger[PlayerCameraEventListener.type].underlying + ), + Behaviors.setup[PlayerCameraEvent](ctx => + Behaviors.receiveMessage { + case CameraMovedUp => + enqueueR(() => { + + camNode.move(0, 1, 0) + }) + Behaviors.same + case CameraMovedDown => + enqueueR(() => { + + camNode.move(0, -1, 0) + }) + Behaviors.same + } + ) + ) +} diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala b/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala index 5659fa8..43dd24e 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala @@ -8,10 +8,11 @@ import com.jme3.input.KeyInput import com.jme3.input.controls.KeyTrigger import monix.bio.UIO import wow.doge.mygame.utils.IOUtils._ -import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import scala.concurrent.duration._ import com.jme3.input.controls.MouseAxisTrigger import com.jme3.input.MouseInput +import wow.doge.mygame.subsystems.events.PlayerCameraEvent // class GameInputHandler( // inputManager: InputManager @@ -24,7 +25,8 @@ object GameInputHandler { inputManager: InputManager, playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] - ] + ], + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] ) { def begin = for { @@ -44,7 +46,12 @@ object GameInputHandler { _ <- toIO( generateCameraEvents( inputManager, - playerMovementEventBus + playerCameraEventBus + ).completedL.startAndForget + ) + _ <- toIO( + myTest( + inputManager ).completedL.startAndForget ) } yield () @@ -53,22 +60,22 @@ object GameInputHandler { def setupKeys(inputManager: InputManager) = inputManager .withMapping( - "Left", + PlayerMovementInput.WalkLeft.entryName, new KeyTrigger(KeyInput.KEY_A) // new KeyTrigger(KeyInput.KEY_LEFT) ) .withMapping( - "Right", + PlayerMovementInput.WalkRight.entryName, new KeyTrigger(KeyInput.KEY_D) // new KeyTrigger(KeyInput.KEY_RIGHT) ) .withMapping( - "Up", + PlayerMovementInput.WalkForward.entryName, new KeyTrigger(KeyInput.KEY_W) // new KeyTrigger(KeyInput.KEY_UP) ) .withMapping( - "Down", + PlayerMovementInput.WalkBackward.entryName, new KeyTrigger(KeyInput.KEY_S) // new KeyTrigger(KeyInput.KEY_DOWN) ) @@ -77,12 +84,12 @@ object GameInputHandler { new KeyTrigger(KeyInput.KEY_SPACE) ) .withMapping( - "ROTATE_RIGHT", + PlayerAnalogInput.TurnRight.entryName, new KeyTrigger(KeyInput.KEY_RIGHT), new MouseAxisTrigger(MouseInput.AXIS_X, true) ) .withMapping( - "ROTATE_LEFT", + PlayerAnalogInput.TurnLeft.entryName, new KeyTrigger(KeyInput.KEY_LEFT), new MouseAxisTrigger(MouseInput.AXIS_X, false) ) @@ -104,62 +111,67 @@ object GameInputHandler { EventBus.Command[PlayerMovementEvent] ] ) = { - val name = "movementInputEventsGenerator" + val name = "playerMovementInputEventsGenerator" inputManager - .observableAction( - "Left", - "Right", - "Up", - "Down", - "Jump", - "ROTATE_RIGHT", - "ROTATE_LEFT" - ) + .enumObservableAction(PlayerMovementInput) // .dump("O") .doOnNext { action => - action.binding.name match { - case "Left" => + action.binding match { + case PlayerMovementInput.WalkLeft => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerMovedLeft(pressed = action.value), name ) ) - case "Right" => + case PlayerMovementInput.WalkRight => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerMovedRight(pressed = action.value), name ) ) - case "Up" => + case PlayerMovementInput.WalkForward => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerMovedForward(pressed = action.value), name ) ) - case "Down" => + case PlayerMovementInput.WalkBackward => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerMovedBackward(pressed = action.value), name ) ) + // case "Jump" if action.value => + // toTask( + // playerMovementEventBus !! EventBus.Publish( + // PlayerMovementEvent.PlayerJumped, + // name + // ) + // ) - case "Jump" if action.value => - toTask( - playerMovementEventBus !! EventBus.Publish( - PlayerMovementEvent.PlayerJumped, - name - ) - ) - - case _ => monix.eval.Task.unit + // case _ => monix.eval.Task.unit } } } + def myTest(inputManager: InputManager) = { + inputManager + .enumObservableAction(PlayerMovementEnum) + .sample(1.millis) + .mapEval(action => + action.binding match { + case PlayerMovementEnum.MOVE_RIGHT => + monix.eval.Task(println("move right")) >> monix.eval.Task.unit + case PlayerMovementEnum.MOVE_LEFT => + monix.eval.Task(println("move left")) + } + ) + } + def generateRotateEvents( inputManager: InputManager, playerMovementEventBus: ActorRef[ @@ -168,34 +180,32 @@ object GameInputHandler { ) = { val name = "rotateMovementEventsGenerator" inputManager - .analogObservable("ROTATE_RIGHT", "ROTATE_LEFT") + .enumAnalogObservable(PlayerAnalogInput) .sample(1.millis) - .mapEval(analogEvent => - analogEvent.binding.name match { - case "ROTATE_RIGHT" => + .map(e => e) + .doOnNext(analogEvent => + analogEvent.binding match { + case PlayerAnalogInput.TurnRight => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerRotatedRight, name ) ) - case "ROTATE_LEFT" => + case PlayerAnalogInput.TurnLeft => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerRotatedLeft, name ) ) - case _ => monix.eval.Task.unit } ) } def generateCameraEvents( inputManager: InputManager, - playerMovementEventBus: ActorRef[ - EventBus.Command[PlayerMovementEvent] - ] + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] ) = { val name = "cameraMovementEventsGenerator" inputManager @@ -205,15 +215,15 @@ object GameInputHandler { analogEvent.binding.name match { case "CAMERA_UP" => toTask( - playerMovementEventBus !! EventBus.Publish( - PlayerMovementEvent.PlayerCameraUp, + playerCameraEventBus !! EventBus.Publish( + PlayerCameraEvent.CameraMovedUp, name ) ) case "CAMERA_DOWN" => toTask( - playerMovementEventBus !! EventBus.Publish( - PlayerMovementEvent.PlayerCameraDown, + playerCameraEventBus !! EventBus.Publish( + PlayerCameraEvent.CameraMovedDown, name ) ) diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala new file mode 100644 index 0000000..a69efa4 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala @@ -0,0 +1,19 @@ +package wow.doge.mygame.game.subsystems.input +import enumeratum._ +import enumeratum.EnumEntry._ + +sealed trait PlayerMovementInput extends EnumEntry with UpperSnakecase +object PlayerMovementInput extends Enum[PlayerMovementInput] { + val values = findValues + case object WalkForward extends PlayerMovementInput + case object WalkRight extends PlayerMovementInput + case object WalkLeft extends PlayerMovementInput + case object WalkBackward extends PlayerMovementInput +} + +sealed trait PlayerAnalogInput extends EnumEntry with UpperSnakecase +object PlayerAnalogInput extends Enum[PlayerAnalogInput] { + val values = findValues + case object TurnRight extends PlayerAnalogInput + case object TurnLeft extends PlayerAnalogInput +} diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala b/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala new file mode 100644 index 0000000..2c0ff02 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala @@ -0,0 +1,70 @@ +package wow.doge.mygame.game.subsystems.movement + +import com.jme3.bullet.control.BetterCharacterControl +import com.jme3.math.FastMath +import com.jme3.math.Quaternion +import com.jme3.math.Vector3f +import wow.doge.mygame.math.ImVector3f +import wow.doge.mygame.subsystems.movement.RotateDir +import wow.doge.mygame.implicits._ +import com.jme3.scene.Spatial +import com.typesafe.scalalogging.LazyLogging + +trait CanMove[-A] { + // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f + def move(inst: A, direction: ImVector3f): Unit + def jump(inst: A): Unit + def stop(inst: A): Unit + def rotate(inst: A, rotateDir: RotateDir): Unit +} + +object CanMove { + implicit val implCanMoveForBetterCharacterControl = + new CanMove[BetterCharacterControl] { + override def move( + inst: BetterCharacterControl, + direction: ImVector3f + ): Unit = { + // val dir = direction.mutable + // inst.setViewDirection(dir) + // inst.setViewDirection(direction.mutable) + inst.setWalkDirection(direction.mutable.multLocal(50f)) + } + override def jump(inst: BetterCharacterControl): Unit = inst.jump() + override def rotate( + inst: BetterCharacterControl, + rotateDir: RotateDir + ): Unit = { + val q = + rotateDir match { + case RotateDir.Left => + new Quaternion() + .fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) + case RotateDir.Right => + new Quaternion() + .fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) + } + + val tmp = new Vector3f() + inst.getViewDirection(tmp) + inst.setViewDirection(q.mult(tmp)) + } + override def stop(inst: BetterCharacterControl) = + inst.setWalkDirection(Vector3f.ZERO) + } + + implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging { + override def move(inst: Spatial, direction: ImVector3f): Unit = { + inst.move(direction.mutable) + } + override def jump(inst: Spatial): Unit = + logger.warn("`Jump` is not implemented for type `Spatial`") + override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = { + rotateDir match { + case RotateDir.Left => inst.rotate(0, -0.01f, 0) + case RotateDir.Right => inst.rotate(0, 0.01f, 0) + } + } + override def stop(inst: Spatial) = { /*not required*/ } + } +} diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala b/src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala index f24270d..6e4492a 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala @@ -3,9 +3,7 @@ package wow.doge.mygame.subsystems.movement import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors -import com.softwaremill.quicklens._ import wow.doge.mygame.implicits._ -import com.jme3.renderer.Camera import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.game.GameApp @@ -13,10 +11,9 @@ import akka.actor.typed.ActorRef import wow.doge.mygame.events.EventBus import com.jme3.math.Vector3f import wow.doge.mygame.state.CardinalDirection -import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent -import akka.actor.typed.LogOptions -import com.typesafe.scalalogging.Logger -import org.slf4j.event.Level +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent +import wow.doge.mygame.game.subsystems.movement.CanMove +import com.softwaremill.quicklens._ sealed trait RotateDir object RotateDir { @@ -24,18 +21,10 @@ object RotateDir { case object Right extends RotateDir } -trait CanMove[-A] { - // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f - def move(inst: A, direction: ImVector3f): Unit - def jump(inst: A): Unit - def stop(inst: A): Unit - def rotate(inst: A, rotateDir: RotateDir): Unit -} - object ImMovementActor { sealed trait Command // final case class Tick(tpf: Float) extends Command - final case class Tick(tpf: Float) extends Command + final case object Tick extends Command sealed trait Movement extends Command final case class MovedLeft(pressed: Boolean) extends Movement @@ -101,51 +90,36 @@ class ImMovementActor[T]( case m: Movement => m match { case MovedLeft(pressed) => - props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) + props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable)) receive(state = state.modify(_.cardinalDir.left).setTo(pressed)) case MovedUp(pressed) => - props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) + props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable)) receive(state = state.modify(_.cardinalDir.up).setTo(pressed)) case MovedRight(pressed) => - props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) + props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable)) receive(state = state.modify(_.cardinalDir.right).setTo(pressed)) case MovedDown(pressed) => - props.app.enqueueF(stopIfNotPressed(pressed, props.movable)) + props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable)) receive(state = state.modify(_.cardinalDir.down).setTo(pressed)) case Jump => - props.app.enqueueF(cm.jump(props.movable)) + props.app.enqueueR(() => cm.jump(props.movable)) Behaviors.same case RotateLeft => - props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left)) + props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Left)) Behaviors.same case RotateRight => - props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right)) + props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Right)) Behaviors.same } - case Tick(tpf) => + case Tick => val walkDir = getDirection(state.cardinalDir, ctx.log.trace) if (walkDir != ImVector3f.ZERO) { - val tmp = walkDir * 25f * tpf - props.app.enqueueF { + val tmp = walkDir * 25f * (1f / 144) + props.app.enqueueR { () => cm.move(props.movable, tmp) } - - // props.app - // .enqueueScala { () => - // 1 - // } - // .map(println)(scala.concurrent.ExecutionContext.global) - // monix.eval.Task - // .fromFuture( - // props.app - // .enqueueScala { () => - // 1 - // } - // ) - // .flatMap(i => monix.eval.Task(println(i))) - // .runToFuture(monix.execution.Scheduler.Implicits.global) } Behaviors.same } @@ -182,3 +156,18 @@ object Methods { ) = if (!pressed) cm.stop(movable) } + +// props.app +// .enqueueScala { () => +// 1 +// } +// .map(println)(scala.concurrent.ExecutionContext.global) +// monix.eval.Task +// .fromFuture( +// props.app +// .enqueueScala { () => +// 1 +// } +// ) +// .flatMap(i => monix.eval.Task(println(i))) +// .runToFuture(monix.execution.Scheduler.Implicits.global) diff --git a/src/main/scala/wow/doge/mygame/implicits/TestEnum.scala b/src/main/scala/wow/doge/mygame/implicits/TestEnum.scala new file mode 100644 index 0000000..d568b2b --- /dev/null +++ b/src/main/scala/wow/doge/mygame/implicits/TestEnum.scala @@ -0,0 +1,43 @@ +package wow.doge.mygame.implicits + +import enumeratum._ + +sealed trait TestEnum extends EnumEntry + +object TestEnum extends Enum[TestEnum] { + val values = findValues + case object Test2 extends TestEnum +} + +sealed trait Greeting extends EnumEntry + +object Greeting extends Enum[Greeting] { + + /* + `findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum` + + You use it to implement the `val values` member + */ + val values = findValues + + case object Hello extends Greeting + case object GoodBye extends Greeting + case object Hi extends Greeting + case object Bye extends Greeting + +} +object ObsTest {} + +sealed trait PlayerMovementEnum extends EnumEntry { + def test: String +} + +object PlayerMovementEnum extends Enum[PlayerMovementEnum] { + val values = findValues + case object MOVE_RIGHT extends PlayerMovementEnum { + val test = "hmm" + } + case object MOVE_LEFT extends PlayerMovementEnum { + val test = "mmh" + } +} diff --git a/src/main/scala/wow/doge/mygame/implicits/package.scala b/src/main/scala/wow/doge/mygame/implicits/package.scala index 5fe9617..09d982b 100644 --- a/src/main/scala/wow/doge/mygame/implicits/package.scala +++ b/src/main/scala/wow/doge/mygame/implicits/package.scala @@ -19,11 +19,7 @@ import com.jme3.input.controls.InputListener import com.jme3.math.Vector3f import wow.doge.mygame.math.ImVector3f import com.jme3.scene.Geometry -import wow.doge.mygame.state.CardinalDirection -import wow.doge.mygame.subsystems.movement.CanMove -import com.jme3.renderer.Camera import scala.jdk.CollectionConverters._ -import wow.doge.mygame.utils.JFXConsoleStreamable import com.jme3.app.Application import com.jme3.scene.SceneGraphVisitor import monix.reactive.Observable @@ -46,20 +42,25 @@ import com.jme3.bullet.BulletAppState import wow.doge.mygame.state.MyBaseState import monix.bio.UIO import com.jme3.bullet.control.BetterCharacterControl -import com.jme3.scene.control.AbstractControl import com.jme3.scene.CameraNode import com.jme3.scene.control.CameraControl.ControlDirection -import com.jme3.bullet.control.AbstractPhysicsControl import com.jme3.scene.control.Control -import com.typesafe.scalalogging.Logger -import com.typesafe.scalalogging.LazyLogging import com.jme3.input.controls.AnalogListener -import com.jme3.math.Quaternion -import com.jme3.math.FastMath -import wow.doge.mygame.subsystems.movement.RotateDir +import enumeratum._ case class ActionEvent(binding: Action, value: Boolean, tpf: Float) +case class EnumActionEvent[T <: EnumEntry]( + binding: T, + value: Boolean, + tpf: Float +) + case class AnalogEvent(binding: Action, value: Float, tpf: Float) +case class EnumAnalogEvent[T <: EnumEntry]( + binding: T, + value: Float, + tpf: Float +) case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float) package object implicits { @@ -69,41 +70,11 @@ package object implicits { implicit class JMEAppExt(private val app: Application) extends AnyVal { - // /** - // * Blocking task. Execute on a thread pool meant for blocking operations. - // * Prefer [[wow.doge.mygame.implicits.JMEAppExt#enqueueT]] instead. - // * - // * @param cb - // * @param ec - // * @return - // */ - // def enqueueF[T](cb: () => T)(implicit ec: ExecutionContext): Future[T] = - // Future { - // app - // .enqueue(new Callable[T]() { - // override def call(): T = cb() - // }) - // .get() - // } - - // /** - // * Blocking task. Execute on a thread pool meant for blocking operations. - // * Same as enqueue, but returns a Monix Task instead of Future - // * @param cb - // * @param ec - // * @return - // */ - // def enqueueL[T](cb: () => T): Task[T] = - // Task - // .deferFutureAction(implicit s => enqueueF(cb)) - - def enqueueF[T](cb: => T) = + def enqueueR(cb: () => Unit) = app.enqueue(new Runnable { - override def run() = cb + override def run() = cb() }) - def enqueueT(cb: => Unit) = - Task(enqueueF(cb)) } implicit class StateManagerExt(private val sm: AppStateManager) extends AnyVal { @@ -190,38 +161,35 @@ package object implicits { def observableDepthFirst(): Observable[Spatial] = { def loop( subscriber: Subscriber[Spatial], - spatial: Spatial - ): Task[Unit] = { - //spatial can be either a node or a geometry, but it's not a sealed trait - spatial match { - - case node: Node => - Task.deferFuture(subscriber.onNext(node)).flatMap { - case Ack.Continue => { - //modifying a node's children list is forbidden + spatials: LazyList[Spatial] + ): Task[Unit] = + spatials match { + // spatial can be either a node or a geometry, but it's not a sealed trait + case head #:: tail => + head match { + case g: Geometry => + Task.deferFuture(subscriber.onNext(g)).flatMap { + case Continue => + loop(subscriber, tail) + case Stop => + Task(subscriber.onComplete()) + } + case node: Node => val children = node.children - if (!children.isEmpty) { - Task.sequence( - children.map(c => loop(subscriber, c)) - ) >> Task.unit - } else { - Task.unit + Task.deferFuture(subscriber.onNext(node)).flatMap { + case Continue => + loop(subscriber, children #::: tail) + case Stop => + Task(subscriber.onComplete()) } - - } - case Ack.Stop => Task.unit - + // case _ => loop(subscriber, tail) } - //geomtries do not/cannot have children - case g: Geometry => - Task.deferFuture(subscriber.onNext(g)) >> Task.unit - case _ => Task.unit + case LazyList() => Task.unit } - } Observable.create(OverflowStrategy.Unbounded) { sub => implicit val sched = sub.scheduler - loop(sub, n).runToFuture + loop(sub, LazyList(n)).runToFuture } } @@ -243,16 +211,24 @@ package object implicits { Task.deferFuture(subscriber.onNext(g)).flatMap { case Continue => loop(subscriber, tail) - case Stop => Task.unit + case Stop => + Task(subscriber.onComplete()) } case node: Node => val children = node.children Task.deferFuture(subscriber.onNext(node)).flatMap { case Continue => loop(subscriber, tail #::: children) - case Stop => Task.unit + case Stop => + Task(subscriber.onComplete()) + } + case unknown => + Task.deferFuture(subscriber.onNext(unknown)).flatMap { + case Continue => + loop(subscriber, tail) + case Stop => + Task(subscriber.onComplete()) } - // case _ => loop(subscriber, tail) } case LazyList() => Task.unit } @@ -369,8 +345,10 @@ package object implicits { ): Unit = { if ( sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop - ) + ) { + sub.onComplete() c.cancel() + } } } @@ -380,9 +358,45 @@ package object implicits { c } } + + def enumObservableAction[T <: EnumEntry]( + mappingEnum: Enum[T] + ): Observable[EnumActionEvent[T]] = { + + Observable.create(OverflowStrategy.DropOld(10)) { sub => + val c = SingleAssignCancelable() + val entryNames = mappingEnum.values.map(_.entryName) + val al = new ActionListener { + override def onAction( + binding: String, + value: Boolean, + tpf: Float + ): Unit = { + mappingEnum.withNameOption(binding).foreach { b => + if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) { + sub.onComplete() + c.cancel() + } + } + } + } + + inputManager.addListener(al, entryNames: _*) + + c := Cancelable(() => inputManager.removeListener(al)) + c + } + } + + // def enumObservableAction[T <: enumeratum.EnumEntry]( + // mappingNames: Enum[T] + // ): Observable[ActionEvent] = { + // observableAction2(mappingNames).doOnNext() + // } + def analogObservable(mappingNames: String*): Observable[AnalogEvent] = { - Observable.create(OverflowStrategy.DropOld(100)) { sub => + Observable.create(OverflowStrategy.DropOld(50)) { sub => val c = SingleAssignCancelable() val al = new AnalogListener { override def onAnalog( @@ -392,8 +406,10 @@ package object implicits { ): Unit = { if ( sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop - ) + ) { + sub.onComplete() c.cancel() + } } } @@ -403,6 +419,35 @@ package object implicits { c } } + + def enumAnalogObservable[T <: EnumEntry]( + mappingEnum: Enum[T] + ): Observable[EnumAnalogEvent[T]] = { + + Observable.create(OverflowStrategy.DropOld(50)) { sub => + val c = SingleAssignCancelable() + val entryNames = mappingEnum.values.map(_.entryName) + val al = new AnalogListener { + override def onAnalog( + binding: String, + value: Float, + tpf: Float + ): Unit = { + mappingEnum.withNameOption(binding).foreach { b => + if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) { + sub.onComplete() + c.cancel() + } + } + } + } + + inputManager.addListener(al, entryNames: _*) + + c := Cancelable(() => inputManager.removeListener(al)) + c + } + } } implicit class PhysicsSpaceExt(private val space: PhysicsSpace) @@ -415,8 +460,10 @@ package object implicits { val cl = new PhysicsCollisionListener { override def collision(event: PhysicsCollisionEvent): Unit = { - if (sub.onNext(event) == Ack.Stop) + if (sub.onNext(event) == Ack.Stop) { + sub.onComplete() c.cancel() + } } } @@ -436,14 +483,18 @@ package object implicits { override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = { val event = PhysicsTickEvent(space, tpf) - if (sub.onNext(Left(event)) == Ack.Stop) + if (sub.onNext(Left(event)) == Ack.Stop) { + sub.onComplete() c.cancel() + } } override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = { val event = PhysicsTickEvent(space, tpf) - if (sub.onNext(Right(event)) == Ack.Stop) + if (sub.onNext(Right(event)) == Ack.Stop) { + sub.onComplete() c.cancel() + } } } @@ -515,72 +566,6 @@ package object implicits { def mutable = new Vector3f(v.x, v.y, v.z) } - implicit val implCanMoveForBetterCharacterControl = - new CanMove[BetterCharacterControl] { - override def move( - inst: BetterCharacterControl, - direction: ImVector3f - ): Unit = { - // val dir = direction.mutable - // inst.setViewDirection(dir) - // inst.setViewDirection(direction.mutable) - inst.setWalkDirection(direction.mutable.multLocal(50f)) - } - override def jump(inst: BetterCharacterControl): Unit = inst.jump() - override def rotate( - inst: BetterCharacterControl, - rotateDir: RotateDir - ): Unit = { - val q = - rotateDir match { - case RotateDir.Left => - new Quaternion() - .fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) - case RotateDir.Right => - new Quaternion() - .fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) - } - - val tmp = new Vector3f() - inst.getViewDirection(tmp) - inst.setViewDirection(q.mult(tmp)) - } - override def stop(inst: BetterCharacterControl) = - inst.setWalkDirection(Vector3f.ZERO) - } - - implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging { - override def move(inst: Spatial, direction: ImVector3f): Unit = { - inst.move(direction.mutable) - } - override def jump(inst: Spatial): Unit = - logger.warn("`Jump` is not implemented for type `Spatial`") - override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = { - rotateDir match { - case RotateDir.Left => inst.rotate(0, -0.01f, 0) - case RotateDir.Right => inst.rotate(0, 0.01f, 0) - } - } - override def stop(inst: Spatial) = {} - } - - implicit val implJFXConsoleStreamForTextArea = - new JFXConsoleStreamable[scalafx.scene.control.TextArea] { - - override def println( - ta: scalafx.scene.control.TextArea, - text: String - ): Unit = - ta.appendText(text + "\n") - - override def print( - ta: scalafx.scene.control.TextArea, - text: String - ): Unit = - ta.appendText(text) - - } - // val TasktoUIO = new FunctionK[Task, UIO] { // def apply[T](f: Task[T]): UIO[T] = // f.hideErrors diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala b/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala index 90eddd9..3d8af9e 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala @@ -11,7 +11,7 @@ import akka.event.EventStream * Copied (and repurposed) from Akka's EventStream */ object EventBus { - sealed trait Command[-A] + sealed trait Command[A] final case class Publish[A, E <: A]( event: E, publisherName: String diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala b/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala index 35a479c..fb27dd1 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala @@ -11,7 +11,7 @@ import akka.actor.typed.LogOptions import com.typesafe.scalalogging.{Logger => SLLogger} import wow.doge.mygame.events.EventBus import akka.actor.typed.scaladsl.Behaviors -import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import akka.actor.typed.SupervisorStrategy trait EventsModule { @@ -20,48 +20,11 @@ trait EventsModule { implicit def timeout: Timeout def eventBusLogger = SLLogger[EventBus[_]] -// val subscribingActor = -// spawnProtocol.askT( -// SpawnProtocol.Spawn[Events.PhysicsTick.type]( -// SubscribingActor(), -// "subscriber-1", -// Props.empty, -// _ -// ) -// ) - lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus") - // spawnProtocol.askL( - // SpawnProtocol.Spawn[EventBus.Command[Events.Tick]]( - // Behaviors.logMessages( - // logOptions = LogOptions().withLogger(eventBusLogger.underlying), - // EventBus[Events.Tick]() - // ), - // "tickEventBus", - // Props.empty, - // _ - // ) - // ) - lazy val playerMovementEventBusTask = createEventBus[PlayerMovementEvent]("movementEventBus") - // spawnProtocol.askL( - // SpawnProtocol.Spawn[EventBus.Command[Events.Movement.PlayerMovement]]( - // Behaviors.logMessages( - // logOptions = LogOptions().withLogger(eventBusLogger.underlying), - // EventBus[Events.Movement.PlayerMovement]() - // ), - // "movementEventBus", - // Props.empty, - // _ - // ) - // ) - -// tickEventBus ! EventBus.Subscribe(subscribingActor) - -// tickEventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self) def createEventBus[T](busName: String) = spawnProtocol.askL( SpawnProtocol.Spawn[EventBus.Command[T]]( @@ -80,3 +43,13 @@ trait EventsModule { object EventTypes { type EventBus[T] = ActorRef[EventBus.Command[T]] } + +// val subscribingActor = +// spawnProtocol.askT( +// SpawnProtocol.Spawn[Events.PhysicsTick.type]( +// SubscribingActor(), +// "subscriber-1", +// Props.empty, +// _ +// ) +// ) diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala b/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala index 1396c82..448db21 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala @@ -1,59 +1,64 @@ -// package wow.doge.mygame.subsystems.events - -// import akka.actor.typed.ActorRef -// import akka.actor.typed.SpawnProtocol -// import wow.doge.mygame.implicits._ -// import akka.actor.typed.Props -// import akka.actor.typed.LogOptions -// import com.typesafe.scalalogging.{Logger => SLLogger} -// import wow.doge.mygame.events.EventBus -// import akka.actor.typed.scaladsl.Behaviors -// import wow.doge.mygame.events.Events -// import cats.effect.Resource -// import monix.bio.Task -// import akka.actor.typed.ActorSystem -// import scala.concurrent.duration._ - -// trait EventsModule2 { -// def eventBusesResource( -// spawnProtocol: ActorSystem[SpawnProtocol.Command], -// eventBusLogger: com.typesafe.scalalogging.Logger = SLLogger[EventBus[_]] -// ): Resource[ -// Task, -// ( -// ActorRef[EventBus.Command[Events.Tick]], -// ActorRef[EventBus.Command[Events.Movement.PlayerMovement]] -// ) -// ] = { -// def createEventBus[T](busName: String) = -// spawnProtocol.askL( -// SpawnProtocol.Spawn[EventBus.Command[T]]( -// Behaviors.logMessages( -// logOptions = LogOptions().withLogger(eventBusLogger.underlying), -// EventBus[T]() -// ), -// busName, -// Props.empty, -// _ -// ) - -// )(1.second, spawnProtocol.scheduler) - -// Resource.liftF { -// { -// lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus") - -// lazy val playerMovementEventBusTask = -// createEventBus[Events.Movement.PlayerMovement]("movementEventBus") - -// // val r = (tickEventBusTask, playerMovementEventBusTask) -// // Task(r) -// for { -// tickEventBus <- tickEventBusTask -// playerMovementEventBus <- playerMovementEventBusTask -// } yield (tickEventBus, playerMovementEventBus) -// } -// } -// } - -// } +package wow.doge.mygame.subsystems.events + +import akka.actor.typed.ActorRef +import akka.actor.typed.SpawnProtocol +import wow.doge.mygame.implicits._ +import akka.actor.typed.Props +import akka.actor.typed.LogOptions +import com.typesafe.scalalogging.{Logger => SLLogger} +import wow.doge.mygame.events.EventBus +import akka.actor.typed.scaladsl.Behaviors +import scala.concurrent.duration._ +import akka.util.Timeout +import akka.actor.typed.SupervisorStrategy +import cats.effect.Resource +import akka.actor.typed.ActorSystem +import monix.bio.Task +import org.slf4j.event.Level +import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent + +class EventsModule2( + spawnProtocol: ActorSystem[SpawnProtocol.Command] +) { + private implicit lazy val s = spawnProtocol.scheduler + + private implicit lazy val timeout = Timeout(1.second) + + private lazy val eventBusLogger = SLLogger[EventBus[_]] + + private lazy val playerMovementEventBusTask = + createEventBus[PlayerMovementEvent]("movementEventBus") + + private lazy val playerCameraEventBusTask = + createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG) + + def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) = + spawnProtocol.askL( + SpawnProtocol.Spawn[EventBus.Command[T]]( + Behaviors.logMessages( + logOptions = LogOptions() + .withLevel(logLevel) + .withLogger(eventBusLogger.underlying), + Behaviors + .supervise(EventBus[T]()) + .onFailure[Exception](SupervisorStrategy.restart) + ), + busName, + Props.empty, + _ + ) + ) + + type EventBuses = ( + ActorRef[ + EventBus.Command[EntityMovementEvent.PlayerMovementEvent], + ], + ActorRef[EventBus.Command[PlayerCameraEvent]] + ) + + val resource: Resource[Task, EventBuses] = + Resource.liftF(for { + playerMovementEventBus <- playerMovementEventBusTask + playerCameraEventBus <- playerCameraEventBusTask + } yield (playerMovementEventBus, playerCameraEventBus)) +} diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala b/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala index e4f39d3..871c4f6 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala @@ -1,20 +1,20 @@ package wow.doge.mygame.subsystems.events -import wow.doge.mygame.subsystems.movement.CanMove +import wow.doge.mygame.game.subsystems.movement.CanMove -sealed trait MovementEvent +sealed trait EntityMovementEvent -object MovementEvent { +object EntityMovementEvent { final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T) - extends MovementEvent + extends EntityMovementEvent final case class MovedUp[T: CanMove](pressed: Boolean, movable: T) - extends MovementEvent + extends EntityMovementEvent final case class MovedRight[T: CanMove](pressed: Boolean, movable: T) - extends MovementEvent + extends EntityMovementEvent final case class MovedDown[T: CanMove](pressed: Boolean, movable: T) - extends MovementEvent + extends EntityMovementEvent - sealed trait PlayerMovementEvent extends MovementEvent + sealed trait PlayerMovementEvent extends EntityMovementEvent object PlayerMovementEvent { final case class PlayerMovedLeft(pressed: Boolean) extends PlayerMovementEvent diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala new file mode 100644 index 0000000..f874ca1 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala @@ -0,0 +1,8 @@ +package wow.doge.mygame.subsystems.events + +sealed trait PlayerCameraEvent + +object PlayerCameraEvent { + final case object CameraMovedUp extends PlayerCameraEvent + final case object CameraMovedDown extends PlayerCameraEvent +} diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala new file mode 100644 index 0000000..b574f4d --- /dev/null +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala @@ -0,0 +1,23 @@ +package wow.doge.mygame.subsystems.scriptsystem + +import wow.doge.mygame.utils.AkkaUtils +import cats.effect.Resource +import wow.doge.mygame.scriptsystem.ScriptCachingActor +import akka.actor.typed.ActorRef +import akka.actor.typed.SpawnProtocol +import akka.util.Timeout +import akka.actor.typed.Scheduler + +class ScriptSystemResource( + path: os.Path, + spawnProtocol: ActorRef[SpawnProtocol.Command] +)(implicit timeout: Timeout, scheduler: Scheduler) { + def make = + Resource.liftF( + AkkaUtils.spawnActorL( + spawnProtocol, + "scriptCachingActor", + ScriptCachingActor() + ) + ) +} diff --git a/src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala b/src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala index 8980757..c4c21eb 100644 --- a/src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala +++ b/src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala @@ -4,7 +4,6 @@ import java.io.PrintStream import java.io.OutputStream import java.io.ByteArrayOutputStream import scalafx.scene.control.TextArea -import wow.doge.mygame.implicits._ import cats.effect.Resource import monix.bio.Task @@ -41,6 +40,23 @@ object JFXConsoleStream { lazy val default = Config() } + implicit val implJFXConsoleStreamForTextArea = + new JFXConsoleStreamable[scalafx.scene.control.TextArea] { + + override def println( + ta: scalafx.scene.control.TextArea, + text: String + ): Unit = + ta.appendText(text + "\n") + + override def print( + ta: scalafx.scene.control.TextArea, + text: String + ): Unit = + ta.appendText(text) + + } + def textAreaStream(ta: TextArea) = Resource.make( Task( @@ -50,4 +66,5 @@ object JFXConsoleStream { ) ) )(s => Task(s.close())) + }