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.

648 lines
18 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package wow.doge.mygame
  2. import scala.jdk.CollectionConverters._
  3. import scala.reflect.ClassTag
  4. import akka.actor.typed.ActorRef
  5. import akka.actor.typed.Scheduler
  6. import akka.util.Timeout
  7. import com.jme3.app.Application
  8. import com.jme3.app.SimpleApplication
  9. import com.jme3.app.state.AppState
  10. import com.jme3.app.state.AppStateManager
  11. import com.jme3.asset.AssetLocator
  12. import com.jme3.asset.AssetManager
  13. import com.jme3.bullet.BulletAppState
  14. import com.jme3.bullet.PhysicsSpace
  15. import com.jme3.bullet.PhysicsTickListener
  16. import com.jme3.bullet.collision.PhysicsCollisionEvent
  17. import com.jme3.bullet.collision.PhysicsCollisionListener
  18. import com.jme3.bullet.control.BetterCharacterControl
  19. import com.jme3.input.Action
  20. import com.jme3.input.InputManager
  21. import com.jme3.input.controls.ActionListener
  22. import com.jme3.input.controls.AnalogListener
  23. import com.jme3.input.controls.InputListener
  24. import com.jme3.input.controls.Trigger
  25. import com.jme3.math.Vector3f
  26. import com.jme3.scene.CameraNode
  27. import com.jme3.scene.Geometry
  28. import com.jme3.scene.Node
  29. import com.jme3.scene.SceneGraphVisitor
  30. import com.jme3.scene.Spatial
  31. import com.jme3.scene.control.CameraControl.ControlDirection
  32. import com.jme3.scene.control.Control
  33. import com.simsilica.es.EntityComponent
  34. import com.simsilica.es.EntityData
  35. import com.simsilica.es.EntityId
  36. import enumeratum._
  37. import monix.bio.Task
  38. import monix.bio.UIO
  39. import monix.execution.Ack
  40. import monix.execution.Ack.Continue
  41. import monix.execution.Ack.Stop
  42. import monix.execution.Cancelable
  43. import monix.execution.cancelables.SingleAssignCancelable
  44. import monix.reactive.Observable
  45. import monix.reactive.OverflowStrategy
  46. import monix.reactive.observers.Subscriber
  47. import wow.doge.mygame.math.ImVector3f
  48. import wow.doge.mygame.state.MyBaseState
  49. import com.jme3.light.Light
  50. import com.jayfella.jme.jfx.JavaFxUI
  51. case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
  52. case class EnumActionEvent[T <: EnumEntry](
  53. binding: T,
  54. value: Boolean,
  55. tpf: Float
  56. )
  57. case class AnalogEvent(binding: Action, value: Float, tpf: Float)
  58. case class EnumAnalogEvent[T <: EnumEntry](
  59. binding: T,
  60. value: Float,
  61. tpf: Float
  62. )
  63. case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
  64. package object implicits {
  65. type PrePhysicsTickEvent = PhysicsTickEvent
  66. type PhysicsTickObservable =
  67. Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
  68. implicit final class JMEAppExt(private val app: Application) extends AnyVal {
  69. def enqueueR(cb: () => Unit) =
  70. app.enqueue(new Runnable {
  71. override def run() = cb()
  72. })
  73. }
  74. implicit final class StateManagerExt(private val asm: AppStateManager)
  75. extends AnyVal {
  76. def state[S <: AppState]()(implicit c: ClassTag[S]): S =
  77. asm.getState(c.runtimeClass.asInstanceOf[Class[S]])
  78. // def appStates = asm.getStates()
  79. }
  80. implicit final class SimpleApplicationExt[T <: SimpleApplication](
  81. private val sa: T
  82. ) extends AnyVal {
  83. def stateManager: AppStateManager = sa.getStateManager()
  84. def inputManager: InputManager = sa.getInputManager()
  85. def assetManager: AssetManager = sa.getAssetManager()
  86. def guiNode = sa.getGuiNode()
  87. def flyCam = Option(sa.getFlyByCamera())
  88. def camera = sa.getCamera()
  89. def viewPort = sa.getViewPort()
  90. def rootNode = sa.getRootNode()
  91. def observableTick: Observable[Float] =
  92. Observable.create(OverflowStrategy.Unbounded) { sub =>
  93. val c = SingleAssignCancelable()
  94. val as = new MyBaseState {
  95. override def init(): Unit = {}
  96. override def update(tpf: Float) = {
  97. if (sub.onNext(tpf) == Ack.Stop)
  98. c.cancel()
  99. }
  100. override def stop(): Unit = {}
  101. override def onEnable() = {}
  102. override def onDisable() = {}
  103. }
  104. sa.stateManager.attach(as)
  105. c := Cancelable(() => sa.stateManager.detach(as))
  106. c
  107. }
  108. }
  109. implicit final class AssetManagerExt(private val am: AssetManager)
  110. extends AnyVal {
  111. def registerLocator(
  112. assetPath: os.RelPath,
  113. locator: Class[_ <: AssetLocator]
  114. ): Unit = {
  115. am.registerLocator(assetPath.toString(), locator)
  116. }
  117. def loadModel(assetPath: os.RelPath): Spatial = {
  118. am.loadModel(assetPath.toString())
  119. }
  120. }
  121. implicit final class BulletAppStateExt(private val bas: BulletAppState)
  122. extends AnyVal {
  123. def physicsSpace = bas.getPhysicsSpace()
  124. def speed = bas.getSpeed()
  125. }
  126. implicit final class BetterCharacterControlExt(
  127. private val bcc: BetterCharacterControl
  128. ) {
  129. def withJumpForce(force: ImVector3f) = {
  130. bcc.setJumpForce(force.mutable)
  131. bcc
  132. }
  133. }
  134. implicit final class SpatialExt[T <: Spatial](private val spat: T)
  135. extends AnyVal {
  136. // def asRef = Ref[Task].of(spat)
  137. }
  138. implicit final class NodeExt[T <: Node](private val n: T) extends AnyVal {
  139. /**
  140. * Attaches the given child
  141. *
  142. * @param s
  143. * @return
  144. */
  145. def withChild(s: Spatial): Node = {
  146. n.attachChild(s)
  147. n
  148. }
  149. /**
  150. * @return Gets the list of children as a monix observable
  151. */
  152. def observableChildren =
  153. Observable.fromIterable(n.getChildren().asScala)
  154. /**
  155. * @return A copy of the list of children of this node as a lazy list
  156. */
  157. def children = LazyList.from(n.getChildren().asScala)
  158. /**
  159. * Attach given children
  160. *
  161. * @param lst
  162. */
  163. def withChildren(lst: Spatial*): Node = {
  164. for (c <- lst) n.attachChild(c)
  165. n
  166. }
  167. def +=(spatial: Spatial) = n.attachChild(spatial)
  168. def :+(spatial: Spatial) = {
  169. n += spatial
  170. n
  171. }
  172. def :+(light: Light) = {
  173. n.addLight(light)
  174. n
  175. }
  176. def -=(spatial: Spatial) = n.detachChild(spatial)
  177. def :-(spatial: Spatial) = {
  178. n -= spatial
  179. n
  180. }
  181. def :-(light: Light) = {
  182. n.removeLight(light)
  183. n
  184. }
  185. def depthFirst(cb: Spatial => Unit) =
  186. n.depthFirstTraversal(new SceneGraphVisitor() {
  187. override def visit(s: Spatial) = cb(s)
  188. })
  189. def observableDepthFirst(): Observable[Spatial] = {
  190. def loop(
  191. subscriber: Subscriber[Spatial],
  192. spatials: LazyList[Spatial]
  193. ): Task[Unit] =
  194. spatials match {
  195. // spatial can be either a node or a geometry, but it's not a sealed trait
  196. case head #:: tail =>
  197. head match {
  198. case g: Geometry =>
  199. Task.deferFuture(subscriber.onNext(g)).flatMap {
  200. case Continue =>
  201. loop(subscriber, tail)
  202. case Stop =>
  203. Task(subscriber.onComplete())
  204. }
  205. case node: Node =>
  206. val children = node.children
  207. Task.deferFuture(subscriber.onNext(node)).flatMap {
  208. case Continue =>
  209. loop(subscriber, children #::: tail)
  210. case Stop =>
  211. Task(subscriber.onComplete())
  212. }
  213. // case _ => loop(subscriber, tail)
  214. }
  215. case LazyList() => Task.unit
  216. }
  217. Observable.create(OverflowStrategy.Unbounded) { sub =>
  218. implicit val sched = sub.scheduler
  219. loop(sub, LazyList(n)).runToFuture
  220. }
  221. }
  222. def breadthFirst(cb: Spatial => Unit) =
  223. n.breadthFirstTraversal(new SceneGraphVisitor() {
  224. override def visit(s: Spatial) = cb(s)
  225. })
  226. def observableBreadthFirst(): Observable[Spatial] = {
  227. def loop(
  228. subscriber: Subscriber[Spatial],
  229. spatials: LazyList[Spatial]
  230. ): Task[Unit] =
  231. spatials match {
  232. // spatial can be either a node or a geometry, but it's not a sealed trait
  233. case head #:: tail =>
  234. head match {
  235. case g: Geometry =>
  236. Task.deferFuture(subscriber.onNext(g)).flatMap {
  237. case Continue =>
  238. loop(subscriber, tail)
  239. case Stop =>
  240. Task(subscriber.onComplete())
  241. }
  242. case node: Node =>
  243. val children = node.children
  244. Task.deferFuture(subscriber.onNext(node)).flatMap {
  245. case Continue =>
  246. loop(subscriber, tail #::: children)
  247. case Stop =>
  248. Task(subscriber.onComplete())
  249. }
  250. case unknown =>
  251. Task.deferFuture(subscriber.onNext(unknown)).flatMap {
  252. case Continue =>
  253. loop(subscriber, tail)
  254. case Stop =>
  255. Task(subscriber.onComplete())
  256. }
  257. }
  258. case LazyList() => Task.unit
  259. }
  260. Observable.create(OverflowStrategy.Unbounded) { sub =>
  261. implicit val sched = sub.scheduler
  262. loop(sub, LazyList(n)).runToFuture
  263. }
  264. }
  265. def withControl[C <: Control](ctrl: C) = {
  266. n.addControl(ctrl)
  267. n
  268. }
  269. def withLocalTranslation(dir: ImVector3f) = {
  270. n.setLocalTranslation(dir.mutable)
  271. n
  272. }
  273. def withRotate(xAngle: Float, yAngle: Float, zAngle: Float) = {
  274. n.rotate(xAngle, yAngle, zAngle)
  275. n
  276. }
  277. def localRotation = n.getLocalRotation()
  278. def localTranslation = n.getLocalTranslation()
  279. }
  280. implicit final class CameraNodeExt(private val cn: CameraNode) {
  281. def withControlDir(controlDir: ControlDirection) = {
  282. cn.setControlDir(controlDir)
  283. cn
  284. }
  285. def withLookAt(position: ImVector3f, upVector: ImVector3f) = {
  286. cn.lookAt(position.mutable, upVector.mutable)
  287. cn
  288. }
  289. }
  290. implicit final class EntityDataExt(private val ed: EntityData)
  291. extends AnyVal {
  292. def query = new EntityQuery(ed)
  293. // def entities[T <: EntityComponent](entities: Seq[T])
  294. }
  295. implicit final class EntityExt(private val e: EntityId) extends AnyVal {
  296. def withComponents(classes: EntityComponent*)(implicit
  297. ed: EntityData
  298. ): EntityId = {
  299. ed.setComponents(e, classes: _*)
  300. e
  301. }
  302. }
  303. implicit final class ActorRefExt[Req](private val a: ActorRef[Req])
  304. extends AnyVal {
  305. import akka.actor.typed.scaladsl.AskPattern._
  306. /**
  307. * @param replyTo
  308. * @param timeout
  309. * @param scheduler
  310. * @return
  311. */
  312. def askL[Res](
  313. replyTo: ActorRef[Res] => Req
  314. )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
  315. Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
  316. }
  317. def ??[Res](
  318. replyTo: ActorRef[Res] => Req
  319. )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
  320. askL(replyTo)
  321. /**
  322. * Same as [[tell]], but wrapped in a Task
  323. *
  324. * @param msg
  325. * @return
  326. */
  327. def tellL(msg: Req) = UIO(a.tell(msg))
  328. def !!(msg: Req) = tellL(msg)
  329. }
  330. // def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = {
  331. // ask(replyTo)(timeout, scheduler)
  332. // }
  333. implicit final class InputManagerExt(private val inputManager: InputManager)
  334. extends AnyVal {
  335. def withMapping(mapping: String, triggers: Trigger*): InputManager = {
  336. inputManager.addMapping(mapping, triggers: _*)
  337. inputManager
  338. }
  339. def withListener(listener: InputListener, mappings: String*) = {
  340. inputManager.addListener(listener, mappings: _*)
  341. inputManager
  342. }
  343. def withEnumMappings[T <: EnumEntry](
  344. mappingEnum: Enum[T]
  345. )(mappingFn: T => Seq[Trigger]): InputManager = {
  346. for (entry <- mappingEnum.values) {
  347. val mappings = mappingFn(entry)
  348. inputManager.addMapping(entry.entryName, mappings: _*)
  349. }
  350. inputManager
  351. }
  352. def observableAction(mappingNames: String*): Observable[ActionEvent] = {
  353. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  354. val c = SingleAssignCancelable()
  355. val al = new ActionListener {
  356. override def onAction(
  357. binding: String,
  358. value: Boolean,
  359. tpf: Float
  360. ): Unit = {
  361. if (
  362. sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
  363. ) {
  364. sub.onComplete()
  365. c.cancel()
  366. }
  367. }
  368. }
  369. inputManager.addListener(al, mappingNames: _*)
  370. c := Cancelable(() => inputManager.removeListener(al))
  371. c
  372. }
  373. }
  374. def enumObservableAction[T <: EnumEntry](
  375. mappingEnum: Enum[T]
  376. ): Observable[EnumActionEvent[T]] = {
  377. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  378. val c = SingleAssignCancelable()
  379. val entryNames = mappingEnum.values.map(_.entryName)
  380. val al = new ActionListener {
  381. override def onAction(
  382. binding: String,
  383. value: Boolean,
  384. tpf: Float
  385. ): Unit = {
  386. mappingEnum.withNameOption(binding).foreach { b =>
  387. if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
  388. sub.onComplete()
  389. c.cancel()
  390. }
  391. }
  392. }
  393. }
  394. inputManager.addListener(al, entryNames: _*)
  395. c := Cancelable(() => inputManager.removeListener(al))
  396. c
  397. }
  398. }
  399. def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
  400. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  401. val c = SingleAssignCancelable()
  402. val al = new AnalogListener {
  403. override def onAnalog(
  404. binding: String,
  405. value: Float,
  406. tpf: Float
  407. ): Unit = {
  408. if (
  409. sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
  410. ) {
  411. sub.onComplete()
  412. c.cancel()
  413. }
  414. }
  415. }
  416. inputManager.addListener(al, mappingNames: _*)
  417. c := Cancelable(() => inputManager.removeListener(al))
  418. c
  419. }
  420. }
  421. def enumAnalogObservable[T <: EnumEntry](
  422. mappingEnum: Enum[T]
  423. ): Observable[EnumAnalogEvent[T]] = {
  424. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  425. val c = SingleAssignCancelable()
  426. val entryNames = mappingEnum.values.map(_.entryName)
  427. val al = new AnalogListener {
  428. override def onAnalog(
  429. binding: String,
  430. value: Float,
  431. tpf: Float
  432. ): Unit = {
  433. mappingEnum.withNameOption(binding).foreach { b =>
  434. if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
  435. sub.onComplete()
  436. c.cancel()
  437. }
  438. }
  439. }
  440. }
  441. inputManager.addListener(al, entryNames: _*)
  442. c := Cancelable(() => inputManager.removeListener(al))
  443. c
  444. }
  445. }
  446. }
  447. implicit final class PhysicsSpaceExt(private val space: PhysicsSpace)
  448. extends AnyVal {
  449. def collisionObservable(): Observable[PhysicsCollisionEvent] = {
  450. Observable.create(OverflowStrategy.Unbounded) { sub =>
  451. val c = SingleAssignCancelable()
  452. val cl = new PhysicsCollisionListener {
  453. override def collision(event: PhysicsCollisionEvent): Unit = {
  454. if (sub.onNext(event) == Ack.Stop) {
  455. sub.onComplete()
  456. c.cancel()
  457. }
  458. }
  459. }
  460. space.addCollisionListener(cl)
  461. c := Cancelable(() => space.removeCollisionListener(cl))
  462. c
  463. }
  464. }
  465. def physicsTickObservable(): PhysicsTickObservable = {
  466. Observable.create(OverflowStrategy.Unbounded) { sub =>
  467. val c = SingleAssignCancelable()
  468. val cl = new PhysicsTickListener {
  469. override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
  470. val event = PhysicsTickEvent(space, tpf)
  471. if (sub.onNext(Left(event)) == Ack.Stop) {
  472. sub.onComplete()
  473. c.cancel()
  474. }
  475. }
  476. override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
  477. val event = PhysicsTickEvent(space, tpf)
  478. if (sub.onNext(Right(event)) == Ack.Stop) {
  479. sub.onComplete()
  480. c.cancel()
  481. }
  482. }
  483. }
  484. space.addTickListener(cl)
  485. c := Cancelable(() => space.removeTickListener(cl))
  486. c
  487. }
  488. }
  489. //TODO Create a typeclass for this
  490. def +=(anyObject: Any) = space.add(anyObject)
  491. def :+(anyObject: Any) = {
  492. space.add(anyObject)
  493. space
  494. }
  495. def :-(anyObject: Any) = {
  496. space.remove(anyObject)
  497. space
  498. }
  499. def +=(spatial: Spatial) = space.addAll(spatial)
  500. def :+(spatial: Spatial) = {
  501. space.addAll(spatial)
  502. space
  503. }
  504. def :-(spatial: Spatial) = {
  505. space.removeAll(spatial)
  506. space
  507. }
  508. // def asRef = Ref[Task].of(space)
  509. }
  510. implicit final class Vector3fExt(private val v: Vector3f) extends AnyVal {
  511. //TODO add more operations
  512. def +=(that: Vector3f) = v.addLocal(that)
  513. def +=(f: Float) = v.addLocal(f, f, f)
  514. def +=(that: ImVector3f) = v.addLocal(that.x, that.y, that.z)
  515. def +=:(that: ImVector3f) = v += that
  516. def *=(that: Vector3f) = v.multLocal(that)
  517. def *=(that: ImVector3f) = v.multLocal(that.x, that.y, that.z)
  518. def *=:(that: ImVector3f) = v *= that
  519. def -=(that: Vector3f) = v.subtractLocal(that)
  520. def -=(that: ImVector3f) = v.subtractLocal(that.x, that.y, that.z)
  521. def -=:(that: ImVector3f) = v *= that
  522. def /=(that: Vector3f) = v.divideLocal(that)
  523. def /=(that: ImVector3f) = v.divideLocal(that.mutable)
  524. def /=:(that: ImVector3f) = v *= that
  525. def unary_- = v.negateLocal()
  526. def immutable = ImVector3f(v.x, v.y, v.z)
  527. }
  528. implicit final class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
  529. def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
  530. def +(f: Float): ImVector3f = v.copy(v.x + f, v.y + f, v.z + f)
  531. def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
  532. def *(f: Float): ImVector3f =
  533. v.copy(v.x * f, v.y * f, v.z * f)
  534. // v * ImVector3f(f, f, f)
  535. def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z)
  536. def -(f: Float): ImVector3f = v.copy(v.x - f, v.y - f, v.z - f)
  537. def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z)
  538. def /(f: Float): ImVector3f = v.copy(v.x / f, v.y / f, v.z / f)
  539. def unary_- = v.copy(-v.x, -v.y, -v.z)
  540. def mutable = new Vector3f(v.x, v.y, v.z)
  541. }
  542. // val TasktoUIO = new FunctionK[Task, UIO] {
  543. // def apply[T](f: Task[T]): UIO[T] =
  544. // f.hideErrors
  545. // }
  546. implicit final class JavaFxUIExt(private val jfxui: JavaFxUI) extends AnyVal {
  547. def +=(node: scalafx.scene.Node) = jfxui.attachChild(node)
  548. def -=(node: scalafx.scene.Node) = jfxui.detachChild(node)
  549. }
  550. }