|
|
package wow.doge.mygame.game
import cats.effect.Resource import cats.effect.concurrent.Deferred import com.jme3.app.state.AppStateManager import com.jme3.asset.AssetManager import com.jme3.input.InputManager import com.jme3.scene.Node import com.jme3.scene.Spatial import com.jme3.system.AppSettings import com.softwaremill.tagging._ import com.typesafe.scalalogging.{Logger => SLogger} import io.odin.Logger import monix.bio.IO import monix.bio.Task import monix.catnap.ConcurrentChannel import monix.catnap.ConsumerF import monix.catnap.Semaphore import monix.eval.Coeval import monix.execution.CancelablePromise import monix.execution.Scheduler import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.game.subsystems.ui.JFxUI import wow.doge.mygame.implicits._ import monix.execution.annotations.UnsafeBecauseImpure import monix.reactive.Observable
sealed trait Error case object FlyCamNotExists extends Error
object GameAppTags { sealed trait RootNode sealed trait GuiNode }
class GameApp(logger: Logger[Task], val app: SimpleAppExt) { import Ops._
def stateManager: Task[AppStateManager] = Task(app.getStateManager()) def inputManager: Task[InputManager] = Task(app.getInputManager()) def assetManager: Task[AssetManager] = Task(app.getAssetManager()) def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode]) def addToGuiNode = guiNode.flatMap(rn => Task(new AddToNode(rn))) def flyCam = IO(app.getFlyByCamera()).onErrorHandleWith(_ => IO.raiseError(FlyCamNotExists) ) def camera = Task(app.getCamera()) def viewPort = Task(app.getViewPort()) def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode]) def rootNode2 = WrappedNode(app.getRootNode()).taggedWith[GameAppTags.RootNode] // def rootNode2 = SynchedObject(app.getRootNode())
def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn))) def enqueue(cb: () => Unit) = app.enqueue(new Runnable { override def run() = cb() }) def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
def start = Task(app.start()) def stop = Task(app.stop()) def scheduler = app.scheduler def jfxUI = JFxUI(app)
}
class WrappedNode private (node: Node) {
// def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
def children: Observable[Spatial] = node.observableChildren def attachChild(n: Node): Task[Unit] = Task(node.attachChild(node)) def add(wn: WrappedNode): Task[Unit] = Task(node.attachChild(wn.unsafeDelegate))
/**
* Get the underlying wrapped value */ @UnsafeBecauseImpure def unsafeDelegate = node } object WrappedNode {
def apply(name: String) = new WrappedNode(new Node(name)) def apply(n: Node) = new WrappedNode(n) implicit class WrappedNodeOps(private val wn: WrappedNode) extends AnyVal { def +=(n: Node) = wn.attachChild(n) def +=(wn: WrappedNode) = wn.add(wn) } }
object GameApp {
def resource( logger: Logger[Task], jmeScheduler: Scheduler, schedulers: Schedulers ) = Resource.make( for { startSignal <- Task(CancelablePromise[Unit]()) app <- Task(new SimpleAppExt(schedulers, startSignal)) _ <- Task { 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) } gameApp <- Task(new GameApp(logger, app)) fib <- gameApp.start.executeOn(jmeScheduler).start _ <- Task.fromCancelablePromise(startSignal) } yield gameApp -> fib )(_._2.cancel)
/**
* Synchronization wrapper for a mutable object * * @param obj the mutable object * @param lock lock for synchronization */ class SynchedObject[A](obj: A, lock: Semaphore[Task]) { def modify(f: A => Unit): Task[Unit] = lock.withPermit(Task(f(obj)))
def flatModify(f: A => Task[Unit]): Task[Unit] = lock.withPermit(f(obj))
def get: Task[A] = lock.withPermit(Task(obj)) }
object SynchedObject { def apply[A](obj: A) = Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock))) }
}
object Ops { final class AddToNode[T <: Node](private val node: T) extends AnyVal {
/**
* Pure version */ def apply(spatial: Spatial)(implicit logger: Logger[Task]) = logger.debug( s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}" ) >> Task(node.attachChild(spatial))
/**
* Impure version */ def apply(spatial: Spatial)(implicit logger: SLogger) = Coeval { logger.debug( s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}" ) node.attachChild(spatial) } } }
object SpawnSystem { sealed trait Result final case object Ok extends Result
sealed trait Complete final case object Complete extends Complete
sealed trait SpawnRequest final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest
final case class SpawnRequestWrapper( spawnRequest: SpawnRequest, result: Deferred[Task, Result] )
def apply(logger: Logger[Task]) = for { spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper] spawnSystem <- Task(new SpawnSystem(logger, spawnChannel)) consumer <- spawnChannel.consume .use(consumer => spawnSystem.receive(consumer)) .startAndForget } yield (spawnSystem) }
class SpawnSystem( logger: Logger[Task], spawnChannel: ConcurrentChannel[ Task, SpawnSystem.Complete, SpawnSystem.SpawnRequestWrapper ] ) { import SpawnSystem._
for { spawnSystem <- SpawnSystem(logger) res <- spawnSystem.request(SpawnSpatial(Task(new Node("Test")))) } yield ()
// val spawnChannel = ConcurrentChannel[Task].of[Result, SpawnRequest]
private def receive( consumer: ConsumerF[Task, Complete, SpawnRequestWrapper] ): Task[Unit] = consumer.pull.flatMap { case Right(message) => for { _ <- logger .debug(s"Received spawn request $message") _ <- handleSpawn(message) } yield receive(consumer) case Left(r) => logger.info("Closing Spawn System") }
private def handleSpawn(spawnRequestWrapper: SpawnRequestWrapper) = spawnRequestWrapper match { case SpawnRequestWrapper(spawnRequest, result) => spawnRequest match { case SpawnSpatial(spatialTask) => spatialTask.flatMap(spatial => logger.debug( s"Spawning spatial with name ${spatial.getName()}" ) >> result .complete(Ok) )
} }
def request(spawnRequest: SpawnRequest) = for { d <- Deferred[Task, Result] _ <- spawnChannel.push(SpawnRequestWrapper(spawnRequest, d)) res <- d.get } yield (res)
def stop = spawnChannel.halt(Complete) }
|