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.
 
 

142 lines
4.0 KiB

package wow.doge.mygame.state
import akka.actor.typed.scaladsl.Behaviors
import ammonite.Main
import akka.actor.typed.ActorRef
import ammonite.runtime.Storage.Folder
import ammonite.main.Defaults
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.ActorContext
import ammonite.util.Res.Success
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
import groovy.util.GroovyScriptEngine
import cats.implicits._
object ScriptActor {
type Kotlin
type MyScriptEngine[T] = ScriptEngine
type KotlinScriptEngine = MyScriptEngine[Kotlin]
final case class Error(reason: String)
sealed trait Command
final case class CompileAny(
path: os.Path,
result: ActorRef[Either[Error, Any]]
) extends Command
def defaultScalaRunner() =
ammonite
.Main(
storageBackend = new Folder(
// os.pwd / "target"
Defaults.ammoniteHome,
isRepl = false
)
)
def defaultKotlinRunner(): KotlinScriptEngine = {
val manager = new ScriptEngineManager()
val engine = manager.getEngineByExtension("main.kts")
engine
}
def defaultGroovyRunner(): GroovyScriptEngine =
new GroovyScriptEngine(os.pwd.toString())
def apply(
scalaRunner: Main = defaultScalaRunner(),
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner(),
groovyRunner: GroovyScriptEngine = defaultGroovyRunner()
// parent: ActorRef[ScriptStoringActor.Command]
): Behavior[ScriptActor.Command] =
Behaviors.setup(ctx =>
new ScriptActor(
scalaRunner,
kotlinRunner,
groovyRunner,
ctx
).receiveMessage
)
sealed trait ScriptType
case object ScalaType extends ScriptType
case object KotlinType extends ScriptType
case object GroovyType extends ScriptType
def determineScriptType(path: os.Path): Either[Error, ScriptType] =
path.toString match {
case s if s.endsWith(".sc") => Right(ScalaType)
case s if s.endsWith(".main.kts") => Right(KotlinType)
case s if s.endsWith(".groovy") => Right(GroovyType)
case _ => Left(Error("Unknown script type"))
}
def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] =
scalaRunner
.runScript(
path,
Seq.empty
)
._1 match {
case ammonite.util.Res.Exception(t, msg) => Left(Error(msg))
case Success(obj) => Right(obj)
case _ => Left(Error("Failed to run script"))
}
def runKotlin(
path: os.Path,
kotlinRunner: KotlinScriptEngine
): Either[Error, Any] =
Either
.catchNonFatal(kotlinRunner.eval(os.read(path)))
.leftMap(t => Error(t.getMessage()))
def runGroovy(
path: os.Path,
groovyRunner: GroovyScriptEngine
): Either[Error, Any] =
Either
.catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString(), ""))
.leftMap(t => Error(t.getMessage()))
def ensureReturnedObjectNotNull(scriptObject: Any): Either[Error, Any] =
Either.fromOption(Option(scriptObject), Error("unknown object"))
}
class ScriptActor(
val scalaRunner: ammonite.Main,
val kotlinRunner: ScriptActor.KotlinScriptEngine,
val groovyRunner: GroovyScriptEngine,
context: ActorContext[ScriptActor.Command]
) {
import ScriptActor._
def receiveMessage: Behavior[Command] =
Behaviors.receiveMessage { msg =>
msg match {
case CompileAny(path, requester) =>
context.log.debug(s"Received $path")
val res = getScript(path)
context.log.debug(s"result = $res")
requester ! res
Behaviors.same
}
}
def getScript(path: os.Path): Either[Error, Any] =
determineScriptType(path) match {
case Right(ScalaType) =>
runScala(path, scalaRunner).flatMap(ensureReturnedObjectNotNull)
case Right(KotlinType) =>
runKotlin(path, kotlinRunner).flatMap(ensureReturnedObjectNotNull)
case Right(GroovyType) =>
runGroovy(path, groovyRunner).flatMap(ensureReturnedObjectNotNull)
case l @ Left(err) => l
}
}