This commit is contained in:
Rohan Sircar 2021-01-10 11:41:39 +05:30
parent 45ab129790
commit 3881aac350
29 changed files with 544 additions and 338 deletions

0
.attach_pid12833 Normal file
View File

0
.attach_pid19972 Normal file
View File

View File

@ -95,7 +95,8 @@ lazy val root = (project in file(".")).settings(
// "com.github.Oshan96" % "CustomStage" % "v1.3.1", // "com.github.Oshan96" % "CustomStage" % "v1.3.1",
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2", "com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
"org.recast4j" % "recast" % "1.2.5", "org.recast4j" % "recast" % "1.2.5",
"org.recast4j" % "detour" % "1.2.5" "org.recast4j" % "detour" % "1.2.5",
"com.lihaoyi" %% "pprint" % "0.6.0"
), ),
// Determine OS version of JavaFX binaries // Determine OS version of JavaFX binaries
@ -155,10 +156,6 @@ lazy val root = (project in file(".")).settings(
// val oldStrategy = (assemblyMergeStrategy in assembly).value // val oldStrategy = (assemblyMergeStrategy in assembly).value
// oldStrategy(x) // oldStrategy(x)
} }
// scalaVersion := "2.13.2", // 2.11.12, or 2.13.3
// semanticdbEnabled := true, // enable SemanticDB
// semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version
// semanticdbVersion := "4.3.24",
) )
initialCommands in (console) := """ammonite.Main.main(Array.empty)""" initialCommands in (console) := """ammonite.Main.main(Array.empty)"""

View File

@ -47,6 +47,10 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
.allocated .allocated
.unsafeRunSync() .unsafeRunSync()
val mainFileLogger2 = mainFileLogger.contramap(lm =>
lm.copy(message = lm.message.map(s => fansi.Str(s).plainText))
)
private lazy val (eventBusFileLogger, release3) = private lazy val (eventBusFileLogger, release3) =
fileLogger[IO]( fileLogger[IO](
"eventbus.log", "eventbus.log",
@ -72,15 +76,22 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
case s if s.startsWith("com.jayfella.jme.jfx.util.JfxPlatform") => case s if s.startsWith("com.jayfella.jme.jfx.util.JfxPlatform") =>
defaultConsoleLogger.withMinimalLevel(Level.Info) defaultConsoleLogger.withMinimalLevel(Level.Info)
// case s case s
// if s.startsWith( if s.startsWith(
// "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler" "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"
// ) => ) =>
defaultConsoleLogger.withMinimalLevel(Level.Info)
case s
if s.startsWith(
"wow.doge.mygame.game.entities.NpcMovementActor"
) =>
defaultConsoleLogger.withMinimalLevel(Level.Trace) |+| mainFileLogger2
.withMinimalLevel(Level.Trace)
// defaultConsoleLogger.withMinimalLevel( Level.Trace) //selectively turn on trace logging for specific classes // defaultConsoleLogger.withMinimalLevel( Level.Trace) //selectively turn on trace logging for specific classes
case s if s.startsWith("wow.doge.mygame.subsystems.events.EventBus") => case s if s.startsWith("wow.doge.mygame.subsystems.events.EventBus") =>
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| eventBusFileLogger defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| eventBusFileLogger
case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") => case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger2
case _ => //if wildcard case isn't provided, default logger is no-op case _ => //if wildcard case isn't provided, default logger is no-op
defaultConsoleLogger.withMinimalLevel(Level.Debug) defaultConsoleLogger.withMinimalLevel(Level.Debug)
} }

View File

@ -5,16 +5,16 @@ import scala.concurrent.duration._
import _root_.monix.bio.BIOApp import _root_.monix.bio.BIOApp
import _root_.monix.bio.Task import _root_.monix.bio.Task
import _root_.monix.bio.UIO import _root_.monix.bio.UIO
import akka.actor.typed.ActorSystem
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import cats.effect.ExitCode import cats.effect.ExitCode
import cats.effect.Resource import cats.effect.Resource
import cats.implicits._ import cats.implicits._
import com.softwaremill.macwire._
import io.odin._ 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.game.GameAppResource
import wow.doge.mygame.utils.GenericConsoleStream import wow.doge.mygame.utils.GenericConsoleStream
object Main extends BIOApp with MainModule { object Main extends BIOApp with MainModule {
@ -34,25 +34,23 @@ object Main extends BIOApp with MainModule {
Formatter.json Formatter.json
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
jmeScheduler <- jMESchedulerResource jmeScheduler <- jMESchedulerResource
actorSystem <- actorSystemResource(logger) implicit0(actorSystem: ActorSystem[SpawnProtocol.Command]) <-
gameApp <- { actorSystemResource(logger)
// new BulletAppState() // gameApp <- {
// bas.setThreadingType(Thr) // // new BulletAppState()
// gameAppResource(new StatsAppState()) // // bas.setThreadingType(Thr)
wire[GameAppResource].get // // gameAppResource(new StatsAppState())
} // wire[GameAppResource].get
// }
_ <- Resource.liftF( _ <- Resource.liftF(
new MainApp( new MainApp(
logger, logger,
gameApp, // gameApp,
actorSystem, // actorSystem,
jmeScheduler, jmeScheduler,
schedulers, schedulers,
consoleStream consoleStream
)( )(actorSystem, timeout, actorSystem.scheduler).program
timeout,
actorSystem.scheduler
).program
) )
} yield () } yield ()
@ -63,7 +61,7 @@ object Main extends BIOApp with MainModule {
Console.withOut(consoleStream)( Console.withOut(consoleStream)(
appResource(consoleStream) appResource(consoleStream)
.use(_ => Task.unit >> Task(consoleStream.close())) .use(_ => Task.unit >> Task(consoleStream.close()))
.onErrorHandle(_.printStackTrace()) .onErrorHandleWith(ex => UIO(ex.printStackTrace()))
.as(ExitCode.Success) .as(ExitCode.Success)
) )
} }

View File

