forked from nova/jmonkey-test
Rohan Sircar
3 years ago
24 changed files with 1053 additions and 473 deletions
-
4build.sbt
-
81src/main/scala/wow/doge/mygame/Main.scala
-
4src/main/scala/wow/doge/mygame/game/GameApp.scala
-
22src/main/scala/wow/doge/mygame/game/GameAppActor.scala
-
18src/main/scala/wow/doge/mygame/game/GameModule.scala
-
94src/main/scala/wow/doge/mygame/game/GameSystemsInitializer.scala
-
2src/main/scala/wow/doge/mygame/game/appstates/PlayerMovementState.scala
-
201src/main/scala/wow/doge/mygame/game/nodes/PlayerActorSupervisor.scala
-
30src/main/scala/wow/doge/mygame/game/nodes/PlayerCameraActor.scala
-
208src/main/scala/wow/doge/mygame/game/nodes/PlayerController.scala
-
42src/main/scala/wow/doge/mygame/game/nodes/PlayerEventListeners.scala
-
102src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala
-
19src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala
-
70src/main/scala/wow/doge/mygame/game/subsystems/movement/CanMove.scala
-
69src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
-
43src/main/scala/wow/doge/mygame/implicits/TestEnum.scala
-
269src/main/scala/wow/doge/mygame/implicits/package.scala
-
2src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala
-
49src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala
-
123src/main/scala/wow/doge/mygame/subsystems/events/EventsModule2.scala
-
16src/main/scala/wow/doge/mygame/subsystems/events/MovementEvents.scala
-
8src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala
-
23src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala
-
19src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.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 |
|||
} |
|||
} |
@ -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 |
|||
} |
|||
} |
@ -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 |
|||
} |
@ -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*/ } |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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)) |
|||
} |
@ -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 |
|||
} |
@ -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() |
|||
) |
|||
) |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue