added npc state machine

This commit is contained in:
Rohan Sircar 2020-11-28 22:25:47 +05:30
parent 2d3fea4fd8
commit 73d657952f
45 changed files with 1345 additions and 2132 deletions

View File

@ -1,39 +0,0 @@
package ammonite
package $file.src.main.resources
import _root_.ammonite.interp.api.InterpBridge.{
value => interp
}
import _root_.ammonite.interp.api.InterpBridge.value.{
exit
}
import _root_.ammonite.interp.api.IvyConstructor.{
ArtifactIdExt,
GroupIdExt
}
import _root_.ammonite.runtime.tools.{
browse,
grep,
time,
tail
}
import _root_.ammonite.repl.tools.{
desugar,
source
}
import _root_.ammonite.main.Router.{
doc,
main
}
import _root_.ammonite.repl.tools.Util.{
pathScoptRead
}
object dep{
/*<script>*/class Test(x: Int)
/*</script>*/ /*<generated>*/
def $main() = { scala.Iterator[String]() }
override def toString = "dep"
/*</generated>*/
}

View File

@ -1,45 +0,0 @@
package ammonite
package $file.src.main.resources
import _root_.ammonite.interp.api.InterpBridge.{
value => interp
}
import _root_.ammonite.interp.api.InterpBridge.value.{
exit
}
import _root_.ammonite.interp.api.IvyConstructor.{
ArtifactIdExt,
GroupIdExt
}
import _root_.ammonite.runtime.tools.{
browse,
grep,
time,
tail
}
import _root_.ammonite.repl.tools.{
desugar,
source
}
import _root_.ammonite.main.Router.{
doc,
main
}
import _root_.ammonite.repl.tools.Util.{
pathScoptRead
}
import ammonite.$file.src.main.resources.{
dep
}
object hello{
/*<script>*/import $file.$
import dep.Test
/*<amm>*/val res_2 = /*</amm>*/new Test(1)
/*</script>*/ /*<generated>*/
def $main() = { scala.Iterator[String]() }
override def toString = "hello"
/*</generated>*/
}

View File

@ -1,134 +0,0 @@
package ammonite
package $file.src.main.resources
import _root_.ammonite.interp.api.InterpBridge.{
value => interp
}
import _root_.ammonite.interp.api.InterpBridge.value.{
exit
}
import _root_.ammonite.interp.api.IvyConstructor.{
ArtifactIdExt,
GroupIdExt
}
import _root_.ammonite.runtime.tools.{
browse,
grep,
time,
tail
}
import _root_.ammonite.repl.tools.{
desugar,
source
}
import _root_.ammonite.main.Router.{
doc,
main
}
import _root_.ammonite.repl.tools.Util.{
pathScoptRead
}
object hello2{
/*<script>*/import $repo.$
// import $repo.`https://bintray.com/jmonkeyengine/com.jme3`
// import $file.dep
import $ivy.$
// import $ivy.`wow.doge:game:1.0-SNAPSHOT`
import $ivy.$
// import wow.doge.game.types.GameScript
import com.jme3.scene.control.Control
// println("hello from script")
// class Scr extends GameScript {
// def start(): Unit = println("hello from start")
// def stop(): Unit = println("hello from stop")
// }
// // class SomeClass extends Control {}
// @main
// def main(): GameScript = new Scr()
import com.simsilica.es.base.DefaultEntityData
import com.simsilica.es.EntityData
import com.jme3.app.Application
import wow.doge.mygame.game.Implicits._
import wow.doge.mygame.components.TestComponent
import com.jme3.scene.shape.Box
import com.jme3.scene.Geometry
import com.jme3.material.Material
import com.jme3.math.ColorRGBA
import com.jme3.asset.AssetManager
import com.jme3.math.Vector3f
import wow.doge.mygame.state._
class TestAppState(
// private var _entity: Option[EntityData] = Some(new DefaultEntityData())
) extends MyBaseState {
protected lazy val b = new Box(1, 1, 1)
protected lazy val geom = new Geometry("Box", b)
protected lazy val mat = Material(
assetManager = assetManager,
path = "Common/MatDefs/Misc/Unshaded.j3md"
)
// def entity = _entity
// override def initialize(app: Application): Unit = {
// super.initialize(app)
// }
override def init() = {
entityData
.createEntity()
.withComponents(TestComponent())
// entityData.setComponents(x, TestComponent())
val es = entityData.getEntities(classOf[TestComponent])
println(es)
geom.setMaterial(mat)
rootNode.attachChild(geom)
// geom.foreach(e => {
// })
}
override def update(tpf: Float) = {
geom.rotate(0, 0.5f * tpf, 0)
geom.move(new Vector3f(0, 1 * tpf, 0))
}
override def cleanup(app: Application): Unit = {
// _entity.map(_.close())
// _entity = None
}
override def onEnable(): Unit = {}
override def onDisable(): Unit = {}
}
object Material {
def apply(
color: String = "Color",
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
assetManager: AssetManager,
path: String
): Material = {
val mat =
new Material(assetManager, path)
mat.setColor(color, colorType)
mat
}
}
@main
def main(): MyBaseState = new TestAppState()
/*</script>*/ /*<generated>*/
def $main() = { scala.Iterator[String]() }
override def toString = "hello2"
/*</generated>*/
}

View File

@ -91,8 +91,8 @@ lazy val root = (project in file(".")).settings(
"com.beachape" %% "enumeratum-circe" % "1.6.1", "com.beachape" %% "enumeratum-circe" % "1.6.1",
"com.lihaoyi" %% "os-lib" % "0.7.1", "com.lihaoyi" %% "os-lib" % "0.7.1",
// "com.jayfella" % "jme-jfx-11" % "1.1.5", // "com.jayfella" % "jme-jfx-11" % "1.1.5",
"com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0", // "com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0",
"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"

View File

@ -34,7 +34,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
private lazy val (defaultConsoleLogger, release1) = private lazy val (defaultConsoleLogger, release1) =
consoleLogger[IO](minLevel = Level.Debug) consoleLogger[IO](minLevel = Level.Debug)
.withAsync(timeWindow = 1.milliseconds) .withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated .allocated
.unsafeRunSync() .unsafeRunSync()
@ -43,7 +43,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
"application-log-2.log", "application-log-2.log",
Formatter.json, Formatter.json,
minLevel = Level.Debug minLevel = Level.Debug
).withAsync(timeWindow = 1.milliseconds) ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated .allocated
.unsafeRunSync() .unsafeRunSync()
@ -52,7 +52,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
"eventbus.log", "eventbus.log",
Formatter.json, Formatter.json,
minLevel = Level.Debug minLevel = Level.Debug
).withAsync(timeWindow = 1.milliseconds) ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated .allocated
.unsafeRunSync() .unsafeRunSync()
@ -77,7 +77,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
// "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler" // "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"
// ) => // ) =>
// 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.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) |+| mainFileLogger

View File

