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.

220 lines
7.7 KiB

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
  1. package wow.doge.mygame
  2. import akka.actor.typed.ActorRef
  3. import akka.actor.typed.ActorSystem
  4. import akka.actor.typed.Scheduler
  5. import akka.actor.typed.SpawnProtocol
  6. import akka.util.Timeout
  7. import cats.effect.concurrent.Ref
  8. import com.jme3.app.state.AppStateManager
  9. import com.jme3.asset.AssetManager
  10. import com.jme3.asset.plugins.ZipLocator
  11. import com.jme3.bullet.BulletAppState
  12. import com.jme3.input.InputManager
  13. import com.jme3.renderer.Camera
  14. import com.jme3.scene.Node
  15. import com.softwaremill.macwire._
  16. import com.softwaremill.tagging._
  17. import io.odin.Logger
  18. import monix.bio.IO
  19. import monix.bio.Task
  20. import wow.doge.mygame.events.EventBus
  21. import wow.doge.mygame.game.GameApp2
  22. import wow.doge.mygame.game.nodes.PlayerTag
  23. import wow.doge.mygame.game.nodes.PlayerController
  24. import wow.doge.mygame.game.subsystems.input.GameInputHandler
  25. import wow.doge.mygame.implicits._
  26. import wow.doge.mygame.math.ImVector3f
  27. import wow.doge.mygame.subsystems.events.EntityMovementEvent
  28. import wow.doge.mygame.subsystems.events.EventsModule2
  29. import wow.doge.mygame.subsystems.events.PlayerCameraEvent
  30. import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
  31. import com.jme3.renderer.ViewPort
  32. import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
  33. import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
  34. import wow.doge.mygame.launcher.Launcher
  35. import wow.doge.mygame.executors.Schedulers
  36. import scalafx.application.JFXApp.PrimaryStage
  37. import scalafx.geometry.Insets
  38. import scalafx.scene.Scene
  39. import scalafx.scene.control.Button
  40. import scalafx.scene.layout.StackPane
  41. import scalafx.scene.paint.Color
  42. import scalafx.scene.shape.Rectangle
  43. import scalafx.Includes._
  44. import scala.concurrent.duration._
  45. import cats.effect.concurrent.Deferred
  46. import monix.bio.Fiber
  47. import wow.doge.mygame.launcher.Launcher.LauncherResult
  48. import scalafx.scene.control.TextArea
  49. import com.jayfella.jme.jfx.JavaFxUI
  50. import wow.doge.mygame.utils.GenericConsoleStream
  51. import java.io.PrintStream
  52. class MainApp(
  53. logger: Logger[Task],
  54. gameApp: GameApp2,
  55. spawnProtocol: ActorSystem[SpawnProtocol.Command],
  56. jmeThread: monix.execution.Scheduler,
  57. schedulers: Schedulers,
  58. consoleStream: GenericConsoleStream[TextArea]
  59. )(implicit
  60. @annotation.unused timeout: Timeout,
  61. @annotation.unused scheduler: Scheduler
  62. ) {
  63. lazy val scriptSystemInit =
  64. new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init
  65. def gameInit: Task[Fiber[Throwable, Unit]] =
  66. for {
  67. eventsModule <- Task(new EventsModule2(spawnProtocol))
  68. playerMovementEventBus <- eventsModule.playerMovementEventBusTask
  69. playerCameraEventBus <- eventsModule.playerCameraEventBusTask
  70. gameAppFib <- gameApp.start.executeOn(jmeThread).start
  71. /**
  72. * schedule a task to run on the JME thread and wait for it's completion
  73. * before proceeding forward, as a signal that JME thread has been
  74. * initialized, otherwise we'll get NPEs trying to access the fields
  75. * of the game app
  76. */
  77. res <- gameApp.enqueueL(() => Task("done")).flatten
  78. _ <- logger.info(s"Result = $res")
  79. inputManager <- gameApp.inputManager
  80. assetManager <- gameApp.assetManager
  81. stateManager <- gameApp.stateManager
  82. camera <- gameApp.camera
  83. rootNode <- gameApp.rootNode
  84. enqueueR <- Task(gameApp.enqueue _)
  85. viewPort <- gameApp.viewPort
  86. _ <- logger.info("before")
  87. // jfxUI <- Task(JavaFxUI.initialize(gameApp.app))
  88. // .executeOn(gameApp.scheduler) >> Task.sleep(500.millis) >> Task(
  89. // JavaFxUI.getInstance()
  90. // )
  91. // .start
  92. jfxUI <- gameApp.jfxUI
  93. consoleTextArea <- Task(new TextArea {
  94. text = "hello"
  95. editable = false
  96. wrapText = true
  97. // maxHeight = 150
  98. // maxWidth = 300
  99. })
  100. _ <- Task(consoleStream := consoleTextArea)
  101. _ <- Task(jfxUI += consoleTextArea)
  102. // consoleStream <- Task(
  103. // GenericConsoleStream.textAreaStream(consoleTextArea)
  104. // )
  105. _ <- logger.info("after")
  106. bulletAppState <- Task(new BulletAppState())
  107. _ <- Task(stateManager.attach(bulletAppState))
  108. _ <- logger.info("Initializing console stream")
  109. // _ <- Task(GenericConsoleStream.textAreaStream(consoleTextArea)).bracket(
  110. // consoleStream =>
  111. // Task { System.setOut(consoleStream) } >> wire[MainAppDelegate]
  112. // .init(gameApp.scheduler, consoleStream)
  113. // // consoleLogger
  114. // // Console.withOut(consoleStream)(
  115. // // wire[MainAppDelegate].init(gameApp.scheduler, consoleStream)
  116. // // )
  117. // )(stream => Task(stream.close()).onErrorHandle(_.printStackTrace()))
  118. // _ <- Task { System.setOut(new PrintStream(consoleStream, true)) }
  119. // _ <- Task {
  120. // Console.withOut(consoleStream)(println("hello"))
  121. // }
  122. _ <- wire[MainAppDelegate].init(gameApp.scheduler)
  123. } yield (gameAppFib)
  124. lazy val program = for {
  125. scriptSystem <- scriptSystemInit
  126. /**
  127. * Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI
  128. * Without this, we get a "Toolkit already initialized" excResult. The launch button
  129. * in the launcher completes the signal. The game init process which listens for this
  130. * signal can then continue
  131. */
  132. launchSignal <- Deferred[Task, Launcher.LauncherResult]
  133. launcher <- new Launcher.Props(schedulers, launchSignal).create
  134. cancelToken <- launcher.init()
  135. launchResult <- launchSignal.get
  136. _ <- cancelToken.cancel
  137. _ <-
  138. if (launchResult == LauncherResult.Exit)
  139. logger.info("Exiting")
  140. else gameInit.flatMap(_.join)
  141. // _ <- Task.sleep(2000.millis)
  142. // gameAppFib <- gameInit
  143. /**
  144. * Wait for game window to close
  145. */
  146. // _ <- gameAppFib.join
  147. } yield ()
  148. }
  149. /**
  150. * Class with all dependencies in one place for easy wiring
  151. */
  152. class MainAppDelegate(
  153. gameApp: GameApp2,
  154. spawnProtocol: ActorSystem[SpawnProtocol.Command],
  155. loggerL: Logger[Task],
  156. // eventBuses: EventsModule2
  157. playerMovementEventBus: ActorRef[
  158. EventBus.Command[EntityMovementEvent.PlayerMovementEvent]
  159. ],
  160. playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
  161. inputManager: InputManager,
  162. assetManager: AssetManager,
  163. stateManager: AppStateManager,
  164. camera: Camera,
  165. viewPort: ViewPort,
  166. enqueueR: Function1[() => Unit, Unit],
  167. rootNode: Ref[Task, Node],
  168. bulletAppState: BulletAppState
  169. )(implicit
  170. @annotation.unused timeout: Timeout,
  171. @annotation.unused scheduler: Scheduler
  172. ) {
  173. def init(
  174. appScheduler: monix.execution.Scheduler
  175. // consoleStream: GenericConsoleStream[TextArea]
  176. ) =
  177. for {
  178. _ <- loggerL.info("Initializing Systems")
  179. _ <- Task(
  180. assetManager.registerLocator(
  181. (os.rel / "assets" / "town.zip"),
  182. classOf[ZipLocator]
  183. )
  184. )
  185. _ <- loggerL.info("test hmm")
  186. // _ <- Task(consoleStream.println("text"))
  187. _ <- DefaultGameLevel(assetManager, viewPort)
  188. .addToGame(
  189. rootNode,
  190. bulletAppState.physicsSpace
  191. )
  192. .executeOn(appScheduler)
  193. _ <- createPlayerController(appScheduler).startAndForget.onErrorRestart(3)
  194. _ <- wire[GameInputHandler.Props].begin.onErrorRestart(3)
  195. } yield ()
  196. def createPlayerController(
  197. appScheduler: monix.execution.Scheduler
  198. // playerMovementEventBus: ActorRef[
  199. // EventBus.Command[PlayerMovementEvent]
  200. // ],
  201. // playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
  202. ): IO[PlayerController.Error, Unit] = {
  203. @annotation.unused
  204. val playerPos = ImVector3f.ZERO
  205. @annotation.unused
  206. val playerNode = None.taggedWith[PlayerTag]
  207. @annotation.unused
  208. val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
  209. wire[PlayerController.Props].create
  210. }
  211. }