forked from nova/jmonkey-test
many changes
This commit is contained in:
parent
67a2bc4385
commit
fd8b3819ff
@ -66,7 +66,8 @@ lazy val root = (project in file(".")).settings(
|
|||||||
"com.lihaoyi" %% "pprint" % "0.6.0",
|
"com.lihaoyi" %% "pprint" % "0.6.0",
|
||||||
"org.scalatest" %% "scalatest" % "3.2.2" % "test",
|
"org.scalatest" %% "scalatest" % "3.2.2" % "test",
|
||||||
"org.typelevel" %% "cats-mtl" % "1.1.1",
|
"org.typelevel" %% "cats-mtl" % "1.1.1",
|
||||||
"io.estatico" %% "newtype" % "0.4.4"
|
"io.estatico" %% "newtype" % "0.4.4",
|
||||||
|
"io.methvin" %% "directory-watcher-better-files" % "0.14.0"
|
||||||
),
|
),
|
||||||
// Determine OS version of JavaFX binaries
|
// Determine OS version of JavaFX binaries
|
||||||
|
|
||||||
|
@ -14,14 +14,16 @@ import io.odin._
|
|||||||
import io.odin.json.Formatter
|
import io.odin.json.Formatter
|
||||||
import io.odin.syntax._
|
import io.odin.syntax._
|
||||||
import scalafx.scene.control.TextArea
|
import scalafx.scene.control.TextArea
|
||||||
|
import wow.doge.mygame.ActorSystemResource
|
||||||
|
import wow.doge.mygame.executors.ExecutorsModule
|
||||||
|
import wow.doge.mygame.types.AkkaScheduler
|
||||||
import wow.doge.mygame.utils.GenericConsoleStream
|
import wow.doge.mygame.utils.GenericConsoleStream
|
||||||
|
object Main extends BIOApp with ExecutorsModule {
|
||||||
object Main extends BIOApp with MainModule {
|
|
||||||
import java.util.logging.{Logger => JLogger, Level}
|
import java.util.logging.{Logger => JLogger, Level}
|
||||||
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
|
override def scheduler: Scheduler = schedulers.async.value
|
||||||
|
|
||||||
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
|
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
|
||||||
for {
|
for {
|
||||||
@ -34,18 +36,18 @@ object Main extends BIOApp with MainModule {
|
|||||||
"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
|
||||||
// backend <- Resource.make(toIO(HttpClientMonixBackend()))(backend =>
|
// backend <- Resource.make(toIO(HttpClientMonixBackend()))(backend =>
|
||||||
// toIO(backend.close())
|
// toIO(backend.close())
|
||||||
// )
|
// )
|
||||||
actorSystem <- actorSystemResource(logger, schedulers.async)
|
actorSystem <- new ActorSystemResource(logger, schedulers.async).get
|
||||||
_ <- Resource.liftF(
|
_ <- Resource.liftF(
|
||||||
new MainApp(
|
new MainApp(
|
||||||
logger,
|
logger,
|
||||||
jmeScheduler,
|
jmeScheduler,
|
||||||
schedulers,
|
schedulers,
|
||||||
consoleStream
|
consoleStream
|
||||||
)(actorSystem, timeout, actorSystem.scheduler).program
|
)(actorSystem, timeout, AkkaScheduler(actorSystem.scheduler)).program
|
||||||
)
|
)
|
||||||
|
|
||||||
} yield ()
|
} yield ()
|
||||||
|
@ -3,7 +3,6 @@ package wow.doge.mygame
|
|||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
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.effect.Resource
|
import cats.effect.Resource
|
||||||
@ -16,6 +15,8 @@ import com.jme3.material.Material
|
|||||||
import com.jme3.material.MaterialDef
|
import com.jme3.material.MaterialDef
|
||||||
import com.jme3.math.ColorRGBA
|
import com.jme3.math.ColorRGBA
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
|
import com.jme3.math.Quaternion
|
||||||
|
import com.jme3.math.Vector3f
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
import com.jme3.renderer.ViewPort
|
import com.jme3.renderer.ViewPort
|
||||||
import com.jme3.scene.Node
|
import com.jme3.scene.Node
|
||||||
@ -27,19 +28,22 @@ import monix.bio.IO
|
|||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.bio.UIO
|
import monix.bio.UIO
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
|
import monix.execution.exceptions.DummyException
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import scalafx.scene.control.TextArea
|
import scalafx.scene.control.TextArea
|
||||||
import wow.doge.mygame.AppError.TimeoutError
|
import wow.doge.mygame.AppError.TimeoutError
|
||||||
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.GameAppResource
|
import wow.doge.mygame.game.GameAppResource
|
||||||
|
import wow.doge.mygame.game.controls.FollowControl
|
||||||
|
import wow.doge.mygame.game.entities.CharacterStats
|
||||||
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.PlayerActorSupervisor
|
||||||
import wow.doge.mygame.game.entities.PlayerController
|
import wow.doge.mygame.game.entities.PlayerController
|
||||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
||||||
|
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
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
|
||||||
@ -50,50 +54,45 @@ import wow.doge.mygame.subsystems.events.EventBus
|
|||||||
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
|
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule
|
import wow.doge.mygame.subsystems.events.EventsModule
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
|
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.subsystems.events.StatsEvent.DamageEvent
|
import wow.doge.mygame.subsystems.events.StatsEvent.DamageEvent
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent
|
import wow.doge.mygame.subsystems.events.TickEvent
|
||||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
||||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||||
|
import wow.doge.mygame.types._
|
||||||
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.IOUtils
|
import wow.doge.mygame.utils.IOUtils
|
||||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
|
||||||
import com.jme3.math.Quaternion
|
|
||||||
import com.jme3.math.Vector3f
|
|
||||||
import wow.doge.mygame.types._
|
|
||||||
import wow.doge.mygame.game.controls.FollowControl
|
|
||||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
|
||||||
class MainApp(
|
class MainApp(
|
||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
jmeThread: monix.execution.Scheduler,
|
jmeThread: JmeScheduler,
|
||||||
schedulers: Schedulers,
|
schedulers: Schedulers,
|
||||||
consoleStream: GenericConsoleStream[TextArea]
|
consoleStream: GenericConsoleStream[TextArea]
|
||||||
)(implicit
|
)(implicit
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
scheduler: Scheduler
|
scheduler: AkkaScheduler
|
||||||
) {
|
) {
|
||||||
|
implicit val as = scheduler.value
|
||||||
|
|
||||||
val scriptSystemInit =
|
val scriptSystemInit =
|
||||||
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
|
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
|
||||||
|
|
||||||
val eventsModule = new EventsModule(scheduler, spawnProtocol)
|
val eventsModule = new EventsModule(scheduler, spawnProtocol)
|
||||||
|
|
||||||
class TestClass(
|
def eval(
|
||||||
playerEventBus: GameEventBus[PlayerEvent],
|
tickEventBus: GameEventBus[TickEvent],
|
||||||
tickEventBus: GameEventBus[TickEvent]
|
gameApp: GameApp,
|
||||||
)
|
fib: Fiber[Nothing, Unit]
|
||||||
|
) =
|
||||||
def eval(gameApp: GameApp, fib: Fiber[Nothing, Unit]) =
|
|
||||||
for {
|
for {
|
||||||
// g <- UIO.pure(gameApp)
|
// g <- UIO.pure(gameApp)
|
||||||
playerEventBus <- eventsModule.playerEventBus
|
playerEventBus <- eventsModule.playerEventBus
|
||||||
mainEventBus <- eventsModule.mainEventBus
|
mainEventBus <- eventsModule.mainEventBus
|
||||||
tickEventBus <- eventsModule.tickEventBus
|
|
||||||
obs <-
|
obs <-
|
||||||
playerEventBus
|
playerEventBus
|
||||||
.askL[Observable[PlayerMovementEvent]](ObservableSubscription(_))
|
.askL[Observable[PlayerMovementEvent]](ObservableSubscription(_))
|
||||||
@ -116,11 +115,6 @@ class MainApp(
|
|||||||
physicsSpace <- UIO.pure(gameApp.physicsSpace)
|
physicsSpace <- UIO.pure(gameApp.physicsSpace)
|
||||||
_ <- logger.infoU("before")
|
_ <- logger.infoU("before")
|
||||||
// jfxUI <- gameApp.jfxUI
|
// jfxUI <- gameApp.jfxUI
|
||||||
gameAppActor <- gameApp.spawnGameActor(
|
|
||||||
GameAppActor.Props(tickEventBus).behavior,
|
|
||||||
Some("gameAppActor")
|
|
||||||
)
|
|
||||||
_ <- gameAppActor !! GameAppActor.Start
|
|
||||||
consoleTextArea <- UIO(new TextArea {
|
consoleTextArea <- UIO(new TextArea {
|
||||||
text = "hello \n"
|
text = "hello \n"
|
||||||
editable = false
|
editable = false
|
||||||
@ -138,25 +132,25 @@ class MainApp(
|
|||||||
.executeOn(gameApp.scheduler.value)
|
.executeOn(gameApp.scheduler.value)
|
||||||
} yield fib
|
} yield fib
|
||||||
|
|
||||||
def gameInit: Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
|
def gameInit(
|
||||||
|
tickEventBus: GameEventBus[TickEvent]
|
||||||
|
): Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
|
||||||
wire[GameAppResource].resource.evalMap {
|
wire[GameAppResource].resource.evalMap {
|
||||||
case Right(gameApp -> gameAppFib) =>
|
case Right(gameApp -> gameAppFib) =>
|
||||||
eval(gameApp, gameAppFib).attempt
|
eval(tickEventBus, gameApp, gameAppFib).attempt
|
||||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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].hideErrors
|
launchSignal <- Deferred[Task, Launcher.LauncherResult].hideErrors
|
||||||
launcher <- new Launcher.Props(schedulers, launchSignal).create
|
launcher <- new Launcher.Props(schedulers.fx, launchSignal).create
|
||||||
launchResult <- launcher.init.use(_ => launchSignal.get).hideErrors
|
launchResult <-
|
||||||
|
launcher.init
|
||||||
|
.use(_ => launchSignal.get)
|
||||||
|
.hideErrors
|
||||||
|
tickEventBus <-
|
||||||
|
eventsModule.tickEventBus.hideErrorsWith(e => DummyException(e.toString))
|
||||||
_ <-
|
_ <-
|
||||||
/**
|
/**
|
||||||
* User chose to quit
|
* User chose to quit
|
||||||
@ -167,7 +161,7 @@ class MainApp(
|
|||||||
* User chose launch. Wait for game window to close
|
* User chose launch. Wait for game window to close
|
||||||
*/
|
*/
|
||||||
else
|
else
|
||||||
gameInit.use {
|
gameInit(tickEventBus).use {
|
||||||
case Right(fib) => fib.join >> Task.unit
|
case Right(fib) => fib.join >> Task.unit
|
||||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||||
}.hideErrors
|
}.hideErrors
|
||||||
@ -195,8 +189,9 @@ class MainAppDelegate(
|
|||||||
)(implicit
|
)(implicit
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
scheduler: Scheduler
|
scheduler: AkkaScheduler
|
||||||
) {
|
) {
|
||||||
|
implicit val as = scheduler.value
|
||||||
|
|
||||||
def init(
|
def init(
|
||||||
// appScheduler: monix.execution.Scheduler
|
// appScheduler: monix.execution.Scheduler
|
||||||
@ -237,9 +232,12 @@ class MainAppDelegate(
|
|||||||
damageObs
|
damageObs
|
||||||
.doOnNextF(event =>
|
.doOnNextF(event =>
|
||||||
(loggerL.debug(s"Received Damage Event $event") >>
|
(loggerL.debug(s"Received Damage Event $event") >>
|
||||||
IO(
|
(if (event.victimName === "PlayerNode")
|
||||||
playerActor ! PlayerActorSupervisor.TakeDamage(event.amount)
|
// playerActor !! PlayerActorSupervisor.TakeDamage(event.amount)
|
||||||
)).toTask
|
playerActor.askL(
|
||||||
|
PlayerActorSupervisor.TakeDamage2(event.amount, _)
|
||||||
|
)
|
||||||
|
else IO.unit)).toTask
|
||||||
)
|
)
|
||||||
.completedL
|
.completedL
|
||||||
.toIO
|
.toIO
|
||||||
@ -251,10 +249,8 @@ class MainAppDelegate(
|
|||||||
.doOnNextF(_ =>
|
.doOnNextF(_ =>
|
||||||
playerActor
|
playerActor
|
||||||
.askL(PlayerActorSupervisor.GetStatus)
|
.askL(PlayerActorSupervisor.GetStatus)
|
||||||
.flatMap(s =>
|
.flatMap(s => loggerL.debug(s"Player actor status: $s"))
|
||||||
loggerL.debug(s"Player actor status: $s") >> UIO.pure(s)
|
|
||||||
)
|
|
||||||
.void
|
|
||||||
// .flatMap(s =>
|
// .flatMap(s =>
|
||||||
// if (s == Status.Alive)
|
// if (s == Status.Alive)
|
||||||
// playerActor
|
// playerActor
|
||||||
@ -276,19 +272,21 @@ class MainAppDelegate(
|
|||||||
.startAndForget
|
.startAndForget
|
||||||
_ <-
|
_ <-
|
||||||
physicsSpace.collisionObservable
|
physicsSpace.collisionObservable
|
||||||
.filter(event =>
|
// .filter(event =>
|
||||||
(for {
|
// (for {
|
||||||
nodeA <- event.nodeA
|
// nodeA <- event.nodeA
|
||||||
nodeB <- event.nodeB
|
// nodeB <- event.nodeB
|
||||||
} yield nodeA.getName === "PlayerNode" && nodeB.getName === "John" ||
|
// } yield nodeA.getName === "PlayerNode" && nodeB.getName === "John" ||
|
||||||
nodeB.getName === "PlayerNode" && nodeA.getName === "John")
|
// nodeB.getName === "PlayerNode" && nodeA.getName === "John")
|
||||||
.getOrElse(false)
|
// .getOrElse(false)
|
||||||
)
|
// )
|
||||||
.doOnNextF(event =>
|
// .doOnNextF(event =>
|
||||||
loggerL
|
// loggerL
|
||||||
.debug(s"$event ${event.appliedImpulse()}")
|
// .debug(s"$event ${event.appliedImpulse()}")
|
||||||
.toTask
|
// .toTask
|
||||||
)
|
// )
|
||||||
|
.filter(_.nodeA.map(_.getName =!= "main-scene_node").getOrElse(false))
|
||||||
|
.filter(_.nodeB.map(_.getName =!= "main-scene_node").getOrElse(false))
|
||||||
.doOnNextF(event =>
|
.doOnNextF(event =>
|
||||||
(for {
|
(for {
|
||||||
victim <- Coeval(for {
|
victim <- Coeval(for {
|
||||||
@ -299,7 +297,11 @@ class MainAppDelegate(
|
|||||||
victim.foreach { v =>
|
victim.foreach { v =>
|
||||||
pprint.log(s"emitted event ${v.getName}")
|
pprint.log(s"emitted event ${v.getName}")
|
||||||
mainEventBus ! EventBus.Publish(
|
mainEventBus ! EventBus.Publish(
|
||||||
DamageEvent("John", v.getName, 10),
|
DamageEvent(
|
||||||
|
"John",
|
||||||
|
v.getName,
|
||||||
|
CharacterStats.DamageHealth(10)
|
||||||
|
),
|
||||||
"damageHandler"
|
"damageHandler"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -327,6 +329,7 @@ class MainAppDelegate(
|
|||||||
): IO[AppError, PlayerActorSupervisor.Ref] = {
|
): IO[AppError, PlayerActorSupervisor.Ref] = {
|
||||||
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 modelPath = os.rel / "Models" / "Oto" / "Oto.mesh.xml"
|
||||||
val playerPhysicsControl =
|
val playerPhysicsControl =
|
||||||
PlayerController.Defaults.defaultPlayerPhysicsControl
|
PlayerController.Defaults.defaultPlayerPhysicsControl
|
||||||
|
|
||||||
@ -335,6 +338,7 @@ class MainAppDelegate(
|
|||||||
assetManager
|
assetManager
|
||||||
.loadModelAs[Node](modelPath)
|
.loadModelAs[Node](modelPath)
|
||||||
.map(_.withRotate(0, FastMath.PI, 0))
|
.map(_.withRotate(0, FastMath.PI, 0))
|
||||||
|
.tapEval(m => UIO(m.center()))
|
||||||
.mapError(AppError.AssetManagerError)
|
.mapError(AppError.AssetManagerError)
|
||||||
playerNode <- UIO(
|
playerNode <- UIO(
|
||||||
PlayerController.Defaults
|
PlayerController.Defaults
|
||||||
@ -346,9 +350,7 @@ class MainAppDelegate(
|
|||||||
)
|
)
|
||||||
cameraPivotNode <- UIO(
|
cameraPivotNode <- UIO(
|
||||||
new Node(EntityIds.CameraPivot.value)
|
new Node(EntityIds.CameraPivot.value)
|
||||||
.withControl(
|
.withControl(new FollowControl(playerNode))
|
||||||
new FollowControl(playerNode)
|
|
||||||
)
|
|
||||||
.taggedWith[PlayerController.Tags.PlayerCameraPivotNode]
|
.taggedWith[PlayerController.Tags.PlayerCameraPivotNode]
|
||||||
)
|
)
|
||||||
camNode <- UIO(
|
camNode <- UIO(
|
||||||
@ -405,24 +407,24 @@ class MainAppDelegate(
|
|||||||
.toIO
|
.toIO
|
||||||
.hideErrors
|
.hideErrors
|
||||||
.startAndForget
|
.startAndForget
|
||||||
_ <-
|
// _ <-
|
||||||
Observable
|
// Observable
|
||||||
.interval(10.millis)
|
// .interval(10.millis)
|
||||||
.doOnNextF(_ =>
|
// .doOnNextF(_ =>
|
||||||
Coeval {
|
// Coeval {
|
||||||
val location = playerNode.getWorldTranslation()
|
// val location = playerNode.getWorldTranslation()
|
||||||
cameraPivotNode.setLocalTranslation(location)
|
// cameraPivotNode.setLocalTranslation(location)
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
.completedL
|
// .completedL
|
||||||
.toIO
|
// .toIO
|
||||||
.hideErrors
|
// .hideErrors
|
||||||
.startAndForget
|
// .startAndForget
|
||||||
sched <- UIO.pure(schedulers.async)
|
sched <- UIO.pure(schedulers.async)
|
||||||
playerActor <- wire[PlayerController.Props].create
|
playerActor <- wire[PlayerController.Props].create
|
||||||
obs <-
|
obs <-
|
||||||
playerActor
|
playerActor
|
||||||
.askL(PlayerActorSupervisor.GetStatsObservable)
|
.askL(PlayerActorSupervisor.GetStatsObservable2)
|
||||||
.onErrorHandleWith(TimeoutError.from)
|
.onErrorHandleWith(TimeoutError.from)
|
||||||
_ <-
|
_ <-
|
||||||
obs
|
obs
|
||||||
|
@ -5,21 +5,16 @@ import akka.actor.typed.SpawnProtocol
|
|||||||
import cats.effect.Resource
|
import cats.effect.Resource
|
||||||
import io.odin.Logger
|
import io.odin.Logger
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.execution.Scheduler
|
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||||
import wow.doge.mygame.executors.ExecutorsModule
|
|
||||||
|
|
||||||
trait MainModule extends ExecutorsModule {
|
class ActorSystemResource(logger: Logger[Task], scheduler: AsyncScheduler) {
|
||||||
|
def get: Resource[Task, ActorSystem[SpawnProtocol.Command]] =
|
||||||
def actorSystemResource(
|
|
||||||
logger: Logger[Task],
|
|
||||||
scheduler: Scheduler
|
|
||||||
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
|
|
||||||
Resource.make(
|
Resource.make(
|
||||||
logger.info("Creating Actor System") >> Task(
|
logger.info("Creating Actor System") >> Task(
|
||||||
ActorSystem(
|
ActorSystem(
|
||||||
SpawnProtocol(),
|
SpawnProtocol(),
|
||||||
name = "GameActorSystem",
|
name = "GameActorSystem",
|
||||||
BootstrapSetup().withDefaultExecutionContext(scheduler)
|
BootstrapSetup().withDefaultExecutionContext(scheduler.value)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)(sys =>
|
)(sys =>
|
||||||
|
81
src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala
Normal file
81
src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package wow.doge.mygame.actor
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
// import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
|
// import wow.doge.mygame.subsystems.events.Event
|
||||||
|
// import scala.reflect.ClassTag
|
||||||
|
// import akka.actor.typed.LogOptions
|
||||||
|
// import wow.doge.mygame.subsystems.events.EventBus
|
||||||
|
// import scala.concurrent.duration._
|
||||||
|
// import akka.util.Timeout
|
||||||
|
// import akka.actor.typed.SupervisorStrategy
|
||||||
|
|
||||||
|
object GameActorSystem {
|
||||||
|
sealed trait Command
|
||||||
|
case class GetSpawnProtocol(
|
||||||
|
replyTo: ActorRef[ActorRef[SpawnProtocol.Command]]
|
||||||
|
) extends Command
|
||||||
|
|
||||||
|
class Props() {
|
||||||
|
def create =
|
||||||
|
Behaviors.setup[Command] { ctx =>
|
||||||
|
val systemSpawnProtocol = ctx.spawnN(SpawnProtocol())
|
||||||
|
new GameActorSystem(ctx, this, systemSpawnProtocol).receive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class GameActorSystem(
|
||||||
|
ctx: ActorContext[GameActorSystem.Command],
|
||||||
|
props: GameActorSystem.Props,
|
||||||
|
sp: ActorRef[SpawnProtocol.Command]
|
||||||
|
) {
|
||||||
|
import GameActorSystem._
|
||||||
|
def receive =
|
||||||
|
Behaviors.receiveMessage[Command] {
|
||||||
|
case GetSpawnProtocol(replyTo) =>
|
||||||
|
replyTo ! sp
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// object EventBusSupervisor {
|
||||||
|
// sealed trait Command
|
||||||
|
// case class GetMainEventBus(replyTo: ActorRef[GameEventBus[Event]])
|
||||||
|
// extends Command
|
||||||
|
// case class GetEventBus[T](replyTo: ActorRef[GameEventBus[T]])(implicit
|
||||||
|
// classTag: ClassTag[T]
|
||||||
|
// ) extends Command {
|
||||||
|
// def ct = classTag
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class Props(val spawnProtocol: ActorRef[SpawnProtocol.Command]) {
|
||||||
|
// def create =
|
||||||
|
// Behaviors.setup[Command] { ctx =>
|
||||||
|
// new EventBusSupervisor(ctx, this).receive
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// class EventBusSupervisor(
|
||||||
|
// ctx: ActorContext[EventBusSupervisor.Command],
|
||||||
|
// props: EventBusSupervisor.Props
|
||||||
|
// ) {
|
||||||
|
// import EventBusSupervisor._
|
||||||
|
// implicit val timeout = Timeout(1.second)
|
||||||
|
// implicit val sp = props.spawnProtocol
|
||||||
|
// def receive =
|
||||||
|
// Behaviors.receiveMessage[Command] {
|
||||||
|
// case g @ GetEventBus(replyTo) =>
|
||||||
|
// implicit val ct = g.ct
|
||||||
|
// Behaviors
|
||||||
|
// .supervise(EventBus())
|
||||||
|
// .onFailure[Exception](
|
||||||
|
// SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
// )
|
||||||
|
// Behaviors.same
|
||||||
|
// case _ => Behaviors.same
|
||||||
|
// }
|
||||||
|
// }
|
@ -1,31 +1,22 @@
|
|||||||
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
|
||||||
|
import wow.doge.mygame.types.JmeScheduler
|
||||||
|
|
||||||
trait ExecutorsModule {
|
trait ExecutorsModule {
|
||||||
val schedulers = Schedulers()
|
|
||||||
val acquire: UIO[Either[Error, Int]] =
|
|
||||||
IO.pure(1).onErrorHandleWith(_ => IO.raiseError(Error)).attempt
|
|
||||||
// : Resource[IO[Error, Unit], Unit]
|
|
||||||
val res = Resource.make(acquire)(_ => IO.unit)
|
|
||||||
val x: Task[Either[Error, Unit]] = res.use {
|
|
||||||
case Right(value) => Task(Right(println(s"got $value")))
|
|
||||||
case Left(value) => Task(Left(value))
|
|
||||||
}
|
|
||||||
val z = x.onErrorHandleWith(ex => UIO(Right(ex.printStackTrace())))
|
|
||||||
val y: IO[Error, Unit] = z >>
|
|
||||||
x.hideErrors.rethrow
|
|
||||||
|
|
||||||
val jMESchedulerResource = Resource.make(
|
val schedulers = Schedulers.default
|
||||||
|
|
||||||
|
val jmeSchedulerResource = Resource.make(
|
||||||
Task(
|
Task(
|
||||||
Scheduler
|
JmeScheduler(
|
||||||
.singleThread(name = "JME-Application-Thread", daemonic = false)
|
Scheduler
|
||||||
|
.singleThread(name = "JME-Application-Thread", daemonic = false)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)(e => Task(e.shutdown()))
|
)(s => Task(s.value.shutdown()))
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
|
@ -13,6 +13,7 @@ import scala.concurrent.ExecutionContext
|
|||||||
import akka.dispatch.DispatcherPrerequisites
|
import akka.dispatch.DispatcherPrerequisites
|
||||||
import akka.dispatch.ExecutorServiceConfigurator
|
import akka.dispatch.ExecutorServiceConfigurator
|
||||||
import akka.dispatch.ExecutorServiceFactory
|
import akka.dispatch.ExecutorServiceFactory
|
||||||
|
import com.jme3.app.Application
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
@ -42,13 +43,11 @@ object SwingExecutorService extends GUIExecutorService {
|
|||||||
|
|
||||||
object JMEExecutorService extends GUIExecutorService {
|
object JMEExecutorService extends GUIExecutorService {
|
||||||
override def execute(command: Runnable) =
|
override def execute(command: Runnable) =
|
||||||
JMERunner.runner.get.apply(command)
|
JMERunner.runner.enqueue(command)
|
||||||
// new SingleThreadEventExecutor()
|
|
||||||
sys.addShutdownHook(JMEExecutorService.shutdown())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object JMERunner {
|
object JMERunner {
|
||||||
var runner: Option[Runnable => Unit] = None
|
var runner: Application = null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +97,8 @@ class SwingEventThreadExecutorServiceConfigurator(
|
|||||||
object JFXExecutionContexts {
|
object JFXExecutionContexts {
|
||||||
val javaFxExecutionContext: ExecutionContext =
|
val javaFxExecutionContext: ExecutionContext =
|
||||||
ExecutionContext.fromExecutor(new Executor {
|
ExecutionContext.fromExecutor(new Executor {
|
||||||
def execute(command: Runnable): Unit = {
|
def execute(command: Runnable): Unit =
|
||||||
Platform.runLater(command)
|
Platform.runLater(command)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
val fxScheduler =
|
val fxScheduler =
|
||||||
Scheduler(javaFxExecutionContext)
|
Scheduler(javaFxExecutionContext)
|
||||||
|
@ -5,13 +5,9 @@ import monix.execution.Scheduler
|
|||||||
import monix.execution.UncaughtExceptionReporter
|
import monix.execution.UncaughtExceptionReporter
|
||||||
|
|
||||||
final case class Schedulers(
|
final case class Schedulers(
|
||||||
blockingIO: Scheduler = Scheduler
|
blockingIO: Schedulers.IoScheduler,
|
||||||
.io()
|
async: Schedulers.AsyncScheduler,
|
||||||
.withUncaughtExceptionReporter(Schedulers.reporter),
|
fx: Schedulers.FxScheduler
|
||||||
async: Scheduler = Scheduler.global
|
|
||||||
.withUncaughtExceptionReporter(Schedulers.reporter),
|
|
||||||
fx: Scheduler = JFXExecutionContexts.fxScheduler
|
|
||||||
.withUncaughtExceptionReporter(Schedulers.reporter)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
object Schedulers {
|
object Schedulers {
|
||||||
@ -20,4 +16,24 @@ object Schedulers {
|
|||||||
logger.error("Uncaught exception", ex)
|
logger.error("Uncaught exception", ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val default = Schedulers(
|
||||||
|
IoScheduler(
|
||||||
|
Scheduler
|
||||||
|
.io()
|
||||||
|
.withUncaughtExceptionReporter(Schedulers.reporter)
|
||||||
|
),
|
||||||
|
AsyncScheduler(
|
||||||
|
Scheduler.global
|
||||||
|
.withUncaughtExceptionReporter(Schedulers.reporter)
|
||||||
|
),
|
||||||
|
FxScheduler(
|
||||||
|
JFXExecutionContexts.fxScheduler
|
||||||
|
.withUncaughtExceptionReporter(Schedulers.reporter)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
case class AsyncScheduler(value: Scheduler)
|
||||||
|
case class IoScheduler(value: Scheduler)
|
||||||
|
case class FxScheduler(value: Scheduler)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,6 @@ import monix.bio.UIO
|
|||||||
import monix.catnap.ConcurrentChannel
|
import monix.catnap.ConcurrentChannel
|
||||||
import monix.catnap.ConsumerF
|
import monix.catnap.ConsumerF
|
||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.execution.CancelableFuture
|
|
||||||
import monix.execution.CancelablePromise
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
import wow.doge.mygame.AppError
|
import wow.doge.mygame.AppError
|
||||||
import wow.doge.mygame.AppError.TimeoutError
|
import wow.doge.mygame.AppError.TimeoutError
|
||||||
import wow.doge.mygame.Dispatchers
|
import wow.doge.mygame.Dispatchers
|
||||||
@ -35,10 +32,11 @@ import wow.doge.mygame.executors.JMERunner
|
|||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
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.subsystems.events.EventsModule.GameEventBus
|
||||||
import wow.doge.mygame.utils.GenericTimerActor
|
import wow.doge.mygame.subsystems.events.TickEvent
|
||||||
import wow.doge.mygame.utils.wrappers.jme._
|
|
||||||
import wow.doge.mygame.types._
|
import wow.doge.mygame.types._
|
||||||
|
import wow.doge.mygame.utils.AkkaUtils
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
object GameAppTags {
|
object GameAppTags {
|
||||||
sealed trait RootNode
|
sealed trait RootNode
|
||||||
sealed trait GuiNode
|
sealed trait GuiNode
|
||||||
@ -47,9 +45,9 @@ object GameAppTags {
|
|||||||
class GameApp private[game] (
|
class GameApp private[game] (
|
||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
app: SimpleAppExt,
|
app: SimpleAppExt,
|
||||||
gameActor: ActorRef[TestGameActor.Command],
|
gameActor: ActorRef[GameAppActor.Command],
|
||||||
gameSpawnProtocol: ActorRef[SpawnProtocol.Command],
|
gameSpawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
scheduler: akka.actor.typed.Scheduler
|
akkaScheduler: AkkaScheduler
|
||||||
) {
|
) {
|
||||||
def inputManager: UIO[InputManager] = UIO(app.getInputManager())
|
def inputManager: UIO[InputManager] = UIO(app.getInputManager())
|
||||||
val assetManager = new AssetManager(app.getAssetManager())
|
val assetManager = new AssetManager(app.getAssetManager())
|
||||||
@ -75,7 +73,7 @@ class GameApp private[game] (
|
|||||||
)(implicit name: sourcecode.Name) =
|
)(implicit name: sourcecode.Name) =
|
||||||
AkkaUtils.spawnActorL(behavior, actorName, props)(
|
AkkaUtils.spawnActorL(behavior, actorName, props)(
|
||||||
2.seconds,
|
2.seconds,
|
||||||
scheduler,
|
akkaScheduler.value,
|
||||||
gameSpawnProtocol,
|
gameSpawnProtocol,
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
@ -87,19 +85,21 @@ class GameApp private[game] (
|
|||||||
|
|
||||||
class GameAppResource(
|
class GameAppResource(
|
||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
jmeThread: Scheduler,
|
jmeThread: JmeScheduler,
|
||||||
schedulers: Schedulers
|
schedulers: Schedulers,
|
||||||
|
tickEventBus: GameEventBus[TickEvent]
|
||||||
)(implicit
|
)(implicit
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
scheduler: akka.actor.typed.Scheduler,
|
scheduler: AkkaScheduler,
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
) {
|
) {
|
||||||
|
implicit val as = scheduler.value
|
||||||
def resource
|
def resource
|
||||||
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
|
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
|
||||||
Resource.make(
|
Resource.make(
|
||||||
(for {
|
(for {
|
||||||
app <- UIO(new SimpleAppExt(schedulers, new BulletAppState))
|
app <- UIO(new SimpleAppExt(schedulers, new BulletAppState))
|
||||||
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
|
_ <- UIO(JMERunner.runner = app)
|
||||||
_ <- UIO {
|
_ <- UIO {
|
||||||
val settings = new AppSettings(true)
|
val settings = new AppSettings(true)
|
||||||
settings.setVSync(true)
|
settings.setVSync(true)
|
||||||
@ -112,20 +112,21 @@ class GameAppResource(
|
|||||||
app.setSettings(settings)
|
app.setSettings(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
fib <- UIO(app.start).executeOn(jmeThread).start
|
fib <- UIO(app.start).executeOn(jmeThread.value).start
|
||||||
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
|
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
|
||||||
testGameActor <- AkkaUtils.spawnActorL(
|
gameAppActor <- AkkaUtils.spawnActorL(
|
||||||
new TestGameActor.Props().create,
|
new GameAppActor.Props(tickEventBus).behavior,
|
||||||
Some("testGameActor"),
|
Some("testGameActor"),
|
||||||
props = Dispatchers.jmeDispatcher
|
props = Dispatchers.jmeDispatcher
|
||||||
)
|
)
|
||||||
|
_ <- gameAppActor !! GameAppActor.Start
|
||||||
sp <-
|
sp <-
|
||||||
testGameActor
|
gameAppActor
|
||||||
.askL(TestGameActor.GetSpawnProtocol)
|
.askL(GameAppActor.GetSpawnProtocol)
|
||||||
.onErrorHandleWith(TimeoutError.from)
|
.onErrorHandleWith(TimeoutError.from)
|
||||||
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp, scheduler))
|
gameApp <- UIO(new GameApp(logger, app, gameAppActor, sp, scheduler))
|
||||||
_ <- UIO {
|
_ <- UIO {
|
||||||
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
|
val fut = () => gameAppActor.ask(GameAppActor.Stop).flatten
|
||||||
app.cancelToken = Some(fut)
|
app.cancelToken = Some(fut)
|
||||||
}
|
}
|
||||||
} yield (gameApp, fib)).attempt
|
} yield (gameApp, fib)).attempt
|
||||||
@ -137,58 +138,6 @@ class GameAppResource(
|
|||||||
|
|
||||||
object GameApp {}
|
object GameApp {}
|
||||||
|
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
|
||||||
import akka.actor.typed.scaladsl.ActorContext
|
|
||||||
|
|
||||||
object TestGameActor {
|
|
||||||
sealed trait Command
|
|
||||||
case object Ping extends Command
|
|
||||||
case class Stop(stopSignal: ActorRef[CancelableFuture[Unit]]) extends Command
|
|
||||||
case class GetSpawnProtocol(
|
|
||||||
replyTo: ActorRef[ActorRef[SpawnProtocol.Command]]
|
|
||||||
) extends Command
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
class Props() {
|
|
||||||
def create =
|
|
||||||
Behaviors.setup[Command] { ctx =>
|
|
||||||
ctx.spawn(
|
|
||||||
GenericTimerActor
|
|
||||||
.Props(ctx.self, Ping, 1000.millis)
|
|
||||||
.behavior,
|
|
||||||
"pingTimer"
|
|
||||||
) ! GenericTimerActor.Start
|
|
||||||
new TestGameActor(ctx, this).receive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class TestGameActor(
|
|
||||||
ctx: ActorContext[TestGameActor.Command],
|
|
||||||
props: TestGameActor.Props
|
|
||||||
) {
|
|
||||||
import TestGameActor._
|
|
||||||
val stopPromise = CancelablePromise[Unit]()
|
|
||||||
def receive =
|
|
||||||
Behaviors
|
|
||||||
.receiveMessage[Command] {
|
|
||||||
case Stop(replyTo) =>
|
|
||||||
ctx.log.infoP("stopping")
|
|
||||||
replyTo ! stopPromise.future
|
|
||||||
Behaviors.stopped
|
|
||||||
case Ping =>
|
|
||||||
ctx.log.debugP("ping")
|
|
||||||
Behaviors.same
|
|
||||||
case GetSpawnProtocol(replyTo) =>
|
|
||||||
val sp = ctx.spawn(SpawnProtocol(), "gameSpawnProtocol")
|
|
||||||
replyTo ! sp
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
.receiveSignal {
|
|
||||||
case (_, akka.actor.typed.PostStop) =>
|
|
||||||
stopPromise.success(())
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Ops {
|
object Ops {
|
||||||
final class AddToNode[T <: Node](private val node: T) extends AnyVal {
|
final class AddToNode[T <: Node](private val node: T) extends AnyVal {
|
||||||
|
|
||||||
|
@ -2,14 +2,18 @@ package wow.doge.mygame.game
|
|||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.PostStop
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
import akka.actor.typed.SupervisorStrategy
|
import akka.actor.typed.SupervisorStrategy
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import monix.execution.CancelableFuture
|
||||||
|
import monix.execution.CancelablePromise
|
||||||
import wow.doge.mygame.game.TickGenerator.Send
|
import wow.doge.mygame.game.TickGenerator.Send
|
||||||
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
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent
|
import wow.doge.mygame.subsystems.events.TickEvent
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent.PhysicsTick
|
|
||||||
import wow.doge.mygame.utils.GenericTimerActor
|
import wow.doge.mygame.utils.GenericTimerActor
|
||||||
|
|
||||||
object GameAppActor {
|
object GameAppActor {
|
||||||
@ -17,7 +21,11 @@ object GameAppActor {
|
|||||||
sealed trait Command
|
sealed trait Command
|
||||||
case object Start extends Command
|
case object Start extends Command
|
||||||
case object Pause extends Command
|
case object Pause extends Command
|
||||||
case object Stop extends 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
|
||||||
|
|
||||||
case class Props(tickEventBus: GameEventBus[TickEvent]) {
|
case class Props(tickEventBus: GameEventBus[TickEvent]) {
|
||||||
def behavior =
|
def behavior =
|
||||||
@ -38,19 +46,43 @@ object GameAppActor {
|
|||||||
.behavior
|
.behavior
|
||||||
)
|
)
|
||||||
|
|
||||||
Behaviors.receiveMessage {
|
val sp = ctx.spawn(SpawnProtocol(), "gameSpawnProtocol")
|
||||||
case Start =>
|
|
||||||
tickGeneratorTimer ! GenericTimerActor.Start
|
|
||||||
Behaviors.same
|
|
||||||
case Pause =>
|
|
||||||
tickGeneratorTimer ! GenericTimerActor.Stop
|
|
||||||
Behaviors.same
|
|
||||||
case Stop =>
|
|
||||||
ctx.log.info("Received stop")
|
|
||||||
tickGeneratorTimer ! GenericTimerActor.Stop
|
|
||||||
Behaviors.stopped
|
|
||||||
|
|
||||||
}
|
ctx.spawn(
|
||||||
|
GenericTimerActor
|
||||||
|
.Props(ctx.self, Ping, 1000.millis)
|
||||||
|
.behavior,
|
||||||
|
"pingTimer"
|
||||||
|
) ! GenericTimerActor.Start
|
||||||
|
|
||||||
|
val stopPromise = CancelablePromise[Unit]()
|
||||||
|
|
||||||
|
Behaviors
|
||||||
|
.receiveMessage[Command] {
|
||||||
|
case Start =>
|
||||||
|
tickGeneratorTimer ! GenericTimerActor.Start
|
||||||
|
Behaviors.same
|
||||||
|
case Pause =>
|
||||||
|
tickGeneratorTimer ! GenericTimerActor.Stop
|
||||||
|
Behaviors.same
|
||||||
|
case Stop(replyTo) =>
|
||||||
|
ctx.log.infoP("Received stop")
|
||||||
|
tickGeneratorTimer ! GenericTimerActor.Stop
|
||||||
|
replyTo ! stopPromise.future
|
||||||
|
Behaviors.stopped
|
||||||
|
case Ping =>
|
||||||
|
ctx.log.debugP("ping")
|
||||||
|
Behaviors.same
|
||||||
|
case GetSpawnProtocol(replyTo) =>
|
||||||
|
replyTo ! sp
|
||||||
|
Behaviors.same
|
||||||
|
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
stopPromise.success(())
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val renderTickGeneratorBehavior =
|
val renderTickGeneratorBehavior =
|
||||||
@ -69,92 +101,3 @@ object TickGenerator {
|
|||||||
sealed trait Command
|
sealed trait Command
|
||||||
case object Send extends Command
|
case object Send extends Command
|
||||||
}
|
}
|
||||||
object SubscribingActor {
|
|
||||||
def apply() =
|
|
||||||
Behaviors.receive[PhysicsTick.type] { (ctx, msg) =>
|
|
||||||
ctx.log.debugP(s"received event $msg")
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
}
|
|
||||||
object Methods {
|
|
||||||
|
|
||||||
def old() = {
|
|
||||||
// val movementActor =
|
|
||||||
// ctx.spawn(
|
|
||||||
// MovementActor(MovementActor.Props(app, geom)),
|
|
||||||
// "movementActor"
|
|
||||||
// // DispatcherSelector.fromConfig("jme-dispatcher")
|
|
||||||
// )
|
|
||||||
|
|
||||||
// val movementActorTimer = ctx.spawn(
|
|
||||||
// MovementActorTimer(movementActor),
|
|
||||||
// "movementActorTimer"
|
|
||||||
// )
|
|
||||||
}
|
|
||||||
|
|
||||||
def old2() = {
|
|
||||||
// ctx.log.info("here")
|
|
||||||
|
|
||||||
// {
|
|
||||||
// implicit val s = schedulers.async
|
|
||||||
// Task
|
|
||||||
// .parZip2(
|
|
||||||
// loggerL.info("Test").executeOn(app.scheduler),
|
|
||||||
// app
|
|
||||||
// .enqueueL(() => loggerL.info("here 2").executeOn(app.scheduler))
|
|
||||||
// .flatten
|
|
||||||
// )
|
|
||||||
// .runToFuture
|
|
||||||
// }
|
|
||||||
|
|
||||||
// app
|
|
||||||
// .getRootNode()
|
|
||||||
// .depthFirst(s =>
|
|
||||||
// // s match {
|
|
||||||
// // case node: Node =>
|
|
||||||
// // println("node" + s.getName() + " children " + node.getChildren())
|
|
||||||
// // case g: Geometry => println(s.getName())
|
|
||||||
// // }
|
|
||||||
// println(s.getName())
|
|
||||||
// )
|
|
||||||
|
|
||||||
// println("----------------")
|
|
||||||
|
|
||||||
// {
|
|
||||||
// app
|
|
||||||
// .getRootNode()
|
|
||||||
// .observableDepthFirst()
|
|
||||||
// .map(s => s.getName())
|
|
||||||
// // .takeWhileInclusive(_.getName() != "level")
|
|
||||||
// .onErrorHandle(e => e.getMessage())
|
|
||||||
// .foreach(println)(schedulers.async)
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// println("----------------")
|
|
||||||
|
|
||||||
// {
|
|
||||||
// app
|
|
||||||
// .getRootNode()
|
|
||||||
// .observableBreadthFirst()
|
|
||||||
// .map(s => s.getName())
|
|
||||||
// // .takeWhileInclusive(_.getName() != "level")
|
|
||||||
// .onErrorHandle(e => e.getMessage())
|
|
||||||
// .foreach(println)(schedulers.async)
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// app.start()
|
|
||||||
// Behaviors.same
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// new PlayerMovementState(
|
|
||||||
// // movementActor,
|
|
||||||
// // movementActorTimer,
|
|
||||||
// imMovementActor,
|
|
||||||
// // geom,
|
|
||||||
// // camNode,
|
|
||||||
// playerNode
|
|
||||||
// // ctx.self
|
|
||||||
// )
|
|
||||||
|
@ -1,19 +1,31 @@
|
|||||||
package wow.doge.mygame.game.controls
|
package wow.doge.mygame.game.controls
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
import com.jme3.math.FastMath
|
||||||
import com.jme3.math.Quaternion
|
import com.jme3.math.Quaternion
|
||||||
import com.jme3.scene.Node
|
import com.jme3.math.Vector3f
|
||||||
import com.jme3.scene.control.AbstractControl
|
|
||||||
import com.jme3.renderer.RenderManager
|
import com.jme3.renderer.RenderManager
|
||||||
import com.jme3.renderer.ViewPort
|
import com.jme3.renderer.ViewPort
|
||||||
import monix.reactive.Observable
|
import com.jme3.scene.Node
|
||||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
|
||||||
import com.jme3.scene.Spatial
|
import com.jme3.scene.Spatial
|
||||||
import monix.{eval => me}
|
import com.jme3.scene.control.AbstractControl
|
||||||
import com.jme3.math.FastMath
|
import monix.execution.Ack
|
||||||
import com.jme3.math.Vector3f
|
|
||||||
import monix.execution.Cancelable
|
import monix.execution.Cancelable
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.Observer
|
||||||
|
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A very low level (and error prone) camera movement control implementation.
|
||||||
|
* Not used currently
|
||||||
|
*
|
||||||
|
* @param rotationBuf
|
||||||
|
* @param obs
|
||||||
|
* @param rotateFn
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
class CameraMovementControl(
|
class CameraMovementControl(
|
||||||
rotationBuf: Quaternion,
|
rotationBuf: Quaternion,
|
||||||
obs: Observable[PlayerCameraInput],
|
obs: Observable[PlayerCameraInput],
|
||||||
@ -23,8 +35,21 @@ class CameraMovementControl(
|
|||||||
private var _event: PlayerCameraInput = null
|
private var _event: PlayerCameraInput = null
|
||||||
private var _subscriptionToken: Cancelable = null
|
private var _subscriptionToken: Cancelable = null
|
||||||
|
|
||||||
|
private val sink = new Observer[PlayerCameraInput] {
|
||||||
|
|
||||||
|
override def onNext(event: PlayerCameraInput): Future[Ack] = {
|
||||||
|
_event = event
|
||||||
|
Ack.Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
override def onError(ex: Throwable): Unit = {}
|
||||||
|
|
||||||
|
override def onComplete(): Unit = {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override def controlUpdate(tpf: Float): Unit =
|
override def controlUpdate(tpf: Float): Unit =
|
||||||
if (_event != null)
|
if (_event != null) {
|
||||||
_event match {
|
_event match {
|
||||||
case PlayerCameraInput.CameraRotateLeft =>
|
case PlayerCameraInput.CameraRotateLeft =>
|
||||||
val rot = rotationBuf
|
val rot = rotationBuf
|
||||||
@ -43,6 +68,8 @@ class CameraMovementControl(
|
|||||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||||
rotateFn(rot)
|
rotateFn(rot)
|
||||||
}
|
}
|
||||||
|
_event = null
|
||||||
|
}
|
||||||
|
|
||||||
override def controlRender(
|
override def controlRender(
|
||||||
x$1: RenderManager,
|
x$1: RenderManager,
|
||||||
@ -51,8 +78,7 @@ class CameraMovementControl(
|
|||||||
override def setSpatial(spatial: Spatial): Unit = {
|
override def setSpatial(spatial: Spatial): Unit = {
|
||||||
super.setSpatial(spatial)
|
super.setSpatial(spatial)
|
||||||
if (this.spatial != null)
|
if (this.spatial != null)
|
||||||
_subscriptionToken =
|
_subscriptionToken = obs.subscribe(sink)
|
||||||
obs.doOnNext(event => me.Task { _event = event }).subscribe()
|
|
||||||
else {
|
else {
|
||||||
_subscriptionToken.cancel()
|
_subscriptionToken.cancel()
|
||||||
_subscriptionToken = null
|
_subscriptionToken = null
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package wow.doge.mygame.game.entities
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import io.estatico.newtype.macros.newtype
|
||||||
|
import wow.doge.mygame.game.entities.CharacterStats.HealHealth
|
||||||
|
|
||||||
|
case class CharacterStats(hp: CharacterStats.Health, stamina: Int)
|
||||||
|
object CharacterStats {
|
||||||
|
@newtype case class HealHealth(toInt: Int)
|
||||||
|
@newtype case class DamageHealth(toInt: Int)
|
||||||
|
@newtype case class Health(toInt: Int)
|
||||||
|
object Health {
|
||||||
|
implicit class HealthOps(private val h: Health) extends AnyVal {
|
||||||
|
// def +(v: Int): Health = Health(h.toInt + v)
|
||||||
|
// def -(v: Int): Health = Health(h.toInt - v)
|
||||||
|
// def *(v: Int): Health = Health(h.toInt * v)
|
||||||
|
// def /(v: Int): Health = Health(h.toInt / v)
|
||||||
|
def :+(v: HealHealth): Health = Health(h.toInt + v.toInt)
|
||||||
|
def -(v: DamageHealth): Health = Health(h.toInt - v.toInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@newtype case class HealStamina(toInt: Int)
|
||||||
|
@newtype case class DamageStamina(toInt: Int)
|
||||||
|
@newtype case class Stamina(toInt: Int)
|
||||||
|
object Stamina {
|
||||||
|
implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
||||||
|
// def +(v: Int): Stamina = Stamina(h.toInt + v)
|
||||||
|
// def -(v: Int): Stamina = Stamina(h.toInt - v)
|
||||||
|
// def *(v: Int): Stamina = Stamina(h.toInt * v)
|
||||||
|
// def /(v: Int): Stamina = Stamina(h.toInt / v)
|
||||||
|
def :+(v: HealStamina): Stamina = Stamina(h.toInt + v.toInt)
|
||||||
|
def -(v: DamageStamina): Stamina = Stamina(h.toInt - v.toInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// object Stamina {
|
||||||
|
// implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
||||||
|
// def +(v: Health): Stamina = Stamina(h.toInt + v.toInt)
|
||||||
|
// def -(v: Health): Stamina = Stamina(h.toInt - v.toInt)
|
||||||
|
// def *(v: Health): Stamina = Stamina(h.toInt * v.toInt)
|
||||||
|
// def /(v: Health): Stamina = Stamina(h.toInt / v.toInt)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// object Damage {
|
||||||
|
// implicit class DamageOps(private val h: Damage) extends AnyVal {
|
||||||
|
// def +(v: Health): Damage = Damage(h.toInt + v.toInt)
|
||||||
|
// def -(v: Health): Damage = Damage(h.toInt - v.toInt)
|
||||||
|
// def *(v: Health): Damage = Damage(h.toInt * v.toInt)
|
||||||
|
// def /(v: Health): Damage = Damage(h.toInt / v.toInt)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object StatsActor {
|
||||||
|
|
||||||
|
sealed trait Command
|
||||||
|
// case class TakeDamage(value: Int) extends Command
|
||||||
|
case class TakeDamageResult(
|
||||||
|
value: CharacterStats.DamageHealth,
|
||||||
|
replyTo: ActorRef[(Boolean, CharacterStats)]
|
||||||
|
) extends Command
|
||||||
|
case class HealResult(value: HealHealth) extends Command
|
||||||
|
case class CurrentStats(replyTo: ActorRef[CharacterStats]) extends Command
|
||||||
|
|
||||||
|
class Props(
|
||||||
|
startingHealth: CharacterStats.Health,
|
||||||
|
startingStamina: CharacterStats.Stamina
|
||||||
|
) {
|
||||||
|
def behavior =
|
||||||
|
Behaviors.setup[Command] { ctx =>
|
||||||
|
new StatsActor(ctx, this)
|
||||||
|
.receive(
|
||||||
|
State(CharacterStats(startingHealth, startingStamina.toInt))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class State(stats: CharacterStats)
|
||||||
|
}
|
||||||
|
class StatsActor(
|
||||||
|
ctx: ActorContext[StatsActor.Command],
|
||||||
|
props: StatsActor.Props
|
||||||
|
) {
|
||||||
|
import StatsActor._
|
||||||
|
import CharacterStats._
|
||||||
|
import com.softwaremill.quicklens._
|
||||||
|
def receive(state: State): Behavior[Command] =
|
||||||
|
Behaviors.receiveMessage[Command] {
|
||||||
|
// Todo add min max values
|
||||||
|
// case TakeDamage(value) =>
|
||||||
|
// val nextState =
|
||||||
|
// if (state.stats.hp - value <= 0)
|
||||||
|
// state.modify(_.stats.hp).setTo(0)
|
||||||
|
// else
|
||||||
|
// state.modify(_.stats.hp).using(_ - value)
|
||||||
|
// receive(nextState)
|
||||||
|
case TakeDamageResult(value, replyTo) =>
|
||||||
|
val nextState = if ((state.stats.hp - value).toInt <= 0) {
|
||||||
|
replyTo ! true -> state.stats
|
||||||
|
state.modify(_.stats.hp).setTo(Health(0))
|
||||||
|
} else {
|
||||||
|
replyTo ! false -> state.stats
|
||||||
|
state.modify(_.stats.hp).using(_ - value)
|
||||||
|
}
|
||||||
|
receive(nextState)
|
||||||
|
case HealResult(value) =>
|
||||||
|
receive(state.modify(_.stats.hp).using(_ :+ value))
|
||||||
|
case CurrentStats(replyTo) =>
|
||||||
|
replyTo ! state.stats
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
import scala.util.Failure
|
||||||
|
import scala.util.Success
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
@ -9,9 +11,15 @@ import akka.actor.typed.PostStop
|
|||||||
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.util.Timeout
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.OverflowStrategy
|
||||||
|
import monix.reactive.subjects.ConcurrentSubject
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
import wow.doge.mygame.Dispatchers
|
import wow.doge.mygame.Dispatchers
|
||||||
|
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||||
|
import wow.doge.mygame.game.entities.StatsActor
|
||||||
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,13 +27,8 @@ 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 scala.util.Success
|
import monix.eval.Coeval
|
||||||
import scala.util.Failure
|
import monix.execution.AsyncQueue
|
||||||
import akka.util.Timeout
|
|
||||||
import monix.reactive.Observable
|
|
||||||
import monix.reactive.subjects.ConcurrentSubject
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
import monix.reactive.OverflowStrategy
|
|
||||||
object PlayerActorSupervisor {
|
object PlayerActorSupervisor {
|
||||||
|
|
||||||
type Ref = ActorRef[PlayerActorSupervisor.Command]
|
type Ref = ActorRef[PlayerActorSupervisor.Command]
|
||||||
@ -37,23 +40,33 @@ object PlayerActorSupervisor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
case class TakeDamage(value: Int) extends Command
|
case class TakeDamage(value: CharacterStats.DamageHealth) extends Command
|
||||||
case class Heal(value: Int) extends Command
|
case class TakeDamage2(
|
||||||
case class CurrentStats(replyTo: ActorRef[StatsActor.State]) extends Command
|
value: CharacterStats.DamageHealth,
|
||||||
|
replyTo: ActorRef[Unit]
|
||||||
|
) extends Command
|
||||||
|
case class Heal(value: CharacterStats.HealHealth) extends Command
|
||||||
|
case class CurrentStats(replyTo: ActorRef[CharacterStats]) extends Command
|
||||||
case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
||||||
case class GetStatsObservable(replyTo: ActorRef[Observable[StatsActor.State]])
|
case class GetStatsObservable(replyTo: ActorRef[Observable[CharacterStats]])
|
||||||
|
extends Command
|
||||||
|
case class GetStatsObservable2(replyTo: ActorRef[Observable[CharacterStats]])
|
||||||
extends Command
|
extends Command
|
||||||
|
|
||||||
private case object Die extends Command
|
private case object Die extends Command
|
||||||
private case class DamageResponse(response: (Boolean, StatsActor.State))
|
private case class DamageResponse(response: (Boolean, CharacterStats))
|
||||||
extends Command
|
extends Command
|
||||||
|
private case class DamageResponse2(
|
||||||
|
response: (Boolean, CharacterStats),
|
||||||
|
replyTo: ActorRef[Unit]
|
||||||
|
) extends Command
|
||||||
// private case class InternalTakeDamage(old: Int, value: Int) extends Command
|
// private case class InternalTakeDamage(old: Int, value: Int) extends Command
|
||||||
private case class LogError(ex: Throwable) extends Command
|
private case class LogError(ex: Throwable) extends Command
|
||||||
class Props(
|
class Props(
|
||||||
val playerEventBus: GameEventBus[PlayerEvent],
|
val playerEventBus: GameEventBus[PlayerEvent],
|
||||||
val tickEventBus: GameEventBus[TickEvent],
|
val tickEventBus: GameEventBus[TickEvent],
|
||||||
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||||
val scheduler: Scheduler
|
val scheduler: AsyncScheduler
|
||||||
) {
|
) {
|
||||||
def behavior =
|
def behavior =
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
@ -78,7 +91,12 @@ object PlayerActorSupervisor {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val playerStatsActor =
|
val playerStatsActor =
|
||||||
ctx.spawnN(new StatsActor.Props(100, 100).behavior)
|
ctx.spawnN(
|
||||||
|
new StatsActor.Props(
|
||||||
|
CharacterStats.Health(100),
|
||||||
|
CharacterStats.Stamina(100)
|
||||||
|
).behavior
|
||||||
|
)
|
||||||
|
|
||||||
val playerMovementEl = ctx.spawnN(
|
val playerMovementEl = ctx.spawnN(
|
||||||
Behaviors
|
Behaviors
|
||||||
@ -115,7 +133,13 @@ object PlayerActorSupervisor {
|
|||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
Children(playerMovementActor, playerStatsActor),
|
Children(playerMovementActor, playerStatsActor),
|
||||||
ConcurrentSubject.publish(OverflowStrategy.DropOld(50))(scheduler)
|
ConcurrentSubject.publish(
|
||||||
|
OverflowStrategy.DropOldAndSignal(
|
||||||
|
50,
|
||||||
|
dropped => Coeval.pure(None)
|
||||||
|
)
|
||||||
|
)(scheduler.value),
|
||||||
|
AsyncQueue.bounded(10)(scheduler.value)
|
||||||
).aliveState
|
).aliveState
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -131,7 +155,8 @@ class PlayerActorSupervisor(
|
|||||||
ctx: ActorContext[PlayerActorSupervisor.Command],
|
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||||
props: PlayerActorSupervisor.Props,
|
props: PlayerActorSupervisor.Props,
|
||||||
children: PlayerActorSupervisor.Children,
|
children: PlayerActorSupervisor.Children,
|
||||||
statsSubject: ConcurrentSubject[StatsActor.State, StatsActor.State]
|
statsSubject: ConcurrentSubject[CharacterStats, CharacterStats],
|
||||||
|
statsQueue: AsyncQueue[CharacterStats]
|
||||||
) {
|
) {
|
||||||
import PlayerActorSupervisor._
|
import PlayerActorSupervisor._
|
||||||
implicit val timeout = Timeout(1.second)
|
implicit val timeout = Timeout(1.second)
|
||||||
@ -149,12 +174,23 @@ class PlayerActorSupervisor(
|
|||||||
case Failure(ex) => LogError(ex)
|
case Failure(ex) => LogError(ex)
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
case TakeDamage2(value, replyTo) =>
|
||||||
|
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||||
|
// ctx.ask(children.statsActor, StatsActor.CurrentStats(_)) {
|
||||||
|
// case Success(status) => InternalTakeDamage(status.hp, value)
|
||||||
|
// case Failure(ex) => LogError(ex)
|
||||||
|
// }
|
||||||
|
ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) {
|
||||||
|
case Success(response) => DamageResponse2(response, replyTo)
|
||||||
|
case Failure(ex) => LogError(ex)
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
case CurrentStats(replyTo) =>
|
case CurrentStats(replyTo) =>
|
||||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case Heal(value) =>
|
case Heal(value) =>
|
||||||
children.statsActor ! StatsActor.Heal(value)
|
children.statsActor ! StatsActor.HealResult(value)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case GetStatus(replyTo) =>
|
case GetStatus(replyTo) =>
|
||||||
replyTo ! Status.Alive
|
replyTo ! Status.Alive
|
||||||
@ -169,6 +205,12 @@ class PlayerActorSupervisor(
|
|||||||
case GetStatsObservable(replyTo) =>
|
case GetStatsObservable(replyTo) =>
|
||||||
replyTo ! statsSubject
|
replyTo ! statsSubject
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
case GetStatsObservable2(replyTo) =>
|
||||||
|
import monix.{eval => me}
|
||||||
|
replyTo ! Observable.repeatEvalF(
|
||||||
|
me.Task.deferFuture(statsQueue.poll())
|
||||||
|
)
|
||||||
|
Behaviors.same
|
||||||
case DamageResponse(response) =>
|
case DamageResponse(response) =>
|
||||||
response match {
|
response match {
|
||||||
case (dead, state) =>
|
case (dead, state) =>
|
||||||
@ -176,6 +218,15 @@ class PlayerActorSupervisor(
|
|||||||
statsSubject.onNext(state)
|
statsSubject.onNext(state)
|
||||||
}
|
}
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
case DamageResponse2(response, replyTo) =>
|
||||||
|
response match {
|
||||||
|
case (dead, stats) =>
|
||||||
|
if (dead) ctx.self ! Die
|
||||||
|
statsQueue
|
||||||
|
.offer(stats)
|
||||||
|
.foreach(_ => replyTo ! ())(props.scheduler.value)
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
case Die => deadState
|
case Die => deadState
|
||||||
case LogError(ex) =>
|
case LogError(ex) =>
|
||||||
ctx.log.error(ex.getMessage)
|
ctx.log.error(ex.getMessage)
|
||||||
@ -215,54 +266,3 @@ class PlayerActorSupervisor(
|
|||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object StatsActor {
|
|
||||||
|
|
||||||
sealed trait Command
|
|
||||||
case class TakeDamage(value: Int) extends Command
|
|
||||||
case class TakeDamageResult(value: Int, replyTo: ActorRef[(Boolean, State)])
|
|
||||||
extends Command
|
|
||||||
case class Heal(value: Int) extends Command
|
|
||||||
case class CurrentStats(replyTo: ActorRef[State]) extends Command
|
|
||||||
|
|
||||||
class Props(startingHealth: Int, startingStamina: Int) {
|
|
||||||
def behavior =
|
|
||||||
Behaviors.setup[Command] { ctx =>
|
|
||||||
new StatsActor(ctx, this)
|
|
||||||
.receive(State(startingHealth, startingStamina))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class State(hp: Int, stamina: Int)
|
|
||||||
}
|
|
||||||
class StatsActor(
|
|
||||||
ctx: ActorContext[StatsActor.Command],
|
|
||||||
props: StatsActor.Props
|
|
||||||
) {
|
|
||||||
import StatsActor._
|
|
||||||
import com.softwaremill.quicklens._
|
|
||||||
def receive(state: State): Behavior[Command] =
|
|
||||||
Behaviors.receiveMessage[Command] {
|
|
||||||
// Todo add min max values
|
|
||||||
case TakeDamage(value) =>
|
|
||||||
val nextState =
|
|
||||||
if (state.hp - value <= 0)
|
|
||||||
state.modify(_.hp).setTo(0)
|
|
||||||
else
|
|
||||||
state.modify(_.hp).using(_ - value)
|
|
||||||
receive(nextState)
|
|
||||||
case TakeDamageResult(value, replyTo) =>
|
|
||||||
val nextState = if (state.hp - value <= 0) {
|
|
||||||
replyTo ! true -> state
|
|
||||||
state
|
|
||||||
} else {
|
|
||||||
replyTo ! false -> state
|
|
||||||
state.modify(_.hp).using(_ - value)
|
|
||||||
}
|
|
||||||
receive(nextState)
|
|
||||||
case Heal(value) => receive(state.modify(_.hp).using(_ + value))
|
|
||||||
case CurrentStats(replyTo) =>
|
|
||||||
replyTo ! state
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -14,6 +14,7 @@ import io.odin.Logger
|
|||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import wow.doge.mygame.AppError
|
import wow.doge.mygame.AppError
|
||||||
|
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||||
import wow.doge.mygame.game.GameApp
|
import wow.doge.mygame.game.GameApp
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.math.ImVector3f
|
import wow.doge.mygame.math.ImVector3f
|
||||||
@ -21,8 +22,8 @@ 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.wrappers.jme._
|
|
||||||
import wow.doge.mygame.types._
|
import wow.doge.mygame.types._
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
|
|
||||||
object PlayerController {
|
object PlayerController {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
@ -49,7 +50,7 @@ object PlayerController {
|
|||||||
playerEventBus: GameEventBus[PlayerEvent],
|
playerEventBus: GameEventBus[PlayerEvent],
|
||||||
playerPhysicsControl: BetterCharacterControl,
|
playerPhysicsControl: BetterCharacterControl,
|
||||||
// appScheduler: monix.execution.Scheduler,
|
// appScheduler: monix.execution.Scheduler,
|
||||||
scheduler: monix.execution.Scheduler,
|
scheduler: AsyncScheduler,
|
||||||
playerNode: PlayerNode,
|
playerNode: PlayerNode,
|
||||||
cameraNode: PlayerCameraNode,
|
cameraNode: PlayerCameraNode,
|
||||||
cameraPivotNode: PlayerCameraPivotNode,
|
cameraPivotNode: PlayerCameraPivotNode,
|
||||||
|
@ -46,6 +46,7 @@ object PlayerMovementEventListener {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//not used
|
||||||
object PlayerCameraEventListener {
|
object PlayerCameraEventListener {
|
||||||
import PlayerCameraEvent._
|
import PlayerCameraEvent._
|
||||||
def apply(playerCameraActor: ActorRef[PlayerCameraActor.Command]) =
|
def apply(playerCameraActor: ActorRef[PlayerCameraActor.Command]) =
|
||||||
|
@ -10,7 +10,6 @@ import com.jme3.scene.Geometry
|
|||||||
import com.softwaremill.quicklens._
|
import com.softwaremill.quicklens._
|
||||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.math.ImVector3f
|
|
||||||
|
|
||||||
final case class CardinalDirection(
|
final case class CardinalDirection(
|
||||||
left: Boolean = false,
|
left: Boolean = false,
|
||||||
|
@ -756,7 +756,7 @@ package object implicits {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit final class Vector3fExt(private val v: Vector3f) extends AnyVal {
|
implicit final class Vector3fOps(private val v: Vector3f) extends AnyVal {
|
||||||
//TODO add more operations
|
//TODO add more operations
|
||||||
def +=(that: Vector3f) = v.addLocal(that)
|
def +=(that: Vector3f) = v.addLocal(that)
|
||||||
def +=(f: Float) = v.addLocal(f, f, f)
|
def +=(f: Float) = v.addLocal(f, f, f)
|
||||||
@ -777,7 +777,7 @@ package object implicits {
|
|||||||
// def transformImmutable(f: Vector3f => Vector3f) = f(v).immutable
|
// def transformImmutable(f: Vector3f => Vector3f) = f(v).immutable
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit final class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
|
implicit final class ImVector3fOps(private val v: ImVector3f) extends AnyVal {
|
||||||
def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
|
def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
|
||||||
def +(f: Float): ImVector3f = v.copy(v.x + f, v.y + f, v.z + f)
|
def +(f: Float): ImVector3f = v.copy(v.x + f, v.y + f, v.z + f)
|
||||||
def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
|
def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
|
||||||
@ -867,7 +867,7 @@ package object implicits {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class OdinLoggerExt(private val logger: io.odin.Logger[Task])
|
implicit final class OdinLoggerExt(private val logger: io.odin.Logger[Task])
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def debugU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
def debugU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
logger.debug(msg).hideErrors
|
logger.debug(msg).hideErrors
|
||||||
@ -881,7 +881,7 @@ package object implicits {
|
|||||||
logger.error(msg).hideErrors
|
logger.error(msg).hideErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class TypedActorContextExt[T](private val ctx: ActorContext[T])
|
implicit final class TypedActorContextExt[T](private val ctx: ActorContext[T])
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def spawnN[U](behavior: Behavior[U], props: Props = Props.empty)(implicit
|
def spawnN[U](behavior: Behavior[U], props: Props = Props.empty)(implicit
|
||||||
name: sourcecode.Name
|
name: sourcecode.Name
|
||||||
@ -889,19 +889,20 @@ package object implicits {
|
|||||||
ctx.spawn(behavior, name.value, props)
|
ctx.spawn(behavior, name.value, props)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class MonixEvalTaskExt[T](private val task: monix.eval.Task[T])
|
implicit final class MonixEvalTaskExt[T](private val task: monix.eval.Task[T])
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def toIO = IO.deferAction(implicit s => IO.from(task))
|
def toIO = IO.deferAction(implicit s => IO.from(task))
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class MonixBioTaskExt[T](private val task: monix.bio.Task[T])
|
implicit final class MonixBioTaskExt[T](private val task: monix.bio.Task[T])
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def toTask =
|
def toTask =
|
||||||
monix.eval.Task.deferAction(implicit s => monix.eval.Task.from(task))
|
monix.eval.Task.deferAction(implicit s => monix.eval.Task.from(task))
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class CoevalEitherExt[L, R](private val coeval: Coeval[Either[L, R]])
|
implicit final class CoevalEitherExt[L, R](
|
||||||
extends AnyVal {
|
private val coeval: Coeval[Either[L, R]]
|
||||||
|
) extends AnyVal {
|
||||||
def toIO = coeval.to[Task].hideErrors.rethrow
|
def toIO = coeval.to[Task].hideErrors.rethrow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import scalafx.application.JFXApp
|
|||||||
import scalafx.application.JFXApp.PrimaryStage
|
import scalafx.application.JFXApp.PrimaryStage
|
||||||
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.FxScheduler
|
||||||
import wow.doge.mygame.implicits.JavaFXMonixObservables._
|
import wow.doge.mygame.implicits.JavaFXMonixObservables._
|
||||||
import wow.doge.mygame.utils.IOUtils._
|
import wow.doge.mygame.utils.IOUtils._
|
||||||
object Launcher {
|
object Launcher {
|
||||||
@ -28,7 +28,8 @@ object Launcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Props(
|
class Props(
|
||||||
val schedulers: Schedulers,
|
// val schedulers: Schedulers,
|
||||||
|
val fxScheduler: FxScheduler,
|
||||||
val signal: Deferred[Task, LauncherResult]
|
val signal: Deferred[Task, LauncherResult]
|
||||||
) {
|
) {
|
||||||
val create = UIO(new Launcher(this))
|
val create = UIO(new Launcher(this))
|
||||||
@ -105,7 +106,7 @@ class Launcher private (props: Launcher.Props) {
|
|||||||
Observable(launchAction, exitAction).merge
|
Observable(launchAction, exitAction).merge
|
||||||
.doOnNext(_ =>
|
.doOnNext(_ =>
|
||||||
me.Task(delegate.stage.close())
|
me.Task(delegate.stage.close())
|
||||||
.executeOn(props.schedulers.fx)
|
.executeOn(props.fxScheduler.value)
|
||||||
)
|
)
|
||||||
.completedL
|
.completedL
|
||||||
)
|
)
|
||||||
@ -113,6 +114,7 @@ class Launcher private (props: Launcher.Props) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.start
|
.start
|
||||||
|
_ <- Task.fromCancelablePromise(startSignal)
|
||||||
c <- CancelableF[Task](combinedFib.cancel)
|
c <- CancelableF[Task](combinedFib.cancel)
|
||||||
} yield c)(_.cancel)
|
} yield c)(_.cancel)
|
||||||
|
|
||||||
|
@ -43,9 +43,8 @@ object EventBus {
|
|||||||
|
|
||||||
final case class ObservableSubscription[A, E <: A](
|
final case class ObservableSubscription[A, E <: A](
|
||||||
replyTo: ActorRef[Observable[E]]
|
replyTo: ActorRef[Observable[E]]
|
||||||
)(implicit
|
)(implicit classTag: ClassTag[E])
|
||||||
classTag: ClassTag[E]
|
extends Command[A] {
|
||||||
) extends Command[A] {
|
|
||||||
def ct = classTag
|
def ct = classTag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package wow.doge.mygame.subsystems.events
|
package wow.doge.mygame.subsystems.events
|
||||||
|
|
||||||
|
import wow.doge.mygame.game.entities.CharacterStats
|
||||||
|
|
||||||
sealed trait Event
|
sealed trait Event
|
||||||
case object BulletFired extends Event
|
case object BulletFired extends Event
|
||||||
// type BulletFired = BulletFired.type
|
// type BulletFired = BulletFired.type
|
||||||
@ -25,6 +27,9 @@ object EntityMovementEvent {
|
|||||||
|
|
||||||
sealed trait StatsEvent extends Event
|
sealed trait StatsEvent extends Event
|
||||||
object StatsEvent {
|
object StatsEvent {
|
||||||
case class DamageEvent(hitBy: String, victimName: String, amount: Int)
|
case class DamageEvent(
|
||||||
extends StatsEvent
|
hitBy: String,
|
||||||
|
victimName: String,
|
||||||
|
amount: CharacterStats.DamageHealth
|
||||||
|
) extends StatsEvent
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import scala.reflect.ClassTag
|
|||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.LogOptions
|
import akka.actor.typed.LogOptions
|
||||||
import akka.actor.typed.Props
|
import akka.actor.typed.Props
|
||||||
import akka.actor.typed.Scheduler
|
|
||||||
import akka.actor.typed.SpawnProtocol
|
import akka.actor.typed.SpawnProtocol
|
||||||
import akka.actor.typed.SupervisorStrategy
|
import akka.actor.typed.SupervisorStrategy
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
@ -20,13 +19,14 @@ 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 wow.doge.mygame.types.AkkaScheduler
|
||||||
|
|
||||||
class EventsModule(
|
class EventsModule(
|
||||||
scheduler: Scheduler,
|
scheduler: AkkaScheduler,
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
) {
|
) {
|
||||||
import EventsModule._
|
import EventsModule._
|
||||||
implicit val s = scheduler
|
implicit val s = scheduler.value
|
||||||
implicit val sp = spawnProtocol
|
implicit val sp = spawnProtocol
|
||||||
implicit val timeout = Timeout(1.second)
|
implicit val timeout = Timeout(1.second)
|
||||||
|
|
||||||
|
@ -0,0 +1,310 @@
|
|||||||
|
package wow.doge.mygame.subsystems.scriptsystem
|
||||||
|
|
||||||
|
import javax.script.ScriptEngine
|
||||||
|
import javax.script.ScriptEngineManager
|
||||||
|
import javax.script.ScriptException
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
import ammonite.main.Defaults
|
||||||
|
import ammonite.runtime.Storage.Folder
|
||||||
|
import ammonite.util.Res
|
||||||
|
import ammonite.util.Res.Failure
|
||||||
|
import cats.effect.Resource
|
||||||
|
import cats.effect.concurrent.Deferred
|
||||||
|
import cats.syntax.either._
|
||||||
|
import cats.syntax.flatMap._
|
||||||
|
import com.softwaremill.macwire._
|
||||||
|
import com.softwaremill.tagging._
|
||||||
|
import groovy.util.GroovyScriptEngine
|
||||||
|
import io.odin.Logger
|
||||||
|
import monix.bio.IO
|
||||||
|
import monix.bio.Task
|
||||||
|
import monix.bio.UIO
|
||||||
|
import monix.catnap.ConcurrentQueue
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import monix.{eval => me}
|
||||||
|
import groovy.util.ResourceException
|
||||||
|
import monix.catnap.MVar
|
||||||
|
|
||||||
|
trait Requestable[A] {
|
||||||
|
|
||||||
|
protected def queue: ConcurrentQueue[Task, A]
|
||||||
|
|
||||||
|
def request[T](
|
||||||
|
compileRequest: Deferred[Task, T] => A
|
||||||
|
)(implicit timeout: FiniteDuration) =
|
||||||
|
for {
|
||||||
|
d <- Deferred[Task, T]
|
||||||
|
req = compileRequest(d)
|
||||||
|
_ <- queue.offer(req)
|
||||||
|
res <- d.get.timeout(timeout).map(_.get)
|
||||||
|
} yield res
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptCompiler private (
|
||||||
|
_queue: ConcurrentQueue[Task, ScriptCompiler.Command]
|
||||||
|
) extends Requestable[ScriptCompiler.Command] {
|
||||||
|
|
||||||
|
override protected def queue = _queue
|
||||||
|
|
||||||
|
// def tell(item: Command) = queue.offer(item)
|
||||||
|
|
||||||
|
}
|
||||||
|
object ScriptCompiler {
|
||||||
|
|
||||||
|
sealed trait State
|
||||||
|
case object Idle extends State
|
||||||
|
case object Active extends State
|
||||||
|
|
||||||
|
/**
|
||||||
|
* script representation
|
||||||
|
*/
|
||||||
|
sealed trait ScriptTag
|
||||||
|
type ScriptObject = Any @@ ScriptTag
|
||||||
|
|
||||||
|
sealed trait KotlinEngineTag
|
||||||
|
type KotlinScriptEngine = ScriptEngine @@ KotlinEngineTag
|
||||||
|
|
||||||
|
sealed trait Error
|
||||||
|
final case class AmmoniteFailure(error: Res.Failure) extends Error
|
||||||
|
final case class AmmoniteException(error: Res.Exception) extends Error
|
||||||
|
final case class ScriptExceptionError(error: ScriptException) extends Error
|
||||||
|
final case class ResourceExceptionError(error: ResourceException)
|
||||||
|
extends Error
|
||||||
|
final case class GroovyScriptExceptionError(
|
||||||
|
error: groovy.util.ScriptException
|
||||||
|
) extends Error
|
||||||
|
final case class SomeError(reason: String) extends Error
|
||||||
|
|
||||||
|
sealed trait Command
|
||||||
|
final case class Get(
|
||||||
|
path: os.Path,
|
||||||
|
result: Deferred[Task, ScriptResult],
|
||||||
|
force: Boolean
|
||||||
|
) extends Command
|
||||||
|
final case class GetData(result: Deferred[Task, Data])
|
||||||
|
final case class ObservableData(result: Deferred[Task, Observable[Data]])
|
||||||
|
extends Command
|
||||||
|
// extends Command
|
||||||
|
// final case class CompileAll(paths: Seq[os.Path]) extends Command
|
||||||
|
|
||||||
|
type ScriptsMap = Map[os.Path, Any]
|
||||||
|
type ScriptResult = Either[Error, Any]
|
||||||
|
|
||||||
|
sealed trait ScriptType
|
||||||
|
case object ScalaType extends ScriptType
|
||||||
|
case object KotlinType extends ScriptType
|
||||||
|
case object GroovyType extends ScriptType
|
||||||
|
|
||||||
|
val defaultScalaRunner =
|
||||||
|
ammonite
|
||||||
|
.Main(
|
||||||
|
storageBackend = new Folder(
|
||||||
|
// os.pwd / "target"
|
||||||
|
Defaults.ammoniteHome,
|
||||||
|
isRepl = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val defaultKotlinRunner: KotlinScriptEngine = {
|
||||||
|
val manager = new ScriptEngineManager()
|
||||||
|
val engine = manager.getEngineByExtension("main.kts")
|
||||||
|
engine.taggedWith[KotlinEngineTag]
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultGroovyRunner: GroovyScriptEngine =
|
||||||
|
new GroovyScriptEngine(os.pwd.toString)
|
||||||
|
|
||||||
|
case class Data(scriptsMap: ScriptsMap)
|
||||||
|
|
||||||
|
class SourceMaker(
|
||||||
|
queue: ConcurrentQueue[Task, Command],
|
||||||
|
worker: ScriptCompilerWorker
|
||||||
|
) {
|
||||||
|
import com.softwaremill.quicklens._
|
||||||
|
def get =
|
||||||
|
for {
|
||||||
|
dataVar <- MVar[Task].of(Data(Map.empty))
|
||||||
|
obs <- Task.deferAction(implicit s =>
|
||||||
|
Task(
|
||||||
|
Observable
|
||||||
|
.repeatEvalF(queue.poll)
|
||||||
|
.scanEval0(me.Task.pure((Active: State) -> Data(Map.empty))) {
|
||||||
|
case state -> data -> command =>
|
||||||
|
val nextState: IO[Error, (State, Data)] = state match {
|
||||||
|
case Idle => IO.pure(Idle -> data)
|
||||||
|
case Active =>
|
||||||
|
command match {
|
||||||
|
case Get(path, result, force) =>
|
||||||
|
def getAndUpdate =
|
||||||
|
worker
|
||||||
|
.request(
|
||||||
|
ScriptCompilerWorker.CompileAny(path, _)
|
||||||
|
)(
|
||||||
|
20.seconds
|
||||||
|
)
|
||||||
|
.flatTap(result.complete)
|
||||||
|
.hideErrors
|
||||||
|
.rethrow
|
||||||
|
.flatMap(res =>
|
||||||
|
UIO(pprint.log(res)) >>
|
||||||
|
UIO.pure(
|
||||||
|
data
|
||||||
|
.modify(_.scriptsMap)
|
||||||
|
.using(_ + (path -> res))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
nextData <-
|
||||||
|
if (force) getAndUpdate
|
||||||
|
else
|
||||||
|
data.scriptsMap.get(path) match {
|
||||||
|
case Some(e) =>
|
||||||
|
result
|
||||||
|
.complete(e.asRight[Error])
|
||||||
|
.hideErrors >> UIO.pure(data)
|
||||||
|
case None => getAndUpdate
|
||||||
|
}
|
||||||
|
} yield Active -> nextData
|
||||||
|
case ObservableData(result) =>
|
||||||
|
result
|
||||||
|
.complete(Observable.repeatEvalF(dataVar.take))
|
||||||
|
.hideErrors >> IO.pure(Active -> data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
nextState
|
||||||
|
.flatTap { case (_, data) => dataVar.put(data).hideErrors }
|
||||||
|
.tapError(err => UIO(pprint.log(err.toString)))
|
||||||
|
.attempt
|
||||||
|
// .mapFilter(_.toOption)
|
||||||
|
.map(_.getOrElse(state -> data))
|
||||||
|
.toTask
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield obs
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptCompileFns(
|
||||||
|
val scalaRunner: ammonite.Main,
|
||||||
|
val kotlinRunner: KotlinScriptEngine,
|
||||||
|
val groovyRunner: GroovyScriptEngine
|
||||||
|
) {
|
||||||
|
def runScala(path: os.Path): Either[Error, Any] =
|
||||||
|
scalaRunner
|
||||||
|
.runScript(path, Seq.empty)
|
||||||
|
._1 match {
|
||||||
|
case e @ Res.Exception(t, msg) => Left(AmmoniteException(e))
|
||||||
|
|
||||||
|
case f @ Failure(msg) => Left(AmmoniteFailure(f))
|
||||||
|
|
||||||
|
case Res.Success(obj) => Right(obj)
|
||||||
|
|
||||||
|
case _ => Left(SomeError("Failed to run script"))
|
||||||
|
}
|
||||||
|
|
||||||
|
def runKotlin(path: os.Path): Either[Error, Any] =
|
||||||
|
Either
|
||||||
|
.catchNonFatal(kotlinRunner.eval(os.read(path)))
|
||||||
|
.leftMap {
|
||||||
|
case ex: ScriptException => ScriptExceptionError(ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
def runGroovy(path: os.Path): Either[Error, Any] =
|
||||||
|
Either
|
||||||
|
.catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString, ""))
|
||||||
|
.leftMap {
|
||||||
|
case ex: ResourceException => ResourceExceptionError(ex)
|
||||||
|
case ex: groovy.util.ScriptException => GroovyScriptExceptionError(ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
def ensureReturnedObjectNotNull(scriptObject: Any): Either[Error, Any] =
|
||||||
|
Either.fromOption(Option(scriptObject), SomeError("unknown object"))
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptCompileSource(
|
||||||
|
fns: ScriptCompileFns,
|
||||||
|
logger: Logger[Task],
|
||||||
|
queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest]
|
||||||
|
) {
|
||||||
|
import fns._
|
||||||
|
|
||||||
|
val source =
|
||||||
|
Task.deferAction(implicit s =>
|
||||||
|
Task(
|
||||||
|
Observable
|
||||||
|
.repeatEvalF(queue.poll)
|
||||||
|
.doOnNextF(el => logger.debug(s"Got $el"))
|
||||||
|
.mapParallelUnorderedF(4) {
|
||||||
|
case ScriptCompilerWorker.CompileAny(path, result) =>
|
||||||
|
for {
|
||||||
|
mbRes <- Task(
|
||||||
|
runScala(path)
|
||||||
|
.flatMap(ensureReturnedObjectNotNull)
|
||||||
|
// .map(_.taggedWith[ScriptTag])
|
||||||
|
)
|
||||||
|
_ <- result.complete(mbRes)
|
||||||
|
} yield mbRes
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// override private val
|
||||||
|
final class ScriptCompilerWorker(
|
||||||
|
logger: Logger[Task],
|
||||||
|
_queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest]
|
||||||
|
) extends Requestable[ScriptCompilerWorker.CompileRequest] {
|
||||||
|
|
||||||
|
override def queue = _queue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object ScriptCompilerWorker {
|
||||||
|
|
||||||
|
sealed trait CompileRequest
|
||||||
|
final case class CompileAny(
|
||||||
|
path: os.Path,
|
||||||
|
result: Deferred[Task, ScriptResult]
|
||||||
|
) extends CompileRequest
|
||||||
|
|
||||||
|
def apply(
|
||||||
|
logger: Logger[Task],
|
||||||
|
scalaRunner: ammonite.Main = defaultScalaRunner,
|
||||||
|
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner,
|
||||||
|
groovyRunner: GroovyScriptEngine = defaultGroovyRunner
|
||||||
|
) = {
|
||||||
|
val acquire = for {
|
||||||
|
queue <- ConcurrentQueue[Task].bounded[CompileRequest](10)
|
||||||
|
fns <- UIO.pure(wire[ScriptCompileFns])
|
||||||
|
worker = wire[ScriptCompilerWorker]
|
||||||
|
fib <- wire[ScriptCompileSource].source.flatMap(_.completedL.toIO.start)
|
||||||
|
// resource = Concurrent[Task].background(
|
||||||
|
// wire[ScriptCompileSource].source.flatMap(_.completedL.toIO)
|
||||||
|
// )
|
||||||
|
} yield worker -> fib
|
||||||
|
Resource
|
||||||
|
.make(acquire) { case worker -> fib => fib.cancel }
|
||||||
|
.map(_._1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(logger: Logger[Task]) = {
|
||||||
|
def acquire(worker: ScriptCompilerWorker) =
|
||||||
|
for {
|
||||||
|
queue <- ConcurrentQueue.bounded[Task, Command](10)
|
||||||
|
fib <- wire[SourceMaker].get.flatMap(_.completedL.toIO.start)
|
||||||
|
} yield new ScriptCompiler(queue) -> fib
|
||||||
|
|
||||||
|
ScriptCompilerWorker(logger)
|
||||||
|
.flatMap(worker =>
|
||||||
|
Resource.make(acquire(worker)) { case (_, fib) => fib.cancel }
|
||||||
|
)
|
||||||
|
.map(_._1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -96,10 +96,7 @@ object ScriptActor {
|
|||||||
|
|
||||||
def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] =
|
def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] =
|
||||||
scalaRunner
|
scalaRunner
|
||||||
.runScript(
|
.runScript(path, Seq.empty)
|
||||||
path,
|
|
||||||
Seq.empty
|
|
||||||
)
|
|
||||||
._1 match {
|
._1 match {
|
||||||
case ammonite.util.Res.Exception(t, msg) => Left(Error(msg))
|
case ammonite.util.Res.Exception(t, msg) => Left(Error(msg))
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ object ScriptCachingActor {
|
|||||||
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
||||||
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
||||||
extends Command
|
extends Command
|
||||||
private[scriptsystem] case object NoOp extends Command
|
private case object NoOp extends Command
|
||||||
|
|
||||||
private[scriptsystem] final case class DelegateToChild(
|
private final case class DelegateToChild(
|
||||||
scriptActor: ActorRef[ScriptActor.Command],
|
scriptActor: ActorRef[ScriptActor.Command],
|
||||||
scriptPath: os.Path,
|
scriptPath: os.Path,
|
||||||
requester: ActorRef[ScriptResult]
|
requester: ActorRef[ScriptResult]
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
package wow.doge.mygame
|
package wow.doge.mygame
|
||||||
|
|
||||||
import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
import akka.actor.typed.Scheduler
|
||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import wow.doge.mygame.game.GameAppTags
|
|
||||||
import monix.execution.Scheduler
|
|
||||||
import io.estatico.newtype.macros.newtype
|
import io.estatico.newtype.macros.newtype
|
||||||
|
import monix.execution.schedulers.SchedulerService
|
||||||
|
import wow.doge.mygame.game.GameAppTags
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
||||||
|
|
||||||
package object types {
|
package object types {
|
||||||
type RootNode = AppNode2 @@ GameAppTags.RootNode
|
type RootNode = AppNode2 @@ GameAppTags.RootNode
|
||||||
type GuiNode = AppNode2 @@ GameAppTags.GuiNode
|
type GuiNode = AppNode2 @@ GameAppTags.GuiNode
|
||||||
@newtype case class AsyncScheduler(value: Scheduler)
|
@newtype case class JmeScheduler(value: SchedulerService)
|
||||||
@newtype case class IoScheduler(value: Scheduler)
|
@newtype case class AkkaScheduler(value: Scheduler)
|
||||||
@newtype case class FxScheduler(value: Scheduler)
|
|
||||||
@newtype case class JmeScheduler(value: Scheduler)
|
|
||||||
@newtype case class AkkaScheduler(value: akka.actor.typed.Scheduler)
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package wow.doge.mygame.utils
|
||||||
|
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import monix.reactive.OverflowStrategy
|
||||||
|
import monix.execution.Cancelable
|
||||||
|
import monix.execution.cancelables.SingleAssignCancelable
|
||||||
|
import monix.execution.Ack
|
||||||
|
|
||||||
|
object MonixDirectoryWatcher {
|
||||||
|
import better.files._
|
||||||
|
import io.methvin.better.files._
|
||||||
|
def apply(path: os.Path) =
|
||||||
|
Observable.create[String](OverflowStrategy.DropNew(50)) { sub =>
|
||||||
|
import sub.scheduler
|
||||||
|
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
|
||||||
|
val myDir = File(path.toString)
|
||||||
|
val watcher = new RecursiveFileMonitor(myDir) {
|
||||||
|
override def onCreate(file: File, count: Int) =
|
||||||
|
println(s"$file got created")
|
||||||
|
override def onModify(file: File, count: Int) =
|
||||||
|
// println(s"$file got modified $count times")
|
||||||
|
if (sub.onNext(file.name) == Ack.Stop) c.cancel()
|
||||||
|
override def onDelete(file: File, count: Int) =
|
||||||
|
println(s"$file got deleted")
|
||||||
|
}
|
||||||
|
watcher.start()(scheduler)
|
||||||
|
c := Cancelable(() => watcher.stop())
|
||||||
|
c
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
63
src/test/scala/wow/doge/mygame/AnimTest.scala
Normal file
63
src/test/scala/wow/doge/mygame/AnimTest.scala
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
import com.jme3.anim.AnimClip
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
|
import com.jme3.asset.DesktopAssetManager
|
||||||
|
import com.jme3.scene.Spatial
|
||||||
|
import monix.bio.UIO
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import com.jme3.scene.Node
|
||||||
|
import com.jme3.anim.AnimComposer
|
||||||
|
import com.jme3.anim.SkinningControl
|
||||||
|
import com.jme3.anim.tween.action.BaseAction
|
||||||
|
import com.jme3.anim.tween.Tweens
|
||||||
|
import scala.jdk.javaapi.CollectionConverters._
|
||||||
|
import com.jme3.anim.tween.Tween
|
||||||
|
import com.jme3.anim.tween.action.ClipAction
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
class AnimTest extends AnyFunSuite {
|
||||||
|
import monix.execution.Scheduler.Implicits.global
|
||||||
|
val assetManager = new AssetManager(new DesktopAssetManager(true))
|
||||||
|
|
||||||
|
test("test 1") {
|
||||||
|
println((for {
|
||||||
|
_ <- UIO.unit
|
||||||
|
model <- assetManager.loadModelAs[Node](
|
||||||
|
os.rel / "Models" / "Oto" / "Oto.mesh.xml"
|
||||||
|
)
|
||||||
|
animcontrol <- UIO(model.getControlMaybe(classOf[AnimComposer]))
|
||||||
|
skinningcontrol <- UIO(model.getControlMaybe(classOf[SkinningControl]))
|
||||||
|
_ <- UIO(println(animcontrol))
|
||||||
|
_ <- UIO(println(skinningcontrol))
|
||||||
|
_ <- UIO {
|
||||||
|
animcontrol.map { ac =>
|
||||||
|
// ac.actionSequence()
|
||||||
|
// ac.makeAction()
|
||||||
|
// new BaseAction(Tweens.sequence())
|
||||||
|
// ac.getAnimClips().a
|
||||||
|
Option(ac.getAnimClip("hmm"))
|
||||||
|
.map(clip => new ClipAction(clip))
|
||||||
|
.map(Tweens.sequence(_))
|
||||||
|
.foreach { t =>
|
||||||
|
val actions = new BaseAction(t)
|
||||||
|
ac.addAction("hmm", actions)
|
||||||
|
}
|
||||||
|
|
||||||
|
val names = List("Walk", "Jump")
|
||||||
|
for {
|
||||||
|
clips <- names.traverse(name =>
|
||||||
|
Option(ac.getAnimClip(name)).map(clip => new ClipAction(clip))
|
||||||
|
)
|
||||||
|
tween <- Tweens.sequence(clips: _*).some
|
||||||
|
actions <- new BaseAction(tween).some
|
||||||
|
_ <- ac.addAction("Sequence 1", actions).some
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} yield model).attempt.runSyncUnsafe(10.seconds))
|
||||||
|
}
|
||||||
|
}
|
30
src/test/scala/wow/doge/mygame/FileWatcherTest.scala
Normal file
30
src/test/scala/wow/doge/mygame/FileWatcherTest.scala
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
import cats.effect.{Resource => CResource}
|
||||||
|
import monix.eval.Task
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
class FileWatcherTest extends AnyFunSuite {
|
||||||
|
test("1") {
|
||||||
|
import better.files._
|
||||||
|
import io.methvin.better.files._
|
||||||
|
|
||||||
|
val myDir =
|
||||||
|
File((os.pwd / "assets" / "scripts").toString)
|
||||||
|
val watcher = new RecursiveFileMonitor(myDir) {
|
||||||
|
override def onCreate(file: File, count: Int) =
|
||||||
|
println(s"$file got created")
|
||||||
|
override def onModify(file: File, count: Int) =
|
||||||
|
println(s"$file got modified $count times")
|
||||||
|
override def onDelete(file: File, count: Int) =
|
||||||
|
println(s"$file got deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
import monix.execution.Scheduler.Implicits.global
|
||||||
|
CResource
|
||||||
|
.make(Task { watcher.start(); watcher })(w => Task(w.stop()))
|
||||||
|
.use(_ => Task.never)
|
||||||
|
.runSyncUnsafe(10.seconds)
|
||||||
|
}
|
||||||
|
}
|
44
src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala
Normal file
44
src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
|
import monix.bio.Task
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import monix.execution.Scheduler.Implicits.global
|
||||||
|
import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler
|
||||||
|
import io.odin.consoleLogger
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
|
class MonixScriptCompilerTest extends AnyFunSuite {
|
||||||
|
|
||||||
|
test("some-test") {
|
||||||
|
ScriptCompiler(consoleLogger[Task]())
|
||||||
|
.use(scriptCompiler =>
|
||||||
|
for {
|
||||||
|
// _ <-
|
||||||
|
// scriptCompiler.source
|
||||||
|
// .doOnNextF(el => Task(println(s"Got $el")))
|
||||||
|
// .completedL
|
||||||
|
// .toIO
|
||||||
|
// .hideErrors
|
||||||
|
// .startAndForget
|
||||||
|
response <- scriptCompiler.request(
|
||||||
|
ScriptCompiler.Get(
|
||||||
|
os.pwd / "assets" / "scripts" / "scala" / "hello2.sc",
|
||||||
|
_,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)(20.seconds)
|
||||||
|
_ <- Task(pprint.log(response.toString))
|
||||||
|
// _ <- Task.sleep(4.seconds)
|
||||||
|
// _ <- scriptCompiler.tell(
|
||||||
|
// ScriptCompiler.CompileAny(
|
||||||
|
// os.pwd / "assets" / "scripts" / "scala" / "hello2.sc"
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// _ <- Task.sleep(4.seconds)
|
||||||
|
// _ <- Task.sleep(8.seconds)
|
||||||
|
} yield ()
|
||||||
|
)
|
||||||
|
.runSyncUnsafe(20.seconds)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user