forked from nova/jmonkey-test
blah
This commit is contained in:
parent
64480e8e03
commit
2e05cb35fe
@ -1 +1,4 @@
|
|||||||
version = "2.6.4"
|
version = "2.6.4"
|
||||||
|
rewrite {
|
||||||
|
rules = [SortImports, RedundantBraces]
|
||||||
|
}
|
||||||
|
12
build.sbt
12
build.sbt
@ -61,15 +61,15 @@ lazy val root = (project in file(".")).settings(
|
|||||||
"org.jmonkeyengine" % "jme3-blender" % jmeVersion,
|
"org.jmonkeyengine" % "jme3-blender" % jmeVersion,
|
||||||
"com.github.stephengold" % "Minie" % "3.0.0",
|
"com.github.stephengold" % "Minie" % "3.0.0",
|
||||||
"com.simsilica" % "zay-es" % "1.2.1",
|
"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,
|
"com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full,
|
||||||
"org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10",
|
"org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10",
|
||||||
"org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10",
|
"org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10",
|
||||||
"org.codehaus.groovy" % "groovy-all" % "3.0.6" pomOnly (),
|
"org.codehaus.groovy" % "groovy-all" % "3.0.6" pomOnly (),
|
||||||
"org.scalafx" %% "scalafx" % "14-R19",
|
"org.scalafx" %% "scalafx" % "14-R19",
|
||||||
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.10",
|
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.10",
|
||||||
"org.typelevel" %% "cats-core" % "2.1.1",
|
"org.typelevel" %% "cats-core" % "2.3.0",
|
||||||
"org.typelevel" %% "cats-effect" % "2.1.4",
|
"org.typelevel" %% "cats-effect" % "2.3.0",
|
||||||
"io.monix" %% "monix" % "3.2.2",
|
"io.monix" %% "monix" % "3.2.2",
|
||||||
"io.monix" %% "monix-bio" % "1.1.0",
|
"io.monix" %% "monix-bio" % "1.1.0",
|
||||||
"io.circe" %% "circe-core" % "0.13.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",
|
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
|
||||||
"org.recast4j" % "recast" % "1.2.5",
|
"org.recast4j" % "recast" % "1.2.5",
|
||||||
"org.recast4j" % "detour" % "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
|
// 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
|
// To learn more about multi-project builds, head over to the official sbt
|
||||||
// documentation at http://www.scala-sbt.org/documentation.html
|
// documentation at http://www.scala-sbt.org/documentation.html
|
||||||
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
|
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"
|
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.4.3"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# jme-dispatcher {
|
jme-dispatcher {
|
||||||
# type = "Dispatcher"
|
type = "Dispatcher"
|
||||||
# name = "JME-Thread"
|
name = "JME-Thread"
|
||||||
# executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
|
executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
|
||||||
# throughput = 1
|
throughput = 1
|
||||||
# }
|
}
|
||||||
# akka.jvm-exit-on-fatal-error = on
|
# akka.jvm-exit-on-fatal-error = on
|
@ -32,13 +32,13 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
|||||||
def apply[A](fa: _root_.monix.bio.Task[A]): IO[A] = fa.to[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)
|
consoleLogger[IO](minLevel = Level.Debug)
|
||||||
.withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
.withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
||||||
.allocated
|
.allocated
|
||||||
.unsafeRunSync()
|
.unsafeRunSync()
|
||||||
|
|
||||||
private lazy val (mainFileLogger, release2) =
|
val (mainFileLogger, release2) =
|
||||||
fileLogger[IO](
|
fileLogger[IO](
|
||||||
"application-log-2.log",
|
"application-log-2.log",
|
||||||
Formatter.json,
|
Formatter.json,
|
||||||
@ -51,7 +51,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
|||||||
lm.copy(message = lm.message.map(s => fansi.Str(s).plainText))
|
lm.copy(message = lm.message.map(s => fansi.Str(s).plainText))
|
||||||
)
|
)
|
||||||
|
|
||||||
private lazy val (eventBusFileLogger, release3) =
|
val (eventBusFileLogger, release3) =
|
||||||
fileLogger[IO](
|
fileLogger[IO](
|
||||||
"eventbus.log",
|
"eventbus.log",
|
||||||
Formatter.json,
|
Formatter.json,
|
||||||
|
6
src/main/scala/wow/doge/mygame/AppError.scala
Normal file
6
src/main/scala/wow/doge/mygame/AppError.scala
Normal file
@ -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
Normal file
7
src/main/scala/wow/doge/mygame/Dispatchers.scala
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import akka.actor.typed.DispatcherSelector
|
||||||
|
|
||||||
|
object Dispatchers {
|
||||||
|
val jmeDispatcher = DispatcherSelector.fromConfig("jme-dispatcher")
|
||||||
|
}
|
@ -5,8 +5,7 @@ import scala.concurrent.duration._
|
|||||||
import _root_.monix.bio.BIOApp
|
import _root_.monix.bio.BIOApp
|
||||||
import _root_.monix.bio.Task
|
import _root_.monix.bio.Task
|
||||||
import _root_.monix.bio.UIO
|
import _root_.monix.bio.UIO
|
||||||
import akka.actor.typed.ActorSystem
|
import _root_.monix.execution.Scheduler
|
||||||
import akka.actor.typed.SpawnProtocol
|
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import cats.effect.ExitCode
|
import cats.effect.ExitCode
|
||||||
import cats.effect.Resource
|
import cats.effect.Resource
|
||||||
@ -22,31 +21,24 @@ object Main extends BIOApp with MainModule {
|
|||||||
JLogger.getLogger("").setLevel(Level.SEVERE)
|
JLogger.getLogger("").setLevel(Level.SEVERE)
|
||||||
implicit val timeout = Timeout(1.second)
|
implicit val timeout = Timeout(1.second)
|
||||||
|
|
||||||
|
override def scheduler: Scheduler = schedulers.async
|
||||||
|
|
||||||
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
|
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
|
||||||
for {
|
for {
|
||||||
logger <-
|
logger <-
|
||||||
consoleLogger().withAsync(
|
consoleLogger().withAsync(
|
||||||
timeWindow = 1.milliseconds,
|
timeWindow = 1.milliseconds,
|
||||||
maxBufferSize = Some(2000)
|
maxBufferSize = Some(100)
|
||||||
) |+|
|
) |+|
|
||||||
fileLogger(
|
fileLogger(
|
||||||
"application-log-1.log",
|
"application-log-1.log",
|
||||||
Formatter.json
|
Formatter.json
|
||||||
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
||||||
jmeScheduler <- jMESchedulerResource
|
jmeScheduler <- jMESchedulerResource
|
||||||
implicit0(actorSystem: ActorSystem[SpawnProtocol.Command]) <-
|
actorSystem <- actorSystemResource(logger, schedulers.async)
|
||||||
actorSystemResource(logger)
|
|
||||||
// gameApp <- {
|
|
||||||
// // new BulletAppState()
|
|
||||||
// // bas.setThreadingType(Thr)
|
|
||||||
// // gameAppResource(new StatsAppState())
|
|
||||||
// wire[GameAppResource].get
|
|
||||||
// }
|
|
||||||
_ <- Resource.liftF(
|
_ <- Resource.liftF(
|
||||||
new MainApp(
|
new MainApp(
|
||||||
logger,
|
logger,
|
||||||
// gameApp,
|
|
||||||
// actorSystem,
|
|
||||||
jmeScheduler,
|
jmeScheduler,
|
||||||
schedulers,
|
schedulers,
|
||||||
consoleStream
|
consoleStream
|
||||||
@ -56,11 +48,11 @@ object Main extends BIOApp with MainModule {
|
|||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
def run(args: List[String]): UIO[ExitCode] = {
|
def run(args: List[String]): UIO[ExitCode] = {
|
||||||
|
val consoleStream = GenericConsoleStream.textAreaStream()
|
||||||
lazy val consoleStream = GenericConsoleStream.textAreaStream()
|
|
||||||
Console.withOut(consoleStream)(
|
Console.withOut(consoleStream)(
|
||||||
appResource(consoleStream)
|
appResource(consoleStream)
|
||||||
.use(_ => Task.unit >> Task(consoleStream.close()))
|
.use(_ => Task.unit)
|
||||||
|
.flatMap(_ => Task(consoleStream.close()))
|
||||||
.onErrorHandleWith(ex => UIO(ex.printStackTrace()))
|
.onErrorHandleWith(ex => UIO(ex.printStackTrace()))
|
||||||
.as(ExitCode.Success)
|
.as(ExitCode.Success)
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package wow.doge.mygame
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.ActorSystem
|
import akka.actor.typed.ActorSystem
|
||||||
import akka.actor.typed.Scheduler
|
import akka.actor.typed.Scheduler
|
||||||
import akka.actor.typed.SpawnProtocol
|
import akka.actor.typed.SpawnProtocol
|
||||||
@ -8,10 +9,12 @@ import cats.effect.Resource
|
|||||||
import cats.effect.concurrent.Deferred
|
import cats.effect.concurrent.Deferred
|
||||||
import cats.syntax.eq._
|
import cats.syntax.eq._
|
||||||
import com.jme3.app.state.AppStateManager
|
import com.jme3.app.state.AppStateManager
|
||||||
import com.jme3.asset.AssetManager
|
|
||||||
import com.jme3.asset.plugins.ZipLocator
|
import com.jme3.asset.plugins.ZipLocator
|
||||||
import com.jme3.bullet.control.BetterCharacterControl
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
import com.jme3.input.InputManager
|
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.Camera
|
||||||
import com.jme3.renderer.RenderManager
|
import com.jme3.renderer.RenderManager
|
||||||
import com.jme3.renderer.ViewPort
|
import com.jme3.renderer.ViewPort
|
||||||
@ -23,18 +26,21 @@ import io.odin.Logger
|
|||||||
import monix.bio.Fiber
|
import monix.bio.Fiber
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.execution.exceptions.DummyException
|
import monix.bio.UIO
|
||||||
import scalafx.scene.control.TextArea
|
import scalafx.scene.control.TextArea
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import wow.doge.mygame.game.GameApp
|
import wow.doge.mygame.game.GameApp
|
||||||
import wow.doge.mygame.game.GameAppActor
|
import wow.doge.mygame.game.GameAppActor
|
||||||
|
import wow.doge.mygame.game.GameAppResource
|
||||||
import wow.doge.mygame.game.GameAppTags
|
import wow.doge.mygame.game.GameAppTags
|
||||||
import wow.doge.mygame.game.entities.EntityIds
|
import wow.doge.mygame.game.entities.EntityIds
|
||||||
import wow.doge.mygame.game.entities.NpcActorSupervisor
|
import wow.doge.mygame.game.entities.NpcActorSupervisor
|
||||||
import wow.doge.mygame.game.entities.NpcMovementActor
|
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.PlayerController
|
||||||
import wow.doge.mygame.game.entities.PlayerControllerTags
|
import wow.doge.mygame.game.entities.PlayerControllerTags
|
||||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
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.implicits._
|
||||||
import wow.doge.mygame.launcher.Launcher
|
import wow.doge.mygame.launcher.Launcher
|
||||||
import wow.doge.mygame.launcher.Launcher.LauncherResult
|
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.AkkaUtils
|
||||||
import wow.doge.mygame.utils.GenericConsoleStream
|
import wow.doge.mygame.utils.GenericConsoleStream
|
||||||
import wow.doge.mygame.utils.wrappers.jme.AppNode
|
import wow.doge.mygame.utils.wrappers.jme.AppNode
|
||||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
import com.jme3.math.FastMath
|
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(
|
class MainApp(
|
||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
@ -57,37 +69,52 @@ class MainApp(
|
|||||||
schedulers: Schedulers,
|
schedulers: Schedulers,
|
||||||
consoleStream: GenericConsoleStream[TextArea]
|
consoleStream: GenericConsoleStream[TextArea]
|
||||||
)(implicit
|
)(implicit
|
||||||
spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
@annotation.unused timeout: Timeout,
|
timeout: Timeout,
|
||||||
@annotation.unused scheduler: Scheduler
|
scheduler: Scheduler
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val scriptSystemInit =
|
val scriptSystemInit =
|
||||||
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
|
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]] =
|
def gameInit: Resource[Task, Fiber[Throwable, Unit]] =
|
||||||
GameApp.resource(logger, jmeThread, schedulers).evalMap {
|
wire[GameAppResource].resource.evalMap {
|
||||||
case gameApp -> gameAppFib =>
|
case gameApp -> gameAppFib =>
|
||||||
for {
|
for {
|
||||||
playerEventBus <- eventsModule.playerEventBusTask
|
playerEventBus <- eventsModule.playerEventBusTask
|
||||||
mainEventBus <- eventsModule.mainEventBusTask
|
mainEventBus <- eventsModule.mainEventBusTask
|
||||||
tickEventBus <- eventsModule.tickEventBusTask
|
tickEventBus <- eventsModule.tickEventBusTask
|
||||||
gameAppActor <- AkkaUtils.spawnActorL(
|
obs <- playerEventBus.askL[Observable[PlayerMovementEvent]](
|
||||||
|
ObservableSubscription(_)
|
||||||
|
)
|
||||||
|
_ <- IOUtils.toIO(
|
||||||
|
obs
|
||||||
|
.doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void)
|
||||||
|
.completedL
|
||||||
|
.startAndForget
|
||||||
|
)
|
||||||
|
inputManager <- gameApp.inputManager
|
||||||
|
assetManager <- UIO.pure(gameApp.assetManager)
|
||||||
|
stateManager <- gameApp.stateManager
|
||||||
|
camera <- gameApp.camera
|
||||||
|
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.Props(tickEventBus).behavior,
|
||||||
"gameAppActor"
|
"gameAppActor"
|
||||||
)
|
)
|
||||||
_ <- gameAppActor !! GameAppActor.Start
|
_ <- gameAppActor !! GameAppActor.Start
|
||||||
inputManager <- gameApp.inputManager
|
|
||||||
assetManager <- gameApp.assetManager
|
|
||||||
stateManager <- gameApp.stateManager
|
|
||||||
camera <- gameApp.camera
|
|
||||||
rootNode <- Task.pure(gameApp.rootNode)
|
|
||||||
enqueueR <- Task(gameApp.enqueue _)
|
|
||||||
viewPort <- gameApp.viewPort
|
|
||||||
_ <- logger.info("before")
|
|
||||||
// jfxUI <- gameApp.jfxUI
|
|
||||||
consoleTextArea <- Task(new TextArea {
|
consoleTextArea <- Task(new TextArea {
|
||||||
text = "hello \n"
|
text = "hello \n"
|
||||||
editable = false
|
editable = false
|
||||||
@ -98,9 +125,6 @@ class MainApp(
|
|||||||
// _ <- Task(consoleStream := consoleTextArea)
|
// _ <- Task(consoleStream := consoleTextArea)
|
||||||
// _ <- Task(jfxUI += consoleTextArea)
|
// _ <- Task(jfxUI += consoleTextArea)
|
||||||
_ <- logger.info("after")
|
_ <- logger.info("after")
|
||||||
// bulletAppState <- Task(new BulletAppState())
|
|
||||||
// _ <- Task(stateManager.attach(bulletAppState))
|
|
||||||
// bulletAppState <- Task.pure(gameApp.bulletAppstate)
|
|
||||||
_ <- logger.info("Initializing console stream")
|
_ <- logger.info("Initializing console stream")
|
||||||
_ <-
|
_ <-
|
||||||
wire[MainAppDelegate]
|
wire[MainAppDelegate]
|
||||||
@ -109,6 +133,13 @@ class MainApp(
|
|||||||
} yield gameAppFib
|
} yield gameAppFib
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// val x: Task[Unit] = for {
|
||||||
|
// tickEventBus <- eventsModule.tickEventBusTask
|
||||||
|
// playerEventBus <- eventsModule.playerEventBusTask
|
||||||
|
// _ <- UIO(wire[TestClass])
|
||||||
|
// _ <- gameInit(tickEventBus).use(_.join)
|
||||||
|
// } yield ()
|
||||||
|
|
||||||
val program = for {
|
val program = for {
|
||||||
scriptSystem <- scriptSystemInit
|
scriptSystem <- scriptSystemInit
|
||||||
launchSignal <- Deferred[Task, Launcher.LauncherResult]
|
launchSignal <- Deferred[Task, Launcher.LauncherResult]
|
||||||
@ -125,6 +156,7 @@ class MainApp(
|
|||||||
*/
|
*/
|
||||||
else
|
else
|
||||||
gameInit.use(_.join)
|
gameInit.use(_.join)
|
||||||
|
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,47 +171,44 @@ class MainAppDelegate(
|
|||||||
inputManager: InputManager,
|
inputManager: InputManager,
|
||||||
assetManager: AssetManager,
|
assetManager: AssetManager,
|
||||||
stateManager: AppStateManager,
|
stateManager: AppStateManager,
|
||||||
|
physicsSpace: PhysicsSpace[Task],
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
viewPort: ViewPort,
|
viewPort: ViewPort,
|
||||||
enqueueR: Function1[() => Unit, Unit],
|
enqueueR: Function1[() => Unit, Unit],
|
||||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode
|
rootNode: AppNode[Task] @@ GameAppTags.RootNode
|
||||||
// bulletAppState: BulletAppState
|
|
||||||
)(implicit
|
)(implicit
|
||||||
spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
@annotation.unused timeout: Timeout,
|
timeout: Timeout,
|
||||||
@annotation.unused scheduler: Scheduler
|
scheduler: Scheduler
|
||||||
) {
|
) {
|
||||||
// val physicsSpace = bulletAppState.physicsSpace
|
|
||||||
val physicsSpace = gameApp.physicsSpace
|
|
||||||
def init(
|
def init(
|
||||||
// appScheduler: monix.execution.Scheduler
|
// appScheduler: monix.execution.Scheduler
|
||||||
// consoleStream: GenericConsoleStream[TextArea]
|
// consoleStream: GenericConsoleStream[TextArea]
|
||||||
) =
|
) =
|
||||||
for {
|
for {
|
||||||
_ <- loggerL.info("Initializing Systems")
|
_ <- loggerL.info("Initializing Systems")
|
||||||
_ <- loggerL.debug(physicsSpace.toString())
|
_ <- assetManager.registerLocator(
|
||||||
_ <- Task(
|
|
||||||
assetManager.registerLocator(
|
|
||||||
os.rel / "assets" / "town.zip",
|
os.rel / "assets" / "town.zip",
|
||||||
classOf[ZipLocator]
|
classOf[ZipLocator]
|
||||||
)
|
)
|
||||||
)
|
|
||||||
_ <- loggerL.info("test")
|
_ <- loggerL.info("test")
|
||||||
// _ <- Task(consoleStream.println("text"))
|
// _ <- Task(consoleStream.println("text"))
|
||||||
_ <- DefaultGameLevel(assetManager, viewPort)
|
_ <- DefaultGameLevel(assetManager, viewPort)
|
||||||
.addToGame(rootNode, physicsSpace)
|
.flatMap(_.addToGame(rootNode, physicsSpace).hideErrors)
|
||||||
|
.onErrorHandleWith(e => loggerL.error(e.toString))
|
||||||
_ <- createPlayerController()
|
_ <- createPlayerController()
|
||||||
.absorbWith(e => DummyException(e.toString()))
|
.onErrorHandleWith(e => loggerL.error(e.toString))
|
||||||
// .onErrorRestart(3)
|
// .onErrorRestart(3)
|
||||||
_ <- wire[GameInputHandler.Props].begin
|
_ <- wire[GameInputHandler.Props].begin
|
||||||
// .onErrorRestart(3)
|
// .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(0, 0, 20))
|
||||||
|
|
||||||
// _ <-
|
// _ <-
|
||||||
// (johnActor !! NpcActorSupervisor.Move(
|
// johnActor
|
||||||
// ImVector3f(-30, 0, 10)
|
// .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
||||||
// )).executeAsync
|
|
||||||
// .delayExecution(2.seconds)
|
// .delayExecution(2.seconds)
|
||||||
// _ <-
|
// _ <-
|
||||||
// IOUtils
|
// IOUtils
|
||||||
@ -195,53 +224,40 @@ class MainAppDelegate(
|
|||||||
|
|
||||||
def createPlayerController(
|
def createPlayerController(
|
||||||
// appScheduler: monix.execution.Scheduler
|
// appScheduler: monix.execution.Scheduler
|
||||||
): IO[PlayerController.Error, Unit] = {
|
): IO[PlayerController.Error, ActorRef[PlayerActorSupervisor.Command]] = {
|
||||||
val playerPos = ImVector3f.ZERO
|
val playerPos = ImVector3f.ZERO
|
||||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||||
val playerPhysicsControl =
|
val playerPhysicsControl =
|
||||||
PlayerController.Defaults.defaultPlayerPhysicsControl
|
PlayerController.Defaults.defaultPlayerPhysicsControl
|
||||||
.taggedWith[PlayerControllerTags.PlayerTag]
|
.taggedWith[PlayerControllerTags.PlayerTag]
|
||||||
// lazy val camNode =
|
for {
|
||||||
// PlayerController.Defaults
|
playerModel <-
|
||||||
// .defaultCamerNode(camera, playerPos)
|
assetManager
|
||||||
// .taggedWith[PlayerControllerTags.PlayerCameraNode]
|
.loadModelAs[Node](modelPath)
|
||||||
val playerModel = assetManager
|
.map(_.withRotate(0, FastMath.PI, 0))
|
||||||
.loadModel(modelPath)
|
.mapError(PlayerController.CouldNotCreatePlayerModel)
|
||||||
.asInstanceOf[Node]
|
playerNode <- UIO(
|
||||||
.withRotate(0, FastMath.PI, 0)
|
PlayerController.Defaults
|
||||||
val mbPlayerNode = PlayerController.Defaults
|
|
||||||
.defaultPlayerNode(
|
.defaultPlayerNode(
|
||||||
playerPos,
|
playerPos,
|
||||||
playerModel,
|
playerModel,
|
||||||
playerPhysicsControl
|
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(
|
cameraPivotNode <- UIO(
|
||||||
|
new Node(EntityIds.CameraPivot.value)
|
||||||
|
.withControl(new FollowControl(playerNode))
|
||||||
|
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
|
||||||
|
)
|
||||||
|
camNode <- UIO(
|
||||||
PlayerController.Defaults
|
PlayerController.Defaults
|
||||||
.defaultCamerNode(camera, cameraPivotNode, playerNode, playerPos)
|
.defaultCamerNode(camera, playerPos)
|
||||||
).onErrorHandleWith(e =>
|
.taggedWith[PlayerControllerTags.PlayerCameraNode]
|
||||||
IO.raiseError(PlayerController.GenericError(e.getMessage()))
|
)
|
||||||
).map(_.taggedWith[PlayerControllerTags.PlayerCameraNode])
|
playerActor <- wire[PlayerController.Props].create
|
||||||
// _ <- Task {
|
} yield playerActor
|
||||||
// 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 ()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def createTestNpc(
|
def createTestNpc(
|
||||||
// appScheduler: monix.execution.Scheduler,
|
// appScheduler: monix.execution.Scheduler,
|
||||||
npcName: String
|
npcName: String
|
||||||
@ -250,12 +266,7 @@ class MainAppDelegate(
|
|||||||
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||||
// (1f, 2.1f, 10f)
|
// (1f, 2.1f, 10f)
|
||||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||||
val mbNpcNode = PlayerController.Defaults.defaultNpcNode(
|
|
||||||
assetManager,
|
|
||||||
initialPos,
|
|
||||||
npcPhysicsControl,
|
|
||||||
npcName
|
|
||||||
)
|
|
||||||
val npcActorTask = AkkaUtils.spawnActorL(
|
val npcActorTask = AkkaUtils.spawnActorL(
|
||||||
NpcActorSupervisor
|
NpcActorSupervisor
|
||||||
.Props(
|
.Props(
|
||||||
@ -263,6 +274,7 @@ class MainAppDelegate(
|
|||||||
enqueueR,
|
enqueueR,
|
||||||
initialPos,
|
initialPos,
|
||||||
// tickEventBus,
|
// tickEventBus,
|
||||||
|
npcName,
|
||||||
npcPhysicsControl
|
npcPhysicsControl
|
||||||
).behavior,
|
).behavior,
|
||||||
npcName,
|
npcName,
|
||||||
@ -271,28 +283,35 @@ class MainAppDelegate(
|
|||||||
.behavior,
|
.behavior,
|
||||||
s"${npcName}-npcActorSupervisor"
|
s"${npcName}-npcActorSupervisor"
|
||||||
)
|
)
|
||||||
// .taggedWith[PlayerControllerTags.PlayerTag]
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
npcNode <- IO.fromEither(mbNpcNode)
|
materialDef <- assetManager.loadAssetAs[MaterialDef](
|
||||||
npcActor <- npcActorTask
|
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||||
// _ <- IO {
|
)
|
||||||
// physicsSpace += npcPhysicsControl
|
material = new Material(materialDef)
|
||||||
// physicsSpace += npcNode
|
_ = material.setColor("Color", ColorRGBA.Blue)
|
||||||
// // rootNode += npcNode
|
mesh = PlayerController.Defaults.defaultMesh.withMaterial(material)
|
||||||
// }
|
npcNode = PlayerController.Defaults.defaultNpcNode(
|
||||||
|
mesh,
|
||||||
|
initialPos,
|
||||||
|
npcPhysicsControl,
|
||||||
|
npcName
|
||||||
|
)
|
||||||
|
npcActor <- npcActorTask.hideErrors
|
||||||
|
_ <- (for {
|
||||||
_ <- physicsSpace += npcPhysicsControl
|
_ <- physicsSpace += npcPhysicsControl
|
||||||
_ <- physicsSpace += npcNode
|
_ <- physicsSpace += npcNode
|
||||||
_ <- rootNode += npcNode
|
_ <- rootNode += npcNode
|
||||||
|
} yield ()).hideErrors
|
||||||
} yield npcActor
|
} yield npcActor
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FollowControl(playerNode: Node) extends AbstractControl {
|
class FollowControl(playerNode: Node) extends AbstractControl {
|
||||||
override def controlUpdate(tpf: Float): Unit = {
|
override def controlUpdate(tpf: Float): Unit =
|
||||||
this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
|
this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
|
||||||
}
|
|
||||||
override def controlRender(
|
override def controlRender(
|
||||||
rm: RenderManager,
|
rm: RenderManager,
|
||||||
vp: ViewPort
|
vp: ViewPort
|
||||||
|
@ -5,15 +5,22 @@ import cats.effect.Resource
|
|||||||
import io.odin.Logger
|
import io.odin.Logger
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import wow.doge.mygame.executors.ExecutorsModule
|
import wow.doge.mygame.executors.ExecutorsModule
|
||||||
|
import akka.actor.BootstrapSetup
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
|
||||||
trait MainModule extends ExecutorsModule {
|
trait MainModule extends ExecutorsModule {
|
||||||
|
|
||||||
def actorSystemResource(
|
def actorSystemResource(
|
||||||
logger: Logger[Task]
|
logger: Logger[Task],
|
||||||
|
scheduler: Scheduler
|
||||||
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
|
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
|
||||||
Resource.make(
|
Resource.make(
|
||||||
logger.info("Creating Actor System") >> Task(
|
logger.info("Creating Actor System") >> Task(
|
||||||
ActorSystem(SpawnProtocol(), name = "GameActorSystem")
|
ActorSystem(
|
||||||
|
SpawnProtocol(),
|
||||||
|
name = "GameActorSystem",
|
||||||
|
BootstrapSetup().withDefaultExecutionContext(scheduler)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)(sys =>
|
)(sys =>
|
||||||
for {
|
for {
|
||||||
|
@ -1,23 +1,32 @@
|
|||||||
package wow.doge.mygame.executors
|
package wow.doge.mygame.executors
|
||||||
|
|
||||||
import cats.effect.Resource
|
import cats.effect.Resource
|
||||||
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import monix.bio.UIO
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
|
|
||||||
trait ExecutorsModule {
|
trait ExecutorsModule {
|
||||||
lazy val schedulers = Schedulers()
|
val schedulers = Schedulers()
|
||||||
// Resource.make(
|
val acquire: UIO[Either[Error, Int]] =
|
||||||
// Task(
|
IO.pure(1).onErrorHandleWith(_ => IO.raiseError(Error)).attempt
|
||||||
// new Schedulers(
|
// : Resource[IO[Error, Unit], Unit]
|
||||||
// jme = Scheduler
|
val res = Resource.make(acquire)(_ => IO.unit)
|
||||||
// .singleThread(name = "JME-Application-Thread", daemonic = false)
|
val x: Task[Either[Error, Unit]] = res.use {
|
||||||
// )
|
case Right(value) => Task(Right(println(s"got $value")))
|
||||||
// )
|
case Left(value) => Task(Left(value))
|
||||||
// )(s => Task(s.jme.shutdown()))
|
}
|
||||||
lazy val jMESchedulerResource = Resource.make(
|
val z = x.onErrorHandleWith(ex => UIO(Right(ex.printStackTrace())))
|
||||||
|
val y: IO[Error, Unit] = z >>
|
||||||
|
x.hideErrors.rethrow
|
||||||
|
|
||||||
|
val jMESchedulerResource = Resource.make(
|
||||||
Task(
|
Task(
|
||||||
Scheduler
|
Scheduler
|
||||||
.singleThread(name = "JME-Application-Thread", daemonic = false)
|
.singleThread(name = "JME-Application-Thread", daemonic = false)
|
||||||
)
|
)
|
||||||
)(e => Task(e.shutdown()))
|
)(e => Task(e.shutdown()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait Error
|
||||||
|
case object Error extends Error
|
||||||
|
@ -42,13 +42,13 @@ object SwingExecutorService extends GUIExecutorService {
|
|||||||
|
|
||||||
object JMEExecutorService extends GUIExecutorService {
|
object JMEExecutorService extends GUIExecutorService {
|
||||||
override def execute(command: Runnable) =
|
override def execute(command: Runnable) =
|
||||||
JMERunner.runner.enqueue(command)
|
JMERunner.runner.get.apply(command)
|
||||||
// new SingleThreadEventExecutor()
|
// new SingleThreadEventExecutor()
|
||||||
sys.addShutdownHook(JMEExecutorService.shutdown())
|
sys.addShutdownHook(JMEExecutorService.shutdown())
|
||||||
}
|
}
|
||||||
|
|
||||||
object JMERunner {
|
object JMERunner {
|
||||||
var runner: com.jme3.app.Application = null
|
var runner: Option[Runnable => Unit] = None
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package wow.doge.mygame.game
|
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.Resource
|
||||||
import cats.effect.concurrent.Deferred
|
import cats.effect.concurrent.Deferred
|
||||||
import com.jme3.app.state.AppStateManager
|
import com.jme3.app.state.AppStateManager
|
||||||
import com.jme3.asset.AssetManager
|
|
||||||
import com.jme3.bullet.BulletAppState
|
import com.jme3.bullet.BulletAppState
|
||||||
import com.jme3.input.InputManager
|
import com.jme3.input.InputManager
|
||||||
import com.jme3.scene.Node
|
import com.jme3.scene.Node
|
||||||
@ -12,20 +19,23 @@ import com.jme3.system.AppSettings
|
|||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import com.typesafe.scalalogging.{Logger => SLogger}
|
import com.typesafe.scalalogging.{Logger => SLogger}
|
||||||
import io.odin.Logger
|
import io.odin.Logger
|
||||||
import monix.bio.IO
|
import monix.bio.Fiber
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import monix.bio.UIO
|
||||||
import monix.catnap.ConcurrentChannel
|
import monix.catnap.ConcurrentChannel
|
||||||
import monix.catnap.ConsumerF
|
import monix.catnap.ConsumerF
|
||||||
import monix.catnap.Semaphore
|
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
|
import monix.execution.CancelableFuture
|
||||||
|
import monix.execution.CancelablePromise
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
|
import wow.doge.mygame.executors.JMERunner
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
|
import wow.doge.mygame.utils.GenericTimerActor
|
||||||
import wow.doge.mygame.game.subsystems.ui.JFxUI
|
import wow.doge.mygame.game.subsystems.ui.JFxUI
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.utils.AkkaUtils
|
||||||
import wow.doge.mygame.utils.wrappers.jme._
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
|
import wow.doge.mygame.Dispatchers
|
||||||
sealed trait Error
|
|
||||||
case object FlyCamNotExists extends Error
|
|
||||||
|
|
||||||
object GameAppTags {
|
object GameAppTags {
|
||||||
sealed trait RootNode
|
sealed trait RootNode
|
||||||
@ -33,47 +43,58 @@ object GameAppTags {
|
|||||||
sealed trait GuiNode
|
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 stateManager: Task[AppStateManager] = Task(app.getStateManager())
|
||||||
def inputManager: Task[InputManager] = Task(app.getInputManager())
|
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 guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
|
||||||
def guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
val guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
||||||
def flyCam =
|
def flyCam = Option(app.getFlyByCamera())
|
||||||
IO(app.getFlyByCamera()).onErrorHandleWith(_ =>
|
|
||||||
IO.raiseError(FlyCamNotExists)
|
|
||||||
)
|
|
||||||
def camera = Task(app.getCamera())
|
def camera = Task(app.getCamera())
|
||||||
def viewPort = Task(app.getViewPort())
|
def viewPort = Task(app.getViewPort())
|
||||||
// def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
// def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
||||||
val rootNode =
|
val rootNode =
|
||||||
AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode]
|
AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode]
|
||||||
|
|
||||||
val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace)
|
val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace)
|
||||||
def enqueue(cb: () => Unit) =
|
def enqueue(cb: () => Unit) = app.enqueueR(() => cb())
|
||||||
app.enqueue(new Runnable {
|
|
||||||
override def run() = cb()
|
|
||||||
})
|
|
||||||
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(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 scheduler = app.scheduler
|
||||||
def jfxUI = JFxUI(app)
|
def jfxUI = JFxUI(app)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object GameApp {
|
class GameAppResource(
|
||||||
|
|
||||||
def resource(
|
|
||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
jmeThread: Scheduler,
|
jmeThread: Scheduler,
|
||||||
schedulers: Schedulers
|
schedulers: Schedulers
|
||||||
) =
|
)(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
scheduler: akka.actor.typed.Scheduler,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
|
) {
|
||||||
|
def resource: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||||
Resource.make {
|
Resource.make {
|
||||||
lazy val bullet = new BulletAppState
|
lazy val bullet = new BulletAppState
|
||||||
for {
|
for {
|
||||||
// bullet <- Task(new BulletAppState)
|
|
||||||
// startSignal <- Task(CancelablePromise[Unit]())
|
|
||||||
app <- Task(new SimpleAppExt(schedulers, bullet))
|
app <- Task(new SimpleAppExt(schedulers, bullet))
|
||||||
|
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
|
||||||
_ <- Task {
|
_ <- Task {
|
||||||
val settings = new AppSettings(true)
|
val settings = new AppSettings(true)
|
||||||
settings.setVSync(true)
|
settings.setVSync(true)
|
||||||
@ -88,34 +109,76 @@ object GameApp {
|
|||||||
|
|
||||||
fib <- Task(app.start).executeOn(jmeThread).start
|
fib <- Task(app.start).executeOn(jmeThread).start
|
||||||
_ <- Task.deferFuture(app.started)
|
_ <- Task.deferFuture(app.started)
|
||||||
// _ <- Task.fromCancelablePromise(startSignal)
|
testGameActor <- AkkaUtils.spawnActorL(
|
||||||
_ <- Task(pprint.log(bullet.toString()))
|
new TestGameActor.Props().create,
|
||||||
_ <- Task(println(bullet.physicsSpace.toString()))
|
"testGameActor",
|
||||||
gameApp <- Task(new GameApp(logger, app))
|
Dispatchers.jmeDispatcher
|
||||||
} yield gameApp -> fib
|
)
|
||||||
}(_._2.cancel)
|
sp <- testGameActor.askL(TestGameActor.GetSpawnProtocol(_))
|
||||||
|
gameApp <- Task(new GameApp(logger, app, testGameActor, sp))
|
||||||
/**
|
_ <- Task {
|
||||||
* Synchronization wrapper for a mutable object
|
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
|
||||||
*
|
app.cancelToken = Some(fut)
|
||||||
* @param obj the mutable object
|
}
|
||||||
* @param lock lock for synchronization
|
} yield (gameApp, fib)
|
||||||
*/
|
} {
|
||||||
class SynchedObject[A](obj: A, lock: Semaphore[Task]) {
|
case (gameApp, fib) =>
|
||||||
def modify(f: A => Unit): Task[Unit] =
|
fib.cancel >> UIO(JMERunner.runner = None)
|
||||||
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 {
|
object GameApp {}
|
||||||
def apply[A](obj: A) =
|
|
||||||
Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock)))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 {
|
object Ops {
|
||||||
|
@ -5,7 +5,7 @@ import scala.concurrent.duration._
|
|||||||
import akka.actor.typed.SupervisorStrategy
|
import akka.actor.typed.SupervisorStrategy
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import wow.doge.mygame.game.TickGenerator.Send
|
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.implicits._
|
||||||
import wow.doge.mygame.subsystems.events.EventBus
|
import wow.doge.mygame.subsystems.events.EventBus
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
@ -19,14 +19,7 @@ object GameAppActor {
|
|||||||
case object Pause extends Command
|
case object Pause extends Command
|
||||||
case object Stop extends Command
|
case object Stop extends Command
|
||||||
|
|
||||||
case class Props(
|
case class Props(tickEventBus: GameEventBus[TickEvent]) {
|
||||||
// app: SimpleAppExt,
|
|
||||||
// akkaScheduler: Scheduler,
|
|
||||||
// schedulers: Schedulers,
|
|
||||||
// spawnProtocol: ActorRef[SpawnProtocol.Command],
|
|
||||||
// loggerL: Logger[Task]
|
|
||||||
tickEventBus: GameEventBus[TickEvent]
|
|
||||||
) {
|
|
||||||
def behavior =
|
def behavior =
|
||||||
Behaviors.setup[Command] { ctx =>
|
Behaviors.setup[Command] { ctx =>
|
||||||
ctx.log.infoP("Hello from GameAppActor")
|
ctx.log.infoP("Hello from GameAppActor")
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package wow.doge.mygame.game
|
package wow.doge.mygame.game
|
||||||
|
|
||||||
import scala.collection.immutable.Queue
|
import scala.collection.immutable.Queue
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
import com.jme3.app.SimpleApplication
|
import com.jme3.app.SimpleApplication
|
||||||
import com.jme3.app.state.AppState
|
import com.jme3.app.state.AppState
|
||||||
|
import com.jme3.bullet.BulletAppState
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.execution.CancelableFuture
|
import monix.execution.CancelableFuture
|
||||||
import monix.execution.CancelablePromise
|
import monix.execution.CancelablePromise
|
||||||
@ -11,7 +14,6 @@ import monix.execution.Scheduler
|
|||||||
import monix.execution.atomic.Atomic
|
import monix.execution.atomic.Atomic
|
||||||
import wow.doge.mygame.executors.GUIExecutorService
|
import wow.doge.mygame.executors.GUIExecutorService
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import com.jme3.bullet.BulletAppState
|
|
||||||
// import wow.doge.mygame.implicits._
|
// import wow.doge.mygame.implicits._
|
||||||
class SimpleAppExt(
|
class SimpleAppExt(
|
||||||
schedulers: Schedulers,
|
schedulers: Schedulers,
|
||||||
@ -25,27 +27,30 @@ class SimpleAppExt(
|
|||||||
*/
|
*/
|
||||||
private val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
|
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()
|
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||||
|
|
||||||
|
var cancelToken: Option[() => Future[Unit]] = None
|
||||||
|
|
||||||
def started: CancelableFuture[Unit] = startSignal.future
|
def started: CancelableFuture[Unit] = startSignal.future
|
||||||
|
|
||||||
override def simpleInitApp(): Unit = {
|
override def simpleInitApp(): Unit = {
|
||||||
// _bulletAppState = new BulletAppState
|
|
||||||
stateManager.attach(bulletAppState)
|
stateManager.attach(bulletAppState)
|
||||||
startSignal.success(())
|
startSignal.success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
override def simpleUpdate(tpf: Float): Unit = {}
|
override def simpleUpdate(tpf: Float): Unit = {}
|
||||||
|
|
||||||
override def stop(): Unit = {
|
override def stop(waitFor: Boolean): Unit = {
|
||||||
super.stop()
|
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] = {
|
def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
|
||||||
@ -74,7 +79,7 @@ class SimpleAppExt(
|
|||||||
enqueue(command)
|
enqueue(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val scheduler = Scheduler(JMEExecutorService)
|
val scheduler = Scheduler(JMEExecutorService)
|
||||||
}
|
}
|
||||||
object SimpleAppExt {
|
object SimpleAppExt {
|
||||||
private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T)
|
private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T)
|
||||||
|
@ -10,6 +10,7 @@ import akka.actor.typed.SupervisorStrategy
|
|||||||
import akka.actor.typed.scaladsl.ActorContext
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import cats.syntax.show._
|
||||||
import monix.execution.CancelableFuture
|
import monix.execution.CancelableFuture
|
||||||
import monix.execution.CancelablePromise
|
import monix.execution.CancelablePromise
|
||||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
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
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
|
import wow.doge.mygame.utils.GenericTimerActor
|
||||||
|
|
||||||
object NpcActorSupervisor {
|
object NpcActorSupervisor {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
@ -35,10 +37,7 @@ object NpcActorSupervisor {
|
|||||||
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
|
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
|
||||||
) extends Command
|
) extends Command
|
||||||
private case object DoneMoving 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 class LogError(err: Throwable) extends Command
|
||||||
private case object NoOp extends Command
|
|
||||||
|
|
||||||
private case class MovementFailed(err: Throwable) extends Command
|
private case class MovementFailed(err: Throwable) extends Command
|
||||||
|
|
||||||
final case class Props(
|
final case class Props(
|
||||||
@ -47,6 +46,7 @@ object NpcActorSupervisor {
|
|||||||
initialPos: ImVector3f
|
initialPos: ImVector3f
|
||||||
) {
|
) {
|
||||||
def behavior =
|
def behavior =
|
||||||
|
Behaviors.withMdc(Map("actorName" -> npcName))(
|
||||||
Behaviors.setup[Command] { ctx =>
|
Behaviors.setup[Command] { ctx =>
|
||||||
val npcMovementActor = ctx.spawn(
|
val npcMovementActor = ctx.spawn(
|
||||||
(npcMovementActorBehavior),
|
(npcMovementActorBehavior),
|
||||||
@ -56,9 +56,9 @@ object NpcActorSupervisor {
|
|||||||
new NpcActorSupervisor(ctx, this, Children(npcMovementActor))
|
new NpcActorSupervisor(ctx, this, Children(npcMovementActor))
|
||||||
.idle(State())
|
.idle(State())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
final case class State(
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
final case class State()
|
||||||
final case class Children(
|
final case class Children(
|
||||||
npcMovementActor: ActorRef[NpcMovementActor.Command]
|
npcMovementActor: ActorRef[NpcMovementActor.Command]
|
||||||
)
|
)
|
||||||
@ -79,12 +79,12 @@ class NpcActorSupervisor(
|
|||||||
100.millis
|
100.millis
|
||||||
)
|
)
|
||||||
.behavior,
|
.behavior,
|
||||||
s"npc-John-NpcActorTimer"
|
s"npc-${props.npcName}-NpcActorTimer"
|
||||||
)
|
)
|
||||||
|
|
||||||
def idle(state: State): Behavior[NpcActorSupervisor.Command] =
|
def idle(state: State): Behavior[NpcActorSupervisor.Command] =
|
||||||
Behaviors.setup { _ =>
|
Behaviors.setup { _ =>
|
||||||
ctx.log.debugP("Inside Idle State")
|
ctx.log.debugP(s"npcActor-${props.npcName}: Entered Idle State")
|
||||||
Behaviors.receiveMessage[Command] {
|
Behaviors.receiveMessage[Command] {
|
||||||
case m @ Move(pos) =>
|
case m @ Move(pos) =>
|
||||||
ctx.ask(
|
ctx.ask(
|
||||||
@ -99,7 +99,7 @@ class NpcActorSupervisor(
|
|||||||
moving(state, move.pos, signal)
|
moving(state, move.pos, signal)
|
||||||
|
|
||||||
case LogError(err) =>
|
case LogError(err) =>
|
||||||
ctx.log.warnP(err.getMessage())
|
logError(err)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case _ => Behaviors.unhandled
|
case _ => Behaviors.unhandled
|
||||||
}
|
}
|
||||||
@ -111,19 +111,15 @@ class NpcActorSupervisor(
|
|||||||
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
|
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
|
||||||
): Behavior[NpcActorSupervisor.Command] =
|
): Behavior[NpcActorSupervisor.Command] =
|
||||||
Behaviors.setup { _ =>
|
Behaviors.setup { _ =>
|
||||||
|
ctx.log.debugP(s"npcActor-${props.npcName}: Entered Moving State")
|
||||||
movementTimer ! GenericTimerActor.Start
|
movementTimer ! GenericTimerActor.Start
|
||||||
|
|
||||||
// ctx
|
|
||||||
// .ask(state.npcMovementActor, NpcMovementActor2.MoveTo(targetPos, _))(
|
|
||||||
// _.fold(LogError(_), MovementResponse(_))
|
|
||||||
// )
|
|
||||||
ctx.pipeToSelf(signal) {
|
ctx.pipeToSelf(signal) {
|
||||||
case Success(value) => DoneMoving
|
case Success(value) => DoneMoving
|
||||||
case Failure(exception) => MovementFailed(exception)
|
case Failure(exception) => MovementFailed(exception)
|
||||||
}
|
}
|
||||||
Behaviors.receiveMessagePartial[Command] {
|
Behaviors.receiveMessagePartial[Command] {
|
||||||
case LogError(err) =>
|
case LogError(err) =>
|
||||||
ctx.log.error(err.getMessage())
|
logError(err)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case MovementFailed(err) =>
|
case MovementFailed(err) =>
|
||||||
ctx.self ! LogError(err)
|
ctx.self ! LogError(err)
|
||||||
@ -132,6 +128,7 @@ class NpcActorSupervisor(
|
|||||||
case m @ Move(pos) =>
|
case m @ Move(pos) =>
|
||||||
movementTimer ! GenericTimerActor.Stop
|
movementTimer ! GenericTimerActor.Stop
|
||||||
children.npcMovementActor ! NpcMovementActor.StopMoving
|
children.npcMovementActor ! NpcMovementActor.StopMoving
|
||||||
|
// new movement request received, cancel previous request
|
||||||
signal.cancel()
|
signal.cancel()
|
||||||
ctx.ask(
|
ctx.ask(
|
||||||
children.npcMovementActor,
|
children.npcMovementActor,
|
||||||
@ -143,15 +140,15 @@ class NpcActorSupervisor(
|
|||||||
Behaviors.same
|
Behaviors.same
|
||||||
case InternalMove(move, signal) =>
|
case InternalMove(move, signal) =>
|
||||||
moving(state, targetPos, signal)
|
moving(state, targetPos, signal)
|
||||||
case NoOp => Behaviors.same
|
|
||||||
// case MovementResponse(x: CancelableFuture[_]) =>
|
|
||||||
// // ctx.pipeToSelf(x)(_.)
|
|
||||||
case DoneMoving =>
|
case DoneMoving =>
|
||||||
movementTimer ! GenericTimerActor.Stop
|
movementTimer ! GenericTimerActor.Stop
|
||||||
idle(state)
|
idle(state)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def logError(err: Throwable) =
|
||||||
|
ctx.log.errorP(s"npcActor-${props.npcName}: " + err.getMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
object NpcMovementActor {
|
object NpcMovementActor {
|
||||||
@ -171,6 +168,7 @@ object NpcMovementActor {
|
|||||||
val enqueueR: Function1[() => Unit, Unit],
|
val enqueueR: Function1[() => Unit, Unit],
|
||||||
val initialPos: ImVector3f,
|
val initialPos: ImVector3f,
|
||||||
// val tickEventBus: GameEventBus[TickEvent],
|
// val tickEventBus: GameEventBus[TickEvent],
|
||||||
|
val npcName: String,
|
||||||
val movable: T
|
val movable: T
|
||||||
) {
|
) {
|
||||||
def behavior =
|
def behavior =
|
||||||
@ -189,9 +187,7 @@ class NpcMovementActor[T](
|
|||||||
|
|
||||||
def location = cm.location(props.movable)
|
def location = cm.location(props.movable)
|
||||||
|
|
||||||
def receive(
|
def receive(state: State): Behavior[NpcMovementActor.Command] =
|
||||||
state: State
|
|
||||||
): Behavior[NpcMovementActor.Command] =
|
|
||||||
Behaviors.receiveMessagePartial {
|
Behaviors.receiveMessagePartial {
|
||||||
case AskPosition(replyTo) =>
|
case AskPosition(replyTo) =>
|
||||||
replyTo ! location
|
replyTo ! location
|
||||||
@ -214,19 +210,25 @@ class NpcMovementActor[T](
|
|||||||
Behaviors.receiveMessagePartial {
|
Behaviors.receiveMessagePartial {
|
||||||
case StopMoving =>
|
case StopMoving =>
|
||||||
ctx.log.debugP(
|
ctx.log.debugP(
|
||||||
"Position at Stop = " + location.toString
|
show"npcActor-${props.npcName}: Position at Stop = " + location
|
||||||
)
|
)
|
||||||
props.enqueueR(() => cm.stop(props.movable))
|
props.enqueueR(() => cm.stop(props.movable))
|
||||||
receive(state)
|
receive(state)
|
||||||
|
|
||||||
case MovementTick =>
|
case MovementTick =>
|
||||||
val dst = ImVector3f.dst(targetPos, location)
|
val dst = ImVector3f.manhattanDst(targetPos, location)
|
||||||
if (dst <= 10f) {
|
if (dst <= 10f) {
|
||||||
ctx.self ! StopMoving
|
ctx.self ! StopMoving
|
||||||
reachDestination.success(DoneMoving)
|
reachDestination.success(DoneMoving)
|
||||||
} else {
|
} else {
|
||||||
ctx.log.traceP("Difference = " + dst.toString())
|
ctx.log.traceP(
|
||||||
ctx.log.traceP("Current pos = " + location.toString())
|
show"npcActor-${props.npcName}: Difference = " + dst.formatted(
|
||||||
|
"%.2f"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ctx.log.traceP(
|
||||||
|
show"npcActor-${props.npcName}: Current pos = " + location
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
@ -295,7 +297,7 @@ object NpcMovementActorNotUsed {
|
|||||||
|
|
||||||
mainEventBus ! EventBus.Subscribe(npcMovementEl)
|
mainEventBus ! EventBus.Subscribe(npcMovementEl)
|
||||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||||
Behaviors.receiveMessage { msg => Behaviors.same }
|
Behaviors.receiveMessage(msg => Behaviors.same)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import scala.util.Random
|
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.LogOptions
|
import akka.actor.typed.LogOptions
|
||||||
import akka.actor.typed.SupervisorStrategy
|
import akka.actor.typed.SupervisorStrategy
|
||||||
import akka.actor.typed.scaladsl.ActorContext
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import akka.actor.typed.scaladsl.TimerScheduler
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
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
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
object PlayerActorSupervisor {
|
object PlayerActorSupervisor {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case class Props(
|
final case class Props(
|
||||||
@ -28,15 +24,15 @@ object PlayerActorSupervisor {
|
|||||||
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||||
playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
|
playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
|
||||||
) {
|
) {
|
||||||
def behavior[T: CanMove](movable: T) =
|
def behavior =
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
LogOptions()
|
LogOptions()
|
||||||
.withLevel(Level.TRACE)
|
.withLevel(Level.TRACE)
|
||||||
.withLogger(
|
.withLogger(
|
||||||
Logger[PlayerActorSupervisor[T]].underlying
|
Logger[PlayerActorSupervisor].underlying
|
||||||
),
|
),
|
||||||
Behaviors.setup[Command] { ctx =>
|
Behaviors.setup[Command] { ctx =>
|
||||||
ctx.log.info("Hello from PlayerActor")
|
ctx.log.infoP("Starting PlayerActor")
|
||||||
|
|
||||||
// spawn children actors
|
// spawn children actors
|
||||||
val movementActor =
|
val movementActor =
|
||||||
@ -44,7 +40,7 @@ object PlayerActorSupervisor {
|
|||||||
Behaviors
|
Behaviors
|
||||||
.supervise(imMovementActorBehavior)
|
.supervise(imMovementActorBehavior)
|
||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
.onFailure[Exception](SupervisorStrategy.restart),
|
||||||
"playerMovementActorChild"
|
"playerMovementActor"
|
||||||
)
|
)
|
||||||
|
|
||||||
val playerCameraActor =
|
val playerCameraActor =
|
||||||
@ -55,19 +51,27 @@ object PlayerActorSupervisor {
|
|||||||
"playerCameraActorEl"
|
"playerCameraActorEl"
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx.spawn(
|
val playerMovementEl = ctx.spawn(
|
||||||
PlayerMovementActor
|
Behaviors
|
||||||
.Props(
|
.supervise(PlayerMovementEventListener(movementActor))
|
||||||
movementActor,
|
.onFailure[Exception](SupervisorStrategy.restart),
|
||||||
playerCameraActor,
|
"playerMovementEventHandler"
|
||||||
playerEventBus,
|
|
||||||
tickEventBus
|
|
||||||
)
|
|
||||||
.behavior,
|
|
||||||
"playerMovementAcor"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//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)
|
playerEventBus ! EventBus.Subscribe(playerCameraEl)
|
||||||
|
|
||||||
new PlayerActorSupervisor(
|
new PlayerActorSupervisor(
|
||||||
@ -84,14 +88,15 @@ object PlayerActorSupervisor {
|
|||||||
movementActor: ActorRef[ImMovementActor.Command]
|
movementActor: ActorRef[ImMovementActor.Command]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
class PlayerActorSupervisor[T: CanMove](
|
class PlayerActorSupervisor(
|
||||||
ctx: ActorContext[PlayerActorSupervisor.Command],
|
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||||
props: PlayerActorSupervisor.Props,
|
props: PlayerActorSupervisor.Props,
|
||||||
children: PlayerActorSupervisor.Children
|
children: PlayerActorSupervisor.Children
|
||||||
) {
|
) {
|
||||||
import PlayerActorSupervisor._
|
import PlayerActorSupervisor._
|
||||||
def receive =
|
def receive =
|
||||||
Behaviors.receiveMessage[Command] {
|
Behaviors
|
||||||
|
.receiveMessage[Command] {
|
||||||
case _ =>
|
case _ =>
|
||||||
// children.movementActor ! ImMovementActor.MovedDown(true)
|
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
@ -115,15 +120,17 @@ object PlayerMovementActor {
|
|||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
.onFailure[Exception](SupervisorStrategy.restart),
|
||||||
"playerMovementEventHandler"
|
"playerMovementEventHandler"
|
||||||
)
|
)
|
||||||
val renderTickElBehavior =
|
|
||||||
|
val renderTickEl = {
|
||||||
|
val behavior =
|
||||||
Behaviors.receiveMessage[RenderTick.type] {
|
Behaviors.receiveMessage[RenderTick.type] {
|
||||||
case RenderTick =>
|
case RenderTick =>
|
||||||
movementActor ! ImMovementActor.Tick
|
movementActor ! ImMovementActor.Tick
|
||||||
// playerCameraActor ! PlayerCameraActor.Tick
|
// playerCameraActor ! PlayerCameraActor.Tick
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
val renderTickEl =
|
ctx.spawn(behavior, "playerMovementTickListener")
|
||||||
ctx.spawn(renderTickElBehavior, "playerMovementTickListener")
|
}
|
||||||
|
|
||||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef
|
|||||||
import akka.actor.typed.Scheduler
|
import akka.actor.typed.Scheduler
|
||||||
import akka.actor.typed.SpawnProtocol
|
import akka.actor.typed.SpawnProtocol
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import cats.implicits._
|
|
||||||
import com.jme3.asset.AssetManager
|
import com.jme3.asset.AssetManager
|
||||||
import com.jme3.bullet.BulletAppState
|
import com.jme3.bullet.BulletAppState
|
||||||
import com.jme3.bullet.control.BetterCharacterControl
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
@ -13,24 +12,22 @@ import com.jme3.renderer.Camera
|
|||||||
import com.jme3.scene.CameraNode
|
import com.jme3.scene.CameraNode
|
||||||
import com.jme3.scene.Geometry
|
import com.jme3.scene.Geometry
|
||||||
import com.jme3.scene.Node
|
import com.jme3.scene.Node
|
||||||
|
import com.jme3.scene.Spatial
|
||||||
import com.jme3.scene.shape.Box
|
import com.jme3.scene.shape.Box
|
||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import io.odin.Logger
|
import io.odin.Logger
|
||||||
import monix.bio.IO
|
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import monix.bio.UIO
|
||||||
import wow.doge.mygame.game.GameAppTags
|
import wow.doge.mygame.game.GameAppTags
|
||||||
import wow.doge.mygame.game.SimpleAppExt
|
import wow.doge.mygame.game.SimpleAppExt
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.math.ImVector3f
|
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.EventsModule.GameEventBus
|
||||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent
|
import wow.doge.mygame.subsystems.events.TickEvent
|
||||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
import wow.doge.mygame.utils.AkkaUtils
|
import wow.doge.mygame.utils.AkkaUtils
|
||||||
import wow.doge.mygame.utils.wrappers.jme._
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
import monix.bio.UIO
|
|
||||||
|
|
||||||
object PlayerControllerTags {
|
object PlayerControllerTags {
|
||||||
sealed trait PlayerTag
|
sealed trait PlayerTag
|
||||||
sealed trait PlayerCameraNode
|
sealed trait PlayerCameraNode
|
||||||
@ -40,7 +37,9 @@ object PlayerControllerTags {
|
|||||||
object PlayerController {
|
object PlayerController {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
case class CouldNotCreatePlayerNode(reason: String) extends 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(
|
class Props(
|
||||||
enqueueR: Function1[() => Unit, Unit],
|
enqueueR: Function1[() => Unit, Unit],
|
||||||
@ -65,9 +64,8 @@ object PlayerController {
|
|||||||
val playerActorBehavior = {
|
val playerActorBehavior = {
|
||||||
val movementActorBeh = new ImMovementActor.Props(
|
val movementActorBeh = new ImMovementActor.Props(
|
||||||
enqueueR,
|
enqueueR,
|
||||||
playerPhysicsControl,
|
|
||||||
camera
|
camera
|
||||||
).behavior
|
).behavior(playerPhysicsControl)
|
||||||
val cameraActorBeh = new PlayerCameraActor.Props(
|
val cameraActorBeh = new PlayerCameraActor.Props(
|
||||||
cameraPivotNode,
|
cameraPivotNode,
|
||||||
enqueueR,
|
enqueueR,
|
||||||
@ -78,36 +76,19 @@ object PlayerController {
|
|||||||
tickEventBus,
|
tickEventBus,
|
||||||
movementActorBeh,
|
movementActorBeh,
|
||||||
cameraActorBeh
|
cameraActorBeh
|
||||||
).behavior(playerPhysicsControl)
|
).behavior
|
||||||
}
|
}
|
||||||
val create: IO[Error, Unit] =
|
val create: UIO[ActorRef[PlayerActorSupervisor.Command]] =
|
||||||
(for {
|
(for {
|
||||||
playerActor <- AkkaUtils.spawnActorL(
|
playerActor <-
|
||||||
playerActorBehavior,
|
AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
|
||||||
"playerActorSupervisor"
|
|
||||||
)
|
|
||||||
// _ <- Task(rootNode += playerNode)
|
|
||||||
// _ <- Task(pprint.log("Physicsspace = " + physicsSpace.toString()))
|
|
||||||
// _ <- Task(pprint.log("playerNode = " + playerNode.toString()))
|
|
||||||
_ <- rootNode += playerNode
|
_ <- rootNode += playerNode
|
||||||
_ <- physicsSpace += playerNode
|
_ <- physicsSpace += playerNode
|
||||||
_ <- physicsSpace += playerPhysicsControl
|
_ <- physicsSpace += playerPhysicsControl
|
||||||
_ <- IO {
|
_ = cameraPivotNode += cameraNode
|
||||||
// physicsSpace += playerNode
|
|
||||||
// physicsSpace += playerPhysicsControl
|
|
||||||
// rootNode += cameraNode
|
|
||||||
cameraPivotNode += cameraNode
|
|
||||||
// playerNode += cameraPivotNode
|
|
||||||
// rootNode += cameraPivotNode
|
|
||||||
}
|
|
||||||
_ <- rootNode += cameraPivotNode
|
_ <- rootNode += cameraPivotNode
|
||||||
} yield ())
|
} yield playerActor).hideErrors
|
||||||
.onErrorHandleWith(e =>
|
|
||||||
UIO(e.printStackTrace()) >> IO.raiseError(
|
|
||||||
GenericError(e.getMessage())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// .executeOn(appScheduler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
@ -146,11 +127,11 @@ object PlayerController {
|
|||||||
val geom = Geometry("playerGeom", b)
|
val geom = Geometry("playerGeom", b)
|
||||||
geom
|
geom
|
||||||
}
|
}
|
||||||
def defaultTexture(assetManager: AssetManager) =
|
// def defaultTexture(assetManager: AssetManager) =
|
||||||
MyMaterial(
|
// MyMaterial(
|
||||||
assetManager = assetManager,
|
// assetManager = assetManager,
|
||||||
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
// path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// new CameraControl(cam) {
|
// new CameraControl(cam) {
|
||||||
// override def controlUpdate(tpf: Float) = {
|
// override def controlUpdate(tpf: Float) = {
|
||||||
@ -164,14 +145,11 @@ object PlayerController {
|
|||||||
|
|
||||||
def defaultCamerNode(
|
def defaultCamerNode(
|
||||||
cam: Camera,
|
cam: Camera,
|
||||||
playerNode: Node,
|
// playerNode: Node,
|
||||||
cameraPivotNode: Node,
|
// cameraPivotNode: Node,
|
||||||
playerPos: ImVector3f
|
playerPos: ImVector3f
|
||||||
) =
|
) =
|
||||||
new CameraNode(
|
new CameraNode("CameraNode", cam)
|
||||||
"CameraNode",
|
|
||||||
cam
|
|
||||||
)
|
|
||||||
// .withControlDir(ControlDirection.SpatialToCamera)
|
// .withControlDir(ControlDirection.SpatialToCamera)
|
||||||
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
|
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
|
||||||
.withLookAt(playerPos, ImVector3f.UNIT_Y)
|
.withLookAt(playerPos, ImVector3f.UNIT_Y)
|
||||||
@ -182,66 +160,32 @@ object PlayerController {
|
|||||||
// camNode: CameraNode,
|
// camNode: CameraNode,
|
||||||
playerPhysicsControl: BetterCharacterControl
|
playerPhysicsControl: BetterCharacterControl
|
||||||
) =
|
) =
|
||||||
Either
|
|
||||||
.catchNonFatal(
|
|
||||||
Node("PlayerNode")
|
Node("PlayerNode")
|
||||||
.withChildren(
|
.withChildren(playerModel)
|
||||||
// camNode,
|
|
||||||
playerModel
|
|
||||||
)
|
|
||||||
.withLocalTranslation(playerPos)
|
.withLocalTranslation(playerPos)
|
||||||
.withControl(playerPhysicsControl)
|
.withControl(playerPhysicsControl)
|
||||||
)
|
.taggedWith[PlayerControllerTags.PlayerTag]
|
||||||
.map(_.taggedWith[PlayerControllerTags.PlayerTag])
|
|
||||||
.leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
|
|
||||||
|
|
||||||
def defaultNpcNode(
|
def defaultNpcNode(
|
||||||
assetManager: AssetManager,
|
// assetManager: AssetManager,
|
||||||
// modelPath: os.RelPath,
|
npcModel: Spatial,
|
||||||
initialPos: ImVector3f,
|
initialPos: ImVector3f,
|
||||||
npcPhysicsControl: BetterCharacterControl,
|
npcPhysicsControl: BetterCharacterControl,
|
||||||
npcName: String
|
npcName: String
|
||||||
) =
|
) =
|
||||||
Either
|
// Either
|
||||||
.catchNonFatal(
|
// .catchNonFatal(
|
||||||
Node(npcName)
|
Node(npcName)
|
||||||
.withChildren(
|
.withChildren(
|
||||||
// assetManager
|
// defaultMesh.withMaterial(defaultTexture(assetManager))
|
||||||
// .loadModel(modelPath)
|
npcModel
|
||||||
// .asInstanceOf[Node]
|
|
||||||
// .withRotate(0, FastMath.PI, 0)
|
|
||||||
defaultMesh.withMaterial(defaultTexture(assetManager))
|
|
||||||
)
|
)
|
||||||
.withLocalTranslation(initialPos)
|
.withLocalTranslation(initialPos)
|
||||||
.withControl(npcPhysicsControl)
|
.withControl(npcPhysicsControl)
|
||||||
)
|
// )
|
||||||
// .map(_.taggedWith[PlayerControllerTags.PlayerTag])
|
|
||||||
// .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
|
|
||||||
|
|
||||||
def defaultPlayerPhysicsControl =
|
def defaultPlayerPhysicsControl =
|
||||||
new BetterCharacterControl(1.5f, 6f, 1f)
|
new BetterCharacterControl(1.5f, 6f, 1f)
|
||||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Methods {}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
// )
|
|
||||||
|
@ -11,9 +11,7 @@ import wow.doge.mygame.subsystems.movement.ImMovementActor
|
|||||||
|
|
||||||
object PlayerMovementEventListener {
|
object PlayerMovementEventListener {
|
||||||
import PlayerMovementEvent._
|
import PlayerMovementEvent._
|
||||||
def apply(
|
def apply(movementActor: ActorRef[ImMovementActor.Command]) =
|
||||||
movementActor: ActorRef[ImMovementActor.Command]
|
|
||||||
) =
|
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
LogOptions()
|
LogOptions()
|
||||||
.withLevel(Level.TRACE)
|
.withLevel(Level.TRACE)
|
||||||
@ -50,9 +48,7 @@ object PlayerMovementEventListener {
|
|||||||
|
|
||||||
object PlayerCameraEventListener {
|
object PlayerCameraEventListener {
|
||||||
import PlayerCameraEvent._
|
import PlayerCameraEvent._
|
||||||
def apply(
|
def apply(playerCameraActor: ActorRef[PlayerCameraActor.Command]) =
|
||||||
playerCameraActor: ActorRef[PlayerCameraActor.Command]
|
|
||||||
) =
|
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
LogOptions()
|
LogOptions()
|
||||||
.withLevel(Level.TRACE)
|
.withLevel(Level.TRACE)
|
||||||
@ -63,7 +59,6 @@ object PlayerCameraEventListener {
|
|||||||
Behaviors.receiveMessagePartial {
|
Behaviors.receiveMessagePartial {
|
||||||
case CameraMovedUp =>
|
case CameraMovedUp =>
|
||||||
playerCameraActor ! PlayerCameraActor.RotateUp
|
playerCameraActor ! PlayerCameraActor.RotateUp
|
||||||
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case CameraMovedDown =>
|
case CameraMovedDown =>
|
||||||
playerCameraActor ! PlayerCameraActor.RotateDown
|
playerCameraActor ! PlayerCameraActor.RotateDown
|
||||||
|
@ -18,6 +18,7 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
|||||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||||
import wow.doge.mygame.utils.IOUtils._
|
import wow.doge.mygame.utils.IOUtils._
|
||||||
|
import monix.bio.UIO
|
||||||
|
|
||||||
object GameInputHandler {
|
object GameInputHandler {
|
||||||
|
|
||||||
@ -29,13 +30,13 @@ object GameInputHandler {
|
|||||||
) {
|
) {
|
||||||
def begin =
|
def begin =
|
||||||
for {
|
for {
|
||||||
_ <- Task(setupMovementKeys(inputManager))
|
_ <- UIO(setupMovementKeys(inputManager))
|
||||||
// _ <- UIO(setupAnalogMovementKeys)
|
// _ <- UIO(setupAnalogMovementKeys)
|
||||||
_ <- Task(setupCameraKeys())
|
_ <- UIO(setupCameraKeys())
|
||||||
_ <- toIO(
|
_ <- toIO(
|
||||||
me.Task.parSequence(
|
me.Task.parSequence(
|
||||||
Seq(
|
Seq(
|
||||||
generateMovementInputEvents(
|
playerMovementInputEventsGenerator(
|
||||||
inputManager,
|
inputManager,
|
||||||
playerEventBus
|
playerEventBus
|
||||||
).completedL,
|
).completedL,
|
||||||
@ -128,11 +129,14 @@ object GameInputHandler {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateMovementInputEvents(
|
def methodName(implicit enclosing: sourcecode.Enclosing) =
|
||||||
|
enclosing.value.split(" ")(0).split("""\.""").last
|
||||||
|
|
||||||
|
def playerMovementInputEventsGenerator(
|
||||||
inputManager: InputManager,
|
inputManager: InputManager,
|
||||||
playerEventBus: GameEventBus[PlayerEvent]
|
playerEventBus: GameEventBus[PlayerEvent]
|
||||||
) = {
|
) = {
|
||||||
val name = "playerMovementInputEventsGenerator"
|
val name = methodName
|
||||||
inputManager
|
inputManager
|
||||||
.enumObservableAction(PlayerMovementInput)
|
.enumObservableAction(PlayerMovementInput)
|
||||||
// .dump("O")
|
// .dump("O")
|
||||||
|
@ -3,33 +3,33 @@ import enumeratum.EnumEntry._
|
|||||||
import enumeratum._
|
import enumeratum._
|
||||||
|
|
||||||
sealed trait PlayerMovementInput extends EnumEntry with UpperSnakecase
|
sealed trait PlayerMovementInput extends EnumEntry with UpperSnakecase
|
||||||
final object PlayerMovementInput extends Enum[PlayerMovementInput] {
|
object PlayerMovementInput extends Enum[PlayerMovementInput] {
|
||||||
val values = findValues
|
val values = findValues
|
||||||
final case object WalkForward extends PlayerMovementInput
|
case object WalkForward extends PlayerMovementInput
|
||||||
final case object WalkRight extends PlayerMovementInput
|
case object WalkRight extends PlayerMovementInput
|
||||||
final case object WalkLeft extends PlayerMovementInput
|
case object WalkLeft extends PlayerMovementInput
|
||||||
final case object WalkBackward extends PlayerMovementInput
|
case object WalkBackward extends PlayerMovementInput
|
||||||
final case object Jump extends PlayerMovementInput
|
case object Jump extends PlayerMovementInput
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
|
sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
|
||||||
final object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
|
object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
|
||||||
val values = findValues
|
val values = findValues
|
||||||
final case object TurnRight extends PlayerAnalogMovementInput
|
case object TurnRight extends PlayerAnalogMovementInput
|
||||||
final case object TurnLeft extends PlayerAnalogMovementInput
|
case object TurnLeft extends PlayerAnalogMovementInput
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait PlayerCameraInput extends EnumEntry with UpperSnakecase
|
sealed trait PlayerCameraInput extends EnumEntry with UpperSnakecase
|
||||||
final object PlayerCameraInput extends Enum[PlayerCameraInput] {
|
object PlayerCameraInput extends Enum[PlayerCameraInput] {
|
||||||
val values = findValues
|
val values = findValues
|
||||||
final case object CameraRotateLeft extends PlayerCameraInput
|
case object CameraRotateLeft extends PlayerCameraInput
|
||||||
final case object CameraRotateRight extends PlayerCameraInput
|
case object CameraRotateRight extends PlayerCameraInput
|
||||||
final case object CameraRotateUp extends PlayerCameraInput
|
case object CameraRotateUp extends PlayerCameraInput
|
||||||
final case object CameraRotateDown extends PlayerCameraInput
|
case object CameraRotateDown extends PlayerCameraInput
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait MiscInput extends EnumEntry with UpperSnakecase
|
sealed trait MiscInput extends EnumEntry with UpperSnakecase
|
||||||
final object MiscInput extends Enum[MiscInput] {
|
object MiscInput extends Enum[MiscInput] {
|
||||||
val values = findValues
|
val values = findValues
|
||||||
final case object ToggleCursor extends MiscInput
|
case object ToggleCursor extends MiscInput
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.jme3.math.ColorRGBA
|
|||||||
import com.jme3.math.Vector3f
|
import com.jme3.math.Vector3f
|
||||||
import com.jme3.renderer.ViewPort
|
import com.jme3.renderer.ViewPort
|
||||||
import com.jme3.scene.Spatial
|
import com.jme3.scene.Spatial
|
||||||
|
import monix.bio.UIO
|
||||||
object DefaultGameLevel {
|
object DefaultGameLevel {
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
@ -22,8 +23,7 @@ object DefaultGameLevel {
|
|||||||
throw new NotImplementedError("No fallback sceneshape")
|
throw new NotImplementedError("No fallback sceneshape")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val landscape: RigidBodyControl =
|
val landscape: RigidBodyControl = new RigidBodyControl(sceneShape, 0)
|
||||||
new RigidBodyControl(sceneShape, 0)
|
|
||||||
|
|
||||||
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
||||||
sceneModel.setLocalScale(2f)
|
sceneModel.setLocalScale(2f)
|
||||||
@ -47,4 +47,58 @@ object DefaultGameLevel {
|
|||||||
directionalLight = dl
|
directionalLight = dl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def apply(
|
||||||
|
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager,
|
||||||
|
viewPort: ViewPort
|
||||||
|
) =
|
||||||
|
// for {
|
||||||
|
// sceneModel <- assetManager.loadModelAs[Node](os.rel / "main.scene")
|
||||||
|
// sceneShape <- UIO(CollisionShapeFactory.createMeshShape(sceneModel))
|
||||||
|
// landscape <- UIO(new RigidBodyControl(sceneShape, 0))
|
||||||
|
|
||||||
|
// _ <- UIO {
|
||||||
|
// viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
||||||
|
// sceneModel.setLocalScale(2f)
|
||||||
|
// sceneModel.addControl(landscape)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// al = {
|
||||||
|
// val al = new AmbientLight()
|
||||||
|
// al.setColor(ColorRGBA.White.mult(1.3f))
|
||||||
|
// al
|
||||||
|
// }
|
||||||
|
|
||||||
|
// dl = {
|
||||||
|
// val dl = new DirectionalLight()
|
||||||
|
// dl.setColor(ColorRGBA.White)
|
||||||
|
// dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal())
|
||||||
|
// dl
|
||||||
|
// }
|
||||||
|
|
||||||
|
// } yield new GameLevel(
|
||||||
|
// model = sceneModel,
|
||||||
|
// physicsControl = landscape,
|
||||||
|
// ambientLight = al,
|
||||||
|
// directionalLight = dl
|
||||||
|
// )
|
||||||
|
GameLevel(
|
||||||
|
os.rel / "main.scene", {
|
||||||
|
val al = new AmbientLight()
|
||||||
|
al.setColor(ColorRGBA.White.mult(1.3f))
|
||||||
|
al
|
||||||
|
}, {
|
||||||
|
val dl = new DirectionalLight()
|
||||||
|
dl.setColor(ColorRGBA.White)
|
||||||
|
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal())
|
||||||
|
dl
|
||||||
|
}
|
||||||
|
)(assetManager).flatMap(gameLevel =>
|
||||||
|
UIO {
|
||||||
|
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
||||||
|
gameLevel.model.setLocalScale(2f)
|
||||||
|
gameLevel.model.addControl(gameLevel.physicsControl)
|
||||||
|
gameLevel
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
package wow.doge.mygame.game.subsystems.level
|
package wow.doge.mygame.game.subsystems.level
|
||||||
|
|
||||||
|
import cats.effect.Resource
|
||||||
import com.jme3.bullet.control.RigidBodyControl
|
import com.jme3.bullet.control.RigidBodyControl
|
||||||
import com.jme3.light.AmbientLight
|
import com.jme3.light.AmbientLight
|
||||||
import com.jme3.light.DirectionalLight
|
import com.jme3.light.DirectionalLight
|
||||||
|
import com.jme3.scene.Node
|
||||||
import com.jme3.scene.Spatial
|
import com.jme3.scene.Spatial
|
||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import monix.bio.UIO
|
||||||
import wow.doge.mygame.game.GameAppTags
|
import wow.doge.mygame.game.GameAppTags
|
||||||
// import wow.doge.mygame.implicits._
|
|
||||||
import wow.doge.mygame.utils.wrappers.jme.AppNode
|
import wow.doge.mygame.utils.wrappers.jme.AppNode
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||||
|
|
||||||
class GameLevel(
|
class GameLevel(
|
||||||
model: Spatial,
|
val model: Spatial,
|
||||||
physicsControl: RigidBodyControl,
|
val physicsControl: RigidBodyControl,
|
||||||
ambientLight: AmbientLight,
|
val ambientLight: AmbientLight,
|
||||||
directionalLight: DirectionalLight
|
val directionalLight: DirectionalLight
|
||||||
) {
|
) {
|
||||||
def addToGame(
|
def addToGame(
|
||||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||||
@ -29,4 +34,56 @@ class GameLevel(
|
|||||||
_ <- physicsSpace += physicsControl
|
_ <- physicsSpace += physicsControl
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
|
def removeFromGame(
|
||||||
|
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||||
|
physicsSpace: PhysicsSpace[Task]
|
||||||
|
) = {
|
||||||
|
for {
|
||||||
|
_ <- rootNode -= model
|
||||||
|
_ <- rootNode -= ambientLight
|
||||||
|
_ <- rootNode -= directionalLight
|
||||||
|
_ <- physicsSpace -= model
|
||||||
|
_ <- physicsSpace -= physicsControl
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
def resource(
|
||||||
|
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||||
|
physicsSpace: PhysicsSpace[Task]
|
||||||
|
) =
|
||||||
|
Resource.make(this.addToGame(rootNode, physicsSpace))(_ =>
|
||||||
|
this.removeFromGame(rootNode, physicsSpace)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object GameLevel {
|
||||||
|
sealed trait Error
|
||||||
|
case class AssetLoadError(err: AssetManager.Error) extends Error
|
||||||
|
case class CollisionShapeCreationFailed(err: CollisionShapeFactory.Error)
|
||||||
|
extends Error
|
||||||
|
|
||||||
|
def apply(
|
||||||
|
modelPath: os.RelPath,
|
||||||
|
al: AmbientLight,
|
||||||
|
dl: DirectionalLight
|
||||||
|
)(implicit
|
||||||
|
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
|
): IO[Error, GameLevel] =
|
||||||
|
for {
|
||||||
|
sceneModel <-
|
||||||
|
assetManager
|
||||||
|
.loadModelAs[Node](modelPath)
|
||||||
|
.mapError(AssetLoadError)
|
||||||
|
sceneShape <-
|
||||||
|
CollisionShapeFactory
|
||||||
|
.createMeshShape(sceneModel)
|
||||||
|
.mapError(CollisionShapeCreationFailed)
|
||||||
|
landscape <- UIO(new RigidBodyControl(sceneShape, 0))
|
||||||
|
|
||||||
|
} yield new GameLevel(
|
||||||
|
model = sceneModel,
|
||||||
|
physicsControl = landscape,
|
||||||
|
ambientLight = al,
|
||||||
|
directionalLight = dl
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -31,16 +31,17 @@ object ImMovementActor {
|
|||||||
// final case object RotateRight extends Movement
|
// final case object RotateRight extends Movement
|
||||||
// final case object RotateLeft extends Movement
|
// final case object RotateLeft extends Movement
|
||||||
|
|
||||||
final class Props[T: CanMove](
|
final class Props(
|
||||||
val enqueueR: Function1[() => Unit, Unit],
|
val enqueueR: Function1[() => Unit, Unit],
|
||||||
val movable: T,
|
|
||||||
// playerMovementEventBus: ActorRef[
|
// playerMovementEventBus: ActorRef[
|
||||||
// EventBus.Command[PlayerMovementEvent]
|
// EventBus.Command[PlayerMovementEvent]
|
||||||
// ]
|
// ]
|
||||||
val camera: Camera
|
val camera: Camera
|
||||||
) {
|
) {
|
||||||
def behavior: Behavior[Command] =
|
def behavior[T: CanMove](movable: T): Behavior[Command] =
|
||||||
Behaviors.setup(ctx => new ImMovementActor(ctx, this).receive(State()))
|
Behaviors.setup(ctx =>
|
||||||
|
new ImMovementActor(ctx, this, movable).receive(State())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +55,8 @@ object ImMovementActor {
|
|||||||
|
|
||||||
class ImMovementActor[T](
|
class ImMovementActor[T](
|
||||||
ctx: ActorContext[ImMovementActor.Command],
|
ctx: ActorContext[ImMovementActor.Command],
|
||||||
props: ImMovementActor.Props[T]
|
props: ImMovementActor.Props,
|
||||||
|
val movable: T
|
||||||
) {
|
) {
|
||||||
import ImMovementActor._
|
import ImMovementActor._
|
||||||
import Methods._
|
import Methods._
|
||||||
@ -66,19 +68,19 @@ class ImMovementActor[T](
|
|||||||
case m: Movement =>
|
case m: Movement =>
|
||||||
m match {
|
m match {
|
||||||
case MovedLeft(pressed) =>
|
case MovedLeft(pressed) =>
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
||||||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
||||||
case MovedUp(pressed) =>
|
case MovedUp(pressed) =>
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
||||||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
||||||
case MovedRight(pressed) =>
|
case MovedRight(pressed) =>
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
||||||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
||||||
case MovedDown(pressed) =>
|
case MovedDown(pressed) =>
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, props.movable))
|
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
||||||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
||||||
case Jump =>
|
case Jump =>
|
||||||
props.enqueueR(() => cm.jump(props.movable))
|
props.enqueueR(() => cm.jump(movable))
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +90,7 @@ class ImMovementActor[T](
|
|||||||
// if (walkDir != ImVector3f.ZERO) {
|
// if (walkDir != ImVector3f.ZERO) {
|
||||||
val tmp = walkDir * 25f * (1f / 144)
|
val tmp = walkDir * 25f * (1f / 144)
|
||||||
props.enqueueR { () =>
|
props.enqueueR { () =>
|
||||||
cm.move(props.movable, tmp)
|
cm.move(movable, tmp)
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
62
src/main/scala/wow/doge/mygame/implicits/CatsImplicits.scala
Normal file
62
src/main/scala/wow/doge/mygame/implicits/CatsImplicits.scala
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package wow.doge.mygame.implicits
|
||||||
|
|
||||||
|
import cats.data.Kleisli
|
||||||
|
import monix.bio.IO
|
||||||
|
import monix.bio.UIO
|
||||||
|
import cats.effect.Resource
|
||||||
|
import cats.Functor
|
||||||
|
import cats.effect.Bracket
|
||||||
|
import monix.bio.Task
|
||||||
|
import cats.Show
|
||||||
|
import cats.syntax.show._
|
||||||
|
|
||||||
|
trait CatsExtensions {
|
||||||
|
implicit class KleisliCompanionExt(k: Kleisli.type) {
|
||||||
|
def io[S, E, A](f: S => IO[E, A]): Kleisli[UIO, S, Either[E, A]] =
|
||||||
|
k.apply(s => f(s).attempt)
|
||||||
|
}
|
||||||
|
implicit class KleisliExt[S, E, A](k: Kleisli[UIO, S, Either[E, A]]) {
|
||||||
|
def runIO(s: S) = k.run(s).rethrow
|
||||||
|
}
|
||||||
|
implicit class ResourceCompanionExt(r: Resource.type) {
|
||||||
|
// def ioMake[UIO, R](acquire: )(release: ) = r.make()()
|
||||||
|
def ioMake[E: Show, A](
|
||||||
|
acquire: IO[E, A]
|
||||||
|
)(
|
||||||
|
release: A => UIO[Unit]
|
||||||
|
)(implicit F: Functor[UIO]): Resource[UIO, Either[E, A]] =
|
||||||
|
r.make(acquire.attempt)(a =>
|
||||||
|
IO.fromEither(a)
|
||||||
|
.onErrorHandleWith(err => IO.terminate(new Exception(err.show)))
|
||||||
|
.flatMap(release)
|
||||||
|
)
|
||||||
|
|
||||||
|
val acq = IO(1).onErrorHandleWith(_ => IO.raiseError(""))
|
||||||
|
|
||||||
|
val res =
|
||||||
|
ioMake(acq)(_ => IO.unit)
|
||||||
|
val result = res
|
||||||
|
.use {
|
||||||
|
case Left(value) => Task(Left(value))
|
||||||
|
case Right(value) => Task(Right(value))
|
||||||
|
}
|
||||||
|
.hideErrors
|
||||||
|
.rethrow
|
||||||
|
|
||||||
|
// IO.unit.bracket()
|
||||||
|
}
|
||||||
|
implicit class ResourceExt[E, A](k: Resource[UIO, Either[E, A]]) {
|
||||||
|
// def runIO(s: S) = k.run(s).rethrow
|
||||||
|
// k.use
|
||||||
|
// : IO[E, B]
|
||||||
|
// def useIO[B](f: Either[E, A] => IO[E, B]) =
|
||||||
|
// k.use(f).rethrow
|
||||||
|
// type test[A] = Tuple2[*, Double]
|
||||||
|
type IoResource[X, D] = Resource[IO[X, *], D]
|
||||||
|
val x: Resource[IO[String, *], String] =
|
||||||
|
Resource.make(IO.raiseError(""))(_ => IO.unit)
|
||||||
|
// x.use(s => Task.unit)
|
||||||
|
val x2: IoResource[String, String] =
|
||||||
|
Resource.make(UIO(""))(_ => IO.unit)
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,8 @@ import com.simsilica.es.EntityComponent
|
|||||||
import com.simsilica.es.EntityData
|
import com.simsilica.es.EntityData
|
||||||
import com.simsilica.es.EntityId
|
import com.simsilica.es.EntityId
|
||||||
import enumeratum._
|
import enumeratum._
|
||||||
|
import io.odin.meta.Position
|
||||||
|
import io.odin.meta.Render
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.bio.UIO
|
import monix.bio.UIO
|
||||||
import monix.execution.Ack
|
import monix.execution.Ack
|
||||||
@ -797,7 +799,7 @@ package object implicits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
implicit class AkkaLoggerExt(private val logger: Logger) extends AnyVal {
|
implicit class AkkaLoggerExt(private val logger: Logger) extends AnyVal {
|
||||||
def logP[T](
|
private def logP[T](
|
||||||
x: sourcecode.Text[T],
|
x: sourcecode.Text[T],
|
||||||
tag: String = "",
|
tag: String = "",
|
||||||
width: Int = 100,
|
width: Int = 100,
|
||||||
@ -806,12 +808,10 @@ package object implicits {
|
|||||||
initialOffset: Int = 0
|
initialOffset: Int = 0
|
||||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||||
|
|
||||||
// def joinSeq[T](seq: Seq[T], sep: T): Seq[T] = {
|
|
||||||
// seq.flatMap(x => Seq(x, sep)).dropRight(1)
|
|
||||||
// }
|
|
||||||
val tagStrs =
|
val tagStrs =
|
||||||
if (tag.isEmpty) Seq.empty
|
if (tag.isEmpty) Seq.empty
|
||||||
else Seq(fansi.Color.Cyan(tag), fansi.Str(" "))
|
else Seq(fansi.Color.Cyan(tag), fansi.Str(" "))
|
||||||
|
// "".slice(1, -1)
|
||||||
val prefix = Seq(
|
val prefix = Seq(
|
||||||
fansi.Color.Magenta(fileName.value),
|
fansi.Color.Magenta(fileName.value),
|
||||||
fansi.Str(":"),
|
fansi.Str(":"),
|
||||||
@ -823,7 +823,6 @@ package object implicits {
|
|||||||
fansi.Str.join(
|
fansi.Str.join(
|
||||||
prefix ++ pprint.tokenize(x.value, width, height, indent).toSeq: _*
|
prefix ++ pprint.tokenize(x.value, width, height, indent).toSeq: _*
|
||||||
)
|
)
|
||||||
// x.value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def warnP[T](
|
def warnP[T](
|
||||||
@ -858,4 +857,18 @@ package object implicits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicit class odinLoggerExt(private val logger: io.odin.Logger[Task])
|
||||||
|
extends AnyVal {
|
||||||
|
def debugU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
|
logger.debug(msg).hideErrors
|
||||||
|
def infoU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
|
logger.info(msg).hideErrors
|
||||||
|
def traceU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
|
logger.trace(msg).hideErrors
|
||||||
|
def warnU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
|
logger.warn(msg).hideErrors
|
||||||
|
def errorU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
|
logger.error(msg).hideErrors
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import cats.effect.Resource
|
|||||||
import cats.effect.concurrent.Deferred
|
import cats.effect.concurrent.Deferred
|
||||||
import cats.kernel.Eq
|
import cats.kernel.Eq
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.beans.value.ObservableValue
|
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.catnap.CancelableF
|
import monix.catnap.CancelableF
|
||||||
import monix.execution.CancelablePromise
|
import monix.execution.CancelablePromise
|
||||||
@ -13,7 +12,6 @@ import monix.{eval => me}
|
|||||||
import scalafx.Includes._
|
import scalafx.Includes._
|
||||||
import scalafx.application.JFXApp
|
import scalafx.application.JFXApp
|
||||||
import scalafx.application.JFXApp.PrimaryStage
|
import scalafx.application.JFXApp.PrimaryStage
|
||||||
import scalafx.beans.property.StringProperty
|
|
||||||
import scalafx.scene.control.Button
|
import scalafx.scene.control.Button
|
||||||
import scalafx.stage.StageStyle
|
import scalafx.stage.StageStyle
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
@ -47,23 +45,10 @@ class Launcher private (props: Launcher.Props) {
|
|||||||
.observableAction()
|
.observableAction()
|
||||||
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))
|
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))
|
||||||
|
|
||||||
def testChangeObs(
|
|
||||||
obs: Observable[(ObservableValue[_ <: String], String, String)]
|
|
||||||
) =
|
|
||||||
obs
|
|
||||||
.doOnNext {
|
|
||||||
case (x, y, z) => monix.eval.Task.unit
|
|
||||||
}
|
|
||||||
// .subscribe()
|
|
||||||
|
|
||||||
private lazy val exitButton = new Button {
|
private lazy val exitButton = new Button {
|
||||||
text = "Exit"
|
text = "Exit"
|
||||||
// text <-- testChangeObs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// exitButton.text.bind
|
|
||||||
StringProperty("") addListener ((_, _, _) => ())
|
|
||||||
|
|
||||||
private lazy val exitAction =
|
private lazy val exitAction =
|
||||||
exitButton
|
exitButton
|
||||||
.observableAction()
|
.observableAction()
|
||||||
@ -103,27 +88,6 @@ class Launcher private (props: Launcher.Props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// import cats.syntax.all._
|
|
||||||
|
|
||||||
// def init(delay: FiniteDuration = 2000.millis) =
|
|
||||||
// for {
|
|
||||||
// _ <- Task(Platform.setImplicitExit(false))
|
|
||||||
// x <- (Task.unit.start, Task.unit.start).parTupled
|
|
||||||
// fxAppStartFib <- Task(internal.main(Array.empty)).start
|
|
||||||
// _ <- Task.sleep(500.millis)
|
|
||||||
// sceneDragFib <- toIO(sceneDragObservable.completedL).start
|
|
||||||
// buttonActionsComposedFib <- toIO(
|
|
||||||
// Observable(launchAction, exitAction).merge
|
|
||||||
// .doOnNext(_ =>
|
|
||||||
// me.Task(internal.stage.close()).executeOn(props.schedulers.fx)
|
|
||||||
// )
|
|
||||||
// .completedL
|
|
||||||
// ).start
|
|
||||||
// c <- CancelableF[Task](
|
|
||||||
// fxAppStartFib.cancel >> buttonActionsComposedFib.cancel >> sceneDragFib.cancel
|
|
||||||
// )
|
|
||||||
// } yield (c)
|
|
||||||
|
|
||||||
def init =
|
def init =
|
||||||
Resource.make(for {
|
Resource.make(for {
|
||||||
_ <- Task(Platform.setImplicitExit(false))
|
_ <- Task(Platform.setImplicitExit(false))
|
||||||
@ -148,13 +112,7 @@ class Launcher private (props: Launcher.Props) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.start
|
.start
|
||||||
c <- CancelableF[Task](
|
c <- CancelableF[Task](combinedFib.cancel)
|
||||||
// Task(println("Cancelling")) >>
|
|
||||||
// combinedFib.cancel >>
|
|
||||||
// fxAppStartFib.cancel
|
|
||||||
// Task.unit
|
|
||||||
combinedFib.cancel
|
|
||||||
)
|
|
||||||
} yield c)(_.cancel)
|
} yield c)(_.cancel)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package wow.doge.mygame.math;
|
package wow.doge.mygame.math;
|
||||||
|
|
||||||
import Math.{sqrt, pow}
|
import cats.Show
|
||||||
|
|
||||||
case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f)
|
import math.{abs, pow, sqrt}
|
||||||
|
|
||||||
|
case class ImVector3f(x: Float, y: Float, z: Float)
|
||||||
object ImVector3f {
|
object ImVector3f {
|
||||||
val ZERO = ImVector3f(0, 0, 0)
|
val ZERO = ImVector3f(0, 0, 0)
|
||||||
val UNIT_X = ImVector3f(1, 0, 0)
|
val UNIT_X = ImVector3f(1, 0, 0)
|
||||||
@ -11,5 +12,18 @@ object ImVector3f {
|
|||||||
val UNIT_Z = ImVector3f(0, 0, 1)
|
val UNIT_Z = ImVector3f(0, 0, 1)
|
||||||
|
|
||||||
def dst(v1: ImVector3f, v2: ImVector3f) =
|
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))
|
sqrt(
|
||||||
|
pow((v1.x - v2.x).toDouble, 2) + pow((v1.y - v2.y).toDouble, 2) + pow(
|
||||||
|
(v1.z - v2.z).toDouble,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
def manhattanDst(v1: ImVector3f, v2: ImVector3f) =
|
||||||
|
abs(v1.x - v2.x) + abs(v1.y - v2.y) + abs(v1.z - v2.z)
|
||||||
|
|
||||||
|
implicit val showForImVector3f = new Show[ImVector3f] {
|
||||||
|
def format(f: Float) = f.formatted("%.2f")
|
||||||
|
override def show(t: ImVector3f): String =
|
||||||
|
s"ImVector3f(${format(t.x)},${format(t.y)},${format(t.z)})"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,20 @@ import akka.actor.typed.ActorRef
|
|||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import akka.event.EventStream
|
import akka.event.EventStream
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.OverflowStrategy
|
||||||
|
import monix.execution.cancelables.SingleAssignCancelable
|
||||||
|
import monix.execution.Ack
|
||||||
|
import akka.util.Timeout
|
||||||
|
import akka.actor.typed.Scheduler
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import scala.util.Random
|
||||||
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
|
import monix.execution.Cancelable
|
||||||
|
import wow.doge.mygame.utils.AkkaUtils
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import monix.bio.UIO
|
||||||
|
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A (typed) event bus
|
* A (typed) event bus
|
||||||
@ -21,27 +35,113 @@ object EventBus {
|
|||||||
final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit
|
final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit
|
||||||
classTag: ClassTag[E]
|
classTag: ClassTag[E]
|
||||||
) extends Command[A] {
|
) extends Command[A] {
|
||||||
|
|
||||||
def topic: Class[_] = classTag.runtimeClass
|
def topic: Class[_] = classTag.runtimeClass
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E])
|
final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E])
|
||||||
extends Command[A]
|
extends Command[A]
|
||||||
|
|
||||||
def apply[A](): Behavior[EventBus.Command[A]] =
|
final case class ObservableSubscription[A, E <: A](
|
||||||
|
replyTo: ActorRef[Observable[E]]
|
||||||
|
)(implicit
|
||||||
|
classTag: ClassTag[E]
|
||||||
|
) extends Command[A] {
|
||||||
|
def ct = classTag
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply[A: ClassTag]()(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
|
): Behavior[EventBus.Command[A]] =
|
||||||
Behaviors.setup { ctx =>
|
Behaviors.setup { ctx =>
|
||||||
val eventStream = new EventStream(ctx.system.classicSystem)
|
val eventStream = new EventStream(ctx.system.classicSystem)
|
||||||
|
implicit val scheduler = ctx.system.scheduler
|
||||||
new EventBus().eventStreamBehavior(eventStream)
|
new EventBus().eventStreamBehavior(eventStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def observable[E](eventBus: ActorRef[EventBus.Command[E]])(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
scheduler: Scheduler,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
ct: ClassTag[E]
|
||||||
|
) =
|
||||||
|
Observable.create[E](OverflowStrategy.DropOld(50)) { sub =>
|
||||||
|
implicit val s = sub.scheduler
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
val behavior = Behaviors.receive[E] { (ctx, msg) =>
|
||||||
|
if (sub.onNext(msg) == Ack.Stop) {
|
||||||
|
c.cancel()
|
||||||
|
Behaviors.stopped
|
||||||
|
} else Behaviors.same
|
||||||
|
|
||||||
|
}
|
||||||
|
val actor =
|
||||||
|
AkkaUtils
|
||||||
|
.spawnActorL(
|
||||||
|
behavior,
|
||||||
|
s"eventBusObservable-${ct.toString}-${Random.nextLong()}"
|
||||||
|
)
|
||||||
|
.tapError {
|
||||||
|
case ex => UIO(sub.onError(ex))
|
||||||
}
|
}
|
||||||
|
|
||||||
class EventBus[B] {
|
(for {
|
||||||
|
a <- actor
|
||||||
|
_ <- eventBus !! Subscribe(a)
|
||||||
|
_ <- UIO(c := Cancelable(() => eventBus ! Unsubscribe(a)))
|
||||||
|
} yield ()).runToFuture
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
def observable2[A, B <: A](eventBus: ActorRef[EventBus.Command[A]])(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
scheduler: Scheduler,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
ct: ClassTag[A],
|
||||||
|
ct2: ClassTag[B]
|
||||||
|
) =
|
||||||
|
Observable.create[B](OverflowStrategy.DropOld(50)) { sub =>
|
||||||
|
implicit val s = sub.scheduler
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
val behavior = Behaviors.receive[B] { (ctx, msg) =>
|
||||||
|
if (sub.onNext(msg) == Ack.Stop) {
|
||||||
|
c.cancel()
|
||||||
|
Behaviors.stopped
|
||||||
|
} else Behaviors.same
|
||||||
|
|
||||||
|
}
|
||||||
|
val actor =
|
||||||
|
AkkaUtils
|
||||||
|
.spawnActorL(
|
||||||
|
behavior,
|
||||||
|
s"eventBusObservable-${ct.toString}-${math.abs(Random.nextLong())}"
|
||||||
|
)
|
||||||
|
.tapError {
|
||||||
|
case ex => UIO(sub.onError(ex))
|
||||||
|
}
|
||||||
|
|
||||||
|
(for {
|
||||||
|
a <- actor
|
||||||
|
_ <- eventBus !! Subscribe(a)
|
||||||
|
_ <- UIO(c := Cancelable(() => eventBus ! Unsubscribe(a)))
|
||||||
|
} yield ()).runToFuture
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventBus[A] {
|
||||||
import akka.actor.typed.scaladsl.adapter._
|
import akka.actor.typed.scaladsl.adapter._
|
||||||
|
|
||||||
private def eventStreamBehavior(
|
private def eventStreamBehavior(
|
||||||
eventStream: akka.event.EventStream
|
eventStream: EventStream
|
||||||
): Behavior[EventBus.Command[B]] =
|
)(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
scheduler: Scheduler,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
ct: ClassTag[A]
|
||||||
|
): Behavior[EventBus.Command[A]] =
|
||||||
|
Behaviors.setup { ctx =>
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case EventBus.Publish(event, name) =>
|
case EventBus.Publish(event, name) =>
|
||||||
eventStream.publish(event)
|
eventStream.publish(event)
|
||||||
@ -52,5 +152,12 @@ class EventBus[B] {
|
|||||||
case EventBus.Unsubscribe(subscriber) =>
|
case EventBus.Unsubscribe(subscriber) =>
|
||||||
eventStream.unsubscribe(subscriber.toClassic)
|
eventStream.unsubscribe(subscriber.toClassic)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
case s @ ObservableSubscription(replyTo) =>
|
||||||
|
val obs = EventBus.observable2(
|
||||||
|
ctx.self
|
||||||
|
)(timeout, scheduler, spawnProtocol, ct, s.ct)
|
||||||
|
replyTo ! obs
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package wow.doge.mygame.subsystems.events
|
|||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.ActorSystem
|
|
||||||
import akka.actor.typed.LogOptions
|
import akka.actor.typed.LogOptions
|
||||||
import akka.actor.typed.Props
|
import akka.actor.typed.Props
|
||||||
import akka.actor.typed.SpawnProtocol
|
import akka.actor.typed.SpawnProtocol
|
||||||
@ -16,10 +15,15 @@ import wow.doge.mygame.implicits._
|
|||||||
import wow.doge.mygame.subsystems.events.Event
|
import wow.doge.mygame.subsystems.events.Event
|
||||||
import wow.doge.mygame.subsystems.events.EventBus
|
import wow.doge.mygame.subsystems.events.EventBus
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent
|
import wow.doge.mygame.subsystems.events.TickEvent
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
import akka.actor.typed.Scheduler
|
||||||
|
|
||||||
class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) {
|
class EventsModule(
|
||||||
implicit val s = spawnProtocol.scheduler
|
scheduler: Scheduler,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
|
) {
|
||||||
|
implicit val s = scheduler
|
||||||
|
implicit val sp = spawnProtocol
|
||||||
implicit val timeout = Timeout(1.second)
|
implicit val timeout = Timeout(1.second)
|
||||||
|
|
||||||
val eventBusLogger = SLogger[EventBus[_]]
|
val eventBusLogger = SLogger[EventBus[_]]
|
||||||
@ -35,7 +39,10 @@ class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) {
|
|||||||
|
|
||||||
val mainEventBusTask = createEventBus[Event]("mainEventBus")
|
val mainEventBusTask = createEventBus[Event]("mainEventBus")
|
||||||
|
|
||||||
def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) =
|
def createEventBus[T: ClassTag](
|
||||||
|
busName: String,
|
||||||
|
logLevel: Level = Level.DEBUG
|
||||||
|
) =
|
||||||
spawnProtocol.askL(
|
spawnProtocol.askL(
|
||||||
SpawnProtocol.Spawn[EventBus.Command[T]](
|
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
|
@ -27,7 +27,7 @@ object Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ModdingSystem {
|
object ModdingSystem {
|
||||||
sealed trait Error extends Serializable with Product
|
sealed trait Error
|
||||||
final case class CouldNotDecode(cause: String) extends Error
|
final case class CouldNotDecode(cause: String) extends Error
|
||||||
final case class ParseFailure(cause: String) extends Error
|
final case class ParseFailure(cause: String) extends Error
|
||||||
final case class FileNotFound(fileName: String) extends Error
|
final case class FileNotFound(fileName: String) extends Error
|
||||||
@ -53,7 +53,7 @@ object ModdingSystem {
|
|||||||
def findAndReadPluginFiles(
|
def findAndReadPluginFiles(
|
||||||
dir: os.Path,
|
dir: os.Path,
|
||||||
plugins: ArraySeq[Plugin]
|
plugins: ArraySeq[Plugin]
|
||||||
) =
|
): (View[(Plugin, Error)], View[(Plugin, String)]) =
|
||||||
plugins
|
plugins
|
||||||
.sortBy(_.priority)
|
.sortBy(_.priority)
|
||||||
.view
|
.view
|
||||||
@ -79,9 +79,8 @@ object ModdingSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def readPluginFiles(filePaths: View[os.Path]) = {
|
def readPluginFiles(filePaths: View[os.Path]) =
|
||||||
filePaths.map(path => os.read(path))
|
filePaths.map(path => os.read(path))
|
||||||
}
|
|
||||||
|
|
||||||
def parsePluginFiles(files: View[(Plugin, String)]) =
|
def parsePluginFiles(files: View[(Plugin, String)]) =
|
||||||
files
|
files
|
||||||
@ -99,11 +98,10 @@ object ModdingSystem {
|
|||||||
case (json, value) => json.deepMerge(value)
|
case (json, value) => json.deepMerge(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
def mergePluginData(plugins: View[(Plugin, Json)]) = {
|
def mergePluginData(plugins: View[(Plugin, Json)]) =
|
||||||
foldMerge(plugins.map {
|
foldMerge(plugins.map {
|
||||||
case (p, json) => json
|
case (p, json) => json
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
def mergePluginDataConsumer =
|
def mergePluginDataConsumer =
|
||||||
Consumer.foldLeft[Json, Json](Json.fromString("empty")) {
|
Consumer.foldLeft[Json, Json](Json.fromString("empty")) {
|
||||||
@ -143,13 +141,13 @@ object ModdingSystem {
|
|||||||
.map { case (p, json) => json }
|
.map { case (p, json) => json }
|
||||||
.consumeWith(loadBalancedPluginDataMerger)
|
.consumeWith(loadBalancedPluginDataMerger)
|
||||||
)
|
)
|
||||||
.onErrorHandle(e => GenericError)
|
.hideErrors
|
||||||
_ <- UIO {
|
_ <- UIO {
|
||||||
println(s"Read Successes = ${readSuccesses.to(Seq)}")
|
println(s"Read Successes = ${readSuccesses.to(Seq)}")
|
||||||
println(s"Read Failures = ${readFailures.to(Seq)}")
|
println(s"Read Failures = ${readFailures.to(Seq)}")
|
||||||
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
|
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
|
||||||
println(s"Parse Failures = ${parseFailures.to(Seq)}")
|
println(s"Parse Failures = ${parseFailures.to(Seq)}")
|
||||||
println(s"Merged = $res")
|
println(show"Merged = $res")
|
||||||
}
|
}
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
|
@ -181,12 +181,11 @@ class ScriptActor(
|
|||||||
os.Path,
|
os.Path,
|
||||||
Either[wow.doge.mygame.state.ScriptActor.Error, Any]
|
Either[wow.doge.mygame.state.ScriptActor.Error, Any]
|
||||||
]
|
]
|
||||||
): LOL = {
|
): LOL =
|
||||||
paths match {
|
paths match {
|
||||||
case head :: next => loop(next, scriptsMap + (head -> getScript(head)))
|
case head :: next => loop(next, scriptsMap + (head -> getScript(head)))
|
||||||
case Nil => scriptsMap
|
case Nil => scriptsMap
|
||||||
}
|
}
|
||||||
}
|
|
||||||
loop(paths, Map.empty)
|
loop(paths, Map.empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,6 @@ class ScriptCachingActor(
|
|||||||
) {
|
) {
|
||||||
import com.softwaremill.quicklens._
|
import com.softwaremill.quicklens._
|
||||||
import ScriptCachingActor._
|
import ScriptCachingActor._
|
||||||
import Methods._
|
|
||||||
def receiveMessage(state: State): Behavior[Command] =
|
def receiveMessage(state: State): Behavior[Command] =
|
||||||
Behaviors.receiveMessage { msg =>
|
Behaviors.receiveMessage { msg =>
|
||||||
msg match {
|
msg match {
|
||||||
@ -108,7 +107,6 @@ class ScriptCachingActor(
|
|||||||
ctx.self ! DelegateToChild(scriptActor, scriptPath, requester)
|
ctx.self ! DelegateToChild(scriptActor, scriptPath, requester)
|
||||||
else
|
else
|
||||||
getOrCompileScript(
|
getOrCompileScript(
|
||||||
ctx,
|
|
||||||
scriptPath,
|
scriptPath,
|
||||||
state.scriptsMap,
|
state.scriptsMap,
|
||||||
scriptActor,
|
scriptActor,
|
||||||
@ -165,7 +163,6 @@ class ScriptCachingActor(
|
|||||||
implicit val timeout = Timeout(15.seconds)
|
implicit val timeout = Timeout(15.seconds)
|
||||||
// child ! ScriptActor.CompileAny(scriptPath, requester)
|
// child ! ScriptActor.CompileAny(scriptPath, requester)
|
||||||
askChildForScriptCompilation(
|
askChildForScriptCompilation(
|
||||||
ctx,
|
|
||||||
scriptActor,
|
scriptActor,
|
||||||
scriptPath,
|
scriptPath,
|
||||||
requester
|
requester
|
||||||
@ -200,29 +197,12 @@ class ScriptCachingActor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object ScriptActorPool {
|
|
||||||
def apply(
|
|
||||||
poolSize: Int
|
|
||||||
): PoolRouter[ScriptActor.Command] =
|
|
||||||
Routers.pool(poolSize = poolSize)(
|
|
||||||
// make sure the workers are restarted if they fail
|
|
||||||
Behaviors
|
|
||||||
.supervise(ScriptActor())
|
|
||||||
.onFailure[Exception](SupervisorStrategy.restart)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private[scriptsystem] object Methods {
|
|
||||||
import ScriptCachingActor._
|
|
||||||
def getOrCompileScript(
|
def getOrCompileScript(
|
||||||
ctx: ActorContext[Command],
|
|
||||||
scriptPath: os.Path,
|
scriptPath: os.Path,
|
||||||
scriptsMap: ScriptsMap,
|
scriptsMap: ScriptsMap,
|
||||||
scriptActor: ActorRef[ScriptActor.Command],
|
scriptActor: ActorRef[ScriptActor.Command],
|
||||||
requester: ActorRef[ScriptResult]
|
requester: ActorRef[ScriptResult]
|
||||||
) = {
|
) =
|
||||||
scriptsMap
|
scriptsMap
|
||||||
.get(scriptPath)
|
.get(scriptPath)
|
||||||
.fold {
|
.fold {
|
||||||
@ -236,10 +216,8 @@ private[scriptsystem] object Methods {
|
|||||||
ctx.log.debugP("Getting script from cache")
|
ctx.log.debugP("Getting script from cache")
|
||||||
requester ! Right(s)
|
requester ! Right(s)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def askChildForScriptCompilation(
|
def askChildForScriptCompilation(
|
||||||
ctx: ActorContext[Command],
|
|
||||||
scriptActor: ActorRef[ScriptActor.Command],
|
scriptActor: ActorRef[ScriptActor.Command],
|
||||||
scriptPath: os.Path,
|
scriptPath: os.Path,
|
||||||
requester: ActorRef[ScriptResult]
|
requester: ActorRef[ScriptResult]
|
||||||
@ -249,7 +227,7 @@ private[scriptsystem] object Methods {
|
|||||||
requester ! value
|
requester ! value
|
||||||
value.fold(
|
value.fold(
|
||||||
err => {
|
err => {
|
||||||
ctx.log.error(err.reason)
|
ctx.log.errorP(err.reason)
|
||||||
NoOp
|
NoOp
|
||||||
},
|
},
|
||||||
res => {
|
res => {
|
||||||
@ -257,9 +235,22 @@ private[scriptsystem] object Methods {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
case Failure(exception) => {
|
case Failure(exception) => {
|
||||||
ctx.log.error(exception.getMessage())
|
ctx.log.errorP(exception.getMessage)
|
||||||
NoOp
|
NoOp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object ScriptActorPool {
|
||||||
|
def apply(
|
||||||
|
poolSize: Int
|
||||||
|
): PoolRouter[ScriptActor.Command] =
|
||||||
|
Routers.pool(poolSize = poolSize)(
|
||||||
|
// make sure the workers are restarted if they fail
|
||||||
|
Behaviors
|
||||||
|
.supervise(ScriptActor())
|
||||||
|
.onFailure[Exception](SupervisorStrategy.restart)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,12 @@ import akka.actor.typed.Scheduler
|
|||||||
import akka.actor.typed.SpawnProtocol
|
import akka.actor.typed.SpawnProtocol
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
|
import java.util.concurrent.TimeoutException
|
||||||
|
import monix.bio.IO
|
||||||
|
import wow.doge.mygame.AppError.TimeoutError
|
||||||
|
|
||||||
object AkkaUtils {
|
object AkkaUtils {
|
||||||
|
|
||||||
def spawnActorOldL[T](
|
def spawnActorOldL[T](
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
actorName: String,
|
actorName: String,
|
||||||
@ -24,18 +28,23 @@ object AkkaUtils {
|
|||||||
)
|
)
|
||||||
def spawnActorL[T](
|
def spawnActorL[T](
|
||||||
behavior: Behavior[T],
|
behavior: Behavior[T],
|
||||||
actorName: String
|
actorName: String,
|
||||||
|
props: Props = Props.empty
|
||||||
)(implicit
|
)(implicit
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
scheduler: Scheduler,
|
scheduler: Scheduler,
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
) =
|
) =
|
||||||
spawnProtocol.askL[ActorRef[T]](
|
spawnProtocol
|
||||||
|
.askL[ActorRef[T]](
|
||||||
SpawnProtocol.Spawn(
|
SpawnProtocol.Spawn(
|
||||||
behavior,
|
behavior,
|
||||||
actorName,
|
actorName,
|
||||||
Props.empty,
|
props,
|
||||||
_
|
_
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
// .onErrorHandleWith {
|
||||||
|
// case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ class GenericConsoleStream[T](
|
|||||||
outputStream: OutputStream,
|
outputStream: OutputStream,
|
||||||
val config: GenericConsoleStream.Config =
|
val config: GenericConsoleStream.Config =
|
||||||
GenericConsoleStream.Config.default,
|
GenericConsoleStream.Config.default,
|
||||||
// TODO make this atomic
|
// TODO make this atomic ?
|
||||||
private var _streamable: Option[T] = None
|
private var _streamable: Option[T] = None
|
||||||
)(implicit
|
)(implicit
|
||||||
cs: ConsoleStreamable[T]
|
cs: ConsoleStreamable[T]
|
||||||
@ -27,28 +27,27 @@ class GenericConsoleStream[T](
|
|||||||
stble.foreach(s => cs.println(s, text))
|
stble.foreach(s => cs.println(s, text))
|
||||||
|
|
||||||
override def println(text: String): Unit =
|
override def println(text: String): Unit =
|
||||||
if (config.exclusive) {
|
if (config.exclusive)
|
||||||
printToStreamable(_streamable, text)
|
printToStreamable(_streamable, text)
|
||||||
} else {
|
else {
|
||||||
defaultOut.println(text)
|
defaultOut.println(text)
|
||||||
printToStreamable(_streamable, text)
|
printToStreamable(_streamable, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def print(text: String): Unit =
|
override def print(text: String): Unit =
|
||||||
if (config.exclusive) {
|
if (config.exclusive)
|
||||||
printToStreamable(_streamable, text)
|
printToStreamable(_streamable, text)
|
||||||
} else {
|
else {
|
||||||
defaultOut.println(text)
|
defaultOut.println(text)
|
||||||
printToStreamable(_streamable, text)
|
printToStreamable(_streamable, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
def :=(s: T) = {
|
def :=(s: T) =
|
||||||
_streamable match {
|
_streamable match {
|
||||||
case Some(value) =>
|
case Some(value) =>
|
||||||
case None => _streamable = Some(s)
|
case None => _streamable = Some(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
object GenericConsoleStream {
|
object GenericConsoleStream {
|
||||||
|
|
||||||
|
66
src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala
Normal file
66
src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package wow.doge.mygame.utils
|
||||||
|
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.scaladsl.TimerScheduler
|
||||||
|
import scala.util.Random
|
||||||
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
|
object GenericTimerActor {
|
||||||
|
sealed trait Command
|
||||||
|
final case object Start extends Command
|
||||||
|
final case object Stop extends Command
|
||||||
|
private case object Tick extends Command
|
||||||
|
case class TimerKey(seed: Long)
|
||||||
|
|
||||||
|
case class Props[T](
|
||||||
|
target: ActorRef[T],
|
||||||
|
messageToSend: T,
|
||||||
|
timeInterval: FiniteDuration
|
||||||
|
) {
|
||||||
|
def behavior =
|
||||||
|
Behaviors.withTimers[Command] { timers =>
|
||||||
|
Behaviors.setup { ctx =>
|
||||||
|
new GenericTimerActor(
|
||||||
|
ctx,
|
||||||
|
timers,
|
||||||
|
TimerKey(Random.nextLong()),
|
||||||
|
this
|
||||||
|
).idle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class GenericTimerActor[T](
|
||||||
|
ctx: ActorContext[GenericTimerActor.Command],
|
||||||
|
timers: TimerScheduler[GenericTimerActor.Command],
|
||||||
|
timerKey: GenericTimerActor.TimerKey,
|
||||||
|
props: GenericTimerActor.Props[T]
|
||||||
|
) {
|
||||||
|
import GenericTimerActor._
|
||||||
|
|
||||||
|
val idle: Behavior[Command] =
|
||||||
|
Behaviors.receiveMessage {
|
||||||
|
case Start =>
|
||||||
|
timers.startTimerWithFixedDelay(timerKey, Tick, props.timeInterval)
|
||||||
|
active
|
||||||
|
case _ => Behaviors.unhandled
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val active: Behavior[Command] =
|
||||||
|
Behaviors.receiveMessage {
|
||||||
|
case Start =>
|
||||||
|
ctx.log.warnP(s"Timer-${timerKey.seed}: Timer already started")
|
||||||
|
Behaviors.same
|
||||||
|
case Tick =>
|
||||||
|
props.target ! props.messageToSend
|
||||||
|
Behaviors.same
|
||||||
|
case Stop =>
|
||||||
|
timers.cancel(timerKey)
|
||||||
|
idle
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package wow.doge.mygame.utils.wrappers.jme
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetLoadException
|
||||||
|
import com.jme3.asset.AssetLocator
|
||||||
|
import com.jme3.asset.AssetNotFoundException
|
||||||
|
import com.jme3.scene.Spatial
|
||||||
|
import com.jme3.{asset => jmea}
|
||||||
|
import monix.bio.IO
|
||||||
|
import monix.bio.UIO
|
||||||
|
|
||||||
|
class AssetManager(assetManager: jmea.AssetManager) {
|
||||||
|
import AssetManager._
|
||||||
|
def loadModel(path: os.RelPath): IO[Error, Spatial] =
|
||||||
|
IO(assetManager.loadModel(path.toString)).onErrorHandleWith {
|
||||||
|
case ex: AssetNotFoundException =>
|
||||||
|
IO.raiseError(AssetNotFound(ex.getMessage))
|
||||||
|
case ex: AssetLoadException =>
|
||||||
|
IO.raiseError(AssetLoadError(ex.getMessage))
|
||||||
|
}
|
||||||
|
def loadModelAs[T <: Spatial](
|
||||||
|
path: os.RelPath
|
||||||
|
)(implicit ct: ClassTag[T]): IO[Error, T] =
|
||||||
|
loadModel(path).flatMap(model =>
|
||||||
|
if (model.getClass == ct.runtimeClass)
|
||||||
|
UIO(model.asInstanceOf[T])
|
||||||
|
else IO.raiseError(CouldNotCastError)
|
||||||
|
)
|
||||||
|
def loadAssetAs[T](path: os.RelPath)(implicit ct: ClassTag[T]): IO[Error, T] =
|
||||||
|
IO(assetManager.loadAsset(path.toString))
|
||||||
|
.onErrorHandleWith {
|
||||||
|
case ex: AssetNotFoundException =>
|
||||||
|
IO.raiseError(AssetNotFound(ex.getMessage))
|
||||||
|
case ex: AssetLoadException =>
|
||||||
|
IO.raiseError(AssetLoadError(ex.getMessage))
|
||||||
|
}
|
||||||
|
.flatMap(asset =>
|
||||||
|
if (asset.getClass == ct.runtimeClass)
|
||||||
|
UIO(asset.asInstanceOf[T])
|
||||||
|
else IO.raiseError(CouldNotCastError)
|
||||||
|
)
|
||||||
|
def registerLocator(path: os.RelPath, locator: Class[_ <: AssetLocator]) =
|
||||||
|
UIO(assetManager.registerLocator(path.toString, locator))
|
||||||
|
|
||||||
|
}
|
||||||
|
object AssetManager {
|
||||||
|
sealed trait Error
|
||||||
|
case class AssetNotFound(message: String) extends Error
|
||||||
|
case class AssetLoadError(message: String) extends Error
|
||||||
|
case object CouldNotCastError extends Error
|
||||||
|
import cats.data.ReaderT
|
||||||
|
type IoReaderT[S, E, A] = ReaderT[UIO, S, Either[E, A]]
|
||||||
|
val IoReaderT = ReaderT
|
||||||
|
val t =
|
||||||
|
ReaderT[UIO, String, Either[Error, Unit]](s => UIO.unit.attempt)
|
||||||
|
.run("s")
|
||||||
|
.rethrow
|
||||||
|
val r: IoReaderT[String, Error, Unit] = IoReaderT(s => UIO.unit.attempt)
|
||||||
|
val t2 = r.run("s").rethrow
|
||||||
|
// Kleisli[IO, String, Unit](s => IO.unit)
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package wow.doge.mygame.utils.wrappers.jme
|
||||||
|
|
||||||
|
import com.jme3.bullet.collision.shapes.CollisionShape
|
||||||
|
import com.jme3.bullet.{util => jmebu}
|
||||||
|
import com.jme3.scene.Spatial
|
||||||
|
import monix.bio.IO
|
||||||
|
|
||||||
|
object CollisionShapeFactory {
|
||||||
|
sealed trait Error
|
||||||
|
case class WrongArgumentError(reason: String) extends Error
|
||||||
|
|
||||||
|
def createMeshShape(subtree: Spatial): IO[Error, CollisionShape] =
|
||||||
|
IO(jmebu.CollisionShapeFactory.createMeshShape(subtree)).onErrorHandleWith {
|
||||||
|
case ex: IllegalArgumentException
|
||||||
|
if (ex.getMessage.startsWith("The spatial must either be a Node")) =>
|
||||||
|
IO.raiseError(WrongArgumentError(ex.getMessage))
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,23 @@
|
|||||||
package wow.doge.mygame.utils.wrappers.jme
|
package wow.doge.mygame.utils.wrappers.jme
|
||||||
|
|
||||||
import cats.effect.Sync
|
import cats.effect.Sync
|
||||||
|
import cats.syntax.eq._
|
||||||
|
import com.jme3.light.Light
|
||||||
import com.jme3.{scene => jmes}
|
import com.jme3.{scene => jmes}
|
||||||
|
import monix.bio.IO
|
||||||
|
import monix.bio.Task
|
||||||
|
import monix.bio.UIO
|
||||||
import monix.execution.annotations.UnsafeBecauseImpure
|
import monix.execution.annotations.UnsafeBecauseImpure
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import com.jme3.light.Light
|
|
||||||
|
|
||||||
trait NodeDelegate {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the underlying wrapped value
|
|
||||||
*/
|
|
||||||
@UnsafeBecauseImpure
|
|
||||||
def unsafeDelegate: jmes.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class NodeWrapper[F[_]: Sync] protected (node: jmes.Node) {
|
abstract class NodeWrapper[F[_]: Sync] protected (node: jmes.Node) {
|
||||||
|
def name = node.getName()
|
||||||
def children: Observable[jmes.Spatial] = node.observableChildren
|
def children: Observable[jmes.Spatial] = node.observableChildren
|
||||||
def attachChild(n: jmes.Spatial): F[Unit] = Sync[F].delay(node.attachChild(n))
|
def attachChild(n: jmes.Spatial): F[Unit] = Sync[F].delay(node.attachChild(n))
|
||||||
def add(wn: Node[F]): F[Unit] =
|
def add(wn: Node[F]): F[Unit] =
|
||||||
Sync[F].delay(node.attachChild(wn.unsafeDelegate))
|
Sync[F].delay(node.attachChild(wn.unsafeDelegate))
|
||||||
def remove(n: jmes.Spatial): F[Unit] =
|
def remove(n: jmes.Spatial): F[Unit] = Sync[F].delay(node.detachChild(n))
|
||||||
Sync[F].delay(node.detachChild(n))
|
|
||||||
def remove(wn: Node[F]): F[Unit] =
|
def remove(wn: Node[F]): F[Unit] =
|
||||||
Sync[F].delay(node.detachChild(wn.unsafeDelegate))
|
Sync[F].delay(node.detachChild(wn.unsafeDelegate))
|
||||||
def addLight(light: Light) =
|
def addLight(light: Light) =
|
||||||
@ -52,8 +47,7 @@ object NodeWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class Node[F[_]: Sync] private (node: jmes.Node)
|
final class Node[F[_]: Sync] private (node: jmes.Node)
|
||||||
extends NodeWrapper[F](node)
|
extends NodeWrapper[F](node) {
|
||||||
with NodeDelegate {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the underlying wrapped value
|
* Get the underlying wrapped value
|
||||||
@ -74,3 +68,73 @@ object AppNode {
|
|||||||
def apply[F[_]: Sync](n: jmes.Node) = new AppNode[F](n)
|
def apply[F[_]: Sync](n: jmes.Node) = new AppNode[F](n)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class NodeWrapper2 protected (node: jmes.Node) {
|
||||||
|
import NodeWrapper2._
|
||||||
|
def name = node.getName()
|
||||||
|
def children: Observable[jmes.Spatial] = node.observableChildren
|
||||||
|
def attachChild(n: jmes.Spatial): IO[AddNodeToItselfError.type, Unit] =
|
||||||
|
IO { node.attachChild(n); () }.onErrorHandleWith {
|
||||||
|
case ex: IllegalArgumentException =>
|
||||||
|
if (ex.getMessage === "Cannot add child to itself")
|
||||||
|
IO.raiseError(AddNodeToItselfError)
|
||||||
|
else IO.unit
|
||||||
|
}
|
||||||
|
def add(wn: Node2): IO[AddNodeToItselfError.type, Unit] =
|
||||||
|
IO { node.attachChild(wn.unsafeDelegate); () }.onErrorHandleWith {
|
||||||
|
case ex: IllegalArgumentException =>
|
||||||
|
if (ex.getMessage === "Cannot add child to itself")
|
||||||
|
IO.raiseError(AddNodeToItselfError)
|
||||||
|
else IO.unit
|
||||||
|
}
|
||||||
|
def remove(n: jmes.Spatial) = UIO(node.detachChild(n))
|
||||||
|
def remove(wn: Node2) =
|
||||||
|
UIO(node.detachChild(wn.unsafeDelegate))
|
||||||
|
def addLight(light: Light) =
|
||||||
|
UIO {
|
||||||
|
node.addLight(light)
|
||||||
|
}
|
||||||
|
def removeLight(light: Light) =
|
||||||
|
UIO {
|
||||||
|
node.removeLight(light)
|
||||||
|
}
|
||||||
|
def asSpatial: Task[jmes.Spatial] = UIO(node)
|
||||||
|
}
|
||||||
|
object NodeWrapper2 {
|
||||||
|
sealed trait Error
|
||||||
|
case object AddNodeToItselfError extends Error
|
||||||
|
implicit class NodeOps[F[_]](private val nw: NodeWrapper2) extends AnyVal {
|
||||||
|
def +=(n: jmes.Spatial) = nw.attachChild(n)
|
||||||
|
def +=(n: Node2) = nw.add(n)
|
||||||
|
def -=(n: jmes.Spatial) = nw.remove(n)
|
||||||
|
def -=(wn: Node2) = nw.remove(wn)
|
||||||
|
def +=(light: Light) = {
|
||||||
|
nw.addLight(light)
|
||||||
|
}
|
||||||
|
|
||||||
|
def -=(light: Light) = {
|
||||||
|
nw.removeLight(light)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Node2 private (node: jmes.Node) extends NodeWrapper2(node) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the underlying wrapped value
|
||||||
|
*/
|
||||||
|
@UnsafeBecauseImpure
|
||||||
|
def unsafeDelegate = node
|
||||||
|
}
|
||||||
|
object Node2 {
|
||||||
|
def apply(name: String) = new Node2(new jmes.Node(name))
|
||||||
|
def apply(n: jmes.Node) = new Node2(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class AppNode2 private (node: jmes.Node) extends NodeWrapper2(node)
|
||||||
|
object AppNode2 {
|
||||||
|
|
||||||
|
def apply(name: String) = new AppNode2(new jmes.Node(name))
|
||||||
|
def apply(n: jmes.Node) = new AppNode2(n)
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -38,6 +38,8 @@ object PhysicsSpace {
|
|||||||
space
|
space
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def -=(anyObject: Any) = space.remove(anyObject)
|
||||||
|
|
||||||
def +=(spatial: jmes.Spatial) = space.addAll(spatial)
|
def +=(spatial: jmes.Spatial) = space.addAll(spatial)
|
||||||
|
|
||||||
def :+(spatial: jmes.Spatial) = {
|
def :+(spatial: jmes.Spatial) = {
|
||||||
@ -49,5 +51,7 @@ object PhysicsSpace {
|
|||||||
space.removeAll(spatial)
|
space.removeAll(spatial)
|
||||||
space
|
space
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def -=(spatial: jmes.Spatial) = space.removeAll(spatial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala
Normal file
57
src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
import org.scalatest.BeforeAndAfterAll
|
||||||
|
import akka.actor.typed.ActorSystem
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
|
import akka.util.Timeout
|
||||||
|
import scala.concurrent.Await
|
||||||
|
|
||||||
|
class ActorTimeoutTest extends AnyFunSuite with BeforeAndAfterAll {
|
||||||
|
import ActorTimeoutTest._
|
||||||
|
implicit val as = ActorSystem(new MyActor.Props().create, "system")
|
||||||
|
implicit val timeout = Timeout(1.millis)
|
||||||
|
|
||||||
|
test("timeoutTest") {
|
||||||
|
val fut = as.ask(MyActor.GetInt(_))
|
||||||
|
val res = Await.result(fut, 1.second)
|
||||||
|
assert(res == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected def afterAll(): Unit = {
|
||||||
|
as.terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object ActorTimeoutTest {
|
||||||
|
object MyActor {
|
||||||
|
sealed trait Command
|
||||||
|
case class GetInt(replyTo: ActorRef[Int]) extends Command
|
||||||
|
|
||||||
|
class Props() {
|
||||||
|
def create =
|
||||||
|
Behaviors.setup[Command] { ctx =>
|
||||||
|
new MyActor(ctx, this).receive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class MyActor(
|
||||||
|
ctx: ActorContext[MyActor.Command],
|
||||||
|
props: MyActor.Props
|
||||||
|
) {
|
||||||
|
import MyActor._
|
||||||
|
def receive =
|
||||||
|
Behaviors.receiveMessage[Command] {
|
||||||
|
case GetInt(replyTo) =>
|
||||||
|
// Thread.sleep(1000)
|
||||||
|
replyTo ! 1
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/test/scala/wow/doge/mygame/AssetManagerTest.scala
Normal file
59
src/test/scala/wow/doge/mygame/AssetManagerTest.scala
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
import monix.execution.Scheduler.Implicits.global
|
||||||
|
import cats.syntax.eq._
|
||||||
|
import com.jme3.{asset => jmea}
|
||||||
|
import com.jme3.asset.DesktopAssetManager
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager.AssetNotFound
|
||||||
|
import com.jme3.scene.Geometry
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager.CouldNotCastError
|
||||||
|
import com.jme3.scene.Node
|
||||||
|
import com.jme3.material.MaterialDef
|
||||||
|
import com.jme3.material.Material
|
||||||
|
|
||||||
|
class AssetManagerTest extends AnyFunSuite {
|
||||||
|
|
||||||
|
val _assetManager: jmea.AssetManager = new DesktopAssetManager(true)
|
||||||
|
val assetManager = new AssetManager(_assetManager)
|
||||||
|
|
||||||
|
test("Test for AssetNotFound error") {
|
||||||
|
val res =
|
||||||
|
assetManager.loadModel(os.rel / "doesnotexist").attempt.runSyncUnsafe()
|
||||||
|
assert(res === Left(AssetNotFound("doesnotexist")))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Test for Model CouldNotCastError") {
|
||||||
|
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||||
|
val res1 = assetManager
|
||||||
|
.loadModelAs[Geometry](modelPath)
|
||||||
|
.attempt
|
||||||
|
.runSyncUnsafe()
|
||||||
|
|
||||||
|
assert(res1 === Left(CouldNotCastError))
|
||||||
|
|
||||||
|
val res2 = assetManager
|
||||||
|
.loadModelAs[Node](modelPath)
|
||||||
|
.attempt
|
||||||
|
.runSyncUnsafe()
|
||||||
|
assert(res2.map(_.getName) === Right("JaimeGeom-ogremesh"))
|
||||||
|
}
|
||||||
|
|
||||||
|
test("Test for Asset CouldNotCastError") {
|
||||||
|
val assetPath = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||||
|
|
||||||
|
val res1 = assetManager
|
||||||
|
.loadAssetAs[Material](assetPath)
|
||||||
|
.attempt
|
||||||
|
.runSyncUnsafe()
|
||||||
|
|
||||||
|
assert(res1 === Left(CouldNotCastError))
|
||||||
|
|
||||||
|
val res2 = assetManager
|
||||||
|
.loadAssetAs[MaterialDef](assetPath)
|
||||||
|
.attempt
|
||||||
|
.runSyncUnsafe()
|
||||||
|
assert(res2.map(_.getName) === Right("Unshaded"))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
import com.jme3.scene.Spatial
|
||||||
|
import com.jme3.collision.{Collidable, CollisionResults}
|
||||||
|
import com.jme3.bounding.BoundingVolume
|
||||||
|
import com.jme3.scene.Spatial.DFSMode
|
||||||
|
import com.jme3.scene.SceneGraphVisitor
|
||||||
|
import java.util.Queue
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||||
|
import monix.execution.Scheduler.Implicits.global
|
||||||
|
import cats.syntax.eq._
|
||||||
|
|
||||||
|
class CollisionShapeFactoryTest extends AnyFunSuite {
|
||||||
|
test("Test for WrongArgumentError") {
|
||||||
|
val res = CollisionShapeFactory
|
||||||
|
.createMeshShape(new TestSpatial)
|
||||||
|
.attempt
|
||||||
|
.runSyncUnsafe()
|
||||||
|
|
||||||
|
assert(
|
||||||
|
res === Left(
|
||||||
|
CollisionShapeFactory.WrongArgumentError(
|
||||||
|
"The spatial must either be a Node or a Geometry!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestSpatial extends Spatial {
|
||||||
|
|
||||||
|
override def collideWith(x$1: Collidable, x$2: CollisionResults): Int = ???
|
||||||
|
|
||||||
|
override def updateModelBound(): Unit = ???
|
||||||
|
|
||||||
|
override def setModelBound(x$1: BoundingVolume): Unit = ???
|
||||||
|
|
||||||
|
override def getVertexCount(): Int = ???
|
||||||
|
|
||||||
|
override def getTriangleCount(): Int = ???
|
||||||
|
|
||||||
|
override def depthFirstTraversal(x$1: SceneGraphVisitor, x$2: DFSMode): Unit =
|
||||||
|
???
|
||||||
|
|
||||||
|
override protected def breadthFirstTraversal(
|
||||||
|
x$1: SceneGraphVisitor,
|
||||||
|
x$2: Queue[Spatial]
|
||||||
|
): Unit = ???
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user