@ -4,6 +4,7 @@ import akka.actor.typed.ActorSystem
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import cats.effect.Resource
import cats.effect.concurrent.Deferred import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager import com.jme3.asset.AssetManager
@ -46,98 +47,80 @@ 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.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 cats.syntax.eq._
import EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
class MainApp( class MainApp(
logger: Logger[Task], logger: Logger[Task],
gameApp: GameApp,
implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
jmeThread: monix.execution.Scheduler, jmeThread: monix.execution.Scheduler,
schedulers: Schedulers, schedulers: Schedulers,
consoleStream: GenericConsoleStream[TextArea] consoleStream: GenericConsoleStream[TextArea]
)(implicit )(implicit
spawnProtocol: ActorSystem[SpawnProtocol.Command],
@annotation.unused timeout: Timeout, @annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler @annotation.unused scheduler: Scheduler
) { ) {
lazy val scriptSystemInit = val scriptSystemInit =
new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
def gameInit: Task[Fiber[Throwable, Unit]] = val eventsModule = new EventsModule(spawnProtocol)
for {
eventsModule <- Task(new EventsModule(spawnProtocol))
playerEventBus <- eventsModule.playerEventBusTask
mainEventBus <- eventsModule.mainEventBusTask
tickEventBus <- eventsModule.tickEventBusTask
gameAppActor <- AkkaUtils.spawnActorL2(
GameAppActor.Props(tickEventBus).behavior,
"gameAppActor"
)
_ <- gameAppActor !! GameAppActor.Start
gameAppFib <- gameApp.start.executeOn(jmeThread).start
/**
* schedule a task to run on the JME thread and wait for it's completion
* before proceeding forward, as a signal that the JME thread has been
* initialized, otherwise we'll get NPEs trying to access the fields
* of the game app
*/
res <- gameApp.enqueueL(() => "done")
_ <- logger.info(s"Result = $res")
/**
* JME Thread has been initialized at this point. We can now access the
* field of the game application
*/
inputManager <- gameApp.inputManager
assetManager <- gameApp.assetManager
stateManager <- gameApp.stateManager
camera <- gameApp.camera
rootNode <- gameApp.rootNode
enqueueR <- Task(gameApp.enqueue _)
viewPort <- gameApp.viewPort
_ <- logger.info("before")
// jfxUI <- gameApp.jfxUI
consoleTextArea <- Task(new TextArea {
text = "hello \n"
editable = false
wrapText = true
// maxHeight = 150
// maxWidth = 300
})
// _ <- Task(consoleStream := consoleTextArea)
// _ <- Task(jfxUI += consoleTextArea)
_ <- logger.info("after")
bulletAppState <- Task(new BulletAppState())
_ <- Task(stateManager.attach(bulletAppState))
_ <- logger.info("Initializing console stream")
_ <- wire[MainAppDelegate].init(gameApp.scheduler)
} yield (gameAppFib)
lazy val program = for { def gameInit: Resource[Task, Fiber[Throwable, Unit]] =
GameApp.resource(logger, jmeThread, schedulers).evalMap {
case gameApp -> gameAppFib =>
for {
playerEventBus <- eventsModule.playerEventBusTask
mainEventBus <- eventsModule.mainEventBusTask
tickEventBus <- eventsModule.tickEventBusTask
gameAppActor <- AkkaUtils.spawnActorL(
GameAppActor.Props(tickEventBus).behavior,
"gameAppActor"
)
_ <- gameAppActor !! GameAppActor.Start
inputManager <- gameApp.inputManager
assetManager <- gameApp.assetManager
stateManager <- gameApp.stateManager
camera <- gameApp.camera
rootNode <- gameApp.rootNode
enqueueR <- Task(gameApp.enqueue _)
viewPort <- gameApp.viewPort
_ <- logger.info("before")
// jfxUI <- gameApp.jfxUI
consoleTextArea <- Task(new TextArea {
text = "hello \n"
editable = false
wrapText = true
// maxHeight = 150
// maxWidth = 300
})
// _ <- Task(consoleStream := consoleTextArea)
// _ <- Task(jfxUI += consoleTextArea)
_ <- logger.info("after")
bulletAppState <- Task(new BulletAppState())
_ <- Task(stateManager.attach(bulletAppState))
_ <- logger.info("Initializing console stream")
_ <- wire[MainAppDelegate].init(gameApp.scheduler)
} yield gameAppFib
}
val program = for {
scriptSystem <- scriptSystemInit scriptSystem <- scriptSystemInit
/**
* Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI
* Without this, we get a "Toolkit already initialized" exception. The launch button
* in the launcher completes the signal. The game init process which listens for this
* signal can then continue
*/
launchSignal <- Deferred[Task, Launcher.LauncherResult] launchSignal <- Deferred[Task, Launcher.LauncherResult]
launcher <- new Launcher.Props(schedulers, launchSignal).create launcher <- new Launcher.Props(schedulers, launchSignal).create
cancelToken <- launcher.init() launchResult <- launcher.init.use(_ => launchSignal.get)
launchResult <- launchSignal.get
_ <- cancelToken.cancel
_ <- _ <-
/** /**
* User chose to quit * User chose to quit
*/ */
if (launchResult == LauncherResult.Exit) if (launchResult === LauncherResult.Exit)
logger.info("Exiting") logger.info("Exiting")
/** /**
* User chose launch. Wait for game window to close * User chose launch. Wait for game window to close
*/ */
else else
gameInit.flatMap(_.join) gameInit.use(_.join)
} yield () } yield ()
} }
@ -146,7 +129,6 @@ class MainApp(
*/ */
class MainAppDelegate( class MainAppDelegate(
gameApp: GameApp, gameApp: GameApp,
implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
loggerL: Logger[Task], loggerL: Logger[Task],
playerEventBus: GameEventBus[PlayerEvent], playerEventBus: GameEventBus[PlayerEvent],
tickEventBus: GameEventBus[TickEvent], tickEventBus: GameEventBus[TickEvent],
@ -159,10 +141,11 @@ class MainAppDelegate(
rootNode: Node @@ GameAppTags.RootNode, rootNode: Node @@ GameAppTags.RootNode,
bulletAppState: BulletAppState bulletAppState: BulletAppState
)(implicit )(implicit
spawnProtocol: ActorSystem[SpawnProtocol.Command],
@annotation.unused timeout: Timeout, @annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler @annotation.unused scheduler: Scheduler
) { ) {
lazy val physicsSpace = bulletAppState.physicsSpace val physicsSpace = bulletAppState.physicsSpace
def init( def init(
appScheduler: monix.execution.Scheduler appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea] // consoleStream: GenericConsoleStream[TextArea]
@ -190,19 +173,21 @@ class MainAppDelegate(
// johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler) // johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler)
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
// _ <- (johnActor !! NpcActorSupervisor.Move( // _ <-
// ImVector3f(-80, 0, 100) // (johnActor !! NpcActorSupervisor.Move(
// )).executeAsync.delayExecution(2.seconds) // ImVector3f(-30, 0, 10)
_ <- // )).executeAsync
IOUtils // .delayExecution(2.seconds)
.toIO( // _ <-
rootNode // IOUtils
.observableBreadthFirst() // .toIO(
.doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) // rootNode
.completedL // .observableBreadthFirst()
) // .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName())))
.executeOn(appScheduler) // .completedL
.startAndForget // )
// .executeOn(appScheduler)
// .startAndForget
} yield () } yield ()
def createPlayerController( def createPlayerController(
@ -210,14 +195,14 @@ class MainAppDelegate(
): IO[PlayerController.Error, Unit] = { ): IO[PlayerController.Error, Unit] = {
val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.ZERO
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
lazy val playerPhysicsControl = val playerPhysicsControl =
PlayerController.Defaults.defaultPlayerPhysicsControl PlayerController.Defaults.defaultPlayerPhysicsControl
.taggedWith[PlayerControllerTags.PlayerTag] .taggedWith[PlayerControllerTags.PlayerTag]
// lazy val camNode = // lazy val camNode =
// PlayerController.Defaults // PlayerController.Defaults
// .defaultCamerNode(camera, playerPos) // .defaultCamerNode(camera, playerPos)
// .taggedWith[PlayerControllerTags.PlayerCameraNode] // .taggedWith[PlayerControllerTags.PlayerCameraNode]
lazy val mbPlayerNode = PlayerController.Defaults val mbPlayerNode = PlayerController.Defaults
.defaultPlayerNode( .defaultPlayerNode(
assetManager, assetManager,
modelPath, modelPath,
@ -225,7 +210,7 @@ class MainAppDelegate(
// camNode // camNode
playerPhysicsControl playerPhysicsControl
) )
lazy val cameraPivotNode = new Node(EntityIds.CameraPivot.value) val cameraPivotNode = new Node(EntityIds.CameraPivot.value)
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode] .taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
for { for {
@ -258,11 +243,11 @@ class MainAppDelegate(
) = ) =
// : IO[PlayerController.Error, Unit] = // : IO[PlayerController.Error, Unit] =
{ {
val initialPos = ImVector3f(100, 0, 0) val initialPos = ImVector3f(50, 5, 0)
// val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" // val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
lazy val npcPhysicsControl = val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
new BetterCharacterControl(1f, 2.1f, 10f) // (1f, 2.1f, 10f)
// .withJumpForce(ImVector3f(0, 5f, 0)) .withJumpForce(ImVector3f(0, 5f, 0))
// val npcMovementActor = AkkaUtils.spawnActorL2( // val npcMovementActor = AkkaUtils.spawnActorL2(
// new NpcMovementActor2.Props( // new NpcMovementActor2.Props(
// initialPos, // initialPos,
@ -271,13 +256,13 @@ class MainAppDelegate(
// ).behavior, // ).behavior,
// s"${npcName}-npcMovementActor" // s"${npcName}-npcMovementActor"
// ) // )
lazy val mbNpcNode = PlayerController.Defaults.defaultNpcNode( val mbNpcNode = PlayerController.Defaults.defaultNpcNode(
assetManager, assetManager,
initialPos, initialPos,
npcPhysicsControl, npcPhysicsControl,
npcName npcName
) )
val npcActorTask = AkkaUtils.spawnActorL2( val npcActorTask = AkkaUtils.spawnActorL(
NpcActorSupervisor NpcActorSupervisor
.Props( .Props(
new NpcMovementActor.Props( new NpcMovementActor.Props(
@ -302,7 +287,7 @@ class MainAppDelegate(
physicsSpace += npcNode physicsSpace += npcNode
rootNode += npcNode rootNode += npcNode
} }
} yield (npcActor) } yield npcActor
} }
} }

View File

@ -11,15 +11,14 @@ trait MainModule extends ExecutorsModule {
def actorSystemResource( def actorSystemResource(
logger: Logger[Task] logger: Logger[Task]
): Resource[Task, ActorSystem[SpawnProtocol.Command]] = ): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
Resource.make(logger.info("Creating Actor System") >> Task { Resource.make(
ActorSystem( logger.info("Creating Actor System") >> Task(
SpawnProtocol(), ActorSystem(SpawnProtocol(), name = "GameActorSystem")
name = "GameActorSystem"
) )
})(sys => )(sys =>
for { for {
_ <- Task(sys.terminate()) _ <- Task(sys.terminate())
_ <- Task.fromFuture(sys.whenTerminated) _ <- Task.deferFuture(sys.whenTerminated)
_ <- logger.info("Actor System Terminated") _ <- logger.info("Actor System Terminated")
} yield () } yield ()
) )

View File

@ -1,11 +1,13 @@
package wow.doge.mygame.game package wow.doge.mygame.game
import cats.effect.Resource
import cats.effect.concurrent.Deferred import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager import com.jme3.asset.AssetManager
import com.jme3.input.InputManager import com.jme3.input.InputManager
import com.jme3.scene.Node import com.jme3.scene.Node
import com.jme3.scene.Spatial import com.jme3.scene.Spatial
import com.jme3.system.AppSettings
import com.softwaremill.tagging._ import com.softwaremill.tagging._
import com.typesafe.scalalogging.{Logger => SLogger} import com.typesafe.scalalogging.{Logger => SLogger}
import io.odin.Logger import io.odin.Logger
@ -15,7 +17,13 @@ import monix.catnap.ConcurrentChannel
import monix.catnap.ConsumerF import monix.catnap.ConsumerF
import monix.catnap.Semaphore import monix.catnap.Semaphore
import monix.eval.Coeval import monix.eval.Coeval
import monix.execution.CancelablePromise
import monix.execution.Scheduler
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.subsystems.ui.JFxUI import wow.doge.mygame.game.subsystems.ui.JFxUI
import wow.doge.mygame.implicits._
import monix.execution.annotations.UnsafeBecauseImpure
import monix.reactive.Observable
sealed trait Error sealed trait Error
case object FlyCamNotExists extends Error case object FlyCamNotExists extends Error
@ -40,6 +48,8 @@ class GameApp(logger: Logger[Task], val app: SimpleAppExt) {
def camera = Task(app.getCamera()) def camera = Task(app.getCamera())
def viewPort = Task(app.getViewPort()) def viewPort = Task(app.getViewPort())
def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode]) def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
def rootNode2 =
WrappedNode(app.getRootNode()).taggedWith[GameAppTags.RootNode]
// def rootNode2 = SynchedObject(app.getRootNode()) // def rootNode2 = SynchedObject(app.getRootNode())
def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn))) def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn)))
def enqueue(cb: () => Unit) = def enqueue(cb: () => Unit) =
@ -55,12 +65,57 @@ class GameApp(logger: Logger[Task], val app: SimpleAppExt) {
} }
class WrappedNode private (node: Node) {
// def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
def children: Observable[Spatial] = node.observableChildren
def attachChild(n: Node): Task[Unit] = Task(node.attachChild(node))
def add(wn: WrappedNode): Task[Unit] =
Task(node.attachChild(wn.unsafeDelegate))
/**
* Get the underlying wrapped value
*/
@UnsafeBecauseImpure
def unsafeDelegate = node
}
object WrappedNode {
def apply(name: String) = new WrappedNode(new Node(name))
def apply(n: Node) = new WrappedNode(n)
implicit class WrappedNodeOps(private val wn: WrappedNode) extends AnyVal {
def +=(n: Node) = wn.attachChild(n)
def +=(wn: WrappedNode) = wn.add(wn)
}
}
object GameApp { object GameApp {
class WrappedNode(node: Node, lock: Semaphore[Task]) { def resource(
logger: Logger[Task],
jmeScheduler: Scheduler,
schedulers: Schedulers
) =
Resource.make(
for {
startSignal <- Task(CancelablePromise[Unit]())
app <- Task(new SimpleAppExt(schedulers, startSignal))
_ <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat))) /**
} * disables the launcher
* We'll be making our own launcher anyway
*/
app.setShowSettings(false)
app.setSettings(settings)
}
gameApp <- Task(new GameApp(logger, app))
fib <- gameApp.start.executeOn(jmeScheduler).start
_ <- Task.fromCancelablePromise(startSignal)
} yield gameApp -> fib
)(_._2.cancel)
/** /**
* Synchronization wrapper for a mutable object * Synchronization wrapper for a mutable object

View File

@ -6,6 +6,7 @@ import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.game.TickGenerator.Send import wow.doge.mygame.game.TickGenerator.Send
import wow.doge.mygame.game.entities.GenericTimerActor import wow.doge.mygame.game.entities.GenericTimerActor
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
@ -28,7 +29,7 @@ object GameAppActor {
) { ) {
def behavior = def behavior =
Behaviors.setup[Command] { ctx => Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from GameAppActor") ctx.log.infoP("Hello from GameAppActor")
val renderTickGenerator = val renderTickGenerator =
ctx.spawn( ctx.spawn(
Behaviors Behaviors
@ -78,7 +79,7 @@ object TickGenerator {
object SubscribingActor { object SubscribingActor {
def apply() = def apply() =
Behaviors.receive[PhysicsTick.type] { (ctx, msg) => Behaviors.receive[PhysicsTick.type] { (ctx, msg) =>
ctx.log.debug(s"received event $msg") ctx.log.debugP(s"received event $msg")
Behaviors.same Behaviors.same
} }
} }

View File

@ -1,36 +1,30 @@
package wow.doge.mygame.game package wow.doge.mygame.game
import cats.effect.Resource
import com.jme3.app.StatsAppState
import com.jme3.system.AppSettings
import io.odin.Logger
import monix.bio.Task
import monix.execution.Scheduler
import wow.doge.mygame.executors.Schedulers
class GameAppResource(
logger: Logger[Task],
jmeScheduler: Scheduler,
schedulers: Schedulers
) {
def get: Resource[Task, GameApp] = // class GameAppResource(
Resource.make( // logger: Logger[Task],
for { // jmeScheduler: Scheduler,
_ <- logger.info("Creating game app") // schedulers: Schedulers
appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState())) // ) {
app <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
/** // def get: Resource[Task, GameApp] =
* disables the launcher // Resource.make(
* We'll be making our own launcher anyway // for {
*/ // _ <- logger.info("Creating game app")
appExt.setShowSettings(false) // appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState()))
appExt.setSettings(settings) // app <- Task {
// JMERunner.runner = app // val settings = new AppSettings(true)
new GameApp(logger, appExt) // settings.setVSync(true)
}
} yield (app) // /**
)(_ => logger.info("Closing game app")) // * disables the launcher
} // * We'll be making our own launcher anyway
// */
// appExt.setShowSettings(false)
// appExt.setSettings(settings)
// // JMERunner.runner = app
// new GameApp(logger, appExt)
// }
// } yield (app)
// )(_ => logger.info("Closing game app"))
// }

