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.

226 lines
6.3 KiB

4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
  1. package wow.doge.mygame.game
  2. import cats.effect.Resource
  3. import cats.effect.concurrent.Deferred
  4. import com.jme3.app.state.AppStateManager
  5. import com.jme3.asset.AssetManager
  6. import com.jme3.bullet.BulletAppState
  7. import com.jme3.input.InputManager
  8. import com.jme3.scene.Node
  9. import com.jme3.scene.Spatial
  10. import com.jme3.system.AppSettings
  11. import com.softwaremill.tagging._
  12. import com.typesafe.scalalogging.{Logger => SLogger}
  13. import io.odin.Logger
  14. import monix.bio.IO
  15. import monix.bio.Task
  16. import monix.catnap.ConcurrentChannel
  17. import monix.catnap.ConsumerF
  18. import monix.catnap.Semaphore
  19. import monix.eval.Coeval
  20. import monix.execution.Scheduler
  21. import wow.doge.mygame.executors.Schedulers
  22. import wow.doge.mygame.game.subsystems.ui.JFxUI
  23. import wow.doge.mygame.implicits._
  24. import wow.doge.mygame.utils.wrappers.jme._
  25. sealed trait Error
  26. case object FlyCamNotExists extends Error
  27. object GameAppTags {
  28. sealed trait RootNode
  29. sealed trait GuiNode
  30. }
  31. class GameApp private (logger: Logger[Task], app: SimpleAppExt) {
  32. def stateManager: Task[AppStateManager] = Task(app.getStateManager())
  33. def inputManager: Task[InputManager] = Task(app.getInputManager())
  34. def assetManager: Task[AssetManager] = Task(app.getAssetManager())
  35. def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
  36. def guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
  37. def flyCam =
  38. IO(app.getFlyByCamera()).onErrorHandleWith(_ =>
  39. IO.raiseError(FlyCamNotExists)
  40. )
  41. def camera = Task(app.getCamera())
  42. def viewPort = Task(app.getViewPort())
  43. // def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
  44. val rootNode =
  45. AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode]
  46. val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace)
  47. def enqueue(cb: () => Unit) =
  48. app.enqueue(new Runnable {
  49. override def run() = cb()
  50. })
  51. def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
  52. def scheduler = app.scheduler
  53. def jfxUI = JFxUI(app)
  54. }
  55. object GameApp {
  56. def resource(
  57. logger: Logger[Task],
  58. jmeThread: Scheduler,
  59. schedulers: Schedulers
  60. ) =
  61. Resource.make {
  62. lazy val bullet = new BulletAppState
  63. for {
  64. // bullet <- Task(new BulletAppState)
  65. // startSignal <- Task(CancelablePromise[Unit]())
  66. app <- Task(new SimpleAppExt(schedulers, bullet))
  67. _ <- Task {
  68. val settings = new AppSettings(true)
  69. settings.setVSync(true)
  70. /**
  71. * disables the launcher
  72. * We'll be making our own launcher anyway
  73. */
  74. app.setShowSettings(false)
  75. app.setSettings(settings)
  76. }
  77. fib <- Task(app.start).executeOn(jmeThread).start
  78. _ <- Task.deferFuture(app.started)
  79. // _ <- Task.fromCancelablePromise(startSignal)
  80. _ <- Task(pprint.log(bullet.toString()))
  81. _ <- Task(println(bullet.physicsSpace.toString()))
  82. gameApp <- Task(new GameApp(logger, app))
  83. } yield gameApp -> fib
  84. }(_._2.cancel)
  85. /**
  86. * Synchronization wrapper for a mutable object
  87. *
  88. * @param obj the mutable object
  89. * @param lock lock for synchronization
  90. */
  91. class SynchedObject[A](obj: A, lock: Semaphore[Task]) {
  92. def modify(f: A => Unit): Task[Unit] =
  93. lock.withPermit(Task(f(obj)))
  94. def flatModify(f: A => Task[Unit]): Task[Unit] =
  95. lock.withPermit(f(obj))
  96. def get: Task[A] = lock.withPermit(Task(obj))
  97. }
  98. object SynchedObject {
  99. def apply[A](obj: A) =
  100. Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock)))
  101. }
  102. }
  103. object Ops {
  104. final class AddToNode[T <: Node](private val node: T) extends AnyVal {
  105. /**
  106. * Pure version
  107. */
  108. def apply(spatial: Spatial)(implicit logger: Logger[Task]) =
  109. logger.debug(
  110. s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}"
  111. ) >> Task(node.attachChild(spatial))
  112. /**
  113. * Impure version
  114. */
  115. def apply(spatial: Spatial)(implicit logger: SLogger) =
  116. Coeval {
  117. logger.debug(
  118. s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}"
  119. )
  120. node.attachChild(spatial)
  121. }
  122. }
  123. }
  124. object SpawnSystem {
  125. sealed trait Result
  126. final case object Ok extends Result
  127. sealed trait Complete
  128. final case object Complete extends Complete
  129. sealed trait SpawnRequest
  130. final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest
  131. final case class SpawnRequestWrapper(
  132. spawnRequest: SpawnRequest,
  133. result: Deferred[Task, Result]
  134. )
  135. def apply(logger: Logger[Task]) =
  136. for {
  137. spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper]
  138. spawnSystem <- Task(new SpawnSystem(logger, spawnChannel))
  139. consumer <-
  140. spawnChannel.consume
  141. .use(consumer => spawnSystem.receive(consumer))
  142. .startAndForget
  143. } yield (spawnSystem)
  144. }
  145. class SpawnSystem(
  146. logger: Logger[Task],
  147. spawnChannel: ConcurrentChannel[
  148. Task,
  149. SpawnSystem.Complete,
  150. SpawnSystem.SpawnRequestWrapper
  151. ]
  152. ) {
  153. import SpawnSystem._
  154. for {
  155. spawnSystem <- SpawnSystem(logger)
  156. res <- spawnSystem.request(SpawnSpatial(Task(new Node("Test"))))
  157. } yield ()
  158. // val spawnChannel = ConcurrentChannel[Task].of[Result, SpawnRequest]
  159. private def receive(
  160. consumer: ConsumerF[Task, Complete, SpawnRequestWrapper]
  161. ): Task[Unit] =
  162. consumer.pull.flatMap {
  163. case Right(message) =>
  164. for {
  165. _ <-
  166. logger
  167. .debug(s"Received spawn request $message")
  168. _ <- handleSpawn(message)
  169. } yield receive(consumer)
  170. case Left(r) =>
  171. logger.info("Closing Spawn System")
  172. }
  173. private def handleSpawn(spawnRequestWrapper: SpawnRequestWrapper) =
  174. spawnRequestWrapper match {
  175. case SpawnRequestWrapper(spawnRequest, result) =>
  176. spawnRequest match {
  177. case SpawnSpatial(spatialTask) =>
  178. spatialTask.flatMap(spatial =>
  179. logger.debug(
  180. s"Spawning spatial with name ${spatial.getName()}"
  181. ) >> result
  182. .complete(Ok)
  183. )
  184. }
  185. }
  186. def request(spawnRequest: SpawnRequest) =
  187. for {
  188. d <- Deferred[Task, Result]
  189. _ <- spawnChannel.push(SpawnRequestWrapper(spawnRequest, d))
  190. res <- d.get
  191. } yield (res)
  192. def stop = spawnChannel.halt(Complete)
  193. }