@ -2,19 +2,19 @@ package wow.doge.mygame
import scala.concurrent.duration._ import scala.concurrent.duration._
import _root_.monix.bio.BIOApp
import _root_.monix.bio.Task
import _root_.monix.bio.UIO
import akka.util.Timeout import akka.util.Timeout
import cats.effect.ExitCode import cats.effect.ExitCode
import cats.effect.Resource
import cats.implicits._ import cats.implicits._
import com.softwaremill.macwire._ 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 wow.doge.mygame.game.GameAppResource
import _root_.monix.bio.BIOApp
import _root_.monix.bio.Task
import _root_.monix.bio.UIO
import cats.effect.Resource
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 {
@ -25,15 +25,16 @@ object Main extends BIOApp with MainModule {
def appResource(consoleStream: GenericConsoleStream[TextArea]) = def appResource(consoleStream: GenericConsoleStream[TextArea]) =
for { for {
logger <- logger <-
consoleLogger().withAsync(timeWindow = 1.milliseconds) |+| consoleLogger().withAsync(
timeWindow = 1.milliseconds,
maxBufferSize = Some(2000)
) |+|
fileLogger( fileLogger(
"application-log-1.log", "application-log-1.log",
Formatter.json Formatter.json
).withAsync(timeWindow = 1.milliseconds) ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
jmeScheduler <- jMESchedulerResource jmeScheduler <- jMESchedulerResource
actorSystem <- actorSystemResource2(logger) actorSystem <- actorSystemResource(logger)
// consoleTextArea <- Resource.liftF(Task(new TextArea()))
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
gameApp <- { gameApp <- {
// new BulletAppState() // new BulletAppState()
// bas.setThreadingType(Thr) // bas.setThreadingType(Thr)
@ -61,7 +62,7 @@ object Main extends BIOApp with MainModule {
lazy val consoleStream = GenericConsoleStream.textAreaStream() lazy val consoleStream = GenericConsoleStream.textAreaStream()
Console.withOut(consoleStream)( Console.withOut(consoleStream)(
appResource(consoleStream) appResource(consoleStream)
.use(_ => Task.unit) .use(_ => Task.unit >> Task(consoleStream.close()))
.onErrorHandle(_.printStackTrace()) .onErrorHandle(_.printStackTrace())
.as(ExitCode.Success) .as(ExitCode.Success)
) )

View File

@ -5,56 +5,54 @@ 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.concurrent.Ref 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.asset.plugins.ZipLocator import com.jme3.asset.plugins.ZipLocator
import com.jme3.bullet.BulletAppState import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager import com.jme3.input.InputManager
import com.jme3.renderer.Camera import com.jme3.renderer.Camera
import com.jme3.renderer.ViewPort
import com.jme3.scene.Node import com.jme3.scene.Node
import com.softwaremill.macwire._ import com.softwaremill.macwire._
import com.softwaremill.tagging._ import com.softwaremill.tagging._
import io.odin.Logger import io.odin.Logger
import monix.bio.Fiber
import monix.bio.IO import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.GameApp2
import wow.doge.mygame.game.nodes.PlayerTag
import wow.doge.mygame.game.nodes.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import wow.doge.mygame.subsystems.events.EventsModule2
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import com.jme3.renderer.ViewPort
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
import wow.doge.mygame.launcher.Launcher
import wow.doge.mygame.executors.Schedulers
import scalafx.application.JFXApp.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.Button
import scalafx.scene.layout.StackPane
import scalafx.scene.paint.Color
import scalafx.scene.shape.Rectangle
import scalafx.Includes._
import scala.concurrent.duration._
import cats.effect.concurrent.Deferred
import monix.bio.Fiber
import wow.doge.mygame.launcher.Launcher.LauncherResult
import scalafx.scene.control.TextArea import scalafx.scene.control.TextArea
import com.jayfella.jme.jfx.JavaFxUI import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.GameApp
import wow.doge.mygame.game.GameAppActor
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.game.entities.PlayerController
import wow.doge.mygame.game.entities.PlayerControllerTags
import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import wow.doge.mygame.implicits._
import wow.doge.mygame.launcher.Launcher
import wow.doge.mygame.launcher.Launcher.LauncherResult
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.GenericConsoleStream import wow.doge.mygame.utils.GenericConsoleStream
import java.io.PrintStream
import EventsModule.GameEventBus
import wow.doge.mygame.game.entities.NpcMovementActor2
import wow.doge.mygame.game.entities.NpcActorSupervisor
import monix.execution.exceptions.DummyException
import com.jme3.bullet.control.BetterCharacterControl
class MainApp( class MainApp(
logger: Logger[Task], logger: Logger[Task],
gameApp: GameApp2, gameApp: GameApp,
spawnProtocol: ActorSystem[SpawnProtocol.Command], implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
jmeThread: monix.execution.Scheduler, jmeThread: monix.execution.Scheduler,
schedulers: Schedulers, schedulers: Schedulers,
consoleStream: GenericConsoleStream[TextArea] consoleStream: GenericConsoleStream[TextArea]
@ -68,19 +66,29 @@ class MainApp(
def gameInit: Task[Fiber[Throwable, Unit]] = def gameInit: Task[Fiber[Throwable, Unit]] =
for { for {
eventsModule <- Task(new EventsModule2(spawnProtocol)) eventsModule <- Task(new EventsModule(spawnProtocol))
playerMovementEventBus <- eventsModule.playerMovementEventBusTask playerMovementEventBus <- eventsModule.playerMovementEventBusTask
playerCameraEventBus <- eventsModule.playerCameraEventBusTask playerCameraEventBus <- eventsModule.playerCameraEventBusTask
mainEventBus <- eventsModule.mainEventBusTask
tickEventBus <- eventsModule.tickEventBusTask
gameAppActor <- AkkaUtils.spawnActorL2(
GameAppActor.Props(tickEventBus).create,
"gameAppActor"
)
_ <- gameAppActor !! GameAppActor.Start
gameAppFib <- gameApp.start.executeOn(jmeThread).start gameAppFib <- gameApp.start.executeOn(jmeThread).start
/** /**
* schedule a task to run on the JME thread and wait for it's completion * schedule a task to run on the JME thread and wait for it's completion
* before proceeding forward, as a signal that JME thread has been * before proceeding forward, as a signal that the JME thread has been
* initialized, otherwise we'll get NPEs trying to access the fields * initialized, otherwise we'll get NPEs trying to access the fields
* of the game app * of the game app
*/ */
res <- gameApp.enqueueL(() => Task("done")).flatten res <- gameApp.enqueueL(() => "done")
_ <- logger.info(s"Result = $res") _ <- 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 inputManager <- gameApp.inputManager
assetManager <- gameApp.assetManager assetManager <- gameApp.assetManager
stateManager <- gameApp.stateManager stateManager <- gameApp.stateManager
@ -89,41 +97,20 @@ class MainApp(
enqueueR <- Task(gameApp.enqueue _) enqueueR <- Task(gameApp.enqueue _)
viewPort <- gameApp.viewPort viewPort <- gameApp.viewPort
_ <- logger.info("before") _ <- logger.info("before")
// jfxUI <- Task(JavaFxUI.initialize(gameApp.app)) // jfxUI <- gameApp.jfxUI
// .executeOn(gameApp.scheduler) >> Task.sleep(500.millis) >> Task(
// JavaFxUI.getInstance()
// )
// .start
jfxUI <- gameApp.jfxUI
consoleTextArea <- Task(new TextArea { consoleTextArea <- Task(new TextArea {
text = "hello" text = "hello \n"
editable = false editable = false
wrapText = true wrapText = true
// maxHeight = 150 // maxHeight = 150
// maxWidth = 300 // maxWidth = 300
}) })
_ <- Task(consoleStream := consoleTextArea) // _ <- Task(consoleStream := consoleTextArea)
_ <- Task(jfxUI += consoleTextArea) // _ <- Task(jfxUI += consoleTextArea)
// consoleStream <- Task(
// GenericConsoleStream.textAreaStream(consoleTextArea)
// )
_ <- logger.info("after") _ <- logger.info("after")
bulletAppState <- Task(new BulletAppState()) bulletAppState <- Task(new BulletAppState())
_ <- Task(stateManager.attach(bulletAppState)) _ <- Task(stateManager.attach(bulletAppState))
_ <- logger.info("Initializing console stream") _ <- logger.info("Initializing console stream")
// _ <- Task(GenericConsoleStream.textAreaStream(consoleTextArea)).bracket(
// consoleStream =>
// Task { System.setOut(consoleStream) } >> wire[MainAppDelegate]
// .init(gameApp.scheduler, consoleStream)
// // consoleLogger
// // Console.withOut(consoleStream)(
// // wire[MainAppDelegate].init(gameApp.scheduler, consoleStream)
// // )
// )(stream => Task(stream.close()).onErrorHandle(_.printStackTrace()))
// _ <- Task { System.setOut(new PrintStream(consoleStream, true)) }
// _ <- Task {
// Console.withOut(consoleStream)(println("hello"))
// }
_ <- wire[MainAppDelegate].init(gameApp.scheduler) _ <- wire[MainAppDelegate].init(gameApp.scheduler)
} yield (gameAppFib) } yield (gameAppFib)
@ -131,7 +118,7 @@ class MainApp(
scriptSystem <- scriptSystemInit scriptSystem <- scriptSystemInit
/** /**
* Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI * Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI
* Without this, we get a "Toolkit already initialized" excResult. The launch button * 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 * in the launcher completes the signal. The game init process which listens for this
* signal can then continue * signal can then continue
*/ */
@ -141,15 +128,16 @@ class MainApp(
launchResult <- launchSignal.get launchResult <- launchSignal.get
_ <- cancelToken.cancel _ <- cancelToken.cancel
_ <- _ <-
/**
* User chose to quit
*/
if (launchResult == LauncherResult.Exit) if (launchResult == LauncherResult.Exit)
logger.info("Exiting") logger.info("Exiting")
else gameInit.flatMap(_.join) /**
// _ <- Task.sleep(2000.millis) * User chose launch. Wait for game window to close
// gameAppFib <- gameInit */
/** else
* Wait for game window to close gameInit.flatMap(_.join)
*/
// _ <- gameAppFib.join
} yield () } yield ()
} }
@ -157,27 +145,27 @@ class MainApp(
* Class with all dependencies in one place for easy wiring * Class with all dependencies in one place for easy wiring
*/ */
class MainAppDelegate( class MainAppDelegate(
gameApp: GameApp2, gameApp: GameApp,
spawnProtocol: ActorSystem[SpawnProtocol.Command], implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
loggerL: Logger[Task], loggerL: Logger[Task],
// eventBuses: EventsModule2
playerMovementEventBus: ActorRef[ playerMovementEventBus: ActorRef[
EventBus.Command[EntityMovementEvent.PlayerMovementEvent] EventBus.Command[PlayerMovementEvent]
], ],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
tickEventBus: GameEventBus[TickEvent],
inputManager: InputManager, inputManager: InputManager,
assetManager: AssetManager, assetManager: AssetManager,
stateManager: AppStateManager, stateManager: AppStateManager,
camera: Camera, camera: Camera,
viewPort: ViewPort, viewPort: ViewPort,
enqueueR: Function1[() => Unit, Unit], enqueueR: Function1[() => Unit, Unit],
rootNode: Ref[Task, Node], rootNode: Node @@ GameAppTags.RootNode,
bulletAppState: BulletAppState bulletAppState: BulletAppState
)(implicit )(implicit
@annotation.unused timeout: Timeout, @annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler @annotation.unused scheduler: Scheduler
) { ) {
lazy val physicsSpace = bulletAppState.physicsSpace
def init( def init(
appScheduler: monix.execution.Scheduler appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea] // consoleStream: GenericConsoleStream[TextArea]
@ -186,11 +174,11 @@ class MainAppDelegate(
_ <- loggerL.info("Initializing Systems") _ <- loggerL.info("Initializing Systems")
_ <- Task( _ <- Task(
assetManager.registerLocator( assetManager.registerLocator(
(os.rel / "assets" / "town.zip"), os.rel / "assets" / "town.zip",
classOf[ZipLocator] classOf[ZipLocator]
) )
) )
_ <- loggerL.info("test hmm") _ <- loggerL.info("test")
// _ <- Task(consoleStream.println("text")) // _ <- Task(consoleStream.println("text"))
_ <- DefaultGameLevel(assetManager, viewPort) _ <- DefaultGameLevel(assetManager, viewPort)
.addToGame( .addToGame(
@ -198,23 +186,91 @@ class MainAppDelegate(
bulletAppState.physicsSpace bulletAppState.physicsSpace
) )
.executeOn(appScheduler) .executeOn(appScheduler)
_ <- createPlayerController(appScheduler).startAndForget.onErrorRestart(3) _ <- createPlayerController(appScheduler)
.absorbWith(e => DummyException("boom"))
.onErrorRestart(3)
johnActor <- createNpc(appScheduler, "John").executeOn(appScheduler)
_ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
_ <- wire[GameInputHandler.Props].begin.onErrorRestart(3) _ <- wire[GameInputHandler.Props].begin.onErrorRestart(3)
} yield () } yield ()
def createPlayerController( def createPlayerController(
appScheduler: monix.execution.Scheduler appScheduler: monix.execution.Scheduler
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ],
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
): IO[PlayerController.Error, Unit] = { ): IO[PlayerController.Error, Unit] = {
@annotation.unused
val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.ZERO
@annotation.unused
val playerNode = None.taggedWith[PlayerTag]
@annotation.unused
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
wire[PlayerController.Props].create val playerPhysicsControl =
PlayerController.Defaults.defaultPlayerPhysicsControl
.taggedWith[PlayerControllerTags.PlayerTag]
val camNode =
PlayerController.Defaults
.defaultCamerNode(camera, playerPos)
.taggedWith[PlayerControllerTags.PlayerCameraNode]
val mbPlayerNode = PlayerController.Defaults
.defaultPlayerNode(
assetManager,
modelPath,
playerPos,
camNode,
playerPhysicsControl
)
for {
playerNode <- IO.fromEither(mbPlayerNode)
_ <- wire[PlayerController.Props].create
} yield ()
} }
def createNpc(
appScheduler: monix.execution.Scheduler,
npcName: String
) =
// : IO[PlayerController.Error, Unit] =
{
val initialPos = ImVector3f(100, 0, 0)
// val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
lazy val npcPhysicsControl =
new BetterCharacterControl(1f, 2.1f, 10f)
// .withJumpForce(ImVector3f(0, 5f, 0))
// val npcMovementActor = AkkaUtils.spawnActorL2(
// new NpcMovementActor2.Props(
// initialPos,
// tickEventBus,
// npcPhysicsControl
// ).create,
// s"${npcName}-npcMovementActor"
// )
lazy val mbNpcNode = PlayerController.Defaults.defaultNpcNode(
assetManager,
initialPos,
npcPhysicsControl,
"John"
)
val npcActorTask = AkkaUtils.spawnActorL2(
NpcActorSupervisor
.Props(
new NpcMovementActor2.Props(
enqueueR,
initialPos,
tickEventBus,
npcPhysicsControl
).create,
npcName,
initialPos
)
.create,
s"${npcName}-npcMovementActorSupervisor"
)
// .taggedWith[PlayerControllerTags.PlayerTag]
for {
npcNode <- IO.fromEither(mbNpcNode)
npcActor <- npcActorTask
_ <- IO {
physicsSpace += npcPhysicsControl
physicsSpace += npcNode
rootNode += npcNode
}
} yield (npcActor)
}
} }

View File

@ -5,11 +5,10 @@ import cats.effect.Resource
import io.odin.Logger import io.odin.Logger
import monix.bio.Task import monix.bio.Task
import wow.doge.mygame.executors.ExecutorsModule import wow.doge.mygame.executors.ExecutorsModule
import wow.doge.mygame.game.GameModule
trait MainModule extends GameModule with ExecutorsModule { trait MainModule extends ExecutorsModule {
def actorSystemResource2( 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(logger.info("Creating Actor System") >> Task {
@ -18,8 +17,10 @@ trait MainModule extends GameModule with ExecutorsModule {
name = "GameActorSystem" name = "GameActorSystem"
) )
})(sys => })(sys =>
logger.info("Shutting down actor system") >> Task( for {
sys.terminate() _ <- Task(sys.terminate())
) _ <- Task.fromFuture(sys.whenTerminated)
_ <- logger.info("Actor System Terminated")
} yield ()
) )
} }

View File

@ -1,132 +1,191 @@
package wow.doge.mygame.game package wow.doge.mygame.game
import scala.collection.immutable.Queue import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager
import com.jme3.app.SimpleApplication import com.jme3.asset.AssetManager
import com.jme3.app.state.AppState import com.jme3.input.InputManager
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.softwaremill.tagging._
import com.typesafe.scalalogging.{Logger => SLogger}
import io.odin.Logger
import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.execution.CancelableFuture import monix.catnap.ConcurrentChannel
import monix.execution.Scheduler import monix.catnap.ConsumerF
import monix.execution.atomic.Atomic import monix.catnap.Semaphore
import monix.execution.{CancelablePromise => Promise} import monix.eval.Coeval
import monix.reactive.MulticastStrategy import wow.doge.mygame.game.subsystems.ui.JFxUI
import monix.reactive.Observable
import monix.reactive.subjects.ConcurrentSubject
import wow.doge.mygame.executors.GUIExecutorService
import wow.doge.mygame.executors.Schedulers
class GameApp( sealed trait Error
schedulers: Schedulers, case object FlyCamNotExists extends Error
appStates: AppState*
) extends SimpleApplication(appStates: _*) { object GameAppTags {
import GameApp._ sealed trait RootNode
sealed trait GuiNode
}
class GameApp(logger: Logger[Task], val app: SimpleAppExt) {
import Ops._
def stateManager: Task[AppStateManager] = Task(app.getStateManager())
def inputManager: Task[InputManager] = Task(app.getInputManager())
def assetManager: Task[AssetManager] = Task(app.getAssetManager())
def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
def addToGuiNode = guiNode.flatMap(rn => Task(new AddToNode(rn)))
def flyCam =
IO(app.getFlyByCamera()).onErrorHandleWith(_ =>
IO.raiseError(FlyCamNotExists)
)
def camera = Task(app.getCamera())
def viewPort = Task(app.getViewPort())
def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
// def rootNode2 = SynchedObject(app.getRootNode())
def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn)))
def enqueue(cb: () => Unit) =
app.enqueue(new Runnable {
override def run() = cb()
})
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
def start = Task(app.start())
def stop = Task(app.stop())
def scheduler = app.scheduler
def jfxUI = JFxUI(app)
}
object GameApp {
class WrappedNode(node: Node, lock: Semaphore[Task]) {
def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
}
/** /**
* A non blocking synchronized queue using an immutable scala queue and monix's atomic class * Synchronization wrapper for a mutable object
*
* @param obj the mutable object
* @param lock lock for synchronization
*/ */
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]]) class SynchedObject[A](obj: A, lock: Semaphore[Task]) {
def modify(f: A => Unit): Task[Unit] =
lock.withPermit(Task(f(obj)))
private val tickSubject = def flatModify(f: A => Task[Unit]): Task[Unit] =
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)( lock.withPermit(f(obj))
schedulers.async
)
def tickObservable: Observable[Float] = tickSubject def get: Task[A] = lock.withPermit(Task(obj))
override def simpleInitApp(): Unit = {}
override def simpleUpdate(tpf: Float): Unit = {
tickSubject.onNext(tpf)
} }
override def stop(): Unit = { object SynchedObject {
tickSubject.onComplete() def apply[A](obj: A) =
super.stop() Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock)))
} }
def enqueueScala[T](cb: () => T): CancelableFuture[T] = { }
val p = Promise[T]()
taskQueue2.transform(_ :+ MyTask(p, cb))
p.future
}
def enqueueL[T](cb: () => T): Task[T] = object Ops {
Task.deferFuture(enqueueScala(cb)) final class AddToNode[T <: Node](private val node: T) extends AnyVal {
override protected def runQueuedTasks(): Unit = { /**
taskQueue2.transform { current => * Pure version
current.dequeueOption.fold(current) { */
case (MyTask(p, cb), updated) => def apply(spatial: Spatial)(implicit logger: Logger[Task]) =
p.success(cb()) logger.debug(
updated s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}"
) >> Task(node.attachChild(spatial))
/**
* Impure version
*/
def apply(spatial: Spatial)(implicit logger: SLogger) =
Coeval {
logger.debug(
s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}"
)
node.attachChild(spatial)
} }
}
}
object SpawnSystem {
sealed trait Result
final case object Ok extends Result
sealed trait Complete
final case object Complete extends Complete
sealed trait SpawnRequest
final case class SpawnSpatial(node: Node) extends SpawnRequest
final case class SpawnRequestWrapper(
spawnRequest: SpawnRequest,
result: Deferred[Task, Result]
)
def apply(logger: Logger[Task]) =
for {
spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper]
spawnSystem <- Task(new SpawnSystem(logger, spawnChannel))
consumer <-
spawnChannel.consume
.use(consumer => spawnSystem.receive(consumer))
.startAndForget
} yield (spawnSystem)
}
class SpawnSystem(
logger: Logger[Task],
spawnChannel: ConcurrentChannel[
Task,
SpawnSystem.Complete,
SpawnSystem.SpawnRequestWrapper
]
) {
import SpawnSystem._
for {
spawnSystem <- SpawnSystem(logger)
n <- spawnSystem.request(SpawnSpatial(new Node("Test")))
} yield ()
// val spawnChannel = ConcurrentChannel[Task].of[Result, SpawnRequest]
private def receive(
consumer: ConsumerF[Task, Complete, SpawnRequestWrapper]
): Task[Unit] =
consumer.pull.flatMap {
case Right(message) =>
for {
_ <-
logger
.debug(s"Received spawn request $message")
_ <- handleSpawn(message)
} yield receive(consumer)
case Left(r) =>
logger.info("Worker $$index is done!")
} }
super.runQueuedTasks() private def handleSpawn(spawnRequestWrapper: SpawnRequestWrapper) =
} spawnRequestWrapper match {
case SpawnRequestWrapper(spawnRequest, result) =>
spawnRequest match {
case SpawnSpatial(spatial) =>
logger.debug(
s"Spawning spatial with name ${spatial.getName()}"
) >> result
.complete(Ok)
object JMEExecutorService extends GUIExecutorService { }
override def execute(command: Runnable): Unit = }
enqueueScala(() => command.run())
// enqueue(command)
// new SingleThreadEventExecutor()
// sys.addShutdownHook(JMEExecutorService.shutdown())
}
lazy val scheduler = Scheduler(JMEExecutorService) def request(spawnRequest: SpawnRequest) =
for {
d <- Deferred[Task, Result]
_ <- spawnChannel.push(SpawnRequestWrapper(spawnRequest, d))
res <- d.get
} yield (res)
def stop = spawnChannel.halt(Complete)
} }
object GameApp {
private[game] case class MyTask[T](p: Promise[T], cb: () => T)
}
// val ship = ed.createEntity()
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
// val mbState = Try(
// stateManager()
// .state[TestAppState]()
// .entity
// ).toOption.flatten.toRight(println("empty"))
// // .flatMap(_.entity)
// val x = mbState.flatMap(
// _.query
// .filter[TestComponent]("name", new Object())
// // .filterOr[TestEntity](
// // Filters
// // .fieldEquals(classOf[TestEntity], "", null)
// // )
// .component[Tag]()
// .component[TestComponent]()
// .result
// .toRight(println("failed"))
// )
// rootNode
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// Future(println("hello"))(jmeEC(this))
// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask(
// SpawnProtocol.Spawn(
// behavior = Greeter(),
// name = "listener",
// DispatcherSelector.fromConfig("jme-dispatcher"),
// _
// )
// )
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
// Task(Promise[T]()).flatMap { p =>
// Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >>
// Task.fromCancelablePromise(p)
// }
// Task.fromCancelablePromise {
// val p = Promise[T]()
// taskQueue2.transform(_ :+ MyTask(p, cb))
// p
// }

View File

