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.

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