View File

@ -6,17 +6,15 @@ import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState import com.jme3.app.state.AppState
import monix.bio.Task import monix.bio.Task
import monix.execution.CancelableFuture import monix.execution.CancelableFuture
import monix.execution.CancelablePromise
import monix.execution.Scheduler import monix.execution.Scheduler
import monix.execution.atomic.Atomic import monix.execution.atomic.Atomic
import monix.execution.{CancelablePromise => Promise}
import monix.reactive.MulticastStrategy
import monix.reactive.Observable
import monix.reactive.subjects.ConcurrentSubject
import wow.doge.mygame.executors.GUIExecutorService import wow.doge.mygame.executors.GUIExecutorService
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers
class SimpleAppExt( class SimpleAppExt(
schedulers: Schedulers, schedulers: Schedulers,
startSignal: CancelablePromise[Unit],
appStates: AppState* appStates: AppState*
) extends SimpleApplication(appStates: _*) { ) extends SimpleApplication(appStates: _*) {
import SimpleAppExt._ import SimpleAppExt._
@ -26,32 +24,26 @@ class SimpleAppExt(
*/ */
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]]) private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
private val tickSubject = // def tickObservable: Observable[Float] = tickSubject
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
schedulers.async
)
def tickObservable: Observable[Float] = tickSubject override def simpleInitApp(): Unit = {
startSignal.success(())
override def simpleInitApp(): Unit = {}
override def simpleUpdate(tpf: Float): Unit = {
tickSubject.onNext(tpf)
} }
override def simpleUpdate(tpf: Float): Unit = {}
override def stop(): Unit = { override def stop(): Unit = {
tickSubject.onComplete()
super.stop() super.stop()
} }
def enqueueScala[T](cb: () => T): CancelableFuture[T] = { def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
val p = Promise[T]() val p = CancelablePromise[T]()
taskQueue2.transform(_ :+ MyTask(p, cb)) taskQueue2.transform(_ :+ MyTask(p, cb))
p.future p.future
} }
def enqueueL[T](cb: () => T): Task[T] = def enqueueL[T](cb: () => T): Task[T] =
Task.deferFuture(enqueueScala(cb)) Task.deferFuture(enqueueFuture(cb))
override protected def runQueuedTasks(): Unit = { override protected def runQueuedTasks(): Unit = {
taskQueue2.transform { current => taskQueue2.transform { current =>
@ -73,7 +65,7 @@ class SimpleAppExt(
lazy val scheduler = Scheduler(JMEExecutorService) lazy val scheduler = Scheduler(JMEExecutorService)
} }
object SimpleAppExt { object SimpleAppExt {
private[game] case class MyTask[T](p: Promise[T], cb: () => T) private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T)
} }
// val ship = ed.createEntity() // val ship = ed.createEntity()

View File

@ -36,11 +36,11 @@ object TestActor {
// ) // )
// ) { // ) {
// case Success(value) => // case Success(value) =>
// ctx.log.debug("Received Value") // ctx.log.debugP("Received Value")
// ctx.log.debug(value.toString()) // ctx.log.debugP(value.toString())
// Done // Done
// case Failure(exception) => // case Failure(exception) =>
// ctx.log.debug(s"Received Error ${exception.getMessage()}") // ctx.log.debugP(s"Received Error ${exception.getMessage()}")
// Done // Done
// } // }
} }
@ -60,24 +60,24 @@ object TestActor {
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// ) { // ) {
// case Success(value) => { // case Success(value) => {
// ctx.log.debug(value.toString()) // ctx.log.debugP(value.toString())
// ctx.ask( // ctx.ask(
// scriptStorer, // scriptStorer,
// ScriptStoringActor // ScriptStoringActor
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// ) { // ) {
// case Success(value) => { // case Success(value) => {
// ctx.log.debug(value.toString()) // ctx.log.debugP(value.toString())
// Done // Done
// } // }
// case Failure(exception) => // case Failure(exception) =>
// ctx.log.debug(exception.getMessage()) // ctx.log.debugP(exception.getMessage())
// Done // Done
// } // }
// Done // Done
// } // }
// case Failure(exception) => // case Failure(exception) =>
// ctx.log.debug(exception.getMessage()) // ctx.log.debugP(exception.getMessage())
// Done // Done
// } // }
} }