@ -1,71 +0,0 @@
package wow.doge.mygame.game
import cats.effect.concurrent.Ref
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
import com.jme3.input.InputManager
import monix.bio.IO
import monix.bio.Task
import com.jme3.scene.Node
import monix.catnap.Semaphore
import com.jme3.scene.Spatial
import wow.doge.mygame.game.GameApp2.SynchedObject
import wow.doge.mygame.game.subsystems.ui.JFxUI
sealed trait Error
case object FlyCamNotExists extends Error
class GameApp2(val app: GameApp) {
def stateManager: Task[AppStateManager] = Task(app.getStateManager())
def inputManager: Task[InputManager] = Task(app.getInputManager())
def assetManager: Task[AssetManager] = Task(app.getAssetManager())
def guiNode = Ref[Task].of(app.getGuiNode())
def flyCam =
IO(app.getFlyByCamera()).onErrorHandleWith(_ =>
IO.raiseError(FlyCamNotExists)
)
def camera = Task(app.getCamera())
def viewPort = Task(app.getViewPort())
def rootNode = Ref[Task].of(app.getRootNode())
def rootNode2 = SynchedObject(app.getRootNode())
def enqueue(cb: () => Unit) =
app.enqueue(new Runnable {
override def run() = cb()
})
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
def start = Task(app.start())
def stop = Task(app.stop())
def scheduler = app.scheduler
def jfxUI = JFxUI(app)
}
object GameApp2 {
class WrappedNode(node: Node, lock: Semaphore[Task]) {
def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
}
/**
* Synchronization wrapper for a mutable object
*
* @param obj the mutable object
* @param lock lock for synchronization
*/
class SynchedObject[A](obj: A, lock: Semaphore[Task]) {
def modify(f: A => Unit): Task[Unit] =
lock.withPermit(Task(f(obj)))
def flatModify(f: A => Task[Unit]): Task[Unit] =
lock.withPermit(f(obj))
def get: Task[A] = lock.withPermit(Task(obj))
}
object SynchedObject {
def apply[A](obj: A) =
Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock)))
}
}

View File

@ -1,53 +1,83 @@
package wow.doge.mygame.game package wow.doge.mygame.game
import akka.actor.typed.ActorRef import scala.concurrent.duration._
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import io.odin.Logger import wow.doge.mygame.game.TickGenerator.Send
import monix.bio.Task import wow.doge.mygame.game.entities.GenericTimerActor
import wow.doge.mygame.events.Events import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.PhysicsTick
object GameAppActor { object GameAppActor {
sealed trait Command sealed trait Command
case object ApplicationStarted extends Command case object Start extends Command
case class ApplicationStartFailed(reason: String) extends Command case object Pause extends Command
case object Stop extends Command case object Stop extends Command
case class Props( case class Props(
app: GameApp, // app: SimpleAppExt,
akkaScheduler: Scheduler, // akkaScheduler: Scheduler,
schedulers: Schedulers, // schedulers: Schedulers,
spawnProtocol: ActorRef[SpawnProtocol.Command], // spawnProtocol: ActorRef[SpawnProtocol.Command],
loggerL: Logger[Task] // loggerL: Logger[Task]
tickEventBus: GameEventBus[TickEvent]
) { ) {
def create = def create =
Behaviors.setup[Command] { ctx => Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from GameAppActor") ctx.log.info("Hello from GameAppActor")
val renderTickGenerator =
ctx.spawn(
Behaviors
.supervise(renderTickGeneratorBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
"tickGeneratorActor"
)
val tickGeneratorTimer = ctx.spawn(
GenericTimerActor
.Props(renderTickGenerator, TickGenerator.Send, 10.millis)
.create,
"tickGeneratorTimer"
)
Behaviors.receiveMessage {
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
Behaviors.receiveMessage { msg =>
msg match {
case Stop =>
ctx.log.info("Received stop")
Behaviors.stopped
case ApplicationStarted =>
ctx.log.info("Application started")
Behaviors.same
case ApplicationStartFailed(reason) =>
ctx.log.error(
s"Failed to start application - $reason"
)
Behaviors.stopped
}
} }
} }
val renderTickGeneratorBehavior =
Behaviors.receiveMessage[TickGenerator.Command] {
case Send =>
tickEventBus ! EventBus.Publish(
TickEvent.RenderTick,
"tickGeneratorActor"
)
Behaviors.same
}
} }
} }
object TickGenerator {
sealed trait Command
case object Send extends Command
}
object SubscribingActor { object SubscribingActor {
def apply() = def apply() =
Behaviors.receive[Events.Tick.PhysicsTick.type] { (ctx, msg) => Behaviors.receive[PhysicsTick.type] { (ctx, msg) =>
ctx.log.debug(s"received event $msg") ctx.log.debug(s"received event $msg")
Behaviors.same Behaviors.same
} }

View File

@ -1,48 +1,24 @@
package wow.doge.mygame.game package wow.doge.mygame.game
import akka.actor.typed.ActorRef
import akka.actor.typed.SpawnProtocol
import cats.effect.Resource import cats.effect.Resource
import com.jme3.app.StatsAppState import com.jme3.app.StatsAppState
import com.jme3.system.AppSettings import com.jme3.system.AppSettings
import io.odin.Logger import io.odin.Logger
import monix.bio.Fiber
import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.execution.Scheduler import monix.execution.Scheduler
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.subsystems.input.GameInputHandler
class GameAppResource( class GameAppResource(
logger: Logger[Task], logger: Logger[Task],
jmeScheduler: Scheduler, jmeScheduler: Scheduler,
schedulers: Schedulers schedulers: Schedulers
) { ) {
def get2: Resource[Task, (GameApp2, Fiber[Throwable, Unit])] =
Resource.make(
for {
_ <- logger.info("Creating game app")
app <- Task(new GameApp(schedulers, new StatsAppState()))
app2 <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
settings.setUseInput(true)
// new FlyCamAppState
// settings.setFrameRate(250)
app.setSettings(settings)
// JMERunner.runner = app
// app
new GameApp2(app)
}
fib <- app2.start.executeOn(jmeScheduler).start
} yield (app2 -> fib)
)(logger.info("Closing game app") >> _._2.cancel)
def get: Resource[Task, GameApp2] = def get: Resource[Task, GameApp] =
Resource.make( Resource.make(
for { for {
_ <- logger.info("Creating game app") _ <- logger.info("Creating game app")
app <- Task(new GameApp(schedulers, new StatsAppState())) appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState()))
app2 <- Task { app <- Task {
val settings = new AppSettings(true) val settings = new AppSettings(true)
settings.setVSync(true) settings.setVSync(true)
@ -50,48 +26,11 @@ class GameAppResource(
* disables the launcher * disables the launcher
* We'll be making our own launcher anyway * We'll be making our own launcher anyway
*/ */
app.setShowSettings(false) appExt.setShowSettings(false)
app.setSettings(settings) appExt.setSettings(settings)
// JMERunner.runner = app // JMERunner.runner = app
new GameApp2(app) new GameApp(logger, appExt)
} }
// fib <- app2.start.executeOn(jmeScheduler).start } yield (app)
} yield (app2)
)(_ => logger.info("Closing game app")) )(_ => logger.info("Closing game app"))
} }
trait GameModule {
def gameAppResource(
logger: Logger[Task],
jmeScheduler: Scheduler,
schedulers: Schedulers
): Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
Resource.make(
(for {
_ <- logger.info("Creating game app")
app <- Task(new GameApp(schedulers))
_ <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
// settings.setFrameRate(250)
app.setSettings(settings)
// JMERunner.runner = app
app
}
fib <- Task(app.start()).executeOn(jmeScheduler).start
} yield (app -> fib))
)(_._2.cancel)
def inputHandlerSystemResource(
props: GameInputHandler.Props
): Resource[Task, Task[Unit]] =
Resource.liftF {
Task.evalAsync(props.begin)
}
def gameSystemsResource(
spawnProtocol: ActorRef[SpawnProtocol.Command],
gameSystems: Task[Unit]*
): Resource[Task, List[Unit]] =
Resource.liftF(IO.defer(Task.parSequence(gameSystems)))
}

View File

@ -1,106 +0,0 @@
package wow.doge.mygame.game
import scala.concurrent.duration._
import akka.actor.typed.ActorRef
import akka.actor.typed.ActorSystem
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import cats.effect.concurrent.Ref
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
import com.jme3.asset.plugins.ZipLocator
import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager
import com.jme3.renderer.Camera
import com.jme3.scene.Node
import com.softwaremill.macwire._
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.IO
import monix.bio.Task
import monix.reactive.Consumer
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.nodes.PlayerTag
import wow.doge.mygame.game.nodes.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.IOUtils
class GameSystemsInitializer(
spawnProtocol: ActorSystem[SpawnProtocol.Command],
loggerL: Logger[Task],
// eventBuses: EventsModule2
playerMovementEventBus: ActorRef[
EventBus.Command[EntityMovementEvent.PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
inputManager: InputManager,
assetManager: AssetManager,
stateManager: AppStateManager,
camera: Camera,
enqueueR: Function1[() => Unit, Unit],
appScheduler: monix.execution.Scheduler,
rootNode: Ref[Task, Node]
) {
implicit val timeout: Timeout = Timeout(1.second)
import GameSystemsInitializer._
implicit val akkaScheduler: Scheduler = spawnProtocol.scheduler
// lazy val inputManager = app.inputManager
// lazy val assetManager = app.assetManager
lazy val bulletAppState = new BulletAppState()
// lazy val playerMovementEventBus = eventBuses.playerMovementEventBusTask
val init =
for {
_ <- loggerL.info("Initializing Systems")
// playerMovementEventBus <- playerMovementEventBusTask
_ <- Task(stateManager.attach(bulletAppState))
_ <- Task(
assetManager.registerLocator(
// "src/main/resources/assets/town.zip",
(os.rel / "assets" / "town.zip"),
classOf[ZipLocator]
)
)
// _ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState))
_ <- wireWith(createPlayerController _).startAndForget
_ <- wire[GameInputHandler.Props].begin
} yield ()
def createPlayerController(
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ],
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
): IO[PlayerController.Error, Unit] = {
@annotation.unused
val playerPos = ImVector3f.ZERO
@annotation.unused
val playerNode = None.taggedWith[PlayerTag]
@annotation.unused
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
wire[PlayerController.Props].create
}
}
object GameSystemsInitializer {
def playerMovementActorTickConsumer(
playerMovementActor: ActorRef[ImMovementActor.Command]
) =
Consumer
.foreachTask[Float](tpf =>
IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick)
)
// .mapTask()
}

View File

@ -0,0 +1,132 @@
package wow.doge.mygame.game
import scala.collection.immutable.Queue
import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState
import monix.bio.Task
import monix.execution.CancelableFuture
import monix.execution.Scheduler
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.Schedulers
class SimpleAppExt(
schedulers: Schedulers,
appStates: AppState*
) extends SimpleApplication(appStates: _*) {
import SimpleAppExt._
/**
* A non blocking synchronized queue using an immutable scala queue and monix's atomic class
*/
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
private val tickSubject =
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
schedulers.async
)
def tickObservable: Observable[Float] = tickSubject
override def simpleInitApp(): Unit = {}
override def simpleUpdate(tpf: Float): Unit = {
tickSubject.onNext(tpf)
}
override def stop(): Unit = {
tickSubject.onComplete()
super.stop()
}
def enqueueScala[T](cb: () => T): CancelableFuture[T] = {
val p = Promise[T]()
taskQueue2.transform(_ :+ MyTask(p, cb))
p.future
}
def enqueueL[T](cb: () => T): Task[T] =
Task.deferFuture(enqueueScala(cb))
override protected def runQueuedTasks(): Unit = {
taskQueue2.transform { current =>
current.dequeueOption.fold(current) {
case (MyTask(p, cb), updated) =>
p.success(cb())
updated
}
}
super.runQueuedTasks()
}
object JMEExecutorService extends GUIExecutorService {
override def execute(command: Runnable): Unit =
enqueueScala(() => command.run())
// enqueue(command)
// new SingleThreadEventExecutor()
// sys.addShutdownHook(JMEExecutorService.shutdown())
}
lazy val scheduler = Scheduler(JMEExecutorService)
}
object SimpleAppExt {
private[game] case class MyTask[T](p: Promise[T], cb: () => T)
}
// val ship = ed.createEntity()
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
// val mbState = Try(
// stateManager()
// .state[TestAppState]()
// .entity
// ).toOption.flatten.toRight(println("empty"))
// // .flatMap(_.entity)
// val x = mbState.flatMap(
// _.query
// .filter[TestComponent]("name", new Object())
// // .filterOr[TestEntity](
// // Filters
// // .fieldEquals(classOf[TestEntity], "", null)
// // )
// .component[Tag]()
// .component[TestComponent]()
// .result
// .toRight(println("failed"))
// )
// rootNode
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// Future(println("hello"))(jmeEC(this))
// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask(
// SpawnProtocol.Spawn(
// behavior = Greeter(),
// name = "listener",
// DispatcherSelector.fromConfig("jme-dispatcher"),
// _
// )
// )
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
// Task(Promise[T]()).flatMap { p =>
// Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >>
// Task.fromCancelablePromise(p)
// }
// Task.fromCancelablePromise {
// val p = Promise[T]()
// taskQueue2.transform(_ :+ MyTask(p, cb))
// p
// }

View File

