Browse Source

dunno lol

master
Rohan Sircar 3 years ago
parent
commit
a2a328d078
  1. 4
      build.sbt
  2. 81
      src/main/scala/wow/doge/mygame/Main.scala
  3. 4
      src/main/scala/wow/doge/mygame/game/GameApp.scala
  4. 24
      src/main/scala/wow/doge/mygame/game/GameAppActor.scala
  5. 18
      src/main/scala/wow/doge/mygame/game/GameModule.scala
  6. 94
      src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala
  7. 2
      src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala
  8. 201
      src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala
  9. 30
      src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala
  10. 212
      src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala
  11. 42
      src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala
  12. 102
      src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala
  13. 19
      src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala
  14. 70
      src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala
  15. 69
      src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
  16. 43
      src/main/scala/wow/doge/mygame/implicits/TestEnum.scala
  17. 271
      src/main/scala/wow/doge/mygame/implicits/package.scala
  18. 2
      src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala
  19. 49
      src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala
  20. 123
      src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala
  21. 16
      src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala
  22. 8
      src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala
  23. 23
      src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala
  24. 19
      src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala

4
build.sbt

@ -89,7 +89,9 @@ lazy val root = (project in file(".")).settings(
"com.github.valskalla" %% "odin-slf4j" % "0.8.1",
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
"io.circe" %% "circe-config" % "0.8.0",
"com.beachape" %% "enumeratum-circe" % "1.6.1"
),
// Determine OS version of JavaFX binaries

81
src/main/scala/wow/doge/mygame/Main.scala

