|
@ -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 = |
|
|
|
|
|
new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init |
|
|
|
|
|
|
|
|
val scriptSystemInit = |
|
|
|
|
|
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init |
|
|
|
|
|
|
|
|
def gameInit: Task[Fiber[Throwable, Unit]] = |
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
val eventsModule = new EventsModule(spawnProtocol) |
|
|
|
|
|
|
|
|
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 <- launchSignal.get |
|
|
|
|
|
_ <- cancelToken.cancel |
|
|
|
|
|
|
|
|
launchResult <- launcher.init.use(_ => launchSignal.get) |
|
|
_ <- |
|
|
_ <- |
|
|
/** |
|
|
/** |
|
|
* 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) |
|
|
|
|
|
// )).executeAsync.delayExecution(2.seconds) |
|
|
|
|
|
_ <- |
|
|
|
|
|
IOUtils |
|
|
|
|
|
.toIO( |
|
|
|
|
|
rootNode |
|
|
|
|
|
.observableBreadthFirst() |
|
|
|
|
|
.doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) |
|
|
|
|
|
.completedL |
|
|
|
|
|
) |
|
|
|
|
|
.executeOn(appScheduler) |
|
|
|
|
|
.startAndForget |
|
|
|
|
|
|
|
|
// _ <- |
|
|
|
|
|
// (johnActor !! NpcActorSupervisor.Move( |
|
|
|
|
|
// ImVector3f(-30, 0, 10) |
|
|
|
|
|
// )).executeAsync |
|
|
|
|
|
// .delayExecution(2.seconds) |
|
|
|
|
|
// _ <- |
|
|
|
|
|
// IOUtils |
|
|
|
|
|
// .toIO( |
|
|
|
|
|
// rootNode |
|
|
|
|
|
// .observableBreadthFirst() |
|
|
|
|
|
// .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) |
|
|
|
|
|
// .completedL |
|
|
|
|
|
// ) |
|
|
|
|
|
// .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 = |
|
|
|
|
|
new BetterCharacterControl(1f, 2.1f, 10f) |
|
|
|
|
|
// .withJumpForce(ImVector3f(0, 5f, 0)) |
|
|
|
|
|
|
|
|
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) |
|
|
|
|
|
// (1f, 2.1f, 10f) |
|
|
|
|
|
.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 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|