@ -0,0 +1,225 @@
package wow.doge.mygame.game.entities
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.subsystems.events.Event
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedLeft
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedUp
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedRight
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedDown
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.implicits._
import akka.util.Timeout
import scala.concurrent.duration._
import scala.util.Success
import scala.util.Failure
object NpcActorSupervisor {
sealed trait Command
case class Move(pos: ImVector3f) extends Command
private case class UpdatePosition(pos: ImVector3f) extends Command
private case class LogError(err: Throwable) extends Command
case object MovementTick extends Command
final case class Props(
npcMovementActorBehavior: Behavior[NpcMovementActor2.Command],
npcName: String,
initialPos: ImVector3f
) {
def create =
Behaviors.setup[Command] { ctx =>
val npcMovementActor = ctx.spawn(
(npcMovementActorBehavior),
s"npc-${npcName}-NpcMovementActor"
)
new NpcActorSupervisor(ctx, this)
.idle(State(npcMovementActor, initialPos))
}
}
final case class State(
npcMovementActor: ActorRef[NpcMovementActor2.Command],
currentPos: ImVector3f
)
}
class NpcActorSupervisor(
ctx: ActorContext[NpcActorSupervisor.Command],
props: NpcActorSupervisor.Props
) {
import NpcActorSupervisor._
implicit val timeout = Timeout(1.second)
def idle(state: State): Behavior[NpcActorSupervisor.Command] =
Behaviors.receiveMessage[Command] {
case Move(pos) => {
state.npcMovementActor ! NpcMovementActor2.Move(pos)
val movementTimer = ctx.spawn(
GenericTimerActor.Props(ctx.self, MovementTick, 100.millis).create,
s"npc-${props.npcName}-NpcActorTimer"
)
movementTimer ! GenericTimerActor.Start
moving(state, pos, movementTimer)
}
case LogError(err) =>
ctx.log.warn(err.getMessage())
Behaviors.same
case _ => Behaviors.unhandled
}
def moving(
state: State,
targetPos: ImVector3f,
movementTimer: ActorRef[GenericTimerActor.Command]
): Behavior[NpcActorSupervisor.Command] =
Behaviors.receiveMessagePartial[Command] {
case LogError(err) =>
ctx.log.warn(err.getMessage())
Behaviors.same
case Move(pos) => moving(state, pos, movementTimer)
case UpdatePosition(pos) =>
ctx.log.trace("Current pos = " + state.currentPos.toString())
moving(state.copy(currentPos = pos), targetPos, movementTimer)
case MovementTick =>
val dst = ImVector3f.dst(targetPos, state.currentPos)
if (dst <= 10f) {
state.npcMovementActor ! NpcMovementActor2.StopMoving
movementTimer ! GenericTimerActor.Stop
idle(state)
} else {
// ctx.log.debug("Difference = " + dst.toString())
// ctx.log.debug("Current pos = " + state.currentPos.toString())
ctx.ask(state.npcMovementActor, NpcMovementActor2.AskPosition(_)) {
case Success(value) =>
UpdatePosition(value)
case Failure(exception) => LogError(exception)
}
// Behaviors.same
moving(state, targetPos, movementTimer)
}
}
}
object NpcMovementActor2 {
sealed trait Command
case class AskPosition(replyTo: ActorRef[ImVector3f]) extends Command
case object StopMoving extends Command
case class Move(target: ImVector3f) extends Command
final class Props[T: CanMove](
val enqueueR: Function1[() => Unit, Unit],
val initialPos: ImVector3f,
val tickEventBus: GameEventBus[TickEvent],
val movable: T
) {
def create =
Behaviors.setup[Command] { ctx =>
new NpcMovementActor2(ctx, this).receive(State(initialPos))
}
}
final case class State(currentPos: ImVector3f)
}
class NpcMovementActor2[T](
ctx: ActorContext[NpcMovementActor2.Command],
props: NpcMovementActor2.Props[T]
) {
import NpcMovementActor2._
def receive(
state: State
)(implicit cm: CanMove[T]): Behavior[NpcMovementActor2.Command] =
Behaviors.receiveMessage[Command] {
case AskPosition(replyTo) =>
replyTo ! cm.location(props.movable)
Behaviors.same
case Move(target: ImVector3f) =>
props.enqueueR(() =>
cm.move(props.movable, (target - state.currentPos) * 0.005f)
)
receive(state = state.copy(currentPos = cm.location(props.movable)))
case StopMoving =>
ctx.log.debug(
"Position at Stop = " + cm.location(props.movable).toString
)
props.enqueueR(() => cm.stop(props.movable))
receive(state = state.copy(currentPos = cm.location(props.movable)))
}
}
object NpcMovementActor {
sealed trait Command
final case class Props(
imMovementActorBehavior: Behavior[ImMovementActor.Command],
npcName: String,
// movementActor: ActorRef[ImMovementActor.Command],
mainEventBus: ActorRef[
EventBus.Command[Event]
],
tickEventBus: GameEventBus[TickEvent]
) {
def create: Behavior[Command] =
Behaviors.setup { ctx =>
val movementActor = ctx.spawn(
Behaviors
.supervise(imMovementActorBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
s"npc-${npcName}-MovementActorChild"
)
val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] {
case event: EntityMovementEvent =>
event match {
case MovedLeft(name, pressed) =>
if (name == npcName)
movementActor ! ImMovementActor.MovedLeft(pressed)
Behaviors.same
case MovedUp(name, pressed) =>
if (name == npcName)
movementActor ! ImMovementActor.MovedUp(pressed)
Behaviors.same
case MovedRight(name, pressed) =>
if (name == npcName)
movementActor ! ImMovementActor.MovedRight(pressed)
Behaviors.same
case MovedDown(name, pressed) =>
if (name == npcName)
movementActor ! ImMovementActor.MovedDown(pressed)
Behaviors.same
}
}
val npcMovementEl = ctx.spawn(
Behaviors
.supervise(npcMovementElBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
s"npc-${npcName}-MovementEventHandler"
)
val renderTickElBehavior =
Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
Behaviors.same
}
val renderTickEl =
ctx.spawn(
renderTickElBehavior,
s"npc-${npcName}-MovementTickListener"
)
mainEventBus ! EventBus.Subscribe(npcMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
Behaviors.receiveMessage { msg => Behaviors.same }
}
}
}

View File

@ -1,6 +1,7 @@
package wow.doge.mygame.game.nodes package wow.doge.mygame.game.entities
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.util.Random
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
@ -9,26 +10,27 @@ import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.TimerScheduler import akka.actor.typed.scaladsl.TimerScheduler
import com.jme3.scene.CameraNode
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.subsystems.events.EntityMovementEvent import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
object PlayerActorSupervisor { object PlayerActorSupervisor {
sealed trait Command sealed trait Command
final case class Props( final case class Props(
enqueueR: Function1[() => Unit, Unit],
camNode: CameraNode,
playerMovementEventBus: ActorRef[ playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent] EventBus.Command[PlayerMovementEvent]
], ],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
tickEventBus: GameEventBus[TickEvent],
imMovementActorBehavior: Behavior[ImMovementActor.Command],
playerCamELBehavior: Behavior[PlayerCameraEvent]
) { ) {
def create[T: CanMove](movable: T) = def create[T: CanMove](movable: T) =
Behaviors.logMessages( Behaviors.logMessages(
@ -44,59 +46,40 @@ object PlayerActorSupervisor {
lazy val movementActor = lazy val movementActor =
ctx.spawn( ctx.spawn(
Behaviors Behaviors
.supervise( .supervise(imMovementActorBehavior)
ImMovementActor
.Props(enqueueR, movable, playerMovementEventBus)
.create
)
.onFailure[Exception](SupervisorStrategy.restart), .onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActor" "playerMovementActorChild"
) )
lazy val playerMovementEventHandler = ctx.spawn( ctx.spawn(
Behaviors PlayerMovementActor
.supervise(PlayerMovementEventListener(movementActor)) .Props(movementActor, playerMovementEventBus, tickEventBus)
.onFailure[Exception](SupervisorStrategy.restart), .create,
"playerMovementEventHandler" "playerMovementAcor"
) )
lazy val movementActorTimer = ctx.spawn(
Behaviors
.supervise(MovementActorTimer(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActorTimer"
)
lazy val playerCameraHandler = { lazy val playerCameraHandler = {
ctx.spawn( ctx.spawn(
Behaviors Behaviors
.supervise( .supervise(playerCamELBehavior)
PlayerCameraEventListener(camNode, enqueueR)
)
.onFailure[Exception](SupervisorStrategy.restart), .onFailure[Exception](SupervisorStrategy.restart),
"playerCameraHandler" "playerCameraHandler"
) )
} }
//init actors //init actors
movementActorTimer ! MovementActorTimer.Start
playerMovementEventBus ! EventBus.Subscribe(
playerMovementEventHandler
)
playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler) playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler)
new PlayerActorSupervisor( new PlayerActorSupervisor(
ctx, ctx,
this, this,
Children(movementActor, playerMovementEventHandler) Children(movementActor)
).receive ).receive
} }
) )
} }
case class Children( case class Children(
movementActor: ActorRef[ImMovementActor.Command], movementActor: ActorRef[ImMovementActor.Command]
playerMovementEventHandler: ActorRef[
EntityMovementEvent.PlayerMovementEvent
]
) )
} }
class PlayerActorSupervisor[T: CanMove]( class PlayerActorSupervisor[T: CanMove](
@ -113,47 +96,40 @@ class PlayerActorSupervisor[T: CanMove](
} }
} }
object MovementActorTimer { object PlayerMovementActor {
sealed trait Command sealed trait Command
final case object Start extends Command final case class Props(
final case object Stop extends Command movementActor: ActorRef[ImMovementActor.Command],
private case object Send extends Command playerMovementEventBus: ActorRef[
case object TimerKey EventBus.Command[PlayerMovementEvent]
],
tickEventBus: GameEventBus[TickEvent]
) {
def apply(target: ActorRef[ImMovementActor.Command]) = def create: Behavior[Command] =
Behaviors.withTimers[Command] { timers => Behaviors.setup { ctx =>
new MovementActorTimer(timers, target).idle val playerMovementEl = ctx.spawn(
} Behaviors
} .supervise(PlayerMovementEventListener(movementActor))
class MovementActorTimer( .onFailure[Exception](SupervisorStrategy.restart),
timers: TimerScheduler[MovementActorTimer.Command], "playerMovementEventHandler"
target: ActorRef[ImMovementActor.Command] )
) { val renderTickElBehavior =
import MovementActorTimer._ Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
Behaviors.same
}
val renderTickEl =
ctx.spawn(renderTickElBehavior, "playerMovementTickListener")
val idle: Behavior[Command] = playerMovementEventBus ! EventBus.Subscribe(
Behaviors.receiveMessage { msg => playerMovementEl
msg match { )
case Start => tickEventBus ! EventBus.Subscribe(renderTickEl)
timers.startTimerWithFixedDelay(TimerKey, Send, (60f / 144).millis) Behaviors.receiveMessage { msg => Behaviors.same }
active
case _ => Behaviors.unhandled
} }
} }
val active: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Send =>
target ! ImMovementActor.Tick
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
idle
case _ => Behaviors.unhandled
}
}
} }
object GenericTimerActor { object GenericTimerActor {
@ -161,7 +137,7 @@ object GenericTimerActor {
final case object Start extends Command final case object Start extends Command
final case object Stop extends Command final case object Stop extends Command
private case object Send extends Command private case object Send extends Command
case object TimerKey case class TimerKey(seed: Long)
case class Props[T]( case class Props[T](
target: ActorRef[T], target: ActorRef[T],
@ -169,12 +145,13 @@ object GenericTimerActor {
timeInterval: FiniteDuration timeInterval: FiniteDuration
) { ) {
val create = Behaviors.withTimers[Command] { timers => val create = Behaviors.withTimers[Command] { timers =>
new GenericTimerActor(timers, this).idle new GenericTimerActor(timers, TimerKey(Random.nextLong()), this).idle
} }
} }
} }
class GenericTimerActor[T]( class GenericTimerActor[T](
timers: TimerScheduler[GenericTimerActor.Command], timers: TimerScheduler[GenericTimerActor.Command],
timerKey: GenericTimerActor.TimerKey,
props: GenericTimerActor.Props[T] props: GenericTimerActor.Props[T]
) { ) {
import GenericTimerActor._ import GenericTimerActor._
@ -182,7 +159,7 @@ class GenericTimerActor[T](
val idle: Behavior[Command] = val idle: Behavior[Command] =
Behaviors.receiveMessage { Behaviors.receiveMessage {
case Start => case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, props.timeInterval) timers.startTimerWithFixedDelay(timerKey, Send, props.timeInterval)
active active
case _ => Behaviors.unhandled case _ => Behaviors.unhandled
@ -194,7 +171,7 @@ class GenericTimerActor[T](
props.target ! props.messageToSend props.target ! props.messageToSend
Behaviors.same Behaviors.same
case Stop => case Stop =>
timers.cancel(TimerKey) timers.cancel(timerKey)
idle idle
} }
} }

View File

@ -1,4 +1,4 @@
package wow.doge.mygame.game.nodes package wow.doge.mygame.game.entities
import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors

View File

@ -0,0 +1,248 @@
package wow.doge.mygame.game.entities
import akka.actor.typed.ActorRef
import akka.actor.typed.Props
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import cats.implicits._
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.bullet.PhysicsSpace
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath
import com.jme3.renderer.Camera
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
import com.jme3.scene.Node
import com.jme3.scene.control.CameraControl.ControlDirection
import com.jme3.scene.shape.Box
import com.softwaremill.macwire._
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.IO
import monix.bio.Task
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.game.SimpleAppExt
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.MyMaterial
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils
object PlayerControllerTags {
sealed trait PlayerTag
sealed trait PlayerCameraNode
}
object PlayerController {
sealed trait Error
case class CouldNotCreatePlayerNode(reason: String) extends Error
case class GenericError(reason: String) extends Error
class Props(
enqueueR: Function1[() => Unit, Unit],
rootNode: Node @@ GameAppTags.RootNode,
loggerL: Logger[Task],
physicsSpace: PhysicsSpace,
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
spawnProtocol: ActorRef[SpawnProtocol.Command],
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
playerPhysicsControl: BetterCharacterControl,
appScheduler: monix.execution.Scheduler,
playerNode: Node @@ PlayerControllerTags.PlayerTag,
cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode,
tickEventBus: GameEventBus[TickEvent]
)(implicit timeout: Timeout, scheduler: Scheduler) {
val create: IO[Error, Unit] =
(for {
playerActor <- AkkaUtils.spawnActorL(
spawnProtocol,
"playerActorSupervisor",
new PlayerActorSupervisor.Props(
playerMovementEventBus,
playerCameraEventBus,
tickEventBus,
ImMovementActor
.Props(enqueueR, playerPhysicsControl)
.create,
wireWith(PlayerCameraEventListener.apply _)
).create(playerPhysicsControl)
)
_ <- IO {
physicsSpace += playerNode
physicsSpace += playerPhysicsControl
}
_ <- Task(rootNode += playerNode)
} yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
.executeOn(appScheduler)
}
def apply(
app: SimpleAppExt,
modelPath: os.RelPath,
cam: Camera
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
lazy val playerPos = ImVector3f.ZERO
lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
lazy val playerNode = new Node("PlayerNode")
.withChildren(
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
{
bulletAppState.physicsSpace += playerNode
bulletAppState.physicsSpace += playerPhysicsControl
}
{
app.rootNode += playerNode
}
playerPhysicsControl
}
object Defaults {
def defaultMesh = {
val b = Box(1, 1, 1)
val geom = Geometry("playerGeom", b)
geom
}
def defaultTexture(assetManager: AssetManager) =
MyMaterial(
assetManager = assetManager,
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
)
def defaultCamerNode(cam: Camera, playerPos: ImVector3f) =
new CameraNode("CameraNode", cam)
.withControlDir(ControlDirection.SpatialToCamera)
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
.withLookAt(playerPos, ImVector3f.UNIT_Y)
def defaultPlayerNode(
assetManager: AssetManager,
modelPath: os.RelPath,
playerPos: ImVector3f,
camNode: CameraNode,
playerPhysicsControl: BetterCharacterControl
) =
Either
.catchNonFatal(
Node("PlayerNode")
.withChildren(
camNode,
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
)
.map(_.taggedWith[PlayerControllerTags.PlayerTag])
.leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
def defaultNpcNode(
assetManager: AssetManager,
// modelPath: os.RelPath,
initialPos: ImVector3f,
npcPhysicsControl: BetterCharacterControl,
npcName: String
) =
Either
.catchNonFatal(
Node(npcName)
.withChildren(
// assetManager
// .loadModel(modelPath)
// .asInstanceOf[Node]
// .withRotate(0, FastMath.PI, 0)
defaultMesh.withMaterial(defaultTexture(assetManager))
)
.withLocalTranslation(initialPos)
.withControl(npcPhysicsControl)
)
// .map(_.taggedWith[PlayerControllerTags.PlayerTag])
// .leftMap(e => PlayerController.CouldNotCreatePlayerNode(e.getMessage()))
def defaultPlayerPhysicsControl =
new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
}
}
object Methods {
def spawnMovementActor(
enqueueR: Function1[() => Unit, Unit],
spawnProtocol: ActorRef[SpawnProtocol.Command],
movable: BetterCharacterControl @@ PlayerControllerTags.PlayerTag,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
loggerL: Logger[Task]
)(implicit timeout: Timeout, scheduler: Scheduler) =
spawnProtocol.askL[ActorRef[ImMovementActor.Command]](
SpawnProtocol.Spawn(
ImMovementActor.Props(enqueueR, movable).create,
"imMovementActor",
Props.empty,
_
)
)
// def spawnPlayerActor(
// app: GameApp,
// spawnProtocol: ActorRef[SpawnProtocol.Command],
// movable: BetterCharacterControl @@ Player,
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ]
// )(implicit timeout: Timeout, scheduler: Scheduler) =
// spawnProtocol.askL[ActorRef[PlayerActorSupervisor.Command]](
// SpawnProtocol.Spawn(
// new PlayerActorSupervisor.Props(
// app,
// movable,
// playerMovementEventBus
// ).create,
// "playerActor",
// Props.empty,
// _
// )
// )
}
// camNode <- IO(
// _cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos))
// )
// playerPhysicsControl <- IO(
// _playerPhysicsControl
// .getOrElse(defaultPlayerPhysicsControl)
// .taggedWith[PlayerTag]
// )
// playerNode <- IO.fromEither(
// _playerNode.fold(
// defaultPlayerNode(
// assetManager,
// modelPath,
// initialPlayerPos,
// camNode,
// playerPhysicsControl
// )
// )(_.asRight)
// )

View File

@ -1,4 +1,4 @@
package wow.doge.mygame.game.nodes package wow.doge.mygame.game.entities
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.LogOptions import akka.actor.typed.LogOptions
@ -6,10 +6,10 @@ import akka.actor.typed.scaladsl.Behaviors
import com.jme3.scene.CameraNode import com.jme3.scene.CameraNode
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedDown import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedDown
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
object PlayerMovementEventListener { object PlayerMovementEventListener {
@ -39,11 +39,9 @@ object PlayerMovementEventListener {
movementActor ! ImMovementActor.Jump movementActor ! ImMovementActor.Jump
Behaviors.same Behaviors.same
case PlayerRotatedRight => case PlayerRotatedRight =>
// ctx.log.warn("right rotate not implemented yet")
movementActor ! ImMovementActor.RotateRight movementActor ! ImMovementActor.RotateRight
Behaviors.same Behaviors.same
case PlayerRotatedLeft => case PlayerRotatedLeft =>
// ctx.log.warn("left rotate not implemented yet")
movementActor ! ImMovementActor.RotateLeft movementActor ! ImMovementActor.RotateLeft
Behaviors.same Behaviors.same
case PlayerCameraUp => case PlayerCameraUp =>

View File

@ -1,222 +0,0 @@
package wow.doge.mygame.game.nodes
import akka.actor.typed.ActorRef
import akka.actor.typed.Props
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import cats.effect.concurrent.Ref
import cats.implicits._
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath
import com.jme3.renderer.Camera
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
import com.jme3.scene.Node
import com.jme3.scene.control.CameraControl.ControlDirection
import com.jme3.scene.shape.Box
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.IO
import monix.bio.Task
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.GameApp
import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.MyMaterial
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils
// class PlayerNode(val name: String) extends Node(name) {}
sealed trait PlayerTag
sealed trait PlayerCameraNode
object PlayerController {
sealed trait Error
case class GenericError(reason: String) extends Error
class Props(
enqueueR: Function1[() => Unit, Unit],
rootNode: Ref[Task, Node],
camera: Camera,
loggerL: Logger[Task],
assetManager: AssetManager,
bulletAppState: BulletAppState,
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
modelPath: os.RelPath,
spawnProtocol: ActorRef[SpawnProtocol.Command],
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
_playerPhysicsControl: Option[BetterCharacterControl],
_playerNode: Option[Node with PlayerTag] = None,
_cameraNode: Option[CameraNode with PlayerCameraNode] = None,
appScheduler: monix.execution.Scheduler
)(implicit timeout: Timeout, scheduler: Scheduler) {
import Defaults._
val create: IO[Error, Unit] =
// IO.raiseError(GenericError("not implemented yet"))
(for {
camNode <- IO(
_cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos))
)
playerPhysicsControl <- IO(
_playerPhysicsControl
.getOrElse(defaultPlayerPhysicsControl)
.taggedWith[PlayerTag]
)
playerNode <- IO.fromEither(
_playerNode.fold(
defaultPlayerNode(
assetManager,
modelPath,
initialPlayerPos,
camNode,
playerPhysicsControl
)
)(_.asRight)
)
playerActor <- AkkaUtils.spawnActorL(
spawnProtocol,
"playerActorSupervisor",
new PlayerActorSupervisor.Props(
enqueueR,
camNode,
playerMovementEventBus,
playerCameraEventBus
).create(playerPhysicsControl)
)
_ <- IO {
bulletAppState.physicsSpace += playerNode
bulletAppState.physicsSpace += playerPhysicsControl
}
_ <- rootNode.update(_ :+ playerNode)
} yield ())
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
.executeOn(appScheduler)
}
def apply(
app: GameApp,
modelPath: os.RelPath,
cam: Camera
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
lazy val playerPos = ImVector3f.ZERO
lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
lazy val playerNode = new Node("PlayerNode")
.withChildren(
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
{
bulletAppState.physicsSpace += playerNode
bulletAppState.physicsSpace += playerPhysicsControl
}
{
app.rootNode += playerNode
}
playerPhysicsControl
}
}
object Defaults {
lazy val defaultMesh = {
val b = Box(1, 1, 1)
val geom = Geometry("playerMesh", b)
geom
}
def defaultTexture(assetManager: AssetManager) =
MyMaterial(
assetManager = assetManager,
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
)
def defaultCamerNode(cam: Camera, playerPos: ImVector3f) =
new CameraNode("CameraNode", cam)
.withControlDir(ControlDirection.SpatialToCamera)
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
.withLookAt(playerPos, ImVector3f.UNIT_Y)
def defaultPlayerNode(
assetManager: AssetManager,
modelPath: os.RelPath,
playerPos: ImVector3f,
camNode: CameraNode,
playerPhysicsControl: BetterCharacterControl
) =
Either.catchNonFatal(
Node("PlayerNode")
.withChildren(
camNode,
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
)
.withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
)
lazy val defaultPlayerPhysicsControl =
new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0))
}
object Methods {
def spawnMovementActor(
enqueueR: Function1[() => Unit, Unit],
spawnProtocol: ActorRef[SpawnProtocol.Command],
movable: BetterCharacterControl @@ PlayerTag,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
loggerL: Logger[Task]
)(implicit timeout: Timeout, scheduler: Scheduler) =
spawnProtocol.askL[ActorRef[ImMovementActor.Command]](
SpawnProtocol.Spawn(
ImMovementActor.Props(enqueueR, movable, playerMovementEventBus).create,
"imMovementActor",
Props.empty,
_
)
)
// def spawnPlayerActor(
// app: GameApp,
// spawnProtocol: ActorRef[SpawnProtocol.Command],
// movable: BetterCharacterControl @@ Player,
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ]
// )(implicit timeout: Timeout, scheduler: Scheduler) =
// spawnProtocol.askL[ActorRef[PlayerActorSupervisor.Command]](
// SpawnProtocol.Spawn(
// new PlayerActorSupervisor.Props(
// app,
// movable,
// playerMovementEventBus
// ).create,
// "playerActor",
// Props.empty,
// _
// )
// )
}
// spawnPlayerActor(
// app,
// spawnProtocol,
// playerPhysicsControl,
// playerMovementEventBus
// )

View File

@ -1,8 +1,13 @@
package wow.doge.mygame.game.subsystems.ai package wow.doge.mygame.game.subsystems.ai
import scala.collection.immutable.ArraySeq
import com.badlogic.gdx.ai.pfa.Connection import com.badlogic.gdx.ai.pfa.Connection
import wow.doge.mygame.game.subsystems.ai.gdx.MyIndexedGraph import wow.doge.mygame.game.subsystems.ai.gdx.MyIndexedGraph
import scala.collection.immutable.ArraySeq import com.badlogic.gdx.ai.steer.Steerable
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.ai.utils.Location
import com.badlogic.gdx.ai.steer.behaviors.Arrive
// import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph // import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph
// import scala.jdk.javaapi.CollectionConverters._ // import scala.jdk.javaapi.CollectionConverters._
@ -31,3 +36,50 @@ class CityGraph extends MyIndexedGraph[City] {
override def getNodeCount(): Int = ??? override def getNodeCount(): Int = ???
} }
class MySteerable extends Steerable[Vector3] {
val arrive = new Arrive[Vector3](this)
override def getPosition(): Vector3 = ???
override def getOrientation(): Float = ???
override def setOrientation(x$1: Float): Unit = ???
override def vectorToAngle(x$1: Vector3): Float = ???
override def angleToVector(x$1: Vector3, x$2: Float): Vector3 = ???
override def newLocation(): Location[Vector3] = ???
override def getZeroLinearSpeedThreshold(): Float = ???
override def setZeroLinearSpeedThreshold(x$1: Float): Unit = ???
override def getMaxLinearSpeed(): Float = ???
override def setMaxLinearSpeed(x$1: Float): Unit = ???
override def getMaxLinearAcceleration(): Float = ???
override def setMaxLinearAcceleration(x$1: Float): Unit = ???
override def getMaxAngularSpeed(): Float = ???
override def setMaxAngularSpeed(x$1: Float): Unit = ???
override def getMaxAngularAcceleration(): Float = ???
override def setMaxAngularAcceleration(x$1: Float): Unit = ???
override def getLinearVelocity(): Vector3 = ???
override def getAngularVelocity(): Float = ???
override def getBoundingRadius(): Float = ???
override def isTagged(): Boolean = ???
override def setTagged(x$1: Boolean): Unit = ???
}

View File

@ -9,20 +9,20 @@ import com.jme3.input.MouseInput
import com.jme3.input.controls.KeyTrigger import com.jme3.input.controls.KeyTrigger
import com.jme3.input.controls.MouseAxisTrigger import com.jme3.input.controls.MouseAxisTrigger
import monix.bio.UIO import monix.bio.UIO
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.utils.IOUtils._ import wow.doge.mygame.utils.IOUtils._
object GameInputHandler { object GameInputHandler {
final case class Props( final case class Props(
inputManager: InputManager, inputManager: InputManager,
playerMovementEventBus: ActorRef[ playerMovementEventBus: GameEventBus[PlayerMovementEvent],
EventBus.Command[PlayerMovementEvent] playerCameraEventBus: GameEventBus[PlayerCameraEvent]
], // tickEventBus: GameEventBus[TickEvent]
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
) { ) {
def begin = def begin =
for { for {
@ -66,12 +66,12 @@ object GameInputHandler {
def setupKeys(inputManager: InputManager) = def setupKeys(inputManager: InputManager) =
inputManager inputManager
.withMapping( .withMapping(
PlayerAnalogInput.TurnRight.entryName, PlayerAnalogMovementInput.TurnRight.entryName,
new KeyTrigger(KeyInput.KEY_RIGHT), new KeyTrigger(KeyInput.KEY_RIGHT),
new MouseAxisTrigger(MouseInput.AXIS_X, true) new MouseAxisTrigger(MouseInput.AXIS_X, true)
) )
.withMapping( .withMapping(
PlayerAnalogInput.TurnLeft.entryName, PlayerAnalogMovementInput.TurnLeft.entryName,
new KeyTrigger(KeyInput.KEY_LEFT), new KeyTrigger(KeyInput.KEY_LEFT),
new MouseAxisTrigger(MouseInput.AXIS_X, false) new MouseAxisTrigger(MouseInput.AXIS_X, false)
) )
@ -85,7 +85,6 @@ object GameInputHandler {
// new KeyTrigger(KeyInput.KEY_LEFT), // new KeyTrigger(KeyInput.KEY_LEFT),
new MouseAxisTrigger(MouseInput.AXIS_Y, true) new MouseAxisTrigger(MouseInput.AXIS_Y, true)
) )
.setCursorVisible(false)
def generateMovementInputEvents( def generateMovementInputEvents(
inputManager: InputManager, inputManager: InputManager,
@ -148,19 +147,18 @@ object GameInputHandler {
) = { ) = {
val name = "rotateMovementEventsGenerator" val name = "rotateMovementEventsGenerator"
inputManager inputManager
.enumAnalogObservable(PlayerAnalogInput) .enumAnalogObservable(PlayerAnalogMovementInput)
.sample(1.millis) .sample(1.millis)
// .map(e => e)
.doOnNext(analogEvent => .doOnNext(analogEvent =>
analogEvent.binding match { analogEvent.binding match {
case PlayerAnalogInput.TurnRight => case PlayerAnalogMovementInput.TurnRight =>
toTask( toTask(
playerMovementEventBus !! EventBus.Publish( playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerRotatedRight, PlayerMovementEvent.PlayerRotatedRight,
name name
) )
) )
case PlayerAnalogInput.TurnLeft => case PlayerAnalogMovementInput.TurnLeft =>
toTask( toTask(
playerMovementEventBus !! EventBus.Publish( playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerRotatedLeft, PlayerMovementEvent.PlayerRotatedLeft,
@ -179,7 +177,7 @@ object GameInputHandler {
inputManager inputManager
.analogObservable("CAMERA_UP", "CAMERA_DOWN") .analogObservable("CAMERA_UP", "CAMERA_DOWN")
.sample(1.millis) .sample(1.millis)
.mapEval(analogEvent => .doOnNext(analogEvent =>
analogEvent.binding.name match { analogEvent.binding.name match {
case "CAMERA_UP" => case "CAMERA_UP" =>
toTask( toTask(

View File

@ -1,9 +0,0 @@
package wow.doge.mygame.game.subsystems.input
object InputConstants {
val PLAYER_MOVE_LEFT = "PLAYER_MOVE_LEFT"
val PLAYER_MOVE_RIGHT = "PLAYER_MOVE_RIGHT"
val PLAYER_MOVE_FORWARD = "PLAYER_MOVE_FORWARD"
val PLAYER_MOVE_BACKWARD = "PLAYER_MOVE_BACKWARD"
val PLAYER_JUMP = "PLAYER_JUMP "
}

View File

@ -12,9 +12,9 @@ object PlayerMovementInput extends Enum[PlayerMovementInput] {
case object Jump extends PlayerMovementInput case object Jump extends PlayerMovementInput
} }
sealed trait PlayerAnalogInput extends EnumEntry with UpperSnakecase sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
object PlayerAnalogInput extends Enum[PlayerAnalogInput] { object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
val values = findValues val values = findValues
case object TurnRight extends PlayerAnalogInput case object TurnRight extends PlayerAnalogMovementInput
case object TurnLeft extends PlayerAnalogInput case object TurnLeft extends PlayerAnalogMovementInput
} }

View File

@ -1,13 +1,14 @@
package wow.doge.mygame.game.subsystems.level package wow.doge.mygame.game.subsystems.level
import com.jme3.bullet.PhysicsSpace
import com.jme3.bullet.control.RigidBodyControl import com.jme3.bullet.control.RigidBodyControl
import com.jme3.light.AmbientLight import com.jme3.light.AmbientLight
import com.jme3.light.DirectionalLight import com.jme3.light.DirectionalLight
import com.jme3.scene.Spatial
import com.jme3.bullet.PhysicsSpace
import cats.effect.concurrent.Ref
import monix.bio.Task
import com.jme3.scene.Node import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.softwaremill.tagging._
import monix.bio.Task
import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
class GameLevel( class GameLevel(
@ -16,11 +17,14 @@ class GameLevel(
ambientLight: AmbientLight, ambientLight: AmbientLight,
directionalLight: DirectionalLight directionalLight: DirectionalLight
) { ) {
def addToGame(rootNode: Ref[Task, Node], physicsSpace: PhysicsSpace) = { def addToGame(
rootNode: Node @@ GameAppTags.RootNode,
physicsSpace: PhysicsSpace
) = {
for { for {
_ <- rootNode.update(_ :+ model) _ <- Task(rootNode += model)
_ <- rootNode.update(_ :+ ambientLight) _ <- Task(rootNode :+ ambientLight)
_ <- rootNode.update(_ :+ directionalLight) _ <- Task(rootNode :+ directionalLight)
_ <- Task(physicsSpace += model) _ <- Task(physicsSpace += model)
_ <- Task(physicsSpace += physicsControl) _ <- Task(physicsSpace += physicsControl)
} yield () } yield ()

View File

@ -13,6 +13,7 @@ import wow.doge.mygame.subsystems.movement.RotateDir
trait CanMove[-A] { trait CanMove[-A] {
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f // def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
def move(inst: A, direction: ImVector3f): Unit def move(inst: A, direction: ImVector3f): Unit
def location(inst: A): ImVector3f
def jump(inst: A): Unit def jump(inst: A): Unit
def stop(inst: A): Unit def stop(inst: A): Unit
def rotate(inst: A, rotateDir: RotateDir): Unit def rotate(inst: A, rotateDir: RotateDir): Unit
@ -30,6 +31,8 @@ object CanMove {
// inst.setViewDirection(direction.mutable) // inst.setViewDirection(direction.mutable)
inst.setWalkDirection(direction.mutable.multLocal(50f)) inst.setWalkDirection(direction.mutable.multLocal(50f))
} }
override def location(inst: BetterCharacterControl) =
inst.getSpatial().getLocalTranslation().immutable
override def jump(inst: BetterCharacterControl): Unit = inst.jump() override def jump(inst: BetterCharacterControl): Unit = inst.jump()
override def rotate( override def rotate(
inst: BetterCharacterControl, inst: BetterCharacterControl,
@ -39,10 +42,10 @@ object CanMove {
rotateDir match { rotateDir match {
case RotateDir.Left => case RotateDir.Left =>
new Quaternion() new Quaternion()
.fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) .fromAngleAxis(-5 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
case RotateDir.Right => case RotateDir.Right =>
new Quaternion() new Quaternion()
.fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y) .fromAngleAxis(5 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
} }
val tmp = new Vector3f() val tmp = new Vector3f()
@ -57,6 +60,8 @@ object CanMove {
override def move(inst: Spatial, direction: ImVector3f): Unit = { override def move(inst: Spatial, direction: ImVector3f): Unit = {
inst.move(direction.mutable) inst.move(direction.mutable)
} }
override def location(inst: Spatial) =
inst.getLocalTranslation().immutable
override def jump(inst: Spatial): Unit = override def jump(inst: Spatial): Unit =
logger.warn("`Jump` is not implemented for type `Spatial`") logger.warn("`Jump` is not implemented for type `Spatial`")
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = { override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {

View File

@ -1,17 +1,14 @@
package wow.doge.mygame.subsystems.movement package wow.doge.mygame.subsystems.movement
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
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 com.jme3.math.Vector3f import com.jme3.math.Vector3f
import com.softwaremill.quicklens._ import com.softwaremill.quicklens._
import wow.doge.mygame.events.EventBus
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 import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.CardinalDirection import wow.doge.mygame.state.CardinalDirection
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
sealed trait RotateDir sealed trait RotateDir
object RotateDir { object RotateDir {
@ -35,21 +32,13 @@ object ImMovementActor {
final case class Props[T: CanMove]( final case class Props[T: CanMove](
enqueueR: Function1[() => Unit, Unit], enqueueR: Function1[() => Unit, Unit],
movable: T, movable: T
playerMovementEventBus: ActorRef[ // playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent] // EventBus.Command[PlayerMovementEvent]
] // ]
) { ) {
def create: Behavior[Command] = def create: Behavior[Command] =
Behaviors.setup(ctx => { Behaviors.setup(ctx => new ImMovementActor(ctx, this).receive(State()))
ctx.log.info("Hello from MovementActor")
// val playerMovementEventHandler = ctx.spawn(
// PlayerMovementEventHandler(ctx.self),
// "playerMovementEventHandler"
// )
// playerMovementEventBus ! EventBus.Subscribe(playerMovementEventHandler)
new ImMovementActor(ctx, this).receive(State())
})
} }
/** /**
@ -59,19 +48,6 @@ object ImMovementActor {
*/ */
final case class State(cardinalDir: CardinalDirection = CardinalDirection()) final case class State(cardinalDir: CardinalDirection = CardinalDirection())
// def apply[T: CanMove](props: Props[T]): Behavior[Command] =
// Behaviors.setup(ctx => {
// ctx.log.info("Hello from MovementActor")
// val playerMovementEventHandler = ctx.spawn(
// PlayerMovementEventHandler(ctx.self),
// "playerMovementEventHandler"
// )
// props.playerMovementEventBus ! EventBus.Subscribe(
// playerMovementEventHandler
// )
// new ImMovementActor(ctx, props).receive(State())
// })
} }
class ImMovementActor[T]( class ImMovementActor[T](

View File

@ -1,43 +1,15 @@
package wow.doge.mygame.game.subsystems.ui package wow.doge.mygame.game.subsystems.ui
import com.jme3.app.Application
import com.jayfella.jme.jfx.JavaFxUI
import scalafx.application.Platform
import monix.execution.CancelablePromise
import monix.bio.Task
import cats.effect.concurrent.Deferred
import scala.concurrent.duration._ import scala.concurrent.duration._
import wow.doge.mygame.game.GameApp
import com.jayfella.jme.jfx.JavaFxUI
import monix.bio.Task
import wow.doge.mygame.game.SimpleAppExt
object JFxUI { object JFxUI {
def apply(app: GameApp) = def apply(app: SimpleAppExt) =
Task(JavaFxUI.initialize(app)) Task(JavaFxUI.initialize(app))
.executeOn(app.scheduler) >> Task.sleep(500.millis) >> Task( .executeOn(app.scheduler) >> Task.sleep(500.millis) >> Task(
JavaFxUI.getInstance() JavaFxUI.getInstance()
) )
// Task {
// Platform.runLater(() => {
// println("here jfx")
// JavaFxUI.initialize(app)
// println("here2 jfx2")
// val inst = JavaFxUI.getInstance()
// println(inst)
// })
// }
// Task.fromFuture {
// val p = CancelablePromise[JavaFxUI]()
// Platform.runLater(() => {
// println("here")
// JavaFxUI.initialize(app)
// println("here2")
// val inst = JavaFxUI.getInstance()
// println(inst)
// p.success(inst)
// })
// p.future
// }
// for {
// d <- Deferred[Task, JavaFxUI]
// _ <- Task(JavaFxUI.initialize(app))
// } yield ()
} }

View File

@ -1,18 +1,12 @@
package wow.doge.mygame.implicits package wow.doge.mygame.implicits
import javafx.{ import javafx.scene.{input => jfxsi}
collections => jfxc, import javafx.{event => jfxe}
event => jfxe,
geometry => jfxg,
scene => jfxs,
util => jfxu
}
import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp}
import scalafx.scene.Scene
import monix.execution.Cancelable
import monix.reactive.OverflowStrategy
import monix.reactive.Observable
import monix.execution.Ack import monix.execution.Ack
import monix.execution.Cancelable
import monix.reactive.Observable
import monix.reactive.OverflowStrategy
import scalafx.scene.Scene
import scalafx.scene.control.ButtonBase import scalafx.scene.control.ButtonBase
object JavaFXMonixObservables { object JavaFXMonixObservables {

View File

@ -1,18 +1,5 @@
package wow.doge.mygame.implicits package wow.doge.mygame.implicits
import javafx.{
collections => jfxc,
event => jfxe,
geometry => jfxg,
scene => jfxs,
util => jfxu
}
import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp}
import scalafx.scene.Scene
import monix.execution.Cancelable
import monix.reactive.OverflowStrategy
import monix.reactive.Observable
import monix.execution.Ack
import scalafx.scene.control.Button
package object observables {} package object observables {}

View File

@ -6,6 +6,7 @@ import scala.reflect.ClassTag
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.util.Timeout import akka.util.Timeout
import com.jayfella.jme.jfx.JavaFxUI
import com.jme3.app.Application import com.jme3.app.Application
import com.jme3.app.SimpleApplication import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState import com.jme3.app.state.AppState
@ -24,6 +25,7 @@ import com.jme3.input.controls.ActionListener
import com.jme3.input.controls.AnalogListener import com.jme3.input.controls.AnalogListener
import com.jme3.input.controls.InputListener import com.jme3.input.controls.InputListener
import com.jme3.input.controls.Trigger import com.jme3.input.controls.Trigger
import com.jme3.light.Light
import com.jme3.math.Vector3f import com.jme3.math.Vector3f
import com.jme3.scene.CameraNode import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry import com.jme3.scene.Geometry
@ -48,23 +50,23 @@ import monix.reactive.OverflowStrategy
import monix.reactive.observers.Subscriber import monix.reactive.observers.Subscriber
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.MyBaseState import wow.doge.mygame.state.MyBaseState
import com.jme3.light.Light import com.jme3.material.Material
import com.jayfella.jme.jfx.JavaFxUI
case class ActionEvent(binding: Action, value: Boolean, tpf: Float) final case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
case class EnumActionEvent[T <: EnumEntry]( final case class EnumActionEvent[T <: EnumEntry](
binding: T, binding: T,
value: Boolean, value: Boolean,
tpf: Float tpf: Float
) )
case class AnalogEvent(binding: Action, value: Float, tpf: Float) final case class AnalogEvent(binding: Action, value: Float, tpf: Float)
case class EnumAnalogEvent[T <: EnumEntry]( final case class EnumAnalogEvent[T <: EnumEntry](
binding: T, binding: T,
value: Float, value: Float,
tpf: Float tpf: Float
) )
case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float) final class PrePhysicsTickEvent(val space: PhysicsSpace) extends AnyVal
final class PhysicsTickEvent(val space: PhysicsSpace) extends AnyVal
package object implicits { package object implicits {
type PrePhysicsTickEvent = PhysicsTickEvent type PrePhysicsTickEvent = PhysicsTickEvent
@ -334,6 +336,13 @@ package object implicits {
} }
} }
implicit final class GeometryExt(private val geom: Geometry) {
def withMaterial(mat: Material) = {
geom.setMaterial(mat)
geom
}
}
implicit final class EntityDataExt(private val ed: EntityData) implicit final class EntityDataExt(private val ed: EntityData)
extends AnyVal { extends AnyVal {
@ -356,10 +365,7 @@ package object implicits {
import akka.actor.typed.scaladsl.AskPattern._ import akka.actor.typed.scaladsl.AskPattern._
/** /**
* @param replyTo * Same as [[ask]] but returns a [[Task]]
* @param timeout
* @param scheduler
* @return
*/ */
def askL[Res]( def askL[Res](
replyTo: ActorRef[Res] => Req replyTo: ActorRef[Res] => Req
@ -378,12 +384,16 @@ package object implicits {
* @return * @return
*/ */
def tellL(msg: Req) = UIO(a.tell(msg)) def tellL(msg: Req) = UIO(a.tell(msg))
/**
* Same as [[tell]], but wrapped in a Task
*
* @param msg
* @return
*/
def !!(msg: Req) = tellL(msg) def !!(msg: Req) = tellL(msg)
} }
// def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
// ask(replyTo)(timeout, scheduler)
// }
implicit final class InputManagerExt(private val inputManager: InputManager) implicit final class InputManagerExt(private val inputManager: InputManager)
extends AnyVal { extends AnyVal {
@ -523,7 +533,7 @@ package object implicits {
def collisionObservable(): Observable[PhysicsCollisionEvent] = { def collisionObservable(): Observable[PhysicsCollisionEvent] = {
Observable.create(OverflowStrategy.Unbounded) { sub => Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable() val c = SingleAssignCancelable()
val cl = new PhysicsCollisionListener { val cl = new PhysicsCollisionListener {
override def collision(event: PhysicsCollisionEvent): Unit = { override def collision(event: PhysicsCollisionEvent): Unit = {
@ -543,23 +553,45 @@ package object implicits {
} }
} }
def physicsTickObservable(): PhysicsTickObservable = { def prePhysicsTickObservable(): Observable[PrePhysicsTickEvent] = {
Observable.create(OverflowStrategy.Unbounded) { sub => Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable() val c = SingleAssignCancelable()
val cl = new PhysicsTickListener { val cl = new PhysicsTickListener {
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = { override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf) val event = new PrePhysicsTickEvent(space)
if (sub.onNext(Left(event)) == Ack.Stop) { if (sub.onNext(event) == Ack.Stop) {
sub.onComplete() sub.onComplete()
c.cancel() c.cancel()
} }
} }
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {}
}
space.addTickListener(cl)
c := Cancelable(() => space.removeTickListener(cl))
c
}
}
def physicsTickObservable(): Observable[PhysicsTickEvent] = {
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable()
val cl = new PhysicsTickListener {
override def prePhysicsTick(
space: PhysicsSpace,
tpf: Float
): Unit = {}
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = { override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf) val event = new PhysicsTickEvent(space)
if (sub.onNext(Right(event)) == Ack.Stop) { if (sub.onNext(event) == Ack.Stop) {
sub.onComplete() sub.onComplete()
c.cancel() c.cancel()
} }
@ -574,7 +606,7 @@ package object implicits {
} }
} }
//TODO Create a typeclass for this //TODO Consider creating a typeclass for this
def +=(anyObject: Any) = space.add(anyObject) def +=(anyObject: Any) = space.add(anyObject)
def :+(anyObject: Any) = { def :+(anyObject: Any) = {

View File

@ -1,18 +1,17 @@
package wow.doge.mygame.launcher package wow.doge.mygame.launcher
import scalafx.geometry.Insets import scalafx.geometry.Insets
import scalafx.geometry.Orientation
import scalafx.geometry.Pos
import scalafx.scene.Scene import scalafx.scene.Scene
import scalafx.scene.control.Button
import scalafx.scene.effect.DropShadow import scalafx.scene.effect.DropShadow
import scalafx.scene.layout.FlowPane
import scalafx.scene.layout.HBox import scalafx.scene.layout.HBox
import scalafx.scene.layout.VBox
import scalafx.scene.paint.Color._ import scalafx.scene.paint.Color._
import scalafx.scene.paint._ import scalafx.scene.paint._
import scalafx.scene.text.Text import scalafx.scene.text.Text
import scalafx.scene.control.Button
import scalafx.scene.layout.VBox
import scalafx.scene.layout.FlowPane
import scalafx.geometry.Orientation
import scalafx.geometry.Pos
import scalafx.stage.Stage
object DefaultUI { object DefaultUI {
def scene( def scene(
@ -64,39 +63,4 @@ object DefaultUI {
// } // }
} }
def box(launchButton: Button, exitButton: Button) =
new VBox {
children = Seq(
new HBox {
padding = Insets(50, 80, 50, 80)
children = Seq(
new Text {
text = "JMonkeyEngine"
style = "-fx-font: normal bold 50pt sans-serif"
fill = new LinearGradient(endX = 0, stops = Stops(Red, DarkRed))
},
new Text {
text = " Game"
style = "-fx-font: italic bold 50pt sans-serif"
fill = new LinearGradient(
endX = 0,
stops = Stops(White, DarkGray)
)
effect = new DropShadow {
color = DarkGray
radius = 15
spread = 0.25
}
}
)
},
new FlowPane {
hgap = 10
padding = Insets(50, 80, 50, 80)
orientation = Orientation.Horizontal
alignment = Pos.Center
children = Seq(launchButton, exitButton)
}
)
}
} }

View File

@ -1,34 +1,22 @@
package wow.doge.mygame.launcher package wow.doge.mygame.launcher
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import wow.doge.mygame.executors.Schedulers
import cats.effect.Resource
import monix.bio.Task
import scala.concurrent.duration._ import scala.concurrent.duration._
import javafx.application.Platform
import scalafx.scene.control.Button
import cats.effect.concurrent.Deferred import cats.effect.concurrent.Deferred
import wow.doge.mygame.utils.IOUtils._ import javafx.application.Platform
import monix.bio.Task
import monix.catnap.CancelableF
import monix.eval.{Task => ETask} import monix.eval.{Task => ETask}
import monix.reactive.Observable import monix.reactive.Observable
import monix.bio.Fiber
import scalafx.stage.StageStyle
import scalafx.Includes._ import scalafx.Includes._
import wow.doge.mygame.utils.ResizeHelper import scalafx.application.JFXApp
import scalafx.scene.Scene import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.layout.VBox import scalafx.scene.control.Button
import scalafx.stage.StageStyle
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.implicits.JavaFXMonixObservables._ import wow.doge.mygame.implicits.JavaFXMonixObservables._
import monix.catnap.cancelables.SingleAssignCancelableF import wow.doge.mygame.utils.IOUtils._
import monix.catnap.CancelableF
// import wow.doge.mygame.implicits.JavaFXMonixObservables._
// import scala.language.implicitConversions
// object Stage {
// implicit def sfxStage2jfx(v: Stage): jfxs.Stage = if (v != null) v.delegate else null
// }
object Launcher { object Launcher {
sealed trait LauncherResult sealed trait LauncherResult
@ -41,19 +29,7 @@ object Launcher {
val schedulers: Schedulers, val schedulers: Schedulers,
val signal: Deferred[Task, LauncherResult] val signal: Deferred[Task, LauncherResult]
) { ) {
// val resource2
// : Resource[Task, (LauncherApp, Task[Ref[Task, Stage]], Fiber[Unit])] =
// Resource.make(for {
// app <- Task(new LauncherApp(this))
// fib <- app.init.start
// } yield ((app, app.stageRef, fib)))(_._3.cancel)
val create = Task(new Launcher(this)) val create = Task(new Launcher(this))
val resource: Resource[Task, Launcher] =
Resource.make(for {
app <- Task(new Launcher(this))
// fib <- app.init.start
} yield (app))(_ => Task.unit)
} }
} }
class Launcher private (props: Launcher.Props) { class Launcher private (props: Launcher.Props) {
@ -62,7 +38,6 @@ class Launcher private (props: Launcher.Props) {
private lazy val launchButton = new Button { private lazy val launchButton = new Button {
text = "Launch" text = "Launch"
} }
// private lazy val launchButtonObs =
private lazy val launchAction = private lazy val launchAction =
launchButton launchButton
@ -72,7 +47,6 @@ class Launcher private (props: Launcher.Props) {
private lazy val exitButton = new Button { private lazy val exitButton = new Button {
text = "Exit" text = "Exit"
} }
// private lazy val exitButtonObs =
private lazy val exitAction = private lazy val exitAction =
exitButton exitButton
@ -80,9 +54,6 @@ class Launcher private (props: Launcher.Props) {
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit))) .doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit)))
private lazy val _scene = private lazy val _scene =
// new Scene {
// content = new VBox
// }
DefaultUI.scene(launchButton, exitButton) DefaultUI.scene(launchButton, exitButton)
private lazy val _stage = new PrimaryStage { private lazy val _stage = new PrimaryStage {
@ -113,41 +84,21 @@ class Launcher private (props: Launcher.Props) {
) )
} }
// var stage = internal.stage
// lazy val stageRef = Ref.of[Task, Stage](internal.stage)
// stage: => PrimaryStage
def init(delay: FiniteDuration = 2000.millis) = def init(delay: FiniteDuration = 2000.millis) =
for { for {
_ <- Task(Platform.setImplicitExit(false)) _ <- Task(Platform.setImplicitExit(false))
fib <- Task(internal.main(Array.empty)).start fib <- Task(internal.main(Array.empty)).start
_ <- Task.sleep(500.millis) _ <- Task.sleep(500.millis)
// _ <- Task {
// // lazy val _stage = new CustomStageBuilder()
// // .setWindowTitle("CustomStage example")
// // .setWindowColor("rgb(34,54,122)")
// // .build()
// internal.stage.scene =
// DefaultUI.scene(internal.stage, launchButton, exitButton)
// // _stage.setScene(DefaultUI.scene(launchButton, exitButton))
// // JFXApp.Stage = _stage
// }.executeOn(props.schedulers.fx)
// c <- SingleAssignCancelableF[Task]
sceneDragFib <- toIO(sceneDragObservable.completedL).start sceneDragFib <- toIO(sceneDragObservable.completedL).start
fib2 <- toIO( fib2 <- toIO(
Observable(launchAction, exitAction).merge Observable(launchAction, exitAction).merge
.doOnNext(_ => .doOnNext(_ =>
ETask(internal.stage.close()).executeOn(props.schedulers.fx) ETask(internal.stage.close()).executeOn(props.schedulers.fx)
) )
// .doOnNext(_ => toTask(fib.cancel))
.completedL .completedL
).start ).start
c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel) c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel)
// _ <- Task {
// internal.stage = stage
// }.executeOn(props.schedulers.fx)
// .delayExecution(delay)
} yield (c) } yield (c)
} }

View File

@ -1,5 +1,7 @@
package wow.doge.mygame.math; package wow.doge.mygame.math;
import Math.{abs, sqrt, pow}
case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f) case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f)
object ImVector3f { object ImVector3f {
@ -8,4 +10,6 @@ object ImVector3f {
val UNIT_Y = ImVector3f(0, 1, 0) val UNIT_Y = ImVector3f(0, 1, 0)
val UNIT_Z = ImVector3f(0, 0, 1) val UNIT_Z = ImVector3f(0, 0, 1)
def dst(v1: ImVector3f, v2: ImVector3f) =
sqrt(pow(v1.x - v2.x, 2) + pow(v1.y - v2.y, 2) + pow(v1.z - v2.z, 2))
} }

View File

@ -1,4 +1,4 @@
package wow.doge.mygame.events package wow.doge.mygame.subsystems.events
import scala.reflect.ClassTag import scala.reflect.ClassTag

View File

@ -1,14 +1,24 @@
package wow.doge.mygame.events package wow.doge.mygame.subsystems.events
object Events { sealed trait Event
sealed trait Event final case object BulletFired extends Event
final case object BulletFired extends Event
// type BulletFired = BulletFired.type // type BulletFired = BulletFired.type
final case class EventWithData(data: Int) extends Event final case class EventWithData(data: Int) extends Event
sealed trait Tick extends Event sealed trait TickEvent extends Event
object Tick { object TickEvent {
final case object RenderTick extends Tick final case object RenderTick extends TickEvent
final case object PhysicsTick extends Tick final case object PhysicsTick extends TickEvent
} }
sealed trait EntityMovementEvent extends Event
object EntityMovementEvent {
final case class MovedLeft(name: String, pressed: Boolean)
extends EntityMovementEvent
final case class MovedUp(name: String, pressed: Boolean)
extends EntityMovementEvent
final case class MovedRight(name: String, pressed: Boolean)
extends EntityMovementEvent
final case class MovedDown(name: String, pressed: Boolean)
extends EntityMovementEvent
} }

View File

@ -10,22 +10,19 @@ 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 akka.util.Timeout import akka.util.Timeout
import cats.effect.Resource import com.typesafe.scalalogging.{Logger => SLogger}
import com.typesafe.scalalogging.{Logger => SLLogger}
import monix.bio.Task
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.Event
import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.TickEvent
class EventsModule2( class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) {
spawnProtocol: ActorSystem[SpawnProtocol.Command]
) {
implicit lazy val s = spawnProtocol.scheduler implicit lazy val s = spawnProtocol.scheduler
implicit lazy val timeout = Timeout(1.second) implicit lazy val timeout = Timeout(1.second)
lazy val eventBusLogger = SLLogger[EventBus[_]] lazy val eventBusLogger = SLogger[EventBus[_]]
lazy val playerMovementEventBusTask = lazy val playerMovementEventBusTask =
createEventBus[PlayerMovementEvent]("movementEventBus") createEventBus[PlayerMovementEvent]("movementEventBus")
@ -33,6 +30,11 @@ class EventsModule2(
lazy val playerCameraEventBusTask = lazy val playerCameraEventBusTask =
createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG) createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
lazy val tickEventBusTask =
createEventBus[TickEvent]("tickEventBus", Level.TRACE)
lazy 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(
SpawnProtocol.Spawn[EventBus.Command[T]]( SpawnProtocol.Spawn[EventBus.Command[T]](
@ -49,17 +51,8 @@ class EventsModule2(
_ _
) )
) )
}
type EventBuses = (
ActorRef[ object EventsModule {
EventBus.Command[EntityMovementEvent.PlayerMovementEvent], type GameEventBus[T] = ActorRef[EventBus.Command[T]]
],
ActorRef[EventBus.Command[PlayerCameraEvent]]
)
val resource: Resource[Task, EventBuses] =
Resource.liftF(for {
playerMovementEventBus <- playerMovementEventBusTask
playerCameraEventBus <- playerCameraEventBusTask
} yield (playerMovementEventBus, playerCameraEventBus))
} }

View File

@ -1,33 +0,0 @@
package wow.doge.mygame.subsystems.events
import wow.doge.mygame.game.subsystems.movement.CanMove
sealed trait EntityMovementEvent
object EntityMovementEvent {
final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T)
extends EntityMovementEvent
final case class MovedUp[T: CanMove](pressed: Boolean, movable: T)
extends EntityMovementEvent
final case class MovedRight[T: CanMove](pressed: Boolean, movable: T)
extends EntityMovementEvent
final case class MovedDown[T: CanMove](pressed: Boolean, movable: T)
extends EntityMovementEvent
sealed trait PlayerMovementEvent extends EntityMovementEvent
object PlayerMovementEvent {
final case class PlayerMovedLeft(pressed: Boolean)
extends PlayerMovementEvent
final case class PlayerMovedRight(pressed: Boolean)
extends PlayerMovementEvent
final case class PlayerMovedForward(pressed: Boolean)
extends PlayerMovementEvent
final case class PlayerMovedBackward(pressed: Boolean)
extends PlayerMovementEvent
final case object PlayerJumped extends PlayerMovementEvent
final case object PlayerRotatedRight extends PlayerMovementEvent
final case object PlayerRotatedLeft extends PlayerMovementEvent
final case object PlayerCameraUp extends PlayerMovementEvent
final case object PlayerCameraDown extends PlayerMovementEvent
}
}

View File

@ -1,8 +1 @@
package wow.doge.mygame.subsystems.events package wow.doge.mygame.subsystems.events
sealed trait PlayerCameraEvent
object PlayerCameraEvent {
final case object CameraMovedUp extends PlayerCameraEvent
final case object CameraMovedDown extends PlayerCameraEvent
}

View File

@ -0,0 +1,24 @@
package wow.doge.mygame.subsystems.events
sealed trait PlayerMovementEvent
object PlayerMovementEvent {
final case class PlayerMovedLeft(pressed: Boolean) extends PlayerMovementEvent
final case class PlayerMovedRight(pressed: Boolean)
extends PlayerMovementEvent
final case class PlayerMovedForward(pressed: Boolean)
extends PlayerMovementEvent
final case class PlayerMovedBackward(pressed: Boolean)
extends PlayerMovementEvent
final case object PlayerJumped extends PlayerMovementEvent
final case object PlayerRotatedRight extends PlayerMovementEvent
final case object PlayerRotatedLeft extends PlayerMovementEvent
final case object PlayerCameraUp extends PlayerMovementEvent
final case object PlayerCameraDown extends PlayerMovementEvent
}
sealed trait PlayerCameraEvent
object PlayerCameraEvent {
final case object CameraMovedUp extends PlayerCameraEvent
final case object CameraMovedDown extends PlayerCameraEvent
}

View File

@ -8,6 +8,7 @@ import scala.util.Try
import cats.implicits._ import cats.implicits._
import io.circe._ import io.circe._
import io.circe.generic.JsonCodec
import io.circe.generic.semiauto._ import io.circe.generic.semiauto._
import io.circe.parser._ import io.circe.parser._
import monix.bio.IO import monix.bio.IO
@ -15,7 +16,6 @@ import monix.bio.UIO
import monix.reactive.Consumer import monix.reactive.Consumer
import monix.reactive.Observable import monix.reactive.Observable
import wow.doge.mygame.utils.IOUtils import wow.doge.mygame.utils.IOUtils
import io.circe.generic.JsonCodec
@JsonCodec @JsonCodec
final case class Test1(hello1: String, hello2: String) final case class Test1(hello1: String, hello2: String)

View File

@ -5,9 +5,9 @@ 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
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
import monix.bio.Task
/** /**
* Scripts can either be searched and compiled at startup (Eager mode) * Scripts can either be searched and compiled at startup (Eager mode)

View File

@ -22,4 +22,20 @@ object AkkaUtils {
_ _
) )
) )
def spawnActorL2[T](
behavior: Behavior[T],
actorName: String
)(implicit
timeout: Timeout,
scheduler: Scheduler,
spawnProtocol: ActorRef[SpawnProtocol.Command]
) =
spawnProtocol.askL[ActorRef[T]](
SpawnProtocol.Spawn(
behavior,
actorName,
Props.empty,
_
)
)
} }

View File

@ -1,750 +0,0 @@
package wow.doge.mygame.utils
/*
* Copyright (c) 2011-2019, ScalaFX Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the ScalaFX Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp}
import javafx.{
collections => jfxc,
event => jfxe,
geometry => jfxg,
scene => jfxs,
util => jfxu
}
import scalafx.Includes._
import scalafx.beans.property.{
ObjectProperty,
ReadOnlyDoubleProperty,
ReadOnlyObjectProperty
}
import scalafx.collections._
import scalafx.delegate.SFXDelegate
import scalafx.geometry.NodeOrientation
import scalafx.scene.image.WritableImage
import scalafx.scene.input.{Dragboard, Mnemonic, TransferMode}
import scalafx.scene.paint.Paint
import com.goxr3plus.fxborderlessscene.borderless.{BorderlessScene => BScene}
import scala.language.implicitConversions
import scalafx.scene.Cursor
import scalafx.scene._
import scalafx.stage.Stage
import scalafx.stage.StageStyle
object BorderlessScene {
implicit def sfxScene2jfx(v: BorderlessScene): Scene =
if (v != null) v.delegate else null
}
/**
* Wraps [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html]].
*
* @constructor Create a new ScalaFX Scene with JavaFX Scene as delegate.
* @param delegate JavaFX Scene delegated. Its default value is a JavaFX Scene with a
* [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Group.html Group]] as root Node.
*/
class BorderlessScene(
override val delegate: BScene
) extends SFXDelegate[BScene] {
def this(stage: Stage, stageStyle: StageStyle, parent: Parent) =
this(new BScene(stage, stageStyle, parent))
/**
* Returns the root Node of the scene graph
*/
def root: ObjectProperty[jfxs.Parent] = delegate.rootProperty
/**
* Sets the root Node of the scene graph
*/
def root_=(v: Parent): Unit = {
root() = v
}
/**
* Returns Nodes children from this Scene's `root`.
*/
def getChildren =
root.value match {
case group: jfxs.Group => group.getChildren
case pane: jfxsl.Pane => pane.getChildren
case _ =>
throw new IllegalStateException(
"Cannot access children of root: " + root + "\n" +
"Use a class that extends Group or Pane, or override the getChildren method."
)
}
/**
* Returns scene's antialiasing setting.
*/
def antialiasing: SceneAntialiasing = delegate.getAntiAliasing
/**
* Returns Content's Node children from this Scene's `root`.
*/
def content: jfxc.ObservableList[jfxs.Node] = getChildren
/**
* Sets the list of Nodes children from this Scene's `root`, replacing the prior content. If you want append to
* current content, use `add` or similar.
*
* @param c list of Nodes children from this Scene's `root` to replace prior content.
*/
def content_=(c: Iterable[Node]): Unit = {
fillSFXCollection(this.content, c)
}
/**
* Sets a Node child, replacing the prior content. If you want append to current content, use `add` or similar.
*
* @param n Node child to replace prior content.
*/
def content_=(n: Node): Unit = {
fillSFXCollectionWithOne(this.content, n)
}
/**
* Specifies the type of camera use for rendering this `Scene`.
*/
def camera: ObjectProperty[jfxs.Camera] = delegate.cameraProperty
def camera_=(v: Camera): Unit = {
camera() = v
}
/**
* Defines the mouse cursor for this `Scene`.
*/
def cursor: ObjectProperty[jfxs.Cursor] = delegate.cursorProperty
def cursor_=(v: Cursor): Unit = {
cursor() = v
}
/** The effective node orientation of a scene resolves the inheritance of node orientation, returning either left-to-right or right-to-left. */
def effectiveNodeOrientation: ReadOnlyObjectProperty[jfxg.NodeOrientation] =
delegate.effectiveNodeOrientationProperty
/**
* Specifies the event dispatcher for this scene.
*/
def eventDispatcher: ObjectProperty[jfxe.EventDispatcher] =
delegate.eventDispatcherProperty
def eventDispatcher_=(v: jfxe.EventDispatcher): Unit = {
eventDispatcher() = v
}
/**
* Defines the background fill of this Scene.
*/
def fill: ObjectProperty[jfxsp.Paint] = delegate.fillProperty
def fill_=(v: Paint): Unit = {
fill() = v
}
/**
* The height of this Scene
*/
def height: ReadOnlyDoubleProperty = delegate.heightProperty
/**
* The width of this Scene
*/
def width: ReadOnlyDoubleProperty = delegate.widthProperty
def nodeOrientation: ObjectProperty[jfxg.NodeOrientation] =
delegate.nodeOrientationProperty
def nodeOrientation_=(v: NodeOrientation): Unit = {
ObjectProperty.fillProperty[jfxg.NodeOrientation](this.nodeOrientation, v)
}
/**
* Defines a function to be called when a mouse button has been clicked (pressed and released) on this `Scene`.
*/
def onContextMenuRequested = delegate.onContextMenuRequestedProperty
def onContextMenuRequested_=(
v: jfxe.EventHandler[_ >: jfxsi.ContextMenuEvent]
): Unit = {
onContextMenuRequested() = v
}
/**
* Defines a function to be called when drag gesture has been detected.
*/
def onDragDetected = delegate.onDragDetectedProperty
def onDragDetected_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onDragDetected() = v
}
/**
* Defines a function to be called when this `Scene` is a drag and drop gesture source after its data has been
* dropped on a drop target.
*/
def onDragDone = delegate.onDragDoneProperty
def onDragDone_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragDone() = v
}
/**
* Defines a function to be called when the mouse button is released on this `Scene` during drag and drop gesture.
*/
def onDragDropped = delegate.onDragDroppedProperty
def onDragDropped_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragDropped() = v
}
/**
* Defines a function to be called when drag gesture enters this Scene.
*/
def onDragEntered = delegate.onDragEnteredProperty
def onDragEntered_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragEntered() = v
}
/**
* Defines a function to be called when drag gesture exits this Scene.
*/
def onDragExited = delegate.onDragExitedProperty
def onDragExited_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragExited() = v
}
/**
* Defines a function to be called when drag gesture progresses within this `Scene`.
*/
def onDragOver = delegate.onDragOverProperty
def onDragOver_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragOver() = v
}
/**
* Defines a function to be called when this `Node` has input focus and the input method text has changed.
*/
def onInputMethodTextChanged = delegate.onInputMethodTextChangedProperty
def onInputMethodTextChanged_=(
v: jfxe.EventHandler[_ >: jfxsi.InputMethodEvent]
): Unit = {
onInputMethodTextChanged() = v
}
/**
* Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been pressed.
*/
def onKeyPressed = delegate.onKeyPressedProperty
def onKeyPressed_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = {
onKeyPressed() = v
}
/**
* Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been released.
*/
def onKeyReleased = delegate.onKeyReleasedProperty
def onKeyReleased_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = {
onKeyReleased() = v
}
/**
* Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been typed.
*/
def onKeyTyped = delegate.onKeyTypedProperty
def onKeyTyped_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = {
onKeyTyped() = v
}
/**
* Defines a function to be called when a mouse button has been clicked (pressed and released) on this `Scene`.
*/
def onMouseClicked = delegate.onMouseClickedProperty
def onMouseClicked_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseClicked() = v
}
/**
* Defines a function to be called when a mouse button is pressed on this `Scene` and then dragged.
*/
def onMouseDragged = delegate.onMouseDraggedProperty
def onMouseDragged_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseDragged() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture enters this `Scene`.
*/
def onMouseDragEntered = delegate.onMouseDragEnteredProperty
def onMouseDragEntered_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragEntered() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture exits this `Scene`.
*/
def onMouseDragExited = delegate.onMouseDragExitedProperty
def onMouseDragExited_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragExited() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture progresses within this `Scene`.
*/
def onMouseDragOver = delegate.onMouseDragOverProperty
def onMouseDragOver_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragOver() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture ends within this `Scene`.
*/
def onMouseDragReleased = delegate.onMouseDragReleasedProperty
def onMouseDragReleased_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragReleased() = v
}
/**
* Defines a function to be called when the mouse enters this `Scene`.
*/
def onMouseEntered = delegate.onMouseEnteredProperty
def onMouseEntered_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseEntered() = v
}
/**
* Defines a function to be called when the mouse exits this `Scene`.
*/
def onMouseExited = delegate.onMouseExitedProperty
def onMouseExited_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseExited() = v
}
/**
* Defines a function to be called when mouse cursor moves within this `Scene` but no buttons have been pushed.
*/
def onMouseMoved = delegate.onMouseMovedProperty
def onMouseMoved_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseMoved() = v
}
/**
* Defines a function to be called when a mouse button has been pressed on this `Scene`.
*/
def onMousePressed = delegate.onMousePressedProperty
def onMousePressed_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMousePressed() = v
}
/**
* Defines a function to be called when a mouse button has been released on this `Scene`.
*/
def onMouseReleased = delegate.onMouseReleasedProperty
def onMouseReleased_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseReleased() = v
}
/**
* Defines a function to be called when user performs a scrolling action.
*/
def onScroll = delegate.onScrollProperty
def onScroll_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = {
onScroll() = v
}
/**
* The URL of the user-agent stylesheet that will be used by this Scene in place of the the platform-default
* user-agent stylesheet. If the URL does not resolve to a valid location, the platform-default user-agent
* stylesheet will be used.
*
* For additional information about using CSS with the scene graph, see the
* [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html CSS Reference Guide]].
*
* @return The URL of the user-agent stylesheet that will be used by this SubScene, or null if has not been set.
*/
def userAgentStylesheet: ObjectProperty[String] =
delegate.userAgentStylesheetProperty
/**
* Set the URL of the user-agent stylesheet that will be used by this Scene in place of the the platform-default
* user-agent stylesheet. If the URL does not resolve to a valid location, the platform-default user-agent
* stylesheet will be used.
*
* For additional information about using CSS with the scene graph, see the
* [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html CSS Reference Guide]].
*
* @param url The URL is a hierarchical URI of the form `[scheme:][//authority][path]`.
* If the URL does not have a `[scheme:]` component, the URL is considered to be the `[path]`
* component only. Any leading '/' character of the `[path]` is ignored and the `[path]` is
* treated as a path relative to the root of the application's classpath.
*/
def userAgentStylesheet_=(url: String): Unit = {
ObjectProperty.fillProperty[String](userAgentStylesheet, url)
}
/**
* The `Window` for this Scene
*/
def window: ReadOnlyObjectProperty[javafx.stage.Window] =
delegate.windowProperty
/**
* The horizontal location of this `Scene` on the `Window`.
*/
def x: ReadOnlyDoubleProperty = delegate.xProperty
/**
* The vertical location of this `Scene` on the `Window`.
*/
def y: ReadOnlyDoubleProperty = delegate.yProperty
/**
* Retrieves the depth buffer attribute for this scene.
*/
def depthBuffer = delegate.isDepthBuffer
/**
* Gets an observable list of string URLs linking to the stylesheets to use with this Parent's contents.
*/
def stylesheets: jfxc.ObservableList[String] = delegate.getStylesheets
/**
* Sets the list of stylesheets URLs, replacing the prior content. If you want append to current content, use `add` or
* similar.
*
* @param c list of stylesheets URLs to replace prior content.
*/
def stylesheets_=(c: Iterable[String]): Unit = {
fillCollection(stylesheets, c)
}
/**
* Looks for any node within the scene graph based on the specified CSS selector.
*
* @param selector The css selector to look up
* @return A [[scala.Some]] containing the Node in the scene which matches the CSS selector, or [[scala.None]]
* if none is found.
*/
def lookup(selector: String): Option[Node] = Option(delegate.lookup(selector))
/**
* Registers the specified mnemonic.
*
* @param m The Mnemonic
*/
def addMnemonic(m: Mnemonic): Unit = {
delegate.addMnemonic(m)
}
/**
* Unregisters the specified mnemonic.
*
* @param m The Mnemonic to be removed.
*/
def removeMnemonic(m: Mnemonic): Unit = {
delegate.removeMnemonic(m)
}
/**
* Gets the list of mnemonics for this `Scene`.
*/
def getMnemonics
: jfxc.ObservableMap[jfxsi.KeyCombination, jfxc.ObservableList[
jfxsi.Mnemonic
]] = delegate.getMnemonics
/**
* Gets the list of accelerators for this Scene.
*/
def accelerators: jfxc.ObservableMap[jfxsi.KeyCombination, Runnable] =
delegate.getAccelerators
/**
* Confirms a potential drag and drop gesture that is recognized over this `Scene`.
*
* @param transferModes The supported `TransferMode`(s) of this `Node`
* @return A `Dragboard` to place this `Scene`'s data on
*/
def startDragAndDrop(transferModes: TransferMode*): Dragboard =
delegate.startDragAndDrop(transferModes.map(_.delegate): _*)
/**
* Starts a full press-drag-release gesture with this scene as gesture source.
*/
def startFullDrag(): Unit = {
delegate.startFullDrag()
}
/**
* The scene's current focus owner node. This node's "focused" variable might be false if this scene has no window,
* or if the window is inactive (window.focused == false).
*
* @since 2.2
*/
def focusOwner: ReadOnlyObjectProperty[jfxs.Node] =
delegate.focusOwnerProperty()
/**
* Defines a function to be called when user performs a rotation action.
*
* @since 2.2
*/
def onRotate = delegate.onRotateProperty
def onRotate_=(v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]): Unit = {
onRotate() = v
}
/**
* Defines a function to be called when a rotation gesture ends.
*
* @since 2.2
*/
def onRotationFinished = delegate.onRotationFinishedProperty()
def onRotationFinished_=(
v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]
): Unit = {
onRotationFinished() = v
}
/**
* Defines a function to be called when a rotation gesture starts.
*
* @since 2.2
*/
def onRotationStarted = delegate.onRotationFinishedProperty()
def onRotationStarted_=(
v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]
): Unit = {
onRotationStarted() = v
}
/**
* Defines a function to be called when a Scroll gesture ends.
*
* @since 2.2
*/
def onScrollFinished = delegate.onScrollFinishedProperty()
def onScrollFinished_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = {
onScrollFinished() = v
}
/**
* Defines a function to be called when a Scroll gesture starts.
*
* @since 2.2
*/
def onScrollStarted = delegate.onScrollStartedProperty()
def onScrollStarted_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = {
onScrollStarted() = v
}
/**
* Defines a function to be called when a Swipe Down gesture starts.
*
* @since 2.2
*/
def onSwipeDown = delegate.onSwipeDownProperty()
def onSwipeDown_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeDown() = v
}
/**
* Defines a function to be called when a Swipe Down gesture starts.
*
* @since 2.2
*/
def onSwipeLeft = delegate.onSwipeLeftProperty()
def onSwipeLeft_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeLeft() = v
}
/**
* Defines a function to be called when a Swipe Up gesture starts.
*
* @since 2.2
*/
def onSwipeUp = delegate.onSwipeUpProperty()
def onSwipeUp_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeUp() = v
}
/**
* Defines a function to be called when a Swipe Right gesture starts.
*
* @since 2.2
*/
def onSwipeRight = delegate.onSwipeRightProperty()
def onSwipeRight_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeRight() = v
}
/**
* Defines a function to be called when user performs a Touch action.
*
* @since 2.2
*/
def onZoom = delegate.onZoomProperty()
def onZoom_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = {
onZoom() = v
}
/**
* Defines a function to be called when a Zoom gesture ends.
*
* @since 2.2
*/
def onZoomFinished = delegate.onZoomFinishedProperty()
def onZoomFinished_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = {
onZoomFinished() = v
}
/**
* Defines a function to be called when a Zoom gesture starts.
*
* @since 2.2
*/
def onZoomStarted = delegate.onZoomStartedProperty()
def onZoomStarted_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = {
onZoomStarted() = v
}
/**
* Defines a function to be called when user performs a Touch Moved action.
*
* @since 2.2
*/
def onTouchMoved = delegate.onTouchMovedProperty()
def onTouchMoved_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchMoved() = v
}
/**
* Defines a function to be called when user performs a Touch Pressed action.
*
* @since 2.2
*/
def onTouchPressed = delegate.onTouchPressedProperty()
def onTouchPressed_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchPressed() = v
}
/**
* Defines a function to be called when user performs a Touch Released action.
*
* @since 2.2
*/
def onTouchReleased = delegate.onTouchReleasedProperty()
def onTouchReleased_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchReleased() = v
}
/**
* Defines a function to be called when user performs a Touch Stationary action.
*
* @since 2.2
*/
def onTouchStationary = delegate.onTouchStationaryProperty()
def onTouchStationary_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchStationary() = v
}
/**
* Takes a snapshot of this scene and returns the rendered image when it is ready.
*
* @param image The writable image that will be used to hold the rendered scene.
* @return the rendered image
*
* @since 2.2
*/
def snapshot(image: WritableImage): WritableImage = delegate.snapshot(image)
/**
* Takes a snapshot of this scene at the next frame and calls the specified callback method when the image is ready.
*
* @param callback A function to be called when the image is ready.
* @param image The writable image that will be used to hold the rendered scene.
*
* @since 2.2
*/
def snapshot(callback: SnapshotResult => Unit, image: WritableImage): Unit = {
val javaCallback = new jfxu.Callback[jfxs.SnapshotResult, java.lang.Void] {
def call(result: jfxs.SnapshotResult): java.lang.Void = {
callback(new SnapshotResult(result))
null
}
}
delegate.snapshot(javaCallback, image)
}
}

View File

@ -4,10 +4,8 @@ import java.io.ByteArrayOutputStream
import java.io.OutputStream import java.io.OutputStream
import java.io.PrintStream import java.io.PrintStream
import cats.effect.Resource
import monix.bio.Task
import scalafx.scene.control.TextArea
import scalafx.application.Platform import scalafx.application.Platform
import scalafx.scene.control.TextArea
trait ConsoleStreamable[T] { trait ConsoleStreamable[T] {
def println(inst: T, text: String): Unit def println(inst: T, text: String): Unit
@ -18,7 +16,8 @@ class GenericConsoleStream[T](
outputStream: OutputStream, outputStream: OutputStream,
val config: GenericConsoleStream.Config = val config: GenericConsoleStream.Config =
GenericConsoleStream.Config.default, GenericConsoleStream.Config.default,
private var streamable: Option[T] = None // TODO make this atomic
private var _streamable: Option[T] = None
)(implicit )(implicit
cs: ConsoleStreamable[T] cs: ConsoleStreamable[T]
) extends PrintStream(outputStream, true) { ) extends PrintStream(outputStream, true) {
@ -29,26 +28,24 @@ class GenericConsoleStream[T](
override def println(text: String): Unit = override def println(text: String): Unit =
if (config.exclusive) { if (config.exclusive) {
printToStreamable(streamable, text) printToStreamable(_streamable, text)
} else { } else {
defaultOut.println(text) defaultOut.println(text)
printToStreamable(streamable, text) printToStreamable(_streamable, text)
} }
override def print(text: String): Unit = override def print(text: String): Unit =
streamable.foreach(s => if (config.exclusive) {
if (config.exclusive) { printToStreamable(_streamable, text)
printToStreamable(streamable, text) } else {
} else { defaultOut.println(text)
defaultOut.println(text) printToStreamable(_streamable, text)
printToStreamable(streamable, text) }
}
)
def :=(s: T) = { def :=(s: T) = {
streamable match { _streamable match {
case Some(value) => case Some(value) =>
case None => streamable = Some(s) case None => _streamable = Some(s)
} }
} }
} }
@ -64,41 +61,25 @@ object GenericConsoleStream {
} }
implicit val implJFXConsoleStreamForTextArea = implicit val implJFXConsoleStreamForTextArea =
new ConsoleStreamable[scalafx.scene.control.TextArea] { new ConsoleStreamable[TextArea] {
override def println( override def println(
ta: scalafx.scene.control.TextArea, ta: TextArea,
text: String text: String
): Unit = ): Unit =
ta.appendText(text + "\n") Platform.runLater(() => ta.appendText(text + "\n"))
override def print( override def print(
ta: scalafx.scene.control.TextArea, ta: TextArea,
text: String text: String
): Unit = ): Unit =
ta.appendText(text) Platform.runLater(() => ta.appendText(text))
} }
// def textAreaStreamResource(ta: TextArea) = def textAreaStream() =
// Resource.make(
// Task(
// new GenericConsoleStream(
// outputStream = new ByteArrayOutputStream(),
// streamable = ta
// )
// )
// )(s => Task(s.close()))
def textAreaStream(
// ta: TextArea
) =
// Task(
new GenericConsoleStream[TextArea]( new GenericConsoleStream[TextArea](
outputStream = new ByteArrayOutputStream() outputStream = new ByteArrayOutputStream()
// streamable = ta
) )
// )
// (s => Task(s.close()))
} }