@ -1,6 +1,5 @@
package wow.doge.mygame
import monix.bio.Task
import cats.effect.Resource
import io.odin.syntax._
@ -10,15 +9,20 @@ import com.softwaremill.macwire._
import scala.concurrent.duration._
import monix.bio.BIOApp
import monix.bio.UIO
import monix.bio.IO
import io.odin._
import wow.doge.mygame.implicits._
import wow.doge.mygame.game.GameAppResource
import io.odin.json.Formatter
import wow.doge.mygame.game.GameSystemsInitializer
import wow.doge.mygame.subsystems.events.EventsModule2
import wow.doge.mygame.implicits._
import com.jme3.bullet.BulletAppState
import akka.util.Timeout
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import ammonite.runtime.tools.time
object Main extends BIOApp with MainModule {
import java.util.logging.{Logger => JLogger, Level}
JLogger.getLogger("").setLevel(Level.SEVERE)
implicit val timeout = Timeout(1.second)
def appResource =
for {
@ -29,23 +33,70 @@ object Main extends BIOApp with MainModule {
Formatter.json
).withAsync(timeWindow = 1.milliseconds)
jmeScheduler <- jMESchedulerResource
actorSystem <- actorSystemResource2(logger)
scriptCacheActor <- new ScriptSystemResource(os.pwd, actorSystem)(
timeout,
actorSystem.scheduler
).make
// akkaScheduler = actorSystemResource2.scheduler
// consoleTextArea <- Resource.liftF(Task(new TextArea()))
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
(gameApp, gameAppFib) <- {
// new BulletAppState()
// bas.setThreadingType(Thr)
// gameAppResource(new StatsAppState())
wire[GameAppResource].make
wire[GameAppResource].get
}
app = gameApp
inputManager = gameApp.inputManager
assetManager = gameApp.assetManager
bulletAppState = new BulletAppState()
(playerMovementEventBus, playerCameraEventBus) <- new EventsModule2(
actorSystem
).resource
// b1 = playerMovementEventBus
// b2 = playerCameraEventBus
// playerPos = ImVector3f.ZERO
// playerNode = None.taggedWith[Player]
// modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o".taggedWith[Player]
// playerController <- Resource.liftF {
// implicit val s = actorSystem.scheduler
// wire[PlayerController.Props].create.onErrorHandle(err =>
// logger.error(err.toString())
// )
// }
// _ <- Resource.liftF(IO(JMERunner.runner = gameApp))
// _ <- Resource.liftF(IO {
// new ActorSystemModule {}
// })
actorSystem <- wireWith(actorSystemResource _)
_ <- Resource.liftF(
gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler))
)
// actorSystem <- wireWith(actorSystemResource _)
// rootActor <- rootActorResource(logger, gameApp, schedulers, as2)
// _ <- Resource.liftF(
// gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler))
// )
// gameSystemsInitializer = new GameSystemsInitializer(
// actorSystem,
// logger,
// playerMovementEventBus,
// playerCameraEventBus
// )(gameApp)
gameSystemsInitializerFib <- Resource.make(
logger.info("creating game systems initializer") >>
gameApp
.enqueueL(() => wire[GameSystemsInitializer])
.start
)(c => logger.info("destroying game systems initializer") >> c.cancel)
_ <- Resource.liftF(gameSystemsInitializerFib.join.flatMap(_.init))
// .runAsync {
// case Left(err) => println(err)
// case _ =>
// }(schedulers.async)
// _ <- Resource.liftF {
// Task {
// implicit val sched = actorSystem.scheduler
@ -73,6 +124,18 @@ object Main extends BIOApp with MainModule {
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
} yield (gameAppFib)
// def createPlayerController(
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ],
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
// ): IO[PlayerController.Error, Unit] = {
// val playerPos = ImVector3f.ZERO
// val playerNode = None.taggedWith[Player]
// val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
// wire[PlayerController.Props].create
// }
def run(args: List[String]): UIO[ExitCode] = {
// Console.withOut(

4
src/main/scala/wow/doge/mygame/game/GameApp.scala

@ -13,9 +13,11 @@ import monix.reactive.MulticastStrategy
import monix.reactive.Observable
import monix.execution.atomic.Atomic
import scala.collection.immutable.Queue
import wow.doge.mygame.executors.Schedulers
class GameApp(
// actorSystem: ActorSystem[SpawnProtocol.Command],
schedulers: Schedulers,
appStates: AppState*
) extends SimpleApplication(appStates: _*) {
import GameApp._
@ -27,7 +29,7 @@ class GameApp(
private val tickSubject =
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
monix.execution.Scheduler.Implicits.global
schedulers.async
)
// (scheduler)

24
src/main/scala/wow/doge/mygame/game/GameAppActor.scala

@ -39,21 +39,21 @@ object GameAppActor {
Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from GameAppActor")
{
implicit val s = schedulers.async
// {
// implicit val s = schedulers.async
val initializer: GameSystemsInitializer = wire[GameSystemsInitializer]
// val initializer: GameSystemsInitializer = wire[GameSystemsInitializer]
schedulers.async.execute(() => initializer.init.runAsyncAndForget)
// schedulers.async.execute(() => initializer.init.runAsyncAndForget)
// ctx.pipeToSelf(application.timed.runToFuture) {
// case Failure(exception) =>
// ApplicationStartFailed(exception.getMessage())
// case Success(value) =>
// println("here applications started")
// ApplicationStarted
// }
}
// // ctx.pipeToSelf(application.timed.runToFuture) {
// // case Failure(exception) =>
// // ApplicationStartFailed(exception.getMessage())
// // case Success(value) =>
// // println("here applications started")
// // ApplicationStarted
// // }
// }
Behaviors.receiveMessage { msg =>
msg match {

18
src/main/scala/wow/doge/mygame/game/GameModule.scala

@ -11,14 +11,17 @@ 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
class GameAppResource(logger: Logger[Task], jmeScheduler: Scheduler) {
def make: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
import wow.doge.mygame.executors.Schedulers
class GameAppResource(
logger: Logger[Task],
jmeScheduler: Scheduler,
schedulers: Schedulers
) {
def get: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
Resource.make(
for {
_ <- logger.info("Creating game app")
app <- Task(new GameApp(new StatsAppState()))
app <- Task(new GameApp(schedulers, new StatsAppState()))
_ <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)
@ -38,12 +41,13 @@ trait GameModule {
def gameAppResource(
logger: Logger[Task],
jmeScheduler: Scheduler
jmeScheduler: Scheduler,
schedulers: Schedulers
): Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
Resource.make(
(for {
_ <- logger.info("Creating game app")
app <- Task(new GameApp())
app <- Task(new GameApp(schedulers))
_ <- Task {
val settings = new AppSettings(true)
settings.setVSync(true)

94
src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala

@ -3,44 +3,54 @@ package wow.doge.mygame.game
import scala.concurrent.duration._
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.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.IOUtils
import wow.doge.mygame.math.ImVector3f
import monix.bio.IO
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import akka.actor.typed.ActorSystem
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
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)
class GameSystemsInitializer(
spawnProtocol: ActorSystem[SpawnProtocol.Command],
loggerL: Logger[Task],
// eventBuses: EventsModule2
playerMovementEventBus: ActorRef[
EventBus.Command[EntityMovementEvent.PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
)(app: GameApp) {
implicit val timeout: Timeout = Timeout(1.second)
import GameSystemsInitializer._
def init =
implicit val akkaScheduler: Scheduler = spawnProtocol.scheduler
lazy val inputManager = app.inputManager
lazy val assetManager = app.assetManager
lazy val bulletAppState = new BulletAppState()
// lazy val playerMovementEventBus = eventBuses.playerMovementEventBusTask
val init =
for {
playerMovementEventBus <- playerMovementEventBusTask
inputManager = app.inputManager
bulletAppState = new BulletAppState()
_ <- loggerL.info("Initializing Systems")
// playerMovementEventBus <- playerMovementEventBusTask
_ <- Task(app.stateManager.attach(bulletAppState))
_ <- Task(
app.assetManager.registerLocator(
@ -50,58 +60,32 @@ class GameSystemsInitializer()(
)
)
_ <- 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)
// )
_ <- wireWith(createPlayerController _).startAndForget
_ <- wire[GameInputHandler.Props].begin
} yield ()
def createPlayerController(
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ],
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
): IO[PlayerController.Error, Unit] = {
val playerPos = ImVector3f.ZERO
val playerNode = None.taggedWith[Player]
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
wire[PlayerController.Props].create
}
}
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))
IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick)
)
// .mapTask()
}

2
src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala

@ -62,7 +62,7 @@ class PlayerMovementState(
override def update(tpf: Float) = {
// movementActor ! MovementActor.Tick(tpf, geom, cam)
imMovementActor ! ImMovementActor.Tick(tpf)
// imMovementActor ! ImMovementActor.Tick(tpf)
// movementActorTimer ! MovementActorTimer.Update(tpf)
}

201
src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala

@ -0,0 +1,201 @@
package wow.doge.mygame.game.nodes
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.ActorContext
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.game.GameApp
import akka.actor.typed.ActorRef
import wow.doge.mygame.events.EventBus
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.EntityMovementEvent
import akka.actor.typed.scaladsl.TimerScheduler
import akka.actor.typed.Behavior
import scala.concurrent.duration._
import akka.actor.typed.LogOptions
import org.slf4j.event.Level
import com.typesafe.scalalogging.Logger
import akka.actor.typed.SupervisorStrategy
import com.jme3.scene.CameraNode
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.game.subsystems.movement.CanMove
import wow.doge.mygame.implicits._
object PlayerActorSupervisor {
sealed trait Command
final case class Props(
app: GameApp,
camNode: CameraNode,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
) {
def create[T: CanMove](movable: T) =
Behaviors.logMessages(
LogOptions()
.withLevel(Level.TRACE)
.withLogger(
Logger[PlayerActorSupervisor[T]].underlying
),
Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from PlayerActor")
// spawn children actors
lazy val movementActor =
ctx.spawn(
Behaviors
.supervise(
ImMovementActor
.Props(app, movable, playerMovementEventBus)
.create
)
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActor"
)
lazy val playerMovementEventHandler = ctx.spawn(
Behaviors
.supervise(PlayerMovementEventListener(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementEventHandler"
)
lazy val movementActorTimer = ctx.spawn(
Behaviors
.supervise(MovementActorTimer(movementActor))
.onFailure[Exception](SupervisorStrategy.restart),
"playerMovementActorTimer"
)
lazy val playerCameraHandler = {
ctx.spawn(
Behaviors
.supervise(
PlayerCameraEventListener(camNode, app.enqueueR)
)
.onFailure[Exception](SupervisorStrategy.restart),
"playerCameraHandler"
)
}
//init actors
movementActorTimer ! MovementActorTimer.Start
playerMovementEventBus ! EventBus.Subscribe(
playerMovementEventHandler
)
playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler)
new PlayerActorSupervisor(
ctx,
this,
Children(movementActor, playerMovementEventHandler)
).receive
}
)
}
case class Children(
movementActor: ActorRef[ImMovementActor.Command],
playerMovementEventHandler: ActorRef[
EntityMovementEvent.PlayerMovementEvent
]
)
}
class PlayerActorSupervisor[T: CanMove](
ctx: ActorContext[PlayerActorSupervisor.Command],
props: PlayerActorSupervisor.Props,
children: PlayerActorSupervisor.Children
) {
import PlayerActorSupervisor._
def receive =
Behaviors.receiveMessage[Command] {
case _ =>
// children.movementActor ! ImMovementActor.MovedDown(true)
Behaviors.same
}
}
object MovementActorTimer {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case object TimerKey
def apply(target: ActorRef[ImMovementActor.Command]) =
Behaviors.withTimers[Command] { timers =>
new MovementActorTimer(timers, target).idle
}
}
class MovementActorTimer(
timers: TimerScheduler[MovementActorTimer.Command],
target: ActorRef[ImMovementActor.Command]
) {
import MovementActorTimer._
val idle: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, (60f / 144).millis)
active
case _ => Behaviors.unhandled
}
}
val active: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case Send =>
target ! ImMovementActor.Tick
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
idle
case _ => Behaviors.unhandled
}
}
}
object GenericTimerActor {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
private case object Send extends Command
case object TimerKey
case class Props[T](
target: ActorRef[T],
messageToSend: T,
timeInterval: FiniteDuration
) {
val create = Behaviors.withTimers[Command] { timers =>
new GenericTimerActor(timers, this).idle
}
}
}
class GenericTimerActor[T](
timers: TimerScheduler[GenericTimerActor.Command],
props: GenericTimerActor.Props[T]
) {
import GenericTimerActor._
val idle: Behavior[Command] =
Behaviors.receiveMessage {
case Start =>
timers.startTimerWithFixedDelay(TimerKey, Send, props.timeInterval)
active
case _ => Behaviors.unhandled
}
val active: Behavior[Command] =
Behaviors.receiveMessagePartial {
case Send =>
props.target ! props.messageToSend
Behaviors.same
case Stop =>
timers.cancel(TimerKey)
idle
}
}

30
src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala

@ -0,0 +1,30 @@
package wow.doge.mygame.game.nodes
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.ActorContext
object PlayerCameraActor {
sealed trait Command
class Props() {
def create =
Behaviors.setup[Command] { ctx =>
new PlayerCameraActor(ctx, this).receive(State.empty)
}
}
case class State()
object State {
val empty = State()
}
}
class PlayerCameraActor(
ctx: ActorContext[PlayerCameraActor.Command],
props: PlayerCameraActor.Props
) {
import PlayerCameraActor._
def receive(state: State) =
Behaviors.receiveMessage[Command] {
case _ => Behaviors.same
}
}

212
src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala

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

42
src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala

@ -5,17 +5,23 @@ 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 wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import com.typesafe.scalalogging.Logger
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedUp
import wow.doge.mygame.subsystems.events.PlayerCameraEvent.CameraMovedDown
import com.jme3.scene.CameraNode
import wow.doge.mygame.game.GameApp
import wow.doge.mygame.implicits._
object PlayerMovementEventHandler {
object PlayerMovementEventListener {
import PlayerMovementEvent._
def apply(movementActor: ActorRef[ImMovementActor.Command]) =
Behaviors.logMessages(
LogOptions()
.withLevel(Level.TRACE)
.withLogger(
Logger[PlayerMovementEventHandler.type].underlying
Logger[PlayerMovementEventListener.type].underlying
),
Behaviors.setup[PlayerMovementEvent](ctx =>
Behaviors.receiveMessage {
@ -52,3 +58,33 @@ object PlayerMovementEventHandler {
)
)
}
object PlayerCameraEventListener {
def apply(
camNode: CameraNode,
enqueueR: Function1[() => Unit, Unit]
) =
Behaviors.logMessages(
LogOptions()
.withLevel(Level.TRACE)
.withLogger(
Logger[PlayerCameraEventListener.type].underlying
),
Behaviors.setup[PlayerCameraEvent](ctx =>
Behaviors.receiveMessage {
case CameraMovedUp =>
enqueueR(() => {
camNode.move(0, 1, 0)
})
Behaviors.same
case CameraMovedDown =>
enqueueR(() => {
camNode.move(0, -1, 0)
})
Behaviors.same
}
)
)
}

102
src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala

@ -8,10 +8,11 @@ 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 wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import scala.concurrent.duration._
import com.jme3.input.controls.MouseAxisTrigger
import com.jme3.input.MouseInput
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
// class GameInputHandler(
// inputManager: InputManager
@ -24,7 +25,8 @@ object GameInputHandler {
inputManager: InputManager,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
]
],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
) {
def begin =
for {
@ -44,7 +46,12 @@ object GameInputHandler {
_ <- toIO(
generateCameraEvents(
inputManager,
playerMovementEventBus
playerCameraEventBus
).completedL.startAndForget
)
_ <- toIO(
myTest(
inputManager
).completedL.startAndForget
)
} yield ()
@ -53,22 +60,22 @@ object GameInputHandler {
def setupKeys(inputManager: InputManager) =
inputManager
.withMapping(
"Left",
PlayerMovementInput.WalkLeft.entryName,
new KeyTrigger(KeyInput.KEY_A)
// new KeyTrigger(KeyInput.KEY_LEFT)
)
.withMapping(
"Right",
PlayerMovementInput.WalkRight.entryName,
new KeyTrigger(KeyInput.KEY_D)
// new KeyTrigger(KeyInput.KEY_RIGHT)
)
.withMapping(
"Up",
PlayerMovementInput.WalkForward.entryName,
new KeyTrigger(KeyInput.KEY_W)
// new KeyTrigger(KeyInput.KEY_UP)
)
.withMapping(
"Down",
PlayerMovementInput.WalkBackward.entryName,
new KeyTrigger(KeyInput.KEY_S)
// new KeyTrigger(KeyInput.KEY_DOWN)
)
@ -77,12 +84,12 @@ object GameInputHandler {
new KeyTrigger(KeyInput.KEY_SPACE)
)
.withMapping(
"ROTATE_RIGHT",
PlayerAnalogInput.TurnRight.entryName,
new KeyTrigger(KeyInput.KEY_RIGHT),
new MouseAxisTrigger(MouseInput.AXIS_X, true)
)
.withMapping(
"ROTATE_LEFT",
PlayerAnalogInput.TurnLeft.entryName,
new KeyTrigger(KeyInput.KEY_LEFT),
new MouseAxisTrigger(MouseInput.AXIS_X, false)
)
@ -104,62 +111,67 @@ object GameInputHandler {
EventBus.Command[PlayerMovementEvent]
]
) = {
val name = "movementInputEventsGenerator"
val name = "playerMovementInputEventsGenerator"
inputManager
.observableAction(
"Left",
"Right",
"Up",
"Down",
"Jump",
"ROTATE_RIGHT",
"ROTATE_LEFT"
)
.enumObservableAction(PlayerMovementInput)
// .dump("O")
.doOnNext { action =>
action.binding.name match {
case "Left" =>
action.binding match {
case PlayerMovementInput.WalkLeft =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerMovedLeft(pressed = action.value),
name
)
)
case "Right" =>
case PlayerMovementInput.WalkRight =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerMovedRight(pressed = action.value),
name
)
)
case "Up" =>
case PlayerMovementInput.WalkForward =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerMovedForward(pressed = action.value),
name
)
)
case "Down" =>
case PlayerMovementInput.WalkBackward =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerMovedBackward(pressed = action.value),
name
)
)
// case "Jump" if action.value =>
// toTask(
// playerMovementEventBus !! EventBus.Publish(
// PlayerMovementEvent.PlayerJumped,
// name
// )
// )
case "Jump" if action.value =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerJumped,
name
)
)
case _ => monix.eval.Task.unit
// case _ => monix.eval.Task.unit
}
}
}
def myTest(inputManager: InputManager) = {
inputManager
.enumObservableAction(PlayerMovementEnum)
.sample(1.millis)
.mapEval(action =>
action.binding match {
case PlayerMovementEnum.MOVE_RIGHT =>
monix.eval.Task(println("move right")) >> monix.eval.Task.unit
case PlayerMovementEnum.MOVE_LEFT =>
monix.eval.Task(println("move left"))
}
)
}
def generateRotateEvents(
inputManager: InputManager,
playerMovementEventBus: ActorRef[
@ -168,34 +180,32 @@ object GameInputHandler {
) = {
val name = "rotateMovementEventsGenerator"
inputManager
.analogObservable("ROTATE_RIGHT", "ROTATE_LEFT")
.enumAnalogObservable(PlayerAnalogInput)
.sample(1.millis)
.mapEval(analogEvent =>
analogEvent.binding.name match {
case "ROTATE_RIGHT" =>
.map(e => e)
.doOnNext(analogEvent =>
analogEvent.binding match {
case PlayerAnalogInput.TurnRight =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerRotatedRight,
name
)
)
case "ROTATE_LEFT" =>
case PlayerAnalogInput.TurnLeft =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerRotatedLeft,
name
)
)
case _ => monix.eval.Task.unit
}
)
}
def generateCameraEvents(
inputManager: InputManager,
playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent]
]
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
) = {
val name = "cameraMovementEventsGenerator"
inputManager
@ -205,15 +215,15 @@ object GameInputHandler {
analogEvent.binding.name match {
case "CAMERA_UP" =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerCameraUp,
playerCameraEventBus !! EventBus.Publish(
PlayerCameraEvent.CameraMovedUp,
name
)
)
case "CAMERA_DOWN" =>
toTask(
playerMovementEventBus !! EventBus.Publish(
PlayerMovementEvent.PlayerCameraDown,
playerCameraEventBus !! EventBus.Publish(
PlayerCameraEvent.CameraMovedDown,
name
)
)

19
src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala

@ -0,0 +1,19 @@
package wow.doge.mygame.game.subsystems.input
import enumeratum._
import enumeratum.EnumEntry._
sealed trait PlayerMovementInput extends EnumEntry with UpperSnakecase
object PlayerMovementInput extends Enum[PlayerMovementInput] {
val values = findValues
case object WalkForward extends PlayerMovementInput
case object WalkRight extends PlayerMovementInput
case object WalkLeft extends PlayerMovementInput
case object WalkBackward extends PlayerMovementInput
}
sealed trait PlayerAnalogInput extends EnumEntry with UpperSnakecase
object PlayerAnalogInput extends Enum[PlayerAnalogInput] {
val values = findValues
case object TurnRight extends PlayerAnalogInput
case object TurnLeft extends PlayerAnalogInput
}

70
src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala

@ -0,0 +1,70 @@
package wow.doge.mygame.game.subsystems.movement
import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath
import com.jme3.math.Quaternion
import com.jme3.math.Vector3f
import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.movement.RotateDir
import wow.doge.mygame.implicits._
import com.jme3.scene.Spatial
import com.typesafe.scalalogging.LazyLogging
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 CanMove {
implicit val implCanMoveForBetterCharacterControl =
new CanMove[BetterCharacterControl] {
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)
}
val tmp = new Vector3f()
inst.getViewDirection(tmp)
inst.setViewDirection(q.mult(tmp))
}
override def stop(inst: BetterCharacterControl) =
inst.setWalkDirection(Vector3f.ZERO)
}
implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging {
override def move(inst: Spatial, direction: ImVector3f): Unit = {
inst.move(direction.mutable)
}
override def jump(inst: Spatial): Unit =
logger.warn("`Jump` is not implemented for type `Spatial`")
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {
rotateDir match {
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
}
}
override def stop(inst: Spatial) = { /*not required*/ }
}
}

