From 73d657952f27a4ba1f3caca8efafb1897c918178 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Sat, 28 Nov 2020 22:25:47 +0530 Subject: [PATCH] added npc state machine --- .../$file/src/main/resources/dep.scala | 39 - .../$file/src/main/resources/hello.scala | 45 -- .../$file/src/main/resources/hello2.scala | 134 ---- build.sbt | 4 +- .../org/slf4j/impl/StaticLoggerBuilder.scala | 8 +- src/main/scala/wow/doge/mygame/Main.scala | 23 +- src/main/scala/wow/doge/mygame/MainApp.scala | 224 ++++-- .../scala/wow/doge/mygame/MainModule.scala | 13 +- .../scala/wow/doge/mygame/game/GameApp.scala | 281 ++++--- .../scala/wow/doge/mygame/game/GameApp2.scala | 71 -- .../wow/doge/mygame/game/GameAppActor.scala | 88 +- .../wow/doge/mygame/game/GameModule.scala | 75 +- .../mygame/game/GameSystemsInitializer.scala | 106 --- .../wow/doge/mygame/game/SimpleAppExt.scala | 132 +++ .../game/entities/NpcActorSupervisor.scala | 225 ++++++ .../player}/PlayerActorSupervisor.scala | 139 ++-- .../player}/PlayerCameraActor.scala | 2 +- .../entities/player/PlayerController.scala | 248 ++++++ .../player}/PlayerEventListeners.scala | 6 +- .../mygame/game/nodes/PlayerController.scala | 222 ------ .../mygame/game/subsystems/ai/GdxAiTest.scala | 54 +- .../subsystems/input/GameInputHandler.scala | 26 +- .../game/subsystems/input/InputConstant.scala | 9 - .../game/subsystems/input/InputEnums.scala | 8 +- .../game/subsystems/level/GameLevel.scala | 18 +- .../game/subsystems/movement/CanMove.scala | 9 +- .../subsystems/movement/MovementActor.scala | 34 +- .../mygame/game/subsystems/ui/JmeJfx.scala | 36 +- .../implicits/JavaFXMonixObservables.scala | 16 +- .../implicits/observables/package.scala | 15 +- .../wow/doge/mygame/implicits/package.scala | 76 +- .../wow/doge/mygame/launcher/DefaultUI.scala | 46 +- .../wow/doge/mygame/launcher/Launcher.scala | 69 +- .../wow/doge/mygame/math/ImVector3f.scala | 4 + .../mygame/subsystems/events/EventBus.scala | 2 +- .../mygame/subsystems/events/Events.scala | 30 +- .../subsystems/events/EventsModule.scala | 35 +- .../subsystems/events/MovementEvents.scala | 33 - .../events/PlayerCameraEvents.scala | 7 - .../subsystems/events/PlayerEvents.scala | 24 + .../moddingsystem/ModdingSystem.scala | 2 +- .../scriptsystem/ScriptSystemModule.scala | 2 +- .../wow/doge/mygame/utils/AkkaUtils.scala | 16 + .../doge/mygame/utils/BorderlessScene.scala | 750 ------------------ .../mygame/utils/GenericConsoleStream.scala | 57 +- 45 files changed, 1338 insertions(+), 2125 deletions(-) delete mode 100644 .ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala delete mode 100644 .ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala delete mode 100644 .ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala delete mode 100644 src/main/scala/wow/doge/mygame/game/GameApp2.scala delete mode 100644 src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala create mode 100644 src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala create mode 100644 src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala rename src/main/scala/wow/doge/mygame/game/{nodes => entities/player}/PlayerActorSupervisor.scala (54%) rename src/main/scala/wow/doge/mygame/game/{nodes => entities/player}/PlayerCameraActor.scala (94%) create mode 100644 src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala rename src/main/scala/wow/doge/mygame/game/{nodes => entities/player}/PlayerEventListeners.scala (91%) delete mode 100644 src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala delete mode 100644 src/main/scala/wow/doge/mygame/game/subsystems/input/InputConstant.scala delete mode 100644 src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala create mode 100644 src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala delete mode 100644 src/main/scala/wow/doge/mygame/utils/BorderlessScene.scala diff --git a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala deleted file mode 100644 index 319342d..0000000 --- a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala +++ /dev/null @@ -1,39 +0,0 @@ - -package ammonite -package $file.src.main.resources -import _root_.ammonite.interp.api.InterpBridge.{ - value => interp -} -import _root_.ammonite.interp.api.InterpBridge.value.{ - exit -} -import _root_.ammonite.interp.api.IvyConstructor.{ - ArtifactIdExt, - GroupIdExt -} -import _root_.ammonite.runtime.tools.{ - browse, - grep, - time, - tail -} -import _root_.ammonite.repl.tools.{ - desugar, - source -} -import _root_.ammonite.main.Router.{ - doc, - main -} -import _root_.ammonite.repl.tools.Util.{ - pathScoptRead -} - - -object dep{ -/**/ /**/ -def $main() = { scala.Iterator[String]() } - override def toString = "dep" - /**/ -} diff --git a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala deleted file mode 100644 index ae97991..0000000 --- a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala +++ /dev/null @@ -1,45 +0,0 @@ - -package ammonite -package $file.src.main.resources -import _root_.ammonite.interp.api.InterpBridge.{ - value => interp -} -import _root_.ammonite.interp.api.InterpBridge.value.{ - exit -} -import _root_.ammonite.interp.api.IvyConstructor.{ - ArtifactIdExt, - GroupIdExt -} -import _root_.ammonite.runtime.tools.{ - browse, - grep, - time, - tail -} -import _root_.ammonite.repl.tools.{ - desugar, - source -} -import _root_.ammonite.main.Router.{ - doc, - main -} -import _root_.ammonite.repl.tools.Util.{ - pathScoptRead -} -import ammonite.$file.src.main.resources.{ - dep -} - - -object hello{ -/**/ /**/ -def $main() = { scala.Iterator[String]() } - override def toString = "hello" - /**/ -} diff --git a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala deleted file mode 100644 index 52f6a44..0000000 --- a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala +++ /dev/null @@ -1,134 +0,0 @@ - -package ammonite -package $file.src.main.resources -import _root_.ammonite.interp.api.InterpBridge.{ - value => interp -} -import _root_.ammonite.interp.api.InterpBridge.value.{ - exit -} -import _root_.ammonite.interp.api.IvyConstructor.{ - ArtifactIdExt, - GroupIdExt -} -import _root_.ammonite.runtime.tools.{ - browse, - grep, - time, - tail -} -import _root_.ammonite.repl.tools.{ - desugar, - source -} -import _root_.ammonite.main.Router.{ - doc, - main -} -import _root_.ammonite.repl.tools.Util.{ - pathScoptRead -} - - -object hello2{ -/**/ /**/ -def $main() = { scala.Iterator[String]() } - override def toString = "hello2" - /**/ -} diff --git a/build.sbt b/build.sbt index d0f09ae..3159136 100644 --- a/build.sbt +++ b/build.sbt @@ -91,8 +91,8 @@ lazy val root = (project in file(".")).settings( "com.beachape" %% "enumeratum-circe" % "1.6.1", "com.lihaoyi" %% "os-lib" % "0.7.1", // "com.jayfella" % "jme-jfx-11" % "1.1.5", - "com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0", - "com.github.Oshan96" % "CustomStage" % "v1.3.1", + // "com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0", + // "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" diff --git a/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala b/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala index 0cb63ea..24e33bb 100644 --- a/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala +++ b/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala @@ -34,7 +34,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] { private lazy val (defaultConsoleLogger, release1) = consoleLogger[IO](minLevel = Level.Debug) - .withAsync(timeWindow = 1.milliseconds) + .withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) .allocated .unsafeRunSync() @@ -43,7 +43,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] { "application-log-2.log", Formatter.json, minLevel = Level.Debug - ).withAsync(timeWindow = 1.milliseconds) + ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) .allocated .unsafeRunSync() @@ -52,7 +52,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] { "eventbus.log", Formatter.json, minLevel = Level.Debug - ).withAsync(timeWindow = 1.milliseconds) + ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) .allocated .unsafeRunSync() @@ -77,7 +77,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] { // "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler" // ) => // defaultConsoleLogger.withMinimalLevel( Level.Trace) //selectively turn on trace logging for specific classes - case s if s.startsWith("wow.doge.mygame.events.EventBus") => + 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 diff --git a/src/main/scala/wow/doge/mygame/Main.scala b/src/main/scala/wow/doge/mygame/Main.scala index 4a0a3b6..6e5177f 100644 --- a/src/main/scala/wow/doge/mygame/Main.scala +++ b/src/main/scala/wow/doge/mygame/Main.scala @@ -2,19 +2,19 @@ package wow.doge.mygame import scala.concurrent.duration._ +import _root_.monix.bio.BIOApp +import _root_.monix.bio.Task +import _root_.monix.bio.UIO 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 wow.doge.mygame.game.GameAppResource -import _root_.monix.bio.BIOApp -import _root_.monix.bio.Task -import _root_.monix.bio.UIO -import cats.effect.Resource import scalafx.scene.control.TextArea +import wow.doge.mygame.game.GameAppResource import wow.doge.mygame.utils.GenericConsoleStream object Main extends BIOApp with MainModule { @@ -25,15 +25,16 @@ object Main extends BIOApp with MainModule { def appResource(consoleStream: GenericConsoleStream[TextArea]) = for { logger <- - consoleLogger().withAsync(timeWindow = 1.milliseconds) |+| + consoleLogger().withAsync( + timeWindow = 1.milliseconds, + maxBufferSize = Some(2000) + ) |+| fileLogger( "application-log-1.log", Formatter.json - ).withAsync(timeWindow = 1.milliseconds) + ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) jmeScheduler <- jMESchedulerResource - actorSystem <- actorSystemResource2(logger) - // consoleTextArea <- Resource.liftF(Task(new TextArea())) - // consoleStream <- wireWith(JFXConsoleStream.textAreaStream _) + actorSystem <- actorSystemResource(logger) gameApp <- { // new BulletAppState() // bas.setThreadingType(Thr) @@ -61,7 +62,7 @@ object Main extends BIOApp with MainModule { lazy val consoleStream = GenericConsoleStream.textAreaStream() Console.withOut(consoleStream)( appResource(consoleStream) - .use(_ => Task.unit) + .use(_ => Task.unit >> Task(consoleStream.close())) .onErrorHandle(_.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 b287386..133a0d1 100644 --- a/src/main/scala/wow/doge/mygame/MainApp.scala +++ b/src/main/scala/wow/doge/mygame/MainApp.scala @@ -5,56 +5,54 @@ import akka.actor.typed.ActorSystem import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout -import cats.effect.concurrent.Ref +import cats.effect.concurrent.Deferred import com.jme3.app.state.AppStateManager import com.jme3.asset.AssetManager import com.jme3.asset.plugins.ZipLocator import com.jme3.bullet.BulletAppState import com.jme3.input.InputManager import com.jme3.renderer.Camera +import com.jme3.renderer.ViewPort import com.jme3.scene.Node import com.softwaremill.macwire._ import com.softwaremill.tagging._ import io.odin.Logger +import monix.bio.Fiber import monix.bio.IO import monix.bio.Task -import wow.doge.mygame.events.EventBus -import wow.doge.mygame.game.GameApp2 -import wow.doge.mygame.game.nodes.PlayerTag -import wow.doge.mygame.game.nodes.PlayerController +import scalafx.scene.control.TextArea +import wow.doge.mygame.executors.Schedulers +import wow.doge.mygame.game.GameApp +import wow.doge.mygame.game.GameAppActor +import wow.doge.mygame.game.GameAppTags +import wow.doge.mygame.game.entities.PlayerController +import wow.doge.mygame.game.entities.PlayerControllerTags 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.launcher.Launcher +import wow.doge.mygame.launcher.Launcher.LauncherResult import wow.doge.mygame.math.ImVector3f -import wow.doge.mygame.subsystems.events.EntityMovementEvent -import wow.doge.mygame.subsystems.events.EventsModule2 +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.EventsModule import wow.doge.mygame.subsystems.events.PlayerCameraEvent -import wow.doge.mygame.game.subsystems.level.DefaultGameLevel -import com.jme3.renderer.ViewPort -import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource +import wow.doge.mygame.subsystems.events.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode -import wow.doge.mygame.launcher.Launcher -import wow.doge.mygame.executors.Schedulers -import scalafx.application.JFXApp.PrimaryStage -import scalafx.geometry.Insets -import scalafx.scene.Scene -import scalafx.scene.control.Button -import scalafx.scene.layout.StackPane -import scalafx.scene.paint.Color -import scalafx.scene.shape.Rectangle -import scalafx.Includes._ -import scala.concurrent.duration._ -import cats.effect.concurrent.Deferred -import monix.bio.Fiber -import wow.doge.mygame.launcher.Launcher.LauncherResult -import scalafx.scene.control.TextArea -import com.jayfella.jme.jfx.JavaFxUI +import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource +import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.GenericConsoleStream -import java.io.PrintStream + +import EventsModule.GameEventBus +import wow.doge.mygame.game.entities.NpcMovementActor2 +import wow.doge.mygame.game.entities.NpcActorSupervisor +import monix.execution.exceptions.DummyException +import com.jme3.bullet.control.BetterCharacterControl class MainApp( logger: Logger[Task], - gameApp: GameApp2, - spawnProtocol: ActorSystem[SpawnProtocol.Command], + gameApp: GameApp, + implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command], jmeThread: monix.execution.Scheduler, schedulers: Schedulers, consoleStream: GenericConsoleStream[TextArea] @@ -68,19 +66,29 @@ class MainApp( def gameInit: Task[Fiber[Throwable, Unit]] = for { - eventsModule <- Task(new EventsModule2(spawnProtocol)) + eventsModule <- Task(new EventsModule(spawnProtocol)) playerMovementEventBus <- eventsModule.playerMovementEventBusTask playerCameraEventBus <- eventsModule.playerCameraEventBusTask + mainEventBus <- eventsModule.mainEventBusTask + tickEventBus <- eventsModule.tickEventBusTask + gameAppActor <- AkkaUtils.spawnActorL2( + GameAppActor.Props(tickEventBus).create, + "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 JME thread has been + * 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(() => Task("done")).flatten - + 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 @@ -89,41 +97,20 @@ class MainApp( enqueueR <- Task(gameApp.enqueue _) viewPort <- gameApp.viewPort _ <- logger.info("before") - // jfxUI <- Task(JavaFxUI.initialize(gameApp.app)) - // .executeOn(gameApp.scheduler) >> Task.sleep(500.millis) >> Task( - // JavaFxUI.getInstance() - // ) - // .start - jfxUI <- gameApp.jfxUI + // jfxUI <- gameApp.jfxUI consoleTextArea <- Task(new TextArea { - text = "hello" + text = "hello \n" editable = false wrapText = true // maxHeight = 150 // maxWidth = 300 }) - _ <- Task(consoleStream := consoleTextArea) - _ <- Task(jfxUI += consoleTextArea) - // consoleStream <- Task( - // GenericConsoleStream.textAreaStream(consoleTextArea) - // ) + // _ <- Task(consoleStream := consoleTextArea) + // _ <- Task(jfxUI += consoleTextArea) _ <- logger.info("after") bulletAppState <- Task(new BulletAppState()) _ <- Task(stateManager.attach(bulletAppState)) _ <- logger.info("Initializing console stream") - // _ <- Task(GenericConsoleStream.textAreaStream(consoleTextArea)).bracket( - // consoleStream => - // Task { System.setOut(consoleStream) } >> wire[MainAppDelegate] - // .init(gameApp.scheduler, consoleStream) - // // consoleLogger - // // Console.withOut(consoleStream)( - // // wire[MainAppDelegate].init(gameApp.scheduler, consoleStream) - // // ) - // )(stream => Task(stream.close()).onErrorHandle(_.printStackTrace())) - // _ <- Task { System.setOut(new PrintStream(consoleStream, true)) } - // _ <- Task { - // Console.withOut(consoleStream)(println("hello")) - // } _ <- wire[MainAppDelegate].init(gameApp.scheduler) } yield (gameAppFib) @@ -131,7 +118,7 @@ class MainApp( scriptSystem <- scriptSystemInit /** * Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI - * Without this, we get a "Toolkit already initialized" excResult. The launch button + * 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 */ @@ -141,15 +128,16 @@ class MainApp( launchResult <- launchSignal.get _ <- cancelToken.cancel _ <- + /** + * User chose to quit + */ if (launchResult == LauncherResult.Exit) logger.info("Exiting") - else gameInit.flatMap(_.join) - // _ <- Task.sleep(2000.millis) - // gameAppFib <- gameInit - /** - * Wait for game window to close - */ - // _ <- gameAppFib.join + /** + * User chose launch. Wait for game window to close + */ + else + gameInit.flatMap(_.join) } yield () } @@ -157,27 +145,27 @@ class MainApp( * Class with all dependencies in one place for easy wiring */ class MainAppDelegate( - gameApp: GameApp2, - spawnProtocol: ActorSystem[SpawnProtocol.Command], + gameApp: GameApp, + implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command], loggerL: Logger[Task], - // eventBuses: EventsModule2 playerMovementEventBus: ActorRef[ - EventBus.Command[EntityMovementEvent.PlayerMovementEvent] + EventBus.Command[PlayerMovementEvent] ], playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], + tickEventBus: GameEventBus[TickEvent], inputManager: InputManager, assetManager: AssetManager, stateManager: AppStateManager, camera: Camera, viewPort: ViewPort, enqueueR: Function1[() => Unit, Unit], - rootNode: Ref[Task, Node], + rootNode: Node @@ GameAppTags.RootNode, bulletAppState: BulletAppState )(implicit @annotation.unused timeout: Timeout, @annotation.unused scheduler: Scheduler ) { - + lazy val physicsSpace = bulletAppState.physicsSpace def init( appScheduler: monix.execution.Scheduler // consoleStream: GenericConsoleStream[TextArea] @@ -186,11 +174,11 @@ class MainAppDelegate( _ <- loggerL.info("Initializing Systems") _ <- Task( assetManager.registerLocator( - (os.rel / "assets" / "town.zip"), + os.rel / "assets" / "town.zip", classOf[ZipLocator] ) ) - _ <- loggerL.info("test hmm") + _ <- loggerL.info("test") // _ <- Task(consoleStream.println("text")) _ <- DefaultGameLevel(assetManager, viewPort) .addToGame( @@ -198,23 +186,91 @@ class MainAppDelegate( bulletAppState.physicsSpace ) .executeOn(appScheduler) - _ <- createPlayerController(appScheduler).startAndForget.onErrorRestart(3) + _ <- createPlayerController(appScheduler) + .absorbWith(e => DummyException("boom")) + .onErrorRestart(3) + johnActor <- createNpc(appScheduler, "John").executeOn(appScheduler) + _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) _ <- wire[GameInputHandler.Props].begin.onErrorRestart(3) } yield () def createPlayerController( appScheduler: monix.execution.Scheduler - // playerMovementEventBus: ActorRef[ - // EventBus.Command[PlayerMovementEvent] - // ], - // playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] ): IO[PlayerController.Error, Unit] = { - @annotation.unused val playerPos = ImVector3f.ZERO - @annotation.unused - val playerNode = None.taggedWith[PlayerTag] - @annotation.unused val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" - wire[PlayerController.Props].create + val playerPhysicsControl = + PlayerController.Defaults.defaultPlayerPhysicsControl + .taggedWith[PlayerControllerTags.PlayerTag] + val camNode = + PlayerController.Defaults + .defaultCamerNode(camera, playerPos) + .taggedWith[PlayerControllerTags.PlayerCameraNode] + val mbPlayerNode = PlayerController.Defaults + .defaultPlayerNode( + assetManager, + modelPath, + playerPos, + camNode, + playerPhysicsControl + ) + + for { + playerNode <- IO.fromEither(mbPlayerNode) + _ <- wire[PlayerController.Props].create + } yield () } + def createNpc( + appScheduler: monix.execution.Scheduler, + npcName: String + ) = + // : IO[PlayerController.Error, Unit] = + { + val initialPos = ImVector3f(100, 0, 0) + // val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" + lazy val npcPhysicsControl = + new BetterCharacterControl(1f, 2.1f, 10f) + // .withJumpForce(ImVector3f(0, 5f, 0)) + // val npcMovementActor = AkkaUtils.spawnActorL2( + // new NpcMovementActor2.Props( + // initialPos, + // tickEventBus, + // npcPhysicsControl + // ).create, + // s"${npcName}-npcMovementActor" + // ) + lazy val mbNpcNode = PlayerController.Defaults.defaultNpcNode( + assetManager, + initialPos, + npcPhysicsControl, + "John" + ) + val npcActorTask = AkkaUtils.spawnActorL2( + NpcActorSupervisor + .Props( + new NpcMovementActor2.Props( + enqueueR, + initialPos, + tickEventBus, + npcPhysicsControl + ).create, + npcName, + initialPos + ) + .create, + s"${npcName}-npcMovementActorSupervisor" + ) + // .taggedWith[PlayerControllerTags.PlayerTag] + + for { + npcNode <- IO.fromEither(mbNpcNode) + npcActor <- npcActorTask + _ <- IO { + physicsSpace += npcPhysicsControl + physicsSpace += npcNode + rootNode += npcNode + } + } yield (npcActor) + } + } diff --git a/src/main/scala/wow/doge/mygame/MainModule.scala b/src/main/scala/wow/doge/mygame/MainModule.scala index 87f51b5..e2ad87a 100644 --- a/src/main/scala/wow/doge/mygame/MainModule.scala +++ b/src/main/scala/wow/doge/mygame/MainModule.scala @@ -5,11 +5,10 @@ import cats.effect.Resource import io.odin.Logger import monix.bio.Task import wow.doge.mygame.executors.ExecutorsModule -import wow.doge.mygame.game.GameModule -trait MainModule extends GameModule with ExecutorsModule { +trait MainModule extends ExecutorsModule { - def actorSystemResource2( + def actorSystemResource( logger: Logger[Task] ): Resource[Task, ActorSystem[SpawnProtocol.Command]] = Resource.make(logger.info("Creating Actor System") >> Task { @@ -18,8 +17,10 @@ trait MainModule extends GameModule with ExecutorsModule { name = "GameActorSystem" ) })(sys => - logger.info("Shutting down actor system") >> Task( - sys.terminate() - ) + for { + _ <- Task(sys.terminate()) + _ <- Task.fromFuture(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 fc29785..8f829c5 100644 --- a/src/main/scala/wow/doge/mygame/game/GameApp.scala +++ b/src/main/scala/wow/doge/mygame/game/GameApp.scala @@ -1,132 +1,191 @@ package wow.doge.mygame.game -import scala.collection.immutable.Queue - -import com.jme3.app.SimpleApplication -import com.jme3.app.state.AppState +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.softwaremill.tagging._ +import com.typesafe.scalalogging.{Logger => SLogger} +import io.odin.Logger +import monix.bio.IO import monix.bio.Task -import monix.execution.CancelableFuture -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 GameApp( - schedulers: Schedulers, - appStates: AppState* -) extends SimpleApplication(appStates: _*) { - import GameApp._ - - /** - * A non blocking synchronized queue using an immutable scala queue and monix's atomic class - */ - private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]]) +import monix.catnap.ConcurrentChannel +import monix.catnap.ConsumerF +import monix.catnap.Semaphore +import monix.eval.Coeval +import wow.doge.mygame.game.subsystems.ui.JFxUI + +sealed trait Error +case object FlyCamNotExists extends Error + +object GameAppTags { + sealed trait RootNode + sealed trait GuiNode +} - private val tickSubject = - ConcurrentSubject[Float](multicast = MulticastStrategy.publish)( - schedulers.async +class GameApp(logger: Logger[Task], val app: SimpleAppExt) { + import Ops._ + + def stateManager: Task[AppStateManager] = Task(app.getStateManager()) + def inputManager: Task[InputManager] = Task(app.getInputManager()) + def assetManager: Task[AssetManager] = Task(app.getAssetManager()) + def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode]) + def addToGuiNode = guiNode.flatMap(rn => Task(new AddToNode(rn))) + def flyCam = + IO(app.getFlyByCamera()).onErrorHandleWith(_ => + IO.raiseError(FlyCamNotExists) ) + def camera = Task(app.getCamera()) + def viewPort = Task(app.getViewPort()) + def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode]) + // def rootNode2 = SynchedObject(app.getRootNode()) + def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn))) + def enqueue(cb: () => Unit) = + app.enqueue(new Runnable { + override def run() = cb() + }) + def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb) + + def start = Task(app.start()) + def stop = Task(app.stop()) + def scheduler = app.scheduler + def jfxUI = JFxUI(app) + +} - def tickObservable: Observable[Float] = tickSubject +object GameApp { - override def simpleInitApp(): Unit = {} + class WrappedNode(node: Node, lock: Semaphore[Task]) { - override def simpleUpdate(tpf: Float): Unit = { - tickSubject.onNext(tpf) + def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat))) } - override def stop(): Unit = { - tickSubject.onComplete() - super.stop() + /** + * Synchronization wrapper for a mutable object + * + * @param obj the mutable object + * @param lock lock for synchronization + */ + class SynchedObject[A](obj: A, lock: Semaphore[Task]) { + def modify(f: A => Unit): Task[Unit] = + lock.withPermit(Task(f(obj))) + + def flatModify(f: A => Task[Unit]): Task[Unit] = + lock.withPermit(f(obj)) + + def get: Task[A] = lock.withPermit(Task(obj)) } - def enqueueScala[T](cb: () => T): CancelableFuture[T] = { - val p = Promise[T]() - taskQueue2.transform(_ :+ MyTask(p, cb)) - p.future + object SynchedObject { + def apply[A](obj: A) = + Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock))) } - def enqueueL[T](cb: () => T): Task[T] = - Task.deferFuture(enqueueScala(cb)) +} - override protected def runQueuedTasks(): Unit = { - taskQueue2.transform { current => - current.dequeueOption.fold(current) { - case (MyTask(p, cb), updated) => - p.success(cb()) - updated +object Ops { + final class AddToNode[T <: Node](private val node: T) extends AnyVal { + + /** + * Pure version + */ + def apply(spatial: Spatial)(implicit logger: Logger[Task]) = + logger.debug( + s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}" + ) >> Task(node.attachChild(spatial)) + + /** + * Impure version + */ + def apply(spatial: Spatial)(implicit logger: SLogger) = + Coeval { + logger.debug( + s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}" + ) + node.attachChild(spatial) } + } +} + +object SpawnSystem { + sealed trait Result + final case object Ok extends Result + + sealed trait Complete + final case object Complete extends Complete + + sealed trait SpawnRequest + final case class SpawnSpatial(node: Node) extends SpawnRequest + + final case class SpawnRequestWrapper( + spawnRequest: SpawnRequest, + result: Deferred[Task, Result] + ) + + def apply(logger: Logger[Task]) = + for { + spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper] + spawnSystem <- Task(new SpawnSystem(logger, spawnChannel)) + consumer <- + spawnChannel.consume + .use(consumer => spawnSystem.receive(consumer)) + .startAndForget + } yield (spawnSystem) +} + +class SpawnSystem( + logger: Logger[Task], + spawnChannel: ConcurrentChannel[ + Task, + SpawnSystem.Complete, + SpawnSystem.SpawnRequestWrapper + ] +) { + import SpawnSystem._ + + for { + spawnSystem <- SpawnSystem(logger) + n <- spawnSystem.request(SpawnSpatial(new Node("Test"))) + } yield () + + // val spawnChannel = ConcurrentChannel[Task].of[Result, SpawnRequest] + + private def receive( + consumer: ConsumerF[Task, Complete, SpawnRequestWrapper] + ): Task[Unit] = + consumer.pull.flatMap { + case Right(message) => + for { + _ <- + logger + .debug(s"Received spawn request $message") + _ <- handleSpawn(message) + } yield receive(consumer) + case Left(r) => + logger.info("Worker $$index is done!") } - super.runQueuedTasks() - } + private def handleSpawn(spawnRequestWrapper: SpawnRequestWrapper) = + spawnRequestWrapper match { + case SpawnRequestWrapper(spawnRequest, result) => + spawnRequest match { + case SpawnSpatial(spatial) => + logger.debug( + s"Spawning spatial with name ${spatial.getName()}" + ) >> result + .complete(Ok) + + } + } - object JMEExecutorService extends GUIExecutorService { - override def execute(command: Runnable): Unit = - enqueueScala(() => command.run()) - // enqueue(command) - // new SingleThreadEventExecutor() - // sys.addShutdownHook(JMEExecutorService.shutdown()) - } + def request(spawnRequest: SpawnRequest) = + for { + d <- Deferred[Task, Result] + _ <- spawnChannel.push(SpawnRequestWrapper(spawnRequest, d)) + res <- d.get + } yield (res) - lazy val scheduler = Scheduler(JMEExecutorService) + def stop = spawnChannel.halt(Complete) } -object GameApp { - private[game] case class MyTask[T](p: Promise[T], cb: () => T) -} - -// val ship = ed.createEntity() -// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData()) -// val mbState = Try( -// stateManager() -// .state[TestAppState]() -// .entity -// ).toOption.flatten.toRight(println("empty")) -// // .flatMap(_.entity) -// val x = mbState.flatMap( -// _.query -// .filter[TestComponent]("name", new Object()) -// // .filterOr[TestEntity]( -// // Filters -// // .fieldEquals(classOf[TestEntity], "", null) -// // ) -// .component[Tag]() -// .component[TestComponent]() -// .result -// .toRight(println("failed")) -// ) - -// rootNode -// .child(geom) -// .child(geom) -// .child(geom) -// .child(geom) -// .child(geom) -// .child(geom) -// .child(geom) -// .child(geom) -// Future(println("hello"))(jmeEC(this)) -// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask( -// SpawnProtocol.Spawn( -// behavior = Greeter(), -// name = "listener", -// DispatcherSelector.fromConfig("jme-dispatcher"), -// _ -// ) -// ) - -// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println)) - -// Task(Promise[T]()).flatMap { p => -// Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >> -// Task.fromCancelablePromise(p) -// } -// Task.fromCancelablePromise { -// val p = Promise[T]() -// taskQueue2.transform(_ :+ MyTask(p, cb)) -// p -// } diff --git a/src/main/scala/wow/doge/mygame/game/GameApp2.scala b/src/main/scala/wow/doge/mygame/game/GameApp2.scala deleted file mode 100644 index 54a9f28..0000000 --- a/src/main/scala/wow/doge/mygame/game/GameApp2.scala +++ /dev/null @@ -1,71 +0,0 @@ -package wow.doge.mygame.game - -import cats.effect.concurrent.Ref -import com.jme3.app.state.AppStateManager -import com.jme3.asset.AssetManager -import com.jme3.input.InputManager -import monix.bio.IO -import monix.bio.Task -import com.jme3.scene.Node -import monix.catnap.Semaphore -import com.jme3.scene.Spatial -import wow.doge.mygame.game.GameApp2.SynchedObject -import wow.doge.mygame.game.subsystems.ui.JFxUI - -sealed trait Error -case object FlyCamNotExists extends Error - -class GameApp2(val app: GameApp) { - def stateManager: Task[AppStateManager] = Task(app.getStateManager()) - def inputManager: Task[InputManager] = Task(app.getInputManager()) - def assetManager: Task[AssetManager] = Task(app.getAssetManager()) - def guiNode = Ref[Task].of(app.getGuiNode()) - def flyCam = - IO(app.getFlyByCamera()).onErrorHandleWith(_ => - IO.raiseError(FlyCamNotExists) - ) - def camera = Task(app.getCamera()) - def viewPort = Task(app.getViewPort()) - def rootNode = Ref[Task].of(app.getRootNode()) - def rootNode2 = SynchedObject(app.getRootNode()) - def enqueue(cb: () => Unit) = - app.enqueue(new Runnable { - override def run() = cb() - }) - def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb) - - def start = Task(app.start()) - def stop = Task(app.stop()) - def scheduler = app.scheduler - def jfxUI = JFxUI(app) - -} - -object GameApp2 { - - class WrappedNode(node: Node, lock: Semaphore[Task]) { - - def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat))) - } - - /** - * Synchronization wrapper for a mutable object - * - * @param obj the mutable object - * @param lock lock for synchronization - */ - class SynchedObject[A](obj: A, lock: Semaphore[Task]) { - def modify(f: A => Unit): Task[Unit] = - lock.withPermit(Task(f(obj))) - - def flatModify(f: A => Task[Unit]): Task[Unit] = - lock.withPermit(f(obj)) - - def get: Task[A] = lock.withPermit(Task(obj)) - } - - object SynchedObject { - def apply[A](obj: A) = - Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock))) - } -} diff --git a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala index 89d3fbd..61ac299 100644 --- a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala +++ b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala @@ -1,53 +1,83 @@ package wow.doge.mygame.game -import akka.actor.typed.ActorRef -import akka.actor.typed.Scheduler -import akka.actor.typed.SpawnProtocol +import scala.concurrent.duration._ + +import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.Behaviors -import io.odin.Logger -import monix.bio.Task -import wow.doge.mygame.events.Events -import wow.doge.mygame.executors.Schedulers +import wow.doge.mygame.game.TickGenerator.Send +import wow.doge.mygame.game.entities.GenericTimerActor +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus +import wow.doge.mygame.subsystems.events.TickEvent +import wow.doge.mygame.subsystems.events.TickEvent.PhysicsTick object GameAppActor { sealed trait Command - case object ApplicationStarted extends Command - case class ApplicationStartFailed(reason: String) extends Command + case object Start extends Command + case object Pause extends Command case object Stop extends Command case class Props( - app: GameApp, - akkaScheduler: Scheduler, - schedulers: Schedulers, - spawnProtocol: ActorRef[SpawnProtocol.Command], - loggerL: Logger[Task] + // app: SimpleAppExt, + // akkaScheduler: Scheduler, + // schedulers: Schedulers, + // spawnProtocol: ActorRef[SpawnProtocol.Command], + // loggerL: Logger[Task] + tickEventBus: GameEventBus[TickEvent] ) { def create = Behaviors.setup[Command] { ctx => ctx.log.info("Hello from GameAppActor") + val renderTickGenerator = + ctx.spawn( + Behaviors + .supervise(renderTickGeneratorBehavior) + .onFailure[Exception](SupervisorStrategy.restart), + "tickGeneratorActor" + ) + + val tickGeneratorTimer = ctx.spawn( + GenericTimerActor + .Props(renderTickGenerator, TickGenerator.Send, 10.millis) + .create, + "tickGeneratorTimer" + ) + + Behaviors.receiveMessage { + case Start => + tickGeneratorTimer ! GenericTimerActor.Start + Behaviors.same + case Pause => + tickGeneratorTimer ! GenericTimerActor.Stop + Behaviors.same + case Stop => + ctx.log.info("Received stop") + tickGeneratorTimer ! GenericTimerActor.Stop + Behaviors.stopped - Behaviors.receiveMessage { msg => - msg match { - case Stop => - ctx.log.info("Received stop") - Behaviors.stopped - case ApplicationStarted => - ctx.log.info("Application started") - Behaviors.same - case ApplicationStartFailed(reason) => - ctx.log.error( - s"Failed to start application - $reason" - ) - Behaviors.stopped - } } } + + val renderTickGeneratorBehavior = + Behaviors.receiveMessage[TickGenerator.Command] { + case Send => + tickEventBus ! EventBus.Publish( + TickEvent.RenderTick, + "tickGeneratorActor" + ) + Behaviors.same + } } } + +object TickGenerator { + sealed trait Command + case object Send extends Command +} object SubscribingActor { def apply() = - Behaviors.receive[Events.Tick.PhysicsTick.type] { (ctx, msg) => + Behaviors.receive[PhysicsTick.type] { (ctx, msg) => ctx.log.debug(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 c7378e2..d29fae7 100644 --- a/src/main/scala/wow/doge/mygame/game/GameModule.scala +++ b/src/main/scala/wow/doge/mygame/game/GameModule.scala @@ -1,48 +1,24 @@ package wow.doge.mygame.game -import akka.actor.typed.ActorRef -import akka.actor.typed.SpawnProtocol import cats.effect.Resource import com.jme3.app.StatsAppState import com.jme3.system.AppSettings import io.odin.Logger -import monix.bio.Fiber -import monix.bio.IO import monix.bio.Task import monix.execution.Scheduler import wow.doge.mygame.executors.Schedulers -import wow.doge.mygame.game.subsystems.input.GameInputHandler class GameAppResource( logger: Logger[Task], jmeScheduler: Scheduler, schedulers: Schedulers ) { - def get2: Resource[Task, (GameApp2, Fiber[Throwable, Unit])] = - Resource.make( - for { - _ <- logger.info("Creating game app") - app <- Task(new GameApp(schedulers, new StatsAppState())) - app2 <- Task { - val settings = new AppSettings(true) - settings.setVSync(true) - settings.setUseInput(true) - // new FlyCamAppState - // settings.setFrameRate(250) - app.setSettings(settings) - // JMERunner.runner = app - // app - new GameApp2(app) - } - fib <- app2.start.executeOn(jmeScheduler).start - } yield (app2 -> fib) - )(logger.info("Closing game app") >> _._2.cancel) - def get: Resource[Task, GameApp2] = + def get: Resource[Task, GameApp] = Resource.make( for { _ <- logger.info("Creating game app") - app <- Task(new GameApp(schedulers, new StatsAppState())) - app2 <- Task { + appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState())) + app <- Task { val settings = new AppSettings(true) settings.setVSync(true) @@ -50,48 +26,11 @@ class GameAppResource( * disables the launcher * We'll be making our own launcher anyway */ - app.setShowSettings(false) - app.setSettings(settings) + appExt.setShowSettings(false) + appExt.setSettings(settings) // JMERunner.runner = app - new GameApp2(app) + new GameApp(logger, appExt) } - // fib <- app2.start.executeOn(jmeScheduler).start - } yield (app2) + } yield (app) )(_ => logger.info("Closing game app")) } - -trait GameModule { - - def gameAppResource( - logger: Logger[Task], - jmeScheduler: Scheduler, - schedulers: Schedulers - ): Resource[Task, (GameApp, Fiber[Throwable, Unit])] = - Resource.make( - (for { - _ <- logger.info("Creating game app") - app <- Task(new GameApp(schedulers)) - _ <- Task { - val settings = new AppSettings(true) - settings.setVSync(true) - // settings.setFrameRate(250) - app.setSettings(settings) - // JMERunner.runner = app - app - } - fib <- Task(app.start()).executeOn(jmeScheduler).start - } yield (app -> fib)) - )(_._2.cancel) - - def inputHandlerSystemResource( - props: GameInputHandler.Props - ): Resource[Task, Task[Unit]] = - Resource.liftF { - Task.evalAsync(props.begin) - } - def gameSystemsResource( - spawnProtocol: ActorRef[SpawnProtocol.Command], - gameSystems: Task[Unit]* - ): Resource[Task, List[Unit]] = - Resource.liftF(IO.defer(Task.parSequence(gameSystems))) -} diff --git a/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala b/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala deleted file mode 100644 index ead2fc0..0000000 --- a/src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala +++ /dev/null @@ -1,106 +0,0 @@ -package wow.doge.mygame.game - -import scala.concurrent.duration._ - -import akka.actor.typed.ActorRef -import akka.actor.typed.ActorSystem -import akka.actor.typed.Scheduler -import akka.actor.typed.SpawnProtocol -import akka.util.Timeout -import cats.effect.concurrent.Ref -import com.jme3.app.state.AppStateManager -import com.jme3.asset.AssetManager -import com.jme3.asset.plugins.ZipLocator -import com.jme3.bullet.BulletAppState -import com.jme3.input.InputManager -import com.jme3.renderer.Camera -import com.jme3.scene.Node -import com.softwaremill.macwire._ -import com.softwaremill.tagging._ -import io.odin.Logger -import monix.bio.IO -import monix.bio.Task -import monix.reactive.Consumer -import wow.doge.mygame.events.EventBus -import wow.doge.mygame.game.nodes.PlayerTag -import wow.doge.mygame.game.nodes.PlayerController -import wow.doge.mygame.game.subsystems.input.GameInputHandler -import wow.doge.mygame.implicits._ -import wow.doge.mygame.math.ImVector3f -import wow.doge.mygame.subsystems.events.EntityMovementEvent -import wow.doge.mygame.subsystems.events.PlayerCameraEvent -import wow.doge.mygame.subsystems.movement.ImMovementActor -import wow.doge.mygame.utils.IOUtils - -class GameSystemsInitializer( - spawnProtocol: ActorSystem[SpawnProtocol.Command], - loggerL: Logger[Task], - // eventBuses: EventsModule2 - playerMovementEventBus: ActorRef[ - EventBus.Command[EntityMovementEvent.PlayerMovementEvent] - ], - playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], - inputManager: InputManager, - assetManager: AssetManager, - stateManager: AppStateManager, - camera: Camera, - enqueueR: Function1[() => Unit, Unit], - appScheduler: monix.execution.Scheduler, - rootNode: Ref[Task, Node] -) { - implicit val timeout: Timeout = Timeout(1.second) - - import GameSystemsInitializer._ - - 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 { - _ <- loggerL.info("Initializing Systems") - // playerMovementEventBus <- playerMovementEventBusTask - _ <- Task(stateManager.attach(bulletAppState)) - _ <- Task( - assetManager.registerLocator( - // "src/main/resources/assets/town.zip", - (os.rel / "assets" / "town.zip"), - classOf[ZipLocator] - ) - ) - - // _ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState)) - _ <- wireWith(createPlayerController _).startAndForget - _ <- wire[GameInputHandler.Props].begin - - } yield () - - def createPlayerController( - // playerMovementEventBus: ActorRef[ - // EventBus.Command[PlayerMovementEvent] - // ], - // playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] - ): IO[PlayerController.Error, Unit] = { - @annotation.unused - val playerPos = ImVector3f.ZERO - @annotation.unused - val playerNode = None.taggedWith[PlayerTag] - @annotation.unused - val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" - wire[PlayerController.Props].create - } -} - -object GameSystemsInitializer { - - def playerMovementActorTickConsumer( - playerMovementActor: ActorRef[ImMovementActor.Command] - ) = - Consumer - .foreachTask[Float](tpf => - IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick) - ) - // .mapTask() -} diff --git a/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala b/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala new file mode 100644 index 0000000..ce541b1 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala @@ -0,0 +1,132 @@ +package wow.doge.mygame.game + +import scala.collection.immutable.Queue + +import com.jme3.app.SimpleApplication +import com.jme3.app.state.AppState +import monix.bio.Task +import monix.execution.CancelableFuture +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, + appStates: AppState* +) extends SimpleApplication(appStates: _*) { + import SimpleAppExt._ + + /** + * A non blocking synchronized queue using an immutable scala queue and monix's atomic class + */ + private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]]) + + private val tickSubject = + ConcurrentSubject[Float](multicast = MulticastStrategy.publish)( + schedulers.async + ) + + def tickObservable: Observable[Float] = tickSubject + + override def simpleInitApp(): Unit = {} + + override def simpleUpdate(tpf: Float): Unit = { + tickSubject.onNext(tpf) + } + + override def stop(): Unit = { + tickSubject.onComplete() + super.stop() + } + + def enqueueScala[T](cb: () => T): CancelableFuture[T] = { + val p = Promise[T]() + taskQueue2.transform(_ :+ MyTask(p, cb)) + p.future + } + + def enqueueL[T](cb: () => T): Task[T] = + Task.deferFuture(enqueueScala(cb)) + + override protected def runQueuedTasks(): Unit = { + taskQueue2.transform { current => + current.dequeueOption.fold(current) { + case (MyTask(p, cb), updated) => + p.success(cb()) + updated + } + } + + super.runQueuedTasks() + } + + object JMEExecutorService extends GUIExecutorService { + override def execute(command: Runnable): Unit = + enqueueScala(() => command.run()) + // enqueue(command) + // new SingleThreadEventExecutor() + // sys.addShutdownHook(JMEExecutorService.shutdown()) + } + + lazy val scheduler = Scheduler(JMEExecutorService) +} +object SimpleAppExt { + private[game] case class MyTask[T](p: Promise[T], cb: () => T) +} + +// val ship = ed.createEntity() +// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData()) +// val mbState = Try( +// stateManager() +// .state[TestAppState]() +// .entity +// ).toOption.flatten.toRight(println("empty")) +// // .flatMap(_.entity) +// val x = mbState.flatMap( +// _.query +// .filter[TestComponent]("name", new Object()) +// // .filterOr[TestEntity]( +// // Filters +// // .fieldEquals(classOf[TestEntity], "", null) +// // ) +// .component[Tag]() +// .component[TestComponent]() +// .result +// .toRight(println("failed")) +// ) + +// rootNode +// .child(geom) +// .child(geom) +// .child(geom) +// .child(geom) +// .child(geom) +// .child(geom) +// .child(geom) +// .child(geom) +// Future(println("hello"))(jmeEC(this)) +// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask( +// SpawnProtocol.Spawn( +// behavior = Greeter(), +// name = "listener", +// DispatcherSelector.fromConfig("jme-dispatcher"), +// _ +// ) +// ) + +// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println)) + +// Task(Promise[T]()).flatMap { p => +// Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >> +// Task.fromCancelablePromise(p) +// } +// Task.fromCancelablePromise { +// val p = Promise[T]() +// taskQueue2.transform(_ :+ MyTask(p, cb)) +// p +// } diff --git a/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala new file mode 100644 index 0000000..dd97885 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala @@ -0,0 +1,225 @@ +package wow.doge.mygame.game.entities + +import akka.actor.typed.ActorRef +import akka.actor.typed.Behavior +import akka.actor.typed.SupervisorStrategy +import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.scaladsl.Behaviors +import wow.doge.mygame.subsystems.events.Event +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus +import wow.doge.mygame.subsystems.events.TickEvent +import wow.doge.mygame.subsystems.events.TickEvent.RenderTick +import wow.doge.mygame.subsystems.movement.ImMovementActor +import wow.doge.mygame.subsystems.events.EntityMovementEvent +import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedLeft +import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedUp +import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedRight +import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedDown +import wow.doge.mygame.math.ImVector3f +import wow.doge.mygame.game.subsystems.movement.CanMove +import wow.doge.mygame.implicits._ +import akka.util.Timeout +import scala.concurrent.duration._ +import scala.util.Success +import scala.util.Failure + +object NpcActorSupervisor { + sealed trait Command + case class Move(pos: ImVector3f) extends Command + private case class UpdatePosition(pos: ImVector3f) extends Command + private case class LogError(err: Throwable) extends Command + case object MovementTick extends Command + + final case class Props( + npcMovementActorBehavior: Behavior[NpcMovementActor2.Command], + npcName: String, + initialPos: ImVector3f + ) { + def create = + Behaviors.setup[Command] { ctx => + val npcMovementActor = ctx.spawn( + (npcMovementActorBehavior), + s"npc-${npcName}-NpcMovementActor" + ) + + new NpcActorSupervisor(ctx, this) + .idle(State(npcMovementActor, initialPos)) + } + } + final case class State( + npcMovementActor: ActorRef[NpcMovementActor2.Command], + currentPos: ImVector3f + ) +} +class NpcActorSupervisor( + ctx: ActorContext[NpcActorSupervisor.Command], + props: NpcActorSupervisor.Props +) { + import NpcActorSupervisor._ + implicit val timeout = Timeout(1.second) + def idle(state: State): Behavior[NpcActorSupervisor.Command] = + Behaviors.receiveMessage[Command] { + case Move(pos) => { + state.npcMovementActor ! NpcMovementActor2.Move(pos) + val movementTimer = ctx.spawn( + GenericTimerActor.Props(ctx.self, MovementTick, 100.millis).create, + s"npc-${props.npcName}-NpcActorTimer" + ) + movementTimer ! GenericTimerActor.Start + moving(state, pos, movementTimer) + } + case LogError(err) => + ctx.log.warn(err.getMessage()) + Behaviors.same + case _ => Behaviors.unhandled + } + + def moving( + state: State, + targetPos: ImVector3f, + movementTimer: ActorRef[GenericTimerActor.Command] + ): Behavior[NpcActorSupervisor.Command] = + Behaviors.receiveMessagePartial[Command] { + case LogError(err) => + ctx.log.warn(err.getMessage()) + Behaviors.same + case Move(pos) => moving(state, pos, movementTimer) + case UpdatePosition(pos) => + ctx.log.trace("Current pos = " + state.currentPos.toString()) + moving(state.copy(currentPos = pos), targetPos, movementTimer) + case MovementTick => + val dst = ImVector3f.dst(targetPos, state.currentPos) + if (dst <= 10f) { + state.npcMovementActor ! NpcMovementActor2.StopMoving + movementTimer ! GenericTimerActor.Stop + idle(state) + } else { + // ctx.log.debug("Difference = " + dst.toString()) + // ctx.log.debug("Current pos = " + state.currentPos.toString()) + + ctx.ask(state.npcMovementActor, NpcMovementActor2.AskPosition(_)) { + case Success(value) => + UpdatePosition(value) + case Failure(exception) => LogError(exception) + } + // Behaviors.same + moving(state, targetPos, movementTimer) + } + } +} + +object NpcMovementActor2 { + sealed trait Command + case class AskPosition(replyTo: ActorRef[ImVector3f]) extends Command + case object StopMoving extends Command + case class Move(target: ImVector3f) extends Command + + final class Props[T: CanMove]( + val enqueueR: Function1[() => Unit, Unit], + val initialPos: ImVector3f, + val tickEventBus: GameEventBus[TickEvent], + val movable: T + ) { + def create = + Behaviors.setup[Command] { ctx => + new NpcMovementActor2(ctx, this).receive(State(initialPos)) + } + } + + final case class State(currentPos: ImVector3f) +} +class NpcMovementActor2[T]( + ctx: ActorContext[NpcMovementActor2.Command], + props: NpcMovementActor2.Props[T] +) { + import NpcMovementActor2._ + def receive( + state: State + )(implicit cm: CanMove[T]): Behavior[NpcMovementActor2.Command] = + Behaviors.receiveMessage[Command] { + case AskPosition(replyTo) => + replyTo ! cm.location(props.movable) + Behaviors.same + case Move(target: ImVector3f) => + props.enqueueR(() => + cm.move(props.movable, (target - state.currentPos) * 0.005f) + ) + receive(state = state.copy(currentPos = cm.location(props.movable))) + case StopMoving => + ctx.log.debug( + "Position at Stop = " + cm.location(props.movable).toString + ) + props.enqueueR(() => cm.stop(props.movable)) + receive(state = state.copy(currentPos = cm.location(props.movable))) + } +} + +object NpcMovementActor { + sealed trait Command + final case class Props( + imMovementActorBehavior: Behavior[ImMovementActor.Command], + npcName: String, + // movementActor: ActorRef[ImMovementActor.Command], + mainEventBus: ActorRef[ + EventBus.Command[Event] + ], + tickEventBus: GameEventBus[TickEvent] + ) { + + def create: Behavior[Command] = + Behaviors.setup { ctx => + val movementActor = ctx.spawn( + Behaviors + .supervise(imMovementActorBehavior) + .onFailure[Exception](SupervisorStrategy.restart), + s"npc-${npcName}-MovementActorChild" + ) + val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] { + case event: EntityMovementEvent => + event match { + case MovedLeft(name, pressed) => + if (name == npcName) + movementActor ! ImMovementActor.MovedLeft(pressed) + Behaviors.same + case MovedUp(name, pressed) => + if (name == npcName) + movementActor ! ImMovementActor.MovedUp(pressed) + Behaviors.same + case MovedRight(name, pressed) => + if (name == npcName) + movementActor ! ImMovementActor.MovedRight(pressed) + Behaviors.same + case MovedDown(name, pressed) => + if (name == npcName) + movementActor ! ImMovementActor.MovedDown(pressed) + Behaviors.same + } + } + + val npcMovementEl = ctx.spawn( + Behaviors + .supervise(npcMovementElBehavior) + .onFailure[Exception](SupervisorStrategy.restart), + s"npc-${npcName}-MovementEventHandler" + ) + val renderTickElBehavior = + Behaviors.receiveMessage[RenderTick.type] { + case RenderTick => + movementActor ! ImMovementActor.Tick + Behaviors.same + } + + val renderTickEl = + ctx.spawn( + renderTickElBehavior, + s"npc-${npcName}-MovementTickListener" + ) + + mainEventBus ! EventBus.Subscribe(npcMovementEl) + tickEventBus ! EventBus.Subscribe(renderTickEl) + Behaviors.receiveMessage { msg => Behaviors.same } + } + + } +} diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala similarity index 54% rename from src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala rename to src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala index 1648ea4..0863763 100644 --- a/src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala @@ -1,6 +1,7 @@ -package wow.doge.mygame.game.nodes +package wow.doge.mygame.game.entities import scala.concurrent.duration._ +import scala.util.Random import akka.actor.typed.ActorRef import akka.actor.typed.Behavior @@ -9,26 +10,27 @@ import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.TimerScheduler -import com.jme3.scene.CameraNode import com.typesafe.scalalogging.Logger import org.slf4j.event.Level -import wow.doge.mygame.events.EventBus import wow.doge.mygame.game.subsystems.movement.CanMove -import wow.doge.mygame.subsystems.events.EntityMovementEvent -import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.PlayerCameraEvent +import wow.doge.mygame.subsystems.events.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.TickEvent +import wow.doge.mygame.subsystems.events.TickEvent.RenderTick import wow.doge.mygame.subsystems.movement.ImMovementActor object PlayerActorSupervisor { sealed trait Command - final case class Props( - enqueueR: Function1[() => Unit, Unit], - camNode: CameraNode, playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] ], - playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], + tickEventBus: GameEventBus[TickEvent], + imMovementActorBehavior: Behavior[ImMovementActor.Command], + playerCamELBehavior: Behavior[PlayerCameraEvent] ) { def create[T: CanMove](movable: T) = Behaviors.logMessages( @@ -44,59 +46,40 @@ object PlayerActorSupervisor { lazy val movementActor = ctx.spawn( Behaviors - .supervise( - ImMovementActor - .Props(enqueueR, movable, playerMovementEventBus) - .create - ) + .supervise(imMovementActorBehavior) .onFailure[Exception](SupervisorStrategy.restart), - "playerMovementActor" + "playerMovementActorChild" ) - 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" + ctx.spawn( + PlayerMovementActor + .Props(movementActor, playerMovementEventBus, tickEventBus) + .create, + "playerMovementAcor" ) - lazy val playerCameraHandler = { ctx.spawn( Behaviors - .supervise( - PlayerCameraEventListener(camNode, enqueueR) - ) + .supervise(playerCamELBehavior) .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) + Children(movementActor) ).receive } ) + } case class Children( - movementActor: ActorRef[ImMovementActor.Command], - playerMovementEventHandler: ActorRef[ - EntityMovementEvent.PlayerMovementEvent - ] + movementActor: ActorRef[ImMovementActor.Command] ) } class PlayerActorSupervisor[T: CanMove]( @@ -113,47 +96,40 @@ class PlayerActorSupervisor[T: CanMove]( } } -object MovementActorTimer { +object PlayerMovementActor { 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 + final case class Props( + movementActor: ActorRef[ImMovementActor.Command], + playerMovementEventBus: ActorRef[ + EventBus.Command[PlayerMovementEvent] + ], + tickEventBus: GameEventBus[TickEvent] + ) { + def create: Behavior[Command] = + Behaviors.setup { ctx => + val playerMovementEl = ctx.spawn( + Behaviors + .supervise(PlayerMovementEventListener(movementActor)) + .onFailure[Exception](SupervisorStrategy.restart), + "playerMovementEventHandler" + ) + val renderTickElBehavior = + Behaviors.receiveMessage[RenderTick.type] { + case RenderTick => + movementActor ! ImMovementActor.Tick + Behaviors.same + } + val renderTickEl = + ctx.spawn(renderTickElBehavior, "playerMovementTickListener") + + playerMovementEventBus ! EventBus.Subscribe( + playerMovementEl + ) + tickEventBus ! EventBus.Subscribe(renderTickEl) + Behaviors.receiveMessage { msg => Behaviors.same } } - } + } } object GenericTimerActor { @@ -161,7 +137,7 @@ object GenericTimerActor { final case object Start extends Command final case object Stop extends Command private case object Send extends Command - case object TimerKey + case class TimerKey(seed: Long) case class Props[T]( target: ActorRef[T], @@ -169,12 +145,13 @@ object GenericTimerActor { timeInterval: FiniteDuration ) { val create = Behaviors.withTimers[Command] { timers => - new GenericTimerActor(timers, this).idle + new GenericTimerActor(timers, TimerKey(Random.nextLong()), this).idle } } } class GenericTimerActor[T]( timers: TimerScheduler[GenericTimerActor.Command], + timerKey: GenericTimerActor.TimerKey, props: GenericTimerActor.Props[T] ) { import GenericTimerActor._ @@ -182,7 +159,7 @@ class GenericTimerActor[T]( val idle: Behavior[Command] = Behaviors.receiveMessage { case Start => - timers.startTimerWithFixedDelay(TimerKey, Send, props.timeInterval) + timers.startTimerWithFixedDelay(timerKey, Send, props.timeInterval) active case _ => Behaviors.unhandled @@ -194,7 +171,7 @@ class GenericTimerActor[T]( props.target ! props.messageToSend Behaviors.same case Stop => - timers.cancel(TimerKey) + 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/entities/player/PlayerCameraActor.scala similarity index 94% rename from src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala rename to src/main/scala/wow/doge/mygame/game/entities/player/PlayerCameraActor.scala index 5b3657f..0b3b966 100644 --- a/src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerCameraActor.scala @@ -1,4 +1,4 @@ -package wow.doge.mygame.game.nodes +package wow.doge.mygame.game.entities import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors 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 new file mode 100644 index 0000000..e22ca0e --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala @@ -0,0 +1,248 @@ +package wow.doge.mygame.game.entities + +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 cats.implicits._ +import com.jme3.asset.AssetManager +import com.jme3.bullet.BulletAppState +import com.jme3.bullet.PhysicsSpace +import com.jme3.bullet.control.BetterCharacterControl +import com.jme3.math.FastMath +import com.jme3.renderer.Camera +import com.jme3.scene.CameraNode +import com.jme3.scene.Geometry +import com.jme3.scene.Node +import com.jme3.scene.control.CameraControl.ControlDirection +import com.jme3.scene.shape.Box +import com.softwaremill.macwire._ +import com.softwaremill.tagging._ +import io.odin.Logger +import monix.bio.IO +import monix.bio.Task +import wow.doge.mygame.game.GameAppTags +import wow.doge.mygame.game.SimpleAppExt +import wow.doge.mygame.implicits._ +import wow.doge.mygame.math.ImVector3f +import wow.doge.mygame.state.MyMaterial +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus +import wow.doge.mygame.subsystems.events.PlayerCameraEvent +import wow.doge.mygame.subsystems.events.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.TickEvent +import wow.doge.mygame.subsystems.movement.ImMovementActor +import wow.doge.mygame.utils.AkkaUtils + +object PlayerControllerTags { + sealed trait PlayerTag + sealed trait PlayerCameraNode +} + +object PlayerController { + sealed trait Error + case class CouldNotCreatePlayerNode(reason: String) extends Error + case class GenericError(reason: String) extends Error + + class Props( + enqueueR: Function1[() => Unit, Unit], + rootNode: Node @@ GameAppTags.RootNode, + loggerL: Logger[Task], + physicsSpace: PhysicsSpace, + initialPlayerPos: ImVector3f = ImVector3f.ZERO, + spawnProtocol: ActorRef[SpawnProtocol.Command], + playerMovementEventBus: ActorRef[ + EventBus.Command[PlayerMovementEvent] + ], + playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], + playerPhysicsControl: BetterCharacterControl, + appScheduler: monix.execution.Scheduler, + playerNode: Node @@ PlayerControllerTags.PlayerTag, + cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode, + tickEventBus: GameEventBus[TickEvent] + )(implicit timeout: Timeout, scheduler: Scheduler) { + val create: IO[Error, Unit] = + (for { + playerActor <- AkkaUtils.spawnActorL( + spawnProtocol, + "playerActorSupervisor", + new PlayerActorSupervisor.Props( + playerMovementEventBus, + playerCameraEventBus, + tickEventBus, + ImMovementActor + .Props(enqueueR, playerPhysicsControl) + .create, + wireWith(PlayerCameraEventListener.apply _) + ).create(playerPhysicsControl) + ) + _ <- IO { + physicsSpace += playerNode + physicsSpace += playerPhysicsControl + } + _ <- Task(rootNode += playerNode) + } yield ()) + .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage()))) + .executeOn(appScheduler) + } + + def apply( + app: SimpleAppExt, + modelPath: os.RelPath, + cam: Camera + )(assetManager: AssetManager, bulletAppState: BulletAppState) = { + lazy val playerPos = ImVector3f.ZERO + lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) + .withJumpForce(ImVector3f(0, 5f, 0)) + lazy val playerNode = new Node("PlayerNode") + .withChildren( + assetManager + .loadModel(modelPath) + .asInstanceOf[Node] + .withRotate(0, FastMath.PI, 0) + ) + .withLocalTranslation(playerPos) + .withControl(playerPhysicsControl) + + { + bulletAppState.physicsSpace += playerNode + bulletAppState.physicsSpace += playerPhysicsControl + } + + { + app.rootNode += playerNode + } + + playerPhysicsControl + } + + object Defaults { + def defaultMesh = { + val b = Box(1, 1, 1) + val geom = Geometry("playerGeom", b) + geom + } + def defaultTexture(assetManager: AssetManager) = + MyMaterial( + assetManager = assetManager, + path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" + ) + + def defaultCamerNode(cam: Camera, playerPos: ImVector3f) = + new CameraNode("CameraNode", cam) + .withControlDir(ControlDirection.SpatialToCamera) + .withLocalTranslation(ImVector3f(0, 1.5f, 10)) + .withLookAt(playerPos, ImVector3f.UNIT_Y) + + 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) + ) + .map(_.taggedWith[PlayerControllerTags.PlayerTag]) + .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage())) + + def defaultNpcNode( + assetManager: AssetManager, + // modelPath: os.RelPath, + initialPos: ImVector3f, + npcPhysicsControl: BetterCharacterControl, + npcName: String + ) = + Either + .catchNonFatal( + Node(npcName) + .withChildren( + // assetManager + // .loadModel(modelPath) + // .asInstanceOf[Node] + // .withRotate(0, FastMath.PI, 0) + defaultMesh.withMaterial(defaultTexture(assetManager)) + ) + .withLocalTranslation(initialPos) + .withControl(npcPhysicsControl) + ) + // .map(_.taggedWith[PlayerControllerTags.PlayerTag]) + // .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage())) + + def defaultPlayerPhysicsControl = + new BetterCharacterControl(1.5f, 6f, 1f) + .withJumpForce(ImVector3f(0, 5f, 0)) + } +} + +object Methods { + def spawnMovementActor( + enqueueR: Function1[() => Unit, Unit], + spawnProtocol: ActorRef[SpawnProtocol.Command], + movable: BetterCharacterControl @@ PlayerControllerTags.PlayerTag, + playerMovementEventBus: ActorRef[ + EventBus.Command[PlayerMovementEvent] + ], + loggerL: Logger[Task] + )(implicit timeout: Timeout, scheduler: Scheduler) = + spawnProtocol.askL[ActorRef[ImMovementActor.Command]]( + SpawnProtocol.Spawn( + ImMovementActor.Props(enqueueR, movable).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, + // _ + // ) + // ) +} + +// camNode <- IO( +// _cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos)) +// ) +// playerPhysicsControl <- IO( +// _playerPhysicsControl +// .getOrElse(defaultPlayerPhysicsControl) +// .taggedWith[PlayerTag] +// ) +// playerNode <- IO.fromEither( +// _playerNode.fold( +// defaultPlayerNode( +// assetManager, +// modelPath, +// initialPlayerPos, +// camNode, +// playerPhysicsControl +// ) +// )(_.asRight) +// ) diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala similarity index 91% rename from src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala rename to src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala index 2b49910..9389198 100644 --- a/src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala @@ -1,4 +1,4 @@ -package wow.doge.mygame.game.nodes +package wow.doge.mygame.game.entities import akka.actor.typed.ActorRef import akka.actor.typed.LogOptions @@ -6,10 +6,10 @@ import akka.actor.typed.scaladsl.Behaviors import com.jme3.scene.CameraNode import com.typesafe.scalalogging.Logger import org.slf4j.event.Level -import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedDown import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp +import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.movement.ImMovementActor object PlayerMovementEventListener { @@ -39,11 +39,9 @@ object PlayerMovementEventListener { movementActor ! ImMovementActor.Jump Behaviors.same case PlayerRotatedRight => - // ctx.log.warn("right rotate not implemented yet") movementActor ! ImMovementActor.RotateRight Behaviors.same case PlayerRotatedLeft => - // ctx.log.warn("left rotate not implemented yet") movementActor ! ImMovementActor.RotateLeft Behaviors.same case PlayerCameraUp => diff --git a/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala b/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala deleted file mode 100644 index 2dcc339..0000000 --- a/src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala +++ /dev/null @@ -1,222 +0,0 @@ -package wow.doge.mygame.game.nodes - -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 cats.effect.concurrent.Ref -import cats.implicits._ -import com.jme3.asset.AssetManager -import com.jme3.bullet.BulletAppState -import com.jme3.bullet.control.BetterCharacterControl -import com.jme3.math.FastMath -import com.jme3.renderer.Camera -import com.jme3.scene.CameraNode -import com.jme3.scene.Geometry -import com.jme3.scene.Node -import com.jme3.scene.control.CameraControl.ControlDirection -import com.jme3.scene.shape.Box -import com.softwaremill.tagging._ -import io.odin.Logger -import monix.bio.IO -import monix.bio.Task -import wow.doge.mygame.events.EventBus -import wow.doge.mygame.game.GameApp -import wow.doge.mygame.implicits._ -import wow.doge.mygame.math.ImVector3f -import wow.doge.mygame.state.MyMaterial -import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent -import wow.doge.mygame.subsystems.events.PlayerCameraEvent -import wow.doge.mygame.subsystems.movement.ImMovementActor -import wow.doge.mygame.utils.AkkaUtils - -// class PlayerNode(val name: String) extends Node(name) {} -sealed trait PlayerTag -sealed trait PlayerCameraNode - -object PlayerController { - sealed trait Error - case class GenericError(reason: String) extends Error - - class Props( - enqueueR: Function1[() => Unit, Unit], - rootNode: Ref[Task, Node], - camera: Camera, - 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 PlayerTag] = None, - _cameraNode: Option[CameraNode with PlayerCameraNode] = None, - appScheduler: monix.execution.Scheduler - )(implicit timeout: Timeout, scheduler: Scheduler) { - import Defaults._ - val create: IO[Error, Unit] = - // IO.raiseError(GenericError("not implemented yet")) - (for { - camNode <- IO( - _cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos)) - ) - playerPhysicsControl <- IO( - _playerPhysicsControl - .getOrElse(defaultPlayerPhysicsControl) - .taggedWith[PlayerTag] - ) - playerNode <- IO.fromEither( - _playerNode.fold( - defaultPlayerNode( - assetManager, - modelPath, - initialPlayerPos, - camNode, - playerPhysicsControl - ) - )(_.asRight) - ) - playerActor <- AkkaUtils.spawnActorL( - spawnProtocol, - "playerActorSupervisor", - new PlayerActorSupervisor.Props( - enqueueR, - camNode, - playerMovementEventBus, - playerCameraEventBus - ).create(playerPhysicsControl) - ) - _ <- IO { - bulletAppState.physicsSpace += playerNode - bulletAppState.physicsSpace += playerPhysicsControl - } - _ <- rootNode.update(_ :+ playerNode) - } yield ()) - .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage()))) - .executeOn(appScheduler) - } - - def apply( - app: GameApp, - modelPath: os.RelPath, - cam: Camera - )(assetManager: AssetManager, bulletAppState: BulletAppState) = { - lazy val playerPos = ImVector3f.ZERO - lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) - .withJumpForce(ImVector3f(0, 5f, 0)) - lazy val playerNode = new Node("PlayerNode") - .withChildren( - assetManager - .loadModel(modelPath) - .asInstanceOf[Node] - .withRotate(0, FastMath.PI, 0) - ) - .withLocalTranslation(playerPos) - .withControl(playerPhysicsControl) - - { - bulletAppState.physicsSpace += playerNode - bulletAppState.physicsSpace += playerPhysicsControl - } - - { - app.rootNode += playerNode - } - - playerPhysicsControl - } -} - -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" - ) - - def defaultCamerNode(cam: Camera, playerPos: ImVector3f) = - new CameraNode("CameraNode", cam) - .withControlDir(ControlDirection.SpatialToCamera) - .withLocalTranslation(ImVector3f(0, 1.5f, 10)) - .withLookAt(playerPos, ImVector3f.UNIT_Y) - - 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) - ) - - lazy val defaultPlayerPhysicsControl = - new BetterCharacterControl(1.5f, 6f, 1f) - .withJumpForce(ImVector3f(0, 5f, 0)) -} - -object Methods { - def spawnMovementActor( - enqueueR: Function1[() => Unit, Unit], - spawnProtocol: ActorRef[SpawnProtocol.Command], - movable: BetterCharacterControl @@ PlayerTag, - playerMovementEventBus: ActorRef[ - EventBus.Command[PlayerMovementEvent] - ], - loggerL: Logger[Task] - )(implicit timeout: Timeout, scheduler: Scheduler) = - spawnProtocol.askL[ActorRef[ImMovementActor.Command]]( - SpawnProtocol.Spawn( - ImMovementActor.Props(enqueueR, 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/subsystems/ai/GdxAiTest.scala b/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala index 124a72d..a5765df 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala @@ -1,8 +1,13 @@ package wow.doge.mygame.game.subsystems.ai +import scala.collection.immutable.ArraySeq + import com.badlogic.gdx.ai.pfa.Connection import wow.doge.mygame.game.subsystems.ai.gdx.MyIndexedGraph -import scala.collection.immutable.ArraySeq +import com.badlogic.gdx.ai.steer.Steerable +import com.badlogic.gdx.math.Vector3 +import com.badlogic.gdx.ai.utils.Location +import com.badlogic.gdx.ai.steer.behaviors.Arrive // import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph // import scala.jdk.javaapi.CollectionConverters._ @@ -31,3 +36,50 @@ class CityGraph extends MyIndexedGraph[City] { override def getNodeCount(): Int = ??? } + +class MySteerable extends Steerable[Vector3] { + val arrive = new Arrive[Vector3](this) + + override def getPosition(): Vector3 = ??? + + override def getOrientation(): Float = ??? + + override def setOrientation(x$1: Float): Unit = ??? + + override def vectorToAngle(x$1: Vector3): Float = ??? + + override def angleToVector(x$1: Vector3, x$2: Float): Vector3 = ??? + + override def newLocation(): Location[Vector3] = ??? + + override def getZeroLinearSpeedThreshold(): Float = ??? + + override def setZeroLinearSpeedThreshold(x$1: Float): Unit = ??? + + override def getMaxLinearSpeed(): Float = ??? + + override def setMaxLinearSpeed(x$1: Float): Unit = ??? + + override def getMaxLinearAcceleration(): Float = ??? + + override def setMaxLinearAcceleration(x$1: Float): Unit = ??? + + override def getMaxAngularSpeed(): Float = ??? + + override def setMaxAngularSpeed(x$1: Float): Unit = ??? + + override def getMaxAngularAcceleration(): Float = ??? + + override def setMaxAngularAcceleration(x$1: Float): Unit = ??? + + override def getLinearVelocity(): Vector3 = ??? + + override def getAngularVelocity(): Float = ??? + + override def getBoundingRadius(): Float = ??? + + override def isTagged(): Boolean = ??? + + override def setTagged(x$1: Boolean): Unit = ??? + +} 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 b8457d7..82379ec 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 @@ -9,20 +9,20 @@ import com.jme3.input.MouseInput import com.jme3.input.controls.KeyTrigger import com.jme3.input.controls.MouseAxisTrigger import monix.bio.UIO -import wow.doge.mygame.events.EventBus import wow.doge.mygame.implicits._ -import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.PlayerCameraEvent +import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.utils.IOUtils._ object GameInputHandler { final case class Props( inputManager: InputManager, - playerMovementEventBus: ActorRef[ - EventBus.Command[PlayerMovementEvent] - ], - playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] + playerMovementEventBus: GameEventBus[PlayerMovementEvent], + playerCameraEventBus: GameEventBus[PlayerCameraEvent] + // tickEventBus: GameEventBus[TickEvent] ) { def begin = for { @@ -66,12 +66,12 @@ object GameInputHandler { def setupKeys(inputManager: InputManager) = inputManager .withMapping( - PlayerAnalogInput.TurnRight.entryName, + PlayerAnalogMovementInput.TurnRight.entryName, new KeyTrigger(KeyInput.KEY_RIGHT), new MouseAxisTrigger(MouseInput.AXIS_X, true) ) .withMapping( - PlayerAnalogInput.TurnLeft.entryName, + PlayerAnalogMovementInput.TurnLeft.entryName, new KeyTrigger(KeyInput.KEY_LEFT), new MouseAxisTrigger(MouseInput.AXIS_X, false) ) @@ -85,7 +85,6 @@ object GameInputHandler { // new KeyTrigger(KeyInput.KEY_LEFT), new MouseAxisTrigger(MouseInput.AXIS_Y, true) ) - .setCursorVisible(false) def generateMovementInputEvents( inputManager: InputManager, @@ -148,19 +147,18 @@ object GameInputHandler { ) = { val name = "rotateMovementEventsGenerator" inputManager - .enumAnalogObservable(PlayerAnalogInput) + .enumAnalogObservable(PlayerAnalogMovementInput) .sample(1.millis) - // .map(e => e) .doOnNext(analogEvent => analogEvent.binding match { - case PlayerAnalogInput.TurnRight => + case PlayerAnalogMovementInput.TurnRight => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerRotatedRight, name ) ) - case PlayerAnalogInput.TurnLeft => + case PlayerAnalogMovementInput.TurnLeft => toTask( playerMovementEventBus !! EventBus.Publish( PlayerMovementEvent.PlayerRotatedLeft, @@ -179,7 +177,7 @@ object GameInputHandler { inputManager .analogObservable("CAMERA_UP", "CAMERA_DOWN") .sample(1.millis) - .mapEval(analogEvent => + .doOnNext(analogEvent => analogEvent.binding.name match { case "CAMERA_UP" => toTask( diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputConstant.scala b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputConstant.scala deleted file mode 100644 index 939114d..0000000 --- a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputConstant.scala +++ /dev/null @@ -1,9 +0,0 @@ -package wow.doge.mygame.game.subsystems.input - -object InputConstants { - val PLAYER_MOVE_LEFT = "PLAYER_MOVE_LEFT" - val PLAYER_MOVE_RIGHT = "PLAYER_MOVE_RIGHT" - val PLAYER_MOVE_FORWARD = "PLAYER_MOVE_FORWARD" - val PLAYER_MOVE_BACKWARD = "PLAYER_MOVE_BACKWARD" - val PLAYER_JUMP = "PLAYER_JUMP " -} 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 index 327ffca..3292220 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala @@ -12,9 +12,9 @@ object PlayerMovementInput extends Enum[PlayerMovementInput] { case object Jump extends PlayerMovementInput } -sealed trait PlayerAnalogInput extends EnumEntry with UpperSnakecase -object PlayerAnalogInput extends Enum[PlayerAnalogInput] { +sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase +object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] { val values = findValues - case object TurnRight extends PlayerAnalogInput - case object TurnLeft extends PlayerAnalogInput + case object TurnRight extends PlayerAnalogMovementInput + case object TurnLeft extends PlayerAnalogMovementInput } diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala b/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala index c260071..0c30d34 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala @@ -1,13 +1,14 @@ package wow.doge.mygame.game.subsystems.level +import com.jme3.bullet.PhysicsSpace import com.jme3.bullet.control.RigidBodyControl import com.jme3.light.AmbientLight import com.jme3.light.DirectionalLight +import com.jme3.scene.Node import com.jme3.scene.Spatial -import com.jme3.bullet.PhysicsSpace -import cats.effect.concurrent.Ref +import com.softwaremill.tagging._ import monix.bio.Task -import com.jme3.scene.Node +import wow.doge.mygame.game.GameAppTags import wow.doge.mygame.implicits._ class GameLevel( @@ -16,11 +17,14 @@ class GameLevel( ambientLight: AmbientLight, directionalLight: DirectionalLight ) { - def addToGame(rootNode: Ref[Task, Node], physicsSpace: PhysicsSpace) = { + def addToGame( + rootNode: Node @@ GameAppTags.RootNode, + physicsSpace: PhysicsSpace + ) = { for { - _ <- rootNode.update(_ :+ model) - _ <- rootNode.update(_ :+ ambientLight) - _ <- rootNode.update(_ :+ directionalLight) + _ <- Task(rootNode += model) + _ <- Task(rootNode :+ ambientLight) + _ <- Task(rootNode :+ directionalLight) _ <- Task(physicsSpace += model) _ <- Task(physicsSpace += physicsControl) } yield () 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 index 8485c5f..f4459c6 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala @@ -13,6 +13,7 @@ import wow.doge.mygame.subsystems.movement.RotateDir trait CanMove[-A] { // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f def move(inst: A, direction: ImVector3f): Unit + def location(inst: A): ImVector3f def jump(inst: A): Unit def stop(inst: A): Unit def rotate(inst: A, rotateDir: RotateDir): Unit @@ -30,6 +31,8 @@ object CanMove { // inst.setViewDirection(direction.mutable) inst.setWalkDirection(direction.mutable.multLocal(50f)) } + override def location(inst: BetterCharacterControl) = + inst.getSpatial().getLocalTranslation().immutable override def jump(inst: BetterCharacterControl): Unit = inst.jump() override def rotate( inst: BetterCharacterControl, @@ -39,10 +42,10 @@ object CanMove { rotateDir match { case RotateDir.Left => new Quaternion() - .fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) + .fromAngleAxis(-5 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) case RotateDir.Right => new Quaternion() - .fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) + .fromAngleAxis(5 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) } val tmp = new Vector3f() @@ -57,6 +60,8 @@ object CanMove { override def move(inst: Spatial, direction: ImVector3f): Unit = { inst.move(direction.mutable) } + override def location(inst: Spatial) = + inst.getLocalTranslation().immutable override def jump(inst: Spatial): Unit = logger.warn("`Jump` is not implemented for type `Spatial`") override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = { 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 9688f45..cd2c338 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 @@ -1,17 +1,14 @@ package wow.doge.mygame.subsystems.movement -import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import com.jme3.math.Vector3f import com.softwaremill.quicklens._ -import wow.doge.mygame.events.EventBus import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.implicits._ import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.state.CardinalDirection -import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent sealed trait RotateDir object RotateDir { @@ -35,21 +32,13 @@ object ImMovementActor { final case class Props[T: CanMove]( enqueueR: Function1[() => Unit, Unit], - movable: T, - playerMovementEventBus: ActorRef[ - EventBus.Command[PlayerMovementEvent] - ] + movable: T + // playerMovementEventBus: ActorRef[ + // EventBus.Command[PlayerMovementEvent] + // ] ) { def create: Behavior[Command] = - Behaviors.setup(ctx => { - ctx.log.info("Hello from MovementActor") - // val playerMovementEventHandler = ctx.spawn( - // PlayerMovementEventHandler(ctx.self), - // "playerMovementEventHandler" - // ) - // playerMovementEventBus ! EventBus.Subscribe(playerMovementEventHandler) - new ImMovementActor(ctx, this).receive(State()) - }) + Behaviors.setup(ctx => new ImMovementActor(ctx, this).receive(State())) } /** @@ -59,19 +48,6 @@ object ImMovementActor { */ final case class State(cardinalDir: CardinalDirection = CardinalDirection()) - // def apply[T: CanMove](props: Props[T]): Behavior[Command] = - // Behaviors.setup(ctx => { - // ctx.log.info("Hello from MovementActor") - // val playerMovementEventHandler = ctx.spawn( - // PlayerMovementEventHandler(ctx.self), - // "playerMovementEventHandler" - // ) - // props.playerMovementEventBus ! EventBus.Subscribe( - // playerMovementEventHandler - // ) - // new ImMovementActor(ctx, props).receive(State()) - // }) - } class ImMovementActor[T]( diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/ui/JmeJfx.scala b/src/main/scala/wow/doge/mygame/game/subsystems/ui/JmeJfx.scala index 5bf3cfc..f40ab7a 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/ui/JmeJfx.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/ui/JmeJfx.scala @@ -1,43 +1,15 @@ package wow.doge.mygame.game.subsystems.ui -import com.jme3.app.Application +import scala.concurrent.duration._ + import com.jayfella.jme.jfx.JavaFxUI -import scalafx.application.Platform -import monix.execution.CancelablePromise import monix.bio.Task -import cats.effect.concurrent.Deferred -import scala.concurrent.duration._ -import wow.doge.mygame.game.GameApp +import wow.doge.mygame.game.SimpleAppExt object JFxUI { - def apply(app: GameApp) = + def apply(app: SimpleAppExt) = Task(JavaFxUI.initialize(app)) .executeOn(app.scheduler) >> Task.sleep(500.millis) >> Task( JavaFxUI.getInstance() ) - // Task { - // Platform.runLater(() => { - // println("here jfx") - // JavaFxUI.initialize(app) - // println("here2 jfx2") - // val inst = JavaFxUI.getInstance() - // println(inst) - // }) - // } - // Task.fromFuture { - // val p = CancelablePromise[JavaFxUI]() - // Platform.runLater(() => { - // println("here") - // JavaFxUI.initialize(app) - // println("here2") - // val inst = JavaFxUI.getInstance() - // println(inst) - // p.success(inst) - // }) - // p.future - // } -// for { -// d <- Deferred[Task, JavaFxUI] -// _ <- Task(JavaFxUI.initialize(app)) -// } yield () } diff --git a/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala b/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala index 34b69d6..1650b5f 100644 --- a/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala +++ b/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala @@ -1,18 +1,12 @@ package wow.doge.mygame.implicits -import javafx.{ - collections => jfxc, - event => jfxe, - geometry => jfxg, - scene => jfxs, - util => jfxu -} -import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp} -import scalafx.scene.Scene +import javafx.scene.{input => jfxsi} +import javafx.{event => jfxe} +import monix.execution.Ack import monix.execution.Cancelable -import monix.reactive.OverflowStrategy import monix.reactive.Observable -import monix.execution.Ack +import monix.reactive.OverflowStrategy +import scalafx.scene.Scene import scalafx.scene.control.ButtonBase object JavaFXMonixObservables { diff --git a/src/main/scala/wow/doge/mygame/implicits/observables/package.scala b/src/main/scala/wow/doge/mygame/implicits/observables/package.scala index d587ffe..8d97f0c 100644 --- a/src/main/scala/wow/doge/mygame/implicits/observables/package.scala +++ b/src/main/scala/wow/doge/mygame/implicits/observables/package.scala @@ -1,18 +1,5 @@ package wow.doge.mygame.implicits -import javafx.{ - collections => jfxc, - event => jfxe, - geometry => jfxg, - scene => jfxs, - util => jfxu -} -import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp} -import scalafx.scene.Scene -import monix.execution.Cancelable -import monix.reactive.OverflowStrategy -import monix.reactive.Observable -import monix.execution.Ack -import scalafx.scene.control.Button + package object observables {} diff --git a/src/main/scala/wow/doge/mygame/implicits/package.scala b/src/main/scala/wow/doge/mygame/implicits/package.scala index e670a14..f08761b 100644 --- a/src/main/scala/wow/doge/mygame/implicits/package.scala +++ b/src/main/scala/wow/doge/mygame/implicits/package.scala @@ -6,6 +6,7 @@ import scala.reflect.ClassTag import akka.actor.typed.ActorRef import akka.actor.typed.Scheduler import akka.util.Timeout +import com.jayfella.jme.jfx.JavaFxUI import com.jme3.app.Application import com.jme3.app.SimpleApplication import com.jme3.app.state.AppState @@ -24,6 +25,7 @@ import com.jme3.input.controls.ActionListener import com.jme3.input.controls.AnalogListener import com.jme3.input.controls.InputListener import com.jme3.input.controls.Trigger +import com.jme3.light.Light import com.jme3.math.Vector3f import com.jme3.scene.CameraNode import com.jme3.scene.Geometry @@ -48,23 +50,23 @@ import monix.reactive.OverflowStrategy import monix.reactive.observers.Subscriber import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.state.MyBaseState -import com.jme3.light.Light -import com.jayfella.jme.jfx.JavaFxUI +import com.jme3.material.Material -case class ActionEvent(binding: Action, value: Boolean, tpf: Float) -case class EnumActionEvent[T <: EnumEntry]( +final case class ActionEvent(binding: Action, value: Boolean, tpf: Float) +final 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]( +final case class AnalogEvent(binding: Action, value: Float, tpf: Float) +final case class EnumAnalogEvent[T <: EnumEntry]( binding: T, value: Float, tpf: Float ) -case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float) +final class PrePhysicsTickEvent(val space: PhysicsSpace) extends AnyVal +final class PhysicsTickEvent(val space: PhysicsSpace) extends AnyVal package object implicits { type PrePhysicsTickEvent = PhysicsTickEvent @@ -334,6 +336,13 @@ package object implicits { } } + implicit final class GeometryExt(private val geom: Geometry) { + def withMaterial(mat: Material) = { + geom.setMaterial(mat) + geom + } + } + implicit final class EntityDataExt(private val ed: EntityData) extends AnyVal { @@ -356,10 +365,7 @@ package object implicits { import akka.actor.typed.scaladsl.AskPattern._ /** - * @param replyTo - * @param timeout - * @param scheduler - * @return + * Same as [[ask]] but returns a [[Task]] */ def askL[Res]( replyTo: ActorRef[Res] => Req @@ -378,12 +384,16 @@ package object implicits { * @return */ def tellL(msg: Req) = UIO(a.tell(msg)) + + /** + * Same as [[tell]], but wrapped in a Task + * + * @param msg + * @return + */ def !!(msg: Req) = tellL(msg) } - // def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = { - // ask(replyTo)(timeout, scheduler) - // } implicit final class InputManagerExt(private val inputManager: InputManager) extends AnyVal { @@ -523,7 +533,7 @@ package object implicits { def collisionObservable(): Observable[PhysicsCollisionEvent] = { - Observable.create(OverflowStrategy.Unbounded) { sub => + Observable.create(OverflowStrategy.DropOld(50)) { sub => val c = SingleAssignCancelable() val cl = new PhysicsCollisionListener { override def collision(event: PhysicsCollisionEvent): Unit = { @@ -543,23 +553,45 @@ package object implicits { } } - def physicsTickObservable(): PhysicsTickObservable = { + def prePhysicsTickObservable(): Observable[PrePhysicsTickEvent] = { - Observable.create(OverflowStrategy.Unbounded) { sub => + Observable.create(OverflowStrategy.DropOld(50)) { sub => val c = SingleAssignCancelable() val cl = new PhysicsTickListener { override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = { - val event = PhysicsTickEvent(space, tpf) - if (sub.onNext(Left(event)) == Ack.Stop) { + val event = new PrePhysicsTickEvent(space) + if (sub.onNext(event) == Ack.Stop) { sub.onComplete() c.cancel() } } + override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {} + + } + + space.addTickListener(cl) + + c := Cancelable(() => space.removeTickListener(cl)) + c + } + } + + def physicsTickObservable(): Observable[PhysicsTickEvent] = { + + Observable.create(OverflowStrategy.DropOld(50)) { sub => + val c = SingleAssignCancelable() + val cl = new PhysicsTickListener { + + override def prePhysicsTick( + space: PhysicsSpace, + tpf: Float + ): Unit = {} + override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = { - val event = PhysicsTickEvent(space, tpf) - if (sub.onNext(Right(event)) == Ack.Stop) { + val event = new PhysicsTickEvent(space) + if (sub.onNext(event) == Ack.Stop) { sub.onComplete() c.cancel() } @@ -574,7 +606,7 @@ package object implicits { } } - //TODO Create a typeclass for this + //TODO Consider creating a typeclass for this def +=(anyObject: Any) = space.add(anyObject) def :+(anyObject: Any) = { diff --git a/src/main/scala/wow/doge/mygame/launcher/DefaultUI.scala b/src/main/scala/wow/doge/mygame/launcher/DefaultUI.scala index 7dfb948..1971d3c 100644 --- a/src/main/scala/wow/doge/mygame/launcher/DefaultUI.scala +++ b/src/main/scala/wow/doge/mygame/launcher/DefaultUI.scala @@ -1,18 +1,17 @@ package wow.doge.mygame.launcher import scalafx.geometry.Insets +import scalafx.geometry.Orientation +import scalafx.geometry.Pos import scalafx.scene.Scene +import scalafx.scene.control.Button import scalafx.scene.effect.DropShadow +import scalafx.scene.layout.FlowPane import scalafx.scene.layout.HBox +import scalafx.scene.layout.VBox import scalafx.scene.paint.Color._ import scalafx.scene.paint._ import scalafx.scene.text.Text -import scalafx.scene.control.Button -import scalafx.scene.layout.VBox -import scalafx.scene.layout.FlowPane -import scalafx.geometry.Orientation -import scalafx.geometry.Pos -import scalafx.stage.Stage object DefaultUI { def scene( @@ -64,39 +63,4 @@ object DefaultUI { // } } - def box(launchButton: Button, exitButton: Button) = - new VBox { - children = Seq( - new HBox { - padding = Insets(50, 80, 50, 80) - children = Seq( - new Text { - text = "JMonkeyEngine" - style = "-fx-font: normal bold 50pt sans-serif" - fill = new LinearGradient(endX = 0, stops = Stops(Red, DarkRed)) - }, - new Text { - text = " Game" - style = "-fx-font: italic bold 50pt sans-serif" - fill = new LinearGradient( - endX = 0, - stops = Stops(White, DarkGray) - ) - effect = new DropShadow { - color = DarkGray - radius = 15 - spread = 0.25 - } - } - ) - }, - new FlowPane { - hgap = 10 - padding = Insets(50, 80, 50, 80) - orientation = Orientation.Horizontal - alignment = Pos.Center - children = Seq(launchButton, exitButton) - } - ) - } } diff --git a/src/main/scala/wow/doge/mygame/launcher/Launcher.scala b/src/main/scala/wow/doge/mygame/launcher/Launcher.scala index 73671cd..67084bc 100644 --- a/src/main/scala/wow/doge/mygame/launcher/Launcher.scala +++ b/src/main/scala/wow/doge/mygame/launcher/Launcher.scala @@ -1,34 +1,22 @@ package wow.doge.mygame.launcher import scala.concurrent.duration.FiniteDuration -import scalafx.application.JFXApp -import scalafx.application.JFXApp.PrimaryStage -import wow.doge.mygame.executors.Schedulers -import cats.effect.Resource -import monix.bio.Task import scala.concurrent.duration._ -import javafx.application.Platform -import scalafx.scene.control.Button + import cats.effect.concurrent.Deferred -import wow.doge.mygame.utils.IOUtils._ +import javafx.application.Platform +import monix.bio.Task +import monix.catnap.CancelableF import monix.eval.{Task => ETask} import monix.reactive.Observable -import monix.bio.Fiber -import scalafx.stage.StageStyle import scalafx.Includes._ -import wow.doge.mygame.utils.ResizeHelper -import scalafx.scene.Scene -import scalafx.scene.layout.VBox +import scalafx.application.JFXApp +import scalafx.application.JFXApp.PrimaryStage +import scalafx.scene.control.Button +import scalafx.stage.StageStyle +import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.implicits.JavaFXMonixObservables._ -import monix.catnap.cancelables.SingleAssignCancelableF -import monix.catnap.CancelableF -// import wow.doge.mygame.implicits.JavaFXMonixObservables._ - -// import scala.language.implicitConversions - -// object Stage { -// implicit def sfxStage2jfx(v: Stage): jfxs.Stage = if (v != null) v.delegate else null -// } +import wow.doge.mygame.utils.IOUtils._ object Launcher { sealed trait LauncherResult @@ -41,19 +29,7 @@ object Launcher { val schedulers: Schedulers, val signal: Deferred[Task, LauncherResult] ) { - // val resource2 - // : Resource[Task, (LauncherApp, Task[Ref[Task, Stage]], Fiber[Unit])] = - // Resource.make(for { - // app <- Task(new LauncherApp(this)) - // fib <- app.init.start - // } yield ((app, app.stageRef, fib)))(_._3.cancel) val create = Task(new Launcher(this)) - - val resource: Resource[Task, Launcher] = - Resource.make(for { - app <- Task(new Launcher(this)) - // fib <- app.init.start - } yield (app))(_ => Task.unit) } } class Launcher private (props: Launcher.Props) { @@ -62,7 +38,6 @@ class Launcher private (props: Launcher.Props) { private lazy val launchButton = new Button { text = "Launch" } - // private lazy val launchButtonObs = private lazy val launchAction = launchButton @@ -72,7 +47,6 @@ class Launcher private (props: Launcher.Props) { private lazy val exitButton = new Button { text = "Exit" } - // private lazy val exitButtonObs = private lazy val exitAction = exitButton @@ -80,9 +54,6 @@ class Launcher private (props: Launcher.Props) { .doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit))) private lazy val _scene = - // new Scene { - // content = new VBox - // } DefaultUI.scene(launchButton, exitButton) private lazy val _stage = new PrimaryStage { @@ -113,41 +84,21 @@ class Launcher private (props: Launcher.Props) { ) } - // var stage = internal.stage - - // lazy val stageRef = Ref.of[Task, Stage](internal.stage) -// stage: => PrimaryStage def init(delay: FiniteDuration = 2000.millis) = for { _ <- Task(Platform.setImplicitExit(false)) fib <- Task(internal.main(Array.empty)).start _ <- Task.sleep(500.millis) - // _ <- Task { - // // lazy val _stage = new CustomStageBuilder() - // // .setWindowTitle("CustomStage example") - // // .setWindowColor("rgb(34,54,122)") - // // .build() - // internal.stage.scene = - // DefaultUI.scene(internal.stage, launchButton, exitButton) - // // _stage.setScene(DefaultUI.scene(launchButton, exitButton)) - // // JFXApp.Stage = _stage - // }.executeOn(props.schedulers.fx) - // c <- SingleAssignCancelableF[Task] sceneDragFib <- toIO(sceneDragObservable.completedL).start fib2 <- toIO( Observable(launchAction, exitAction).merge .doOnNext(_ => ETask(internal.stage.close()).executeOn(props.schedulers.fx) ) - // .doOnNext(_ => toTask(fib.cancel)) .completedL ).start c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel) - // _ <- Task { - // internal.stage = stage - // }.executeOn(props.schedulers.fx) - // .delayExecution(delay) } yield (c) } diff --git a/src/main/scala/wow/doge/mygame/math/ImVector3f.scala b/src/main/scala/wow/doge/mygame/math/ImVector3f.scala index e630a46..b3dfe3d 100644 --- a/src/main/scala/wow/doge/mygame/math/ImVector3f.scala +++ b/src/main/scala/wow/doge/mygame/math/ImVector3f.scala @@ -1,5 +1,7 @@ package wow.doge.mygame.math; +import Math.{abs, sqrt, pow} + case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f) object ImVector3f { @@ -8,4 +10,6 @@ object ImVector3f { val UNIT_Y = ImVector3f(0, 1, 0) val UNIT_Z = ImVector3f(0, 0, 1) + def dst(v1: ImVector3f, v2: ImVector3f) = + sqrt(pow(v1.x - v2.x, 2) + pow(v1.y - v2.y, 2) + pow(v1.z - v2.z, 2)) } 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 6c1696d..c5decdb 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala @@ -1,4 +1,4 @@ -package wow.doge.mygame.events +package wow.doge.mygame.subsystems.events import scala.reflect.ClassTag diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala b/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala index 37517f7..436e37e 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala @@ -1,14 +1,24 @@ -package wow.doge.mygame.events +package wow.doge.mygame.subsystems.events -object Events { - sealed trait Event - final case object BulletFired extends Event +sealed trait Event +final case object BulletFired extends Event // type BulletFired = BulletFired.type - final case class EventWithData(data: Int) extends Event +final case class EventWithData(data: Int) extends Event - sealed trait Tick extends Event - object Tick { - final case object RenderTick extends Tick - final case object PhysicsTick extends Tick - } +sealed trait TickEvent extends Event +object TickEvent { + final case object RenderTick extends TickEvent + final case object PhysicsTick extends TickEvent +} + +sealed trait EntityMovementEvent extends Event +object EntityMovementEvent { + final case class MovedLeft(name: String, pressed: Boolean) + extends EntityMovementEvent + final case class MovedUp(name: String, pressed: Boolean) + extends EntityMovementEvent + final case class MovedRight(name: String, pressed: Boolean) + extends EntityMovementEvent + final case class MovedDown(name: String, pressed: Boolean) + extends EntityMovementEvent } 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 8ab4495..0c3a106 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala @@ -10,22 +10,19 @@ import akka.actor.typed.SpawnProtocol import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.Behaviors import akka.util.Timeout -import cats.effect.Resource -import com.typesafe.scalalogging.{Logger => SLLogger} -import monix.bio.Task +import com.typesafe.scalalogging.{Logger => SLogger} import org.slf4j.event.Level -import wow.doge.mygame.events.EventBus import wow.doge.mygame.implicits._ -import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent +import wow.doge.mygame.subsystems.events.Event +import wow.doge.mygame.subsystems.events.EventBus +import wow.doge.mygame.subsystems.events.TickEvent -class EventsModule2( - spawnProtocol: ActorSystem[SpawnProtocol.Command] -) { +class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) { implicit lazy val s = spawnProtocol.scheduler implicit lazy val timeout = Timeout(1.second) - lazy val eventBusLogger = SLLogger[EventBus[_]] + lazy val eventBusLogger = SLogger[EventBus[_]] lazy val playerMovementEventBusTask = createEventBus[PlayerMovementEvent]("movementEventBus") @@ -33,6 +30,11 @@ class EventsModule2( lazy val playerCameraEventBusTask = createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG) + lazy val tickEventBusTask = + createEventBus[TickEvent]("tickEventBus", Level.TRACE) + + lazy val mainEventBusTask = createEventBus[Event]("mainEventBus") + def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) = spawnProtocol.askL( SpawnProtocol.Spawn[EventBus.Command[T]]( @@ -49,17 +51,8 @@ class EventsModule2( _ ) ) +} - 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)) +object EventsModule { + type GameEventBus[T] = ActorRef[EventBus.Command[T]] } diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala b/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala deleted file mode 100644 index c6b8e39..0000000 --- a/src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala +++ /dev/null @@ -1,33 +0,0 @@ -package wow.doge.mygame.subsystems.events - -import wow.doge.mygame.game.subsystems.movement.CanMove - -sealed trait EntityMovementEvent -object EntityMovementEvent { - final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T) - extends EntityMovementEvent - final case class MovedUp[T: CanMove](pressed: Boolean, movable: T) - extends EntityMovementEvent - final case class MovedRight[T: CanMove](pressed: Boolean, movable: T) - extends EntityMovementEvent - final case class MovedDown[T: CanMove](pressed: Boolean, movable: T) - extends EntityMovementEvent - - sealed trait PlayerMovementEvent extends EntityMovementEvent - object PlayerMovementEvent { - final case class PlayerMovedLeft(pressed: Boolean) - extends PlayerMovementEvent - final case class PlayerMovedRight(pressed: Boolean) - extends PlayerMovementEvent - final case class PlayerMovedForward(pressed: Boolean) - extends PlayerMovementEvent - final case class PlayerMovedBackward(pressed: Boolean) - extends PlayerMovementEvent - final case object PlayerJumped extends PlayerMovementEvent - final case object PlayerRotatedRight extends PlayerMovementEvent - final case object PlayerRotatedLeft extends PlayerMovementEvent - final case object PlayerCameraUp extends PlayerMovementEvent - final case object PlayerCameraDown 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 index f874ca1..1016f29 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala @@ -1,8 +1 @@ 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/events/PlayerEvents.scala b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala new file mode 100644 index 0000000..6486c91 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala @@ -0,0 +1,24 @@ +package wow.doge.mygame.subsystems.events + +sealed trait PlayerMovementEvent +object PlayerMovementEvent { + final case class PlayerMovedLeft(pressed: Boolean) extends PlayerMovementEvent + final case class PlayerMovedRight(pressed: Boolean) + extends PlayerMovementEvent + final case class PlayerMovedForward(pressed: Boolean) + extends PlayerMovementEvent + final case class PlayerMovedBackward(pressed: Boolean) + extends PlayerMovementEvent + final case object PlayerJumped extends PlayerMovementEvent + final case object PlayerRotatedRight extends PlayerMovementEvent + final case object PlayerRotatedLeft extends PlayerMovementEvent + final case object PlayerCameraUp extends PlayerMovementEvent + final case object PlayerCameraDown extends PlayerMovementEvent +} + +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/moddingsystem/ModdingSystem.scala b/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala index f85683d..f54af19 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala @@ -8,6 +8,7 @@ import scala.util.Try import cats.implicits._ import io.circe._ +import io.circe.generic.JsonCodec import io.circe.generic.semiauto._ import io.circe.parser._ import monix.bio.IO @@ -15,7 +16,6 @@ import monix.bio.UIO import monix.reactive.Consumer import monix.reactive.Observable import wow.doge.mygame.utils.IOUtils -import io.circe.generic.JsonCodec @JsonCodec final case class Test1(hello1: String, hello2: String) 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 5214c4e..975ce98 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala @@ -5,9 +5,9 @@ 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 -import monix.bio.Task /** * Scripts can either be searched and compiled at startup (Eager mode) diff --git a/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala b/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala index d36c913..3c47b1c 100644 --- a/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala +++ b/src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala @@ -22,4 +22,20 @@ object AkkaUtils { _ ) ) + def spawnActorL2[T]( + behavior: Behavior[T], + actorName: String + )(implicit + timeout: Timeout, + scheduler: Scheduler, + spawnProtocol: ActorRef[SpawnProtocol.Command] + ) = + spawnProtocol.askL[ActorRef[T]]( + SpawnProtocol.Spawn( + behavior, + actorName, + Props.empty, + _ + ) + ) } diff --git a/src/main/scala/wow/doge/mygame/utils/BorderlessScene.scala b/src/main/scala/wow/doge/mygame/utils/BorderlessScene.scala deleted file mode 100644 index 8fe3f0c..0000000 --- a/src/main/scala/wow/doge/mygame/utils/BorderlessScene.scala +++ /dev/null @@ -1,750 +0,0 @@ -package wow.doge.mygame.utils - -/* - * Copyright (c) 2011-2019, ScalaFX Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the ScalaFX Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp} -import javafx.{ - collections => jfxc, - event => jfxe, - geometry => jfxg, - scene => jfxs, - util => jfxu -} -import scalafx.Includes._ -import scalafx.beans.property.{ - ObjectProperty, - ReadOnlyDoubleProperty, - ReadOnlyObjectProperty -} -import scalafx.collections._ -import scalafx.delegate.SFXDelegate -import scalafx.geometry.NodeOrientation -import scalafx.scene.image.WritableImage -import scalafx.scene.input.{Dragboard, Mnemonic, TransferMode} -import scalafx.scene.paint.Paint -import com.goxr3plus.fxborderlessscene.borderless.{BorderlessScene => BScene} - -import scala.language.implicitConversions -import scalafx.scene.Cursor -import scalafx.scene._ -import scalafx.stage.Stage -import scalafx.stage.StageStyle - -object BorderlessScene { - implicit def sfxScene2jfx(v: BorderlessScene): Scene = - if (v != null) v.delegate else null -} - -/** - * Wraps [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html]]. - * - * @constructor Create a new ScalaFX Scene with JavaFX Scene as delegate. - * @param delegate JavaFX Scene delegated. Its default value is a JavaFX Scene with a - * [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Group.html Group]] as root Node. - */ -class BorderlessScene( - override val delegate: BScene -) extends SFXDelegate[BScene] { - - def this(stage: Stage, stageStyle: StageStyle, parent: Parent) = - this(new BScene(stage, stageStyle, parent)) - - /** - * Returns the root Node of the scene graph - */ - def root: ObjectProperty[jfxs.Parent] = delegate.rootProperty - - /** - * Sets the root Node of the scene graph - */ - def root_=(v: Parent): Unit = { - root() = v - } - - /** - * Returns Nodes children from this Scene's `root`. - */ - def getChildren = - root.value match { - case group: jfxs.Group => group.getChildren - case pane: jfxsl.Pane => pane.getChildren - case _ => - throw new IllegalStateException( - "Cannot access children of root: " + root + "\n" + - "Use a class that extends Group or Pane, or override the getChildren method." - ) - } - - /** - * Returns scene's antialiasing setting. - */ - def antialiasing: SceneAntialiasing = delegate.getAntiAliasing - - /** - * Returns Content's Node children from this Scene's `root`. - */ - def content: jfxc.ObservableList[jfxs.Node] = getChildren - - /** - * Sets the list of Nodes children from this Scene's `root`, replacing the prior content. If you want append to - * current content, use `add` or similar. - * - * @param c list of Nodes children from this Scene's `root` to replace prior content. - */ - def content_=(c: Iterable[Node]): Unit = { - fillSFXCollection(this.content, c) - } - - /** - * Sets a Node child, replacing the prior content. If you want append to current content, use `add` or similar. - * - * @param n Node child to replace prior content. - */ - def content_=(n: Node): Unit = { - fillSFXCollectionWithOne(this.content, n) - } - - /** - * Specifies the type of camera use for rendering this `Scene`. - */ - def camera: ObjectProperty[jfxs.Camera] = delegate.cameraProperty - - def camera_=(v: Camera): Unit = { - camera() = v - } - - /** - * Defines the mouse cursor for this `Scene`. - */ - def cursor: ObjectProperty[jfxs.Cursor] = delegate.cursorProperty - - def cursor_=(v: Cursor): Unit = { - cursor() = v - } - - /** The effective node orientation of a scene resolves the inheritance of node orientation, returning either left-to-right or right-to-left. */ - def effectiveNodeOrientation: ReadOnlyObjectProperty[jfxg.NodeOrientation] = - delegate.effectiveNodeOrientationProperty - - /** - * Specifies the event dispatcher for this scene. - */ - def eventDispatcher: ObjectProperty[jfxe.EventDispatcher] = - delegate.eventDispatcherProperty - - def eventDispatcher_=(v: jfxe.EventDispatcher): Unit = { - eventDispatcher() = v - } - - /** - * Defines the background fill of this Scene. - */ - def fill: ObjectProperty[jfxsp.Paint] = delegate.fillProperty - - def fill_=(v: Paint): Unit = { - fill() = v - } - - /** - * The height of this Scene - */ - def height: ReadOnlyDoubleProperty = delegate.heightProperty - - /** - * The width of this Scene - */ - def width: ReadOnlyDoubleProperty = delegate.widthProperty - - def nodeOrientation: ObjectProperty[jfxg.NodeOrientation] = - delegate.nodeOrientationProperty - - def nodeOrientation_=(v: NodeOrientation): Unit = { - ObjectProperty.fillProperty[jfxg.NodeOrientation](this.nodeOrientation, v) - } - - /** - * Defines a function to be called when a mouse button has been clicked (pressed and released) on this `Scene`. - */ - def onContextMenuRequested = delegate.onContextMenuRequestedProperty - - def onContextMenuRequested_=( - v: jfxe.EventHandler[_ >: jfxsi.ContextMenuEvent] - ): Unit = { - onContextMenuRequested() = v - } - - /** - * Defines a function to be called when drag gesture has been detected. - */ - def onDragDetected = delegate.onDragDetectedProperty - - def onDragDetected_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onDragDetected() = v - } - - /** - * Defines a function to be called when this `Scene` is a drag and drop gesture source after its data has been - * dropped on a drop target. - */ - def onDragDone = delegate.onDragDoneProperty - - def onDragDone_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = { - onDragDone() = v - } - - /** - * Defines a function to be called when the mouse button is released on this `Scene` during drag and drop gesture. - */ - def onDragDropped = delegate.onDragDroppedProperty - - def onDragDropped_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = { - onDragDropped() = v - } - - /** - * Defines a function to be called when drag gesture enters this Scene. - */ - def onDragEntered = delegate.onDragEnteredProperty - - def onDragEntered_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = { - onDragEntered() = v - } - - /** - * Defines a function to be called when drag gesture exits this Scene. - */ - def onDragExited = delegate.onDragExitedProperty - - def onDragExited_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = { - onDragExited() = v - } - - /** - * Defines a function to be called when drag gesture progresses within this `Scene`. - */ - def onDragOver = delegate.onDragOverProperty - - def onDragOver_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = { - onDragOver() = v - } - - /** - * Defines a function to be called when this `Node` has input focus and the input method text has changed. - */ - def onInputMethodTextChanged = delegate.onInputMethodTextChangedProperty - - def onInputMethodTextChanged_=( - v: jfxe.EventHandler[_ >: jfxsi.InputMethodEvent] - ): Unit = { - onInputMethodTextChanged() = v - } - - /** - * Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been pressed. - */ - def onKeyPressed = delegate.onKeyPressedProperty - - def onKeyPressed_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = { - onKeyPressed() = v - } - - /** - * Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been released. - */ - def onKeyReleased = delegate.onKeyReleasedProperty - - def onKeyReleased_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = { - onKeyReleased() = v - } - - /** - * Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been typed. - */ - def onKeyTyped = delegate.onKeyTypedProperty - - def onKeyTyped_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = { - onKeyTyped() = v - } - - /** - * Defines a function to be called when a mouse button has been clicked (pressed and released) on this `Scene`. - */ - def onMouseClicked = delegate.onMouseClickedProperty - - def onMouseClicked_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMouseClicked() = v - } - - /** - * Defines a function to be called when a mouse button is pressed on this `Scene` and then dragged. - */ - def onMouseDragged = delegate.onMouseDraggedProperty - - def onMouseDragged_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMouseDragged() = v - } - - /** - * Defines a function to be called when a full press-drag-release gesture enters this `Scene`. - */ - def onMouseDragEntered = delegate.onMouseDragEnteredProperty - - def onMouseDragEntered_=( - v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent] - ): Unit = { - onMouseDragEntered() = v - } - - /** - * Defines a function to be called when a full press-drag-release gesture exits this `Scene`. - */ - def onMouseDragExited = delegate.onMouseDragExitedProperty - - def onMouseDragExited_=( - v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent] - ): Unit = { - onMouseDragExited() = v - } - - /** - * Defines a function to be called when a full press-drag-release gesture progresses within this `Scene`. - */ - def onMouseDragOver = delegate.onMouseDragOverProperty - - def onMouseDragOver_=( - v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent] - ): Unit = { - onMouseDragOver() = v - } - - /** - * Defines a function to be called when a full press-drag-release gesture ends within this `Scene`. - */ - def onMouseDragReleased = delegate.onMouseDragReleasedProperty - - def onMouseDragReleased_=( - v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent] - ): Unit = { - onMouseDragReleased() = v - } - - /** - * Defines a function to be called when the mouse enters this `Scene`. - */ - def onMouseEntered = delegate.onMouseEnteredProperty - - def onMouseEntered_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMouseEntered() = v - } - - /** - * Defines a function to be called when the mouse exits this `Scene`. - */ - def onMouseExited = delegate.onMouseExitedProperty - - def onMouseExited_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMouseExited() = v - } - - /** - * Defines a function to be called when mouse cursor moves within this `Scene` but no buttons have been pushed. - */ - def onMouseMoved = delegate.onMouseMovedProperty - - def onMouseMoved_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMouseMoved() = v - } - - /** - * Defines a function to be called when a mouse button has been pressed on this `Scene`. - */ - def onMousePressed = delegate.onMousePressedProperty - - def onMousePressed_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMousePressed() = v - } - - /** - * Defines a function to be called when a mouse button has been released on this `Scene`. - */ - def onMouseReleased = delegate.onMouseReleasedProperty - - def onMouseReleased_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = { - onMouseReleased() = v - } - - /** - * Defines a function to be called when user performs a scrolling action. - */ - def onScroll = delegate.onScrollProperty - - def onScroll_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = { - onScroll() = v - } - - /** - * The URL of the user-agent stylesheet that will be used by this Scene in place of the the platform-default - * user-agent stylesheet. If the URL does not resolve to a valid location, the platform-default user-agent - * stylesheet will be used. - * - * For additional information about using CSS with the scene graph, see the - * [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html CSS Reference Guide]]. - * - * @return The URL of the user-agent stylesheet that will be used by this SubScene, or null if has not been set. - */ - def userAgentStylesheet: ObjectProperty[String] = - delegate.userAgentStylesheetProperty - - /** - * Set the URL of the user-agent stylesheet that will be used by this Scene in place of the the platform-default - * user-agent stylesheet. If the URL does not resolve to a valid location, the platform-default user-agent - * stylesheet will be used. - * - * For additional information about using CSS with the scene graph, see the - * [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html CSS Reference Guide]]. - * - * @param url The URL is a hierarchical URI of the form `[scheme:][//authority][path]`. - * If the URL does not have a `[scheme:]` component, the URL is considered to be the `[path]` - * component only. Any leading '/' character of the `[path]` is ignored and the `[path]` is - * treated as a path relative to the root of the application's classpath. - */ - def userAgentStylesheet_=(url: String): Unit = { - ObjectProperty.fillProperty[String](userAgentStylesheet, url) - } - - /** - * The `Window` for this Scene - */ - def window: ReadOnlyObjectProperty[javafx.stage.Window] = - delegate.windowProperty - - /** - * The horizontal location of this `Scene` on the `Window`. - */ - def x: ReadOnlyDoubleProperty = delegate.xProperty - - /** - * The vertical location of this `Scene` on the `Window`. - */ - def y: ReadOnlyDoubleProperty = delegate.yProperty - - /** - * Retrieves the depth buffer attribute for this scene. - */ - def depthBuffer = delegate.isDepthBuffer - - /** - * Gets an observable list of string URLs linking to the stylesheets to use with this Parent's contents. - */ - def stylesheets: jfxc.ObservableList[String] = delegate.getStylesheets - - /** - * Sets the list of stylesheets URLs, replacing the prior content. If you want append to current content, use `add` or - * similar. - * - * @param c list of stylesheets URLs to replace prior content. - */ - def stylesheets_=(c: Iterable[String]): Unit = { - fillCollection(stylesheets, c) - } - - /** - * Looks for any node within the scene graph based on the specified CSS selector. - * - * @param selector The css selector to look up - * @return A [[scala.Some]] containing the Node in the scene which matches the CSS selector, or [[scala.None]] - * if none is found. - */ - def lookup(selector: String): Option[Node] = Option(delegate.lookup(selector)) - - /** - * Registers the specified mnemonic. - * - * @param m The Mnemonic - */ - def addMnemonic(m: Mnemonic): Unit = { - delegate.addMnemonic(m) - } - - /** - * Unregisters the specified mnemonic. - * - * @param m The Mnemonic to be removed. - */ - def removeMnemonic(m: Mnemonic): Unit = { - delegate.removeMnemonic(m) - } - - /** - * Gets the list of mnemonics for this `Scene`. - */ - def getMnemonics - : jfxc.ObservableMap[jfxsi.KeyCombination, jfxc.ObservableList[ - jfxsi.Mnemonic - ]] = delegate.getMnemonics - - /** - * Gets the list of accelerators for this Scene. - */ - def accelerators: jfxc.ObservableMap[jfxsi.KeyCombination, Runnable] = - delegate.getAccelerators - - /** - * Confirms a potential drag and drop gesture that is recognized over this `Scene`. - * - * @param transferModes The supported `TransferMode`(s) of this `Node` - * @return A `Dragboard` to place this `Scene`'s data on - */ - def startDragAndDrop(transferModes: TransferMode*): Dragboard = - delegate.startDragAndDrop(transferModes.map(_.delegate): _*) - - /** - * Starts a full press-drag-release gesture with this scene as gesture source. - */ - def startFullDrag(): Unit = { - delegate.startFullDrag() - } - - /** - * The scene's current focus owner node. This node's "focused" variable might be false if this scene has no window, - * or if the window is inactive (window.focused == false). - * - * @since 2.2 - */ - def focusOwner: ReadOnlyObjectProperty[jfxs.Node] = - delegate.focusOwnerProperty() - - /** - * Defines a function to be called when user performs a rotation action. - * - * @since 2.2 - */ - def onRotate = delegate.onRotateProperty - - def onRotate_=(v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]): Unit = { - onRotate() = v - } - - /** - * Defines a function to be called when a rotation gesture ends. - * - * @since 2.2 - */ - def onRotationFinished = delegate.onRotationFinishedProperty() - - def onRotationFinished_=( - v: jfxe.EventHandler[_ >: jfxsi.RotateEvent] - ): Unit = { - onRotationFinished() = v - } - - /** - * Defines a function to be called when a rotation gesture starts. - * - * @since 2.2 - */ - def onRotationStarted = delegate.onRotationFinishedProperty() - - def onRotationStarted_=( - v: jfxe.EventHandler[_ >: jfxsi.RotateEvent] - ): Unit = { - onRotationStarted() = v - } - - /** - * Defines a function to be called when a Scroll gesture ends. - * - * @since 2.2 - */ - def onScrollFinished = delegate.onScrollFinishedProperty() - - def onScrollFinished_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = { - onScrollFinished() = v - } - - /** - * Defines a function to be called when a Scroll gesture starts. - * - * @since 2.2 - */ - def onScrollStarted = delegate.onScrollStartedProperty() - - def onScrollStarted_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = { - onScrollStarted() = v - } - - /** - * Defines a function to be called when a Swipe Down gesture starts. - * - * @since 2.2 - */ - def onSwipeDown = delegate.onSwipeDownProperty() - - def onSwipeDown_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = { - onSwipeDown() = v - } - - /** - * Defines a function to be called when a Swipe Down gesture starts. - * - * @since 2.2 - */ - def onSwipeLeft = delegate.onSwipeLeftProperty() - - def onSwipeLeft_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = { - onSwipeLeft() = v - } - - /** - * Defines a function to be called when a Swipe Up gesture starts. - * - * @since 2.2 - */ - def onSwipeUp = delegate.onSwipeUpProperty() - - def onSwipeUp_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = { - onSwipeUp() = v - } - - /** - * Defines a function to be called when a Swipe Right gesture starts. - * - * @since 2.2 - */ - def onSwipeRight = delegate.onSwipeRightProperty() - - def onSwipeRight_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = { - onSwipeRight() = v - } - - /** - * Defines a function to be called when user performs a Touch action. - * - * @since 2.2 - */ - def onZoom = delegate.onZoomProperty() - - def onZoom_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = { - onZoom() = v - } - - /** - * Defines a function to be called when a Zoom gesture ends. - * - * @since 2.2 - */ - def onZoomFinished = delegate.onZoomFinishedProperty() - - def onZoomFinished_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = { - onZoomFinished() = v - } - - /** - * Defines a function to be called when a Zoom gesture starts. - * - * @since 2.2 - */ - def onZoomStarted = delegate.onZoomStartedProperty() - - def onZoomStarted_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = { - onZoomStarted() = v - } - - /** - * Defines a function to be called when user performs a Touch Moved action. - * - * @since 2.2 - */ - def onTouchMoved = delegate.onTouchMovedProperty() - - def onTouchMoved_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = { - onTouchMoved() = v - } - - /** - * Defines a function to be called when user performs a Touch Pressed action. - * - * @since 2.2 - */ - def onTouchPressed = delegate.onTouchPressedProperty() - - def onTouchPressed_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = { - onTouchPressed() = v - } - - /** - * Defines a function to be called when user performs a Touch Released action. - * - * @since 2.2 - */ - def onTouchReleased = delegate.onTouchReleasedProperty() - - def onTouchReleased_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = { - onTouchReleased() = v - } - - /** - * Defines a function to be called when user performs a Touch Stationary action. - * - * @since 2.2 - */ - def onTouchStationary = delegate.onTouchStationaryProperty() - - def onTouchStationary_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = { - onTouchStationary() = v - } - - /** - * Takes a snapshot of this scene and returns the rendered image when it is ready. - * - * @param image The writable image that will be used to hold the rendered scene. - * @return the rendered image - * - * @since 2.2 - */ - def snapshot(image: WritableImage): WritableImage = delegate.snapshot(image) - - /** - * Takes a snapshot of this scene at the next frame and calls the specified callback method when the image is ready. - * - * @param callback A function to be called when the image is ready. - * @param image The writable image that will be used to hold the rendered scene. - * - * @since 2.2 - */ - def snapshot(callback: SnapshotResult => Unit, image: WritableImage): Unit = { - val javaCallback = new jfxu.Callback[jfxs.SnapshotResult, java.lang.Void] { - def call(result: jfxs.SnapshotResult): java.lang.Void = { - callback(new SnapshotResult(result)) - null - } - } - delegate.snapshot(javaCallback, image) - } - -} diff --git a/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala b/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala index 3b1ac28..e4afd5a 100644 --- a/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala +++ b/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala @@ -4,10 +4,8 @@ import java.io.ByteArrayOutputStream import java.io.OutputStream import java.io.PrintStream -import cats.effect.Resource -import monix.bio.Task -import scalafx.scene.control.TextArea import scalafx.application.Platform +import scalafx.scene.control.TextArea trait ConsoleStreamable[T] { def println(inst: T, text: String): Unit @@ -18,7 +16,8 @@ class GenericConsoleStream[T]( outputStream: OutputStream, val config: GenericConsoleStream.Config = GenericConsoleStream.Config.default, - private var streamable: Option[T] = None + // TODO make this atomic + private var _streamable: Option[T] = None )(implicit cs: ConsoleStreamable[T] ) extends PrintStream(outputStream, true) { @@ -29,26 +28,24 @@ class GenericConsoleStream[T]( override def println(text: String): Unit = if (config.exclusive) { - printToStreamable(streamable, text) + printToStreamable(_streamable, text) } else { defaultOut.println(text) - printToStreamable(streamable, text) + printToStreamable(_streamable, text) } override def print(text: String): Unit = - streamable.foreach(s => - if (config.exclusive) { - printToStreamable(streamable, text) - } else { - defaultOut.println(text) - printToStreamable(streamable, text) - } - ) + if (config.exclusive) { + printToStreamable(_streamable, text) + } else { + defaultOut.println(text) + printToStreamable(_streamable, text) + } def :=(s: T) = { - streamable match { + _streamable match { case Some(value) => - case None => streamable = Some(s) + case None => _streamable = Some(s) } } } @@ -64,41 +61,25 @@ object GenericConsoleStream { } implicit val implJFXConsoleStreamForTextArea = - new ConsoleStreamable[scalafx.scene.control.TextArea] { + new ConsoleStreamable[TextArea] { override def println( - ta: scalafx.scene.control.TextArea, + ta: TextArea, text: String ): Unit = - ta.appendText(text + "\n") + Platform.runLater(() => ta.appendText(text + "\n")) override def print( - ta: scalafx.scene.control.TextArea, + ta: TextArea, text: String ): Unit = - ta.appendText(text) + Platform.runLater(() => ta.appendText(text)) } - // def textAreaStreamResource(ta: TextArea) = - // Resource.make( - // Task( - // new GenericConsoleStream( - // outputStream = new ByteArrayOutputStream(), - // streamable = ta - // ) - // ) - // )(s => Task(s.close())) - - def textAreaStream( - // ta: TextArea - ) = - // Task( + def textAreaStream() = new GenericConsoleStream[TextArea]( outputStream = new ByteArrayOutputStream() - // streamable = ta ) - // ) - // (s => Task(s.close())) }