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.lihaoyi" %% "os-lib" % "0.7.1",
// "com.jayfella" % "jme-jfx-11" % "1.1.5",
"com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0",
"com.github.Oshan96" % "CustomStage" % "v1.3.1",
// "com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0",
// "com.github.Oshan96" % "CustomStage" % "v1.3.1",
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
"org.recast4j" % "recast" % "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) =
consoleLogger[IO](minLevel = Level.Debug)
.withAsync(timeWindow = 1.milliseconds)
.withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated
.unsafeRunSync()
@ -43,7 +43,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
"application-log-2.log",
Formatter.json,
minLevel = Level.Debug
).withAsync(timeWindow = 1.milliseconds)
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated
.unsafeRunSync()
@ -52,7 +52,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
"eventbus.log",
Formatter.json,
minLevel = Level.Debug
).withAsync(timeWindow = 1.milliseconds)
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
.allocated
.unsafeRunSync()
@ -77,7 +77,7 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
// "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"
// ) =>
// 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
case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger

View File

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

View File

@ -5,56 +5,54 @@ 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 cats.effect.concurrent.Deferred
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.renderer.ViewPort
import com.jme3.scene.Node
import com.softwaremill.macwire._
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.Fiber
import monix.bio.IO
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 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 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(
logger: Logger[Task],
gameApp: GameApp2,
spawnProtocol: ActorSystem[SpawnProtocol.Command],
gameApp: GameApp,
implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
jmeThread: monix.execution.Scheduler,
schedulers: Schedulers,
consoleStream: GenericConsoleStream[TextArea]
@ -68,19 +66,29 @@ class MainApp(
def gameInit: Task[Fiber[Throwable, Unit]] =
for {
eventsModule <- Task(new EventsModule2(spawnProtocol))
eventsModule <- Task(new EventsModule(spawnProtocol))
playerMovementEventBus <- eventsModule.playerMovementEventBusTask
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
/**
* 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
* of the game app
*/
res <- gameApp.enqueueL(() => Task("done")).flatten
res <- gameApp.enqueueL(() => "done")
_ <- logger.info(s"Result = $res")
/**
* JME Thread has been initialized at this point. We can now access the
* field of the game application
*/
inputManager <- gameApp.inputManager
assetManager <- gameApp.assetManager
stateManager <- gameApp.stateManager
@ -89,41 +97,20 @@ class MainApp(
enqueueR <- Task(gameApp.enqueue _)
viewPort <- gameApp.viewPort
_ <- logger.info("before")
// jfxUI <- Task(JavaFxUI.initialize(gameApp.app))
// .executeOn(gameApp.scheduler) >> Task.sleep(500.millis) >> Task(
// JavaFxUI.getInstance()
// )
// .start
jfxUI <- gameApp.jfxUI
// jfxUI <- gameApp.jfxUI
consoleTextArea <- Task(new TextArea {
text = "hello"
text = "hello \n"
editable = false
wrapText = true
// maxHeight = 150
// maxWidth = 300
})
_ <- Task(consoleStream := consoleTextArea)
_ <- Task(jfxUI += consoleTextArea)
// consoleStream <- Task(
// GenericConsoleStream.textAreaStream(consoleTextArea)
// )
// _ <- Task(consoleStream := consoleTextArea)
// _ <- Task(jfxUI += consoleTextArea)
_ <- logger.info("after")
bulletAppState <- Task(new BulletAppState())
_ <- Task(stateManager.attach(bulletAppState))
_ <- 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)
} yield (gameAppFib)
@ -131,7 +118,7 @@ class MainApp(
scriptSystem <- scriptSystemInit
/**
* 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
* signal can then continue
*/
@ -141,15 +128,16 @@ class MainApp(
launchResult <- launchSignal.get
_ <- cancelToken.cancel
_ <-
/**
* User chose to quit
*/
if (launchResult == LauncherResult.Exit)
logger.info("Exiting")
else gameInit.flatMap(_.join)
// _ <- Task.sleep(2000.millis)
// gameAppFib <- gameInit
/**
* Wait for game window to close
* User chose launch. Wait for game window to close
*/
// _ <- gameAppFib.join
else
gameInit.flatMap(_.join)
} yield ()
}
@ -157,27 +145,27 @@ class MainApp(
* Class with all dependencies in one place for easy wiring
*/
class MainAppDelegate(
gameApp: GameApp2,
spawnProtocol: ActorSystem[SpawnProtocol.Command],
gameApp: GameApp,
implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
loggerL: Logger[Task],
// eventBuses: EventsModule2
playerMovementEventBus: ActorRef[
EventBus.Command[EntityMovementEvent.PlayerMovementEvent]
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
tickEventBus: GameEventBus[TickEvent],
inputManager: InputManager,
assetManager: AssetManager,
stateManager: AppStateManager,
camera: Camera,
viewPort: ViewPort,
enqueueR: Function1[() => Unit, Unit],
rootNode: Ref[Task, Node],
rootNode: Node @@ GameAppTags.RootNode,
bulletAppState: BulletAppState
)(implicit
@annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler
) {
lazy val physicsSpace = bulletAppState.physicsSpace
def init(
appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea]
@ -186,11 +174,11 @@ class MainAppDelegate(
_ <- loggerL.info("Initializing Systems")
_ <- Task(
assetManager.registerLocator(
(os.rel / "assets" / "town.zip"),
os.rel / "assets" / "town.zip",
classOf[ZipLocator]
)
)
_ <- loggerL.info("test hmm")
_ <- loggerL.info("test")
// _ <- Task(consoleStream.println("text"))
_ <- DefaultGameLevel(assetManager, viewPort)
.addToGame(
@ -198,23 +186,91 @@ class MainAppDelegate(
bulletAppState.physicsSpace
)
.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)
} yield ()
def createPlayerController(
appScheduler: monix.execution.Scheduler
// 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
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 monix.bio.Task
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]
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
Resource.make(logger.info("Creating Actor System") >> Task {
@ -18,8 +17,10 @@ trait MainModule extends GameModule with ExecutorsModule {
name = "GameActorSystem"
)
})(sys =>
logger.info("Shutting down actor system") >> Task(
sys.terminate()
)
for {
_ <- Task(sys.terminate())
_ <- Task.fromFuture(sys.whenTerminated)
_ <- logger.info("Actor System Terminated")
} yield ()
)
}

View File

@ -1,132 +1,191 @@
package wow.doge.mygame.game
import scala.collection.immutable.Queue
import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState
import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager
import com.jme3.asset.AssetManager
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.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
import monix.catnap.ConcurrentChannel
import monix.catnap.ConsumerF
import monix.catnap.Semaphore
import monix.eval.Coeval
import wow.doge.mygame.game.subsystems.ui.JFxUI
class GameApp(
schedulers: Schedulers,
appStates: AppState*
) extends SimpleApplication(appStates: _*) {
import GameApp._
sealed trait Error
case object FlyCamNotExists extends Error
object GameAppTags {
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 =
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
schedulers.async
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)))
}
}
object Ops {
final class AddToNode[T <: Node](private val node: T) extends AnyVal {
/**
* Pure version
*/
def apply(spatial: Spatial)(implicit logger: Logger[Task]) =
logger.debug(
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 tickObservable: Observable[Float] = tickSubject
override def simpleInitApp(): Unit = {}
override def simpleUpdate(tpf: Float): Unit = {
tickSubject.onNext(tpf)
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)
}
override def stop(): Unit = {
tickSubject.onComplete()
super.stop()
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!")
}
def enqueueScala[T](cb: () => T): CancelableFuture[T] = {
val p = Promise[T]()
taskQueue2.transform(_ :+ MyTask(p, cb))
p.future
}
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)
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()
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 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 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
import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol
import scala.concurrent.duration._
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors
import io.odin.Logger
import monix.bio.Task
import wow.doge.mygame.events.Events
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.TickGenerator.Send
import wow.doge.mygame.game.entities.GenericTimerActor
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.PhysicsTick
object GameAppActor {
sealed trait Command
case object ApplicationStarted extends Command
case class ApplicationStartFailed(reason: String) extends Command
case object Start extends Command
case object Pause extends Command
case object Stop extends Command
case class Props(
app: GameApp,
akkaScheduler: Scheduler,
schedulers: Schedulers,
spawnProtocol: ActorRef[SpawnProtocol.Command],
loggerL: Logger[Task]
// app: SimpleAppExt,
// akkaScheduler: Scheduler,
// schedulers: Schedulers,
// spawnProtocol: ActorRef[SpawnProtocol.Command],
// loggerL: Logger[Task]
tickEventBus: GameEventBus[TickEvent]
) {
def create =
Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from GameAppActor")
val renderTickGenerator =
ctx.spawn(
Behaviors
.supervise(renderTickGeneratorBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
"tickGeneratorActor"
)
Behaviors.receiveMessage { msg =>
msg match {
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
case ApplicationStarted =>
ctx.log.info("Application started")
Behaviors.same
case ApplicationStartFailed(reason) =>
ctx.log.error(
s"Failed to start application - $reason"
}
}
val renderTickGeneratorBehavior =
Behaviors.receiveMessage[TickGenerator.Command] {
case Send =>
tickEventBus ! EventBus.Publish(
TickEvent.RenderTick,
"tickGeneratorActor"
)
Behaviors.stopped
}
Behaviors.same
}
}
}
object TickGenerator {
sealed trait Command
case object Send extends Command
}
object SubscribingActor {
def apply() =
Behaviors.receive[Events.Tick.PhysicsTick.type] { (ctx, msg) =>
Behaviors.receive[PhysicsTick.type] { (ctx, msg) =>
ctx.log.debug(s"received event $msg")
Behaviors.same
}

View File

@ -1,48 +1,24 @@
package wow.doge.mygame.game
import akka.actor.typed.ActorRef
import akka.actor.typed.SpawnProtocol
import cats.effect.Resource
import com.jme3.app.StatsAppState
import com.jme3.system.AppSettings
import io.odin.Logger
import monix.bio.Fiber
import monix.bio.IO
import monix.bio.Task
import monix.execution.Scheduler
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.subsystems.input.GameInputHandler
class GameAppResource(
logger: Logger[Task],
jmeScheduler: Scheduler,
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(
for {
_ <- logger.info("Creating game app")
app <- Task(new GameApp(schedulers, new StatsAppState()))
app2 <- Task {
appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState()))
app <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
@ -50,48 +26,11 @@ class GameAppResource(
* disables the launcher
* We'll be making our own launcher anyway
*/
app.setShowSettings(false)
app.setSettings(settings)
appExt.setShowSettings(false)
appExt.setSettings(settings)
// JMERunner.runner = app
new GameApp2(app)
new GameApp(logger, appExt)
}
// fib <- app2.start.executeOn(jmeScheduler).start
} yield (app2)
} yield (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.util.Random
import akka.actor.typed.ActorRef
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.Behaviors
import akka.actor.typed.scaladsl.TimerScheduler
import com.jme3.scene.CameraNode
import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.subsystems.events.EntityMovementEvent
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.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor
object PlayerActorSupervisor {
sealed trait Command
final case class Props(
enqueueR: Function1[() => Unit, Unit],
camNode: CameraNode,
playerMovementEventBus: ActorRef[
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) =
Behaviors.logMessages(
@ -44,59 +46,40 @@ object PlayerActorSupervisor {
lazy val movementActor =
ctx.spawn(
Behaviors
.supervise(
ImMovementActor
.Props(enqueueR, movable, playerMovementEventBus)
.create
)
.supervise(imMovementActorBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActor"
"playerMovementActorChild"
)
lazy val playerMovementEventHandler = ctx.spawn(
Behaviors
.supervise(PlayerMovementEventListener(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementEventHandler"
ctx.spawn(
PlayerMovementActor
.Props(movementActor, playerMovementEventBus, tickEventBus)
.create,
"playerMovementAcor"
)
lazy val movementActorTimer = ctx.spawn(
Behaviors
.supervise(MovementActorTimer(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActorTimer"
)
lazy val playerCameraHandler = {
ctx.spawn(
Behaviors
.supervise(
PlayerCameraEventListener(camNode, enqueueR)
)
.supervise(playerCamELBehavior)
.onFailure[Exception](SupervisorStrategy.restart),
"playerCameraHandler"
)
}
//init actors
movementActorTimer ! MovementActorTimer.Start
playerMovementEventBus ! EventBus.Subscribe(
playerMovementEventHandler
)
playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler)
new PlayerActorSupervisor(
ctx,
this,
Children(movementActor, playerMovementEventHandler)
Children(movementActor)
).receive
}
)
}
case class Children(
movementActor: ActorRef[ImMovementActor.Command],
playerMovementEventHandler: ActorRef[
EntityMovementEvent.PlayerMovementEvent
]
movementActor: ActorRef[ImMovementActor.Command]
)
}
class PlayerActorSupervisor[T: CanMove](
@ -113,45 +96,38 @@ class PlayerActorSupervisor[T: CanMove](
}
}
object MovementActorTimer {
object PlayerMovementActor {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case object TimerKey
def apply(target: ActorRef[ImMovementActor.Command]) =
Behaviors.withTimers[Command] { timers =>
new MovementActorTimer(timers, target).idle
}
}
class MovementActorTimer(
timers: TimerScheduler[MovementActorTimer.Command],
target: ActorRef[ImMovementActor.Command]
final case class Props(
movementActor: ActorRef[ImMovementActor.Command],
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
tickEventBus: GameEventBus[TickEvent]
) {
import MovementActorTimer._
val idle: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, (60f / 144).millis)
active
case _ => Behaviors.unhandled
}
}
val active: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Send =>
target ! ImMovementActor.Tick
def create: Behavior[Command] =
Behaviors.setup { ctx =>
val playerMovementEl = ctx.spawn(
Behaviors
.supervise(PlayerMovementEventListener(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementEventHandler"
)
val renderTickElBehavior =
Behaviors.receiveMessage[RenderTick.type] {
case RenderTick =>
movementActor ! ImMovementActor.Tick
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
idle
case _ => Behaviors.unhandled
}
val renderTickEl =
ctx.spawn(renderTickElBehavior, "playerMovementTickListener")
playerMovementEventBus ! EventBus.Subscribe(
playerMovementEl
)
tickEventBus ! EventBus.Subscribe(renderTickEl)
Behaviors.receiveMessage { msg => Behaviors.same }
}
}
}
@ -161,7 +137,7 @@ object GenericTimerActor {
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case object TimerKey
case class TimerKey(seed: Long)
case class Props[T](
target: ActorRef[T],
@ -169,12 +145,13 @@ object GenericTimerActor {
timeInterval: FiniteDuration
) {
val create = Behaviors.withTimers[Command] { timers =>
new GenericTimerActor(timers, this).idle
new GenericTimerActor(timers, TimerKey(Random.nextLong()), this).idle
}
}
}
class GenericTimerActor[T](
timers: TimerScheduler[GenericTimerActor.Command],
timerKey: GenericTimerActor.TimerKey,
props: GenericTimerActor.Props[T]
) {
import GenericTimerActor._
@ -182,7 +159,7 @@ class GenericTimerActor[T](
val idle: Behavior[Command] =
Behaviors.receiveMessage {
case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, props.timeInterval)
timers.startTimerWithFixedDelay(timerKey, Send, props.timeInterval)
active
case _ => Behaviors.unhandled
@ -194,7 +171,7 @@ class GenericTimerActor[T](
props.target ! props.messageToSend
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
timers.cancel(timerKey)
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.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.LogOptions
@ -6,10 +6,10 @@ import akka.actor.typed.scaladsl.Behaviors
import com.jme3.scene.CameraNode
import com.typesafe.scalalogging.Logger
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.CameraMovedDown
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
object PlayerMovementEventListener {
@ -39,11 +39,9 @@ object PlayerMovementEventListener {
movementActor ! ImMovementActor.Jump
Behaviors.same
case PlayerRotatedRight =>
// ctx.log.warn("right rotate not implemented yet")
movementActor ! ImMovementActor.RotateRight
Behaviors.same
case PlayerRotatedLeft =>
// ctx.log.warn("left rotate not implemented yet")
movementActor ! ImMovementActor.RotateLeft
Behaviors.same
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
import scala.collection.immutable.ArraySeq
import com.badlogic.gdx.ai.pfa.Connection
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 scala.jdk.javaapi.CollectionConverters._
@ -31,3 +36,50 @@ class CityGraph extends MyIndexedGraph[City] {
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.MouseAxisTrigger
import monix.bio.UIO
import wow.doge.mygame.events.EventBus
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.PlayerMovementEvent
import wow.doge.mygame.utils.IOUtils._
object GameInputHandler {
final case class Props(
inputManager: InputManager,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
playerMovementEventBus: GameEventBus[PlayerMovementEvent],
playerCameraEventBus: GameEventBus[PlayerCameraEvent]
// tickEventBus: GameEventBus[TickEvent]
) {
def begin =
for {
@ -66,12 +66,12 @@ object GameInputHandler {
def setupKeys(inputManager: InputManager) =
inputManager
.withMapping(
PlayerAnalogInput.TurnRight.entryName,
PlayerAnalogMovementInput.TurnRight.entryName,
new KeyTrigger(KeyInput.KEY_RIGHT),
new MouseAxisTrigger(MouseInput.AXIS_X, true)
)
.withMapping(
PlayerAnalogInput.TurnLeft.entryName,
PlayerAnalogMovementInput.TurnLeft.entryName,
new KeyTrigger(KeyInput.KEY_LEFT),
new MouseAxisTrigger(MouseInput.AXIS_X, false)
)
@ -85,7 +85,6 @@ object GameInputHandler {
// new KeyTrigger(KeyInput.KEY_LEFT),
new MouseAxisTrigger(MouseInput.AXIS_Y, true)
)
.setCursorVisible(false)
def generateMovementInputEvents(
inputManager: InputManager,
@ -148,19 +147,18 @@ object GameInputHandler {
) = {
val name = "rotateMovementEventsGenerator"
inputManager
.enumAnalogObservable(PlayerAnalogInput)
.enumAnalogObservable(PlayerAnalogMovementInput)
.sample(1.millis)
// .map(e => e)
.doOnNext(analogEvent =>
analogEvent.binding match {
case PlayerAnalogInput.TurnRight =>
case PlayerAnalogMovementInput.TurnRight =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerRotatedRight,
name
)
)
case PlayerAnalogInput.TurnLeft =>
case PlayerAnalogMovementInput.TurnLeft =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerRotatedLeft,
@ -179,7 +177,7 @@ object GameInputHandler {
inputManager
.analogObservable("CAMERA_UP", "CAMERA_DOWN")
.sample(1.millis)
.mapEval(analogEvent =>
.doOnNext(analogEvent =>
analogEvent.binding.name match {
case "CAMERA_UP" =>
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
}
sealed trait PlayerAnalogInput extends EnumEntry with UpperSnakecase
object PlayerAnalogInput extends Enum[PlayerAnalogInput] {
sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
val values = findValues
case object TurnRight extends PlayerAnalogInput
case object TurnLeft extends PlayerAnalogInput
case object TurnRight extends PlayerAnalogMovementInput
case object TurnLeft extends PlayerAnalogMovementInput
}

View File

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

View File

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

View File

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

View File

@ -1,43 +1,15 @@
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 wow.doge.mygame.game.GameApp
import com.jayfella.jme.jfx.JavaFxUI
import monix.bio.Task
import wow.doge.mygame.game.SimpleAppExt
object JFxUI {
def apply(app: GameApp) =
def apply(app: SimpleAppExt) =
Task(JavaFxUI.initialize(app))
.executeOn(app.scheduler) >> Task.sleep(500.millis) >> Task(
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
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 javafx.scene.{input => jfxsi}
import javafx.{event => jfxe}
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
object JavaFXMonixObservables {

View File

@ -1,18 +1,5 @@
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 {}

View File

@ -6,6 +6,7 @@ import scala.reflect.ClassTag
import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler
import akka.util.Timeout
import com.jayfella.jme.jfx.JavaFxUI
import com.jme3.app.Application
import com.jme3.app.SimpleApplication
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.InputListener
import com.jme3.input.controls.Trigger
import com.jme3.light.Light
import com.jme3.math.Vector3f
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
@ -48,23 +50,23 @@ import monix.reactive.OverflowStrategy
import monix.reactive.observers.Subscriber
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.state.MyBaseState
import com.jme3.light.Light
import com.jayfella.jme.jfx.JavaFxUI
import com.jme3.material.Material
case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
case class EnumActionEvent[T <: EnumEntry](
final case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
final case class EnumActionEvent[T <: EnumEntry](
binding: T,
value: Boolean,
tpf: Float
)
case class AnalogEvent(binding: Action, value: Float, tpf: Float)
case class EnumAnalogEvent[T <: EnumEntry](
final case class AnalogEvent(binding: Action, value: Float, tpf: Float)
final case class EnumAnalogEvent[T <: EnumEntry](
binding: T,
value: 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 {
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)
extends AnyVal {
@ -356,10 +365,7 @@ package object implicits {
import akka.actor.typed.scaladsl.AskPattern._
/**
* @param replyTo
* @param timeout
* @param scheduler
* @return
* Same as [[ask]] but returns a [[Task]]
*/
def askL[Res](
replyTo: ActorRef[Res] => Req
@ -378,12 +384,16 @@ package object implicits {
* @return
*/
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 ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
// ask(replyTo)(timeout, scheduler)
// }
implicit final class InputManagerExt(private val inputManager: InputManager)
extends AnyVal {
@ -523,7 +533,7 @@ package object implicits {
def collisionObservable(): Observable[PhysicsCollisionEvent] = {
Observable.create(OverflowStrategy.Unbounded) { sub =>
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable()
val cl = new PhysicsCollisionListener {
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 cl = new PhysicsTickListener {
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Left(event)) == Ack.Stop) {
val event = new PrePhysicsTickEvent(space)
if (sub.onNext(event) == Ack.Stop) {
sub.onComplete()
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 = {
val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Right(event)) == Ack.Stop) {
val event = new PhysicsTickEvent(space)
if (sub.onNext(event) == Ack.Stop) {
sub.onComplete()
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) = {

View File

@ -1,18 +1,17 @@
package wow.doge.mygame.launcher
import scalafx.geometry.Insets
import scalafx.geometry.Orientation
import scalafx.geometry.Pos
import scalafx.scene.Scene
import scalafx.scene.control.Button
import scalafx.scene.effect.DropShadow
import scalafx.scene.layout.FlowPane
import scalafx.scene.layout.HBox
import scalafx.scene.layout.VBox
import scalafx.scene.paint.Color._
import scalafx.scene.paint._
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 {
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
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 javafx.application.Platform
import scalafx.scene.control.Button
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.reactive.Observable
import monix.bio.Fiber
import scalafx.stage.StageStyle
import scalafx.Includes._
import wow.doge.mygame.utils.ResizeHelper
import scalafx.scene.Scene
import scalafx.scene.layout.VBox
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.control.Button
import scalafx.stage.StageStyle
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.implicits.JavaFXMonixObservables._
import monix.catnap.cancelables.SingleAssignCancelableF
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
// }
import wow.doge.mygame.utils.IOUtils._
object Launcher {
sealed trait LauncherResult
@ -41,19 +29,7 @@ object Launcher {
val schedulers: Schedulers,
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 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) {
@ -62,7 +38,6 @@ class Launcher private (props: Launcher.Props) {
private lazy val launchButton = new Button {
text = "Launch"
}
// private lazy val launchButtonObs =
private lazy val launchAction =
launchButton
@ -72,7 +47,6 @@ class Launcher private (props: Launcher.Props) {
private lazy val exitButton = new Button {
text = "Exit"
}
// private lazy val exitButtonObs =
private lazy val exitAction =
exitButton
@ -80,9 +54,6 @@ class Launcher private (props: Launcher.Props) {
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit)))
private lazy val _scene =
// new Scene {
// content = new VBox
// }
DefaultUI.scene(launchButton, exitButton)
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) =
for {
_ <- Task(Platform.setImplicitExit(false))
fib <- Task(internal.main(Array.empty)).start
_ <- 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
fib2 <- toIO(
Observable(launchAction, exitAction).merge
.doOnNext(_ =>
ETask(internal.stage.close()).executeOn(props.schedulers.fx)
)
// .doOnNext(_ => toTask(fib.cancel))
.completedL
).start
c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel)
// _ <- Task {
// internal.stage = stage
// }.executeOn(props.schedulers.fx)
// .delayExecution(delay)
} yield (c)
}

View File

@ -1,5 +1,7 @@
package wow.doge.mygame.math;
import Math.{abs, sqrt, pow}
case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f)
object ImVector3f {
@ -8,4 +10,6 @@ object ImVector3f {
val UNIT_Y = ImVector3f(0, 1, 0)
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

View File

@ -1,14 +1,24 @@
package wow.doge.mygame.events
package wow.doge.mygame.subsystems.events
object Events {
sealed trait Event
final case object BulletFired extends Event
// type BulletFired = BulletFired.type
final case class EventWithData(data: Int) extends Event
sealed trait Tick extends Event
object Tick {
final case object RenderTick extends Tick
final case object PhysicsTick extends Tick
sealed trait TickEvent extends Event
object TickEvent {
final case object RenderTick extends TickEvent
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.scaladsl.Behaviors
import akka.util.Timeout
import cats.effect.Resource
import com.typesafe.scalalogging.{Logger => SLLogger}
import monix.bio.Task
import com.typesafe.scalalogging.{Logger => SLogger}
import org.slf4j.event.Level
import wow.doge.mygame.events.EventBus
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(
spawnProtocol: ActorSystem[SpawnProtocol.Command]
) {
class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) {
implicit lazy val s = spawnProtocol.scheduler
implicit lazy val timeout = Timeout(1.second)
lazy val eventBusLogger = SLLogger[EventBus[_]]
lazy val eventBusLogger = SLogger[EventBus[_]]
lazy val playerMovementEventBusTask =
createEventBus[PlayerMovementEvent]("movementEventBus")
@ -33,6 +30,11 @@ class EventsModule2(
lazy val playerCameraEventBusTask =
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) =
spawnProtocol.askL(
SpawnProtocol.Spawn[EventBus.Command[T]](
@ -49,17 +51,8 @@ class EventsModule2(
_
)
)
type EventBuses = (
ActorRef[
EventBus.Command[EntityMovementEvent.PlayerMovementEvent],
],
ActorRef[EventBus.Command[PlayerCameraEvent]]
)
val resource: Resource[Task, EventBuses] =
Resource.liftF(for {
playerMovementEventBus <- playerMovementEventBusTask
playerCameraEventBus <- playerCameraEventBusTask
} yield (playerMovementEventBus, playerCameraEventBus))
}
object EventsModule {
type GameEventBus[T] = ActorRef[EventBus.Command[T]]
}

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
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 io.circe._
import io.circe.generic.JsonCodec
import io.circe.generic.semiauto._
import io.circe.parser._
import monix.bio.IO
@ -15,7 +16,6 @@ import monix.bio.UIO
import monix.reactive.Consumer
import monix.reactive.Observable
import wow.doge.mygame.utils.IOUtils
import io.circe.generic.JsonCodec
@JsonCodec
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.util.Timeout
import cats.effect.Resource
import monix.bio.Task
import wow.doge.mygame.scriptsystem.ScriptCachingActor
import wow.doge.mygame.utils.AkkaUtils
import monix.bio.Task
/**
* 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.PrintStream
import cats.effect.Resource
import monix.bio.Task
import scalafx.scene.control.TextArea
import scalafx.application.Platform
import scalafx.scene.control.TextArea
trait ConsoleStreamable[T] {
def println(inst: T, text: String): Unit
@ -18,7 +16,8 @@ class GenericConsoleStream[T](
outputStream: OutputStream,
val config: GenericConsoleStream.Config =
GenericConsoleStream.Config.default,
private var streamable: Option[T] = None
// TODO make this atomic
private var _streamable: Option[T] = None
)(implicit
cs: ConsoleStreamable[T]
) extends PrintStream(outputStream, true) {
@ -29,26 +28,24 @@ class GenericConsoleStream[T](
override def println(text: String): Unit =
if (config.exclusive) {
printToStreamable(streamable, text)
printToStreamable(_streamable, text)
} else {
defaultOut.println(text)
printToStreamable(streamable, text)
printToStreamable(_streamable, text)
}
override def print(text: String): Unit =
streamable.foreach(s =>
if (config.exclusive) {
printToStreamable(streamable, text)
printToStreamable(_streamable, text)
} else {
defaultOut.println(text)
printToStreamable(streamable, text)
printToStreamable(_streamable, text)
}
)
def :=(s: T) = {
streamable match {
_streamable match {
case Some(value) =>
case None => streamable = Some(s)
case None => _streamable = Some(s)
}
}
}
@ -64,41 +61,25 @@ object GenericConsoleStream {
}
implicit val implJFXConsoleStreamForTextArea =
new ConsoleStreamable[scalafx.scene.control.TextArea] {
new ConsoleStreamable[TextArea] {
override def println(
ta: scalafx.scene.control.TextArea,
ta: TextArea,
text: String
): Unit =
ta.appendText(text + "\n")
Platform.runLater(() => ta.appendText(text + "\n"))
override def print(
ta: scalafx.scene.control.TextArea,
ta: TextArea,
text: String
): Unit =
ta.appendText(text)
Platform.runLater(() => ta.appendText(text))
}
// def textAreaStreamResource(ta: TextArea) =
// Resource.make(
// Task(
// new GenericConsoleStream(
// outputStream = new ByteArrayOutputStream(),
// streamable = ta
// )
// )
// )(s => Task(s.close()))
def textAreaStream(
// ta: TextArea
) =
// Task(
def textAreaStream() =
new GenericConsoleStream[TextArea](
outputStream = new ByteArrayOutputStream()
// streamable = ta
)
// )
// (s => Task(s.close()))
}