69
src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala

@ -3,9 +3,7 @@ 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
@ -13,10 +11,9 @@ 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
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import wow.doge.mygame.game.subsystems.movement.CanMove
import com.softwaremill.quicklens._
sealed trait RotateDir
object RotateDir {
@ -24,18 +21,10 @@ object 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
final case object Tick extends Command
sealed trait Movement extends Command
final case class MovedLeft(pressed: Boolean) extends Movement
@ -101,51 +90,36 @@ class ImMovementActor[T](
case m: Movement =>
m match {
case MovedLeft(pressed) =>
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
case MovedUp(pressed) =>
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
case MovedRight(pressed) =>
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
case MovedDown(pressed) =>
props.app.enqueueF(stopIfNotPressed(pressed, props.movable))
props.app.enqueueR(() => stopIfNotPressed(pressed, props.movable))
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
case Jump =>
props.app.enqueueF(cm.jump(props.movable))
props.app.enqueueR(() => cm.jump(props.movable))
Behaviors.same
case RotateLeft =>
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left))
props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Left))
Behaviors.same
case RotateRight =>
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right))
props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Right))
Behaviors.same
}
case Tick(tpf) =>
case Tick =>
val walkDir =
getDirection(state.cardinalDir, ctx.log.trace)
if (walkDir != ImVector3f.ZERO) {
val tmp = walkDir * 25f * tpf
props.app.enqueueF {
val tmp = walkDir * 25f * (1f / 144)
props.app.enqueueR { () =>
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
}
@ -182,3 +156,18 @@ object Methods {
) =
if (!pressed) cm.stop(movable)
}
// 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)

43
src/main/scala/wow/doge/mygame/implicits/TestEnum.scala

@ -0,0 +1,43 @@
package wow.doge.mygame.implicits
import enumeratum._
sealed trait TestEnum extends EnumEntry
object TestEnum extends Enum[TestEnum] {
val values = findValues
case object Test2 extends TestEnum
}
sealed trait Greeting extends EnumEntry
object Greeting extends Enum[Greeting] {
/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/
val values = findValues
case object Hello extends Greeting
case object GoodBye extends Greeting
case object Hi extends Greeting
case object Bye extends Greeting
}
object ObsTest {}
sealed trait PlayerMovementEnum extends EnumEntry {
def test: String
}
object PlayerMovementEnum extends Enum[PlayerMovementEnum] {
val values = findValues
case object MOVE_RIGHT extends PlayerMovementEnum {
val test = "hmm"
}
case object MOVE_LEFT extends PlayerMovementEnum {
val test = "mmh"
}
}

271
src/main/scala/wow/doge/mygame/implicits/package.scala

@ -19,11 +19,7 @@ import com.jme3.input.controls.InputListener
import com.jme3.math.Vector3f
import wow.doge.mygame.math.ImVector3f
import com.jme3.scene.Geometry
import wow.doge.mygame.state.CardinalDirection
import wow.doge.mygame.subsystems.movement.CanMove
import com.jme3.renderer.Camera
import scala.jdk.CollectionConverters._
import wow.doge.mygame.utils.JFXConsoleStreamable
import com.jme3.app.Application
import com.jme3.scene.SceneGraphVisitor
import monix.reactive.Observable
@ -46,20 +42,25 @@ 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
import enumeratum._
case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
case class EnumActionEvent[T <: EnumEntry](
binding: T,
value: Boolean,
tpf: Float
)
case class AnalogEvent(binding: Action, value: Float, tpf: Float)
case class EnumAnalogEvent[T <: EnumEntry](
binding: T,
value: Float,
tpf: Float
)
case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
package object implicits {
@ -69,41 +70,11 @@ package object implicits {
implicit class JMEAppExt(private val app: Application) extends AnyVal {
// /**
// * Blocking task. Execute on a thread pool meant for blocking operations.
// * Prefer [[wow.doge.mygame.implicits.JMEAppExt#enqueueT]] instead.
// *
// * @param cb
// * @param ec
// * @return
// */
// def enqueueF[T](cb: () => T)(implicit ec: ExecutionContext): Future[T] =
// Future {
// app
// .enqueue(new Callable[T]() {
// override def call(): T = cb()
// })
// .get()
// }
// /**
// * Blocking task. Execute on a thread pool meant for blocking operations.
// * Same as enqueue, but returns a Monix Task instead of Future
// * @param cb
// * @param ec
// * @return
// */
// def enqueueL[T](cb: () => T): Task[T] =
// Task
// .deferFutureAction(implicit s => enqueueF(cb))
def enqueueF[T](cb: => T) =
def enqueueR(cb: () => Unit) =
app.enqueue(new Runnable {
override def run() = cb
override def run() = cb()
})
def enqueueT(cb: => Unit) =
Task(enqueueF(cb))
}
implicit class StateManagerExt(private val sm: AppStateManager)
extends AnyVal {
@ -190,38 +161,35 @@ package object implicits {
def observableDepthFirst(): Observable[Spatial] = {
def loop(
subscriber: Subscriber[Spatial],
spatial: Spatial
): Task[Unit] = {
//spatial can be either a node or a geometry, but it's not a sealed trait
spatial match {
case node: Node =>
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Ack.Continue => {
//modifying a node's children list is forbidden
spatials: LazyList[Spatial]
): Task[Unit] =
spatials match {
// spatial can be either a node or a geometry, but it's not a sealed trait
case head #:: tail =>
head match {
case g: Geometry =>
Task.deferFuture(subscriber.onNext(g)).flatMap {
case Continue =>
loop(subscriber, tail)
case Stop =>
Task(subscriber.onComplete())
}
case node: Node =>
val children = node.children
if (!children.isEmpty) {
Task.sequence(
children.map(c => loop(subscriber, c))
) >> Task.unit
} else {
Task.unit
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Continue =>
loop(subscriber, children #::: tail)
case Stop =>
Task(subscriber.onComplete())
}
}
case Ack.Stop => Task.unit
// case _ => loop(subscriber, tail)
}
//geomtries do not/cannot have children
case g: Geometry =>
Task.deferFuture(subscriber.onNext(g)) >> Task.unit
case _ => Task.unit
case LazyList() => Task.unit
}
}
Observable.create(OverflowStrategy.Unbounded) { sub =>
implicit val sched = sub.scheduler
loop(sub, n).runToFuture
loop(sub, LazyList(n)).runToFuture
}
}
@ -243,16 +211,24 @@ package object implicits {
Task.deferFuture(subscriber.onNext(g)).flatMap {
case Continue =>
loop(subscriber, tail)
case Stop => Task.unit
case Stop =>
Task(subscriber.onComplete())
}
case node: Node =>
val children = node.children
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Continue =>
loop(subscriber, tail #::: children)
case Stop => Task.unit
case Stop =>
Task(subscriber.onComplete())
}
case unknown =>
Task.deferFuture(subscriber.onNext(unknown)).flatMap {
case Continue =>
loop(subscriber, tail)
case Stop =>
Task(subscriber.onComplete())
}
// case _ => loop(subscriber, tail)
}
case LazyList() => Task.unit
}
@ -369,8 +345,10 @@ package object implicits {
): Unit = {
if (
sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
)
) {
sub.onComplete()
c.cancel()
}
}
}
@ -380,9 +358,45 @@ package object implicits {
c
}
}
def enumObservableAction[T <: EnumEntry](
mappingEnum: Enum[T]
): Observable[EnumActionEvent[T]] = {
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
val c = SingleAssignCancelable()
val entryNames = mappingEnum.values.map(_.entryName)
val al = new ActionListener {
override def onAction(
binding: String,
value: Boolean,
tpf: Float
): Unit = {
mappingEnum.withNameOption(binding).foreach { b =>
if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
sub.onComplete()
c.cancel()
}
}
}
}
inputManager.addListener(al, entryNames: _*)
c := Cancelable(() => inputManager.removeListener(al))
c
}
}
// def enumObservableAction[T <: enumeratum.EnumEntry](
// mappingNames: Enum[T]
// ): Observable[ActionEvent] = {
// observableAction2(mappingNames).doOnNext()
// }
def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
Observable.create(OverflowStrategy.DropOld(100)) { sub =>
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable()
val al = new AnalogListener {
override def onAnalog(
@ -392,8 +406,10 @@ package object implicits {
): Unit = {
if (
sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
)
) {
sub.onComplete()
c.cancel()
}
}
}
@ -403,6 +419,35 @@ package object implicits {
c
}
}
def enumAnalogObservable[T <: EnumEntry](
mappingEnum: Enum[T]
): Observable[EnumAnalogEvent[T]] = {
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable()
val entryNames = mappingEnum.values.map(_.entryName)
val al = new AnalogListener {
override def onAnalog(
binding: String,
value: Float,
tpf: Float
): Unit = {
mappingEnum.withNameOption(binding).foreach { b =>
if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
sub.onComplete()
c.cancel()
}
}
}
}
inputManager.addListener(al, entryNames: _*)
c := Cancelable(() => inputManager.removeListener(al))
c
}
}
}
implicit class PhysicsSpaceExt(private val space: PhysicsSpace)
@ -415,8 +460,10 @@ package object implicits {
val cl = new PhysicsCollisionListener {
override def collision(event: PhysicsCollisionEvent): Unit = {
if (sub.onNext(event) == Ack.Stop)
if (sub.onNext(event) == Ack.Stop) {
sub.onComplete()
c.cancel()
}
}
}
@ -436,14 +483,18 @@ package object implicits {
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Left(event)) == Ack.Stop)
if (sub.onNext(Left(event)) == Ack.Stop) {
sub.onComplete()
c.cancel()
}
}
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Right(event)) == Ack.Stop)
if (sub.onNext(Right(event)) == Ack.Stop) {
sub.onComplete()
c.cancel()
}
}
}
@ -515,72 +566,6 @@ package object implicits {
def mutable = new Vector3f(v.x, v.y, v.z)
}
implicit val implCanMoveForBetterCharacterControl =
new CanMove[BetterCharacterControl] {
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)
}
val tmp = new Vector3f()
inst.getViewDirection(tmp)
inst.setViewDirection(q.mult(tmp))
}
override def stop(inst: BetterCharacterControl) =
inst.setWalkDirection(Vector3f.ZERO)
}
implicit val implCanMoveForGeom = new CanMove[Spatial] with LazyLogging {
override def move(inst: Spatial, direction: ImVector3f): Unit = {
inst.move(direction.mutable)
}
override def jump(inst: Spatial): Unit =
logger.warn("`Jump` is not implemented for type `Spatial`")
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {
rotateDir match {
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
}
}
override def stop(inst: Spatial) = {}
}
implicit val implJFXConsoleStreamForTextArea =
new JFXConsoleStreamable[scalafx.scene.control.TextArea] {
override def println(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text + "\n")
override def print(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text)
}
// val TasktoUIO = new FunctionK[Task, UIO] {
// def apply[T](f: Task[T]): UIO[T] =
// f.hideErrors

