dunno lol

This commit is contained in:
Rohan Sircar 2020-11-21 20:04:25 +05:30
parent 85a28b3c39
commit a2a328d078
24 changed files with 1052 additions and 472 deletions

View File

@ -89,7 +89,9 @@ lazy val root = (project in file(".")).settings(
"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" "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 // Determine OS version of JavaFX binaries

View File

@ -1,6 +1,5 @@
package wow.doge.mygame package wow.doge.mygame
import monix.bio.Task
import cats.effect.Resource import cats.effect.Resource
import io.odin.syntax._ import io.odin.syntax._
@ -10,15 +9,20 @@ import com.softwaremill.macwire._
import scala.concurrent.duration._ import scala.concurrent.duration._
import monix.bio.BIOApp import monix.bio.BIOApp
import monix.bio.UIO import monix.bio.UIO
import monix.bio.IO
import io.odin._ import io.odin._
import wow.doge.mygame.implicits._
import wow.doge.mygame.game.GameAppResource import wow.doge.mygame.game.GameAppResource
import io.odin.json.Formatter 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 { 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)
implicit val timeout = Timeout(1.second)
def appResource = def appResource =
for { for {
@ -29,23 +33,70 @@ object Main extends BIOApp with MainModule {
Formatter.json Formatter.json
).withAsync(timeWindow = 1.milliseconds) ).withAsync(timeWindow = 1.milliseconds)
jmeScheduler <- jMESchedulerResource jmeScheduler <- jMESchedulerResource
actorSystem <- actorSystemResource2(logger)
scriptCacheActor <- new ScriptSystemResource(os.pwd, actorSystem)(
timeout,
actorSystem.scheduler
).make
// akkaScheduler = actorSystemResource2.scheduler
// consoleTextArea <- Resource.liftF(Task(new TextArea())) // consoleTextArea <- Resource.liftF(Task(new TextArea()))
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _) // consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
(gameApp, gameAppFib) <- { (gameApp, gameAppFib) <- {
// new BulletAppState() // new BulletAppState()
// bas.setThreadingType(Thr) // bas.setThreadingType(Thr)
// gameAppResource(new StatsAppState()) // 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(JMERunner.runner = gameApp))
// _ <- Resource.liftF(IO { // _ <- Resource.liftF(IO {
// new ActorSystemModule {} // new ActorSystemModule {}
// }) // })
actorSystem <- wireWith(actorSystemResource _) // actorSystem <- wireWith(actorSystemResource _)
_ <- Resource.liftF(
gameApp.enqueueT(actorSystem ! RootActor.Start(actorSystem.scheduler)) // 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 { // _ <- Resource.liftF {
// Task { // Task {
// implicit val sched = actorSystem.scheduler // implicit val sched = actorSystem.scheduler
@ -73,6 +124,18 @@ object Main extends BIOApp with MainModule {
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop))) // (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
} yield (gameAppFib) } 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] = { def run(args: List[String]): UIO[ExitCode] = {
// Console.withOut( // Console.withOut(

View File

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

View File

@ -39,21 +39,21 @@ object GameAppActor {
Behaviors.setup[Command] { ctx => Behaviors.setup[Command] { ctx =>
ctx.log.info("Hello from GameAppActor") 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) { // // ctx.pipeToSelf(application.timed.runToFuture) {
// case Failure(exception) => // // case Failure(exception) =>
// ApplicationStartFailed(exception.getMessage()) // // ApplicationStartFailed(exception.getMessage())
// case Success(value) => // // case Success(value) =>
// println("here applications started") // // println("here applications started")
// ApplicationStarted // // ApplicationStarted
// // }
// } // }
}
Behaviors.receiveMessage { msg => Behaviors.receiveMessage { msg =>
msg match { msg match {

View File

@ -11,14 +11,17 @@ import monix.bio.IO
import monix.bio.Fiber import monix.bio.Fiber
import monix.execution.Scheduler import monix.execution.Scheduler
import com.jme3.app.StatsAppState import com.jme3.app.StatsAppState
import com.jme3.app.FlyCamAppState import wow.doge.mygame.executors.Schedulers
// import wow.doge.mygame.executors.JMERunner class GameAppResource(
class GameAppResource(logger: Logger[Task], jmeScheduler: Scheduler) { logger: Logger[Task],
def make: Resource[Task, (GameApp, Fiber[Throwable, Unit])] = jmeScheduler: Scheduler,
schedulers: Schedulers
) {
def get: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
Resource.make( Resource.make(
for { for {
_ <- logger.info("Creating game app") _ <- logger.info("Creating game app")
app <- Task(new GameApp(new StatsAppState())) app <- Task(new GameApp(schedulers, new StatsAppState()))
_ <- Task { _ <- Task {
val settings = new AppSettings(true) val settings = new AppSettings(true)
settings.setVSync(true) settings.setVSync(true)
@ -38,12 +41,13 @@ trait GameModule {
def gameAppResource( def gameAppResource(
logger: Logger[Task], logger: Logger[Task],
jmeScheduler: Scheduler jmeScheduler: Scheduler,
schedulers: Schedulers
): Resource[Task, (GameApp, Fiber[Throwable, Unit])] = ): Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
Resource.make( Resource.make(
(for { (for {
_ <- logger.info("Creating game app") _ <- logger.info("Creating game app")
app <- Task(new GameApp()) app <- Task(new GameApp(schedulers))
_ <- Task { _ <- Task {
val settings = new AppSettings(true) val settings = new AppSettings(true)
settings.setVSync(true) settings.setVSync(true)

View File

@ -3,44 +3,54 @@ package wow.doge.mygame.game
import scala.concurrent.duration._ import scala.concurrent.duration._
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Props
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import com.jme3.asset.plugins.ZipLocator import com.jme3.asset.plugins.ZipLocator
import com.jme3.bullet.BulletAppState import com.jme3.bullet.BulletAppState
import com.jme3.bullet.control.BetterCharacterControl
import com.softwaremill.macwire._ import com.softwaremill.macwire._
import com.softwaremill.tagging._ import com.softwaremill.tagging._
import io.odin.Logger import io.odin.Logger
import monix.bio.Task import monix.bio.Task
import monix.reactive.Consumer import monix.reactive.Consumer
import wow.doge.mygame.events.EventBus 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.Player
import wow.doge.mygame.game.nodes.PlayerController import wow.doge.mygame.game.nodes.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import wow.doge.mygame.implicits._ 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.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.IOUtils 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()( class GameSystemsInitializer(
override val spawnProtocol: ActorRef[SpawnProtocol.Command], spawnProtocol: ActorSystem[SpawnProtocol.Command],
override implicit val akkaScheduler: Scheduler, loggerL: Logger[Task],
app: GameApp, // eventBuses: EventsModule2
loggerL: Logger[Task] playerMovementEventBus: ActorRef[
) extends EventsModule { EventBus.Command[EntityMovementEvent.PlayerMovementEvent]
override implicit val timeout: Timeout = Timeout(1.second) ],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
)(app: GameApp) {
implicit val timeout: Timeout = Timeout(1.second)
import GameSystemsInitializer._ 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 { for {
playerMovementEventBus <- playerMovementEventBusTask _ <- loggerL.info("Initializing Systems")
inputManager = app.inputManager // playerMovementEventBus <- playerMovementEventBusTask
bulletAppState = new BulletAppState()
_ <- Task(app.stateManager.attach(bulletAppState)) _ <- Task(app.stateManager.attach(bulletAppState))
_ <- Task( _ <- Task(
app.assetManager.registerLocator( app.assetManager.registerLocator(
@ -50,58 +60,32 @@ class GameSystemsInitializer()(
) )
) )
_ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState)) _ <- app.enqueueL(() => DefaultGameLevel(app, bulletAppState))
playerController <- app.enqueueL(() => _ <- wireWith(createPlayerController _).startAndForget
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 _ <- wire[GameInputHandler.Props].begin
} yield () } 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 { 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( def playerMovementActorTickConsumer(
playerMovementActor: ActorRef[ImMovementActor.Command] playerMovementActor: ActorRef[ImMovementActor.Command]
) = ) =
Consumer Consumer
.foreachTask[Float](tpf => .foreachTask[Float](tpf =>
IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick(tpf)) IOUtils.toTask(playerMovementActor !! ImMovementActor.Tick)
) )
// .mapTask() // .mapTask()
} }

View File

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

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -6,9 +6,7 @@ import com.jme3.scene.Geometry
import com.jme3.renderer.Camera import com.jme3.renderer.Camera
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.scene.control.CameraControl.ControlDirection import com.jme3.scene.control.CameraControl.ControlDirection
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.control.BetterCharacterControl
import com.jme3.bullet.BulletAppState import com.jme3.bullet.BulletAppState
@ -16,20 +14,90 @@ import wow.doge.mygame.game.GameApp
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import com.jme3.math.FastMath 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) {} // class PlayerNode(val name: String) extends Node(name) {}
trait Player sealed trait Player
sealed trait PlayerCameraNode
object PlayerController { object PlayerController {
def defaultMesh = { sealed trait Error
lazy val b = new Box(1, 1, 1) case class GenericError(reason: String) extends Error
lazy val geom = new Geometry("playerMesh", b)
geom class Props(
} app: GameApp,
def defaultTexture(assetManager: AssetManager) = loggerL: Logger[Task],
MyMaterial( assetManager: AssetManager,
assetManager = assetManager, bulletAppState: BulletAppState,
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" 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 apply( def apply(
app: GameApp, app: GameApp,
@ -41,10 +109,6 @@ object PlayerController {
.withJumpForce(ImVector3f(0, 5f, 0)) .withJumpForce(ImVector3f(0, 5f, 0))
lazy val playerNode = new Node("PlayerNode") lazy val playerNode = new Node("PlayerNode")
.withChildren( .withChildren(
new CameraNode("CameraNode", cam)
.withControlDir(ControlDirection.SpatialToCamera)
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
.withLookAt(playerPos, ImVector3f.UNIT_Y),
assetManager assetManager
.loadModel(modelPath) .loadModel(modelPath)
.asInstanceOf[Node] .asInstanceOf[Node]
@ -64,41 +128,93 @@ object PlayerController {
playerPhysicsControl 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") object Defaults {
lazy val camNode = new CameraNode("CameraNode", cam) lazy val defaultMesh = {
val b = Box(1, 1, 1)
{ val geom = Geometry("playerMesh", b)
val mat = MyMaterial( geom
}
def defaultTexture(assetManager: AssetManager) =
MyMaterial(
assetManager = assetManager, assetManager = assetManager,
path = texturePath path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
) )
mesh.setMaterial(mat)
}
// lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera()) def defaultCamerNode(cam: Camera, playerPos: ImVector3f) =
// camNode.setCamera(simpleApp.getCamera()) new CameraNode("CameraNode", cam)
discard { .withControlDir(ControlDirection.SpatialToCamera)
playerNode .withLocalTranslation(ImVector3f(0, 1.5f, 10))
.withChild(camNode) .withLookAt(playerPos, ImVector3f.UNIT_Y)
.withChild(mesh)
// playerNode.children(Seq(camNode, geom))
}
{ def defaultPlayerNode(
camNode.setControlDir(ControlDirection.SpatialToCamera) assetManager: AssetManager,
camNode.setLocalTranslation( modelPath: os.RelPath,
new Vector3f(0, 1.5f, 10) playerPos: ImVector3f,
camNode: CameraNode,
playerPhysicsControl: BetterCharacterControl
) =
Either.catchNonFatal(
Node("PlayerNode")
.withChildren(
camNode,
assetManager
.loadModel(modelPath)
.asInstanceOf[Node]
.withRotate(0, FastMath.PI, 0)
) )
camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y) .withLocalTranslation(playerPos)
.withControl(playerPhysicsControl)
)
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
// )

View File

@ -5,17 +5,23 @@ import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
import org.slf4j.event.Level import org.slf4j.event.Level
import akka.actor.typed.LogOptions 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 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._ import PlayerMovementEvent._
def apply(movementActor: ActorRef[ImMovementActor.Command]) = def apply(movementActor: ActorRef[ImMovementActor.Command]) =
Behaviors.logMessages( Behaviors.logMessages(
LogOptions() LogOptions()
.withLevel(Level.TRACE) .withLevel(Level.TRACE)
.withLogger( .withLogger(
Logger[PlayerMovementEventHandler.type].underlying Logger[PlayerMovementEventListener.type].underlying
), ),
Behaviors.setup[PlayerMovementEvent](ctx => Behaviors.setup[PlayerMovementEvent](ctx =>
Behaviors.receiveMessage { 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
}
)
)
}

View File

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

View File

@ -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
}

View File

@ -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*/ }
}
}

View File

@ -3,9 +3,7 @@ package wow.doge.mygame.subsystems.movement
import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import com.softwaremill.quicklens._
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import com.jme3.renderer.Camera
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.game.GameApp import wow.doge.mygame.game.GameApp
@ -13,10 +11,9 @@ import akka.actor.typed.ActorRef
import wow.doge.mygame.events.EventBus import wow.doge.mygame.events.EventBus
import com.jme3.math.Vector3f import com.jme3.math.Vector3f
import wow.doge.mygame.state.CardinalDirection import wow.doge.mygame.state.CardinalDirection
import wow.doge.mygame.subsystems.events.MovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
import akka.actor.typed.LogOptions import wow.doge.mygame.game.subsystems.movement.CanMove
import com.typesafe.scalalogging.Logger import com.softwaremill.quicklens._
import org.slf4j.event.Level
sealed trait RotateDir sealed trait RotateDir
object RotateDir { object RotateDir {
@ -24,18 +21,10 @@ object RotateDir {
case object Right 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 { object ImMovementActor {
sealed trait Command sealed trait Command
// final case class Tick(tpf: Float) extends 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 sealed trait Movement extends Command
final case class MovedLeft(pressed: Boolean) extends Movement final case class MovedLeft(pressed: Boolean) extends Movement
@ -101,51 +90,36 @@ class ImMovementActor[T](
case m: Movement => case m: Movement =>
m match { m match {
case MovedLeft(pressed) => 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)) receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
case MovedUp(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)) receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
case MovedRight(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)) receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
case MovedDown(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)) receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
case Jump => case Jump =>
props.app.enqueueF(cm.jump(props.movable)) props.app.enqueueR(() => cm.jump(props.movable))
Behaviors.same Behaviors.same
case RotateLeft => case RotateLeft =>
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Left)) props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Left))
Behaviors.same Behaviors.same
case RotateRight => case RotateRight =>
props.app.enqueueF(cm.rotate(props.movable, RotateDir.Right)) props.app.enqueueR(() => cm.rotate(props.movable, RotateDir.Right))
Behaviors.same Behaviors.same
} }
case Tick(tpf) => case Tick =>
val walkDir = val walkDir =
getDirection(state.cardinalDir, ctx.log.trace) getDirection(state.cardinalDir, ctx.log.trace)
if (walkDir != ImVector3f.ZERO) { if (walkDir != ImVector3f.ZERO) {
val tmp = walkDir * 25f * tpf val tmp = walkDir * 25f * (1f / 144)
props.app.enqueueF { props.app.enqueueR { () =>
cm.move(props.movable, tmp) 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 Behaviors.same
} }
@ -182,3 +156,18 @@ object Methods {
) = ) =
if (!pressed) cm.stop(movable) 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)

View File

@ -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"
}
}

View File

@ -19,11 +19,7 @@ import com.jme3.input.controls.InputListener
import com.jme3.math.Vector3f 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.subsystems.movement.CanMove
import com.jme3.renderer.Camera
import scala.jdk.CollectionConverters._ import scala.jdk.CollectionConverters._
import wow.doge.mygame.utils.JFXConsoleStreamable
import com.jme3.app.Application import com.jme3.app.Application
import com.jme3.scene.SceneGraphVisitor import com.jme3.scene.SceneGraphVisitor
import monix.reactive.Observable import monix.reactive.Observable
@ -46,20 +42,25 @@ import com.jme3.bullet.BulletAppState
import wow.doge.mygame.state.MyBaseState import wow.doge.mygame.state.MyBaseState
import monix.bio.UIO import monix.bio.UIO
import com.jme3.bullet.control.BetterCharacterControl import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.scene.control.AbstractControl
import com.jme3.scene.CameraNode import com.jme3.scene.CameraNode
import com.jme3.scene.control.CameraControl.ControlDirection import com.jme3.scene.control.CameraControl.ControlDirection
import com.jme3.bullet.control.AbstractPhysicsControl
import com.jme3.scene.control.Control 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.input.controls.AnalogListener
import com.jme3.math.Quaternion import enumeratum._
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 EnumActionEvent[T <: EnumEntry](
binding: T,
value: Boolean,
tpf: Float
)
case class AnalogEvent(binding: Action, value: Float, 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) case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
package object implicits { package object implicits {
@ -69,41 +70,11 @@ package object implicits {
implicit class JMEAppExt(private val app: Application) extends AnyVal { implicit class JMEAppExt(private val app: Application) extends AnyVal {
// /** def enqueueR(cb: () => Unit) =
// * 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) =
app.enqueue(new Runnable { 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) implicit class StateManagerExt(private val sm: AppStateManager)
extends AnyVal { extends AnyVal {
@ -190,38 +161,35 @@ package object implicits {
def observableDepthFirst(): Observable[Spatial] = { def observableDepthFirst(): Observable[Spatial] = {
def loop( def loop(
subscriber: Subscriber[Spatial], subscriber: Subscriber[Spatial],
spatial: Spatial spatials: LazyList[Spatial]
): Task[Unit] = { ): Task[Unit] =
spatials match {
// spatial can be either a node or a geometry, but it's not a sealed trait // spatial can be either a node or a geometry, but it's not a sealed trait
spatial match { case head #:: tail =>
head match {
case node: Node =>
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Ack.Continue => {
//modifying a node's children list is forbidden
val children = node.children
if (!children.isEmpty) {
Task.sequence(
children.map(c => loop(subscriber, c))
) >> Task.unit
} else {
Task.unit
}
}
case Ack.Stop => Task.unit
}
//geomtries do not/cannot have children
case g: Geometry => case g: Geometry =>
Task.deferFuture(subscriber.onNext(g)) >> Task.unit Task.deferFuture(subscriber.onNext(g)).flatMap {
case _ => Task.unit case Continue =>
loop(subscriber, tail)
case Stop =>
Task(subscriber.onComplete())
} }
case node: Node =>
val children = node.children
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Continue =>
loop(subscriber, children #::: tail)
case Stop =>
Task(subscriber.onComplete())
}
// case _ => loop(subscriber, tail)
}
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, n).runToFuture loop(sub, LazyList(n)).runToFuture
} }
} }
@ -243,16 +211,24 @@ package object implicits {
Task.deferFuture(subscriber.onNext(g)).flatMap { Task.deferFuture(subscriber.onNext(g)).flatMap {
case Continue => case Continue =>
loop(subscriber, tail) loop(subscriber, tail)
case Stop => Task.unit case Stop =>
Task(subscriber.onComplete())
} }
case node: Node => case node: Node =>
val children = node.children 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)
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 case LazyList() => Task.unit
} }
@ -369,10 +345,12 @@ package object implicits {
): Unit = { ): Unit = {
if ( if (
sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
) ) {
sub.onComplete()
c.cancel() c.cancel()
} }
} }
}
inputManager.addListener(al, mappingNames: _*) inputManager.addListener(al, mappingNames: _*)
@ -380,9 +358,45 @@ package object implicits {
c 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] = { def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
Observable.create(OverflowStrategy.DropOld(100)) { sub => Observable.create(OverflowStrategy.DropOld(50)) { sub =>
val c = SingleAssignCancelable() val c = SingleAssignCancelable()
val al = new AnalogListener { val al = new AnalogListener {
override def onAnalog( override def onAnalog(
@ -392,10 +406,12 @@ package object implicits {
): Unit = { ): Unit = {
if ( if (
sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
) ) {
sub.onComplete()
c.cancel() c.cancel()
} }
} }
}
inputManager.addListener(al, mappingNames: _*) inputManager.addListener(al, mappingNames: _*)
@ -403,6 +419,35 @@ package object implicits {
c 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) implicit class PhysicsSpaceExt(private val space: PhysicsSpace)
@ -415,8 +460,10 @@ package object implicits {
val cl = new PhysicsCollisionListener { val cl = new PhysicsCollisionListener {
override def collision(event: PhysicsCollisionEvent): Unit = { override def collision(event: PhysicsCollisionEvent): Unit = {
if (sub.onNext(event) == Ack.Stop) if (sub.onNext(event) == Ack.Stop) {
sub.onComplete()
c.cancel() c.cancel()
}
} }
} }
@ -436,15 +483,19 @@ package object implicits {
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = { override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf) val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Left(event)) == Ack.Stop) if (sub.onNext(Left(event)) == Ack.Stop) {
sub.onComplete()
c.cancel() c.cancel()
} }
}
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = { override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf) val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Right(event)) == Ack.Stop) if (sub.onNext(Right(event)) == Ack.Stop) {
sub.onComplete()
c.cancel() c.cancel()
} }
}
} }
@ -515,72 +566,6 @@ 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 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] { // val TasktoUIO = new FunctionK[Task, UIO] {
// def apply[T](f: Task[T]): UIO[T] = // def apply[T](f: Task[T]): UIO[T] =
// f.hideErrors // f.hideErrors

View File

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

View File

@ -11,7 +11,7 @@ import akka.actor.typed.LogOptions
import com.typesafe.scalalogging.{Logger => SLLogger} import com.typesafe.scalalogging.{Logger => SLLogger}
import wow.doge.mygame.events.EventBus import wow.doge.mygame.events.EventBus
import akka.actor.typed.scaladsl.Behaviors 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 import akka.actor.typed.SupervisorStrategy
trait EventsModule { trait EventsModule {
@ -20,48 +20,11 @@ trait EventsModule {
implicit def timeout: Timeout implicit def timeout: Timeout
def eventBusLogger = SLLogger[EventBus[_]] 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") 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 = lazy val playerMovementEventBusTask =
createEventBus[PlayerMovementEvent]("movementEventBus") 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) = def createEventBus[T](busName: String) =
spawnProtocol.askL( spawnProtocol.askL(
SpawnProtocol.Spawn[EventBus.Command[T]]( SpawnProtocol.Spawn[EventBus.Command[T]](
@ -80,3 +43,13 @@ trait EventsModule {
object EventTypes { object EventTypes {
type EventBus[T] = ActorRef[EventBus.Command[T]] type EventBus[T] = ActorRef[EventBus.Command[T]]
} }
// val subscribingActor =
// spawnProtocol.askT(
// SpawnProtocol.Spawn[Events.PhysicsTick.type](
// SubscribingActor(),
// "subscriber-1",
// Props.empty,
// _
// )
// )

View File

@ -1,59 +1,64 @@
// package wow.doge.mygame.subsystems.events package wow.doge.mygame.subsystems.events
// import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
// import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
// import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
// import akka.actor.typed.Props import akka.actor.typed.Props
// import akka.actor.typed.LogOptions import akka.actor.typed.LogOptions
// import com.typesafe.scalalogging.{Logger => SLLogger} import com.typesafe.scalalogging.{Logger => SLLogger}
// import wow.doge.mygame.events.EventBus import wow.doge.mygame.events.EventBus
// import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
// import wow.doge.mygame.events.Events import scala.concurrent.duration._
// import cats.effect.Resource import akka.util.Timeout
// import monix.bio.Task import akka.actor.typed.SupervisorStrategy
// import akka.actor.typed.ActorSystem import cats.effect.Resource
// import scala.concurrent.duration._ import akka.actor.typed.ActorSystem
import monix.bio.Task
import org.slf4j.event.Level
import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent
// trait EventsModule2 { class EventsModule2(
// def eventBusesResource( spawnProtocol: ActorSystem[SpawnProtocol.Command]
// spawnProtocol: ActorSystem[SpawnProtocol.Command], ) {
// eventBusLogger: com.typesafe.scalalogging.Logger = SLLogger[EventBus[_]] private implicit lazy val s = spawnProtocol.scheduler
// ): 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) private implicit lazy val timeout = Timeout(1.second)
// Resource.liftF { private lazy val eventBusLogger = SLLogger[EventBus[_]]
// {
// lazy val tickEventBusTask = createEventBus[Events.Tick]("tickEventBus")
// lazy val playerMovementEventBusTask = private lazy val playerMovementEventBusTask =
// createEventBus[Events.Movement.PlayerMovement]("movementEventBus") createEventBus[PlayerMovementEvent]("movementEventBus")
// // val r = (tickEventBusTask, playerMovementEventBusTask) private lazy val playerCameraEventBusTask =
// // Task(r) createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
// for {
// tickEventBus <- tickEventBusTask
// playerMovementEventBus <- playerMovementEventBusTask
// } yield (tickEventBus, playerMovementEventBus)
// }
// }
// }
// } 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))
}

View File

@ -1,20 +1,20 @@
package wow.doge.mygame.subsystems.events 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) final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent extends EntityMovementEvent
final case class MovedUp[T: CanMove](pressed: Boolean, movable: T) final case class MovedUp[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent extends EntityMovementEvent
final case class MovedRight[T: CanMove](pressed: Boolean, movable: T) final case class MovedRight[T: CanMove](pressed: Boolean, movable: T)
extends MovementEvent extends EntityMovementEvent
final case class MovedDown[T: CanMove](pressed: Boolean, movable: T) 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 { object PlayerMovementEvent {
final case class PlayerMovedLeft(pressed: Boolean) final case class PlayerMovedLeft(pressed: Boolean)
extends PlayerMovementEvent extends PlayerMovementEvent

View File

@ -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
}

View File

@ -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()
)
)
}

View File

@ -4,7 +4,6 @@ import java.io.PrintStream
import java.io.OutputStream import java.io.OutputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import scalafx.scene.control.TextArea import scalafx.scene.control.TextArea
import wow.doge.mygame.implicits._
import cats.effect.Resource import cats.effect.Resource
import monix.bio.Task import monix.bio.Task
@ -41,6 +40,23 @@ object JFXConsoleStream {
lazy val default = Config() 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) = def textAreaStream(ta: TextArea) =
Resource.make( Resource.make(
Task( Task(
@ -50,4 +66,5 @@ object JFXConsoleStream {
) )
) )
)(s => Task(s.close())) )(s => Task(s.close()))
} }