View File

@ -193,23 +193,23 @@ class MovementActor(
// val walkDir = new Vector3f // val walkDir = new Vector3f
val dir = state.cardinalDir val dir = state.cardinalDir
if (dir.up) { if (dir.up) {
ctx.log.debug("up") ctx.log.debugP("up")
// ctx.log.debug(Thread.currentThread().getName()) // ctx.log.debugP(Thread.currentThread().getName())
// walkDir.addLocal(0, 0, -1) // walkDir.addLocal(0, 0, -1)
walkDir += camDir walkDir += camDir
} }
if (dir.left) { if (dir.left) {
ctx.log.debug("left") ctx.log.debugP("left")
// walkDir.addLocal(-1, 0, 0) // walkDir.addLocal(-1, 0, 0)
walkDir.addLocal(camLeft) walkDir.addLocal(camLeft)
} }
if (dir.right) { if (dir.right) {
ctx.log.debug("right") ctx.log.debugP("right")
// walkDir.addLocal(1, 0, 0) // walkDir.addLocal(1, 0, 0)
walkDir.addLocal(camLeft.negateLocal()) walkDir.addLocal(camLeft.negateLocal())
} }
if (dir.down) { if (dir.down) {
ctx.log.debug("down") ctx.log.debugP("down")
walkDir.addLocal(camDir.negateLocal()) walkDir.addLocal(camDir.negateLocal())
// walkDir.addLocal(0, 0, 1) // walkDir.addLocal(0, 0, 1)
} }

View File

@ -39,6 +39,8 @@ object NpcActorSupervisor {
private case class LogError(err: Throwable) extends Command private case class LogError(err: Throwable) extends Command
private case object NoOp extends Command private case object NoOp extends Command
private case class MovementFailed(err: Throwable) extends Command
final case class Props( final case class Props(
npcMovementActorBehavior: Behavior[NpcMovementActor.Command], npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
npcName: String, npcName: String,
@ -82,7 +84,7 @@ class NpcActorSupervisor(
def idle(state: State): Behavior[NpcActorSupervisor.Command] = def idle(state: State): Behavior[NpcActorSupervisor.Command] =
Behaviors.setup { _ => Behaviors.setup { _ =>
ctx.log.debug("Inside Idle State") ctx.log.debugP("Inside Idle State")
Behaviors.receiveMessage[Command] { Behaviors.receiveMessage[Command] {
case m @ Move(pos) => case m @ Move(pos) =>
ctx.ask( ctx.ask(
@ -97,7 +99,7 @@ class NpcActorSupervisor(
moving(state, move.pos, signal) moving(state, move.pos, signal)
case LogError(err) => case LogError(err) =>
ctx.log.warn(err.getMessage()) ctx.log.warnP(err.getMessage())
Behaviors.same Behaviors.same
case _ => Behaviors.unhandled case _ => Behaviors.unhandled
} }
@ -117,12 +119,16 @@ class NpcActorSupervisor(
// ) // )
ctx.pipeToSelf(signal) { ctx.pipeToSelf(signal) {
case Success(value) => DoneMoving case Success(value) => DoneMoving
case Failure(exception) => LogError(exception) case Failure(exception) => MovementFailed(exception)
} }
Behaviors.receiveMessagePartial[Command] { Behaviors.receiveMessagePartial[Command] {
case LogError(err) => case LogError(err) =>
ctx.log.error(err.getMessage()) ctx.log.error(err.getMessage())
Behaviors.same Behaviors.same
case MovementFailed(err) =>
ctx.self ! LogError(err)
movementTimer ! GenericTimerActor.Stop
idle(state)
case m @ Move(pos) => case m @ Move(pos) =>
movementTimer ! GenericTimerActor.Stop movementTimer ! GenericTimerActor.Stop
children.npcMovementActor ! NpcMovementActor.StopMoving children.npcMovementActor ! NpcMovementActor.StopMoving
@ -132,7 +138,7 @@ class NpcActorSupervisor(
NpcMovementActor.MoveTo(pos, _) NpcMovementActor.MoveTo(pos, _)
) { ) {
case Success(signal) => InternalMove(m, signal) case Success(signal) => InternalMove(m, signal)
case Failure(exception) => LogError(exception) case Failure(exception) => MovementFailed(exception)
} }
Behaviors.same Behaviors.same
case InternalMove(move, signal) => case InternalMove(move, signal) =>
@ -190,12 +196,6 @@ class NpcMovementActor[T](
case AskPosition(replyTo) => case AskPosition(replyTo) =>
replyTo ! location replyTo ! location
Behaviors.same Behaviors.same
case StopMoving =>
ctx.log.debug(
"Position at Stop = " + location.toString
)
props.enqueueR(() => cm.stop(props.movable))
receive(state)
case MoveTo( case MoveTo(
target: ImVector3f, target: ImVector3f,
replyTo: ActorRef[CancelableFuture[DoneMoving.type]] replyTo: ActorRef[CancelableFuture[DoneMoving.type]]
@ -204,7 +204,6 @@ class NpcMovementActor[T](
val p = CancelablePromise[DoneMoving.type]() val p = CancelablePromise[DoneMoving.type]()
replyTo ! p.future replyTo ! p.future
ticking(p, target, state) ticking(p, target, state)
} }
def ticking( def ticking(
@ -214,7 +213,7 @@ class NpcMovementActor[T](
): Behavior[NpcMovementActor.Command] = ): Behavior[NpcMovementActor.Command] =
Behaviors.receiveMessagePartial { Behaviors.receiveMessagePartial {
case StopMoving => case StopMoving =>
ctx.log.debug( ctx.log.debugP(
"Position at Stop = " + location.toString "Position at Stop = " + location.toString
) )
props.enqueueR(() => cm.stop(props.movable)) props.enqueueR(() => cm.stop(props.movable))
@ -225,12 +224,11 @@ class NpcMovementActor[T](
if (dst <= 10f) { if (dst <= 10f) {
ctx.self ! StopMoving ctx.self ! StopMoving
reachDestination.success(DoneMoving) reachDestination.success(DoneMoving)
receive(state)
} else { } else {
ctx.log.trace("Difference = " + dst.toString()) ctx.log.traceP("Difference = " + dst.toString())
ctx.log.trace("Current pos = " + location.toString()) ctx.log.traceP("Current pos = " + location.toString())
Behaviors.same
} }
Behaviors.same
} }
} }

View File

@ -39,7 +39,7 @@ object PlayerActorSupervisor {
ctx.log.info("Hello from PlayerActor") ctx.log.info("Hello from PlayerActor")
// spawn children actors // spawn children actors
lazy val movementActor = val movementActor =
ctx.spawn( ctx.spawn(
Behaviors Behaviors
.supervise(imMovementActorBehavior) .supervise(imMovementActorBehavior)

View File

@ -29,6 +29,7 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.AkkaUtils
import com.softwaremill.macwire._
object PlayerControllerTags { object PlayerControllerTags {
sealed trait PlayerTag sealed trait PlayerTag
@ -47,7 +48,6 @@ object PlayerController {
loggerL: Logger[Task], loggerL: Logger[Task],
physicsSpace: PhysicsSpace, physicsSpace: PhysicsSpace,
initialPlayerPos: ImVector3f = ImVector3f.ZERO, initialPlayerPos: ImVector3f = ImVector3f.ZERO,
spawnProtocol: ActorRef[SpawnProtocol.Command],
playerEventBus: GameEventBus[PlayerEvent], playerEventBus: GameEventBus[PlayerEvent],
playerPhysicsControl: BetterCharacterControl, playerPhysicsControl: BetterCharacterControl,
appScheduler: monix.execution.Scheduler, appScheduler: monix.execution.Scheduler,
@ -56,29 +56,34 @@ object PlayerController {
cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode, cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode,
tickEventBus: GameEventBus[TickEvent], tickEventBus: GameEventBus[TickEvent],
camera: Camera camera: Camera
)(implicit timeout: Timeout, scheduler: Scheduler) { )(implicit
spawnProtocol: ActorRef[SpawnProtocol.Command],
timeout: Timeout,
scheduler: Scheduler
) {
val playerActorBehavior = {
val movementActorBeh = new ImMovementActor.Props(
enqueueR,
playerPhysicsControl,
camera
).behavior
val cameraActorBeh = new PlayerCameraActor.Props(
cameraPivotNode,
enqueueR,
playerNode.getWorldTranslation _
).behavior
new PlayerActorSupervisor.Props(
playerEventBus,
tickEventBus,
movementActorBeh,
cameraActorBeh
).behavior(playerPhysicsControl)
}
val create: IO[Error, Unit] = val create: IO[Error, Unit] =
(for { (for {
playerActor <- AkkaUtils.spawnActorL( playerActor <- AkkaUtils.spawnActorL(
spawnProtocol, playerActorBehavior,
"playerActorSupervisor", "playerActorSupervisor"
new PlayerActorSupervisor.Props(
playerEventBus,
// playerCameraEventBus,
tickEventBus,
new ImMovementActor.Props(
enqueueR,
playerPhysicsControl,
camera
).behavior,
// wireWith(PlayerCameraEventListener.apply _)
// PlayerCameraEventListener()
new PlayerCameraActor.Props(
cameraPivotNode,
enqueueR,
playerNode.getWorldTranslation _
).behavior
).behavior(playerPhysicsControl)
) )
_ <- Task(rootNode += playerNode) _ <- Task(rootNode += playerNode)
_ <- IO { _ <- IO {
@ -88,9 +93,7 @@ object PlayerController {
cameraPivotNode += cameraNode cameraPivotNode += cameraNode
// playerNode += cameraPivotNode // playerNode += cameraPivotNode
rootNode += cameraPivotNode rootNode += cameraPivotNode
} }
} yield ()) } yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage()))) .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
.executeOn(appScheduler) .executeOn(appScheduler)
@ -101,10 +104,10 @@ object PlayerController {
modelPath: os.RelPath, modelPath: os.RelPath,
cam: Camera cam: Camera
)(assetManager: AssetManager, bulletAppState: BulletAppState) = { )(assetManager: AssetManager, bulletAppState: BulletAppState) = {
lazy val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.ZERO
lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0)) .withJumpForce(ImVector3f(0, 5f, 0))
lazy val playerNode = new Node("PlayerNode") val playerNode = new Node("PlayerNode")
.withChildren( .withChildren(
assetManager assetManager
.loadModel(modelPath) .loadModel(modelPath)

View File

@ -18,10 +18,11 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.utils.IOUtils._ import wow.doge.mygame.utils.IOUtils._
import monix.bio.Task
object GameInputHandler { object GameInputHandler {
final class Props( class Props(
inputManager: InputManager, inputManager: InputManager,
playerEventBus: GameEventBus[PlayerEvent] playerEventBus: GameEventBus[PlayerEvent]
// playerCameraEventBus: GameEventBus[PlayerCameraEvent] // playerCameraEventBus: GameEventBus[PlayerCameraEvent]
@ -29,44 +30,44 @@ object GameInputHandler {
) { ) {
def begin = def begin =
for { for {
_ <- UIO(setupMovementKeys(inputManager)) _ <- Task(setupMovementKeys(inputManager))
// _ <- UIO(setupAnalogMovementKeys) // _ <- UIO(setupAnalogMovementKeys)
_ <- UIO(setupCameraKeys()) _ <- Task(setupCameraKeys())
_ <- toIO( _ <- toIO(
generateMovementInputEvents( me.Task.parSequence(
inputManager, Seq(
playerEventBus generateMovementInputEvents(
).completedL.startAndForget inputManager,
) playerEventBus
_ <- toIO( ).completedL,
generateAnalogMovementEvents( // generateAnalogMovementEvents(
inputManager, // inputManager,
playerEventBus // playerEventBus
).completedL.startAndForget // ).completedL,
) generateCameraEvents(
_ <- toIO( inputManager,
generateCameraEvents( playerEventBus
inputManager, ).completedL,
playerEventBus Ref
).completedL.startAndForget .of[me.Task, Boolean](false)
) .flatMap(value => cursorToggle(value))
_ <- toIO( )
Ref.of[me.Task, Boolean](false).flatMap(value => cursorToggle(value)) )
) ).startAndForget
} yield () } yield ()
def setupMovementKeys(inputManager: InputManager) = def setupMovementKeys(inputManager: InputManager) =
inputManager.withEnumMappings(PlayerMovementInput) { inputManager.withEnumMappings(PlayerMovementInput) {
case PlayerMovementInput.WalkRight => case PlayerMovementInput.WalkRight =>
Seq(new KeyTrigger(KeyInput.KEY_D)) new KeyTrigger(KeyInput.KEY_D) :: Nil
case PlayerMovementInput.WalkLeft => case PlayerMovementInput.WalkLeft =>
Seq(new KeyTrigger(KeyInput.KEY_A)) new KeyTrigger(KeyInput.KEY_A) :: Nil
case PlayerMovementInput.WalkForward => case PlayerMovementInput.WalkForward =>
Seq(new KeyTrigger(KeyInput.KEY_W)) new KeyTrigger(KeyInput.KEY_W) :: Nil
case PlayerMovementInput.WalkBackward => case PlayerMovementInput.WalkBackward =>
Seq(new KeyTrigger(KeyInput.KEY_S)) new KeyTrigger(KeyInput.KEY_S) :: Nil
case PlayerMovementInput.Jump => case PlayerMovementInput.Jump =>
Seq(new KeyTrigger(KeyInput.KEY_SPACE)) new KeyTrigger(KeyInput.KEY_SPACE) :: Nil
} }
def setupAnalogMovementKeys() = def setupAnalogMovementKeys() =
@ -124,7 +125,6 @@ object GameInputHandler {
} }
) )
.completedL .completedL
.startAndForget
} yield () } yield ()
} }

View File

@ -14,15 +14,15 @@ object DefaultGameLevel {
assetManager: AssetManager, assetManager: AssetManager,
viewPort: ViewPort viewPort: ViewPort
) = { ) = {
lazy val sceneModel: Spatial = assetManager.loadModel("main.scene") val sceneModel: Spatial = assetManager.loadModel("main.scene")
lazy val sceneShape = CollisionShapeFactory.createMeshShape( val sceneShape = CollisionShapeFactory.createMeshShape(
sceneModel.toNode match { sceneModel.toNode match {
case Right(node) => node case Right(node) => node
case Left(ex) => case Left(ex) =>
throw new NotImplementedError("No fallback sceneshape") throw new NotImplementedError("No fallback sceneshape")
} }
) )
lazy val landscape: RigidBodyControl = val landscape: RigidBodyControl =
new RigidBodyControl(sceneShape, 0) new RigidBodyControl(sceneShape, 0)
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f)) viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))

View File

@ -1,5 +1,6 @@
package wow.doge.mygame.game.subsystems.movement package wow.doge.mygame.game.subsystems.movement
import cats.Id
import com.jme3.bullet.control.BetterCharacterControl import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath import com.jme3.math.FastMath
import com.jme3.math.Quaternion import com.jme3.math.Quaternion
@ -8,7 +9,6 @@ import monix.eval.Coeval
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.movement.RotateDir import wow.doge.mygame.subsystems.movement.RotateDir
// experiment to see if it would be useful to use an effect wrapper for a typeclass like this // experiment to see if it would be useful to use an effect wrapper for a typeclass like this
trait CanMove2[-A, F[_]] { trait CanMove2[-A, F[_]] {
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
@ -19,7 +19,35 @@ trait CanMove2[-A, F[_]] {
def rotate(inst: A, rotateDir: RotateDir): F[Unit] def rotate(inst: A, rotateDir: RotateDir): F[Unit]
} }
object Test {
val x = new BetterCharacterControl(4, 10, 5)
def test[T](x: T)(implicit cm: CanMove2[T, Id]) = {
cm.move(x, ImVector3f.ZERO)
}
}
object CanMove2 { object CanMove2 {
implicit val testImpl = new CanMove2[BetterCharacterControl, Id] {
override def move(
inst: BetterCharacterControl,
direction: ImVector3f,
speedFactor: Float
): Id[Unit] = {}
override def location(inst: BetterCharacterControl): Id[ImVector3f] =
ImVector3f.ZERO
override def jump(inst: BetterCharacterControl): Id[Unit] = ???
override def stop(inst: BetterCharacterControl): Id[Unit] = ???
override def rotate(
inst: BetterCharacterControl,
rotateDir: RotateDir
): Id[Unit] = ???
}
implicit val implCanMoveForBetterCharacterControl = implicit val implCanMoveForBetterCharacterControl =
new CanMove2[BetterCharacterControl, Coeval] { new CanMove2[BetterCharacterControl, Coeval] {
override def move( override def move(

View File

@ -84,7 +84,7 @@ class ImMovementActor[T](
case Tick => case Tick =>
val walkDir = val walkDir =
getDirection2(state.cardinalDir, ctx.log.trace) getDirection2(state.cardinalDir, ctx.log.traceP)
// if (walkDir != ImVector3f.ZERO) { // if (walkDir != ImVector3f.ZERO) {
val tmp = walkDir * 25f * (1f / 144) val tmp = walkDir * 25f * (1f / 144)
props.enqueueR { () => props.enqueueR { () =>
@ -94,7 +94,10 @@ class ImMovementActor[T](
Behaviors.same Behaviors.same
} }
def getDirection2(cardinalDir: CardinalDirection, debug: String => Unit) = { def getDirection2(
cardinalDir: CardinalDirection,
debug: sourcecode.Text[String] => sourcecode.Text[String]
) = {
val camDir = val camDir =
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f) props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f) val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)

View File

@ -1,11 +1,14 @@
package wow.doge.mygame.implicits package wow.doge.mygame.implicits
import javafx.beans.value.ObservableValue
import javafx.beans.{value => jfxbv}
import javafx.scene.{input => jfxsi} import javafx.scene.{input => jfxsi}
import javafx.{event => jfxe} import javafx.{event => jfxe}
import monix.execution.Ack import monix.execution.Ack
import monix.execution.Cancelable import monix.execution.Cancelable
import monix.reactive.Observable import monix.reactive.Observable
import monix.reactive.OverflowStrategy import monix.reactive.OverflowStrategy
import scalafx.beans.property.Property
import scalafx.scene.Scene import scalafx.scene.Scene
import scalafx.scene.control.ButtonBase import scalafx.scene.control.ButtonBase
@ -55,6 +58,35 @@ object JavaFXMonixObservables {
} }
} }
implicit final class BindObs[A, B](private val prop: Property[A, B])
extends AnyVal {
def <--[T](op: Observable[(ObservableValue[_ <: B], B, B)] => T) = {
op(prop.observableChange())
}
def observableChange(): Observable[(ObservableValue[_ <: B], B, B)] = {
import monix.execution.cancelables.SingleAssignCancelable
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val listener = new jfxbv.ChangeListener[B] {
override def changed(
observable: ObservableValue[_ <: B],
oldValue: B,
newValue: B
): Unit = {
sub.onNext((observable, oldValue, newValue))
}
}
prop.addListener(listener)
c := Cancelable(() => prop.removeListener(listener))
c
}
}
}
implicit final class OnActionObservable( implicit final class OnActionObservable(
private val button: ButtonBase private val button: ButtonBase
) extends AnyVal { ) extends AnyVal {

View File

@ -49,6 +49,7 @@ import monix.execution.cancelables.SingleAssignCancelable
import monix.reactive.Observable import monix.reactive.Observable
import monix.reactive.OverflowStrategy import monix.reactive.OverflowStrategy
import monix.reactive.observers.Subscriber import monix.reactive.observers.Subscriber
import org.slf4j.Logger
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.MyBaseState import wow.doge.mygame.state.MyBaseState
@ -794,4 +795,67 @@ package object implicits {
def +=(node: scalafx.scene.Node) = jfxui.attachChild(node) def +=(node: scalafx.scene.Node) = jfxui.attachChild(node)
def -=(node: scalafx.scene.Node) = jfxui.detachChild(node) def -=(node: scalafx.scene.Node) = jfxui.detachChild(node)
} }
implicit class AkkaLoggerExt(private val logger: Logger) extends AnyVal {
def logP[T](
x: sourcecode.Text[T],
tag: String = "",
width: Int = 100,
height: Int = 500,
indent: Int = 2,
initialOffset: Int = 0
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
// def joinSeq[T](seq: Seq[T], sep: T): Seq[T] = {
// seq.flatMap(x => Seq(x, sep)).dropRight(1)
// }
val tagStrs =
if (tag.isEmpty) Seq.empty
else Seq(fansi.Color.Cyan(tag), fansi.Str(" "))
val prefix = Seq(
fansi.Color.Magenta(fileName.value),
fansi.Str(":"),
fansi.Color.Green(line.value.toString),
fansi.Str(" "),
fansi.Color.Cyan(x.source),
fansi.Str(": ")
) ++ tagStrs
fansi.Str.join(
prefix ++ pprint.tokenize(x.value, width, height, indent).toSeq: _*
)
// x.value
}
def warnP[T](
s: sourcecode.Text[T]
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
logger.warn(logP(s).render)
s
}
def errorP[T](
s: sourcecode.Text[T]
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
logger.error(logP(s).render)
s
}
def infoP[T](
s: sourcecode.Text[T]
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
logger.info(logP(s).render)
s
}
def debugP[T](
s: sourcecode.Text[T]
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
logger.debug(logP(s).render)
s
}
def traceP[T](
s: sourcecode.Text[T]
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
logger.trace(logP(s).render)
s
}
}
} }

View File

@ -1,28 +1,31 @@
package wow.doge.mygame.launcher package wow.doge.mygame.launcher
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration._
import cats.effect.concurrent.Deferred import cats.effect.concurrent.Deferred
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.value.ObservableValue
import monix.bio.Task import monix.bio.Task
import monix.catnap.CancelableF import monix.catnap.CancelableF
import monix.eval.{Task => ETask} import monix.execution.CancelablePromise
import monix.reactive.Observable import monix.reactive.Observable
import monix.{eval => me}
import scalafx.Includes._ import scalafx.Includes._
import scalafx.application.JFXApp import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage import scalafx.application.JFXApp.PrimaryStage
import scalafx.beans.property.StringProperty
import scalafx.scene.control.Button import scalafx.scene.control.Button
import scalafx.stage.StageStyle import scalafx.stage.StageStyle
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.implicits.JavaFXMonixObservables._ import wow.doge.mygame.implicits.JavaFXMonixObservables._
import wow.doge.mygame.utils.IOUtils._ import wow.doge.mygame.utils.IOUtils._
import cats.effect.Resource
import cats.kernel.Eq
object Launcher { object Launcher {
sealed trait LauncherResult sealed trait LauncherResult
object LauncherResult { object LauncherResult {
case object LaunchGame extends LauncherResult case object LaunchGame extends LauncherResult
case object Exit extends LauncherResult case object Exit extends LauncherResult
implicit val eqForLR = Eq.fromUniversalEquals[LauncherResult]
} }
class Props( class Props(
@ -44,10 +47,23 @@ class Launcher private (props: Launcher.Props) {
.observableAction() .observableAction()
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame))) .doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))
def testChangeObs(
obs: Observable[(ObservableValue[_ <: String], String, String)]
) =
obs
.doOnNext {
case (x, y, z) => monix.eval.Task.unit
}
// .subscribe()
private lazy val exitButton = new Button { private lazy val exitButton = new Button {
text = "Exit" text = "Exit"
// text <-- testChangeObs
} }
// exitButton.text.bind
StringProperty("") addListener ((_, _, _) => ())
private lazy val exitAction = private lazy val exitAction =
exitButton exitButton
.observableAction() .observableAction()
@ -60,22 +76,25 @@ class Launcher private (props: Launcher.Props) {
scene = _scene scene = _scene
} }
private lazy val internal = new JFXApp { private def internal(startSignal: CancelablePromise[Unit]) =
stage = _stage new JFXApp {
stage.initStyle(StageStyle.Undecorated) stage = _stage
// ResizeHelper.addResizeListener(stage) stage.initStyle(StageStyle.Undecorated)
} // ResizeHelper.addResizeListener(stage)
startSignal.success(())
}
private lazy val sceneDragObservable = { private lazy val sceneDragObservable = {
lazy val mpo = _scene.observableMousePressed() val mpo = _scene.observableMousePressed()
lazy val mdo = _scene.observableMouseDragged() val mdo = _scene.observableMouseDragged()
mpo.mergeMap(pressEvent => mpo.concatMap(pressEvent =>
mdo.doOnNext(dragEvent => mdo.doOnNext(dragEvent =>
ETask( me.Task(pprint.log("emitted")) >>
_stage.setX(dragEvent.screenX - pressEvent.sceneX) me.Task(
) >> _stage.setX(dragEvent.screenX - pressEvent.sceneX)
ETask( ) >>
me.Task(
_stage.setY( _stage.setY(
dragEvent.screenY - pressEvent.sceneY dragEvent.screenY - pressEvent.sceneY
) )
@ -84,21 +103,58 @@ class Launcher private (props: Launcher.Props) {
) )
} }
def init(delay: FiniteDuration = 2000.millis) = // import cats.syntax.all._
for {
_ <- Task(Platform.setImplicitExit(false))
fib <- Task(internal.main(Array.empty)).start // def init(delay: FiniteDuration = 2000.millis) =
_ <- Task.sleep(500.millis) // for {
sceneDragFib <- toIO(sceneDragObservable.completedL).start // _ <- Task(Platform.setImplicitExit(false))
fib2 <- toIO( // x <- (Task.unit.start, Task.unit.start).parTupled
Observable(launchAction, exitAction).merge // fxAppStartFib <- Task(internal.main(Array.empty)).start
.doOnNext(_ => // _ <- Task.sleep(500.millis)
ETask(internal.stage.close()).executeOn(props.schedulers.fx) // sceneDragFib <- toIO(sceneDragObservable.completedL).start
// buttonActionsComposedFib <- toIO(
// Observable(launchAction, exitAction).merge
// .doOnNext(_ =>
// me.Task(internal.stage.close()).executeOn(props.schedulers.fx)
// )
// .completedL
// ).start
// c <- CancelableF[Task](
// fxAppStartFib.cancel >> buttonActionsComposedFib.cancel >> sceneDragFib.cancel
// )
// } yield (c)
def init =
Resource.make(for {
_ <- Task(Platform.setImplicitExit(false))
startSignal <- Task(CancelablePromise[Unit]())
delegate <- Task(internal(startSignal))
combinedFib <-
Task
.parZip2(
Task(delegate.main(Array.empty)),
Task.fromCancelablePromise(startSignal) >> toIO(
me.Task.parSequence(
List(
sceneDragObservable.completedL,
Observable(launchAction, exitAction).merge
.doOnNext(_ =>
me.Task(delegate.stage.close())
.executeOn(props.schedulers.fx)
)
.completedL
)
)
)
) )
.completedL .start
).start c <- CancelableF[Task](
c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel) // Task(println("Cancelling")) >>
} yield (c) // combinedFib.cancel >>
// fxAppStartFib.cancel
// Task.unit
combinedFib.cancel
)
} yield c)(_.cancel)
} }

View File

@ -18,22 +18,22 @@ import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) { class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) {
implicit lazy val s = spawnProtocol.scheduler implicit val s = spawnProtocol.scheduler
implicit lazy val timeout = Timeout(1.second) implicit val timeout = Timeout(1.second)
lazy val eventBusLogger = SLogger[EventBus[_]] val eventBusLogger = SLogger[EventBus[_]]
lazy val playerEventBusTask = val playerEventBusTask =
createEventBus[PlayerEvent]("playerEventBus") createEventBus[PlayerEvent]("playerEventBus")
// lazy val playerCameraEventBusTask = // val playerCameraEventBusTask =
// createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG) // createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
lazy val tickEventBusTask = val tickEventBusTask =
createEventBus[TickEvent]("tickEventBus", Level.TRACE) createEventBus[TickEvent]("tickEventBus", Level.TRACE)
lazy val mainEventBusTask = createEventBus[Event]("mainEventBus") val mainEventBusTask = createEventBus[Event]("mainEventBus")
def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) = def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) =
spawnProtocol.askL( spawnProtocol.askL(

View File

@ -41,7 +41,7 @@ object ScriptActor {
result: ActorRef[Map[os.Path, Either[Error, Any]]] result: ActorRef[Map[os.Path, Either[Error, Any]]]
) extends Command ) extends Command
lazy val defaultScalaRunner = val defaultScalaRunner =
ammonite ammonite
.Main( .Main(
storageBackend = new Folder( storageBackend = new Folder(
@ -51,13 +51,13 @@ object ScriptActor {
) )
) )
lazy val defaultKotlinRunner: KotlinScriptEngine = { val defaultKotlinRunner: KotlinScriptEngine = {
val manager = new ScriptEngineManager() val manager = new ScriptEngineManager()
val engine = manager.getEngineByExtension("main.kts") val engine = manager.getEngineByExtension("main.kts")
engine.taggedWith[Kotlin] engine.taggedWith[Kotlin]
} }
lazy val defaultGroovyRunner: GroovyScriptEngine = val defaultGroovyRunner: GroovyScriptEngine =
new GroovyScriptEngine(os.pwd.toString) new GroovyScriptEngine(os.pwd.toString)
def apply( def apply(

View File

@ -1,5 +1,6 @@
package wow.doge.mygame.scriptsystem package wow.doge.mygame.scriptsystem
import scala.concurrent.duration._
import scala.util.Failure import scala.util.Failure
import scala.util.Success import scala.util.Success
@ -14,8 +15,9 @@ import akka.actor.typed.scaladsl.Routers
import akka.util.Timeout import akka.util.Timeout
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.implicits._
import wow.doge.mygame.state.ScriptActor import wow.doge.mygame.state.ScriptActor
import scala.concurrent.duration._
import ScriptActor.ScriptObject import ScriptActor.ScriptObject
object ScriptCachingActor { object ScriptCachingActor {
@ -188,10 +190,10 @@ class ScriptCachingActor(
Behaviors.same Behaviors.same
case Put(scriptPath, script) => case Put(scriptPath, script) =>
ctx.log.debug(s"Putting $script at path $scriptPath") ctx.log.debugP(s"Putting $script at path $scriptPath")
val newState = val newState =
state.modify(_.scriptsMap).using(_ + (scriptPath -> script)) state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
ctx.log.trace(newState.toString()) ctx.log.traceP(newState.toString())
receiveMessage(state = newState) receiveMessage(state = newState)
case NoOp => Behaviors.same case NoOp => Behaviors.same
@ -224,14 +226,14 @@ private[scriptsystem] object Methods {
scriptsMap scriptsMap
.get(scriptPath) .get(scriptPath)
.fold { .fold {
ctx.log.debug("Delegating to child") ctx.log.debugP("Delegating to child")
ctx.self ! DelegateToChild( ctx.self ! DelegateToChild(
scriptActor, scriptActor,
scriptPath, scriptPath,
requester requester
) )
} { s => } { s =>
ctx.log.debug("Getting script from cache") ctx.log.debugP("Getting script from cache")
requester ! Right(s) requester ! Right(s)
} }
} }

View File

@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import cats.effect.Resource
import monix.bio.Task import monix.bio.Task
import wow.doge.mygame.scriptsystem.ScriptCachingActor import wow.doge.mygame.scriptsystem.ScriptCachingActor
import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.AkkaUtils
@ -20,29 +19,18 @@ object ScriptInitMode {
} }
class ScriptSystemResource( class ScriptSystemResource(
path: os.Path, path: os.Path,
spawnProtocol: ActorRef[SpawnProtocol.Command],
mode: ScriptInitMode = ScriptInitMode.Lazy mode: ScriptInitMode = ScriptInitMode.Lazy
)(implicit timeout: Timeout, scheduler: Scheduler) { )(implicit
val make = { spawnProtocol: ActorRef[SpawnProtocol.Command],
// throw new Exception("boom") timeout: Timeout,
findScriptFiles(os.pwd / "assets" / "scripts") scheduler: Scheduler
) {
lazy val scriptCacheActor = AkkaUtils.spawnActorL(
spawnProtocol,
"scriptCachingActor",
ScriptCachingActor()
)
Resource.liftF(scriptCacheActor)
}
// sys.ask(ref => ScriptCachingActor.GetAll(os.pwd/'assets/'scripts/'scala/"hello2.sc",ref, false))
val init = for { val init = for {
scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts")) scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts"))
scriptCacheActor <- AkkaUtils.spawnActorL( scriptCacheActor <- AkkaUtils.spawnActorL(
spawnProtocol, ScriptCachingActor(),
"scriptCachingActor", "scriptCachingActor"
ScriptCachingActor()
) )
} yield (scriptCacheActor) } yield (scriptCacheActor)

View File

@ -9,7 +9,7 @@ import akka.util.Timeout
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
object AkkaUtils { object AkkaUtils {
def spawnActorL[T]( def spawnActorOldL[T](
spawnProtocol: ActorRef[SpawnProtocol.Command], spawnProtocol: ActorRef[SpawnProtocol.Command],
actorName: String, actorName: String,
behavior: Behavior[T] behavior: Behavior[T]
@ -22,7 +22,7 @@ object AkkaUtils {
_ _
) )
) )
def spawnActorL2[T]( def spawnActorL[T](
behavior: Behavior[T], behavior: Behavior[T],
actorName: String actorName: String
)(implicit )(implicit

View File

@ -21,7 +21,7 @@ class GenericConsoleStream[T](
)(implicit )(implicit
cs: ConsoleStreamable[T] cs: ConsoleStreamable[T]
) extends PrintStream(outputStream, true) { ) extends PrintStream(outputStream, true) {
private lazy val defaultOut = System.out private val defaultOut = System.out
def printToStreamable(stble: Option[T], text: String) = def printToStreamable(stble: Option[T], text: String) =
stble.foreach(s => cs.println(s, text)) stble.foreach(s => cs.println(s, text))
@ -57,7 +57,7 @@ object GenericConsoleStream {
*/ */
case class Config(exclusive: Boolean = false) case class Config(exclusive: Boolean = false)
object Config { object Config {
lazy val default = Config() val default = Config()
} }
implicit val implJFXConsoleStreamForTextArea = implicit val implJFXConsoleStreamForTextArea =