2
src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala

@ -11,7 +11,7 @@ import akka.event.EventStream
* Copied (and repurposed) from Akka's EventStream
*/
object EventBus {
sealed trait Command[-A]
sealed trait Command[A]
final case class Publish[A, E <: A](
event: E,
publisherName: String

49
src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala

@ -11,7 +11,7 @@ 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 wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import akka.actor.typed.SupervisorStrategy
trait EventsModule {
@ -20,48 +20,11 @@ trait EventsModule {
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]](
@ -80,3 +43,13 @@ trait EventsModule {
object EventTypes {
type EventBus[T] = ActorRef[EventBus.Command[T]]
}
// val subscribingActor =
// spawnProtocol.askT(
// SpawnProtocol.Spawn[Events.PhysicsTick.type](
// SubscribingActor(),
// "subscriber-1",
// Props.empty,
// _
// )
// )

123
src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala

@ -1,59 +1,64 @@
// 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)
// }
// }
// }
// }
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 scala.concurrent.duration._
import akka.util.Timeout
import akka.actor.typed.SupervisorStrategy
import cats.effect.Resource
import akka.actor.typed.ActorSystem
import monix.bio.Task
import org.slf4j.event.Level
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
class EventsModule2(
spawnProtocol: ActorSystem[SpawnProtocol.Command]
) {
private implicit lazy val s = spawnProtocol.scheduler
private implicit lazy val timeout = Timeout(1.second)
private lazy val eventBusLogger = SLLogger[EventBus[_]]
private lazy val playerMovementEventBusTask =
createEventBus[PlayerMovementEvent]("movementEventBus")
private lazy val playerCameraEventBusTask =
createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) =
spawnProtocol.askL(
SpawnProtocol.Spawn[EventBus.Command[T]](
Behaviors.logMessages(
logOptions = LogOptions()
.withLevel(logLevel)
.withLogger(eventBusLogger.underlying),
Behaviors
.supervise(EventBus[T]())
.onFailure[Exception](SupervisorStrategy.restart)
),
busName,
Props.empty,
_
)
)
type EventBuses = (
ActorRef[
EventBus.Command[EntityMovementEvent.PlayerMovementEvent],
],
ActorRef[EventBus.Command[PlayerCameraEvent]]
)
val resource: Resource[Task, EventBuses] =
Resource.liftF(for {
playerMovementEventBus <- playerMovementEventBusTask
playerCameraEventBus <- playerCameraEventBusTask
} yield (playerMovementEventBus, playerCameraEventBus))
}

16
src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala

@ -1,20 +1,20 @@
package wow.doge.mygame.subsystems.events
import wow.doge.mygame.subsystems.movement.CanMove
import wow.doge.mygame.game.subsystems.movement.CanMove
sealed trait MovementEvent
sealed trait EntityMovementEvent
object MovementEvent {
object EntityMovementEvent {
final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent
extends EntityMovementEvent
final case class MovedUp[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent
extends EntityMovementEvent
final case class MovedRight[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent
extends EntityMovementEvent
final case class MovedDown[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent
extends EntityMovementEvent
sealed trait PlayerMovementEvent extends MovementEvent
sealed trait PlayerMovementEvent extends EntityMovementEvent
object PlayerMovementEvent {
final case class PlayerMovedLeft(pressed: Boolean)
extends PlayerMovementEvent

8
src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala

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

23
src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala

@ -0,0 +1,23 @@
package wow.doge.mygame.subsystems.scriptsystem
import wow.doge.mygame.utils.AkkaUtils
import cats.effect.Resource
import wow.doge.mygame.scriptsystem.ScriptCachingActor
import akka.actor.typed.ActorRef
import akka.actor.typed.SpawnProtocol
import akka.util.Timeout
import akka.actor.typed.Scheduler
class ScriptSystemResource(
path: os.Path,
spawnProtocol: ActorRef[SpawnProtocol.Command]
)(implicit timeout: Timeout, scheduler: Scheduler) {
def make =
Resource.liftF(
AkkaUtils.spawnActorL(
spawnProtocol,
"scriptCachingActor",
ScriptCachingActor()
)
)
}

19
src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala

@ -4,7 +4,6 @@ import java.io.PrintStream
import java.io.OutputStream
import java.io.ByteArrayOutputStream
import scalafx.scene.control.TextArea
import wow.doge.mygame.implicits._
import cats.effect.Resource
import monix.bio.Task
@ -41,6 +40,23 @@ object JFXConsoleStream {
lazy val default = Config()
}
implicit val implJFXConsoleStreamForTextArea =
new JFXConsoleStreamable[scalafx.scene.control.TextArea] {
override def println(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text + "\n")
override def print(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text)
}
def textAreaStream(ta: TextArea) =
Resource.make(
Task(
@ -50,4 +66,5 @@ object JFXConsoleStream {
)
)
)(s => Task(s.close()))
}
Loading…
Cancel
Save