forked from nova/jmonkey-test
Added camera movement, lighting, physics
This commit is contained in:
parent
b0994bcdbe
commit
85a28b3c39
@ -82,12 +82,14 @@ lazy val root = (project in file(".")).settings(
|
|||||||
"com.softwaremill.sttp.client" %% "circe" % "2.2.5",
|
"com.softwaremill.sttp.client" %% "circe" % "2.2.5",
|
||||||
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5",
|
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5",
|
||||||
"com.github.valskalla" %% "odin-monix" % "0.8.1",
|
"com.github.valskalla" %% "odin-monix" % "0.8.1",
|
||||||
|
"com.github.valskalla" %% "odin-json" % "0.9.1",
|
||||||
"com.softwaremill.macwire" %% "util" % "2.3.7",
|
"com.softwaremill.macwire" %% "util" % "2.3.7",
|
||||||
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided",
|
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided",
|
||||||
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
|
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
|
||||||
"com.github.valskalla" %% "odin-slf4j" % "0.8.1",
|
"com.github.valskalla" %% "odin-slf4j" % "0.8.1",
|
||||||
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
|
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
|
||||||
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1"
|
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1",
|
||||||
|
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
|
||||||
),
|
),
|
||||||
// Determine OS version of JavaFX binaries
|
// Determine OS version of JavaFX binaries
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
jme-dispatcher {
|
# jme-dispatcher {
|
||||||
type = "Dispatcher"
|
# type = "Dispatcher"
|
||||||
name = "JME-Thread"
|
# name = "JME-Thread"
|
||||||
executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
|
# executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator"
|
||||||
throughput = 1
|
# throughput = 1
|
||||||
}
|
# }
|
||||||
akka.jvm-exit-on-fatal-error = on
|
# akka.jvm-exit-on-fatal-error = on
|
@ -4,7 +4,7 @@ import com.jme3.input.Action
|
|||||||
|
|
||||||
package object animation {
|
package object animation {
|
||||||
|
|
||||||
implicit class AnimChannelWrap(val uval: AnimChannel) extends AnyVal {
|
implicit class AnimChannelWrap(private val uval: AnimChannel) extends AnyVal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current animation that is played by this AnimChannel.
|
* Set the current animation that is played by this AnimChannel.
|
||||||
@ -33,7 +33,7 @@ package object animation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class AnimEventListenerWrap(val uval: AnimEventListener) extends AnyVal {
|
implicit class AnimEventListenerWrap(private val uval: AnimEventListener) extends AnyVal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when an animation "cycle" is done. For non-looping animations,
|
* Invoked when an animation "cycle" is done. For non-looping animations,
|
||||||
|
@ -5,7 +5,7 @@ package com.jme3
|
|||||||
*/
|
*/
|
||||||
package object app {
|
package object app {
|
||||||
|
|
||||||
implicit class SimpleApplicationWrap(val uval: SimpleApplication) extends AnyVal {
|
implicit class SimpleApplicationWrap(private val uval: SimpleApplication) extends AnyVal {
|
||||||
|
|
||||||
//FIXME: proof of concept, remove later
|
//FIXME: proof of concept, remove later
|
||||||
def testWrap: String = uval.hashCode().toString
|
def testWrap: String = uval.hashCode().toString
|
||||||
|
@ -5,7 +5,7 @@ package com.jme3.input
|
|||||||
*/
|
*/
|
||||||
package object controls {
|
package object controls {
|
||||||
|
|
||||||
implicit class ActionListenerWrap(val uval: ActionListener) extends AnyVal {
|
implicit class ActionListenerWrap(private val uval: ActionListener) extends AnyVal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an input to which this listener is registered to is invoked.
|
* Called when an input to which this listener is registered to is invoked.
|
||||||
@ -18,7 +18,7 @@ package object controls {
|
|||||||
uval.onAction(action.name, keyPressed, tpf)
|
uval.onAction(action.name, keyPressed, tpf)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class AnalogListenerWrap(val uval: AnalogListener) extends AnyVal {
|
implicit class AnalogListenerWrap(private val uval: AnalogListener) extends AnyVal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to notify the implementation that an analog event has occurred.
|
* Called to notify the implementation that an analog event has occurred.
|
||||||
|
@ -7,7 +7,7 @@ import com.jme3.input.controls.{InputListener, Trigger}
|
|||||||
*/
|
*/
|
||||||
package object input {
|
package object input {
|
||||||
|
|
||||||
implicit class InputManagerWrap(val uval: InputManager) extends AnyVal {
|
implicit class InputManagerWrap(private val uval: InputManager) extends AnyVal {
|
||||||
def addMapping(action: Action, triggers: Trigger*): Unit =
|
def addMapping(action: Action, triggers: Trigger*): Unit =
|
||||||
uval.addMapping(action.name, triggers: _*)
|
uval.addMapping(action.name, triggers: _*)
|
||||||
def addListener(listener: InputListener, actions: Action*): Unit =
|
def addListener(listener: InputListener, actions: Action*): Unit =
|
||||||
|
@ -33,14 +33,14 @@ package object scene {
|
|||||||
def apply(name: String): Node = new Node(name)
|
def apply(name: String): Node = new Node(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class NodeWrap(val uval: Node) extends AnyVal {
|
implicit class NodeWrap(private val uval: Node) extends AnyVal {
|
||||||
|
|
||||||
def getControlMaybe[T <: Control](controlType: Class[T]): Option[T] =
|
def getControlMaybe[T <: Control](controlType: Class[T]): Option[T] =
|
||||||
Option(uval.getControl(controlType))
|
Option(uval.getControl(controlType))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class SpatialWrap(val uval: Spatial) extends AnyVal {
|
implicit class SpatialWrap(private val uval: Spatial) extends AnyVal {
|
||||||
|
|
||||||
def toNode: Either[ClassCastException, Node] =
|
def toNode: Either[ClassCastException, Node] =
|
||||||
uval match {
|
uval match {
|
||||||
|
@ -11,6 +11,8 @@ import cats.arrow.FunctionK
|
|||||||
import _root_.monix.execution.Scheduler.Implicits.global
|
import _root_.monix.execution.Scheduler.Implicits.global
|
||||||
import io.odin.syntax._
|
import io.odin.syntax._
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
import scala.collection.immutable.ArraySeq
|
||||||
|
import io.odin.json.Formatter
|
||||||
|
|
||||||
//effect type should be specified inbefore
|
//effect type should be specified inbefore
|
||||||
//log line will be recorded right after the call with no suspension
|
//log line will be recorded right after the call with no suspension
|
||||||
@ -26,27 +28,54 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
|||||||
def apply[A](fa: _root_.monix.bio.Task[A]): IO[A] = fa.to[IO]
|
def apply[A](fa: _root_.monix.bio.Task[A]): IO[A] = fa.to[IO]
|
||||||
}
|
}
|
||||||
|
|
||||||
val (fLogger, release) =
|
private lazy val (defaultConsoleLogger, release1) =
|
||||||
// MainModule.DefaultFileLogger.mapK(monixToCats).allocated.unsafeRunSync()
|
consoleLogger[IO](minLevel = Level.Debug)
|
||||||
|
.withAsync(timeWindow = 1.milliseconds)
|
||||||
|
.allocated
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
private lazy val (mainFileLogger, release2) =
|
||||||
fileLogger[IO](
|
fileLogger[IO](
|
||||||
"log2.log"
|
"application-log-2.log",
|
||||||
).withAsync(timeWindow = 1.seconds).allocated.unsafeRunSync()
|
Formatter.json,
|
||||||
// Runtime
|
minLevel = Level.Debug
|
||||||
// .getRuntime()
|
).withAsync(timeWindow = 1.milliseconds)
|
||||||
// .addShutdownHook(new Thread {
|
.allocated
|
||||||
// release.unsafeRunSync()
|
.unsafeRunSync()
|
||||||
// })
|
|
||||||
scala.sys.addShutdownHook(release.unsafeRunSync())
|
private lazy val (eventBusFileLogger, release3) =
|
||||||
|
fileLogger[IO](
|
||||||
|
"eventbus.log",
|
||||||
|
Formatter.json,
|
||||||
|
minLevel = Level.Debug
|
||||||
|
).withAsync(timeWindow = 1.milliseconds)
|
||||||
|
.allocated
|
||||||
|
.unsafeRunSync()
|
||||||
|
|
||||||
|
{
|
||||||
|
ArraySeq(release1, release2, release3).foreach(r =>
|
||||||
|
sys.addShutdownHook(r.unsafeRunSync())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val loggers: PartialFunction[String, Logger[IO]] = {
|
val loggers: PartialFunction[String, Logger[IO]] = {
|
||||||
case "some.external.package.SpecificClass" =>
|
case "some.external.package.SpecificClass" =>
|
||||||
consoleLogger[IO](minLevel = Level.Warn) //disable noisy external logs
|
//disable noisy external logs
|
||||||
|
defaultConsoleLogger.withMinimalLevel(Level.Warn)
|
||||||
case asyncHttpClient
|
case asyncHttpClient
|
||||||
if asyncHttpClient.startsWith("org.asynchttpclient.netty") =>
|
if asyncHttpClient.startsWith("org.asynchttpclient.netty") =>
|
||||||
consoleLogger[IO](minLevel = Level.Warn)
|
defaultConsoleLogger.withMinimalLevel(Level.Warn)
|
||||||
|
// case s
|
||||||
|
// if s.startsWith(
|
||||||
|
// "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") =>
|
||||||
|
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| eventBusFileLogger
|
||||||
case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
|
case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
|
||||||
consoleLogger[IO]() |+| fLogger
|
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger
|
||||||
case _ => //if wildcard case isn't provided, default logger is no-op
|
case _ => //if wildcard case isn't provided, default logger is no-op
|
||||||
consoleLogger[IO]()
|
defaultConsoleLogger.withMinimalLevel(Level.Debug)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
src/main/scala/wow/doge/mygame/ActorSystemModule.scala
Normal file
27
src/main/scala/wow/doge/mygame/ActorSystemModule.scala
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorSystem
|
||||||
|
import cats.effect.Resource
|
||||||
|
import monix.bio.Task
|
||||||
|
import io.odin.Logger
|
||||||
|
import wow.doge.mygame.game.GameApp
|
||||||
|
import wow.doge.mygame.executors.Schedulers
|
||||||
|
|
||||||
|
trait ActorSystemModule {
|
||||||
|
|
||||||
|
def logger: Logger[Task]
|
||||||
|
def app: GameApp
|
||||||
|
def schedulers: Schedulers
|
||||||
|
|
||||||
|
lazy val actorsResource =
|
||||||
|
Resource.make(logger.info("Creating Actor System") >> Task {
|
||||||
|
ActorSystem(
|
||||||
|
RootActor(app, schedulers, logger = logger),
|
||||||
|
name = "GameActorSystem"
|
||||||
|
)
|
||||||
|
})(sys =>
|
||||||
|
logger.info("Shutting down actor system") >> Task(
|
||||||
|
sys.terminate()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -1,12 +1,9 @@
|
|||||||
package wow.doge.mygame
|
package wow.doge.mygame
|
||||||
|
|
||||||
import com.jme3.app.StatsAppState
|
|
||||||
|
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import cats.effect.Resource
|
import cats.effect.Resource
|
||||||
import io.odin.syntax._
|
import io.odin.syntax._
|
||||||
|
|
||||||
// import io.odin.monix._
|
|
||||||
import cats.effect.ExitCode
|
import cats.effect.ExitCode
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import com.softwaremill.macwire._
|
import com.softwaremill.macwire._
|
||||||
@ -15,72 +12,68 @@ import monix.bio.BIOApp
|
|||||||
import monix.bio.UIO
|
import monix.bio.UIO
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import io.odin._
|
import io.odin._
|
||||||
import wow.doge.mygame.executors.JMERunner
|
|
||||||
import com.jme3.bullet.BulletAppState
|
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.game.GameAppResource
|
||||||
// import wow.doge.mygame.implicits._
|
import io.odin.json.Formatter
|
||||||
|
|
||||||
// object Main extends App {
|
|
||||||
// import java.util.logging.{Logger, Level}
|
|
||||||
// Logger.getLogger("").setLevel(Level.SEVERE)
|
|
||||||
|
|
||||||
// // runner.runCode("""|println("starting scala script engine")""".stripMargin)
|
|
||||||
// val gameApp = new GameApp(
|
|
||||||
// // new EntityDataState(),
|
|
||||||
// // new TestAppState(),
|
|
||||||
// // new PlayerMovementState(),
|
|
||||||
// // new FlyCamAppState(),
|
|
||||||
// new StatsAppState()
|
|
||||||
// )
|
|
||||||
// val settings = new AppSettings(true)
|
|
||||||
// // settings.setVSync(true)
|
|
||||||
// settings.setFrameRate(144)
|
|
||||||
// gameApp.setSettings(settings)
|
|
||||||
// val actorSystem = ActorSystem(RootActor(gameApp), "rootActor")
|
|
||||||
// // actorSystem.eventStream
|
|
||||||
// // gameApp.start()
|
|
||||||
// println("here 1")
|
|
||||||
// // actorSystem.terminate()
|
|
||||||
// // JMEExecutorService.shutdown()
|
|
||||||
// // println("here 2")
|
|
||||||
// //FIXME remove this
|
|
||||||
// // System.exit(0)
|
|
||||||
// }
|
|
||||||
|
|
||||||
object Main extends BIOApp with MainModule {
|
object Main extends BIOApp with MainModule {
|
||||||
import java.util.logging.{Logger => JLogger, Level}
|
import java.util.logging.{Logger => JLogger, Level}
|
||||||
JLogger.getLogger("").setLevel(Level.SEVERE)
|
JLogger.getLogger("").setLevel(Level.SEVERE)
|
||||||
|
|
||||||
def run(args: List[String]): UIO[ExitCode] = {
|
def appResource =
|
||||||
|
for {
|
||||||
lazy val appResource = for {
|
|
||||||
logger <-
|
logger <-
|
||||||
consoleLogger().withAsync(timeWindow = 1.seconds) |+| fileLogger(
|
consoleLogger().withAsync(timeWindow = 1.milliseconds) |+|
|
||||||
"log.log"
|
fileLogger(
|
||||||
).withAsync(timeWindow = 1.seconds)
|
"application-log-1.log",
|
||||||
|
Formatter.json
|
||||||
|
).withAsync(timeWindow = 1.milliseconds)
|
||||||
jmeScheduler <- jMESchedulerResource
|
jmeScheduler <- jMESchedulerResource
|
||||||
// consoleTextArea <- Resource.liftF(Task(new TextArea()))
|
// consoleTextArea <- Resource.liftF(Task(new TextArea()))
|
||||||
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
|
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
|
||||||
gameApp <- {
|
(gameApp, gameAppFib) <- {
|
||||||
new BulletAppState()
|
// new BulletAppState()
|
||||||
// bas.setThreadingType(Thr)
|
// bas.setThreadingType(Thr)
|
||||||
gameAppResource(new StatsAppState())
|
// gameAppResource(new StatsAppState())
|
||||||
|
wire[GameAppResource].make
|
||||||
}
|
}
|
||||||
_ <- Resource.liftF(IO(JMERunner.runner = gameApp))
|
|
||||||
actorSystem <- wireWith(actorSystemResource _)
|
|
||||||
// _ <- Resource.liftF(
|
|
||||||
// Task(gameApp.start()).asyncBoundary
|
|
||||||
// .executeOn(Scheduler(JMEExecutorService))
|
|
||||||
// )
|
|
||||||
|
|
||||||
_ <- Resource.liftF(gameApp.enqueueT(actorSystem ! RootActor.Start))
|
// _ <- Resource.liftF(IO(JMERunner.runner = gameApp))
|
||||||
_ <- Resource.liftF {
|
// _ <- Resource.liftF(IO {
|
||||||
IO(gameApp.start())
|
// new ActorSystemModule {}
|
||||||
.executeOn(jmeScheduler)
|
// })
|
||||||
}
|
actorSystem <- wireWith(actorSystemResource _)
|
||||||
|
_ <- Resource.liftF(
|
||||||
|
gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler))
|
||||||
|
)
|
||||||
|
// _ <- Resource.liftF {
|
||||||
|
// Task {
|
||||||
|
// implicit val sched = actorSystem.scheduler
|
||||||
|
// implicit val timeout = Timeout(2.seconds)
|
||||||
|
// // val module = new EventsModule {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// actorSystem <- wireWith(actorSystemResource2 _)
|
||||||
|
// (tickEventBus, playerMovementEventBus) <- wireWith(eventBusesResource _)
|
||||||
|
// rootActor <- wireWith(rootActorResource _)
|
||||||
|
// inputSystemHandler <- {
|
||||||
|
// inputHandlerSystemResource(
|
||||||
|
// GameInputHandler.Props(gameApp.inputManager, playerMovementEventBus)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// gameSystemsInitializer <-
|
||||||
|
// gameSystemsResource(actorSystem, inputSystemHandler)
|
||||||
|
// _ <- Resource.liftF(
|
||||||
|
// gameApp.enqueueT(rootActor ! RootActor.Start(actorSystem.scheduler))
|
||||||
|
// )
|
||||||
|
// _ <- Resource.liftF {
|
||||||
|
// IO(gameApp.start())
|
||||||
|
// .executeOn(jmeScheduler)
|
||||||
|
// }
|
||||||
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
|
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
|
||||||
} yield ()
|
} yield (gameAppFib)
|
||||||
|
|
||||||
|
def run(args: List[String]): UIO[ExitCode] = {
|
||||||
|
|
||||||
// Console.withOut(
|
// Console.withOut(
|
||||||
// new JFXConsoleStream(
|
// new JFXConsoleStream(
|
||||||
@ -89,16 +82,7 @@ object Main extends BIOApp with MainModule {
|
|||||||
// )
|
// )
|
||||||
// )(())
|
// )(())
|
||||||
appResource
|
appResource
|
||||||
.use(_ =>
|
.use(_.join)
|
||||||
// Task(gameApp.start())
|
|
||||||
// .executeOn(Scheduler(JMEExecutorService))
|
|
||||||
// .asyncBoundary
|
|
||||||
// Task.never
|
|
||||||
Task.unit
|
|
||||||
// >>
|
|
||||||
|
|
||||||
// .executeOn(Scheduler(JMEExecutorService))
|
|
||||||
)
|
|
||||||
.onErrorHandle(_.printStackTrace())
|
.onErrorHandle(_.printStackTrace())
|
||||||
.as(ExitCode.Success)
|
.as(ExitCode.Success)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,12 @@ import wow.doge.mygame.executors.ExecutorsModule
|
|||||||
import akka.actor.typed.scaladsl.ActorContext
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import com.softwaremill.macwire._
|
import com.softwaremill.macwire._
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import akka.actor.typed.Scheduler
|
||||||
|
import wow.doge.mygame.utils.AkkaUtils
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
trait MainModule extends GameModule with ExecutorsModule {
|
trait MainModule extends GameModule with ExecutorsModule {
|
||||||
def actorSystemResource(
|
def actorSystemResource(
|
||||||
@ -21,34 +27,74 @@ trait MainModule extends GameModule with ExecutorsModule {
|
|||||||
schedulers: Schedulers
|
schedulers: Schedulers
|
||||||
): Resource[Task, ActorSystem[RootActor.Command]] =
|
): Resource[Task, ActorSystem[RootActor.Command]] =
|
||||||
Resource.make(logger.info("Creating Actor System") >> Task {
|
Resource.make(logger.info("Creating Actor System") >> Task {
|
||||||
ActorSystem(RootActor(app, schedulers), name = "GameActorSystem")
|
ActorSystem(
|
||||||
|
wire[RootActor.Props].create(RootActor.State.empty),
|
||||||
|
name = "GameActorSystem"
|
||||||
|
)
|
||||||
})(sys =>
|
})(sys =>
|
||||||
logger.info("Shutting down actor system") >> Task(
|
logger.info("Shutting down actor system") >> Task(
|
||||||
sys.terminate()
|
sys.terminate()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
object MainModule {
|
def actorSystemResource2(
|
||||||
|
logger: Logger[Task]
|
||||||
|
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
|
||||||
|
Resource.make(logger.info("Creating Actor System") >> Task {
|
||||||
|
ActorSystem(
|
||||||
|
SpawnProtocol(),
|
||||||
|
name = "GameActorSystem2"
|
||||||
|
)
|
||||||
|
})(sys =>
|
||||||
|
logger.info("Shutting down actor system") >> Task(
|
||||||
|
sys.terminate()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// import cats.implicits._
|
def rootActorResource(
|
||||||
import scala.concurrent.duration._
|
loggerL: Logger[Task],
|
||||||
val DefaultFileLogger: Resource[Task, Logger[Task]] =
|
app: GameApp,
|
||||||
fileLogger[Task](
|
schedulers: Schedulers,
|
||||||
"log.log"
|
spawnProtocol: ActorSystem[SpawnProtocol.Command]
|
||||||
).withAsync(timeWindow = 1.seconds)
|
): Resource[Task, ActorRef[RootActor.Command]] =
|
||||||
|
Resource.make(
|
||||||
|
loggerL.info("Creating Root Actor") >>
|
||||||
|
AkkaUtils.spawnActorL(
|
||||||
|
behavior = RootActor(app, schedulers, logger = loggerL),
|
||||||
|
actorName = "RootActor",
|
||||||
|
spawnProtocol = spawnProtocol
|
||||||
|
)(1.seconds, spawnProtocol.scheduler)
|
||||||
|
)(actor =>
|
||||||
|
loggerL.info("Shutting down root actor") >> (actor !! RootActor.Stop)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object RootActor {
|
object RootActor {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case object Start extends Command
|
final case class Start(akkaScheduler: Scheduler) extends Command
|
||||||
final case object Stop extends Command
|
final case object Stop extends Command
|
||||||
|
|
||||||
|
final case class Props(
|
||||||
|
app: GameApp,
|
||||||
|
schedulers: Schedulers,
|
||||||
|
logger: Logger[Task]
|
||||||
|
) {
|
||||||
|
def create(state: State): Behavior[Command] =
|
||||||
|
Behaviors.setup { ctx =>
|
||||||
|
ctx.log.info("Hello from root actor")
|
||||||
|
wire[RootActor].receive(State.empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final case class State(initialized: Boolean = false)
|
final case class State(initialized: Boolean = false)
|
||||||
|
object State {
|
||||||
|
val empty = State()
|
||||||
|
}
|
||||||
def apply(
|
def apply(
|
||||||
app: GameApp,
|
app: GameApp,
|
||||||
schedulers: Schedulers,
|
schedulers: Schedulers,
|
||||||
state: State = State()
|
state: State = State.empty,
|
||||||
|
logger: Logger[Task]
|
||||||
): Behavior[Command] =
|
): Behavior[Command] =
|
||||||
Behaviors.setup { ctx =>
|
Behaviors.setup { ctx =>
|
||||||
ctx.log.info("Hello from root actor")
|
ctx.log.info("Hello from root actor")
|
||||||
@ -59,17 +105,19 @@ object RootActor {
|
|||||||
class RootActor(
|
class RootActor(
|
||||||
ctx: ActorContext[RootActor.Command],
|
ctx: ActorContext[RootActor.Command],
|
||||||
app: GameApp,
|
app: GameApp,
|
||||||
schedulers: Schedulers
|
schedulers: Schedulers,
|
||||||
|
logger: Logger[Task]
|
||||||
) {
|
) {
|
||||||
import RootActor._
|
import RootActor._
|
||||||
def receive(state: State): Behavior[Command] =
|
def receive(state: State): Behavior[Command] =
|
||||||
Behaviors.receiveMessage(msg =>
|
Behaviors.receiveMessage(msg =>
|
||||||
msg match {
|
msg match {
|
||||||
case Start =>
|
case Start(akkaScheduler) =>
|
||||||
if (!state.initialized) {
|
if (!state.initialized) {
|
||||||
ctx.log.info("Starting GameAppActor")
|
ctx.log.info("Starting GameAppActor")
|
||||||
|
val spawnProtocol = ctx.spawn(SpawnProtocol(), "spawnProtocol")
|
||||||
val _ = ctx.spawn(
|
val _ = ctx.spawn(
|
||||||
wireWith(GameAppActor.apply _),
|
wire[GameAppActor.Props].create,
|
||||||
"gameAppActor"
|
"gameAppActor"
|
||||||
// DispatcherSelector.fromConfig("jme-dispatcher")
|
// DispatcherSelector.fromConfig("jme-dispatcher")
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
package wow.doge.mygame.executors
|
package wow.doge.mygame.executors
|
||||||
|
|
||||||
import monix.execution.Scheduler
|
import monix.execution.Scheduler
|
||||||
|
import monix.execution.UncaughtExceptionReporter
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
final case class Schedulers(
|
final case class Schedulers(
|
||||||
blockingIO: Scheduler = Scheduler.io(),
|
blockingIO: Scheduler = Scheduler
|
||||||
async: Scheduler = Scheduler.global,
|
.io()
|
||||||
|
.withUncaughtExceptionReporter(Schedulers.reporter),
|
||||||
|
async: Scheduler = Scheduler.global
|
||||||
|
.withUncaughtExceptionReporter(Schedulers.reporter),
|
||||||
fx: Scheduler = JFXExecutionContexts.fxScheduler
|
fx: Scheduler = JFXExecutionContexts.fxScheduler
|
||||||
// jme: SchedulerService
|
.withUncaughtExceptionReporter(Schedulers.reporter)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object Schedulers {
|
||||||
|
val reporter = UncaughtExceptionReporter { ex =>
|
||||||
|
val logger = Logger[Schedulers]
|
||||||
|
logger.error("Uncaught exception", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -3,62 +3,35 @@ package wow.doge.mygame.game
|
|||||||
import com.jme3.app.SimpleApplication
|
import com.jme3.app.SimpleApplication
|
||||||
|
|
||||||
import com.jme3.app.state.AppState
|
import com.jme3.app.state.AppState
|
||||||
import com.jme3.bullet.BulletAppState
|
import monix.execution.{CancelablePromise => Promise}
|
||||||
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape
|
import monix.execution.CancelableFuture
|
||||||
import com.jme3.bullet.control.CharacterControl
|
import monix.bio.Task
|
||||||
import com.jme3.bullet.control.RigidBodyControl
|
import monix.execution.Scheduler
|
||||||
import com.jme3.bullet.util.CollisionShapeFactory
|
import wow.doge.mygame.executors.GUIExecutorService
|
||||||
import com.jme3.scene.Spatial
|
import monix.reactive.subjects.ConcurrentSubject
|
||||||
import com.jme3.syntax._
|
import monix.reactive.MulticastStrategy
|
||||||
import com.jme3.asset.plugins.ZipLocator
|
import monix.reactive.Observable
|
||||||
import com.jme3.math.ColorRGBA
|
import monix.execution.atomic.Atomic
|
||||||
import wow.doge.mygame.implicits._
|
import scala.collection.immutable.Queue
|
||||||
|
|
||||||
class GameApp(
|
class GameApp(
|
||||||
// actorSystem: ActorSystem[SpawnProtocol.Command],
|
// actorSystem: ActorSystem[SpawnProtocol.Command],
|
||||||
appStates: AppState*
|
appStates: AppState*
|
||||||
) extends SimpleApplication(appStates: _*) {
|
) extends SimpleApplication(appStates: _*) {
|
||||||
|
import GameApp._
|
||||||
// implicit val timeout = Timeout(10.seconds)
|
// implicit val timeout = Timeout(10.seconds)
|
||||||
// implicit val scheduler = actorSystem.scheduler
|
// implicit val scheduler = actorSystem.scheduler
|
||||||
private lazy val sceneModel: Spatial = assetManager.loadModel("main.scene")
|
|
||||||
private lazy val bulletAppState: BulletAppState = new BulletAppState()
|
|
||||||
// bulletAppState.setThreadingType(ThreadingType.SEQUENTIAL)
|
|
||||||
|
|
||||||
// We set up collision detection for the scene by creating a
|
// private lazy val taskQueueS = new ConcurrentLinkedQueue[MyTask[_]]()
|
||||||
// compound collision shape and a static RigidBodyControl with mass zero.
|
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
|
||||||
private lazy val sceneShape = CollisionShapeFactory.createMeshShape(
|
|
||||||
sceneModel.toNode match {
|
|
||||||
case util.Right(node) => node
|
|
||||||
case util.Left(ex) =>
|
|
||||||
throw new NotImplementedError("No fallback sceneshape")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
private lazy val landscape: RigidBodyControl =
|
|
||||||
new RigidBodyControl(sceneShape, 0)
|
|
||||||
|
|
||||||
// We set up collision detection for the player by creating
|
private val tickSubject =
|
||||||
// a capsule collision shape and a CharacterControl.
|
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
|
||||||
// The CharacterControl offers extra settings for
|
monix.execution.Scheduler.Implicits.global
|
||||||
// size, stepheight, jumping, falling, and gravity.
|
)
|
||||||
// We also put the player in its starting position.
|
// (scheduler)
|
||||||
protected lazy val capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1)
|
|
||||||
|
|
||||||
private lazy val player: CharacterControl =
|
|
||||||
new CharacterControl(capsuleShape, 0.05f)
|
|
||||||
|
|
||||||
override def simpleInitApp(): Unit = {
|
override def simpleInitApp(): Unit = {
|
||||||
discard { stateManager.attach(bulletAppState) }
|
|
||||||
assetManager.registerLocator(
|
|
||||||
// "src/main/resources/assets/town.zip",
|
|
||||||
(os.rel / "src" / "main" / "resources" / "assets" / "town.zip"),
|
|
||||||
classOf[ZipLocator]
|
|
||||||
)
|
|
||||||
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
|
||||||
sceneModel.setLocalScale(2f)
|
|
||||||
sceneModel.addControl(landscape)
|
|
||||||
discard { rootNode.attachChild(sceneModel) }
|
|
||||||
bulletAppState.getPhysicsSpace.add(landscape)
|
|
||||||
bulletAppState.getPhysicsSpace.add(player)
|
|
||||||
|
|
||||||
println("gameapp" + Thread.currentThread().getName())
|
println("gameapp" + Thread.currentThread().getName())
|
||||||
|
|
||||||
@ -105,6 +78,9 @@ class GameApp(
|
|||||||
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
|
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def tickObservable: Observable[Float] = tickSubject
|
||||||
|
|
||||||
override def simpleUpdate(tpf: Float): Unit = {
|
override def simpleUpdate(tpf: Float): Unit = {
|
||||||
// val rot2 = rot.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1))
|
// val rot2 = rot.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1))
|
||||||
// val rotation = geom.getLocalRotation()
|
// val rotation = geom.getLocalRotation()
|
||||||
@ -113,30 +89,62 @@ class GameApp(
|
|||||||
|
|
||||||
// geom.updateModelBound()
|
// geom.updateModelBound()
|
||||||
// geom.updateGeometricState()
|
// geom.updateGeometricState()
|
||||||
|
tickSubject.onNext(tpf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// override def stop(): Unit = {
|
override def stop(): Unit = {
|
||||||
// actorSystem.terminate()
|
tickSubject.onComplete()
|
||||||
// super.stop()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// override def start(): Unit = {
|
|
||||||
// monix.eval.Task(super.start()).runToFuture(Scheduler(JMEExecutorService))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// def start(system: ActorRef[RootActor.Command]) = {
|
|
||||||
// // system ! RootActor.Start
|
|
||||||
// super.start()
|
|
||||||
|
|
||||||
// }
|
|
||||||
// override def stop(): Unit = {
|
|
||||||
// println("stopping")
|
|
||||||
// }
|
|
||||||
def stop[T](cb: () => T): Unit = {
|
|
||||||
println("destroy")
|
|
||||||
cb()
|
|
||||||
super.stop()
|
super.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// override def stop(): Unit = {}
|
def enqueueScala[T](cb: () => T): CancelableFuture[T] = {
|
||||||
|
val p = Promise[T]()
|
||||||
|
// p.success(cb())
|
||||||
|
// taskQueueS.add(MyTask(p, cb))
|
||||||
|
taskQueue2.transform(_ :+ MyTask(p, cb))
|
||||||
|
p.future
|
||||||
|
}
|
||||||
|
// taskQueue2.transform(_ :+ MyTask(p, cb))
|
||||||
|
// p
|
||||||
|
def enqueueL[T](cb: () => T): Task[T] =
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
Task.deferFuture(enqueueScala(cb))
|
||||||
|
|
||||||
|
// taskQueueS.add(MyTask(p, cb))
|
||||||
|
|
||||||
|
override protected def runQueuedTasks(): Unit = {
|
||||||
|
// Option(taskQueueS.poll()).foreach {
|
||||||
|
// case MyTask(p, cb) =>
|
||||||
|
// p.success(cb())
|
||||||
|
// }
|
||||||
|
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) =
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
@ -1,161 +1,86 @@
|
|||||||
package wow.doge.mygame.game
|
package wow.doge.mygame.game
|
||||||
|
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import wow.doge.mygame.state.PlayerMovementState
|
|
||||||
import com.jme3.scene.Geometry
|
import com.jme3.scene.Geometry
|
||||||
|
|
||||||
import wow.doge.mygame.events.EventBus
|
|
||||||
import wow.doge.mygame.events.Events
|
import wow.doge.mygame.events.Events
|
||||||
import wow.doge.mygame.state.ImMovementActor
|
|
||||||
import com.jme3.scene.CameraNode
|
import com.jme3.scene.CameraNode
|
||||||
import com.jme3.scene.Node
|
import com.jme3.scene.Node
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import wow.doge.mygame.game.nodes.PlayerNode
|
|
||||||
import com.softwaremill.macwire._
|
import com.softwaremill.macwire._
|
||||||
|
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import io.odin.Logger
|
||||||
|
import monix.bio.Task
|
||||||
|
import akka.actor.typed.Scheduler
|
||||||
|
import scala.util.Failure
|
||||||
|
import scala.util.Success
|
||||||
|
import com.jme3.app.StatsAppState
|
||||||
|
|
||||||
object GameAppActor {
|
object GameAppActor {
|
||||||
import Methods._
|
import Methods._
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
case object XD extends Command
|
case object ApplicationStarted extends Command
|
||||||
|
case class ApplicationStartFailed(reason: String) extends Command
|
||||||
case object Stop extends Command
|
case object Stop extends Command
|
||||||
def apply(app: GameApp, schedulers: Schedulers) =
|
|
||||||
Behaviors.setup[Command] { ctx =>
|
|
||||||
ctx.log.info("Hello from GameAppActor")
|
|
||||||
// lazy val b = new Box(1, 1, 1)
|
|
||||||
// lazy val geom = new Geometry("Box", b)
|
|
||||||
// lazy val playerNode = new Node("PlayerNode")
|
|
||||||
// lazy val camNode = new CameraNode("CameraNode", app.getCamera())
|
|
||||||
// lazy val players = createPlayer(geom, app.getCamera())
|
|
||||||
|
|
||||||
// ctx.pipeToSelf(
|
case class Props(
|
||||||
// app.enqueueF(() => ())(monix.execution.Scheduler.io("aege"))
|
app: GameApp,
|
||||||
// ) {
|
akkaScheduler: Scheduler,
|
||||||
// case x =>
|
schedulers: Schedulers,
|
||||||
// println("SENDEDEEEEEd")
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
// XD
|
loggerL: Logger[Task]
|
||||||
// }
|
) {
|
||||||
// ctx.pipeToSelf(
|
def create =
|
||||||
// createPlayer(
|
Behaviors.setup[Command] { ctx =>
|
||||||
// geom,
|
ctx.log.info("Hello from GameAppActor")
|
||||||
// app.getCamera(),
|
|
||||||
// schedulers.jme
|
|
||||||
// )
|
|
||||||
// ) {
|
|
||||||
// case x =>
|
|
||||||
// println(x)
|
|
||||||
// XD
|
|
||||||
// }
|
|
||||||
val subscribingActor = ctx.spawn(SubscribingActor(), "subscriber-1")
|
|
||||||
|
|
||||||
val tickEventBus =
|
{
|
||||||
ctx.spawn(Behaviors.logMessages(EventBus[Events.Tick]()), "eventBus1")
|
implicit val s = schedulers.async
|
||||||
|
|
||||||
tickEventBus ! EventBus.Subscribe(subscribingActor)
|
val initializer: GameSystemsInitializer = wire[GameSystemsInitializer]
|
||||||
|
|
||||||
tickEventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self)
|
schedulers.async.execute(() => initializer.init.runAsyncAndForget)
|
||||||
|
|
||||||
// {
|
// ctx.pipeToSelf(application.timed.runToFuture) {
|
||||||
// app
|
// case Failure(exception) =>
|
||||||
// .getInputManager()
|
// ApplicationStartFailed(exception.getMessage())
|
||||||
// .observableAction("Left")
|
// case Success(value) =>
|
||||||
// .map { action =>
|
// println("here applications started")
|
||||||
// action.binding.name match {
|
// ApplicationStarted
|
||||||
// case "Left" => Task(println("Pressed left"))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// binding match {
|
|
||||||
// case "Left" =>
|
|
||||||
|
|
||||||
def playerNodeFactory =
|
|
||||||
PlayerNode(
|
|
||||||
modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o",
|
|
||||||
cam = app.camera
|
|
||||||
) _
|
|
||||||
// (assetManager = app.assetManager)
|
|
||||||
{
|
|
||||||
lazy val playerNode = playerNodeFactory(app.assetManager)
|
|
||||||
|
|
||||||
lazy val actor = ctx.spawn(
|
|
||||||
ImMovementActor(ImMovementActor.Props(app, playerNode)),
|
|
||||||
"imMovementActor"
|
|
||||||
)
|
|
||||||
lazy val state = wire[PlayerMovementState]
|
|
||||||
app.stateManager.attach(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.sleep(2000)
|
|
||||||
|
|
||||||
app
|
|
||||||
.getRootNode()
|
|
||||||
.depthFirst(s =>
|
|
||||||
// s match {
|
|
||||||
// case node: Node =>
|
|
||||||
// println("node" + s.getName() + " children " + node.getChildren())
|
|
||||||
// case g: Geometry => println(s.getName())
|
|
||||||
// }
|
// }
|
||||||
println(s.getName())
|
}
|
||||||
)
|
|
||||||
|
|
||||||
println("----------------")
|
|
||||||
|
|
||||||
{
|
|
||||||
app
|
|
||||||
.getRootNode()
|
|
||||||
.observableDepthFirst()
|
|
||||||
.map(s => s.getName())
|
|
||||||
// .takeWhileInclusive(_.getName() != "level")
|
|
||||||
.onErrorHandle(e => e.getMessage())
|
|
||||||
.foreach(println)(schedulers.async)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
println("----------------")
|
|
||||||
|
|
||||||
{
|
|
||||||
app
|
|
||||||
.getRootNode()
|
|
||||||
.observableBreadthFirst()
|
|
||||||
.map(s => s.getName())
|
|
||||||
// .takeWhileInclusive(_.getName() != "level")
|
|
||||||
.onErrorHandle(e => e.getMessage())
|
|
||||||
.foreach(println)(schedulers.async)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// app.start()
|
|
||||||
// Behaviors.same
|
|
||||||
Behaviors.receiveMessage { msg =>
|
|
||||||
msg match {
|
|
||||||
case XD =>
|
|
||||||
ctx.log.info("RECEEEEEIVED")
|
|
||||||
ctx.log.info(app.camera.toString())
|
|
||||||
Behaviors.same
|
|
||||||
case Stop =>
|
|
||||||
ctx.log.info("Received stop")
|
|
||||||
Behaviors.stopped
|
|
||||||
|
|
||||||
|
Behaviors.receiveMessage { msg =>
|
||||||
|
msg match {
|
||||||
|
case Stop =>
|
||||||
|
ctx.log.info("Received stop")
|
||||||
|
Behaviors.stopped
|
||||||
|
case ApplicationStarted =>
|
||||||
|
ctx.log.info("Application started")
|
||||||
|
Behaviors.same
|
||||||
|
case ApplicationStartFailed(reason) =>
|
||||||
|
ctx.log.error(
|
||||||
|
s"Failed to start application - $reason"
|
||||||
|
)
|
||||||
|
Behaviors.stopped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object SubscribingActor {
|
||||||
|
def apply() =
|
||||||
|
Behaviors.receive[Events.Tick.PhysicsTick.type] { (ctx, msg) =>
|
||||||
|
ctx.log.debug(s"received event $msg")
|
||||||
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Methods {
|
object Methods {
|
||||||
def createPlayer(
|
|
||||||
geom: Geometry,
|
|
||||||
cam: Camera
|
|
||||||
): Node = {
|
|
||||||
val playerNode = new Node("PlayerNode")
|
|
||||||
lazy val camNode = new CameraNode("CameraNode", cam)
|
|
||||||
playerNode
|
|
||||||
.child(camNode)
|
|
||||||
.child(geom)
|
|
||||||
playerNode
|
|
||||||
}
|
|
||||||
|
|
||||||
def old() = {
|
def old() = {
|
||||||
// val movementActor =
|
// val movementActor =
|
||||||
@ -170,15 +95,64 @@ object Methods {
|
|||||||
// "movementActorTimer"
|
// "movementActorTimer"
|
||||||
// )
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def old2() = {
|
||||||
|
// ctx.log.info("here")
|
||||||
|
|
||||||
|
// {
|
||||||
|
// implicit val s = schedulers.async
|
||||||
|
// Task
|
||||||
|
// .parZip2(
|
||||||
|
// loggerL.info("Test").executeOn(app.scheduler),
|
||||||
|
// app
|
||||||
|
// .enqueueL(() => loggerL.info("here 2").executeOn(app.scheduler))
|
||||||
|
// .flatten
|
||||||
|
// )
|
||||||
|
// .runToFuture
|
||||||
|
// }
|
||||||
|
|
||||||
|
// app
|
||||||
|
// .getRootNode()
|
||||||
|
// .depthFirst(s =>
|
||||||
|
// // s match {
|
||||||
|
// // case node: Node =>
|
||||||
|
// // println("node" + s.getName() + " children " + node.getChildren())
|
||||||
|
// // case g: Geometry => println(s.getName())
|
||||||
|
// // }
|
||||||
|
// println(s.getName())
|
||||||
|
// )
|
||||||
|
|
||||||
|
// println("----------------")
|
||||||
|
|
||||||
|
// {
|
||||||
|
// app
|
||||||
|
// .getRootNode()
|
||||||
|
// .observableDepthFirst()
|
||||||
|
// .map(s => s.getName())
|
||||||
|
// // .takeWhileInclusive(_.getName() != "level")
|
||||||
|
// .onErrorHandle(e => e.getMessage())
|
||||||
|
// .foreach(println)(schedulers.async)
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// println("----------------")
|
||||||
|
|
||||||
|
// {
|
||||||
|
// app
|
||||||
|
// .getRootNode()
|
||||||
|
// .observableBreadthFirst()
|
||||||
|
// .map(s => s.getName())
|
||||||
|
// // .takeWhileInclusive(_.getName() != "level")
|
||||||
|
// .onErrorHandle(e => e.getMessage())
|
||||||
|
// .foreach(println)(schedulers.async)
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// app.start()
|
||||||
|
// Behaviors.same
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object SubscribingActor {
|
|
||||||
def apply() =
|
|
||||||
Behaviors.receive[Events.PhysicsTick.type] { (ctx, msg) =>
|
|
||||||
ctx.log.debug(s"received event $msg")
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// new PlayerMovementState(
|
// new PlayerMovementState(
|
||||||
// // movementActor,
|
// // movementActor,
|
||||||
// // movementActorTimer,
|
// // movementActorTimer,
|
||||||
|
@ -1,25 +1,70 @@
|
|||||||
package wow.doge.mygame.game
|
package wow.doge.mygame.game
|
||||||
|
|
||||||
import cats.effect.Resource
|
import cats.effect.Resource
|
||||||
import com.jme3.app.state.AppState
|
|
||||||
import com.jme3.system.AppSettings
|
import com.jme3.system.AppSettings
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import io.odin.Logger
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
||||||
|
import monix.bio.IO
|
||||||
|
import monix.bio.Fiber
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import com.jme3.app.StatsAppState
|
||||||
|
import com.jme3.app.FlyCamAppState
|
||||||
// import wow.doge.mygame.executors.JMERunner
|
// import wow.doge.mygame.executors.JMERunner
|
||||||
|
class GameAppResource(logger: Logger[Task], jmeScheduler: Scheduler) {
|
||||||
trait GameModule {
|
def make: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||||
|
Resource.make(
|
||||||
def gameAppResource(appStates: AppState*): Resource[Task, GameApp] =
|
|
||||||
Resource.liftF {
|
|
||||||
for {
|
for {
|
||||||
app <- Task(new GameApp(appStates: _*))
|
_ <- logger.info("Creating game app")
|
||||||
|
app <- Task(new GameApp(new StatsAppState()))
|
||||||
_ <- Task {
|
_ <- Task {
|
||||||
val settings = new AppSettings(true)
|
val settings = new AppSettings(true)
|
||||||
// settings.setVSync(true)
|
settings.setVSync(true)
|
||||||
settings.setFrameRate(144)
|
settings.setUseInput(true)
|
||||||
|
// new FlyCamAppState
|
||||||
|
// settings.setFrameRate(250)
|
||||||
app.setSettings(settings)
|
app.setSettings(settings)
|
||||||
// JMERunner.runner = app
|
// JMERunner.runner = app
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
} yield (app)
|
fib <- Task(app.start()).executeOn(jmeScheduler).start
|
||||||
}
|
} yield (app -> fib)
|
||||||
|
)(logger.info("Closing game app") >> _._2.cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait GameModule {
|
||||||
|
|
||||||
|
def gameAppResource(
|
||||||
|
logger: Logger[Task],
|
||||||
|
jmeScheduler: Scheduler
|
||||||
|
): Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||||
|
Resource.make(
|
||||||
|
(for {
|
||||||
|
_ <- logger.info("Creating game app")
|
||||||
|
app <- Task(new GameApp())
|
||||||
|
_ <- 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)))
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,107 @@
|
|||||||
package wow.doge.mygame.game
|
package wow.doge.mygame.game
|
||||||
|
|
||||||
import wow.doge.mygame.state.MyBaseState
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class GameSystemsInitializer extends MyBaseState {
|
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 com.jme3.asset.plugins.ZipLocator
|
||||||
|
import com.jme3.bullet.BulletAppState
|
||||||
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
|
import com.softwaremill.macwire._
|
||||||
|
import com.softwaremill.tagging._
|
||||||
|
import io.odin.Logger
|
||||||
|
import monix.bio.Task
|
||||||
|
import monix.reactive.Consumer
|
||||||
|
import wow.doge.mygame.events.EventBus
|
||||||
|
import wow.doge.mygame.events.EventsModule
|
||||||
|
import wow.doge.mygame.game.nodes.Player
|
||||||
|
import wow.doge.mygame.game.nodes.PlayerController
|
||||||
|
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.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||||
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
|
import wow.doge.mygame.utils.IOUtils
|
||||||
|
|
||||||
override protected def onEnable(): Unit = {}
|
class GameSystemsInitializer()(
|
||||||
|
override val spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
override implicit val akkaScheduler: Scheduler,
|
||||||
|
app: GameApp,
|
||||||
|
loggerL: Logger[Task]
|
||||||
|
) extends EventsModule {
|
||||||
|
override implicit val timeout: Timeout = Timeout(1.second)
|
||||||
|
|
||||||
override protected def onDisable(): Unit = {}
|
import GameSystemsInitializer._
|
||||||
|
|
||||||
override protected def init(): Unit = {}
|
def init =
|
||||||
override def stop(): Unit = {}
|
for {
|
||||||
|
playerMovementEventBus <- playerMovementEventBusTask
|
||||||
|
inputManager = app.inputManager
|
||||||
|
bulletAppState = new BulletAppState()
|
||||||
|
_ <- Task(app.stateManager.attach(bulletAppState))
|
||||||
|
_ <- Task(
|
||||||
|
app.assetManager.registerLocator(
|
||||||
|
// "src/main/resources/assets/town.zip",
|
||||||
|
(os.rel / "src" / "main" / "resources" / "assets" / "town.zip"),
|
||||||
|
classOf[ZipLocator]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState))
|
||||||
|
playerController <- app.enqueueL(() =>
|
||||||
|
PlayerController(
|
||||||
|
app,
|
||||||
|
modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o",
|
||||||
|
cam = app.camera
|
||||||
|
)(app.assetManager, bulletAppState)
|
||||||
|
.taggedWith[Player]
|
||||||
|
)
|
||||||
|
// _ <- loggerL.debug(playerNode.getName())
|
||||||
|
// _ <- Task(app.rootNode.attachChild(playerNode))
|
||||||
|
// playerMovementActor <- wireWith(spawnMovementActor _)
|
||||||
|
// _ <-
|
||||||
|
// IOUtils
|
||||||
|
// .toIO(
|
||||||
|
// app.tickObservable
|
||||||
|
// .doOnNext { tpf =>
|
||||||
|
// IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf))
|
||||||
|
// }
|
||||||
|
// .completedL
|
||||||
|
// .startAndForget
|
||||||
|
// .onErrorRestart(3)
|
||||||
|
// )
|
||||||
|
_ <- wire[GameInputHandler.Props].begin
|
||||||
|
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
object GameSystemsInitializer {
|
||||||
|
def spawnMovementActor(
|
||||||
|
app: GameApp,
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
playerNode: BetterCharacterControl @@ Player,
|
||||||
|
playerMovementEventBus: ActorRef[
|
||||||
|
EventBus.Command[PlayerMovementEvent]
|
||||||
|
],
|
||||||
|
loggerL: Logger[Task]
|
||||||
|
)(implicit timeout: Timeout, scheduler: Scheduler) =
|
||||||
|
spawnProtocol.askL[ActorRef[ImMovementActor.Command]](
|
||||||
|
SpawnProtocol.Spawn(
|
||||||
|
ImMovementActor.Props(app, playerNode, playerMovementEventBus).create,
|
||||||
|
"imMovementActor",
|
||||||
|
Props.empty,
|
||||||
|
_
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def playerMovementActorTickConsumer(
|
||||||
|
playerMovementActor: ActorRef[ImMovementActor.Command]
|
||||||
|
) =
|
||||||
|
Consumer
|
||||||
|
.foreachTask[Float](tpf =>
|
||||||
|
IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf))
|
||||||
|
)
|
||||||
|
// .mapTask()
|
||||||
}
|
}
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
package wow.doge.mygame.state
|
|
||||||
|
|
||||||
import akka.actor.typed.scaladsl.ActorContext
|
|
||||||
import akka.actor.typed.Behavior
|
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
|
||||||
import com.softwaremill.quicklens._
|
|
||||||
import wow.doge.mygame.implicits._
|
|
||||||
import com.jme3.renderer.Camera
|
|
||||||
import wow.doge.mygame.math.ImVector3f
|
|
||||||
|
|
||||||
trait CanMove[-A] {
|
|
||||||
def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
|
||||||
def move(inst: A, direction: ImVector3f): Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
object ImMovementActor {
|
|
||||||
sealed trait Command
|
|
||||||
// final case class Tick(tpf: Float) extends Command
|
|
||||||
final case class Tick(tpf: Float) extends Command
|
|
||||||
|
|
||||||
sealed trait Movement extends Command
|
|
||||||
final case class MovedLeft(pressed: Boolean) extends Movement
|
|
||||||
final case class MovedUp(pressed: Boolean) extends Movement
|
|
||||||
final case class MovedRight(pressed: Boolean) extends Movement
|
|
||||||
final case class MovedDown(pressed: Boolean) extends Movement
|
|
||||||
|
|
||||||
final case class Props[T: CanMove](
|
|
||||||
app: com.jme3.app.Application,
|
|
||||||
movable: T
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal state of the actor
|
|
||||||
*
|
|
||||||
* @param cardinalDir Immutable, can be shared as is
|
|
||||||
* @param walkDirection Immutable
|
|
||||||
*/
|
|
||||||
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")
|
|
||||||
new ImMovementActor(ctx, props).receive(State())
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ImMovementActor[T](
|
|
||||||
ctx: ActorContext[ImMovementActor.Command],
|
|
||||||
props: ImMovementActor.Props[T]
|
|
||||||
) {
|
|
||||||
import ImMovementActor._
|
|
||||||
|
|
||||||
def receive(
|
|
||||||
state: ImMovementActor.State
|
|
||||||
)(implicit cm: CanMove[T]): Behavior[Command] =
|
|
||||||
Behaviors.receiveMessage { msg =>
|
|
||||||
msg match {
|
|
||||||
case m: Movement =>
|
|
||||||
m match {
|
|
||||||
case MovedLeft(pressed) =>
|
|
||||||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
|
||||||
case MovedUp(pressed) =>
|
|
||||||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
|
||||||
case MovedRight(pressed) =>
|
|
||||||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
|
||||||
case MovedDown(pressed) =>
|
|
||||||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
|
||||||
}
|
|
||||||
|
|
||||||
case Tick(tpf) =>
|
|
||||||
val walkDir =
|
|
||||||
cm.getDirection(props.app.getCamera(), state.cardinalDir)
|
|
||||||
if (walkDir != ImVector3f.ZERO) {
|
|
||||||
val tmp = walkDir * 25f * tpf
|
|
||||||
// props.app.enqueue(new Runnable {
|
|
||||||
// override def run(): Unit = {
|
|
||||||
// cm.move(props.movable, tmp)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
props.app.enqueueF {
|
|
||||||
cm.move(props.movable, tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behaviors.same
|
|
||||||
// receive(state = state.modify(_.walkDirection).setTo(walkDir))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,6 @@ import scala.concurrent.duration.DurationInt
|
|||||||
|
|
||||||
import com.jme3.input.InputManager
|
import com.jme3.input.InputManager
|
||||||
import com.jme3.input.KeyInput
|
import com.jme3.input.KeyInput
|
||||||
import com.jme3.input.controls.ActionListener
|
|
||||||
import com.jme3.input.controls.KeyTrigger
|
import com.jme3.input.controls.KeyTrigger
|
||||||
import com.jme3.math.Vector3f
|
import com.jme3.math.Vector3f
|
||||||
|
|
||||||
@ -16,19 +15,19 @@ import com.jme3.scene.Geometry
|
|||||||
import akka.actor.typed.scaladsl.TimerScheduler
|
import akka.actor.typed.scaladsl.TimerScheduler
|
||||||
|
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import com.jme3.scene.Node
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
import com.jme3.syntax._
|
|
||||||
|
|
||||||
class PlayerMovementState(
|
class PlayerMovementState(
|
||||||
// movementActor: ActorRef[MovementActor.Command],
|
// movementActor: ActorRef[MovementActor.Command],
|
||||||
// movementActorTimer: ActorRef[MovementActorTimer.Command],
|
// movementActorTimer: ActorRef[MovementActorTimer.Command],
|
||||||
imMovementActor: ActorRef[ImMovementActor.Command],
|
imMovementActor: ActorRef[ImMovementActor.Command]
|
||||||
// geom: Geometry,
|
// geom: Geometry,
|
||||||
// camNode: CameraNode,
|
// camNode: CameraNode,
|
||||||
playerNode: Node
|
// playerNode: Node
|
||||||
// gameAppActor: ActorRef[GameAppActor.Command]
|
// gameAppActor: ActorRef[GameAppActor.Command]
|
||||||
) extends MyBaseState
|
) extends MyBaseState
|
||||||
with ActionListener {
|
// with ActionListener
|
||||||
|
{
|
||||||
|
|
||||||
protected lazy val mat = MyMaterial(
|
protected lazy val mat = MyMaterial(
|
||||||
assetManager = assetManager,
|
assetManager = assetManager,
|
||||||
@ -37,8 +36,8 @@ class PlayerMovementState(
|
|||||||
|
|
||||||
override protected def init(): Unit = {
|
override protected def init(): Unit = {
|
||||||
|
|
||||||
setupKeys(inputManager)
|
// setupKeys(inputManager)
|
||||||
println("playermovementstate " + Thread.currentThread().getName())
|
// println("playermovementstate " + Thread.currentThread().getName())
|
||||||
|
|
||||||
// geom.setMaterial(mat)
|
// geom.setMaterial(mat)
|
||||||
|
|
||||||
@ -51,7 +50,7 @@ class PlayerMovementState(
|
|||||||
// .child(geom)
|
// .child(geom)
|
||||||
// // playerNode.children(Seq(camNode, geom))
|
// // playerNode.children(Seq(camNode, geom))
|
||||||
// }
|
// }
|
||||||
discard { rootNode.child(playerNode) }
|
// discard { rootNode.withChild(playerNode) }
|
||||||
// camNode.setLocalTranslation(
|
// camNode.setLocalTranslation(
|
||||||
// new Vector3f(0, 1.5f, 10)
|
// new Vector3f(0, 1.5f, 10)
|
||||||
// )
|
// )
|
||||||
@ -107,23 +106,23 @@ class PlayerMovementState(
|
|||||||
new KeyTrigger(KeyInput.KEY_R),
|
new KeyTrigger(KeyInput.KEY_R),
|
||||||
new KeyTrigger(KeyInput.KEY_RETURN)
|
new KeyTrigger(KeyInput.KEY_RETURN)
|
||||||
)
|
)
|
||||||
.withListener(this, "Left")
|
// .withListener(this, "Left")
|
||||||
.withListener(this, "Right")
|
// .withListener(this, "Right")
|
||||||
.withListener(this, "Up")
|
// .withListener(this, "Up")
|
||||||
.withListener(this, "Down")
|
// .withListener(this, "Down")
|
||||||
.withListener(this, "Space")
|
// .withListener(this, "Space")
|
||||||
.withListener(this, "Reset")
|
// .withListener(this, "Reset")
|
||||||
}
|
}
|
||||||
|
|
||||||
def onAction(binding: String, value: Boolean, tpf: Float) =
|
// def onAction(binding: String, value: Boolean, tpf: Float) =
|
||||||
binding match {
|
// binding match {
|
||||||
case "Left" => imMovementActor ! ImMovementActor.MovedLeft(value)
|
// case "Left" => imMovementActor ! ImMovementActor.MovedLeft(value)
|
||||||
case "Right" => imMovementActor ! ImMovementActor.MovedRight(value)
|
// case "Right" => imMovementActor ! ImMovementActor.MovedRight(value)
|
||||||
case "Up" => imMovementActor ! ImMovementActor.MovedUp(value)
|
// case "Up" => imMovementActor ! ImMovementActor.MovedUp(value)
|
||||||
case "Down" => imMovementActor ! ImMovementActor.MovedDown(value)
|
// case "Down" => imMovementActor ! ImMovementActor.MovedDown(value)
|
||||||
case "Space" =>
|
// case "Space" =>
|
||||||
case _ =>
|
// case _ =>
|
||||||
}
|
// }
|
||||||
|
|
||||||
override protected def onEnable(): Unit = {}
|
override protected def onEnable(): Unit = {}
|
||||||
|
|
||||||
|
@ -4,17 +4,23 @@ import com.jme3.scene.Node
|
|||||||
import com.jme3.scene.CameraNode
|
import com.jme3.scene.CameraNode
|
||||||
import com.jme3.scene.Geometry
|
import com.jme3.scene.Geometry
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
import wow.doge.mygame.implicits._
|
|
||||||
import com.jme3.asset.AssetManager
|
import com.jme3.asset.AssetManager
|
||||||
import wow.doge.mygame.state.MyMaterial
|
import wow.doge.mygame.state.MyMaterial
|
||||||
import com.jme3.math.Vector3f
|
import com.jme3.math.Vector3f
|
||||||
import com.jme3.scene.control.CameraControl.ControlDirection
|
import com.jme3.scene.control.CameraControl.ControlDirection
|
||||||
import com.jme3.syntax._
|
import com.jme3.syntax._
|
||||||
import com.jme3.scene.shape.Box
|
import com.jme3.scene.shape.Box
|
||||||
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
|
import com.jme3.bullet.BulletAppState
|
||||||
|
import wow.doge.mygame.game.GameApp
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.math.ImVector3f
|
||||||
|
import com.jme3.math.FastMath
|
||||||
|
|
||||||
// class PlayerNode(val name: String) extends Node(name) {}
|
// class PlayerNode(val name: String) extends Node(name) {}
|
||||||
object PlayerNode {
|
trait Player
|
||||||
def defaultMesh() = {
|
object PlayerController {
|
||||||
|
def defaultMesh = {
|
||||||
lazy val b = new Box(1, 1, 1)
|
lazy val b = new Box(1, 1, 1)
|
||||||
lazy val geom = new Geometry("playerMesh", b)
|
lazy val geom = new Geometry("playerMesh", b)
|
||||||
geom
|
geom
|
||||||
@ -26,36 +32,40 @@ object PlayerNode {
|
|||||||
)
|
)
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
|
app: GameApp,
|
||||||
modelPath: os.RelPath,
|
modelPath: os.RelPath,
|
||||||
cam: Camera
|
cam: Camera
|
||||||
)(assetManager: AssetManager) = {
|
)(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")
|
lazy val playerNode = new Node("PlayerNode")
|
||||||
lazy val camNode = new CameraNode("CameraNode", cam)
|
.withChildren(
|
||||||
|
new CameraNode("CameraNode", cam)
|
||||||
|
.withControlDir(ControlDirection.SpatialToCamera)
|
||||||
|
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
|
||||||
|
.withLookAt(playerPos, ImVector3f.UNIT_Y),
|
||||||
|
assetManager
|
||||||
|
.loadModel(modelPath)
|
||||||
|
.asInstanceOf[Node]
|
||||||
|
.withRotate(0, FastMath.PI, 0)
|
||||||
|
)
|
||||||
|
.withLocalTranslation(playerPos)
|
||||||
|
.withControl(playerPhysicsControl)
|
||||||
|
|
||||||
// lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera())
|
{
|
||||||
// camNode.setCamera(simpleApp.getCamera())
|
bulletAppState.physicsSpace += playerNode
|
||||||
|
bulletAppState.physicsSpace += playerPhysicsControl
|
||||||
val playerModel: Node =
|
|
||||||
assetManager.loadModel(modelPath).asInstanceOf[Node]
|
|
||||||
discard {
|
|
||||||
playerNode
|
|
||||||
.child(camNode)
|
|
||||||
.child(playerModel)
|
|
||||||
// playerNode.children(Seq(camNode, geom))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
camNode.setControlDir(ControlDirection.SpatialToCamera)
|
app.rootNode += playerNode
|
||||||
camNode.setLocalTranslation(
|
|
||||||
new Vector3f(0, 1.5f, 10)
|
|
||||||
)
|
|
||||||
camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerNode
|
playerPhysicsControl
|
||||||
}
|
}
|
||||||
def apply(
|
def apply(
|
||||||
mesh: Geometry = defaultMesh(),
|
mesh: Geometry = defaultMesh,
|
||||||
texturePath: os.RelPath =
|
texturePath: os.RelPath =
|
||||||
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md",
|
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md",
|
||||||
cam: Camera
|
cam: Camera
|
||||||
@ -76,8 +86,8 @@ object PlayerNode {
|
|||||||
// camNode.setCamera(simpleApp.getCamera())
|
// camNode.setCamera(simpleApp.getCamera())
|
||||||
discard {
|
discard {
|
||||||
playerNode
|
playerNode
|
||||||
.child(camNode)
|
.withChild(camNode)
|
||||||
.child(mesh)
|
.withChild(mesh)
|
||||||
// playerNode.children(Seq(camNode, geom))
|
// playerNode.children(Seq(camNode, geom))
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
|||||||
|
package wow.doge.mygame.game.nodes
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
|
import org.slf4j.event.Level
|
||||||
|
import akka.actor.typed.LogOptions
|
||||||
|
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
|
object PlayerMovementEventHandler {
|
||||||
|
import PlayerMovementEvent._
|
||||||
|
def apply(movementActor: ActorRef[ImMovementActor.Command]) =
|
||||||
|
Behaviors.logMessages(
|
||||||
|
LogOptions()
|
||||||
|
.withLevel(Level.TRACE)
|
||||||
|
.withLogger(
|
||||||
|
Logger[PlayerMovementEventHandler.type].underlying
|
||||||
|
),
|
||||||
|
Behaviors.setup[PlayerMovementEvent](ctx =>
|
||||||
|
Behaviors.receiveMessage {
|
||||||
|
case PlayerMovedLeft(pressed) =>
|
||||||
|
movementActor ! ImMovementActor.MovedLeft(pressed)
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerMovedRight(pressed) =>
|
||||||
|
movementActor ! ImMovementActor.MovedRight(pressed)
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerMovedForward(pressed) =>
|
||||||
|
movementActor ! ImMovementActor.MovedUp(pressed)
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerMovedBackward(pressed) =>
|
||||||
|
movementActor ! ImMovementActor.MovedDown(pressed)
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerJumped =>
|
||||||
|
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 =>
|
||||||
|
ctx.log.warn("camera up not implemented yet")
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerCameraDown =>
|
||||||
|
ctx.log.warn("camera down not implemented yet")
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,237 @@
|
|||||||
|
package wow.doge.mygame.game.subsystems.input
|
||||||
|
|
||||||
|
import com.jme3.input.InputManager
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import wow.doge.mygame.events.EventBus
|
||||||
|
import com.jme3.input.KeyInput
|
||||||
|
import com.jme3.input.controls.KeyTrigger
|
||||||
|
import monix.bio.UIO
|
||||||
|
import wow.doge.mygame.utils.IOUtils._
|
||||||
|
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import com.jme3.input.controls.MouseAxisTrigger
|
||||||
|
import com.jme3.input.MouseInput
|
||||||
|
|
||||||
|
// class GameInputHandler(
|
||||||
|
// inputManager: InputManager
|
||||||
|
// // inputEventBus: InputEventBus
|
||||||
|
// ) {}
|
||||||
|
|
||||||
|
object GameInputHandler {
|
||||||
|
|
||||||
|
final case class Props(
|
||||||
|
inputManager: InputManager,
|
||||||
|
playerMovementEventBus: ActorRef[
|
||||||
|
EventBus.Command[PlayerMovementEvent]
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
def begin =
|
||||||
|
for {
|
||||||
|
_ <- UIO(setupKeys(inputManager))
|
||||||
|
_ <- toIO(
|
||||||
|
generateMovementInputEvents(
|
||||||
|
inputManager,
|
||||||
|
playerMovementEventBus
|
||||||
|
).completedL.startAndForget
|
||||||
|
)
|
||||||
|
_ <- toIO(
|
||||||
|
generateRotateEvents(
|
||||||
|
inputManager,
|
||||||
|
playerMovementEventBus
|
||||||
|
).completedL.startAndForget
|
||||||
|
)
|
||||||
|
_ <- toIO(
|
||||||
|
generateCameraEvents(
|
||||||
|
inputManager,
|
||||||
|
playerMovementEventBus
|
||||||
|
).completedL.startAndForget
|
||||||
|
)
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setupKeys(inputManager: InputManager) =
|
||||||
|
inputManager
|
||||||
|
.withMapping(
|
||||||
|
"Left",
|
||||||
|
new KeyTrigger(KeyInput.KEY_A)
|
||||||
|
// new KeyTrigger(KeyInput.KEY_LEFT)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"Right",
|
||||||
|
new KeyTrigger(KeyInput.KEY_D)
|
||||||
|
// new KeyTrigger(KeyInput.KEY_RIGHT)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"Up",
|
||||||
|
new KeyTrigger(KeyInput.KEY_W)
|
||||||
|
// new KeyTrigger(KeyInput.KEY_UP)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"Down",
|
||||||
|
new KeyTrigger(KeyInput.KEY_S)
|
||||||
|
// new KeyTrigger(KeyInput.KEY_DOWN)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"Jump",
|
||||||
|
new KeyTrigger(KeyInput.KEY_SPACE)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"ROTATE_RIGHT",
|
||||||
|
new KeyTrigger(KeyInput.KEY_RIGHT),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_X, true)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"ROTATE_LEFT",
|
||||||
|
new KeyTrigger(KeyInput.KEY_LEFT),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_X, false)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"CAMERA_UP",
|
||||||
|
// new KeyTrigger(KeyInput.KEY_LEFT),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_Y, false)
|
||||||
|
)
|
||||||
|
.withMapping(
|
||||||
|
"CAMERA_DOWN",
|
||||||
|
// new KeyTrigger(KeyInput.KEY_LEFT),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_Y, true)
|
||||||
|
)
|
||||||
|
.setCursorVisible(false)
|
||||||
|
|
||||||
|
def generateMovementInputEvents(
|
||||||
|
inputManager: InputManager,
|
||||||
|
playerMovementEventBus: ActorRef[
|
||||||
|
EventBus.Command[PlayerMovementEvent]
|
||||||
|
]
|
||||||
|
) = {
|
||||||
|
val name = "movementInputEventsGenerator"
|
||||||
|
inputManager
|
||||||
|
.observableAction(
|
||||||
|
"Left",
|
||||||
|
"Right",
|
||||||
|
"Up",
|
||||||
|
"Down",
|
||||||
|
"Jump",
|
||||||
|
"ROTATE_RIGHT",
|
||||||
|
"ROTATE_LEFT"
|
||||||
|
)
|
||||||
|
// .dump("O")
|
||||||
|
.doOnNext { action =>
|
||||||
|
action.binding.name match {
|
||||||
|
case "Left" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerMovedLeft(pressed = action.value),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case "Right" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerMovedRight(pressed = action.value),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case "Up" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerMovedForward(pressed = action.value),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case "Down" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerMovedBackward(pressed = action.value),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
case "Jump" if action.value =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerJumped,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
case _ => monix.eval.Task.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def generateRotateEvents(
|
||||||
|
inputManager: InputManager,
|
||||||
|
playerMovementEventBus: ActorRef[
|
||||||
|
EventBus.Command[PlayerMovementEvent]
|
||||||
|
]
|
||||||
|
) = {
|
||||||
|
val name = "rotateMovementEventsGenerator"
|
||||||
|
inputManager
|
||||||
|
.analogObservable("ROTATE_RIGHT", "ROTATE_LEFT")
|
||||||
|
.sample(1.millis)
|
||||||
|
.mapEval(analogEvent =>
|
||||||
|
analogEvent.binding.name match {
|
||||||
|
case "ROTATE_RIGHT" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerRotatedRight,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case "ROTATE_LEFT" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerRotatedLeft,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ => monix.eval.Task.unit
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def generateCameraEvents(
|
||||||
|
inputManager: InputManager,
|
||||||
|
playerMovementEventBus: ActorRef[
|
||||||
|
EventBus.Command[PlayerMovementEvent]
|
||||||
|
]
|
||||||
|
) = {
|
||||||
|
val name = "cameraMovementEventsGenerator"
|
||||||
|
inputManager
|
||||||
|
.analogObservable("CAMERA_UP", "CAMERA_DOWN")
|
||||||
|
.sample(1.millis)
|
||||||
|
.mapEval(analogEvent =>
|
||||||
|
analogEvent.binding.name match {
|
||||||
|
case "CAMERA_UP" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerCameraUp,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case "CAMERA_DOWN" =>
|
||||||
|
toTask(
|
||||||
|
playerMovementEventBus !! EventBus.Publish(
|
||||||
|
PlayerMovementEvent.PlayerCameraDown,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ => monix.eval.Task.unit
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// def bindMappings(inputManager: InputManager, mappings: ActionMapping*) = {
|
||||||
|
// inputManager
|
||||||
|
// .observableAction(mappings.map(_.name): _*)
|
||||||
|
// .doOnNext(action =>
|
||||||
|
// mappings.map(m =>
|
||||||
|
// if (action.binding.name == m.name) toTask(m.cb(action))
|
||||||
|
// else monix.eval.Task.unit
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// case class ActionMapping(name: String, cb: ActionEvent => Task[Unit])
|
@ -0,0 +1,9 @@
|
|||||||
|
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 "
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package wow.doge.mygame.game.subsystems.level
|
||||||
|
import com.jme3.bullet.BulletAppState
|
||||||
|
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape
|
||||||
|
import com.jme3.bullet.control.CharacterControl
|
||||||
|
import com.jme3.bullet.control.RigidBodyControl
|
||||||
|
import com.jme3.bullet.util.CollisionShapeFactory
|
||||||
|
import com.jme3.scene.Spatial
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.game.GameApp
|
||||||
|
import com.jme3.syntax._
|
||||||
|
import com.jme3.math.ColorRGBA
|
||||||
|
import com.jme3.light.DirectionalLight
|
||||||
|
import com.jme3.math.Vector3f
|
||||||
|
import com.jme3.light.AmbientLight
|
||||||
|
object DefaultGameLevel {
|
||||||
|
|
||||||
|
// lazy valbulletAppState: BulletAppState
|
||||||
|
// bulletAppState.setThreadingType(ThreadingType.SEQUENTIAL)
|
||||||
|
|
||||||
|
// We set up collision detection for the scene by creating a
|
||||||
|
// compound collision shape and a static RigidBodyControl with mass zero.
|
||||||
|
|
||||||
|
// We set up collision detection for the player by creating
|
||||||
|
// a capsule collision shape and a CharacterControl.
|
||||||
|
// The CharacterControl offers extra settings for
|
||||||
|
// size, stepheight, jumping, falling, and gravity.
|
||||||
|
// We also put the player in its starting position.
|
||||||
|
lazy val capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1)
|
||||||
|
|
||||||
|
lazy val player: CharacterControl =
|
||||||
|
new CharacterControl(capsuleShape, 0.05f)
|
||||||
|
def apply(app: GameApp, bulletAppState: BulletAppState) = {
|
||||||
|
lazy val sceneModel: Spatial = app.assetManager.loadModel("main.scene")
|
||||||
|
lazy val sceneShape = CollisionShapeFactory.createMeshShape(
|
||||||
|
sceneModel.toNode match {
|
||||||
|
case util.Right(node) => node
|
||||||
|
case util.Left(ex) =>
|
||||||
|
throw new NotImplementedError("No fallback sceneshape")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
lazy val landscape: RigidBodyControl =
|
||||||
|
new RigidBodyControl(sceneShape, 0)
|
||||||
|
|
||||||
|
// // discard { app.stateManager.attach(bulletAppState) }
|
||||||
|
|
||||||
|
app.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
||||||
|
sceneModel.setLocalScale(2f)
|
||||||
|
sceneModel.addControl(landscape)
|
||||||
|
discard { app.rootNode.attachChild(sceneModel) }
|
||||||
|
bulletAppState.getPhysicsSpace.add(landscape)
|
||||||
|
bulletAppState.getPhysicsSpace.add(player)
|
||||||
|
|
||||||
|
val al = new AmbientLight();
|
||||||
|
al.setColor(ColorRGBA.White.mult(1.3f));
|
||||||
|
app.rootNode.addLight(al);
|
||||||
|
|
||||||
|
val dl = new DirectionalLight();
|
||||||
|
dl.setColor(ColorRGBA.White);
|
||||||
|
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
|
||||||
|
app.rootNode.addLight(dl);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,184 @@
|
|||||||
|
package wow.doge.mygame.subsystems.movement
|
||||||
|
|
||||||
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import com.softwaremill.quicklens._
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import com.jme3.renderer.Camera
|
||||||
|
import wow.doge.mygame.math.ImVector3f
|
||||||
|
import wow.doge.mygame.game.GameApp
|
||||||
|
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import wow.doge.mygame.events.EventBus
|
||||||
|
import com.jme3.math.Vector3f
|
||||||
|
import wow.doge.mygame.state.CardinalDirection
|
||||||
|
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||||
|
import akka.actor.typed.LogOptions
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import org.slf4j.event.Level
|
||||||
|
|
||||||
|
sealed trait RotateDir
|
||||||
|
object RotateDir {
|
||||||
|
case object Left extends RotateDir
|
||||||
|
case object Right extends RotateDir
|
||||||
|
}
|
||||||
|
|
||||||
|
trait CanMove[-A] {
|
||||||
|
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||||
|
def move(inst: A, direction: ImVector3f): Unit
|
||||||
|
def jump(inst: A): Unit
|
||||||
|
def stop(inst: A): Unit
|
||||||
|
def rotate(inst: A, rotateDir: RotateDir): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
object ImMovementActor {
|
||||||
|
sealed trait Command
|
||||||
|
// final case class Tick(tpf: Float) extends Command
|
||||||
|
final case class Tick(tpf: Float) extends Command
|
||||||
|
|
||||||
|
sealed trait Movement extends Command
|
||||||
|
final case class MovedLeft(pressed: Boolean) extends Movement
|
||||||
|
final case class MovedUp(pressed: Boolean) extends Movement
|
||||||
|
final case class MovedRight(pressed: Boolean) extends Movement
|
||||||
|
final case class MovedDown(pressed: Boolean) extends Movement
|
||||||
|
final case object Jump extends Movement
|
||||||
|
final case object RotateRight extends Movement
|
||||||
|
final case object RotateLeft extends Movement
|
||||||
|
|
||||||
|
final case class Props[T: CanMove](
|
||||||
|
app: GameApp,
|
||||||
|
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())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal state of the actor
|
||||||
|
*
|
||||||
|
* @param cardinalDir The four directions the character can move
|
||||||
|
*/
|
||||||
|
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](
|
||||||
|
ctx: ActorContext[ImMovementActor.Command],
|
||||||
|
props: ImMovementActor.Props[T]
|
||||||
|
) {
|
||||||
|
import ImMovementActor._
|
||||||
|
import Methods._
|
||||||
|
|
||||||
|
def receive(
|
||||||
|
state: ImMovementActor.State
|
||||||
|
)(implicit cm: CanMove[T]): Behavior[Command] =
|
||||||
|
Behaviors.receiveMessage {
|
||||||
|
case m: Movement =>
|
||||||
|
m match {
|
||||||
|
case MovedLeft(pressed) =>
|
||||||
|
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||||
|
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
||||||
|
case MovedUp(pressed) =>
|
||||||
|
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||||
|
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
||||||
|
case MovedRight(pressed) =>
|
||||||
|
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||||
|
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
||||||
|
case MovedDown(pressed) =>
|
||||||
|
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
|
||||||
|
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
||||||
|
case Jump =>
|
||||||
|
props.app.enqueueF(cm.jump(props.movable))
|
||||||
|
Behaviors.same
|
||||||
|
case RotateLeft =>
|
||||||
|
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left))
|
||||||
|
Behaviors.same
|
||||||
|
case RotateRight =>
|
||||||
|
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right))
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
|
||||||
|
case Tick(tpf) =>
|
||||||
|
val walkDir =
|
||||||
|
getDirection(state.cardinalDir, ctx.log.trace)
|
||||||
|
if (walkDir != ImVector3f.ZERO) {
|
||||||
|
val tmp = walkDir * 25f * tpf
|
||||||
|
props.app.enqueueF {
|
||||||
|
cm.move(props.movable, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// props.app
|
||||||
|
// .enqueueScala { () =>
|
||||||
|
// 1
|
||||||
|
// }
|
||||||
|
// .map(println)(scala.concurrent.ExecutionContext.global)
|
||||||
|
// monix.eval.Task
|
||||||
|
// .fromFuture(
|
||||||
|
// props.app
|
||||||
|
// .enqueueScala { () =>
|
||||||
|
// 1
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// .flatMap(i => monix.eval.Task(println(i)))
|
||||||
|
// .runToFuture(monix.execution.Scheduler.Implicits.global)
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object Methods {
|
||||||
|
def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = {
|
||||||
|
val zero = ImVector3f.ZERO
|
||||||
|
val dir = cardinalDir
|
||||||
|
val walkDir = {
|
||||||
|
val mutWalkDir = new Vector3f()
|
||||||
|
if (dir.left) {
|
||||||
|
trace("left")
|
||||||
|
mutWalkDir += zero +=: new Vector3f(-1, 0, 0)
|
||||||
|
}
|
||||||
|
if (dir.right) {
|
||||||
|
trace("right")
|
||||||
|
mutWalkDir += zero +=: new Vector3f(1, 0, 0)
|
||||||
|
}
|
||||||
|
if (dir.up) {
|
||||||
|
trace("up")
|
||||||
|
mutWalkDir += zero +=: new Vector3f(0, 0, -1)
|
||||||
|
}
|
||||||
|
if (dir.down) {
|
||||||
|
trace("down")
|
||||||
|
mutWalkDir += zero +=: new Vector3f(0, 0, 1)
|
||||||
|
}
|
||||||
|
mutWalkDir.immutable
|
||||||
|
}
|
||||||
|
walkDir
|
||||||
|
}
|
||||||
|
|
||||||
|
def stopIfNotPressed[T](pressed: Boolean, movable: T)(implicit
|
||||||
|
cm: CanMove[T]
|
||||||
|
) =
|
||||||
|
if (!pressed) cm.stop(movable)
|
||||||
|
}
|
@ -20,14 +20,11 @@ import com.jme3.math.Vector3f
|
|||||||
import wow.doge.mygame.math.ImVector3f
|
import wow.doge.mygame.math.ImVector3f
|
||||||
import com.jme3.scene.Geometry
|
import com.jme3.scene.Geometry
|
||||||
import wow.doge.mygame.state.CardinalDirection
|
import wow.doge.mygame.state.CardinalDirection
|
||||||
import wow.doge.mygame.state.CanMove
|
import wow.doge.mygame.subsystems.movement.CanMove
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
import wow.doge.mygame.utils.JFXConsoleStreamable
|
import wow.doge.mygame.utils.JFXConsoleStreamable
|
||||||
import com.jme3.app.Application
|
import com.jme3.app.Application
|
||||||
import java.util.concurrent.Callable
|
|
||||||
import scala.concurrent.Future
|
|
||||||
import scala.concurrent.ExecutionContext
|
|
||||||
import com.jme3.scene.SceneGraphVisitor
|
import com.jme3.scene.SceneGraphVisitor
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import com.jme3.asset.AssetManager
|
import com.jme3.asset.AssetManager
|
||||||
@ -45,8 +42,24 @@ import com.jme3.bullet.PhysicsTickListener
|
|||||||
import monix.reactive.observers.Subscriber
|
import monix.reactive.observers.Subscriber
|
||||||
import monix.execution.Ack.Continue
|
import monix.execution.Ack.Continue
|
||||||
import monix.execution.Ack.Stop
|
import monix.execution.Ack.Stop
|
||||||
|
import com.jme3.bullet.BulletAppState
|
||||||
|
import wow.doge.mygame.state.MyBaseState
|
||||||
|
import monix.bio.UIO
|
||||||
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
|
import com.jme3.scene.control.AbstractControl
|
||||||
|
import com.jme3.scene.CameraNode
|
||||||
|
import com.jme3.scene.control.CameraControl.ControlDirection
|
||||||
|
import com.jme3.bullet.control.AbstractPhysicsControl
|
||||||
|
import com.jme3.scene.control.Control
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
import com.jme3.input.controls.AnalogListener
|
||||||
|
import com.jme3.math.Quaternion
|
||||||
|
import com.jme3.math.FastMath
|
||||||
|
import wow.doge.mygame.subsystems.movement.RotateDir
|
||||||
|
|
||||||
case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
|
case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
|
||||||
|
case class AnalogEvent(binding: Action, value: Float, tpf: Float)
|
||||||
case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
|
case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
|
||||||
|
|
||||||
package object implicits {
|
package object implicits {
|
||||||
@ -54,35 +67,35 @@ package object implicits {
|
|||||||
type PhysicsTickObservable =
|
type PhysicsTickObservable =
|
||||||
Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
|
Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
|
||||||
|
|
||||||
implicit class JMEAppExt(val app: Application) extends AnyVal {
|
implicit class JMEAppExt(private val app: Application) extends AnyVal {
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Blocking task. Execute on a thread pool meant for blocking operations.
|
// * Blocking task. Execute on a thread pool meant for blocking operations.
|
||||||
* Prefer [[wow.doge.mygame.implicits.JMEAppExt#enqueueT]] instead.
|
// * Prefer [[wow.doge.mygame.implicits.JMEAppExt#enqueueT]] instead.
|
||||||
*
|
// *
|
||||||
* @param cb
|
// * @param cb
|
||||||
* @param ec
|
// * @param ec
|
||||||
* @return
|
// * @return
|
||||||
*/
|
// */
|
||||||
def enqueueF[T](cb: () => T)(implicit ec: ExecutionContext): Future[T] =
|
// def enqueueF[T](cb: () => T)(implicit ec: ExecutionContext): Future[T] =
|
||||||
Future {
|
// Future {
|
||||||
app
|
// app
|
||||||
.enqueue(new Callable[T]() {
|
// .enqueue(new Callable[T]() {
|
||||||
override def call(): T = cb()
|
// override def call(): T = cb()
|
||||||
})
|
// })
|
||||||
.get()
|
// .get()
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Blocking task. Execute on a thread pool meant for blocking operations.
|
// * Blocking task. Execute on a thread pool meant for blocking operations.
|
||||||
* Same as enqueue, but returns a Monix Task instead of Future
|
// * Same as enqueue, but returns a Monix Task instead of Future
|
||||||
* @param cb
|
// * @param cb
|
||||||
* @param ec
|
// * @param ec
|
||||||
* @return
|
// * @return
|
||||||
*/
|
// */
|
||||||
def enqueueL[T](cb: () => T): Task[T] =
|
// def enqueueL[T](cb: () => T): Task[T] =
|
||||||
Task
|
// Task
|
||||||
.deferFutureAction(implicit s => enqueueF(cb))
|
// .deferFutureAction(implicit s => enqueueF(cb))
|
||||||
|
|
||||||
def enqueueF[T](cb: => T) =
|
def enqueueF[T](cb: => T) =
|
||||||
app.enqueue(new Runnable {
|
app.enqueue(new Runnable {
|
||||||
@ -92,13 +105,14 @@ package object implicits {
|
|||||||
def enqueueT(cb: => Unit) =
|
def enqueueT(cb: => Unit) =
|
||||||
Task(enqueueF(cb))
|
Task(enqueueF(cb))
|
||||||
}
|
}
|
||||||
implicit class StateManagerExt(val sm: AppStateManager) extends AnyVal {
|
implicit class StateManagerExt(private val sm: AppStateManager)
|
||||||
|
extends AnyVal {
|
||||||
def state[S <: AppState]()(implicit c: ClassTag[S]): S =
|
def state[S <: AppState]()(implicit c: ClassTag[S]): S =
|
||||||
sm.getState(c.runtimeClass.asInstanceOf[Class[S]])
|
sm.getState(c.runtimeClass.asInstanceOf[Class[S]])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class SimpleApplicationExt(val sa: SimpleApplication)
|
implicit class SimpleApplicationExt[T <: SimpleApplication](private val sa: T)
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def stateManager: AppStateManager = sa.getStateManager()
|
def stateManager: AppStateManager = sa.getStateManager()
|
||||||
def inputManager: InputManager = sa.getInputManager()
|
def inputManager: InputManager = sa.getInputManager()
|
||||||
@ -107,9 +121,32 @@ package object implicits {
|
|||||||
def flyCam = Option(sa.getFlyByCamera())
|
def flyCam = Option(sa.getFlyByCamera())
|
||||||
def camera = sa.getCamera()
|
def camera = sa.getCamera()
|
||||||
def viewPort = sa.getViewPort()
|
def viewPort = sa.getViewPort()
|
||||||
|
def rootNode = sa.getRootNode()
|
||||||
|
|
||||||
|
def observableTick: Observable[Float] =
|
||||||
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
val as = new MyBaseState {
|
||||||
|
|
||||||
|
override def init(): Unit = {}
|
||||||
|
|
||||||
|
override def update(tpf: Float) = {
|
||||||
|
if (sub.onNext(tpf) == Ack.Stop)
|
||||||
|
c.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def stop(): Unit = {}
|
||||||
|
|
||||||
|
override def onEnable() = {}
|
||||||
|
override def onDisable() = {}
|
||||||
|
}
|
||||||
|
sa.stateManager.attach(as)
|
||||||
|
c := Cancelable(() => sa.stateManager.detach(as))
|
||||||
|
c
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class NodeExt(val n: Node) extends AnyVal {
|
implicit class NodeExt[T <: Node](private val n: T) extends AnyVal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches the given child
|
* Attaches the given child
|
||||||
@ -117,28 +154,34 @@ package object implicits {
|
|||||||
* @param s
|
* @param s
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def child(s: Spatial): Node = {
|
def withChild(s: Spatial): Node = {
|
||||||
n.attachChild(s)
|
n.attachChild(s)
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the list of children as a scala collection
|
* Gets the list of children as a monix observable
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
// def children = n.getChildren().asScala.toSeq
|
// def children = n.getChildren().asScala.toSeq
|
||||||
def children = Observable.fromIterable(n.getChildren().asScala)
|
def observableChildren =
|
||||||
|
Observable.fromIterable(n.getChildren().asScala)
|
||||||
|
|
||||||
|
def children = LazyList.from(n.getChildren().asScala)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach given children
|
* Attach given children
|
||||||
*
|
*
|
||||||
* @param lst
|
* @param lst
|
||||||
*/
|
*/
|
||||||
def children(lst: Iterable[Spatial]): Unit = {
|
def withChildren(lst: Spatial*): Node = {
|
||||||
for (c <- lst) n.child(c)
|
for (c <- lst) n.withChild(c)
|
||||||
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def +=(spatial: Spatial) = n.attachChild(spatial)
|
||||||
|
|
||||||
def depthFirst(cb: Spatial => Unit) =
|
def depthFirst(cb: Spatial => Unit) =
|
||||||
n.depthFirstTraversal(new SceneGraphVisitor() {
|
n.depthFirstTraversal(new SceneGraphVisitor() {
|
||||||
override def visit(s: Spatial) = cb(s)
|
override def visit(s: Spatial) = cb(s)
|
||||||
@ -156,7 +199,7 @@ package object implicits {
|
|||||||
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
||||||
case Ack.Continue => {
|
case Ack.Continue => {
|
||||||
//modifying a node's children list is forbidden
|
//modifying a node's children list is forbidden
|
||||||
val children = node.getChildren().asScala.to(LazyList)
|
val children = node.children
|
||||||
if (!children.isEmpty) {
|
if (!children.isEmpty) {
|
||||||
Task.sequence(
|
Task.sequence(
|
||||||
children.map(c => loop(subscriber, c))
|
children.map(c => loop(subscriber, c))
|
||||||
@ -191,9 +234,9 @@ package object implicits {
|
|||||||
def loop(
|
def loop(
|
||||||
subscriber: Subscriber[Spatial],
|
subscriber: Subscriber[Spatial],
|
||||||
spatials: LazyList[Spatial]
|
spatials: LazyList[Spatial]
|
||||||
): Task[Unit] = {
|
): Task[Unit] =
|
||||||
// spatial can be either a node or a geometry, but it's not a sealed trait
|
|
||||||
spatials match {
|
spatials match {
|
||||||
|
// spatial can be either a node or a geometry, but it's not a sealed trait
|
||||||
case head #:: tail =>
|
case head #:: tail =>
|
||||||
head match {
|
head match {
|
||||||
case g: Geometry =>
|
case g: Geometry =>
|
||||||
@ -203,7 +246,7 @@ package object implicits {
|
|||||||
case Stop => Task.unit
|
case Stop => Task.unit
|
||||||
}
|
}
|
||||||
case node: Node =>
|
case node: Node =>
|
||||||
val children = node.getChildren().asScala.to(LazyList)
|
val children = node.children
|
||||||
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
Task.deferFuture(subscriber.onNext(node)).flatMap {
|
||||||
case Continue =>
|
case Continue =>
|
||||||
loop(subscriber, tail #::: children)
|
loop(subscriber, tail #::: children)
|
||||||
@ -213,23 +256,54 @@ package object implicits {
|
|||||||
}
|
}
|
||||||
case LazyList() => Task.unit
|
case LazyList() => Task.unit
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||||
implicit val sched = sub.scheduler
|
implicit val sched = sub.scheduler
|
||||||
loop(sub, LazyList(n)).runToFuture
|
loop(sub, LazyList(n)).runToFuture
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def withControl[C <: Control](ctrl: C) = {
|
||||||
|
n.addControl(ctrl)
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
def withLocalTranslation(dir: ImVector3f) = {
|
||||||
|
n.setLocalTranslation(dir.mutable)
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
def withRotate(xAngle: Float, yAngle: Float, zAngle: Float) = {
|
||||||
|
n.rotate(xAngle, yAngle, zAngle)
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
def localRotation = n.getLocalRotation()
|
||||||
|
|
||||||
|
def localTranslation = n.getLocalTranslation()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class EntityDataExt(val ed: EntityData) extends AnyVal {
|
implicit class CameraNodeExt(private val cn: CameraNode) {
|
||||||
|
def withControlDir(controlDir: ControlDirection) = {
|
||||||
|
cn.setControlDir(controlDir)
|
||||||
|
cn
|
||||||
|
}
|
||||||
|
|
||||||
|
def withLookAt(position: ImVector3f, upVector: ImVector3f) = {
|
||||||
|
cn.lookAt(position.mutable, upVector.mutable)
|
||||||
|
cn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class EntityDataExt(private val ed: EntityData) extends AnyVal {
|
||||||
|
|
||||||
def query = new EntityQuery(ed)
|
def query = new EntityQuery(ed)
|
||||||
|
|
||||||
// def entities[T <: EntityComponent](entities: Seq[T])
|
// def entities[T <: EntityComponent](entities: Seq[T])
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class EntityExt(val e: EntityId) extends AnyVal {
|
implicit class EntityExt(private val e: EntityId) extends AnyVal {
|
||||||
def withComponents(classes: EntityComponent*)(implicit
|
def withComponents(classes: EntityComponent*)(implicit
|
||||||
ed: EntityData
|
ed: EntityData
|
||||||
): EntityId = {
|
): EntityId = {
|
||||||
@ -238,19 +312,40 @@ package object implicits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class ActorRefExt[Req](val a: ActorRef[Req]) extends AnyVal {
|
implicit class ActorRefExt[Req](private val a: ActorRef[Req]) extends AnyVal {
|
||||||
import akka.actor.typed.scaladsl.AskPattern._
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
def askT[Res](
|
|
||||||
|
/**
|
||||||
|
* @param replyTo
|
||||||
|
* @param timeout
|
||||||
|
* @param scheduler
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def askL[Res](
|
||||||
replyTo: ActorRef[Res] => Req
|
replyTo: ActorRef[Res] => Req
|
||||||
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
|
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
|
||||||
Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
|
Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
|
||||||
}
|
}
|
||||||
|
def ??[Res](
|
||||||
|
replyTo: ActorRef[Res] => Req
|
||||||
|
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
|
||||||
|
askL(replyTo)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as [[tell]], but wrapped in a Task
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def tellL(msg: Req) = UIO(a.tell(msg))
|
||||||
|
def !!(msg: Req) = tellL(msg)
|
||||||
|
|
||||||
}
|
}
|
||||||
// def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
|
// def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
|
||||||
// ask(replyTo)(timeout, scheduler)
|
// ask(replyTo)(timeout, scheduler)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
implicit class InputManagerExt(val inputManager: InputManager)
|
implicit class InputManagerExt(private val inputManager: InputManager)
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def withMapping(mapping: String, triggers: Trigger*): InputManager = {
|
def withMapping(mapping: String, triggers: Trigger*): InputManager = {
|
||||||
inputManager.addMapping(mapping, triggers: _*)
|
inputManager.addMapping(mapping, triggers: _*)
|
||||||
@ -264,7 +359,7 @@ package object implicits {
|
|||||||
|
|
||||||
def observableAction(mappingNames: String*): Observable[ActionEvent] = {
|
def observableAction(mappingNames: String*): Observable[ActionEvent] = {
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val al = new ActionListener {
|
val al = new ActionListener {
|
||||||
override def onAction(
|
override def onAction(
|
||||||
@ -281,13 +376,37 @@ package object implicits {
|
|||||||
|
|
||||||
inputManager.addListener(al, mappingNames: _*)
|
inputManager.addListener(al, mappingNames: _*)
|
||||||
|
|
||||||
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
|
||||||
|
|
||||||
|
Observable.create(OverflowStrategy.DropOld(100)) { sub =>
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
val al = new AnalogListener {
|
||||||
|
override def onAnalog(
|
||||||
|
binding: String,
|
||||||
|
value: Float,
|
||||||
|
tpf: Float
|
||||||
|
): Unit = {
|
||||||
|
if (
|
||||||
|
sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
|
||||||
|
)
|
||||||
|
c.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputManager.addListener(al, mappingNames: _*)
|
||||||
|
|
||||||
c := Cancelable(() => inputManager.removeListener(al))
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class PhysicsSpaceExt(val space: PhysicsSpace) extends AnyVal {
|
implicit class PhysicsSpaceExt(private val space: PhysicsSpace)
|
||||||
|
extends AnyVal {
|
||||||
|
|
||||||
def collisionObservable(): Observable[PhysicsCollisionEvent] = {
|
def collisionObservable(): Observable[PhysicsCollisionEvent] = {
|
||||||
|
|
||||||
@ -335,9 +454,15 @@ package object implicits {
|
|||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO Create a typeclass for this
|
||||||
|
def +=(anyObject: Any) = space.add(anyObject)
|
||||||
|
|
||||||
|
def +=(spatial: Spatial) = space.addAll(spatial)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class AssetManagerExt(val am: AssetManager) extends AnyVal {
|
implicit class AssetManagerExt(private val am: AssetManager) extends AnyVal {
|
||||||
def registerLocator(
|
def registerLocator(
|
||||||
assetPath: os.RelPath,
|
assetPath: os.RelPath,
|
||||||
locator: Class[_ <: AssetLocator]
|
locator: Class[_ <: AssetLocator]
|
||||||
@ -350,8 +475,26 @@ package object implicits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class Vector3fExt(val v: Vector3f) extends AnyVal {
|
implicit class BulletAppStateExt(private val bas: BulletAppState)
|
||||||
|
extends AnyVal {
|
||||||
|
def physicsSpace = bas.getPhysicsSpace()
|
||||||
|
def speed = bas.getSpeed()
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class BetterCharacterControlExt(
|
||||||
|
private val bcc: BetterCharacterControl
|
||||||
|
) {
|
||||||
|
def withJumpForce(force: ImVector3f) = {
|
||||||
|
bcc.setJumpForce(force.mutable)
|
||||||
|
bcc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class Vector3fExt(private val v: Vector3f) extends AnyVal {
|
||||||
|
//TODO add more operations
|
||||||
def +=(that: Vector3f) = v.addLocal(that)
|
def +=(that: Vector3f) = v.addLocal(that)
|
||||||
|
def +=(that: ImVector3f) = v.addLocal(that.x, that.y, that.z)
|
||||||
|
def +=:(that: ImVector3f) = v += that
|
||||||
def *=(that: Vector3f) = v.multLocal(that)
|
def *=(that: Vector3f) = v.multLocal(that)
|
||||||
def -=(that: Vector3f) = v.subtractLocal(that)
|
def -=(that: Vector3f) = v.subtractLocal(that)
|
||||||
def /=(that: Vector3f) = v.divideLocal(that)
|
def /=(that: Vector3f) = v.divideLocal(that)
|
||||||
@ -359,12 +502,12 @@ package object implicits {
|
|||||||
def immutable = ImVector3f(v.x, v.y, v.z)
|
def immutable = ImVector3f(v.x, v.y, v.z)
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class ImVector3fExt(val v: ImVector3f) extends AnyVal {
|
implicit class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
|
||||||
def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
|
def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
|
||||||
def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
|
def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
|
||||||
def *(f: Float): ImVector3f =
|
def *(f: Float): ImVector3f =
|
||||||
// v.copy(v.x * f, v.y * f, v.x * f)
|
v.copy(v.x * f, v.y * f, v.z * f)
|
||||||
v * ImVector3f(f, f, f)
|
// v * ImVector3f(f, f, f)
|
||||||
def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z)
|
def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z)
|
||||||
def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z)
|
def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z)
|
||||||
def unary_- = v.copy(-v.x, -v.y, -v.z)
|
def unary_- = v.copy(-v.x, -v.y, -v.z)
|
||||||
@ -372,106 +515,53 @@ package object implicits {
|
|||||||
def mutable = new Vector3f(v.x, v.y, v.z)
|
def mutable = new Vector3f(v.x, v.y, v.z)
|
||||||
}
|
}
|
||||||
|
|
||||||
// implicit val implVector3fForVector3 = new Vector3[Vector3f] {
|
implicit val implCanMoveForBetterCharacterControl =
|
||||||
// override def +(implicit v: Vector3f, that: com.jme3.math.Vector3f): Unit =
|
new CanMove[BetterCharacterControl] {
|
||||||
// v += that
|
override def move(
|
||||||
|
inst: BetterCharacterControl,
|
||||||
|
direction: ImVector3f
|
||||||
|
): Unit = {
|
||||||
|
// val dir = direction.mutable
|
||||||
|
// inst.setViewDirection(dir)
|
||||||
|
// inst.setViewDirection(direction.mutable)
|
||||||
|
inst.setWalkDirection(direction.mutable.multLocal(50f))
|
||||||
|
}
|
||||||
|
override def jump(inst: BetterCharacterControl): Unit = inst.jump()
|
||||||
|
override def rotate(
|
||||||
|
inst: BetterCharacterControl,
|
||||||
|
rotateDir: RotateDir
|
||||||
|
): Unit = {
|
||||||
|
val q =
|
||||||
|
rotateDir match {
|
||||||
|
case RotateDir.Left =>
|
||||||
|
new Quaternion()
|
||||||
|
.fromAngleAxis(-10f * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||||
|
case RotateDir.Right =>
|
||||||
|
new Quaternion()
|
||||||
|
.fromAngleAxis(10 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||||
|
}
|
||||||
|
|
||||||
// override def *(implicit v: Vector3f, that: Vector3f): Unit = v *= that
|
val tmp = new Vector3f()
|
||||||
|
inst.getViewDirection(tmp)
|
||||||
// override def -(implicit v: Vector3f, that: Vector3f): Unit = v -= that
|
inst.setViewDirection(q.mult(tmp))
|
||||||
|
}
|
||||||
// override def /(implicit v: Vector3f, that: Vector3f): Unit = v /= that
|
override def stop(inst: BetterCharacterControl) =
|
||||||
|
inst.setWalkDirection(Vector3f.ZERO)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// implicit val implImVector3fForVector3 = new Vector3[ImVector3f] {
|
|
||||||
// override def +(implicit v: ImVector3f, that: ImVector3f): Unit =
|
|
||||||
// v + that
|
|
||||||
|
|
||||||
// override def *(implicit v: ImVector3f, that: ImVector3f): Unit = v * that
|
|
||||||
|
|
||||||
// override def -(implicit v: ImVector3f, that: ImVector3f): Unit = v - that
|
|
||||||
|
|
||||||
// override def /(implicit v: ImVector3f, that: ImVector3f): Unit = v / that
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// def test[T](v: T)(implicit ev: Vector3[T]) = {
|
|
||||||
// import ev._
|
|
||||||
// ev.+
|
|
||||||
// }
|
|
||||||
|
|
||||||
implicit val implCanMoveForGeom = new CanMove[Spatial] {
|
|
||||||
|
|
||||||
|
implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging {
|
||||||
override def move(inst: Spatial, direction: ImVector3f): Unit = {
|
override def move(inst: Spatial, direction: ImVector3f): Unit = {
|
||||||
// val v = inst.getLocalTranslation()
|
|
||||||
// inst match {
|
|
||||||
// case n: Node => println(n.getChildren())
|
|
||||||
// case _ =>
|
|
||||||
// }
|
|
||||||
inst.move(direction.mutable)
|
inst.move(direction.mutable)
|
||||||
}
|
}
|
||||||
|
override def jump(inst: Spatial): Unit =
|
||||||
override def getDirection(
|
logger.warn("`Jump` is not implemented for type `Spatial`")
|
||||||
cam: Camera,
|
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {
|
||||||
cardinalDir: CardinalDirection
|
rotateDir match {
|
||||||
): ImVector3f = {
|
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
|
||||||
// val camDir =
|
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
|
||||||
// cam.getDirection().immutable * 0.6f
|
|
||||||
// val camLeft = cam.getLeft().immutable * 0.4f
|
|
||||||
|
|
||||||
// val zero = ImVector3f.ZERO
|
|
||||||
// val dir = cardinalDir
|
|
||||||
// val walkDir = {
|
|
||||||
// val mutWalkDir = new Vector3f()
|
|
||||||
// if (dir.left) {
|
|
||||||
// // ctx.log.trace("left")
|
|
||||||
// mutWalkDir += (zero + camLeft).mutable
|
|
||||||
// }
|
|
||||||
// if (dir.right) {
|
|
||||||
// // ctx.log.trace("right")
|
|
||||||
// mutWalkDir += (zero + -camLeft).mutable
|
|
||||||
// }
|
|
||||||
// if (dir.up) {
|
|
||||||
// // ctx.log.trace("up")
|
|
||||||
// mutWalkDir += (zero + camDir).mutable
|
|
||||||
// }
|
|
||||||
// if (dir.down) {
|
|
||||||
// // ctx.log.trace("down")
|
|
||||||
// mutWalkDir += (zero + -camDir).mutable
|
|
||||||
// }
|
|
||||||
// mutWalkDir.immutable
|
|
||||||
// }
|
|
||||||
// walkDir
|
|
||||||
|
|
||||||
// val camDir =
|
|
||||||
// cam.getDirection().immutable * 0.6f
|
|
||||||
// val camLeft = cam.getLeft().immutable * 0.4f
|
|
||||||
|
|
||||||
val zero = ImVector3f.ZERO
|
|
||||||
val dir = cardinalDir
|
|
||||||
val walkDir = {
|
|
||||||
val mutWalkDir = new Vector3f()
|
|
||||||
if (dir.left) {
|
|
||||||
// ctx.log.trace("left")
|
|
||||||
mutWalkDir += (zero + ImVector3f(-1, 0, 0)).mutable
|
|
||||||
}
|
|
||||||
if (dir.right) {
|
|
||||||
// ctx.log.trace("right")
|
|
||||||
mutWalkDir += (zero + ImVector3f(1, 0, 0)).mutable
|
|
||||||
}
|
|
||||||
if (dir.up) {
|
|
||||||
// ctx.log.trace("up")
|
|
||||||
mutWalkDir += (zero + ImVector3f(0, 0, -1)).mutable
|
|
||||||
}
|
|
||||||
if (dir.down) {
|
|
||||||
// ctx.log.trace("down")
|
|
||||||
mutWalkDir += (zero + ImVector3f(0, 0, 1)).mutable
|
|
||||||
}
|
|
||||||
mutWalkDir.immutable
|
|
||||||
}
|
}
|
||||||
walkDir
|
|
||||||
}
|
}
|
||||||
|
override def stop(inst: Spatial) = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val implJFXConsoleStreamForTextArea =
|
implicit val implJFXConsoleStreamForTextArea =
|
||||||
|
@ -1,102 +1,22 @@
|
|||||||
package wow.doge.mygame.events
|
package wow.doge.mygame.events
|
||||||
|
|
||||||
// import akka.event.ActorEventBus
|
|
||||||
// import akka.event.ManagedActorClassification
|
|
||||||
// import akka.event.ActorClassifier
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
// import akka.actor.ActorSystem
|
|
||||||
// import akka.event.EventBus
|
|
||||||
// import akka.util.Subclassification
|
|
||||||
// import java.util.concurrent.atomic.AtomicReference
|
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import akka.event.EventStream
|
import akka.event.EventStream
|
||||||
|
|
||||||
// private[events] final case class ClassificationMessage(ref: ActorRef, id: Int)
|
/**
|
||||||
|
* A (typed) event bus
|
||||||
// class ActorBusImpl(val system: ActorSystem, val busSize: Int)
|
* Copied (and repurposed) from Akka's EventStream
|
||||||
// extends ActorEventBus
|
*/
|
||||||
// with ActorClassifier
|
|
||||||
// with ManagedActorClassification {
|
|
||||||
// type Event = ClassificationMessage
|
|
||||||
|
|
||||||
// // is used for extracting the classifier from the incoming events
|
|
||||||
// override protected def classify(event: Event): ActorRef = event.ref
|
|
||||||
|
|
||||||
// // determines the initial size of the index data structure
|
|
||||||
// // used internally (i.e. the expected number of different classifiers)
|
|
||||||
// override protected def mapSize: Int = busSize
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class StartsWithSubclassification extends Subclassification[String] {
|
|
||||||
// override def isEqual(x: String, y: String): Boolean =
|
|
||||||
// x == y
|
|
||||||
|
|
||||||
// override def isSubclass(x: String, y: String): Boolean =
|
|
||||||
// x.startsWith(y)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// import akka.event.SubchannelClassification
|
|
||||||
|
|
||||||
// final case class MsgEnvelope(topic: String, payload: Any)
|
|
||||||
// import akka.actor.typed.scaladsl.adapter._
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Publishes the payload of the MsgEnvelope when the topic of the
|
|
||||||
// * MsgEnvelope starts with the String specified when subscribing.
|
|
||||||
// */
|
|
||||||
// class SubchannelBusImpl extends EventBus with SubchannelClassification {
|
|
||||||
// type Event = Any
|
|
||||||
// type Classifier = Class[_]
|
|
||||||
// type Subscriber = ActorRef
|
|
||||||
|
|
||||||
// // Subclassification is an object providing `isEqual` and `isSubclass`
|
|
||||||
// // to be consumed by the other methods of this classifier
|
|
||||||
// // override protected val subclassification: Subclassification[Classifier] =
|
|
||||||
// // new StartsWithSubclassification
|
|
||||||
|
|
||||||
// private val initiallySubscribedOrUnsubscriber =
|
|
||||||
// new AtomicReference[Either[Set[ActorRef], ActorRef]](Left(Set.empty))
|
|
||||||
|
|
||||||
// override protected implicit val subclassification
|
|
||||||
// : Subclassification[Classifier] = new Subclassification[Class[_]] {
|
|
||||||
// def isEqual(x: Class[_], y: Class[_]) = x == y
|
|
||||||
// def isSubclass(x: Class[_], y: Class[_]) = y.isAssignableFrom(x)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // is used for extracting the classifier from the incoming events
|
|
||||||
// override protected def classify(event: Event): Classifier = event.getClass()
|
|
||||||
|
|
||||||
// // will be invoked for each event for all subscribers which registered
|
|
||||||
// // themselves for the event’s classifier
|
|
||||||
// override protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
|
||||||
// // subscriber ! event.payload
|
|
||||||
// subscriber ! event
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
object EventBus {
|
object EventBus {
|
||||||
sealed trait Command[A]
|
sealed trait Command[-A]
|
||||||
final case class Publish[A, E <: A](event: E, publisher: ActorRef[_])
|
final case class Publish[A, E <: A](
|
||||||
extends Command[A]
|
event: E,
|
||||||
|
publisherName: String
|
||||||
|
) extends Command[A]
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribe a typed actor to listen for types or subtypes of E
|
|
||||||
* by sending this command to the [[akka.actor.typed.ActorSystem.eventStream]].
|
|
||||||
*
|
|
||||||
* ==Simple example==
|
|
||||||
* {{{
|
|
||||||
* sealed trait A
|
|
||||||
* case object A1 extends A
|
|
||||||
* //listen for all As
|
|
||||||
* def subscribe(actorSystem: ActorSystem[_], actorRef: ActorRef[A]) =
|
|
||||||
* actorSystem.eventStream ! EventStream.Subscribe(actorRef)
|
|
||||||
* //listen for A1s only
|
|
||||||
* def subscribe(actorSystem: ActorSystem[_], actorRef: ActorRef[A]) =
|
|
||||||
* actorSystem.eventStream ! EventStream.Subscribe[A1](actorRef)
|
|
||||||
* }}}
|
|
||||||
*/
|
|
||||||
final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit
|
final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit
|
||||||
classTag: ClassTag[E]
|
classTag: ClassTag[E]
|
||||||
) extends Command[A] {
|
) extends Command[A] {
|
||||||
@ -104,10 +24,6 @@ object EventBus {
|
|||||||
def topic: Class[_] = classTag.runtimeClass
|
def topic: Class[_] = classTag.runtimeClass
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribe an actor ref from the event stream
|
|
||||||
* by sending this command to the [[akka.actor.typed.ActorSystem.eventStream]].
|
|
||||||
*/
|
|
||||||
final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E])
|
final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E])
|
||||||
extends Command[A]
|
extends Command[A]
|
||||||
|
|
||||||
@ -126,7 +42,7 @@ class EventBus[B] {
|
|||||||
eventStream: akka.event.EventStream
|
eventStream: akka.event.EventStream
|
||||||
): Behavior[EventBus.Command[B]] =
|
): Behavior[EventBus.Command[B]] =
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case EventBus.Publish(event, publisher) =>
|
case EventBus.Publish(event, name) =>
|
||||||
eventStream.publish(event)
|
eventStream.publish(event)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case s @ EventBus.Subscribe(subscriber) =>
|
case s @ EventBus.Subscribe(subscriber) =>
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
package wow.doge.mygame.events
|
package wow.doge.mygame.events
|
||||||
|
|
||||||
// object Test {
|
|
||||||
|
|
||||||
// Events.BulletFired
|
|
||||||
// }
|
|
||||||
|
|
||||||
object Events {
|
object Events {
|
||||||
sealed trait Event
|
sealed trait Event
|
||||||
case object BulletFired extends Event
|
final case object BulletFired extends Event
|
||||||
// type BulletFired = BulletFired.type
|
// type BulletFired = BulletFired.type
|
||||||
case class EventWithData(data: Int) extends Event
|
final case class EventWithData(data: Int) extends Event
|
||||||
|
|
||||||
sealed trait Tick extends Event
|
sealed trait Tick extends Event
|
||||||
case object RenderTick extends Tick
|
object Tick {
|
||||||
case object PhysicsTick extends Tick
|
final case object RenderTick extends Tick
|
||||||
|
final case object PhysicsTick extends Tick
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,82 @@
|
|||||||
package wow.doge.mygame.events
|
package wow.doge.mygame.events
|
||||||
|
|
||||||
trait EventsModule {}
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
|
import akka.actor.typed.Props
|
||||||
|
import akka.util.Timeout
|
||||||
|
import akka.actor.typed.Scheduler
|
||||||
|
import akka.actor.typed.LogOptions
|
||||||
|
import com.typesafe.scalalogging.{Logger => SLLogger}
|
||||||
|
import wow.doge.mygame.events.EventBus
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent
|
||||||
|
import akka.actor.typed.SupervisorStrategy
|
||||||
|
|
||||||
|
trait EventsModule {
|
||||||
|
def spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||||
|
implicit def akkaScheduler: Scheduler
|
||||||
|
implicit def timeout: Timeout
|
||||||
|
def eventBusLogger = SLLogger[EventBus[_]]
|
||||||
|
|
||||||
|
// val subscribingActor =
|
||||||
|
// spawnProtocol.askT(
|
||||||
|
// SpawnProtocol.Spawn[Events.PhysicsTick.type](
|
||||||
|
// SubscribingActor(),
|
||||||
|
// "subscriber-1",
|
||||||
|
// Props.empty,
|
||||||
|
// _
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
|
||||||
|
lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus")
|
||||||
|
|
||||||
|
// spawnProtocol.askL(
|
||||||
|
// SpawnProtocol.Spawn[EventBus.Command[Events.Tick]](
|
||||||
|
// Behaviors.logMessages(
|
||||||
|
// logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||||
|
// EventBus[Events.Tick]()
|
||||||
|
// ),
|
||||||
|
// "tickEventBus",
|
||||||
|
// Props.empty,
|
||||||
|
// _
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
|
||||||
|
lazy val playerMovementEventBusTask =
|
||||||
|
createEventBus[PlayerMovementEvent]("movementEventBus")
|
||||||
|
|
||||||
|
// spawnProtocol.askL(
|
||||||
|
// SpawnProtocol.Spawn[EventBus.Command[Events.Movement.PlayerMovement]](
|
||||||
|
// Behaviors.logMessages(
|
||||||
|
// logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||||
|
// EventBus[Events.Movement.PlayerMovement]()
|
||||||
|
// ),
|
||||||
|
// "movementEventBus",
|
||||||
|
// Props.empty,
|
||||||
|
// _
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
|
||||||
|
// tickEventBus ! EventBus.Subscribe(subscribingActor)
|
||||||
|
|
||||||
|
// tickEventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self)
|
||||||
|
def createEventBus[T](busName: String) =
|
||||||
|
spawnProtocol.askL(
|
||||||
|
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||||
|
Behaviors.logMessages(
|
||||||
|
logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||||
|
Behaviors
|
||||||
|
.supervise(EventBus[T]())
|
||||||
|
.onFailure[Exception](SupervisorStrategy.restart)
|
||||||
|
),
|
||||||
|
busName,
|
||||||
|
Props.empty,
|
||||||
|
_
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
object EventTypes {
|
||||||
|
type EventBus[T] = ActorRef[EventBus.Command[T]]
|
||||||
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
// package wow.doge.mygame.subsystems.events
|
||||||
|
|
||||||
|
// import akka.actor.typed.ActorRef
|
||||||
|
// import akka.actor.typed.SpawnProtocol
|
||||||
|
// import wow.doge.mygame.implicits._
|
||||||
|
// import akka.actor.typed.Props
|
||||||
|
// import akka.actor.typed.LogOptions
|
||||||
|
// import com.typesafe.scalalogging.{Logger => SLLogger}
|
||||||
|
// import wow.doge.mygame.events.EventBus
|
||||||
|
// import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
// import wow.doge.mygame.events.Events
|
||||||
|
// import cats.effect.Resource
|
||||||
|
// import monix.bio.Task
|
||||||
|
// import akka.actor.typed.ActorSystem
|
||||||
|
// import scala.concurrent.duration._
|
||||||
|
|
||||||
|
// trait EventsModule2 {
|
||||||
|
// def eventBusesResource(
|
||||||
|
// spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||||
|
// eventBusLogger: com.typesafe.scalalogging.Logger = SLLogger[EventBus[_]]
|
||||||
|
// ): Resource[
|
||||||
|
// Task,
|
||||||
|
// (
|
||||||
|
// ActorRef[EventBus.Command[Events.Tick]],
|
||||||
|
// ActorRef[EventBus.Command[Events.Movement.PlayerMovement]]
|
||||||
|
// )
|
||||||
|
// ] = {
|
||||||
|
// def createEventBus[T](busName: String) =
|
||||||
|
// spawnProtocol.askL(
|
||||||
|
// SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||||
|
// Behaviors.logMessages(
|
||||||
|
// logOptions = LogOptions().withLogger(eventBusLogger.underlying),
|
||||||
|
// EventBus[T]()
|
||||||
|
// ),
|
||||||
|
// busName,
|
||||||
|
// Props.empty,
|
||||||
|
// _
|
||||||
|
// )
|
||||||
|
|
||||||
|
// )(1.second, spawnProtocol.scheduler)
|
||||||
|
|
||||||
|
// Resource.liftF {
|
||||||
|
// {
|
||||||
|
// lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus")
|
||||||
|
|
||||||
|
// lazy val playerMovementEventBusTask =
|
||||||
|
// createEventBus[Events.Movement.PlayerMovement]("movementEventBus")
|
||||||
|
|
||||||
|
// // val r = (tickEventBusTask, playerMovementEventBusTask)
|
||||||
|
// // Task(r)
|
||||||
|
// for {
|
||||||
|
// tickEventBus <- tickEventBusTask
|
||||||
|
// playerMovementEventBus <- playerMovementEventBusTask
|
||||||
|
// } yield (tickEventBus, playerMovementEventBus)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
@ -0,0 +1,33 @@
|
|||||||
|
package wow.doge.mygame.subsystems.events
|
||||||
|
|
||||||
|
import wow.doge.mygame.subsystems.movement.CanMove
|
||||||
|
|
||||||
|
sealed trait MovementEvent
|
||||||
|
|
||||||
|
object MovementEvent {
|
||||||
|
final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T)
|
||||||
|
extends MovementEvent
|
||||||
|
final case class MovedUp[T: CanMove](pressed: Boolean, movable: T)
|
||||||
|
extends MovementEvent
|
||||||
|
final case class MovedRight[T: CanMove](pressed: Boolean, movable: T)
|
||||||
|
extends MovementEvent
|
||||||
|
final case class MovedDown[T: CanMove](pressed: Boolean, movable: T)
|
||||||
|
extends MovementEvent
|
||||||
|
|
||||||
|
sealed trait PlayerMovementEvent extends MovementEvent
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -15,19 +15,19 @@ import java.nio.file.NoSuchFileException
|
|||||||
import io.circe.generic.JsonCodec
|
import io.circe.generic.JsonCodec
|
||||||
|
|
||||||
@JsonCodec
|
@JsonCodec
|
||||||
case class Test1(hello1: String, hello2: String)
|
final case class Test1(hello1: String, hello2: String)
|
||||||
@JsonCodec
|
@JsonCodec
|
||||||
case class Test2(hello1: String)
|
final case class Test2(hello1: String)
|
||||||
case class Plugin(name: String, priority: Int)
|
final case class Plugin(name: String, priority: Int)
|
||||||
object Plugin {
|
object Plugin {
|
||||||
implicit val pluginFormat: Decoder[Plugin] = deriveDecoder
|
implicit val pluginFormat: Decoder[Plugin] = deriveDecoder
|
||||||
}
|
}
|
||||||
|
|
||||||
object ModdingSystem {
|
object ModdingSystem {
|
||||||
sealed trait Error extends Serializable with Product
|
sealed trait Error extends Serializable with Product
|
||||||
case class CouldNotDecode(cause: String) extends Error
|
final case class CouldNotDecode(cause: String) extends Error
|
||||||
case class ParseFailure(cause: String) extends Error
|
final case class ParseFailure(cause: String) extends Error
|
||||||
case class FileNotFound(name: String) extends Error
|
final case class FileNotFound(fileName: String) extends Error
|
||||||
case object GenericError extends Error
|
case object GenericError extends Error
|
||||||
|
|
||||||
def readPluginsList(dir: os.Path): Try[Either[Error, ArraySeq[Plugin]]] =
|
def readPluginsList(dir: os.Path): Try[Either[Error, ArraySeq[Plugin]]] =
|
||||||
@ -119,7 +119,7 @@ object ModdingSystem {
|
|||||||
(readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins))
|
(readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins))
|
||||||
(parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
|
(parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
|
||||||
res <- UIO(mergePluginData(parseSuccesses))
|
res <- UIO(mergePluginData(parseSuccesses))
|
||||||
f <- UIO {
|
_ <- UIO {
|
||||||
println(s"Read Successes = ${readSuccesses.to(Seq)}")
|
println(s"Read Successes = ${readSuccesses.to(Seq)}")
|
||||||
println(s"Read Failures = ${readFailures.to(Seq)}")
|
println(s"Read Failures = ${readFailures.to(Seq)}")
|
||||||
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
|
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
|
||||||
|
@ -12,21 +12,23 @@ import javax.script.ScriptEngine
|
|||||||
import javax.script.ScriptEngineManager
|
import javax.script.ScriptEngineManager
|
||||||
import groovy.util.GroovyScriptEngine
|
import groovy.util.GroovyScriptEngine
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
import akka.actor.typed.LogOptions
|
||||||
|
import org.slf4j.event.Level
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import com.softwaremill.tagging._
|
||||||
|
|
||||||
object ScriptActor {
|
object ScriptActor {
|
||||||
type Kotlin
|
trait Kotlin
|
||||||
type MyScriptEngine[T] = ScriptEngine
|
type KotlinScriptEngine = ScriptEngine @@ Kotlin
|
||||||
type KotlinScriptEngine = MyScriptEngine[Kotlin]
|
|
||||||
|
|
||||||
final case class Error(reason: String)
|
final case class Error(reason: String)
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case class CompileAny(
|
final case class CompileAny(
|
||||||
path: os.Path,
|
path: os.Path,
|
||||||
result: ActorRef[Either[Error, Any]]
|
result: ActorRef[Either[Error, Any]]
|
||||||
) extends Command
|
) extends Command
|
||||||
|
|
||||||
def defaultScalaRunner() =
|
lazy val defaultScalaRunner =
|
||||||
ammonite
|
ammonite
|
||||||
.Main(
|
.Main(
|
||||||
storageBackend = new Folder(
|
storageBackend = new Folder(
|
||||||
@ -36,28 +38,34 @@ object ScriptActor {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def defaultKotlinRunner(): KotlinScriptEngine = {
|
lazy val defaultKotlinRunner: KotlinScriptEngine = {
|
||||||
val manager = new ScriptEngineManager()
|
val manager = new ScriptEngineManager()
|
||||||
val engine = manager.getEngineByExtension("main.kts")
|
val engine = manager.getEngineByExtension("main.kts")
|
||||||
engine
|
engine.taggedWith[Kotlin]
|
||||||
}
|
}
|
||||||
|
|
||||||
def defaultGroovyRunner(): GroovyScriptEngine =
|
lazy val defaultGroovyRunner: GroovyScriptEngine =
|
||||||
new GroovyScriptEngine(os.pwd.toString())
|
new GroovyScriptEngine(os.pwd.toString)
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
scalaRunner: Main = defaultScalaRunner(),
|
scalaRunner: Main = defaultScalaRunner,
|
||||||
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner(),
|
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner,
|
||||||
groovyRunner: GroovyScriptEngine = defaultGroovyRunner()
|
groovyRunner: GroovyScriptEngine = defaultGroovyRunner
|
||||||
// parent: ActorRef[ScriptStoringActor.Command]
|
|
||||||
): Behavior[ScriptActor.Command] =
|
): Behavior[ScriptActor.Command] =
|
||||||
Behaviors.setup(ctx =>
|
Behaviors.logMessages(
|
||||||
new ScriptActor(
|
LogOptions()
|
||||||
scalaRunner,
|
.withLevel(Level.TRACE)
|
||||||
kotlinRunner,
|
.withLogger(
|
||||||
groovyRunner,
|
Logger[ScriptActor].underlying
|
||||||
ctx
|
),
|
||||||
).receiveMessage
|
Behaviors.setup(ctx =>
|
||||||
|
new ScriptActor(
|
||||||
|
scalaRunner,
|
||||||
|
kotlinRunner,
|
||||||
|
groovyRunner,
|
||||||
|
ctx
|
||||||
|
).receiveMessage
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed trait ScriptType
|
sealed trait ScriptType
|
||||||
|
@ -11,6 +11,9 @@ import akka.util.Timeout
|
|||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
import scala.util.Failure
|
import scala.util.Failure
|
||||||
import akka.actor.typed.SupervisorStrategy
|
import akka.actor.typed.SupervisorStrategy
|
||||||
|
import akka.actor.typed.LogOptions
|
||||||
|
import org.slf4j.event.Level
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
object ScriptCachingActor {
|
object ScriptCachingActor {
|
||||||
|
|
||||||
@ -22,9 +25,16 @@ object ScriptCachingActor {
|
|||||||
type ScriptResult = Either[ScriptActor.Error, ScriptObject]
|
type ScriptResult = Either[ScriptActor.Error, ScriptObject]
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param scriptPath path of the script to compile
|
||||||
|
* @param requester return address of the asking actor
|
||||||
|
* @param force if true, forces script compilation even if a previous cached version exists
|
||||||
|
*/
|
||||||
final case class Get(
|
final case class Get(
|
||||||
scriptPath: os.Path,
|
scriptPath: os.Path,
|
||||||
requester: ActorRef[ScriptResult]
|
requester: ActorRef[ScriptResult],
|
||||||
|
force: Boolean = false
|
||||||
) extends Command
|
) extends Command
|
||||||
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
||||||
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
||||||
@ -37,37 +47,58 @@ object ScriptCachingActor {
|
|||||||
requester: ActorRef[ScriptResult]
|
requester: ActorRef[ScriptResult]
|
||||||
) extends Command
|
) extends Command
|
||||||
|
|
||||||
final case class Props(
|
// final case class Props(
|
||||||
ctx: ActorContext[Command],
|
// ctx: ActorContext[Command],
|
||||||
scriptActor: ActorRef[ScriptActor.Command]
|
// scriptActor: ActorRef[ScriptActor.Command]
|
||||||
)
|
// ) {
|
||||||
|
// def create(state: State = State(Map.empty)): Behavior[Command] =
|
||||||
|
// Behaviors.logMessages {
|
||||||
|
// Behaviors.setup { ctx =>
|
||||||
|
// val pool = ScriptActorPool(4)
|
||||||
|
// val scriptsRouter = ctx.spawn(pool, "script-actors-pool")
|
||||||
|
// new ScriptCachingActor(this)
|
||||||
|
// .receiveMessage(state)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
final case class State(scriptsMap: ScriptsMap)
|
final case class State(scriptsMap: ScriptsMap)
|
||||||
|
|
||||||
def apply(state: State = State(Map.empty)): Behavior[Command] =
|
def apply(state: State = State(Map.empty)): Behavior[Command] =
|
||||||
Behaviors.logMessages {
|
Behaviors.logMessages(
|
||||||
|
LogOptions()
|
||||||
|
.withLevel(Level.TRACE)
|
||||||
|
.withLogger(
|
||||||
|
Logger[ScriptCachingActor].underlying
|
||||||
|
),
|
||||||
Behaviors.setup { ctx =>
|
Behaviors.setup { ctx =>
|
||||||
val pool = ScriptActorPool(4)
|
val pool = ScriptActorPool(4)
|
||||||
val scriptsRouter = ctx.spawn(pool, "script-actors-pool")
|
val scriptsRouter = ctx.spawn(pool, "script-actors-pool")
|
||||||
new ScriptCachingActor(Props(ctx, scriptsRouter)).receiveMessage(state)
|
new ScriptCachingActor(ctx, scriptsRouter).receiveMessage(state)
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScriptCachingActor(props: ScriptCachingActor.Props) {
|
class ScriptCachingActor(
|
||||||
|
ctx: ActorContext[ScriptCachingActor.Command],
|
||||||
|
scriptActor: ActorRef[ScriptActor.Command]
|
||||||
|
) {
|
||||||
import com.softwaremill.quicklens._
|
import com.softwaremill.quicklens._
|
||||||
import ScriptCachingActor._
|
import ScriptCachingActor._
|
||||||
import Methods._
|
import Methods._
|
||||||
def receiveMessage(state: State): Behavior[Command] =
|
def receiveMessage(state: State): Behavior[Command] =
|
||||||
Behaviors.receiveMessage { msg =>
|
Behaviors.receiveMessage { msg =>
|
||||||
msg match {
|
msg match {
|
||||||
case Get(scriptPath, requester) =>
|
case Get(scriptPath, requester, force) =>
|
||||||
getOrCompileScript(
|
if (force)
|
||||||
props.ctx,
|
ctx.self ! DelegateToChild(scriptActor, scriptPath, requester)
|
||||||
scriptPath,
|
else
|
||||||
state.scriptsMap,
|
getOrCompileScript(
|
||||||
props.scriptActor,
|
ctx,
|
||||||
requester
|
scriptPath,
|
||||||
)
|
state.scriptsMap,
|
||||||
|
scriptActor,
|
||||||
|
requester
|
||||||
|
)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case DelegateToChild(scriptActor, scriptPath, requester) =>
|
case DelegateToChild(scriptActor, scriptPath, requester) =>
|
||||||
@ -75,7 +106,7 @@ class ScriptCachingActor(props: ScriptCachingActor.Props) {
|
|||||||
implicit val timeout = Timeout(15.seconds)
|
implicit val timeout = Timeout(15.seconds)
|
||||||
// child ! ScriptActor.CompileAny(scriptPath, requester)
|
// child ! ScriptActor.CompileAny(scriptPath, requester)
|
||||||
askChildForScriptCompilation(
|
askChildForScriptCompilation(
|
||||||
props.ctx,
|
ctx,
|
||||||
scriptActor,
|
scriptActor,
|
||||||
scriptPath,
|
scriptPath,
|
||||||
requester
|
requester
|
||||||
@ -87,10 +118,10 @@ class ScriptCachingActor(props: ScriptCachingActor.Props) {
|
|||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case Put(scriptPath, script) =>
|
case Put(scriptPath, script) =>
|
||||||
props.ctx.log.debug(s"Putting $script at path $scriptPath")
|
ctx.log.debug(s"Putting $script at path $scriptPath")
|
||||||
val newState =
|
val newState =
|
||||||
state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
|
state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
|
||||||
props.ctx.log.trace(newState.toString())
|
ctx.log.trace(newState.toString())
|
||||||
receiveMessage(state = newState)
|
receiveMessage(state = newState)
|
||||||
|
|
||||||
case NoOp => Behaviors.same
|
case NoOp => Behaviors.same
|
||||||
|
25
src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala
Normal file
25
src/main/scala/wow/doge/mygame/utils/AkkaUtils.scala
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package wow.doge.mygame.utils
|
||||||
|
|
||||||
|
import akka.actor.typed.Props
|
||||||
|
import akka.util.Timeout
|
||||||
|
import akka.actor.typed.Scheduler
|
||||||
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.SpawnProtocol
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
|
object AkkaUtils {
|
||||||
|
def spawnActorL[T](
|
||||||
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
actorName: String,
|
||||||
|
behavior: Behavior[T]
|
||||||
|
)(implicit timeout: Timeout, scheduler: Scheduler) =
|
||||||
|
spawnProtocol.askL[ActorRef[T]](
|
||||||
|
SpawnProtocol.Spawn(
|
||||||
|
behavior,
|
||||||
|
actorName,
|
||||||
|
Props.empty,
|
||||||
|
_
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
12
src/main/scala/wow/doge/mygame/utils/IOUtils.scala
Normal file
12
src/main/scala/wow/doge/mygame/utils/IOUtils.scala
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package wow.doge.mygame.utils
|
||||||
|
|
||||||
|
import monix.bio.IO
|
||||||
|
|
||||||
|
object IOUtils {
|
||||||
|
def toTask[T](bio: monix.bio.IO[Throwable, T]) =
|
||||||
|
monix.eval.Task.deferAction(implicit s => bio.to[monix.eval.Task])
|
||||||
|
|
||||||
|
def toIO[T](task: monix.eval.Task[T]) =
|
||||||
|
IO.deferAction(implicit s => IO.from(task))
|
||||||
|
|
||||||
|
}
|
14
src/main/scala/wow/doge/mygame/utils/Settings.scala
Normal file
14
src/main/scala/wow/doge/mygame/utils/Settings.scala
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package wow.doge.mygame.utils
|
||||||
|
|
||||||
|
case class Display(
|
||||||
|
width: Int = 640,
|
||||||
|
height: Int = 480,
|
||||||
|
title: String = "JME-Game",
|
||||||
|
fullScren: Boolean = false,
|
||||||
|
vsync: Boolean = false,
|
||||||
|
frameRate: Int = -1
|
||||||
|
)
|
||||||
|
object Display {
|
||||||
|
val default = Display()
|
||||||
|
}
|
||||||
|
case class GlobalSettings(display: Display = Display.default)
|
Loading…
Reference in New Issue
Block a user