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.

150 lines
4.3 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
  1. package wow.doge.mygame.state
  2. import akka.actor.typed.scaladsl.Behaviors
  3. import ammonite.Main
  4. import akka.actor.typed.ActorRef
  5. import ammonite.runtime.Storage.Folder
  6. import ammonite.main.Defaults
  7. import akka.actor.typed.Behavior
  8. import akka.actor.typed.scaladsl.ActorContext
  9. import ammonite.util.Res.Success
  10. import javax.script.ScriptEngine
  11. import javax.script.ScriptEngineManager
  12. import groovy.util.GroovyScriptEngine
  13. import cats.implicits._
  14. import akka.actor.typed.LogOptions
  15. import org.slf4j.event.Level
  16. import com.typesafe.scalalogging.Logger
  17. import com.softwaremill.tagging._
  18. object ScriptActor {
  19. trait Kotlin
  20. type KotlinScriptEngine = ScriptEngine @@ Kotlin
  21. final case class Error(reason: String)
  22. sealed trait Command
  23. final case class CompileAny(
  24. path: os.Path,
  25. result: ActorRef[Either[Error, Any]]
  26. ) extends Command
  27. lazy val defaultScalaRunner =
  28. ammonite
  29. .Main(
  30. storageBackend = new Folder(
  31. // os.pwd / "target"
  32. Defaults.ammoniteHome,
  33. isRepl = false
  34. )
  35. )
  36. lazy val defaultKotlinRunner: KotlinScriptEngine = {
  37. val manager = new ScriptEngineManager()
  38. val engine = manager.getEngineByExtension("main.kts")
  39. engine.taggedWith[Kotlin]
  40. }
  41. lazy val defaultGroovyRunner: GroovyScriptEngine =
  42. new GroovyScriptEngine(os.pwd.toString)
  43. def apply(
  44. scalaRunner: Main = defaultScalaRunner,
  45. kotlinRunner: KotlinScriptEngine = defaultKotlinRunner,
  46. groovyRunner: GroovyScriptEngine = defaultGroovyRunner
  47. ): Behavior[ScriptActor.Command] =
  48. Behaviors.logMessages(
  49. LogOptions()
  50. .withLevel(Level.TRACE)
  51. .withLogger(
  52. Logger[ScriptActor].underlying
  53. ),
  54. Behaviors.setup(ctx =>
  55. new ScriptActor(
  56. scalaRunner,
  57. kotlinRunner,
  58. groovyRunner,
  59. ctx
  60. ).receiveMessage
  61. )
  62. )
  63. sealed trait ScriptType
  64. case object ScalaType extends ScriptType
  65. case object KotlinType extends ScriptType
  66. case object GroovyType extends ScriptType
  67. def determineScriptType(path: os.Path): Either[Error, ScriptType] =
  68. path.toString match {
  69. case s if s.endsWith(".sc") => Right(ScalaType)
  70. case s if s.endsWith(".main.kts") => Right(KotlinType)
  71. case s if s.endsWith(".groovy") => Right(GroovyType)
  72. case _ => Left(Error("Unknown script type"))
  73. }
  74. def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] =
  75. scalaRunner
  76. .runScript(
  77. path,
  78. Seq.empty
  79. )
  80. ._1 match {
  81. case ammonite.util.Res.Exception(t, msg) => Left(Error(msg))
  82. case Success(obj) => Right(obj)
  83. case _ => Left(Error("Failed to run script"))
  84. }
  85. def runKotlin(
  86. path: os.Path,
  87. kotlinRunner: KotlinScriptEngine
  88. ): Either[Error, Any] =
  89. Either
  90. .catchNonFatal(kotlinRunner.eval(os.read(path)))
  91. .leftMap(t => Error(t.getMessage()))
  92. def runGroovy(
  93. path: os.Path,
  94. groovyRunner: GroovyScriptEngine
  95. ): Either[Error, Any] =
  96. Either
  97. .catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString(), ""))
  98. .leftMap(t => Error(t.getMessage()))
  99. def ensureReturnedObjectNotNull(scriptObject: Any): Either[Error, Any] =
  100. Either.fromOption(Option(scriptObject), Error("unknown object"))
  101. }
  102. class ScriptActor(
  103. val scalaRunner: ammonite.Main,
  104. val kotlinRunner: ScriptActor.KotlinScriptEngine,
  105. val groovyRunner: GroovyScriptEngine,
  106. context: ActorContext[ScriptActor.Command]
  107. ) {
  108. import ScriptActor._
  109. def receiveMessage: Behavior[Command] =
  110. Behaviors.receiveMessage { msg =>
  111. msg match {
  112. case CompileAny(path, requester) =>
  113. context.log.debug(s"Received $path")
  114. val res = getScript(path)
  115. context.log.debug(s"result = $res")
  116. requester ! res
  117. Behaviors.same
  118. }
  119. }
  120. def getScript(path: os.Path): Either[Error, Any] =
  121. determineScriptType(path) match {
  122. case Right(ScalaType) =>
  123. runScala(path, scalaRunner).flatMap(ensureReturnedObjectNotNull)
  124. case Right(KotlinType) =>
  125. runKotlin(path, kotlinRunner).flatMap(ensureReturnedObjectNotNull)
  126. case Right(GroovyType) =>
  127. runGroovy(path, groovyRunner).flatMap(ensureReturnedObjectNotNull)
  128. case l @ Left(err) => l
  129. }
  130. }