Testing out JmonkeyEngine to make a game in Scala with Akka Actors within a pure FP layer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

130 lines
4.1 KiB

package wow.doge.mygame.game
import scala.concurrent.duration._
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.Props
import akka.actor.typed.SpawnProtocol
import akka.actor.typed.scaladsl.AskPattern._
import akka.util.Timeout
import cats.effect.Resource
import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager
import com.jme3.system.AppSettings
import com.softwaremill.tagging._
import io.odin.Logger
import monix.bio.Fiber
import monix.bio.IO
import monix.bio.Task
import monix.bio.UIO
import wow.doge.mygame.AppError
import wow.doge.mygame.AppError.TimeoutError
import wow.doge.mygame.Dispatchers
import wow.doge.mygame.executors.JMERunner
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.subsystems.ui.JFxUI
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.types._
import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.wrappers.jme._
object GameAppTags {
sealed trait RootNode
sealed trait GuiNode
}
class GameApp private[game] (
logger: Logger[Task],
app: SimpleAppExt,
gameActor: ActorRef[GameAppActor.Command],
gameSpawnProtocol: ActorRef[SpawnProtocol.Command],
akkaScheduler: AkkaScheduler
) {
def inputManager: UIO[InputManager] = UIO(app.getInputManager())
val assetManager = new AssetManager(app.getAssetManager())
val guiNode: GuiNode =
AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
// def flyCam = Option(app.getFlyByCamera())
def camera = UIO(app.getCamera())
def viewPort = UIO(app.getViewPort())
val rootNode: RootNode =
AppNode2(app.getRootNode()).taggedWith[GameAppTags.RootNode]
val physicsSpace =
new PhysicsSpace(app.bulletAppState.physicsSpace)
def enqueue(cb: () => Unit) = app.enqueueR(() => cb())
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
def whenTerminated: IO[AppError, Unit] =
IO.deferFuture(app.whenTerminated).onErrorHandleWith(TimeoutError.from)
def spawnGameActor[T](
behavior: Behavior[T],
actorName: Option[String] = None,
props: Props = Dispatchers.jmeDispatcher
)(implicit name: sourcecode.Name) =
AkkaUtils.spawnActorL(behavior, actorName, props)(
2.seconds,
akkaScheduler.value,
gameSpawnProtocol,
name
)
def scheduler = JmeScheduler(app.scheduler)
def jfxUI = JFxUI(app)
}
class GameAppResource(
logger: Logger[Task],
jmeThread: JmeScheduler,
schedulers: Schedulers,
tickEventBus: GameEventBus[TickEvent]
)(implicit
timeout: Timeout,
scheduler: AkkaScheduler,
spawnProtocol: ActorRef[SpawnProtocol.Command]
) {
implicit val as = scheduler.value
def resource
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
Resource.make(
(for {
app <- UIO(new SimpleAppExt(schedulers, new BulletAppState))
_ <- UIO(JMERunner.runner = app)
_ <- UIO {
val settings = new AppSettings(true)
settings.setVSync(true)
/**
* disables the launcher
* We'll be making our own launcher anyway
*/
app.setShowSettings(false)
app.setSettings(settings)
}
fib <- UIO(app.start).executeOn(jmeThread.value).start
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
gameAppActor <- AkkaUtils.spawnActorL(
new GameAppActor.Props(tickEventBus).behavior,
Some("testGameActor"),
props = Dispatchers.jmeDispatcher
)
_ <- gameAppActor !! GameAppActor.Start
sp <-
gameAppActor
.askL(GameAppActor.GetSpawnProtocol)
.onErrorHandleWith(TimeoutError.from)
gameApp <- UIO(new GameApp(logger, app, gameAppActor, sp, scheduler))
_ <- UIO {
val fut = () => gameAppActor.ask(GameAppActor.Stop).flatten
app.cancelToken = Some(fut)
}
} yield (gameApp, fib)).attempt
) {
case Right(gameApp -> fib) => fib.cancel
case Left(error) => IO.terminate(new Exception(error.toString))
}
}