Browse Source

blah

development
Rohan Sircar 7 months ago
parent
commit
2e05cb35fe
  1. 3
      .scalafmt.conf
  2. 12
      build.sbt
  3. 12
      src/main/resources/application.conf
  4. 6
      src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
  5. 6
      src/main/scala/wow/doge/mygame/AppError.scala
  6. 7
      src/main/scala/wow/doge/mygame/Dispatchers.scala
  7. 24
      src/main/scala/wow/doge/mygame/Main.scala
  8. 209
      src/main/scala/wow/doge/mygame/MainApp.scala
  9. 11
      src/main/scala/wow/doge/mygame/MainModule.scala
  10. 29
      src/main/scala/wow/doge/mygame/executors/ExecutorsModule.scala
  11. 4
      src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala
  12. 169
      src/main/scala/wow/doge/mygame/game/GameApp.scala
  13. 11
      src/main/scala/wow/doge/mygame/game/GameAppActor.scala
  14. 29
      src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala
  15. 68
      src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala
  16. 121
      src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala
  17. 130
      src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala
  18. 9
      src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala
  19. 14
      src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala
  20. 32
      src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala
  21. 58
      src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala
  22. 67
      src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala
  23. 24
      src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
  24. 62
      src/main/scala/wow/doge/mygame/implicits/CatsImplicits.scala
  25. 23
      src/main/scala/wow/doge/mygame/implicits/package.scala
  26. 44
      src/main/scala/wow/doge/mygame/launcher/Launcher.scala
  27. 20
      src/main/scala/wow/doge/mygame/math/ImVector3f.scala
  28. 137
      src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala
  29. 17
      src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala
  30. 14
      src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala
  31. 3
      src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala
  32. 41
      src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala
  33. 25
      src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala
  34. 13
      src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala
  35. 66
      src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala
  36. 61
      src/main/scala/wow/doge/mygame/utils/wrappers/jme/AssetManager.scala
  37. 18
      src/main/scala/wow/doge/mygame/utils/wrappers/jme/CollisionShapeFactory.scala
  38. 92
      src/main/scala/wow/doge/mygame/utils/wrappers/jme/Node.scala
  39. 4
      src/main/scala/wow/doge/mygame/utils/wrappers/jme/PhysicsSpace.scala
  40. 57
      src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala
  41. 59
      src/test/scala/wow/doge/mygame/AssetManagerTest.scala
  42. 51
      src/test/scala/wow/doge/mygame/CollisionShapeFactoryTest.scala

3
.scalafmt.conf

@ -1 +1,4 @@
version = "2.6.4"
rewrite {
rules = [SortImports, RedundantBraces]
}

12
build.sbt

@ -61,15 +61,15 @@ lazy val root = (project in file(".")).settings(
"org.jmonkeyengine" % "jme3-blender" % jmeVersion,
"com.github.stephengold" % "Minie" % "3.0.0",
"com.simsilica" % "zay-es" % "1.2.1",
"org.typelevel" %% "cats-core" % "2.1.1",
"org.typelevel" %% "cats-core" % "2.3.0",
"com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full,
"org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10",
"org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10",
"org.codehaus.groovy" % "groovy-all" % "3.0.6" pomOnly (),
"org.scalafx" %% "scalafx" % "14-R19",
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.10",
"org.typelevel" %% "cats-core" % "2.1.1",
"org.typelevel" %% "cats-effect" % "2.1.4",
"org.typelevel" %% "cats-core" % "2.3.0",
"org.typelevel" %% "cats-effect" % "2.3.0",
"io.monix" %% "monix" % "3.2.2",
"io.monix" %% "monix-bio" % "1.1.0",
"io.circe" %% "circe-core" % "0.13.0",
@ -96,7 +96,8 @@ lazy val root = (project in file(".")).settings(
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
"org.recast4j" % "recast" % "1.2.5",
"org.recast4j" % "detour" % "1.2.5",
"com.lihaoyi" %% "pprint" % "0.6.0"
"com.lihaoyi" %% "pprint" % "0.6.0",
"org.scalatest" %% "scalatest" % "3.2.2" % "test"
),
// Determine OS version of JavaFX binaries
@ -208,4 +209,7 @@ initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
// To learn more about multi-project builds, head over to the official sbt
// documentation at http://www.scala-sbt.org/documentation.html
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
addCompilerPlugin(
"org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full
)
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.4.3"

12
src/main/resources/application.conf

@ -1,7 +1,7 @@
# jme-dispatcher {
# type = "Dispatcher"
# name = "JME-Thread"
# executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
# throughput = 1
# }
jme-dispatcher {
type = "Dispatcher"
name = "JME-Thread"
executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
throughput = 1
}
# akka.jvm-exit-on-fatal-error = on

6
src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala

@ -32,13 +32,13 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
def apply[A](fa: _root_.monix.bio.Task[A]): IO[A] = fa.to[IO]
}
private lazy val (defaultConsoleLogger, release1) =
val (defaultConsoleLogger, release1) =
consoleLogger[IO](minLevel = Level.Debug)
.withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated
.unsafeRunSync()
private lazy val (mainFileLogger, release2) =
val (mainFileLogger, release2) =
fileLogger[IO](
"application-log-2.log",
Formatter.json,
@ -51,7 +51,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
lm.copy(message = lm.message.map(s => fansi.Str(s).plainText))
)
private lazy val (eventBusFileLogger, release3) =
val (eventBusFileLogger, release3) =
fileLogger[IO](
"eventbus.log",
Formatter.json,

6
src/main/scala/wow/doge/mygame/AppError.scala

@ -0,0 +1,6 @@
package wow.doge.mygame
sealed trait AppError
object AppError {
case class TimeoutError(reason: String) extends AppError
}

7
src/main/scala/wow/doge/mygame/Dispatchers.scala

@ -0,0 +1,7 @@
package wow.doge.mygame
import akka.actor.typed.DispatcherSelector
object Dispatchers {
val jmeDispatcher = DispatcherSelector.fromConfig("jme-dispatcher")
}

24
src/main/scala/wow/doge/mygame/Main.scala

@ -5,8 +5,7 @@ import scala.concurrent.duration._
import _root_.monix.bio.BIOApp
import _root_.monix.bio.Task
import _root_.monix.bio.UIO
import akka.actor.typed.ActorSystem
import akka.actor.typed.SpawnProtocol
import _root_.monix.execution.Scheduler
import akka.util.Timeout
import cats.effect.ExitCode
import cats.effect.Resource
@ -22,31 +21,24 @@ object Main extends BIOApp with MainModule {
JLogger.getLogger("").setLevel(Level.SEVERE)
implicit val timeout = Timeout(1.second)
override def scheduler: Scheduler = schedulers.async
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
for {
logger <-
consoleLogger().withAsync(
timeWindow = 1.milliseconds,
maxBufferSize = Some(2000)
maxBufferSize = Some(100)
) |+|
fileLogger(
"application-log-1.log",
Formatter.json
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
jmeScheduler <- jMESchedulerResource
implicit0(actorSystem: ActorSystem[SpawnProtocol.Command]) <-
actorSystemResource(logger)
// gameApp <- {
// // new BulletAppState()
// // bas.setThreadingType(Thr)
// // gameAppResource(new StatsAppState())
// wire[GameAppResource].get
// }
actorSystem <- actorSystemResource(logger, schedulers.async)
_ <- Resource.liftF(
new MainApp(
logger,
// gameApp,
// actorSystem,
jmeScheduler,
schedulers,
consoleStream
@ -56,11 +48,11 @@ object Main extends BIOApp with MainModule {
} yield ()
def run(args: List[String]): UIO[ExitCode] = {
lazy val consoleStream = GenericConsoleStream.textAreaStream()
val consoleStream = GenericConsoleStream.textAreaStream()
Console.withOut(consoleStream)(
appResource(consoleStream)
.use(_ => Task.unit >> Task(consoleStream.close()))
.use(_ => Task.unit)
.flatMap(_ => Task(consoleStream.close()))
.onErrorHandleWith(ex => UIO(ex.printStackTrace()))
.as(ExitCode.Success)
)

209
src/main/scala/wow/doge/mygame/MainApp.scala

@ -1,5 +1,6 @@
package wow.doge.mygame
import akka.actor.typed.ActorRef
import akka.actor.typed.ActorSystem
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
@ -8,10 +9,12 @@ import cats.effect.Resource
import cats.effect.concurrent.Deferred
import cats.syntax.eq._
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
import com.jme3.asset.plugins.ZipLocator
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.input.InputManager
import com.jme3.material.Material
import com.jme3.material.MaterialDef
import com.jme3.math.FastMath
import com.jme3.renderer.Camera
import com.jme3.renderer.RenderManager
import com.jme3.renderer.ViewPort
@ -23,18 +26,21 @@ import io.odin.Logger
import monix.bio.Fiber
import monix.bio.IO
import monix.bio.Task
import monix.execution.exceptions.DummyException
import monix.bio.UIO
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.GameAppResource
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.game.entities.EntityIds
import wow.doge.mygame.game.entities.NpcActorSupervisor
import wow.doge.mygame.game.entities.NpcMovementActor
import wow.doge.mygame.game.entities.PlayerActorSupervisor
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
@ -48,8 +54,14 @@ import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.GenericConsoleStream
import wow.doge.mygame.utils.wrappers.jme.AppNode
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import com.jme3.math.FastMath
import wow.doge.mygame.utils.wrappers.jme.AssetManager
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import monix.reactive.Observable
import monix.eval.Coeval
import wow.doge.mygame.utils.IOUtils
import com.jme3.math.ColorRGBA
class MainApp(
logger: Logger[Task],
@ -57,37 +69,52 @@ class MainApp(
schedulers: Schedulers,
consoleStream: GenericConsoleStream[TextArea]
)(implicit
spawnProtocol: ActorSystem[SpawnProtocol.Command],
@annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler
spawnProtocol: ActorRef[SpawnProtocol.Command],
timeout: Timeout,
scheduler: Scheduler
) {
val scriptSystemInit =
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
val eventsModule = new EventsModule(spawnProtocol)
val eventsModule = new EventsModule(scheduler, spawnProtocol)
class TestClass(
playerEventBus: GameEventBus[PlayerEvent],
tickEventBus: GameEventBus[TickEvent]
)
def gameInit: Resource[Task, Fiber[Throwable, Unit]] =
GameApp.resource(logger, jmeThread, schedulers).evalMap {
wire[GameAppResource].resource.evalMap {
case gameApp -> gameAppFib =>
for {
playerEventBus <- eventsModule.playerEventBusTask
mainEventBus <- eventsModule.mainEventBusTask
tickEventBus <- eventsModule.tickEventBusTask
gameAppActor <- AkkaUtils.spawnActorL(
GameAppActor.Props(tickEventBus).behavior,
"gameAppActor"
obs <- playerEventBus.askL[Observable[PlayerMovementEvent]](
ObservableSubscription(_)
)
_ <- IOUtils.toIO(
obs
.doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void)
.completedL
.startAndForget
)
_ <- gameAppActor !! GameAppActor.Start
inputManager <- gameApp.inputManager
assetManager <- gameApp.assetManager
assetManager <- UIO.pure(gameApp.assetManager)
stateManager <- gameApp.stateManager
camera <- gameApp.camera
rootNode <- Task.pure(gameApp.rootNode)
enqueueR <- Task(gameApp.enqueue _)
rootNode <- UIO.pure(gameApp.rootNode)
enqueueR <- UIO(gameApp.enqueue _)
viewPort <- gameApp.viewPort
physicsSpace <- UIO.pure(gameApp.physicsSpace)
_ <- logger.info("before")
// jfxUI <- gameApp.jfxUI
gameAppActor <- gameApp.spawnGameActor(
GameAppActor.Props(tickEventBus).behavior,
"gameAppActor"
)
_ <- gameAppActor !! GameAppActor.Start
consoleTextArea <- Task(new TextArea {
text = "hello \n"
editable = false
@ -98,9 +125,6 @@ class MainApp(
// _ <- Task(consoleStream := consoleTextArea)
// _ <- Task(jfxUI += consoleTextArea)
_ <- logger.info("after")
// bulletAppState <- Task(new BulletAppState())
// _ <- Task(stateManager.attach(bulletAppState))
// bulletAppState <- Task.pure(gameApp.bulletAppstate)
_ <- logger.info("Initializing console stream")
_ <-
wire[MainAppDelegate]
@ -109,6 +133,13 @@ class MainApp(
} yield gameAppFib
}
// val x: Task[Unit] = for {
// tickEventBus <- eventsModule.tickEventBusTask
// playerEventBus <- eventsModule.playerEventBusTask
// _ <- UIO(wire[TestClass])
// _ <- gameInit(tickEventBus).use(_.join)
// } yield ()
val program = for {
scriptSystem <- scriptSystemInit
launchSignal <- Deferred[Task, Launcher.LauncherResult]
@ -125,6 +156,7 @@ class MainApp(
*/
else
gameInit.use(_.join)
} yield ()
}
@ -139,47 +171,44 @@ class MainAppDelegate(
inputManager: InputManager,
assetManager: AssetManager,
stateManager: AppStateManager,
physicsSpace: PhysicsSpace[Task],
camera: Camera,
viewPort: ViewPort,
enqueueR: Function1[() => Unit, Unit],
rootNode: AppNode[Task] @@ GameAppTags.RootNode
// bulletAppState: BulletAppState
)(implicit
spawnProtocol: ActorSystem[SpawnProtocol.Command],
@annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler
spawnProtocol: ActorRef[SpawnProtocol.Command],
timeout: Timeout,
scheduler: Scheduler
) {
// val physicsSpace = bulletAppState.physicsSpace
val physicsSpace = gameApp.physicsSpace
def init(
// appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea]
) =
for {
_ <- loggerL.info("Initializing Systems")
_ <- loggerL.debug(physicsSpace.toString())
_ <- Task(
assetManager.registerLocator(
os.rel / "assets" / "town.zip",
classOf[ZipLocator]
)
_ <- assetManager.registerLocator(
os.rel / "assets" / "town.zip",
classOf[ZipLocator]
)
_ <- loggerL.info("test")
// _ <- Task(consoleStream.println("text"))
_ <- DefaultGameLevel(assetManager, viewPort)
.addToGame(rootNode, physicsSpace)
.flatMap(_.addToGame(rootNode, physicsSpace).hideErrors)
.onErrorHandleWith(e => loggerL.error(e.toString))
_ <- createPlayerController()
.absorbWith(e => DummyException(e.toString()))
.onErrorHandleWith(e => loggerL.error(e.toString))
// .onErrorRestart(3)
_ <- wire[GameInputHandler.Props].begin
// .onErrorRestart(3)
// johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler)
johnActor <- createTestNpc("John")
.onErrorHandleWith(e => IO.raiseError(new Throwable(e.toString)))
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
// _ <-
// (johnActor !! NpcActorSupervisor.Move(
// ImVector3f(-30, 0, 10)
// )).executeAsync
// johnActor
// .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
// .delayExecution(2.seconds)
// _ <-
// IOUtils
@ -195,53 +224,40 @@ class MainAppDelegate(
def createPlayerController(
// appScheduler: monix.execution.Scheduler
): IO[PlayerController.Error, Unit] = {
): IO[PlayerController.Error, ActorRef[PlayerActorSupervisor.Command]] = {
val playerPos = ImVector3f.ZERO
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
val playerPhysicsControl =
PlayerController.Defaults.defaultPlayerPhysicsControl
.taggedWith[PlayerControllerTags.PlayerTag]
// lazy val camNode =
// PlayerController.Defaults
// .defaultCamerNode(camera, playerPos)
// .taggedWith[PlayerControllerTags.PlayerCameraNode]
val playerModel = assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
val mbPlayerNode = PlayerController.Defaults
.defaultPlayerNode(
playerPos,
playerModel,
playerPhysicsControl
)
val cameraPivotNode = new Node(EntityIds.CameraPivot.value)
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
for {
playerNode <- IO.fromEither(mbPlayerNode)
_ <- IO(cameraPivotNode.addControl(new FollowControl(playerNode)))
.onErrorHandleWith(e =>
IO.raiseError(PlayerController.GenericError(e.getMessage()))
)
camNode <- IO(
playerModel <-
assetManager
.loadModelAs[Node](modelPath)
.map(_.withRotate(0, FastMath.PI, 0))
.mapError(PlayerController.CouldNotCreatePlayerModel)
playerNode <- UIO(
PlayerController.Defaults
.defaultCamerNode(camera, cameraPivotNode, playerNode, playerPos)
).onErrorHandleWith(e =>
IO.raiseError(PlayerController.GenericError(e.getMessage()))
).map(_.taggedWith[PlayerControllerTags.PlayerCameraNode])
// _ <- Task {
// val chaseCam = new ChaseCamera(camera, playerNode, inputManager)
// chaseCam.setSmoothMotion(false)
// chaseCam.setLookAtOffset(new Vector3f(0, 1.5f, 10))
// chaseCam
// }
// .onErrorHandleWith(e =>
// IO.raiseError(PlayerController.GenericError(e.getMessage()))
// )
_ <- wire[PlayerController.Props].create
} yield ()
.defaultPlayerNode(
playerPos,
playerModel,
playerPhysicsControl
)
)
cameraPivotNode <- UIO(
new Node(EntityIds.CameraPivot.value)
.withControl(new FollowControl(playerNode))
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
)
camNode <- UIO(
PlayerController.Defaults
.defaultCamerNode(camera, playerPos)
.taggedWith[PlayerControllerTags.PlayerCameraNode]
)
playerActor <- wire[PlayerController.Props].create
} yield playerActor
}
def createTestNpc(
// appScheduler: monix.execution.Scheduler,
npcName: String
@ -250,12 +266,7 @@ class MainAppDelegate(
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
// (1f, 2.1f, 10f)
.withJumpForce(ImVector3f(0, 5f, 0))
val mbNpcNode = PlayerController.Defaults.defaultNpcNode(
assetManager,
initialPos,
npcPhysicsControl,
npcName
)
val npcActorTask = AkkaUtils.spawnActorL(
NpcActorSupervisor
.Props(
@ -263,6 +274,7 @@ class MainAppDelegate(
enqueueR,
initialPos,
// tickEventBus,
npcName,
npcPhysicsControl
).behavior,
npcName,
@ -271,28 +283,35 @@ class MainAppDelegate(
.behavior,
s"${npcName}-npcActorSupervisor"
)
// .taggedWith[PlayerControllerTags.PlayerTag]
for {
npcNode <- IO.fromEither(mbNpcNode)
npcActor <- npcActorTask
// _ <- IO {
// physicsSpace += npcPhysicsControl
// physicsSpace += npcNode
// // rootNode += npcNode
// }
_ <- physicsSpace += npcPhysicsControl
_ <- physicsSpace += npcNode
_ <- rootNode += npcNode
materialDef <- assetManager.loadAssetAs[MaterialDef](
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
)
material = new Material(materialDef)
_ = material.setColor("Color", ColorRGBA.Blue)
mesh = PlayerController.Defaults.defaultMesh.withMaterial(material)
npcNode = PlayerController.Defaults.defaultNpcNode(
mesh,
initialPos,
npcPhysicsControl,
npcName
)
npcActor <- npcActorTask.hideErrors
_ <- (for {
_ <- physicsSpace += npcPhysicsControl
_ <- physicsSpace += npcNode
_ <- rootNode += npcNode
} yield ()).hideErrors
} yield npcActor
}
}
class FollowControl(playerNode: Node) extends AbstractControl {
override def controlUpdate(tpf: Float): Unit = {
override def controlUpdate(tpf: Float): Unit =
this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
}
override def controlRender(
rm: RenderManager,
vp: ViewPort

11
src/main/scala/wow/doge/mygame/MainModule.scala

@ -5,15 +5,22 @@ import cats.effect.Resource
import io.odin.Logger
import monix.bio.Task
import wow.doge.mygame.executors.ExecutorsModule
import akka.actor.BootstrapSetup
import monix.execution.Scheduler
trait MainModule extends ExecutorsModule {
def actorSystemResource(
logger: Logger[Task]
logger: Logger[Task],
scheduler: Scheduler
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
Resource.make(
logger.info("Creating Actor System") >> Task(
ActorSystem(SpawnProtocol(), name = "GameActorSystem")
ActorSystem(
SpawnProtocol(),
name = "GameActorSystem",
BootstrapSetup().withDefaultExecutionContext(scheduler)
)
)
)(sys =>
for {

29
src/main/scala/wow/doge/mygame/executors/ExecutorsModule.scala

@ -1,23 +1,32 @@
package wow.doge.mygame.executors
import cats.effect.Resource
import monix.bio.IO
import monix.bio.Task
import monix.bio.UIO
import monix.execution.Scheduler
trait ExecutorsModule {
lazy val schedulers = Schedulers()
// Resource.make(
// Task(
// new Schedulers(
// jme = Scheduler
// .singleThread(name = "JME-Application-Thread", daemonic = false)
// )
// )
// )(s => Task(s.jme.shutdown()))
lazy val jMESchedulerResource = Resource.make(
val schedulers = Schedulers()
val acquire: UIO[Either[Error, Int]] =
IO.pure(1).onErrorHandleWith(_ => IO.raiseError(Error)).attempt
// : Resource[IO[Error, Unit], Unit]
val res = Resource.make(acquire)(_ => IO.unit)
val x: Task[Either[Error, Unit]] = res.use {
case Right(value) => Task(Right(println(s"got $value")))
case Left(value) => Task(Left(value))
}
val z = x.onErrorHandleWith(ex => UIO(Right(ex.printStackTrace())))
val y: IO[Error, Unit] = z >>
x.hideErrors.rethrow
val jMESchedulerResource = Resource.make(
Task(
Scheduler
.singleThread(name = "JME-Application-Thread", daemonic = false)
)
)(e => Task(e.shutdown()))
}
sealed trait Error
case object Error extends Error

4
src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala

@ -42,13 +42,13 @@ object SwingExecutorService extends GUIExecutorService {
object JMEExecutorService extends GUIExecutorService {
override def execute(command: Runnable) =
JMERunner.runner.enqueue(command)
JMERunner.runner.get.apply(command)
// new SingleThreadEventExecutor()
sys.addShutdownHook(JMEExecutorService.shutdown())
}
object JMERunner {
var runner: com.jme3.app.Application = null
var runner: Option[Runnable => Unit] = None
}

169
src/main/scala/wow/doge/mygame/game/GameApp.scala

@ -1,9 +1,16 @@
package wow.doge.mygame.game
import scala.concurrent.duration._
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.Props
import akka.actor.typed.SpawnProtocol
import akka.actor.typed.scaladsl.AskPattern._
import akka.util.Timeout
import cats.effect.Resource
import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager
import com.jme3.scene.Node
@ -12,20 +19,23 @@ import com.jme3.system.AppSettings
import com.softwaremill.tagging._
import com.typesafe.scalalogging.{Logger => SLogger}
import io.odin.Logger
import monix.bio.IO
import monix.bio.Fiber
import monix.bio.Task
import monix.bio.UIO
import monix.catnap.ConcurrentChannel
import monix.catnap.ConsumerF
import monix.catnap.Semaphore
import monix.eval.Coeval
import monix.execution.CancelableFuture
import monix.execution.CancelablePromise
import monix.execution.Scheduler
import wow.doge.mygame.executors.JMERunner
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.utils.GenericTimerActor
import wow.doge.mygame.game.subsystems.ui.JFxUI
import wow.doge.mygame.implicits._
import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.wrappers.jme._
sealed trait Error
case object FlyCamNotExists extends Error
import wow.doge.mygame.Dispatchers
object GameAppTags {
sealed trait RootNode
@ -33,47 +43,58 @@ object GameAppTags {
sealed trait GuiNode
}
class GameApp private (logger: Logger[Task], app: SimpleAppExt) {
class GameApp private[game] (
logger: Logger[Task],
app: SimpleAppExt,
gameActor: ActorRef[TestGameActor.Command],
gameSpawnProtocol: ActorRef[SpawnProtocol.Command]
) {
def stateManager: Task[AppStateManager] = Task(app.getStateManager())
def inputManager: Task[InputManager] = Task(app.getInputManager())
def assetManager: Task[AssetManager] = Task(app.getAssetManager())
def assetManager = new AssetManager(app.getAssetManager())
def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
def guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
def flyCam =
IO(app.getFlyByCamera()).onErrorHandleWith(_ =>
IO.raiseError(FlyCamNotExists)
)
val guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
def flyCam = Option(app.getFlyByCamera())
def camera = Task(app.getCamera())
def viewPort = Task(app.getViewPort())
// def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
val rootNode =
AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode]
val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace)
def enqueue(cb: () => Unit) =
app.enqueue(new Runnable {
override def run() = cb()
})
def enqueue(cb: () => Unit) = app.enqueueR(() => cb())
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
def spawnGameActor[T](
behavior: Behavior[T],
actorName: String,
props: Props = Dispatchers.jmeDispatcher
)(implicit scheduler: akka.actor.typed.Scheduler) =
AkkaUtils.spawnActorL(behavior, actorName, props)(
2.seconds,
scheduler,
gameSpawnProtocol
)
def scheduler = app.scheduler
def jfxUI = JFxUI(app)
}
object GameApp {
def resource(
logger: Logger[Task],
jmeThread: Scheduler,
schedulers: Schedulers
) =
class GameAppResource(
logger: Logger[Task],
jmeThread: Scheduler,
schedulers: Schedulers
)(implicit
timeout: Timeout,
scheduler: akka.actor.typed.Scheduler,
spawnProtocol: ActorRef[SpawnProtocol.Command]
) {
def resource: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
Resource.make {
lazy val bullet = new BulletAppState
for {
// bullet <- Task(new BulletAppState)
// startSignal <- Task(CancelablePromise[Unit]())
app <- Task(new SimpleAppExt(schedulers, bullet))
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
_ <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
@ -88,34 +109,76 @@ object GameApp {
fib <- Task(app.start).executeOn(jmeThread).start
_ <- Task.deferFuture(app.started)
// _ <- Task.fromCancelablePromise(startSignal)
_ <- Task(pprint.log(bullet.toString()))
_ <- Task(println(bullet.physicsSpace.toString()))
gameApp <- Task(new GameApp(logger, app))
} yield gameApp -> fib
}(_._2.cancel)
/**
* 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))
}
testGameActor <- AkkaUtils.spawnActorL(
new TestGameActor.Props().create,
"testGameActor",
Dispatchers.jmeDispatcher
)
sp <- testGameActor.askL(TestGameActor.GetSpawnProtocol(_))
gameApp <- Task(new GameApp(logger, app, testGameActor, sp))
_ <- Task {
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
app.cancelToken = Some(fut)
}
} yield (gameApp, fib)
} {
case (gameApp, fib) =>
fib.cancel >> UIO(JMERunner.runner = None)
}
}
object SynchedObject {
def apply[A](obj: A) =
Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock)))
object GameApp {}
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.ActorContext
object TestGameActor {
sealed trait Command
case object Ping extends Command
case class Stop(stopSignal: ActorRef[CancelableFuture[Unit]]) extends Command
case class GetSpawnProtocol(
replyTo: ActorRef[ActorRef[SpawnProtocol.Command]]
) extends Command
import scala.concurrent.duration._
class Props() {
def create =
Behaviors.setup[Command] { ctx =>
ctx.spawn(
GenericTimerActor
.Props(ctx.self, Ping, 1000.millis)
.behavior,
"pingTimer"
) ! GenericTimerActor.Start
new TestGameActor(ctx, this).receive
}
}
}
class TestGameActor(
ctx: ActorContext[TestGameActor.Command],
props: TestGameActor.Props
) {
import TestGameActor._
val stopPromise = CancelablePromise[Unit]()
def receive =
Behaviors
.receiveMessage[Command] {
case Stop(replyTo) =>
ctx.log.infoP("stopping")
replyTo ! stopPromise.future
Behaviors.stopped
case Ping =>
ctx.log.debugP("ping")
Behaviors.same
case GetSpawnProtocol(replyTo) =>
val sp = ctx.spawn(SpawnProtocol(), "gameSpawnProtocol")
replyTo ! sp
Behaviors.same
}
.receiveSignal {
case (_, akka.actor.typed.PostStop) =>
stopPromise.success(())
Behaviors.same
}
}
object Ops {

11
src/main/scala/wow/doge/mygame/game/GameAppActor.scala

@ -5,7 +5,7 @@ import scala.concurrent.duration._
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.game.TickGenerator.Send
import wow.doge.mygame.game.entities.GenericTimerActor
import wow.doge.mygame.utils.GenericTimerActor
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
@ -19,14 +19,7 @@ object GameAppActor {
case object Pause extends Command
case object Stop extends Command
case class Props(
// app: SimpleAppExt,
// akkaScheduler: Scheduler,
// schedulers: Schedulers,
// spawnProtocol: ActorRef[SpawnProtocol.Command],
// loggerL: Logger[Task]
tickEventBus: GameEventBus[TickEvent]
) {
case class Props(tickEventBus: GameEventBus[TickEvent]) {
def behavior =
Behaviors.setup[Command] { ctx =>
ctx.log.infoP("Hello from GameAppActor")

29
src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala

@ -1,9 +1,12 @@
package wow.doge.mygame.game
import scala.collection.immutable.Queue
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState
import com.jme3.bullet.BulletAppState
import monix.bio.Task
import monix.execution.CancelableFuture
import monix.execution.CancelablePromise
@ -11,7 +14,6 @@ import monix.execution.Scheduler
import monix.execution.atomic.Atomic
import wow.doge.mygame.executors.GUIExecutorService
import wow.doge.mygame.executors.Schedulers
import com.jme3.bullet.BulletAppState
// import wow.doge.mygame.implicits._
class SimpleAppExt(
schedulers: Schedulers,
@ -25,27 +27,30 @@ class SimpleAppExt(
*/
private val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
// lazy val bulletAppState: BulletAppState = new BulletAppState
// def bulletAppState = synchronized(_bulletAppState)
// def tickObservable: Observable[Float] = tickSubject
// lazy val bulletAppState = stateManager.state[BulletAppState]()
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
var cancelToken: Option[() => Future[Unit]] = None
def started: CancelableFuture[Unit] = startSignal.future
override def simpleInitApp(): Unit = {
// _bulletAppState = new BulletAppState
stateManager.attach(bulletAppState)
startSignal.success(())
}
override def simpleUpdate(tpf: Float): Unit = {}
override def stop(): Unit = {
super.stop()
override def stop(waitFor: Boolean): Unit = {
cancelToken match {
case Some(value) =>
value().foreach { _ =>
pprint.log("Called cancel in simpleapp")
super.stop(true)
}
case None =>
pprint.log("Called cancel in simpleapp")
super.stop(true)
}
}
def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
@ -74,7 +79,7 @@ class SimpleAppExt(
enqueue(command)
}
lazy val scheduler = Scheduler(JMEExecutorService)
val scheduler = Scheduler(JMEExecutorService)
}
object SimpleAppExt {
private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T)

68
src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala

@ -10,6 +10,7 @@ import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout
import cats.syntax.show._
import monix.execution.CancelableFuture
import monix.execution.CancelablePromise
import wow.doge.mygame.game.subsystems.movement.CanMove
@ -26,6 +27,7 @@ import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.GenericTimerActor
object NpcActorSupervisor {
sealed trait Command
@ -35,10 +37,7 @@ object NpcActorSupervisor {
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
) extends Command
private case object DoneMoving extends Command
// private case class MovementResponse(response: CancelableFuture[_]) extends Command
private case class LogError(err: Throwable) extends Command
private case object NoOp extends Command
private case class MovementFailed(err: Throwable) extends Command
final case class Props(
@ -47,18 +46,19 @@ object NpcActorSupervisor {
initialPos: ImVector3f
) {
def behavior =
Behaviors.setup[Command] { ctx =>
val npcMovementActor = ctx.spawn(
(npcMovementActorBehavior),
s"npc-${npcName}-NpcMovementActor"
)
Behaviors.withMdc(Map("actorName" -> npcName))(
Behaviors.setup[Command] { ctx =>
val npcMovementActor = ctx.spawn(
(npcMovementActorBehavior),
s"npc-${npcName}-NpcMovementActor"
)
new NpcActorSupervisor(ctx, this, Children(npcMovementActor))
.idle(State())
}
new NpcActorSupervisor(ctx, this, Children(npcMovementActor))
.idle(State())
}
)
}
final case class State(
)
final case class State()
final case class Children(
npcMovementActor: ActorRef[NpcMovementActor.Command]
)
@ -79,12 +79,12 @@ class NpcActorSupervisor(
100.millis
)
.behavior,
s"npc-John-NpcActorTimer"
s"npc-${props.npcName}-NpcActorTimer"
)
def idle(state: State): Behavior[NpcActorSupervisor.Command] =
Behaviors.setup { _ =>
ctx.log.debugP("Inside Idle State")
ctx.log.debugP(s"npcActor-${props.npcName}: Entered Idle State")
Behaviors.receiveMessage[Command] {
case m @ Move(pos) =>
ctx.ask(
@ -99,7 +99,7 @@ class NpcActorSupervisor(
moving(state, move.pos, signal)
case LogError(err) =>
ctx.log.warnP(err.getMessage())
logError(err)
Behaviors.same
case _ => Behaviors.unhandled
}
@ -111,19 +111,15 @@ class NpcActorSupervisor(
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
): Behavior[NpcActorSupervisor.Command] =
Behaviors.setup { _ =>
ctx.log.debugP(s"npcActor-${props.npcName}: Entered Moving State")
movementTimer ! GenericTimerActor.Start
// ctx
// .ask(state.npcMovementActor, NpcMovementActor2.MoveTo(targetPos, _))(
// _.fold(LogError(_), MovementResponse(_))
// )
ctx.pipeToSelf(signal) {
case Success(value) => DoneMoving
case Failure(exception) => MovementFailed(exception)
}
Behaviors.receiveMessagePartial[Command] {
case LogError(err) =>
ctx.log.error(err.getMessage())
logError(err)
Behaviors.same
case MovementFailed(err) =>
ctx.self ! LogError(err)
@ -132,6 +128,7 @@ class NpcActorSupervisor(
case m @ Move(pos) =>
movementTimer ! GenericTimerActor.Stop
children.npcMovementActor ! NpcMovementActor.StopMoving
// new movement request received, cancel previous request
signal.cancel()
ctx.ask(
children.npcMovementActor,
@ -143,15 +140,15 @@ class NpcActorSupervisor(
Behaviors.same
case InternalMove(move, signal) =>
moving(state, targetPos, signal)
case NoOp => Behaviors.same
// case MovementResponse(x: CancelableFuture[_]) =>
// // ctx.pipeToSelf(x)(_.)
case DoneMoving =>
movementTimer ! GenericTimerActor.Stop
idle(state)
}
}
def logError(err: Throwable) =
ctx.log.errorP(s"npcActor-${props.npcName}: " + err.getMessage)
}
object NpcMovementActor {
@ -171,6 +168,7 @@ object NpcMovementActor {
val enqueueR: Function1[() => Unit, Unit],
val initialPos: ImVector3f,
// val tickEventBus: GameEventBus[TickEvent],
val npcName: String,
val movable: T
) {
def behavior =
@ -189,9 +187,7 @@ class NpcMovementActor[T](
def location = cm.location(props.movable)
def receive(
state: State
): Behavior[NpcMovementActor.Command] =
def receive(state: State): Behavior[NpcMovementActor.Command] =
Behaviors.receiveMessagePartial {
case AskPosition(replyTo) =>
replyTo ! location
@ -214,19 +210,25 @@ class NpcMovementActor[T](
Behaviors.receiveMessagePartial {
case StopMoving =>
ctx.log.debugP(
"Position at Stop = " + location.toString
show"npcActor-${props.npcName}: Position at Stop = " + location
)
props.enqueueR(() => cm.stop(props.movable))
receive(state)
case MovementTick =>
val dst = ImVector3f.dst(targetPos, location)
val dst = ImVector3f.manhattanDst(targetPos, location)
if (dst <= 10f) {
ctx.self ! StopMoving
reachDestination.success(DoneMoving)
} else {
ctx.log.traceP("Difference = " + dst.toString())
ctx.log.traceP("Current pos = " + location.toString())
ctx.log.traceP(
show"npcActor-${props.npcName}: Difference = " + dst.formatted(
"%.2f"
)
)
ctx.log.traceP(
show"npcActor-${props.npcName}: Current pos = " + location
)
}
Behaviors.same
}
@ -295,7 +297,7 @@ object NpcMovementActorNotUsed {
mainEventBus ! EventBus.Subscribe(npcMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
Behaviors.receiveMessage { msg => Behaviors.same }
Behaviors.receiveMessage(msg => Behaviors.same)
}
}

121
src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala

@ -1,15 +1,11 @@
package wow.doge.mygame.game.entities
import scala.concurrent.duration._
import scala.util.Random
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.LogOptions
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.TimerScheduler
import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level
import wow.doge.mygame.game.subsystems.movement.CanMove
@ -19,7 +15,7 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
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.implicits._
object PlayerActorSupervisor {
sealed trait Command
final case class Props(
@ -28,15 +24,15 @@ object PlayerActorSupervisor {
imMovementActorBehavior: Behavior[ImMovementActor.Command],
playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
) {
def behavior[T: CanMove](movable: T) =
def behavior =
Behaviors.logMessages(
LogOptions()
.withLevel(Level.TRACE)
.withLogger(
Logger[PlayerActorSupervisor[T]].underlying
Logger[PlayerActorSupervisor].underlying
),
Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from PlayerActor")
ctx.log.infoP("Starting PlayerActor")
// spawn children actors
val movementActor =
@ -44,7 +40,7 @@ object PlayerActorSupervisor {
Behaviors
.supervise(imMovementActorBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActorChild"
"playerMovementActor"
)
val playerCameraActor =
@ -55,19 +51,27 @@ object PlayerActorSupervisor {
"playerCameraActorEl"
)
ctx.spawn(
PlayerMovementActor
.Props(
movementActor,
playerCameraActor,
playerEventBus,
tickEventBus
)
.behavior,
"playerMovementAcor"
val playerMovementEl = ctx.spawn(
Behaviors
.supervise(PlayerMovementEventListener(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementEventHandler"
)
//init actors
val renderTickEl = {
val behavior =
Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
// playerCameraActor ! PlayerCameraActor.Tick
Behaviors.same
}
ctx.spawn(behavior, "playerMovementTickListener")
}
//init listeners
playerEventBus ! EventBus.Subscribe(playerMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
playerEventBus ! EventBus.Subscribe(playerCameraEl)
new PlayerActorSupervisor(
@ -84,18 +88,19 @@ object PlayerActorSupervisor {
movementActor: ActorRef[ImMovementActor.Command]
)
}
class PlayerActorSupervisor[T: CanMove](
class PlayerActorSupervisor(
ctx: ActorContext[PlayerActorSupervisor.Command],
props: PlayerActorSupervisor.Props,
children: PlayerActorSupervisor.Children
) {
import PlayerActorSupervisor._
def receive =
Behaviors.receiveMessage[Command] {
case _ =>
// children.movementActor ! ImMovementActor.MovedDown(true)
Behaviors.same
}
Behaviors
.receiveMessage[Command] {
case _ =>
// children.movementActor ! ImMovementActor.MovedDown(true)
Behaviors.same
}
}
object PlayerMovementActor {
@ -115,15 +120,17 @@ object PlayerMovementActor {
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementEventHandler"
)
val renderTickElBehavior =
Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
// playerCameraActor ! PlayerCameraActor.Tick
Behaviors.same
}
val renderTickEl =
ctx.spawn(renderTickElBehavior, "playerMovementTickListener")
val renderTickEl = {
val behavior =
Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
// playerCameraActor ! PlayerCameraActor.Tick
Behaviors.same
}
ctx.spawn(behavior, "playerMovementTickListener")
}
playerEventBus ! EventBus.Subscribe(playerMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
@ -131,47 +138,3 @@ object PlayerMovementActor {
}
}
}
object GenericTimerActor {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case class TimerKey(seed: Long)
case class Props[T](
target: ActorRef[T],
messageToSend: T,
timeInterval: FiniteDuration
) {
val behavior = Behaviors.withTimers[Command] { timers =>
new GenericTimerActor(timers, TimerKey(Random.nextLong()), this).idle
}
}
}
class GenericTimerActor[T](
timers: TimerScheduler[GenericTimerActor.Command],
timerKey: GenericTimerActor.TimerKey,
props: GenericTimerActor.Props[T]
) {
import GenericTimerActor._
val idle: Behavior[Command] =
Behaviors.receiveMessage {
case Start =>
timers.startTimerWithFixedDelay(timerKey, Send, props.timeInterval)
active
case _ => Behaviors.unhandled
}
val active: Behavior[Command] =
Behaviors.receiveMessagePartial {
case Send =>
props.target ! props.messageToSend
Behaviors.same
case Stop =>
timers.cancel(timerKey)
idle
}
}

130
src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala

@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import cats.implicits._
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.bullet.control.BetterCharacterControl
@ -13,24 +12,22 @@ import com.jme3.renderer.Camera
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.jme3.scene.shape.Box
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.IO
import monix.bio.Task
import monix.bio.UIO
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.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.wrappers.jme._
import monix.bio.UIO
object PlayerControllerTags {
sealed trait PlayerTag
sealed trait PlayerCameraNode
@ -40,7 +37,9 @@ object PlayerControllerTags {
object PlayerController {
sealed trait Error
case class CouldNotCreatePlayerNode(reason: String) extends Error
case class GenericError(reason: String) extends Error
case class CouldNotCreatePlayerModel(
reason: wow.doge.mygame.utils.wrappers.jme.AssetManager.Error
) extends Error
class Props(
enqueueR: Function1[() => Unit, Unit],
@ -65,9 +64,8 @@ object PlayerController {
val playerActorBehavior = {
val movementActorBeh = new ImMovementActor.Props(
enqueueR,
playerPhysicsControl,
camera
).behavior
).behavior(playerPhysicsControl)
val cameraActorBeh = new PlayerCameraActor.Props(
cameraPivotNode,
enqueueR,
@ -78,36 +76,19 @@ object PlayerController {
tickEventBus,
movementActorBeh,
cameraActorBeh
).behavior(playerPhysicsControl)
).behavior
}
val create: IO[Error, Unit] =
val create: UIO[ActorRef[PlayerActorSupervisor.Command]] =
(for {
playerActor <- AkkaUtils.spawnActorL(
playerActorBehavior,
"playerActorSupervisor"
)
// _ <- Task(rootNode += playerNode)
// _ <- Task(pprint.log("Physicsspace = " + physicsSpace.toString()))
// _ <- Task(pprint.log("playerNode = " + playerNode.toString()))
playerActor <-
AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
_ <- rootNode += playerNode
_ <- physicsSpace += playerNode
_ <- physicsSpace += playerPhysicsControl
_ <- IO {
// physicsSpace += playerNode
// physicsSpace += playerPhysicsControl
// rootNode += cameraNode
cameraPivotNode += cameraNode
// playerNode += cameraPivotNode
// rootNode += cameraPivotNode
}
_ = cameraPivotNode += cameraNode
_ <- rootNode += cameraPivotNode
} yield ())
.onErrorHandleWith(e =>
UIO(e.printStackTrace()) >> IO.raiseError(
GenericError(e.getMessage())
)
)
// .executeOn(appScheduler)