From 3881aac350d0e19be0b95445669a348d4859c226 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Sun, 10 Jan 2021 11:41:39 +0530 Subject: [PATCH] Commit 1 --- .attach_pid12833 | 0 .attach_pid19972 | 0 build.sbt | 7 +- .../org/slf4j/impl/StaticLoggerBuilder.scala | 21 ++- src/main/scala/wow/doge/mygame/Main.scala | 30 ++-- src/main/scala/wow/doge/mygame/MainApp.scala | 167 ++++++++---------- .../scala/wow/doge/mygame/MainModule.scala | 11 +- .../scala/wow/doge/mygame/game/GameApp.scala | 61 ++++++- .../wow/doge/mygame/game/GameAppActor.scala | 5 +- .../wow/doge/mygame/game/GameModule.scala | 58 +++--- .../wow/doge/mygame/game/SimpleAppExt.scala | 30 ++-- .../wow/doge/mygame/game/TestActor.scala | 14 +- .../game/appstates/PlayerMovementState.scala | 10 +- .../game/entities/NpcActorSupervisor.scala | 30 ++-- .../player/PlayerActorSupervisor.scala | 2 +- .../entities/player/PlayerController.scala | 55 +++--- .../subsystems/input/GameInputHandler.scala | 58 +++--- .../subsystems/level/DefaultGameLevel.scala | 6 +- .../game/subsystems/movement/CanMove2.scala | 30 +++- .../subsystems/movement/MovementActor.scala | 7 +- .../implicits/JavaFXMonixObservables.scala | 32 ++++ .../wow/doge/mygame/implicits/package.scala | 64 +++++++ .../wow/doge/mygame/launcher/Launcher.scala | 118 +++++++++---- .../subsystems/events/EventsModule.scala | 14 +- .../subsystems/scriptsystem/ScriptActor.scala | 6 +- .../scriptsystem/ScriptCachingActor.scala | 12 +- .../scriptsystem/ScriptSystemModule.scala | 26 +-- .../wow/doge/mygame/utils/AkkaUtils.scala | 4 +- .../mygame/utils/GenericConsoleStream.scala | 4 +- 29 files changed, 544 insertions(+), 338 deletions(-) create mode 100644 .attach_pid12833 create mode 100644 .attach_pid19972 diff --git a/.attach_pid12833 b/.attach_pid12833 new file mode 100644 index 0000000..e69de29 diff --git a/.attach_pid19972 b/.attach_pid19972 new file mode 100644 index 0000000..e69de29 diff --git a/build.sbt b/build.sbt index 3159136..cd13e29 100644 --- a/build.sbt +++ b/build.sbt @@ -95,7 +95,8 @@ lazy val root = (project in file(".")).settings( // "com.github.Oshan96" % "CustomStage" % "v1.3.1", "com.badlogicgames.gdx" % "gdx-ai" % "1.8.2", "org.recast4j" % "recast" % "1.2.5", - "org.recast4j" % "detour" % "1.2.5" + "org.recast4j" % "detour" % "1.2.5", + "com.lihaoyi" %% "pprint" % "0.6.0" ), // Determine OS version of JavaFX binaries @@ -155,10 +156,6 @@ lazy val root = (project in file(".")).settings( // val oldStrategy = (assemblyMergeStrategy in assembly).value // oldStrategy(x) } - // scalaVersion := "2.13.2", // 2.11.12, or 2.13.3 - // semanticdbEnabled := true, // enable SemanticDB - // semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version - // semanticdbVersion := "4.3.24", ) initialCommands in (console) := """ammonite.Main.main(Array.empty)""" diff --git a/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala b/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala index 24e33bb..d09fb49 100644 --- a/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala +++ b/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala @@ -47,6 +47,10 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] { .allocated .unsafeRunSync() + val mainFileLogger2 = mainFileLogger.contramap(lm => + lm.copy(message = lm.message.map(s => fansi.Str(s).plainText)) + ) + private lazy val (eventBusFileLogger, release3) = fileLogger[IO]( "eventbus.log", @@ -72,15 +76,22 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] { case s if s.startsWith("com.jayfella.jme.jfx.util.JfxPlatform") => defaultConsoleLogger.withMinimalLevel(Level.Info) - // case s - // if s.startsWith( - // "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler" - // ) => + case s + if s.startsWith( + "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler" + ) => + defaultConsoleLogger.withMinimalLevel(Level.Info) + case s + if s.startsWith( + "wow.doge.mygame.game.entities.NpcMovementActor" + ) => + defaultConsoleLogger.withMinimalLevel(Level.Trace) |+| mainFileLogger2 + .withMinimalLevel(Level.Trace) // defaultConsoleLogger.withMinimalLevel( Level.Trace) //selectively turn on trace logging for specific classes case s if s.startsWith("wow.doge.mygame.subsystems.events.EventBus") => defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| eventBusFileLogger case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") => - defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger + defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger2 case _ => //if wildcard case isn't provided, default logger is no-op defaultConsoleLogger.withMinimalLevel(Level.Debug) } diff --git a/src/main/scala/wow/doge/mygame/Main.scala b/src/main/scala/wow/doge/mygame/Main.scala index 6e5177f..7e8c5df 100644 --- a/src/main/scala/wow/doge/mygame/Main.scala +++ b/src/main/scala/wow/doge/mygame/Main.scala @@ -5,16 +5,16 @@ import scala.concurrent.duration._ import _root_.monix.bio.BIOApp import _root_.monix.bio.Task import _root_.monix.bio.UIO +import akka.actor.typed.ActorSystem +import akka.actor.typed.SpawnProtocol import akka.util.Timeout import cats.effect.ExitCode import cats.effect.Resource import cats.implicits._ -import com.softwaremill.macwire._ import io.odin._ import io.odin.json.Formatter import io.odin.syntax._ import scalafx.scene.control.TextArea -import wow.doge.mygame.game.GameAppResource import wow.doge.mygame.utils.GenericConsoleStream object Main extends BIOApp with MainModule { @@ -34,25 +34,23 @@ object Main extends BIOApp with MainModule { Formatter.json ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) jmeScheduler <- jMESchedulerResource - actorSystem <- actorSystemResource(logger) - gameApp <- { - // new BulletAppState() - // bas.setThreadingType(Thr) - // gameAppResource(new StatsAppState()) - wire[GameAppResource].get - } + implicit0(actorSystem: ActorSystem[SpawnProtocol.Command]) <- + actorSystemResource(logger) + // gameApp <- { + // // new BulletAppState() + // // bas.setThreadingType(Thr) + // // gameAppResource(new StatsAppState()) + // wire[GameAppResource].get + // } _ <- Resource.liftF( new MainApp( logger, - gameApp, - actorSystem, + // gameApp, + // actorSystem, jmeScheduler, schedulers, consoleStream - )( - timeout, - actorSystem.scheduler - ).program + )(actorSystem, timeout, actorSystem.scheduler).program ) } yield () @@ -63,7 +61,7 @@ object Main extends BIOApp with MainModule { Console.withOut(consoleStream)( appResource(consoleStream) .use(_ => Task.unit >> Task(consoleStream.close())) - .onErrorHandle(_.printStackTrace()) + .onErrorHandleWith(ex => UIO(ex.printStackTrace())) .as(ExitCode.Success) ) } diff --git a/src/main/scala/wow/doge/mygame/MainApp.scala b/src/main/scala/wow/doge/mygame/MainApp.scala index 91f9dcd..eb153a7 100644 --- a/src/main/scala/wow/doge/mygame/MainApp.scala +++ b/src/main/scala/wow/doge/mygame/MainApp.scala @@ -4,6 +4,7 @@ import akka.actor.typed.ActorSystem import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout +import cats.effect.Resource import cats.effect.concurrent.Deferred import com.jme3.app.state.AppStateManager import com.jme3.asset.AssetManager @@ -46,98 +47,80 @@ import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.GenericConsoleStream -import wow.doge.mygame.utils.IOUtils +import cats.syntax.eq._ -import EventsModule.GameEventBus +import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus class MainApp( logger: Logger[Task], - gameApp: GameApp, - implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command], jmeThread: monix.execution.Scheduler, schedulers: Schedulers, consoleStream: GenericConsoleStream[TextArea] )(implicit + spawnProtocol: ActorSystem[SpawnProtocol.Command], @annotation.unused timeout: Timeout, @annotation.unused scheduler: Scheduler ) { - lazy val scriptSystemInit = - new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init + val scriptSystemInit = + new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init - def gameInit: Task[Fiber[Throwable, Unit]] = - for { - eventsModule <- Task(new EventsModule(spawnProtocol)) - playerEventBus <- eventsModule.playerEventBusTask - mainEventBus <- eventsModule.mainEventBusTask - tickEventBus <- eventsModule.tickEventBusTask - gameAppActor <- AkkaUtils.spawnActorL2( - GameAppActor.Props(tickEventBus).behavior, - "gameAppActor" - ) - _ <- gameAppActor !! GameAppActor.Start - gameAppFib <- gameApp.start.executeOn(jmeThread).start - /** - * schedule a task to run on the JME thread and wait for it's completion - * before proceeding forward, as a signal that the JME thread has been - * initialized, otherwise we'll get NPEs trying to access the fields - * of the game app - */ - res <- gameApp.enqueueL(() => "done") - _ <- logger.info(s"Result = $res") - /** - * JME Thread has been initialized at this point. We can now access the - * field of the game application - */ - inputManager <- gameApp.inputManager - assetManager <- gameApp.assetManager - stateManager <- gameApp.stateManager - camera <- gameApp.camera - rootNode <- gameApp.rootNode - enqueueR <- Task(gameApp.enqueue _) - viewPort <- gameApp.viewPort - _ <- logger.info("before") - // jfxUI <- gameApp.jfxUI - consoleTextArea <- Task(new TextArea { - text = "hello \n" - editable = false - wrapText = true - // maxHeight = 150 - // maxWidth = 300 - }) - // _ <- Task(consoleStream := consoleTextArea) - // _ <- Task(jfxUI += consoleTextArea) - _ <- logger.info("after") - bulletAppState <- Task(new BulletAppState()) - _ <- Task(stateManager.attach(bulletAppState)) - _ <- logger.info("Initializing console stream") - _ <- wire[MainAppDelegate].init(gameApp.scheduler) - } yield (gameAppFib) + val eventsModule = new EventsModule(spawnProtocol) - lazy val program = for { + def gameInit: Resource[Task, Fiber[Throwable, Unit]] = + GameApp.resource(logger, jmeThread, schedulers).evalMap { + case gameApp -> gameAppFib => + for { + playerEventBus <- eventsModule.playerEventBusTask + mainEventBus <- eventsModule.mainEventBusTask + tickEventBus <- eventsModule.tickEventBusTask + gameAppActor <- AkkaUtils.spawnActorL( + GameAppActor.Props(tickEventBus).behavior, + "gameAppActor" + ) + _ <- gameAppActor !! GameAppActor.Start + inputManager <- gameApp.inputManager + assetManager <- gameApp.assetManager + stateManager <- gameApp.stateManager + camera <- gameApp.camera + rootNode <- gameApp.rootNode + enqueueR <- Task(gameApp.enqueue _) + viewPort <- gameApp.viewPort + _ <- logger.info("before") + // jfxUI <- gameApp.jfxUI + consoleTextArea <- Task(new TextArea { + text = "hello \n" + editable = false + wrapText = true + // maxHeight = 150 + // maxWidth = 300 + }) + // _ <- Task(consoleStream := consoleTextArea) + // _ <- Task(jfxUI += consoleTextArea) + _ <- logger.info("after") + bulletAppState <- Task(new BulletAppState()) + _ <- Task(stateManager.attach(bulletAppState)) + _ <- logger.info("Initializing console stream") + _ <- wire[MainAppDelegate].init(gameApp.scheduler) + } yield gameAppFib + } + + val program = for { scriptSystem <- scriptSystemInit - /** - * Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI - * Without this, we get a "Toolkit already initialized" exception. The launch button - * in the launcher completes the signal. The game init process which listens for this - * signal can then continue - */ launchSignal <- Deferred[Task, Launcher.LauncherResult] launcher <- new Launcher.Props(schedulers, launchSignal).create - cancelToken <- launcher.init() - launchResult <- launchSignal.get - _ <- cancelToken.cancel + launchResult <- launcher.init.use(_ => launchSignal.get) _ <- /** * User chose to quit */ - if (launchResult == LauncherResult.Exit) + if (launchResult === LauncherResult.Exit) logger.info("Exiting") /** * User chose launch. Wait for game window to close */ else - gameInit.flatMap(_.join) + gameInit.use(_.join) } yield () } @@ -146,7 +129,6 @@ class MainApp( */ class MainAppDelegate( gameApp: GameApp, - implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command], loggerL: Logger[Task], playerEventBus: GameEventBus[PlayerEvent], tickEventBus: GameEventBus[TickEvent], @@ -159,10 +141,11 @@ class MainAppDelegate( rootNode: Node @@ GameAppTags.RootNode, bulletAppState: BulletAppState )(implicit + spawnProtocol: ActorSystem[SpawnProtocol.Command], @annotation.unused timeout: Timeout, @annotation.unused scheduler: Scheduler ) { - lazy val physicsSpace = bulletAppState.physicsSpace + val physicsSpace = bulletAppState.physicsSpace def init( appScheduler: monix.execution.Scheduler // consoleStream: GenericConsoleStream[TextArea] @@ -190,19 +173,21 @@ class MainAppDelegate( // johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler) // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) - // _ <- (johnActor !! NpcActorSupervisor.Move( - // ImVector3f(-80, 0, 100) - // )).executeAsync.delayExecution(2.seconds) - _ <- - IOUtils - .toIO( - rootNode - .observableBreadthFirst() - .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) - .completedL - ) - .executeOn(appScheduler) - .startAndForget + // _ <- + // (johnActor !! NpcActorSupervisor.Move( + // ImVector3f(-30, 0, 10) + // )).executeAsync + // .delayExecution(2.seconds) + // _ <- + // IOUtils + // .toIO( + // rootNode + // .observableBreadthFirst() + // .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) + // .completedL + // ) + // .executeOn(appScheduler) + // .startAndForget } yield () def createPlayerController( @@ -210,14 +195,14 @@ class MainAppDelegate( ): IO[PlayerController.Error, Unit] = { val playerPos = ImVector3f.ZERO val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" - lazy val playerPhysicsControl = + val playerPhysicsControl = PlayerController.Defaults.defaultPlayerPhysicsControl .taggedWith[PlayerControllerTags.PlayerTag] // lazy val camNode = // PlayerController.Defaults // .defaultCamerNode(camera, playerPos) // .taggedWith[PlayerControllerTags.PlayerCameraNode] - lazy val mbPlayerNode = PlayerController.Defaults + val mbPlayerNode = PlayerController.Defaults .defaultPlayerNode( assetManager, modelPath, @@ -225,7 +210,7 @@ class MainAppDelegate( // camNode playerPhysicsControl ) - lazy val cameraPivotNode = new Node(EntityIds.CameraPivot.value) + val cameraPivotNode = new Node(EntityIds.CameraPivot.value) .taggedWith[PlayerControllerTags.PlayerCameraPivotNode] for { @@ -258,11 +243,11 @@ class MainAppDelegate( ) = // : IO[PlayerController.Error, Unit] = { - val initialPos = ImVector3f(100, 0, 0) + val initialPos = ImVector3f(50, 5, 0) // val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" - lazy val npcPhysicsControl = - new BetterCharacterControl(1f, 2.1f, 10f) - // .withJumpForce(ImVector3f(0, 5f, 0)) + val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) + // (1f, 2.1f, 10f) + .withJumpForce(ImVector3f(0, 5f, 0)) // val npcMovementActor = AkkaUtils.spawnActorL2( // new NpcMovementActor2.Props( // initialPos, @@ -271,13 +256,13 @@ class MainAppDelegate( // ).behavior, // s"${npcName}-npcMovementActor" // ) - lazy val mbNpcNode = PlayerController.Defaults.defaultNpcNode( + val mbNpcNode = PlayerController.Defaults.defaultNpcNode( assetManager, initialPos, npcPhysicsControl, npcName ) - val npcActorTask = AkkaUtils.spawnActorL2( + val npcActorTask = AkkaUtils.spawnActorL( NpcActorSupervisor .Props( new NpcMovementActor.Props( @@ -302,7 +287,7 @@ class MainAppDelegate( physicsSpace += npcNode rootNode += npcNode } - } yield (npcActor) + } yield npcActor } } diff --git a/src/main/scala/wow/doge/mygame/MainModule.scala b/src/main/scala/wow/doge/mygame/MainModule.scala index e2ad87a..431cbbb 100644 --- a/src/main/scala/wow/doge/mygame/MainModule.scala +++ b/src/main/scala/wow/doge/mygame/MainModule.scala @@ -11,15 +11,14 @@ trait MainModule extends ExecutorsModule { def actorSystemResource( logger: Logger[Task] ): Resource[Task, ActorSystem[SpawnProtocol.Command]] = - Resource.make(logger.info("Creating Actor System") >> Task { - ActorSystem( - SpawnProtocol(), - name = "GameActorSystem" + Resource.make( + logger.info("Creating Actor System") >> Task( + ActorSystem(SpawnProtocol(), name = "GameActorSystem") ) - })(sys => + )(sys => for { _ <- Task(sys.terminate()) - _ <- Task.fromFuture(sys.whenTerminated) + _ <- Task.deferFuture(sys.whenTerminated) _ <- logger.info("Actor System Terminated") } yield () ) diff --git a/src/main/scala/wow/doge/mygame/game/GameApp.scala b/src/main/scala/wow/doge/mygame/game/GameApp.scala index 188784d..eb2b7fc 100644 --- a/src/main/scala/wow/doge/mygame/game/GameApp.scala +++ b/src/main/scala/wow/doge/mygame/game/GameApp.scala @@ -1,11 +1,13 @@ package wow.doge.mygame.game +import cats.effect.Resource import cats.effect.concurrent.Deferred import com.jme3.app.state.AppStateManager import com.jme3.asset.AssetManager import com.jme3.input.InputManager import com.jme3.scene.Node import com.jme3.scene.Spatial +import com.jme3.system.AppSettings import com.softwaremill.tagging._ import com.typesafe.scalalogging.{Logger => SLogger} import io.odin.Logger @@ -15,7 +17,13 @@ import monix.catnap.ConcurrentChannel import monix.catnap.ConsumerF import monix.catnap.Semaphore import monix.eval.Coeval +import monix.execution.CancelablePromise +import monix.execution.Scheduler +import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.game.subsystems.ui.JFxUI +import wow.doge.mygame.implicits._ +import monix.execution.annotations.UnsafeBecauseImpure +import monix.reactive.Observable sealed trait Error case object FlyCamNotExists extends Error @@ -40,6 +48,8 @@ class GameApp(logger: Logger[Task], val app: SimpleAppExt) { def camera = Task(app.getCamera()) def viewPort = Task(app.getViewPort()) def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode]) + def rootNode2 = + WrappedNode(app.getRootNode()).taggedWith[GameAppTags.RootNode] // def rootNode2 = SynchedObject(app.getRootNode()) def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn))) def enqueue(cb: () => Unit) = @@ -55,12 +65,57 @@ class GameApp(logger: Logger[Task], val app: SimpleAppExt) { } -object GameApp { +class WrappedNode private (node: Node) { + + // def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat))) + def children: Observable[Spatial] = node.observableChildren + def attachChild(n: Node): Task[Unit] = Task(node.attachChild(node)) + def add(wn: WrappedNode): Task[Unit] = + Task(node.attachChild(wn.unsafeDelegate)) - class WrappedNode(node: Node, lock: Semaphore[Task]) { + /** + * Get the underlying wrapped value + */ + @UnsafeBecauseImpure + def unsafeDelegate = node +} +object WrappedNode { - def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat))) + def apply(name: String) = new WrappedNode(new Node(name)) + def apply(n: Node) = new WrappedNode(n) + implicit class WrappedNodeOps(private val wn: WrappedNode) extends AnyVal { + def +=(n: Node) = wn.attachChild(n) + def +=(wn: WrappedNode) = wn.add(wn) } +} + +object GameApp { + + def resource( + logger: Logger[Task], + jmeScheduler: Scheduler, + schedulers: Schedulers + ) = + Resource.make( + for { + startSignal <- Task(CancelablePromise[Unit]()) + app <- Task(new SimpleAppExt(schedulers, startSignal)) + _ <- Task { + val settings = new AppSettings(true) + settings.setVSync(true) + + /** + * disables the launcher + * We'll be making our own launcher anyway + */ + app.setShowSettings(false) + app.setSettings(settings) + } + gameApp <- Task(new GameApp(logger, app)) + fib <- gameApp.start.executeOn(jmeScheduler).start + _ <- Task.fromCancelablePromise(startSignal) + } yield gameApp -> fib + )(_._2.cancel) /** * Synchronization wrapper for a mutable object diff --git a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala index ec2d537..ed170ce 100644 --- a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala +++ b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala @@ -6,6 +6,7 @@ import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.Behaviors import wow.doge.mygame.game.TickGenerator.Send import wow.doge.mygame.game.entities.GenericTimerActor +import wow.doge.mygame.implicits._ import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.TickEvent @@ -28,7 +29,7 @@ object GameAppActor { ) { def behavior = Behaviors.setup[Command] { ctx => - ctx.log.info("Hello from GameAppActor") + ctx.log.infoP("Hello from GameAppActor") val renderTickGenerator = ctx.spawn( Behaviors @@ -78,7 +79,7 @@ object TickGenerator { object SubscribingActor { def apply() = Behaviors.receive[PhysicsTick.type] { (ctx, msg) => - ctx.log.debug(s"received event $msg") + ctx.log.debugP(s"received event $msg") Behaviors.same } } diff --git a/src/main/scala/wow/doge/mygame/game/GameModule.scala b/src/main/scala/wow/doge/mygame/game/GameModule.scala index d29fae7..8f717a8 100644 --- a/src/main/scala/wow/doge/mygame/game/GameModule.scala +++ b/src/main/scala/wow/doge/mygame/game/GameModule.scala @@ -1,36 +1,30 @@ package wow.doge.mygame.game -import cats.effect.Resource -import com.jme3.app.StatsAppState -import com.jme3.system.AppSettings -import io.odin.Logger -import monix.bio.Task -import monix.execution.Scheduler -import wow.doge.mygame.executors.Schedulers -class GameAppResource( - logger: Logger[Task], - jmeScheduler: Scheduler, - schedulers: Schedulers -) { - def get: Resource[Task, GameApp] = - Resource.make( - for { - _ <- logger.info("Creating game app") - appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState())) - app <- Task { - val settings = new AppSettings(true) - settings.setVSync(true) +// class GameAppResource( +// logger: Logger[Task], +// jmeScheduler: Scheduler, +// schedulers: Schedulers +// ) { - /** - * disables the launcher - * We'll be making our own launcher anyway - */ - appExt.setShowSettings(false) - appExt.setSettings(settings) - // JMERunner.runner = app - new GameApp(logger, appExt) - } - } yield (app) - )(_ => logger.info("Closing game app")) -} +// def get: Resource[Task, GameApp] = +// Resource.make( +// for { +// _ <- logger.info("Creating game app") +// appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState())) +// app <- Task { +// val settings = new AppSettings(true) +// settings.setVSync(true) + +// /** +// * disables the launcher +// * We'll be making our own launcher anyway +// */ +// appExt.setShowSettings(false) +// appExt.setSettings(settings) +// // JMERunner.runner = app +// new GameApp(logger, appExt) +// } +// } yield (app) +// )(_ => logger.info("Closing game app")) +// } diff --git a/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala b/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala index 7a0e85d..e006ee0 100644 --- a/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala +++ b/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala @@ -6,17 +6,15 @@ import com.jme3.app.SimpleApplication import com.jme3.app.state.AppState import monix.bio.Task import monix.execution.CancelableFuture +import monix.execution.CancelablePromise import monix.execution.Scheduler import monix.execution.atomic.Atomic -import monix.execution.{CancelablePromise => Promise} -import monix.reactive.MulticastStrategy -import monix.reactive.Observable -import monix.reactive.subjects.ConcurrentSubject import wow.doge.mygame.executors.GUIExecutorService import wow.doge.mygame.executors.Schedulers class SimpleAppExt( schedulers: Schedulers, + startSignal: CancelablePromise[Unit], appStates: AppState* ) extends SimpleApplication(appStates: _*) { import SimpleAppExt._ @@ -26,32 +24,26 @@ class SimpleAppExt( */ private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]]) - private val tickSubject = - ConcurrentSubject[Float](multicast = MulticastStrategy.publish)( - schedulers.async - ) + // def tickObservable: Observable[Float] = tickSubject - def tickObservable: Observable[Float] = tickSubject - - override def simpleInitApp(): Unit = {} - - override def simpleUpdate(tpf: Float): Unit = { - tickSubject.onNext(tpf) + override def simpleInitApp(): Unit = { + startSignal.success(()) } + override def simpleUpdate(tpf: Float): Unit = {} + override def stop(): Unit = { - tickSubject.onComplete() super.stop() } - def enqueueScala[T](cb: () => T): CancelableFuture[T] = { - val p = Promise[T]() + def enqueueFuture[T](cb: () => T): CancelableFuture[T] = { + val p = CancelablePromise[T]() taskQueue2.transform(_ :+ MyTask(p, cb)) p.future } def enqueueL[T](cb: () => T): Task[T] = - Task.deferFuture(enqueueScala(cb)) + Task.deferFuture(enqueueFuture(cb)) override protected def runQueuedTasks(): Unit = { taskQueue2.transform { current => @@ -73,7 +65,7 @@ class SimpleAppExt( lazy val scheduler = Scheduler(JMEExecutorService) } object SimpleAppExt { - private[game] case class MyTask[T](p: Promise[T], cb: () => T) + private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T) } // val ship = ed.createEntity() diff --git a/src/main/scala/wow/doge/mygame/game/TestActor.scala b/src/main/scala/wow/doge/mygame/game/TestActor.scala index 118c438..70f6208 100644 --- a/src/main/scala/wow/doge/mygame/game/TestActor.scala +++ b/src/main/scala/wow/doge/mygame/game/TestActor.scala @@ -36,11 +36,11 @@ object TestActor { // ) // ) { // case Success(value) => - // ctx.log.debug("Received Value") - // ctx.log.debug(value.toString()) + // ctx.log.debugP("Received Value") + // ctx.log.debugP(value.toString()) // Done // case Failure(exception) => - // ctx.log.debug(s"Received Error ${exception.getMessage()}") + // ctx.log.debugP(s"Received Error ${exception.getMessage()}") // Done // } } @@ -60,24 +60,24 @@ object TestActor { // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) // ) { // case Success(value) => { - // ctx.log.debug(value.toString()) + // ctx.log.debugP(value.toString()) // ctx.ask( // scriptStorer, // ScriptStoringActor // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) // ) { // case Success(value) => { - // ctx.log.debug(value.toString()) + // ctx.log.debugP(value.toString()) // Done // } // case Failure(exception) => - // ctx.log.debug(exception.getMessage()) + // ctx.log.debugP(exception.getMessage()) // Done // } // Done // } // case Failure(exception) => - // ctx.log.debug(exception.getMessage()) + // ctx.log.debugP(exception.getMessage()) // Done // } } 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 ead9db2..8a68fdb 100644 --- a/src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala +++ b/src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala @@ -193,23 +193,23 @@ class MovementActor( // val walkDir = new Vector3f val dir = state.cardinalDir if (dir.up) { - ctx.log.debug("up") - // ctx.log.debug(Thread.currentThread().getName()) + ctx.log.debugP("up") + // ctx.log.debugP(Thread.currentThread().getName()) // walkDir.addLocal(0, 0, -1) walkDir += camDir } if (dir.left) { - ctx.log.debug("left") + ctx.log.debugP("left") // walkDir.addLocal(-1, 0, 0) walkDir.addLocal(camLeft) } if (dir.right) { - ctx.log.debug("right") + ctx.log.debugP("right") // walkDir.addLocal(1, 0, 0) walkDir.addLocal(camLeft.negateLocal()) } if (dir.down) { - ctx.log.debug("down") + ctx.log.debugP("down") walkDir.addLocal(camDir.negateLocal()) // walkDir.addLocal(0, 0, 1) } diff --git a/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala index 88e87b2..f84064c 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala @@ -39,6 +39,8 @@ object NpcActorSupervisor { private case class LogError(err: Throwable) extends Command private case object NoOp extends Command + private case class MovementFailed(err: Throwable) extends Command + final case class Props( npcMovementActorBehavior: Behavior[NpcMovementActor.Command], npcName: String, @@ -82,7 +84,7 @@ class NpcActorSupervisor( def idle(state: State): Behavior[NpcActorSupervisor.Command] = Behaviors.setup { _ => - ctx.log.debug("Inside Idle State") + ctx.log.debugP("Inside Idle State") Behaviors.receiveMessage[Command] { case m @ Move(pos) => ctx.ask( @@ -97,7 +99,7 @@ class NpcActorSupervisor( moving(state, move.pos, signal) case LogError(err) => - ctx.log.warn(err.getMessage()) + ctx.log.warnP(err.getMessage()) Behaviors.same case _ => Behaviors.unhandled } @@ -117,12 +119,16 @@ class NpcActorSupervisor( // ) ctx.pipeToSelf(signal) { case Success(value) => DoneMoving - case Failure(exception) => LogError(exception) + case Failure(exception) => MovementFailed(exception) } Behaviors.receiveMessagePartial[Command] { case LogError(err) => ctx.log.error(err.getMessage()) Behaviors.same + case MovementFailed(err) => + ctx.self ! LogError(err) + movementTimer ! GenericTimerActor.Stop + idle(state) case m @ Move(pos) => movementTimer ! GenericTimerActor.Stop children.npcMovementActor ! NpcMovementActor.StopMoving @@ -132,7 +138,7 @@ class NpcActorSupervisor( NpcMovementActor.MoveTo(pos, _) ) { case Success(signal) => InternalMove(m, signal) - case Failure(exception) => LogError(exception) + case Failure(exception) => MovementFailed(exception) } Behaviors.same case InternalMove(move, signal) => @@ -190,12 +196,6 @@ class NpcMovementActor[T]( case AskPosition(replyTo) => replyTo ! location Behaviors.same - case StopMoving => - ctx.log.debug( - "Position at Stop = " + location.toString - ) - props.enqueueR(() => cm.stop(props.movable)) - receive(state) case MoveTo( target: ImVector3f, replyTo: ActorRef[CancelableFuture[DoneMoving.type]] @@ -204,7 +204,6 @@ class NpcMovementActor[T]( val p = CancelablePromise[DoneMoving.type]() replyTo ! p.future ticking(p, target, state) - } def ticking( @@ -214,7 +213,7 @@ class NpcMovementActor[T]( ): Behavior[NpcMovementActor.Command] = Behaviors.receiveMessagePartial { case StopMoving => - ctx.log.debug( + ctx.log.debugP( "Position at Stop = " + location.toString ) props.enqueueR(() => cm.stop(props.movable)) @@ -225,12 +224,11 @@ class NpcMovementActor[T]( if (dst <= 10f) { ctx.self ! StopMoving reachDestination.success(DoneMoving) - receive(state) } else { - ctx.log.trace("Difference = " + dst.toString()) - ctx.log.trace("Current pos = " + location.toString()) - Behaviors.same + ctx.log.traceP("Difference = " + dst.toString()) + ctx.log.traceP("Current pos = " + location.toString()) } + Behaviors.same } } diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala index 58851dc..491f4cb 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala @@ -39,7 +39,7 @@ object PlayerActorSupervisor { ctx.log.info("Hello from PlayerActor") // spawn children actors - lazy val movementActor = + val movementActor = ctx.spawn( Behaviors .supervise(imMovementActorBehavior) diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala index 0547a3e..908a3c4 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala @@ -29,6 +29,7 @@ import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.utils.AkkaUtils +import com.softwaremill.macwire._ object PlayerControllerTags { sealed trait PlayerTag @@ -47,7 +48,6 @@ object PlayerController { loggerL: Logger[Task], physicsSpace: PhysicsSpace, initialPlayerPos: ImVector3f = ImVector3f.ZERO, - spawnProtocol: ActorRef[SpawnProtocol.Command], playerEventBus: GameEventBus[PlayerEvent], playerPhysicsControl: BetterCharacterControl, appScheduler: monix.execution.Scheduler, @@ -56,29 +56,34 @@ object PlayerController { cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode, tickEventBus: GameEventBus[TickEvent], camera: Camera - )(implicit timeout: Timeout, scheduler: Scheduler) { + )(implicit + spawnProtocol: ActorRef[SpawnProtocol.Command], + timeout: Timeout, + scheduler: Scheduler + ) { + val playerActorBehavior = { + val movementActorBeh = new ImMovementActor.Props( + enqueueR, + playerPhysicsControl, + camera + ).behavior + val cameraActorBeh = new PlayerCameraActor.Props( + cameraPivotNode, + enqueueR, + playerNode.getWorldTranslation _ + ).behavior + new PlayerActorSupervisor.Props( + playerEventBus, + tickEventBus, + movementActorBeh, + cameraActorBeh + ).behavior(playerPhysicsControl) + } val create: IO[Error, Unit] = (for { playerActor <- AkkaUtils.spawnActorL( - spawnProtocol, - "playerActorSupervisor", - new PlayerActorSupervisor.Props( - playerEventBus, - // playerCameraEventBus, - tickEventBus, - new ImMovementActor.Props( - enqueueR, - playerPhysicsControl, - camera - ).behavior, - // wireWith(PlayerCameraEventListener.apply _) - // PlayerCameraEventListener() - new PlayerCameraActor.Props( - cameraPivotNode, - enqueueR, - playerNode.getWorldTranslation _ - ).behavior - ).behavior(playerPhysicsControl) + playerActorBehavior, + "playerActorSupervisor" ) _ <- Task(rootNode += playerNode) _ <- IO { @@ -88,9 +93,7 @@ object PlayerController { cameraPivotNode += cameraNode // playerNode += cameraPivotNode rootNode += cameraPivotNode - } - } yield ()) .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage()))) .executeOn(appScheduler) @@ -101,10 +104,10 @@ object PlayerController { modelPath: os.RelPath, cam: Camera )(assetManager: AssetManager, bulletAppState: BulletAppState) = { - lazy val playerPos = ImVector3f.ZERO - lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) + val playerPos = ImVector3f.ZERO + val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) .withJumpForce(ImVector3f(0, 5f, 0)) - lazy val playerNode = new Node("PlayerNode") + val playerNode = new Node("PlayerNode") .withChildren( assetManager .loadModel(modelPath) 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 57223ab..b7733b9 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 @@ -18,10 +18,11 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.utils.IOUtils._ +import monix.bio.Task object GameInputHandler { - final class Props( + class Props( inputManager: InputManager, playerEventBus: GameEventBus[PlayerEvent] // playerCameraEventBus: GameEventBus[PlayerCameraEvent] @@ -29,44 +30,44 @@ object GameInputHandler { ) { def begin = for { - _ <- UIO(setupMovementKeys(inputManager)) + _ <- Task(setupMovementKeys(inputManager)) // _ <- UIO(setupAnalogMovementKeys) - _ <- UIO(setupCameraKeys()) + _ <- Task(setupCameraKeys()) _ <- toIO( - generateMovementInputEvents( - inputManager, - playerEventBus - ).completedL.startAndForget - ) - _ <- toIO( - generateAnalogMovementEvents( - inputManager, - playerEventBus - ).completedL.startAndForget - ) - _ <- toIO( - generateCameraEvents( - inputManager, - playerEventBus - ).completedL.startAndForget - ) - _ <- toIO( - Ref.of[me.Task, Boolean](false).flatMap(value => cursorToggle(value)) - ) + me.Task.parSequence( + Seq( + generateMovementInputEvents( + inputManager, + playerEventBus + ).completedL, + // generateAnalogMovementEvents( + // inputManager, + // playerEventBus + // ).completedL, + generateCameraEvents( + inputManager, + playerEventBus + ).completedL, + Ref + .of[me.Task, Boolean](false) + .flatMap(value => cursorToggle(value)) + ) + ) + ).startAndForget } yield () def setupMovementKeys(inputManager: InputManager) = inputManager.withEnumMappings(PlayerMovementInput) { case PlayerMovementInput.WalkRight => - Seq(new KeyTrigger(KeyInput.KEY_D)) + new KeyTrigger(KeyInput.KEY_D) :: Nil case PlayerMovementInput.WalkLeft => - Seq(new KeyTrigger(KeyInput.KEY_A)) + new KeyTrigger(KeyInput.KEY_A) :: Nil case PlayerMovementInput.WalkForward => - Seq(new KeyTrigger(KeyInput.KEY_W)) + new KeyTrigger(KeyInput.KEY_W) :: Nil case PlayerMovementInput.WalkBackward => - Seq(new KeyTrigger(KeyInput.KEY_S)) + new KeyTrigger(KeyInput.KEY_S) :: Nil case PlayerMovementInput.Jump => - Seq(new KeyTrigger(KeyInput.KEY_SPACE)) + new KeyTrigger(KeyInput.KEY_SPACE) :: Nil } def setupAnalogMovementKeys() = @@ -124,7 +125,6 @@ object GameInputHandler { } ) .completedL - .startAndForget } yield () } diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala b/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala index d1eeacc..98c4631 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala @@ -14,15 +14,15 @@ object DefaultGameLevel { assetManager: AssetManager, viewPort: ViewPort ) = { - lazy val sceneModel: Spatial = assetManager.loadModel("main.scene") - lazy val sceneShape = CollisionShapeFactory.createMeshShape( + val sceneModel: Spatial = assetManager.loadModel("main.scene") + val sceneShape = CollisionShapeFactory.createMeshShape( sceneModel.toNode match { case Right(node) => node case Left(ex) => throw new NotImplementedError("No fallback sceneshape") } ) - lazy val landscape: RigidBodyControl = + val landscape: RigidBodyControl = new RigidBodyControl(sceneShape, 0) viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f)) diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove2.scala b/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove2.scala index adc1497..8a888f6 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove2.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove2.scala @@ -1,5 +1,6 @@ package wow.doge.mygame.game.subsystems.movement +import cats.Id import com.jme3.bullet.control.BetterCharacterControl import com.jme3.math.FastMath import com.jme3.math.Quaternion @@ -8,7 +9,6 @@ import monix.eval.Coeval import wow.doge.mygame.implicits._ import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.subsystems.movement.RotateDir - // experiment to see if it would be useful to use an effect wrapper for a typeclass like this trait CanMove2[-A, F[_]] { // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f @@ -19,7 +19,35 @@ trait CanMove2[-A, F[_]] { def rotate(inst: A, rotateDir: RotateDir): F[Unit] } +object Test { + val x = new BetterCharacterControl(4, 10, 5) + def test[T](x: T)(implicit cm: CanMove2[T, Id]) = { + cm.move(x, ImVector3f.ZERO) + } +} + object CanMove2 { + implicit val testImpl = new CanMove2[BetterCharacterControl, Id] { + + override def move( + inst: BetterCharacterControl, + direction: ImVector3f, + speedFactor: Float + ): Id[Unit] = {} + + override def location(inst: BetterCharacterControl): Id[ImVector3f] = + ImVector3f.ZERO + + override def jump(inst: BetterCharacterControl): Id[Unit] = ??? + + override def stop(inst: BetterCharacterControl): Id[Unit] = ??? + + override def rotate( + inst: BetterCharacterControl, + rotateDir: RotateDir + ): Id[Unit] = ??? + + } implicit val implCanMoveForBetterCharacterControl = new CanMove2[BetterCharacterControl, Coeval] { override def move( 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 1830bdc..54f2263 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 @@ -84,7 +84,7 @@ class ImMovementActor[T]( case Tick => val walkDir = - getDirection2(state.cardinalDir, ctx.log.trace) + getDirection2(state.cardinalDir, ctx.log.traceP) // if (walkDir != ImVector3f.ZERO) { val tmp = walkDir * 25f * (1f / 144) props.enqueueR { () => @@ -94,7 +94,10 @@ class ImMovementActor[T]( Behaviors.same } - def getDirection2(cardinalDir: CardinalDirection, debug: String => Unit) = { + def getDirection2( + cardinalDir: CardinalDirection, + debug: sourcecode.Text[String] => sourcecode.Text[String] + ) = { val camDir = props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f) val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f) diff --git a/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala b/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala index 1650b5f..75d3ae7 100644 --- a/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala +++ b/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala @@ -1,11 +1,14 @@ package wow.doge.mygame.implicits +import javafx.beans.value.ObservableValue +import javafx.beans.{value => jfxbv} import javafx.scene.{input => jfxsi} import javafx.{event => jfxe} import monix.execution.Ack import monix.execution.Cancelable import monix.reactive.Observable import monix.reactive.OverflowStrategy +import scalafx.beans.property.Property import scalafx.scene.Scene import scalafx.scene.control.ButtonBase @@ -55,6 +58,35 @@ object JavaFXMonixObservables { } } + implicit final class BindObs[A, B](private val prop: Property[A, B]) + extends AnyVal { + def <--[T](op: Observable[(ObservableValue[_ <: B], B, B)] => T) = { + op(prop.observableChange()) + } + + def observableChange(): Observable[(ObservableValue[_ <: B], B, B)] = { + import monix.execution.cancelables.SingleAssignCancelable + Observable.create(OverflowStrategy.Unbounded) { sub => + val c = SingleAssignCancelable() + + val listener = new jfxbv.ChangeListener[B] { + override def changed( + observable: ObservableValue[_ <: B], + oldValue: B, + newValue: B + ): Unit = { + sub.onNext((observable, oldValue, newValue)) + } + } + + prop.addListener(listener) + + c := Cancelable(() => prop.removeListener(listener)) + c + } + } + } + implicit final class OnActionObservable( private val button: ButtonBase ) extends AnyVal { diff --git a/src/main/scala/wow/doge/mygame/implicits/package.scala b/src/main/scala/wow/doge/mygame/implicits/package.scala index 41f6c20..0dbf5ed 100644 --- a/src/main/scala/wow/doge/mygame/implicits/package.scala +++ b/src/main/scala/wow/doge/mygame/implicits/package.scala @@ -49,6 +49,7 @@ import monix.execution.cancelables.SingleAssignCancelable import monix.reactive.Observable import monix.reactive.OverflowStrategy import monix.reactive.observers.Subscriber +import org.slf4j.Logger import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.state.MyBaseState @@ -794,4 +795,67 @@ package object implicits { def +=(node: scalafx.scene.Node) = jfxui.attachChild(node) def -=(node: scalafx.scene.Node) = jfxui.detachChild(node) } + + implicit class AkkaLoggerExt(private val logger: Logger) extends AnyVal { + def logP[T]( + x: sourcecode.Text[T], + tag: String = "", + width: Int = 100, + height: Int = 500, + indent: Int = 2, + initialOffset: Int = 0 + )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = { + + // def joinSeq[T](seq: Seq[T], sep: T): Seq[T] = { + // seq.flatMap(x => Seq(x, sep)).dropRight(1) + // } + val tagStrs = + if (tag.isEmpty) Seq.empty + else Seq(fansi.Color.Cyan(tag), fansi.Str(" ")) + val prefix = Seq( + fansi.Color.Magenta(fileName.value), + fansi.Str(":"), + fansi.Color.Green(line.value.toString), + fansi.Str(" "), + fansi.Color.Cyan(x.source), + fansi.Str(": ") + ) ++ tagStrs + fansi.Str.join( + prefix ++ pprint.tokenize(x.value, width, height, indent).toSeq: _* + ) + // x.value + } + + def warnP[T]( + s: sourcecode.Text[T] + )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = { + logger.warn(logP(s).render) + s + } + def errorP[T]( + s: sourcecode.Text[T] + )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = { + logger.error(logP(s).render) + s + } + def infoP[T]( + s: sourcecode.Text[T] + )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = { + logger.info(logP(s).render) + s + } + def debugP[T]( + s: sourcecode.Text[T] + )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = { + logger.debug(logP(s).render) + s + } + def traceP[T]( + s: sourcecode.Text[T] + )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = { + logger.trace(logP(s).render) + s + } + + } } diff --git a/src/main/scala/wow/doge/mygame/launcher/Launcher.scala b/src/main/scala/wow/doge/mygame/launcher/Launcher.scala index 67084bc..bab5282 100644 --- a/src/main/scala/wow/doge/mygame/launcher/Launcher.scala +++ b/src/main/scala/wow/doge/mygame/launcher/Launcher.scala @@ -1,28 +1,31 @@ package wow.doge.mygame.launcher -import scala.concurrent.duration.FiniteDuration -import scala.concurrent.duration._ - import cats.effect.concurrent.Deferred import javafx.application.Platform +import javafx.beans.value.ObservableValue import monix.bio.Task import monix.catnap.CancelableF -import monix.eval.{Task => ETask} +import monix.execution.CancelablePromise import monix.reactive.Observable +import monix.{eval => me} import scalafx.Includes._ import scalafx.application.JFXApp import scalafx.application.JFXApp.PrimaryStage +import scalafx.beans.property.StringProperty import scalafx.scene.control.Button import scalafx.stage.StageStyle import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.implicits.JavaFXMonixObservables._ import wow.doge.mygame.utils.IOUtils._ - +import cats.effect.Resource +import cats.kernel.Eq object Launcher { sealed trait LauncherResult object LauncherResult { case object LaunchGame extends LauncherResult case object Exit extends LauncherResult + + implicit val eqForLR = Eq.fromUniversalEquals[LauncherResult] } class Props( @@ -44,10 +47,23 @@ class Launcher private (props: Launcher.Props) { .observableAction() .doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame))) + def testChangeObs( + obs: Observable[(ObservableValue[_ <: String], String, String)] + ) = + obs + .doOnNext { + case (x, y, z) => monix.eval.Task.unit + } + // .subscribe() + private lazy val exitButton = new Button { text = "Exit" + // text <-- testChangeObs } + // exitButton.text.bind + StringProperty("") addListener ((_, _, _) => ()) + private lazy val exitAction = exitButton .observableAction() @@ -60,22 +76,25 @@ class Launcher private (props: Launcher.Props) { scene = _scene } - private lazy val internal = new JFXApp { - stage = _stage - stage.initStyle(StageStyle.Undecorated) - // ResizeHelper.addResizeListener(stage) - } + private def internal(startSignal: CancelablePromise[Unit]) = + new JFXApp { + stage = _stage + stage.initStyle(StageStyle.Undecorated) + // ResizeHelper.addResizeListener(stage) + startSignal.success(()) + } private lazy val sceneDragObservable = { - lazy val mpo = _scene.observableMousePressed() - lazy val mdo = _scene.observableMouseDragged() + val mpo = _scene.observableMousePressed() + val mdo = _scene.observableMouseDragged() - mpo.mergeMap(pressEvent => + mpo.concatMap(pressEvent => mdo.doOnNext(dragEvent => - ETask( - _stage.setX(dragEvent.screenX - pressEvent.sceneX) - ) >> - ETask( + me.Task(pprint.log("emitted")) >> + me.Task( + _stage.setX(dragEvent.screenX - pressEvent.sceneX) + ) >> + me.Task( _stage.setY( dragEvent.screenY - pressEvent.sceneY ) @@ -84,21 +103,58 @@ class Launcher private (props: Launcher.Props) { ) } - def init(delay: FiniteDuration = 2000.millis) = - for { + // import cats.syntax.all._ + + // def init(delay: FiniteDuration = 2000.millis) = + // for { + // _ <- Task(Platform.setImplicitExit(false)) + // x <- (Task.unit.start, Task.unit.start).parTupled + // fxAppStartFib <- Task(internal.main(Array.empty)).start + // _ <- Task.sleep(500.millis) + // sceneDragFib <- toIO(sceneDragObservable.completedL).start + // buttonActionsComposedFib <- toIO( + // Observable(launchAction, exitAction).merge + // .doOnNext(_ => + // me.Task(internal.stage.close()).executeOn(props.schedulers.fx) + // ) + // .completedL + // ).start + // c <- CancelableF[Task]( + // fxAppStartFib.cancel >> buttonActionsComposedFib.cancel >> sceneDragFib.cancel + // ) + // } yield (c) + + def init = + Resource.make(for { _ <- Task(Platform.setImplicitExit(false)) - - fib <- Task(internal.main(Array.empty)).start - _ <- Task.sleep(500.millis) - sceneDragFib <- toIO(sceneDragObservable.completedL).start - fib2 <- toIO( - Observable(launchAction, exitAction).merge - .doOnNext(_ => - ETask(internal.stage.close()).executeOn(props.schedulers.fx) + startSignal <- Task(CancelablePromise[Unit]()) + delegate <- Task(internal(startSignal)) + combinedFib <- + Task + .parZip2( + Task(delegate.main(Array.empty)), + Task.fromCancelablePromise(startSignal) >> toIO( + me.Task.parSequence( + List( + sceneDragObservable.completedL, + Observable(launchAction, exitAction).merge + .doOnNext(_ => + me.Task(delegate.stage.close()) + .executeOn(props.schedulers.fx) + ) + .completedL + ) + ) + ) ) - .completedL - ).start - c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel) - } yield (c) + .start + c <- CancelableF[Task]( + // Task(println("Cancelling")) >> + // combinedFib.cancel >> + // fxAppStartFib.cancel + // Task.unit + combinedFib.cancel + ) + } yield c)(_.cancel) } 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 0f4bde6..9919d3f 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala @@ -18,22 +18,22 @@ import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.TickEvent class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) { - implicit lazy val s = spawnProtocol.scheduler + implicit val s = spawnProtocol.scheduler - implicit lazy val timeout = Timeout(1.second) + implicit val timeout = Timeout(1.second) - lazy val eventBusLogger = SLogger[EventBus[_]] + val eventBusLogger = SLogger[EventBus[_]] - lazy val playerEventBusTask = + val playerEventBusTask = createEventBus[PlayerEvent]("playerEventBus") - // lazy val playerCameraEventBusTask = + // val playerCameraEventBusTask = // createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG) - lazy val tickEventBusTask = + val tickEventBusTask = createEventBus[TickEvent]("tickEventBus", Level.TRACE) - lazy val mainEventBusTask = createEventBus[Event]("mainEventBus") + val mainEventBusTask = createEventBus[Event]("mainEventBus") def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) = spawnProtocol.askL( diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala index f75c7a0..2b92bb2 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala @@ -41,7 +41,7 @@ object ScriptActor { result: ActorRef[Map[os.Path, Either[Error, Any]]] ) extends Command - lazy val defaultScalaRunner = + val defaultScalaRunner = ammonite .Main( storageBackend = new Folder( @@ -51,13 +51,13 @@ object ScriptActor { ) ) - lazy val defaultKotlinRunner: KotlinScriptEngine = { + val defaultKotlinRunner: KotlinScriptEngine = { val manager = new ScriptEngineManager() val engine = manager.getEngineByExtension("main.kts") engine.taggedWith[Kotlin] } - lazy val defaultGroovyRunner: GroovyScriptEngine = + val defaultGroovyRunner: GroovyScriptEngine = new GroovyScriptEngine(os.pwd.toString) def apply( diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala index 3f2d811..f2953a5 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala @@ -1,5 +1,6 @@ package wow.doge.mygame.scriptsystem +import scala.concurrent.duration._ import scala.util.Failure import scala.util.Success @@ -14,8 +15,9 @@ import akka.actor.typed.scaladsl.Routers import akka.util.Timeout import com.typesafe.scalalogging.Logger import org.slf4j.event.Level +import wow.doge.mygame.implicits._ import wow.doge.mygame.state.ScriptActor -import scala.concurrent.duration._ + import ScriptActor.ScriptObject object ScriptCachingActor { @@ -188,10 +190,10 @@ class ScriptCachingActor( Behaviors.same case Put(scriptPath, script) => - ctx.log.debug(s"Putting $script at path $scriptPath") + ctx.log.debugP(s"Putting $script at path $scriptPath") val newState = state.modify(_.scriptsMap).using(_ + (scriptPath -> script)) - ctx.log.trace(newState.toString()) + ctx.log.traceP(newState.toString()) receiveMessage(state = newState) case NoOp => Behaviors.same @@ -224,14 +226,14 @@ private[scriptsystem] object Methods { scriptsMap .get(scriptPath) .fold { - ctx.log.debug("Delegating to child") + ctx.log.debugP("Delegating to child") ctx.self ! DelegateToChild( scriptActor, scriptPath, requester ) } { s => - ctx.log.debug("Getting script from cache") + ctx.log.debugP("Getting script from cache") requester ! Right(s) } } diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala index 975ce98..08e1737 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala @@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout -import cats.effect.Resource import monix.bio.Task import wow.doge.mygame.scriptsystem.ScriptCachingActor import wow.doge.mygame.utils.AkkaUtils @@ -20,29 +19,18 @@ object ScriptInitMode { } class ScriptSystemResource( path: os.Path, - spawnProtocol: ActorRef[SpawnProtocol.Command], mode: ScriptInitMode = ScriptInitMode.Lazy -)(implicit timeout: Timeout, scheduler: Scheduler) { - val make = { - // throw new Exception("boom") - findScriptFiles(os.pwd / "assets" / "scripts") - - lazy val scriptCacheActor = AkkaUtils.spawnActorL( - spawnProtocol, - "scriptCachingActor", - ScriptCachingActor() - ) - - Resource.liftF(scriptCacheActor) - } - // sys.ask(ref => ScriptCachingActor.GetAll(os.pwd/'assets/'scripts/'scala/"hello2.sc",ref, false)) +)(implicit + spawnProtocol: ActorRef[SpawnProtocol.Command], + timeout: Timeout, + scheduler: Scheduler +) { val init = for { scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts")) scriptCacheActor <- AkkaUtils.spawnActorL( - spawnProtocol, - "scriptCachingActor", - ScriptCachingActor() + ScriptCachingActor(), + "scriptCachingActor" ) } yield (scriptCacheActor) diff --git a/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala b/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala index 3c47b1c..2953f01 100644 --- a/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala +++ b/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala @@ -9,7 +9,7 @@ import akka.util.Timeout import wow.doge.mygame.implicits._ object AkkaUtils { - def spawnActorL[T]( + def spawnActorOldL[T]( spawnProtocol: ActorRef[SpawnProtocol.Command], actorName: String, behavior: Behavior[T] @@ -22,7 +22,7 @@ object AkkaUtils { _ ) ) - def spawnActorL2[T]( + def spawnActorL[T]( behavior: Behavior[T], actorName: String )(implicit diff --git a/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala b/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala index e4afd5a..318df1a 100644 --- a/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala +++ b/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala @@ -21,7 +21,7 @@ class GenericConsoleStream[T]( )(implicit cs: ConsoleStreamable[T] ) extends PrintStream(outputStream, true) { - private lazy val defaultOut = System.out + private val defaultOut = System.out def printToStreamable(stble: Option[T], text: String) = stble.foreach(s => cs.println(s, text)) @@ -57,7 +57,7 @@ object GenericConsoleStream { */ case class Config(exclusive: Boolean = false) object Config { - lazy val default = Config() + val default = Config() } implicit val implJFXConsoleStreamForTextArea =