many changes

This commit is contained in:
Rohan Sircar 2020-11-13 19:44:57 +05:30
parent 1f55e08fa5
commit b0994bcdbe
41 changed files with 1494 additions and 413 deletions

4
.jvmopts Normal file
View File

@ -0,0 +1,4 @@
-Xms2G
-Xmx2G
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50

View File

@ -66,10 +66,11 @@ lazy val root = (project in file(".")).settings(
"com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full, "com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full,
"org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10", "org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10",
"org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10", "org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10",
"org.codehaus.groovy" % "groovy-all" % "3.0.6" pomOnly (),
// "wow.doge" % "game" % "1.0-SNAPSHOT", // "wow.doge" % "game" % "1.0-SNAPSHOT",
"org.scalafx" %% "scalafx" % "14-R19", "org.scalafx" %% "scalafx" % "14-R19",
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.10", "com.typesafe.akka" %% "akka-actor-typed" % "2.6.10",
"ch.qos.logback" % "logback-classic" % "1.2.3", // "ch.qos.logback" % "logback-classic" % "1.2.3",
"org.typelevel" %% "cats-core" % "2.1.1", "org.typelevel" %% "cats-core" % "2.1.1",
"org.typelevel" %% "cats-effect" % "2.1.4", "org.typelevel" %% "cats-effect" % "2.1.4",
"io.monix" %% "monix" % "3.2.2", "io.monix" %% "monix" % "3.2.2",
@ -85,7 +86,8 @@ lazy val root = (project in file(".")).settings(
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided", "com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided",
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided", "com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
"com.github.valskalla" %% "odin-slf4j" % "0.8.1", "com.github.valskalla" %% "odin-slf4j" % "0.8.1",
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1" "com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1"
), ),
// Determine OS version of JavaFX binaries // Determine OS version of JavaFX binaries
@ -102,6 +104,7 @@ lazy val root = (project in file(".")).settings(
"-Xlint", "-Xlint",
"-Ywarn-numeric-widen", "-Ywarn-numeric-widen",
"-Ymacro-annotations", "-Ymacro-annotations",
"-Xlint:byname-implicit",
// "utf-8", // Specify character encoding used by source files. // "utf-8", // Specify character encoding used by source files.
"-explaintypes" // Explain type errors in more detail. "-explaintypes" // Explain type errors in more detail.
), ),
@ -141,6 +144,7 @@ lazy val root = (project in file(".")).settings(
// semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version // semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version
// semanticdbVersion := "4.3.24", // semanticdbVersion := "4.3.24",
) )
initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
// Here, `libraryDependencies` is a set of dependencies, and by using `+=`, // Here, `libraryDependencies` is a set of dependencies, and by using `+=`,
// we're adding the scala-parser-combinators dependency to the set of dependencies // we're adding the scala-parser-combinators dependency to the set of dependencies
@ -190,3 +194,4 @@ lazy val root = (project in file(".")).settings(
// To learn more about multi-project builds, head over to the official sbt // To learn more about multi-project builds, head over to the official sbt
// documentation at http://www.scala-sbt.org/documentation.html // documentation at http://www.scala-sbt.org/documentation.html
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")

3
launch.sh Normal file
View File

@ -0,0 +1,3 @@
# sdk use java 20.2.0.r11-grl
# java -J-Xms2G -J-Xmx2G -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=50 -jar target/scala-2.13/mygame-assembly-1.0-SNAPSHOT.jar
java -Xms2G -Xmx2G -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -jar target/scala-2.13/mygame-assembly-1.0-SNAPSHOT.jar

BIN
lib/jme3-testdata.jar Normal file

Binary file not shown.

4
plugins.json Normal file
View File

@ -0,0 +1,4 @@
[
{ "name": "test", "priority": 1 },
{ "name": "test2", "priority": 2 }
]

View File

@ -1,2 +1,3 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23")
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")

View File

@ -0,0 +1,3 @@
println "hello"
this

View File

@ -1,4 +1,5 @@
#!/usr/bin/env amm #!/usr/bin/env amm
// scala 2.13.3
// import coursierapi.MavenRepository // import coursierapi.MavenRepository

View File

@ -0,0 +1,59 @@
package org.slf4j.impl
import cats.effect.{ContextShift, Clock, Effect, IO, Timer}
import io.odin._
import io.odin.slf4j.OdinLoggerBinder
import cats.implicits._
import scala.concurrent.ExecutionContext
import _root_.monix.execution.Scheduler
import cats.arrow.FunctionK
import _root_.monix.execution.Scheduler.Implicits.global
import io.odin.syntax._
import scala.concurrent.duration._
//effect type should be specified inbefore
//log line will be recorded right after the call with no suspension
class StaticLoggerBinder extends OdinLoggerBinder[IO] {
val ec: ExecutionContext = Scheduler.global
implicit val timer: Timer[IO] = IO.timer(ec)
implicit val clock: Clock[IO] = timer.clock
implicit val cs: ContextShift[IO] = IO.contextShift(ec)
implicit val F: Effect[IO] = IO.ioEffect
val monixToCats = new FunctionK[_root_.monix.bio.Task, IO] {
def apply[A](fa: _root_.monix.bio.Task[A]): IO[A] = fa.to[IO]
}
val (fLogger, release) =
// MainModule.DefaultFileLogger.mapK(monixToCats).allocated.unsafeRunSync()
fileLogger[IO](
"log2.log"
).withAsync(timeWindow = 1.seconds).allocated.unsafeRunSync()
// Runtime
// .getRuntime()
// .addShutdownHook(new Thread {
// release.unsafeRunSync()
// })
scala.sys.addShutdownHook(release.unsafeRunSync())
val loggers: PartialFunction[String, Logger[IO]] = {
case "some.external.package.SpecificClass" =>
consoleLogger[IO](minLevel = Level.Warn) //disable noisy external logs
case asyncHttpClient
if asyncHttpClient.startsWith("org.asynchttpclient.netty") =>
consoleLogger[IO](minLevel = Level.Warn)
case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
consoleLogger[IO]() |+| fLogger
case _ => //if wildcard case isn't provided, default logger is no-op
consoleLogger[IO]()
}
}
object StaticLoggerBinder extends StaticLoggerBinder {
var REQUESTED_API_VERSION: String = "1.7"
def getSingleton: StaticLoggerBinder = this
}

View File

@ -1,127 +1,105 @@
package wow.doge.mygame package wow.doge.mygame
import game.GameApp
import com.jme3.app.StatsAppState import com.jme3.app.StatsAppState
import akka.actor.typed.ActorSystem import monix.bio.Task
import akka.actor.typed.SpawnProtocol import cats.effect.Resource
import akka.actor.typed.scaladsl.Behaviors import io.odin.syntax._
import akka.actor.typed.Behavior
import akka.util.Timeout
import com.jme3.system.AppSettings
import wow.doge.mygame.game.GameAppActor
import wow.doge.mygame.scriptsystem.ScriptCachingActor
object Main extends App {
import java.util.logging.{Logger, Level}
Logger.getLogger("").setLevel(Level.SEVERE)
// runner.runCode("""|println("starting scala script engine")""".stripMargin) // import io.odin.monix._
val gameApp = new GameApp( import cats.effect.ExitCode
// new EntityDataState(), import cats.implicits._
// new TestAppState(), import com.softwaremill.macwire._
// new PlayerMovementState(), import scala.concurrent.duration._
// new FlyCamAppState(), import monix.bio.BIOApp
new StatsAppState() import monix.bio.UIO
) import monix.bio.IO
val settings = new AppSettings(true) import io.odin._
// settings.setVSync(true) import wow.doge.mygame.executors.JMERunner
settings.setFrameRate(144) import com.jme3.bullet.BulletAppState
gameApp.setSettings(settings) import wow.doge.mygame.implicits._
val actorSystem = ActorSystem(RootActor(gameApp), "rootActor")
// actorSystem.eventStream
// gameApp.start()
println("here 1")
// actorSystem.terminate()
// JMEExecutorService.shutdown()
// println("here 2")
//FIXME remove this
// System.exit(0)
}
object RootActor { // import wow.doge.mygame.implicits._
def apply(app: GameApp): Behavior[SpawnProtocol.Command] =
Behaviors.setup { ctx =>
ctx.log.debug("Starting root actor")
val testActor = ctx.spawn(TestActor(), "testActor")
val _ = ctx.spawn(GameAppActor(app), "gameAppActor")
testActor ! TestActor.Test // object Main extends App {
SpawnProtocol() // import java.util.logging.{Logger, Level}
// Logger.getLogger("").setLevel(Level.SEVERE)
// // runner.runCode("""|println("starting scala script engine")""".stripMargin)
// val gameApp = new GameApp(
// // new EntityDataState(),
// // new TestAppState(),
// // new PlayerMovementState(),
// // new FlyCamAppState(),
// new StatsAppState()
// )
// val settings = new AppSettings(true)
// // settings.setVSync(true)
// settings.setFrameRate(144)
// gameApp.setSettings(settings)
// val actorSystem = ActorSystem(RootActor(gameApp), "rootActor")
// // actorSystem.eventStream
// // gameApp.start()
// println("here 1")
// // actorSystem.terminate()
// // JMEExecutorService.shutdown()
// // println("here 2")
// //FIXME remove this
// // System.exit(0)
// }
object Main extends BIOApp with MainModule {
import java.util.logging.{Logger => JLogger, Level}
JLogger.getLogger("").setLevel(Level.SEVERE)
def run(args: List[String]): UIO[ExitCode] = {
lazy val appResource = for {
logger <-
consoleLogger().withAsync(timeWindow = 1.seconds) |+| fileLogger(
"log.log"
).withAsync(timeWindow = 1.seconds)
jmeScheduler <- jMESchedulerResource
// consoleTextArea <- Resource.liftF(Task(new TextArea()))
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
gameApp <- {
new BulletAppState()
// bas.setThreadingType(Thr)
gameAppResource(new StatsAppState())
} }
} _ <- Resource.liftF(IO(JMERunner.runner = gameApp))
actorSystem <- wireWith(actorSystemResource _)
object TestActor { // _ <- Resource.liftF(
sealed trait Command // Task(gameApp.start()).asyncBoundary
case object Test extends Command // .executeOn(Scheduler(JMEExecutorService))
private case object Done extends Command
// sealed trait Result
// case object Done extends Result
import scala.concurrent.duration._
implicit val timeout = Timeout(15.seconds)
// implicit val scheduler =
def apply(): Behavior[Command] =
Behaviors.setup { ctx =>
ctx.spawn(ScriptCachingActor(), "scriptCacher")
Behaviors.receiveMessage { msg =>
msg match {
case Test =>
// ctx.ask(
// router,
// ScriptActor.Compile(
// // os.pwd / "some.sc",
// os.pwd / "src" / "main" / "resources" / "hello2.main.kts",
// _
// ) // )
// ) {
// case Success(value) =>
// ctx.log.debug("Received Value")
// ctx.log.debug(value.toString())
// Done
// case Failure(exception) =>
// ctx.log.debug(s"Received Error ${exception.getMessage()}")
// Done
// }
// val x = scriptStorer
// .askT(
// ScriptStoringActor
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// )(timeout, ctx.system.scheduler)
// ctx.ask( _ <- Resource.liftF(gameApp.enqueueT(actorSystem ! RootActor.Start))
// scriptStorer, _ <- Resource.liftF {
// ScriptStoringActor IO(gameApp.start())
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) .executeOn(jmeScheduler)
// ) {
// case Success(value) => {
// ctx.log.debug(value.toString())
// ctx.ask(
// scriptStorer,
// ScriptStoringActor
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// ) {
// case Success(value) => {
// ctx.log.debug(value.toString())
// Done
// }
// case Failure(exception) =>
// ctx.log.debug(exception.getMessage())
// Done
// }
// Done
// }
// case Failure(exception) =>
// ctx.log.debug(exception.getMessage())
// Done
// }
Behaviors.same
case Done => Behaviors.same
} }
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
} yield ()
// SpawnProtocol() // Console.withOut(
// Behaviors.same // new JFXConsoleStream(
} // new scalafx.scene.control.TextArea(),
// new ByteArrayOutputStream(35)
// )
// )(())
appResource
.use(_ =>
// Task(gameApp.start())
// .executeOn(Scheduler(JMEExecutorService))
// .asyncBoundary
// Task.never
Task.unit
// >>
// .executeOn(Scheduler(JMEExecutorService))
)
.onErrorHandle(_.printStackTrace())
.as(ExitCode.Success)
} }
} }

View File

@ -0,0 +1,87 @@
package wow.doge.mygame
import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.game.GameApp
import akka.actor.typed.Behavior
import wow.doge.mygame.game.GameAppActor
import cats.effect.Resource
import akka.actor.typed.ActorSystem
import monix.bio.Task
import wow.doge.mygame.game.GameModule
import io.odin._
import io.odin.syntax._
import wow.doge.mygame.executors.ExecutorsModule
import akka.actor.typed.scaladsl.ActorContext
import wow.doge.mygame.executors.Schedulers
import com.softwaremill.macwire._
trait MainModule extends GameModule with ExecutorsModule {
def actorSystemResource(
logger: Logger[Task],
app: GameApp,
schedulers: Schedulers
): Resource[Task, ActorSystem[RootActor.Command]] =
Resource.make(logger.info("Creating Actor System") >> Task {
ActorSystem(RootActor(app, schedulers), name = "GameActorSystem")
})(sys =>
logger.info("Shutting down actor system") >> Task(
sys.terminate()
)
)
}
object MainModule {
// import cats.implicits._
import scala.concurrent.duration._
val DefaultFileLogger: Resource[Task, Logger[Task]] =
fileLogger[Task](
"log.log"
).withAsync(timeWindow = 1.seconds)
}
object RootActor {
sealed trait Command
final case object Start extends Command
final case object Stop extends Command
final case class State(initialized: Boolean = false)
def apply(
app: GameApp,
schedulers: Schedulers,
state: State = State()
): Behavior[Command] =
Behaviors.setup { ctx =>
ctx.log.info("Hello from root actor")
wire[RootActor].receive(state)
}
}
class RootActor(
ctx: ActorContext[RootActor.Command],
app: GameApp,
schedulers: Schedulers
) {
import RootActor._
def receive(state: State): Behavior[Command] =
Behaviors.receiveMessage(msg =>
msg match {
case Start =>
if (!state.initialized) {
ctx.log.info("Starting GameAppActor")
val _ = ctx.spawn(
wireWith(GameAppActor.apply _),
"gameAppActor"
// DispatcherSelector.fromConfig("jme-dispatcher")
)
receive(state.copy(initialized = true))
} else {
ctx.log.warn("Already Initialized")
Behaviors.same
}
case Stop =>
ctx.log.info("Stopping")
Behaviors.stopped
}
)
}

View File

@ -1,5 +1,23 @@
package wow.doge.mygame.executors package wow.doge.mygame.executors
import monix.bio.Task
import cats.effect.Resource
import monix.execution.Scheduler
trait ExecutorsModule { trait ExecutorsModule {
lazy val schedulers = new Schedulers() lazy val schedulers = Schedulers()
// Resource.make(
// Task(
// new Schedulers(
// jme = Scheduler
// .singleThread(name = "JME-Application-Thread", daemonic = false)
// )
// )
// )(s => Task(s.jme.shutdown()))
lazy val jMESchedulerResource = Resource.make(
Task(
Scheduler
.singleThread(name = "JME-Application-Thread", daemonic = false)
)
)(e => Task(e.shutdown()))
} }

View File

@ -18,7 +18,6 @@ import javafx.application.Platform
import monix.execution.Scheduler import monix.execution.Scheduler
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
import java.util.concurrent.Executor import java.util.concurrent.Executor
import wow.doge.mygame.Main
// First we wrap invokeLater/runLater as an ExecutorService // First we wrap invokeLater/runLater as an ExecutorService
trait GUIExecutorService extends AbstractExecutorService { trait GUIExecutorService extends AbstractExecutorService {
@ -44,7 +43,15 @@ object SwingExecutorService extends GUIExecutorService {
} }
object JMEExecutorService extends GUIExecutorService { object JMEExecutorService extends GUIExecutorService {
override def execute(command: Runnable) = Main.gameApp.enqueue(command) override def execute(command: Runnable) =
JMERunner.runner.enqueue(command)
// new SingleThreadEventExecutor()
sys.addShutdownHook(JMEExecutorService.shutdown())
}
object JMERunner {
var runner: com.jme3.app.Application = null
} }
class JavaFXEventThreadExecutorServiceConfigurator( class JavaFXEventThreadExecutorServiceConfigurator(

View File

@ -4,7 +4,7 @@ import monix.execution.Scheduler
final case class Schedulers( final case class Schedulers(
blockingIO: Scheduler = Scheduler.io(), blockingIO: Scheduler = Scheduler.io(),
cpu: Scheduler = Scheduler.global, async: Scheduler = Scheduler.global,
fx: Scheduler = JFXExecutionContexts.fxScheduler, fx: Scheduler = JFXExecutionContexts.fxScheduler
jme: Option[Scheduler] = None // jme: SchedulerService
) )

View File

@ -3,23 +3,16 @@ package wow.doge.mygame.game
import com.jme3.app.SimpleApplication import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState import com.jme3.app.state.AppState
import akka.actor.typed.ActorRef import com.jme3.bullet.BulletAppState
import akka.actor.typed.Behavior import com.jme3.bullet.collision.shapes.CapsuleCollisionShape
import akka.actor.typed.scaladsl.Behaviors import com.jme3.bullet.control.CharacterControl
import com.jme3.bullet.control.RigidBodyControl
object Greeter { import com.jme3.bullet.util.CollisionShapeFactory
final case class Greet(whom: String, replyTo: ActorRef[Greeted]) import com.jme3.scene.Spatial
final case class Greeted(whom: String, from: ActorRef[Greet]) import com.jme3.syntax._
import com.jme3.asset.plugins.ZipLocator
def apply(): Behavior[Greet] = import com.jme3.math.ColorRGBA
Behaviors.receive { (context, message) => import wow.doge.mygame.implicits._
// context.log.info("Hello {}!", message.whom)
//#greeter-send-messages
message.replyTo ! Greeted(message.whom, context.self)
//#greeter-send-messages
Behaviors.same
}
}
class GameApp( class GameApp(
// actorSystem: ActorSystem[SpawnProtocol.Command], // actorSystem: ActorSystem[SpawnProtocol.Command],
@ -27,8 +20,47 @@ class GameApp(
) extends SimpleApplication(appStates: _*) { ) extends SimpleApplication(appStates: _*) {
// implicit val timeout = Timeout(10.seconds) // implicit val timeout = Timeout(10.seconds)
// implicit val scheduler = actorSystem.scheduler // implicit val scheduler = actorSystem.scheduler
private lazy val sceneModel: Spatial = assetManager.loadModel("main.scene")
private lazy val bulletAppState: BulletAppState = new BulletAppState()
// bulletAppState.setThreadingType(ThreadingType.SEQUENTIAL)
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass zero.
private lazy val sceneShape = CollisionShapeFactory.createMeshShape(
sceneModel.toNode match {
case util.Right(node) => node
case util.Left(ex) =>
throw new NotImplementedError("No fallback sceneshape")
}
)
private lazy val landscape: RigidBodyControl =
new RigidBodyControl(sceneShape, 0)
// We set up collision detection for the player by creating
// a capsule collision shape and a CharacterControl.
// The CharacterControl offers extra settings for
// size, stepheight, jumping, falling, and gravity.
// We also put the player in its starting position.
protected lazy val capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1)
private lazy val player: CharacterControl =
new CharacterControl(capsuleShape, 0.05f)
override def simpleInitApp(): Unit = { override def simpleInitApp(): Unit = {
discard { stateManager.attach(bulletAppState) }
assetManager.registerLocator(
// "src/main/resources/assets/town.zip",
(os.rel / "src" / "main" / "resources" / "assets" / "town.zip"),
classOf[ZipLocator]
)
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
sceneModel.setLocalScale(2f)
sceneModel.addControl(landscape)
discard { rootNode.attachChild(sceneModel) }
bulletAppState.getPhysicsSpace.add(landscape)
bulletAppState.getPhysicsSpace.add(player)
println("gameapp" + Thread.currentThread().getName())
// val ship = ed.createEntity() // val ship = ed.createEntity()
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData()) // val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
@ -88,22 +120,23 @@ class GameApp(
// super.stop() // super.stop()
// } // }
} // override def start(): Unit = {
// monix.eval.Task(super.start()).runToFuture(Scheduler(JMEExecutorService))
object GameApp {
// def myExec(app: SimpleApplication, command: Runnable) = {
// app.enqueue(command)
// } // }
// val javaFxExecutionContext: ExecutionContext =
// ExecutionContext.fromExecutor(new Executor {
// def execute(command: Runnable): Unit = {
// Platform.runLater(command)
// }
// })
// def jmeEC(app: SimpleApplication): ExecutionContext =
// ExecutionContext.fromExecutor(new Executor {
// override def execute(command: Runnable): Unit = app.enqueue(command)
// })
// def jmeScheduler(app: SimpleApplication) = Sch // def start(system: ActorRef[RootActor.Command]) = {
// // system ! RootActor.Start
// super.start()
// }
// override def stop(): Unit = {
// println("stopping")
// }
def stop[T](cb: () => T): Unit = {
println("destroy")
cb()
super.stop()
}
// override def stop(): Unit = {}
} }

View File

@ -1,60 +1,174 @@
package wow.doge.mygame.game package wow.doge.mygame.game
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.state.MovementActor import wow.doge.mygame.state.PlayerMovementState
import wow.doge.mygame.state.PlayerMovementState2
import wow.doge.mygame.state.MovementActorTimer
import com.jme3.scene.shape.Box
import com.jme3.scene.Geometry import com.jme3.scene.Geometry
import wow.doge.mygame.implicits._
import wow.doge.mygame.events.EventBus import wow.doge.mygame.events.EventBus
import wow.doge.mygame.events.Events import wow.doge.mygame.events.Events
import wow.doge.mygame.state.ImMovementActor import wow.doge.mygame.state.ImMovementActor
import com.jme3.scene.CameraNode
import com.jme3.scene.Node
import com.jme3.renderer.Camera
import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.nodes.PlayerNode
import com.softwaremill.macwire._
import wow.doge.mygame.implicits._
object GameAppActor { object GameAppActor {
import Methods._
sealed trait Command sealed trait Command
def apply(app: GameApp) = case object XD extends Command
case object Stop extends Command
def apply(app: GameApp, schedulers: Schedulers) =
Behaviors.setup[Command] { ctx => Behaviors.setup[Command] { ctx =>
lazy val b = new Box(1, 1, 1) ctx.log.info("Hello from GameAppActor")
lazy val geom = new Geometry("Box", b) // lazy val b = new Box(1, 1, 1)
val movementActor = // lazy val geom = new Geometry("Box", b)
ctx.spawn( // lazy val playerNode = new Node("PlayerNode")
MovementActor(MovementActor.Props(app, geom)), // lazy val camNode = new CameraNode("CameraNode", app.getCamera())
"movementActor" // lazy val players = createPlayer(geom, app.getCamera())
// DispatcherSelector.fromConfig("jme-dispatcher")
)
val movementActorTimer = ctx.spawn(
MovementActorTimer(movementActor),
"movementActorTimer"
)
val imMovementActor = ctx.spawn(
ImMovementActor(ImMovementActor.Props(app, geom)),
"imMovementActor"
)
// ctx.pipeToSelf(
// app.enqueueF(() => ())(monix.execution.Scheduler.io("aege"))
// ) {
// case x =>
// println("SENDEDEEEEEd")
// XD
// }
// ctx.pipeToSelf(
// createPlayer(
// geom,
// app.getCamera(),
// schedulers.jme
// )
// ) {
// case x =>
// println(x)
// XD
// }
val subscribingActor = ctx.spawn(SubscribingActor(), "subscriber-1") val subscribingActor = ctx.spawn(SubscribingActor(), "subscriber-1")
val eventBus = val tickEventBus =
ctx.spawn(Behaviors.logMessages(EventBus[Events.Tick]()), "eventBus1") ctx.spawn(Behaviors.logMessages(EventBus[Events.Tick]()), "eventBus1")
eventBus ! EventBus.Subscribe(subscribingActor) tickEventBus ! EventBus.Subscribe(subscribingActor)
eventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self) tickEventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self)
// {
// app
// .getInputManager()
// .observableAction("Left")
// .map { action =>
// action.binding.name match {
// case "Left" => Task(println("Pressed left"))
// }
// }
// }
// binding match {
// case "Left" =>
def playerNodeFactory =
PlayerNode(
modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o",
cam = app.camera
) _
// (assetManager = app.assetManager)
{
lazy val playerNode = playerNodeFactory(app.assetManager)
lazy val actor = ctx.spawn(
ImMovementActor(ImMovementActor.Props(app, playerNode)),
"imMovementActor"
)
lazy val state = wire[PlayerMovementState]
app.stateManager.attach(state)
}
Thread.sleep(2000)
app app
.getStateManager() .getRootNode()
.attach( .depthFirst(s =>
new PlayerMovementState2( // s match {
movementActor, // case node: Node =>
movementActorTimer, // println("node" + s.getName() + " children " + node.getChildren())
imMovementActor, // case g: Geometry => println(s.getName())
geom // }
println(s.getName())
) )
)
app.start() println("----------------")
{
app
.getRootNode()
.observableDepthFirst()
.map(s => s.getName())
// .takeWhileInclusive(_.getName() != "level")
.onErrorHandle(e => e.getMessage())
.foreach(println)(schedulers.async)
}
println("----------------")
{
app
.getRootNode()
.observableBreadthFirst()
.map(s => s.getName())
// .takeWhileInclusive(_.getName() != "level")
.onErrorHandle(e => e.getMessage())
.foreach(println)(schedulers.async)
}
// app.start()
// Behaviors.same
Behaviors.receiveMessage { msg =>
msg match {
case XD =>
ctx.log.info("RECEEEEEIVED")
ctx.log.info(app.camera.toString())
Behaviors.same
case Stop =>
ctx.log.info("Received stop")
Behaviors.stopped Behaviors.stopped
}
}
}
}
object Methods {
def createPlayer(
geom: Geometry,
cam: Camera
): Node = {
val playerNode = new Node("PlayerNode")
lazy val camNode = new CameraNode("CameraNode", cam)
playerNode
.child(camNode)
.child(geom)
playerNode
}
def old() = {
// val movementActor =
// ctx.spawn(
// MovementActor(MovementActor.Props(app, geom)),
// "movementActor"
// // DispatcherSelector.fromConfig("jme-dispatcher")
// )
// val movementActorTimer = ctx.spawn(
// MovementActorTimer(movementActor),
// "movementActorTimer"
// )
} }
} }
@ -65,3 +179,12 @@ object SubscribingActor {
Behaviors.same Behaviors.same
} }
} }
// new PlayerMovementState(
// // movementActor,
// // movementActorTimer,
// imMovementActor,
// // geom,
// // camNode,
// playerNode
// // ctx.self
// )

View File

@ -0,0 +1,25 @@
package wow.doge.mygame.game
import cats.effect.Resource
import com.jme3.app.state.AppState
import com.jme3.system.AppSettings
import monix.bio.Task
// import wow.doge.mygame.executors.JMERunner
trait GameModule {
def gameAppResource(appStates: AppState*): Resource[Task, GameApp] =
Resource.liftF {
for {
app <- Task(new GameApp(appStates: _*))
_ <- Task {
val settings = new AppSettings(true)
// settings.setVSync(true)
settings.setFrameRate(144)
app.setSettings(settings)
// JMERunner.runner = app
app
}
} yield (app)
}
}

View File

@ -0,0 +1,13 @@
package wow.doge.mygame.game
import wow.doge.mygame.state.MyBaseState
class GameSystemsInitializer extends MyBaseState {
override protected def onEnable(): Unit = {}
override protected def onDisable(): Unit = {}
override protected def init(): Unit = {}
override def stop(): Unit = {}
}

View File

@ -0,0 +1,84 @@
package wow.doge.mygame.game
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout
import wow.doge.mygame.scriptsystem.ScriptCachingActor
object TestActor {
sealed trait Command
case object Test extends Command
private case object Done extends Command
import scala.concurrent.duration._
implicit val timeout = Timeout(15.seconds)
def apply(
// scriptCacheBehavior: Behavior[ScriptCachingActor.Command]
): Behavior[Command] =
Behaviors.setup { ctx =>
ctx.spawn(ScriptCachingActor(), "scriptCacher")
Behaviors.receiveMessage { msg =>
msg match {
case Test =>
Behaviors.same
case Done => Behaviors.same
}
}
}
def testKotlinScriptCompilation() = {
// ctx.ask(
// router,
// ScriptActor.Compile(
// // os.pwd / "some.sc",
// os.pwd / "src" / "main" / "resources" / "hello2.main.kts",
// _
// )
// ) {
// case Success(value) =>
// ctx.log.debug("Received Value")
// ctx.log.debug(value.toString())
// Done
// case Failure(exception) =>
// ctx.log.debug(s"Received Error ${exception.getMessage()}")
// Done
// }
}
def testTaskWrapper() = {
// val x = scriptStorer
// .askT(
// ScriptStoringActor
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// )(timeout, ctx.system.scheduler)
}
def testScriptCaching() = {
// ctx.ask(
// scriptStorer,
// ScriptStoringActor
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// ) {
// case Success(value) => {
// ctx.log.debug(value.toString())
// ctx.ask(
// scriptStorer,
// ScriptStoringActor
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
// ) {
// case Success(value) => {
// ctx.log.debug(value.toString())
// Done
// }
// case Failure(exception) =>
// ctx.log.debug(exception.getMessage())
// Done
// }
// Done
// }
// case Failure(exception) =>
// ctx.log.debug(exception.getMessage())
// Done
// }
}
}

View File

@ -1,4 +1,4 @@
package wow.doge.mygame.state; package wow.doge.mygame.game.appstates;
import com.jme3.app.state.AbstractAppState; import com.jme3.app.state.AbstractAppState;
import com.simsilica.es.EntityData; import com.simsilica.es.EntityData;

View File

@ -8,9 +8,9 @@ import wow.doge.mygame.implicits._
import com.jme3.renderer.Camera import com.jme3.renderer.Camera
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
trait CanMove[T] { trait CanMove[-A] {
def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
def move(inst: T, direction: ImVector3f): Unit def move(inst: A, direction: ImVector3f): Unit
} }
object ImMovementActor { object ImMovementActor {
@ -40,7 +40,10 @@ object ImMovementActor {
) )
def apply[T: CanMove](props: Props[T]): Behavior[Command] = def apply[T: CanMove](props: Props[T]): Behavior[Command] =
Behaviors.setup(ctx => new ImMovementActor(ctx, props).receive(State())) Behaviors.setup(ctx => {
ctx.log.info("Hello from MovementActor")
new ImMovementActor(ctx, props).receive(State())
})
} }
@ -71,12 +74,15 @@ class ImMovementActor[T](
val walkDir = val walkDir =
cm.getDirection(props.app.getCamera(), state.cardinalDir) cm.getDirection(props.app.getCamera(), state.cardinalDir)
if (walkDir != ImVector3f.ZERO) { if (walkDir != ImVector3f.ZERO) {
val tmp = walkDir * 2f val tmp = walkDir * 25f * tpf
props.app.enqueue(new Runnable { // props.app.enqueue(new Runnable {
override def run(): Unit = { // override def run(): Unit = {
// cm.move(props.movable, tmp)
// }
// })
props.app.enqueueF {
cm.move(props.movable, tmp) cm.move(props.movable, tmp)
} }
})
} }
Behaviors.same Behaviors.same
// receive(state = state.modify(_.walkDirection).setTo(walkDir)) // receive(state = state.modify(_.walkDirection).setTo(walkDir))

View File

@ -7,6 +7,7 @@ import com.jme3.scene.Node
import com.jme3.app.state.BaseAppState import com.jme3.app.state.BaseAppState
import com.simsilica.es.EntityData import com.simsilica.es.EntityData
import com.simsilica.es.base.DefaultEntityData import com.simsilica.es.base.DefaultEntityData
import com.jme3.scene.Spatial
trait MyBaseState extends BaseAppState { trait MyBaseState extends BaseAppState {
@ -26,17 +27,22 @@ trait MyBaseState extends BaseAppState {
} }
protected def init(): Unit protected def init(): Unit
protected def stop(): Unit
override protected def cleanup(app: Application): Unit = { override protected def cleanup(app: Application): Unit = {
entityData.close() entityData.close()
// stop()
} }
protected def getOrCreateNode(parent: Node, id: String) = protected def getChildOption(parent: Node, id: String) =
Option(parent.getChild(id)).fold { Option(parent.getChild(id))
val node = new Node(id)
protected def getOrCreateSpatial(parent: Node, id: String): Spatial =
Option(parent.getChild(id)).getOrElse {
val node: Spatial = new Node(id)
parent.attachChild(node) parent.attachChild(node)
node node
}(node => node.asInstanceOf[Node]) }
protected def enableStates(classes: Class[_ <: AppState]*) = protected def enableStates(classes: Class[_ <: AppState]*) =
setEnabledToStates(true, classes: _*) setEnabledToStates(true, classes: _*)

View File

@ -1,6 +1,5 @@
package wow.doge.mygame.state package wow.doge.mygame.state
import scala.concurrent.duration.DurationInt import scala.concurrent.duration.DurationInt
import com.jme3.input.InputManager import com.jme3.input.InputManager
@ -17,55 +16,64 @@ import com.jme3.scene.Geometry
import akka.actor.typed.scaladsl.TimerScheduler import akka.actor.typed.scaladsl.TimerScheduler
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import com.jme3.scene.Node
import com.jme3.syntax._
class PlayerMovementState2( class PlayerMovementState(
movementActor: ActorRef[MovementActor.Command], // movementActor: ActorRef[MovementActor.Command],
movementActorTimer: ActorRef[MovementActorTimer.Command], // movementActorTimer: ActorRef[MovementActorTimer.Command],
imMovementActor: ActorRef[ImMovementActor.Command], imMovementActor: ActorRef[ImMovementActor.Command],
geom: Geometry // geom: Geometry,
// camNode: CameraNode,
playerNode: Node
// gameAppActor: ActorRef[GameAppActor.Command]
) extends MyBaseState ) extends MyBaseState
with ActionListener { with ActionListener {
protected lazy val mat = MyMaterial( protected lazy val mat = MyMaterial(
assetManager = assetManager, assetManager = assetManager,
path = "Common/MatDefs/Misc/Unshaded.j3md" path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
) )
override protected[state] def onEnable(): Unit = {}
override protected[state] def onDisable(): Unit = {}
override protected def init(): Unit = { override protected def init(): Unit = {
setupKeys(inputManager) setupKeys(inputManager)
geom.setMaterial(mat) println("playermovementstate " + Thread.currentThread().getName())
rootNode.attachChild(geom)
// geom.setMaterial(mat)
// camNode.setControlDir(ControlDirection.SpatialToCamera)
// // lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera())
// // camNode.setCamera(simpleApp.getCamera())
// discard {
// playerNode
// .child(camNode)
// .child(geom)
// // playerNode.children(Seq(camNode, geom))
// }
discard { rootNode.child(playerNode) }
// camNode.setLocalTranslation(
// new Vector3f(0, 1.5f, 10)
// )
// camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y)
// movementActorTimer ! MovementActorTimer.Start(geom, cam) // movementActorTimer ! MovementActorTimer.Start(geom, cam)
// movementActorTimer ! MovementActorTimer.Start // movementActorTimer ! MovementActorTimer.Start
} }
// def system =
// simpleApp.getStateManager.getState(classOf[ActorSystemState]).system
// def player = system.actorSelection("/user/player") //.resolveOne(1.second)
var lastDir: Vector3f = Vector3f.UNIT_X
override def update(tpf: Float) = { override def update(tpf: Float) = {
// val direction = new Vector3f()
// direction.multLocal(10 * tpf)
// if (direction.length() > 0f) {
// // player ! PlayerMove(direction)
// lastDir = direction.normalize
// }
// if (shoot) {
// shoot = false
// // player ! Shoot(lastDir)
// }
// movementActor ! MovementActor.Tick(tpf, geom, cam) // movementActor ! MovementActor.Tick(tpf, geom, cam)
imMovementActor ! ImMovementActor.Tick(tpf) imMovementActor ! ImMovementActor.Tick(tpf)
// movementActorTimer ! MovementActorTimer.Update(tpf) // movementActorTimer ! MovementActorTimer.Update(tpf)
} }
override def stop(): Unit = {}
// override protected def cleanup(app: Application): Unit = {
// // gameAppActor ! GameAppActor.Stop
// super.cleanup(app)
// }
def setupKeys(inputManager: InputManager) = { def setupKeys(inputManager: InputManager) = {
inputManager inputManager
@ -79,33 +87,32 @@ class PlayerMovementState2(
// new KeyTrigger(KeyInput.KEY_D), // new KeyTrigger(KeyInput.KEY_D),
new KeyTrigger(KeyInput.KEY_RIGHT) new KeyTrigger(KeyInput.KEY_RIGHT)
) )
inputManager.addMapping( .withMapping(
"Up", "Up",
// new KeyTrigger(KeyInput.KEY_W), // new KeyTrigger(KeyInput.KEY_W),
new KeyTrigger(KeyInput.KEY_UP) new KeyTrigger(KeyInput.KEY_UP)
) )
inputManager.addMapping( .withMapping(
"Down", "Down",
// new KeyTrigger(KeyInput.KEY_S), // new KeyTrigger(KeyInput.KEY_S),
new KeyTrigger(KeyInput.KEY_DOWN) new KeyTrigger(KeyInput.KEY_DOWN)
) )
inputManager.addMapping( .withMapping(
"Space", "Space",
new KeyTrigger(KeyInput.KEY_SPACE), new KeyTrigger(KeyInput.KEY_SPACE),
new KeyTrigger(KeyInput.KEY_H) new KeyTrigger(KeyInput.KEY_H)
) )
inputManager.addMapping( .withMapping(
"Reset", "Reset",
new KeyTrigger(KeyInput.KEY_R), new KeyTrigger(KeyInput.KEY_R),
new KeyTrigger(KeyInput.KEY_RETURN) new KeyTrigger(KeyInput.KEY_RETURN)
) )
inputManager
.withListener(this, "Left") .withListener(this, "Left")
.withListener(this, "Right") .withListener(this, "Right")
inputManager.addListener(this, "Up") .withListener(this, "Up")
inputManager.addListener(this, "Down") .withListener(this, "Down")
inputManager.addListener(this, "Space") .withListener(this, "Space")
inputManager.addListener(this, "Reset") .withListener(this, "Reset")
} }
def onAction(binding: String, value: Boolean, tpf: Float) = def onAction(binding: String, value: Boolean, tpf: Float) =
@ -118,6 +125,10 @@ class PlayerMovementState2(
case _ => case _ =>
} }
override protected def onEnable(): Unit = {}
override protected def onDisable(): Unit = {}
} }
final case class CardinalDirection( final case class CardinalDirection(

View File

@ -4,7 +4,6 @@ import ammonite.runtime.Storage.Folder
import ammonite.main.Defaults import ammonite.main.Defaults
import ammonite.Main import ammonite.Main
import javax.script.ScriptEngine import javax.script.ScriptEngine
import com.jme3.app.Application
import com.jme3.app.state.AppState import com.jme3.app.state.AppState
import akka.actor.typed.scaladsl.AbstractBehavior import akka.actor.typed.scaladsl.AbstractBehavior
import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.ActorContext
@ -31,10 +30,11 @@ class ScriptingEngineState(
// _ // _
// ) // )
// ) // )
override def stop(): Unit = {}
override protected def cleanup(app: Application): Unit = { // override protected def cleanup(app: Application): Unit = {
// actorSystem.terminate() // // actorSystem.terminate()
} // }
// override protected def initialize(app: Application): Unit = { // override protected def initialize(app: Application): Unit = {
// super.initialize(app) // super.initialize(app)

View File

@ -1,6 +1,5 @@
package wow.doge.mygame.state package wow.doge.mygame.state
import com.jme3.app.Application
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.components.TestComponent import wow.doge.mygame.components.TestComponent
import com.jme3.scene.shape.Box import com.jme3.scene.shape.Box
@ -32,7 +31,7 @@ class TestAppState(
val mat = MyMaterial( val mat = MyMaterial(
assetManager = assetManager, assetManager = assetManager,
path = "Common/MatDefs/Misc/Unshaded.j3md" path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
) )
geom.foreach(e => { geom.foreach(e => {
e.setMaterial(mat) e.setMaterial(mat)
@ -45,14 +44,15 @@ class TestAppState(
geom.foreach(_.move(new Vector3f(0, 1 * tpf, 0))) geom.foreach(_.move(new Vector3f(0, 1 * tpf, 0)))
} }
override def cleanup(app: Application): Unit = { // override def cleanup(app: Application): Unit = {
// _entity.map(_.close()) // // _entity.map(_.close())
// _entity = None // // _entity = None
} // }
override def onEnable(): Unit = {} override def onEnable(): Unit = {}
override def onDisable(): Unit = {} override def onDisable(): Unit = {}
override def stop(): Unit = {}
} }
@ -61,10 +61,10 @@ object MyMaterial {
color: String = "Color", color: String = "Color",
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue, colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
assetManager: AssetManager, assetManager: AssetManager,
path: String path: os.RelPath
): Material = { ): Material = {
val mat = val mat =
new Material(assetManager, path) new Material(assetManager, path.toString())
mat.setColor(color, colorType) mat.setColor(color, colorType)
mat mat
} }

View File

@ -0,0 +1,94 @@
package wow.doge.mygame.game.nodes
import com.jme3.scene.Node
import com.jme3.scene.CameraNode
import com.jme3.scene.Geometry
import com.jme3.renderer.Camera
import wow.doge.mygame.implicits._
import com.jme3.asset.AssetManager
import wow.doge.mygame.state.MyMaterial
import com.jme3.math.Vector3f
import com.jme3.scene.control.CameraControl.ControlDirection
import com.jme3.syntax._
import com.jme3.scene.shape.Box
// class PlayerNode(val name: String) extends Node(name) {}
object PlayerNode {
def defaultMesh() = {
lazy val b = new Box(1, 1, 1)
lazy val geom = new Geometry("playerMesh", b)
geom
}
def defaultTexture(assetManager: AssetManager) =
MyMaterial(
assetManager = assetManager,
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
)
def apply(
modelPath: os.RelPath,
cam: Camera
)(assetManager: AssetManager) = {
lazy val playerNode = new Node("PlayerNode")
lazy val camNode = new CameraNode("CameraNode", cam)
// lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera())
// camNode.setCamera(simpleApp.getCamera())
val playerModel: Node =
assetManager.loadModel(modelPath).asInstanceOf[Node]
discard {
playerNode
.child(camNode)
.child(playerModel)
// playerNode.children(Seq(camNode, geom))
}
{
camNode.setControlDir(ControlDirection.SpatialToCamera)
camNode.setLocalTranslation(
new Vector3f(0, 1.5f, 10)
)
camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y)
}
playerNode
}
def apply(
mesh: Geometry = defaultMesh(),
texturePath: os.RelPath =
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md",
cam: Camera
)(assetManager: AssetManager): Node = {
lazy val playerNode = new Node("PlayerNode")
lazy val camNode = new CameraNode("CameraNode", cam)
{
val mat = MyMaterial(
assetManager = assetManager,
path = texturePath
)
mesh.setMaterial(mat)
}
// lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera())
// camNode.setCamera(simpleApp.getCamera())
discard {
playerNode
.child(camNode)
.child(mesh)
// playerNode.children(Seq(camNode, geom))
}
{
camNode.setControlDir(ControlDirection.SpatialToCamera)
camNode.setLocalTranslation(
new Vector3f(0, 1.5f, 10)
)
camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y)
}
playerNode
}
}

View File

@ -12,7 +12,7 @@ import com.simsilica.es.EntityId
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.util.Timeout import akka.util.Timeout
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import monix.eval.Task import monix.bio.Task
import com.jme3.input.InputManager import com.jme3.input.InputManager
import com.jme3.input.controls.Trigger import com.jme3.input.controls.Trigger
import com.jme3.input.controls.InputListener import com.jme3.input.controls.InputListener
@ -22,8 +22,76 @@ import com.jme3.scene.Geometry
import wow.doge.mygame.state.CardinalDirection import wow.doge.mygame.state.CardinalDirection
import wow.doge.mygame.state.CanMove import wow.doge.mygame.state.CanMove
import com.jme3.renderer.Camera import com.jme3.renderer.Camera
import scala.jdk.CollectionConverters._
import wow.doge.mygame.utils.JFXConsoleStreamable
import com.jme3.app.Application
import java.util.concurrent.Callable
import scala.concurrent.Future
import scala.concurrent.ExecutionContext
import com.jme3.scene.SceneGraphVisitor
import monix.reactive.Observable
import com.jme3.asset.AssetManager
import com.jme3.asset.AssetLocator
import com.jme3.input.controls.ActionListener
import monix.reactive.OverflowStrategy
import monix.execution.Ack
import monix.execution.Cancelable
import monix.execution.cancelables.SingleAssignCancelable
import com.jme3.input.Action
import com.jme3.bullet.PhysicsSpace
import com.jme3.bullet.collision.PhysicsCollisionListener
import com.jme3.bullet.collision.PhysicsCollisionEvent
import com.jme3.bullet.PhysicsTickListener
import monix.reactive.observers.Subscriber
import monix.execution.Ack.Continue
import monix.execution.Ack.Stop
case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
case class PhysicsTickEvent(space: PhysicsSpace, tpf: Float)
package object implicits { package object implicits {
type PrePhysicsTickEvent = PhysicsTickEvent
type PhysicsTickObservable =
Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
implicit class JMEAppExt(val app: Application) extends AnyVal {
/**
* Blocking task. Execute on a thread pool meant for blocking operations.
* Prefer [[wow.doge.mygame.implicits.JMEAppExt#enqueueT]] instead.
*
* @param cb
* @param ec
* @return
*/
def enqueueF[T](cb: () => T)(implicit ec: ExecutionContext): Future[T] =
Future {
app
.enqueue(new Callable[T]() {
override def call(): T = cb()
})
.get()
}
/**
* Blocking task. Execute on a thread pool meant for blocking operations.
* Same as enqueue, but returns a Monix Task instead of Future
* @param cb
* @param ec
* @return
*/
def enqueueL[T](cb: () => T): Task[T] =
Task
.deferFutureAction(implicit s => enqueueF(cb))
def enqueueF[T](cb: => T) =
app.enqueue(new Runnable {
override def run() = cb
})
def enqueueT(cb: => Unit) =
Task(enqueueF(cb))
}
implicit class StateManagerExt(val sm: AppStateManager) extends AnyVal { implicit class StateManagerExt(val sm: AppStateManager) extends AnyVal {
def state[S <: AppState]()(implicit c: ClassTag[S]): S = def state[S <: AppState]()(implicit c: ClassTag[S]): S =
sm.getState(c.runtimeClass.asInstanceOf[Class[S]]) sm.getState(c.runtimeClass.asInstanceOf[Class[S]])
@ -32,14 +100,126 @@ package object implicits {
implicit class SimpleApplicationExt(val sa: SimpleApplication) implicit class SimpleApplicationExt(val sa: SimpleApplication)
extends AnyVal { extends AnyVal {
def stateManager() = sa.getStateManager() def stateManager: AppStateManager = sa.getStateManager()
def inputManager: InputManager = sa.getInputManager()
def assetManager: AssetManager = sa.getAssetManager()
def guiNode = sa.getGuiNode()
def flyCam = Option(sa.getFlyByCamera())
def camera = sa.getCamera()
def viewPort = sa.getViewPort()
} }
implicit class NodeExt(val n: Node) extends AnyVal { implicit class NodeExt(val n: Node) extends AnyVal {
/**
* Attaches the given child
*
* @param s
* @return
*/
def child(s: Spatial): Node = { def child(s: Spatial): Node = {
n.attachChild(s) n.attachChild(s)
n n
} }
/**
* Gets the list of children as a scala collection
*
* @return
*/
// def children = n.getChildren().asScala.toSeq
def children = Observable.fromIterable(n.getChildren().asScala)
/**
* Attach given children
*
* @param lst
*/
def children(lst: Iterable[Spatial]): Unit = {
for (c <- lst) n.child(c)
}
def depthFirst(cb: Spatial => Unit) =
n.depthFirstTraversal(new SceneGraphVisitor() {
override def visit(s: Spatial) = cb(s)
})
def observableDepthFirst(): Observable[Spatial] = {
def loop(
subscriber: Subscriber[Spatial],
spatial: Spatial
): Task[Unit] = {
//spatial can be either a node or a geometry, but it's not a sealed trait
spatial match {
case node: Node =>
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Ack.Continue => {
//modifying a node's children list is forbidden
val children = node.getChildren().asScala.to(LazyList)
if (!children.isEmpty) {
Task.sequence(
children.map(c => loop(subscriber, c))
) >> Task.unit
} else {
Task.unit
}
}
case Ack.Stop => Task.unit
}
//geomtries do not/cannot have children
case g: Geometry =>
Task.deferFuture(subscriber.onNext(g)) >> Task.unit
case _ => Task.unit
}
}
Observable.create(OverflowStrategy.Unbounded) { sub =>
implicit val sched = sub.scheduler
loop(sub, n).runToFuture
}
}
def breadthFirst(cb: Spatial => Unit) =
n.breadthFirstTraversal(new SceneGraphVisitor() {
override def visit(s: Spatial) = cb(s)
})
def observableBreadthFirst(): Observable[Spatial] = {
def loop(
subscriber: Subscriber[Spatial],
spatials: LazyList[Spatial]
): Task[Unit] = {
// spatial can be either a node or a geometry, but it's not a sealed trait
spatials match {
case head #:: tail =>
head match {
case g: Geometry =>
Task.deferFuture(subscriber.onNext(g)).flatMap {
case Continue =>
loop(subscriber, tail)
case Stop => Task.unit
}
case node: Node =>
val children = node.getChildren().asScala.to(LazyList)
Task.deferFuture(subscriber.onNext(node)).flatMap {
case Continue =>
loop(subscriber, tail #::: children)
case Stop => Task.unit
}
// case _ => loop(subscriber, tail)
}
case LazyList() => Task.unit
}
}
Observable.create(OverflowStrategy.Unbounded) { sub =>
implicit val sched = sub.scheduler
loop(sub, LazyList(n)).runToFuture
}
}
} }
implicit class EntityDataExt(val ed: EntityData) extends AnyVal { implicit class EntityDataExt(val ed: EntityData) extends AnyVal {
@ -81,6 +261,93 @@ package object implicits {
inputManager.addListener(listener, mappings: _*) inputManager.addListener(listener, mappings: _*)
inputManager inputManager
} }
def observableAction(mappingNames: String*): Observable[ActionEvent] = {
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val al = new ActionListener {
override def onAction(
binding: String,
value: Boolean,
tpf: Float
): Unit = {
if (
sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
)
c.cancel()
}
}
inputManager.addListener(al, mappingNames: _*)
c := Cancelable(() => inputManager.removeListener(al))
c
}
}
}
implicit class PhysicsSpaceExt(val space: PhysicsSpace) extends AnyVal {
def collisionObservable(): Observable[PhysicsCollisionEvent] = {
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val cl = new PhysicsCollisionListener {
override def collision(event: PhysicsCollisionEvent): Unit = {
if (sub.onNext(event) == Ack.Stop)
c.cancel()
}
}
space.addCollisionListener(cl)
c := Cancelable(() => space.removeCollisionListener(cl))
c
}
}
def physicsTickObservable(): PhysicsTickObservable = {
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val cl = new PhysicsTickListener {
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Left(event)) == Ack.Stop)
c.cancel()
}
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
val event = PhysicsTickEvent(space, tpf)
if (sub.onNext(Right(event)) == Ack.Stop)
c.cancel()
}
}
space.addTickListener(cl)
c := Cancelable(() => space.removeTickListener(cl))
c
}
}
}
implicit class AssetManagerExt(val am: AssetManager) extends AnyVal {
def registerLocator(
assetPath: os.RelPath,
locator: Class[_ <: AssetLocator]
): Unit = {
am.registerLocator(assetPath.toString(), locator)
}
def loadModel(assetPath: os.RelPath): Spatial = {
am.loadModel(assetPath.toString())
}
} }
implicit class Vector3fExt(val v: Vector3f) extends AnyVal { implicit class Vector3fExt(val v: Vector3f) extends AnyVal {
@ -134,14 +401,52 @@ package object implicits {
// ev.+ // ev.+
// } // }
implicit val implCanMoveForGeom = new CanMove[Geometry] { implicit val implCanMoveForGeom = new CanMove[Spatial] {
override def move(inst: Spatial, direction: ImVector3f): Unit = {
// val v = inst.getLocalTranslation()
// inst match {
// case n: Node => println(n.getChildren())
// case _ =>
// }
inst.move(direction.mutable)
}
override def getDirection( override def getDirection(
cam: Camera, cam: Camera,
cardinalDir: CardinalDirection cardinalDir: CardinalDirection
): ImVector3f = { ): ImVector3f = {
val camDir = // val camDir =
cam.getDirection().immutable * 0.6f // cam.getDirection().immutable * 0.6f
val camLeft = cam.getLeft().immutable * 0.4f // val camLeft = cam.getLeft().immutable * 0.4f
// val zero = ImVector3f.ZERO
// val dir = cardinalDir
// val walkDir = {
// val mutWalkDir = new Vector3f()
// if (dir.left) {
// // ctx.log.trace("left")
// mutWalkDir += (zero + camLeft).mutable
// }
// if (dir.right) {
// // ctx.log.trace("right")
// mutWalkDir += (zero + -camLeft).mutable
// }
// if (dir.up) {
// // ctx.log.trace("up")
// mutWalkDir += (zero + camDir).mutable
// }
// if (dir.down) {
// // ctx.log.trace("down")
// mutWalkDir += (zero + -camDir).mutable
// }
// mutWalkDir.immutable
// }
// walkDir
// val camDir =
// cam.getDirection().immutable * 0.6f
// val camLeft = cam.getLeft().immutable * 0.4f
val zero = ImVector3f.ZERO val zero = ImVector3f.ZERO
val dir = cardinalDir val dir = cardinalDir
@ -149,27 +454,61 @@ package object implicits {
val mutWalkDir = new Vector3f() val mutWalkDir = new Vector3f()
if (dir.left) { if (dir.left) {
// ctx.log.trace("left") // ctx.log.trace("left")
mutWalkDir += (zero + camLeft).mutable mutWalkDir += (zero + ImVector3f(-1, 0, 0)).mutable
} }
if (dir.right) { if (dir.right) {
// ctx.log.trace("right") // ctx.log.trace("right")
mutWalkDir += (zero + -camLeft).mutable mutWalkDir += (zero + ImVector3f(1, 0, 0)).mutable
} }
if (dir.up) { if (dir.up) {
// ctx.log.trace("up") // ctx.log.trace("up")
mutWalkDir += (zero + camDir).mutable mutWalkDir += (zero + ImVector3f(0, 0, -1)).mutable
} }
if (dir.down) { if (dir.down) {
// ctx.log.trace("down") // ctx.log.trace("down")
mutWalkDir += (zero + -camDir).mutable mutWalkDir += (zero + ImVector3f(0, 0, 1)).mutable
} }
mutWalkDir.immutable mutWalkDir.immutable
} }
walkDir walkDir
} }
override def move(geom: Geometry, direction: ImVector3f): Unit = {
val v = geom.getLocalTranslation()
geom.setLocalTranslation(v += direction.mutable)
} }
implicit val implJFXConsoleStreamForTextArea =
new JFXConsoleStreamable[scalafx.scene.control.TextArea] {
override def println(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text + "\n")
override def print(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text)
} }
// val TasktoUIO = new FunctionK[Task, UIO] {
// def apply[T](f: Task[T]): UIO[T] =
// f.hideErrors
// }
} }
// Observable.create(OverflowStrategy.Unbounded) { sub =>
// // val c = SingleAssignCancelable()
// val visitor = new SceneGraphVisitor {
// override def visit(s: Spatial): Unit = {
// sub.onNext(s)
// // if (sub.onNext(s) == Ack.Stop)
// // c.cancel()
// }
// }
// n.depthFirstTraversal(visitor)
// // c := Cancelable(() => ???)
// // c
// Cancelable.empty

View File

@ -1,4 +1,4 @@
package wow.doge.mygame.math package wow.doge.mygame.math;
case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f) case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f)

View File

@ -0,0 +1,142 @@
package wow.doge.mygame.subsystems.moddingsystem
import java.io.FileNotFoundException
import scala.collection.View
import scala.collection.immutable.ArraySeq
import scala.util.Try
import cats.implicits._
import io.circe._
import io.circe.generic.semiauto._
import io.circe.parser._
import monix.bio.IO
import monix.bio.UIO
import java.nio.file.NoSuchFileException
import io.circe.generic.JsonCodec
@JsonCodec
case class Test1(hello1: String, hello2: String)
@JsonCodec
case class Test2(hello1: String)
case class Plugin(name: String, priority: Int)
object Plugin {
implicit val pluginFormat: Decoder[Plugin] = deriveDecoder
}
object ModdingSystem {
sealed trait Error extends Serializable with Product
case class CouldNotDecode(cause: String) extends Error
case class ParseFailure(cause: String) extends Error
case class FileNotFound(name: String) extends Error
case object GenericError extends Error
def readPluginsList(dir: os.Path): Try[Either[Error, ArraySeq[Plugin]]] =
Try(
parse(os.read(dir / "plugins.json"))
.map(
_.as[ArraySeq[Plugin]]
.leftMap(e => CouldNotDecode(e.getMessage()))
)
.leftMap((e: ParsingFailure) => ParseFailure(e.message))
.flatten
)
// .toValidated
def findPluginFiles(dir: os.Path): View[os.Path] =
os.list(dir)
.view
.filter(f => f.ext == "json" && f.baseName.endsWith("plugin"))
def findAndReadPluginFiles(
dir: os.Path,
plugins: ArraySeq[Plugin]
) =
plugins
.sortBy(_.priority)
.view
.map(p =>
p ->
Either
.catchNonFatal {
val path = dir / os.RelPath(p.name + ".plugin.json")
os.read(path)
}
.leftMap {
case _: FileNotFoundException =>
FileNotFound(p.name)
case _: NoSuchFileException => FileNotFound(p.name)
case e => GenericError
}
)
.partitionMap {
case (p, either) =>
either match {
case Left(value) => Left(p -> value)
case Right(value) => Right(p -> value)
}
}
def readPluginFiles(filePaths: View[os.Path]) = {
filePaths.map(path => os.read(path))
}
def parsePluginFiles(files: View[(Plugin, String)]) =
files
.map {
case (p, s) => p -> parse(s)
}
.partitionMap {
case (p, Left(value)) => Left(p -> value)
case (p, Right(value)) => Right(p -> value)
}
def mergePluginData(plugins: View[(Plugin, Json)]) = {
plugins.foldLeft(Json.fromString("empty")) {
case (json, that) =>
that match {
case (p, io.circe.Json.Null) => json //ignore null values
case (p, value) => json.deepMerge(value)
}
}
}
// def test =
// for {
// filePaths <- Task(findPluginFiles(os.pwd))
// files <- Task(readPluginFiles(filePaths))
// (failures, successes) <- Task(parsePluginFiles(files))
// merged <- Task(mergePluginData(successes))
// _ <- Task {
// println(s"Successes = ${successes.to(Seq)}")
// println(s"Failure = ${failures.to(Seq)}")
// println(s"Merged = $merged")
// }
// } yield ()
def test(wd: os.Path = os.pwd) =
for {
plugins <- IO.fromTryEither(readPluginsList(wd))
(readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins))
(parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
res <- UIO(mergePluginData(parseSuccesses))
f <- UIO {
println(s"Read Successes = ${readSuccesses.to(Seq)}")
println(s"Read Failures = ${readFailures.to(Seq)}")
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
println(s"Parse Failures = ${parseFailures.to(Seq)}")
println(s"Merged = $res")
}
} yield ()
// monix.eval.Task.deferAction(implicit s =>
// ModdingSystem
// .test()
// .leftMap(e => new Throwable(e.toString()))
// .to[monix.eval.Task]
// )
// def test3(wd: os.Path = os.pwd) = {
// (readPluginsList(os.pwd).toValidatedNec)
// }
;
}

View File

@ -3,7 +3,6 @@ package wow.doge.mygame.state
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import ammonite.Main import ammonite.Main
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import com.jme3.app.state.AppState
import ammonite.runtime.Storage.Folder import ammonite.runtime.Storage.Folder
import ammonite.main.Defaults import ammonite.main.Defaults
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
@ -11,18 +10,17 @@ import akka.actor.typed.scaladsl.ActorContext
import ammonite.util.Res.Success import ammonite.util.Res.Success
import javax.script.ScriptEngine import javax.script.ScriptEngine
import javax.script.ScriptEngineManager import javax.script.ScriptEngineManager
import scala.util.Try import groovy.util.GroovyScriptEngine
import cats.implicits._ import cats.implicits._
object ScriptActor { object ScriptActor {
sealed trait Result type Kotlin
final case class AppStateResult(state: AppState) extends Result type MyScriptEngine[T] = ScriptEngine
final case class Error(reason: String) extends Result type KotlinScriptEngine = MyScriptEngine[Kotlin]
final case class Error(reason: String)
sealed trait Command sealed trait Command
final case class Compile(path: os.Path, sender: ActorRef[Result])
extends Command
final case class CompileAny( final case class CompileAny(
path: os.Path, path: os.Path,
result: ActorRef[Either[Error, Any]] result: ActorRef[Either[Error, Any]]
@ -38,19 +36,28 @@ object ScriptActor {
) )
) )
def defaultKotlinRunner(): ScriptEngine = { def defaultKotlinRunner(): KotlinScriptEngine = {
val manager = new ScriptEngineManager() val manager = new ScriptEngineManager()
val engine = manager.getEngineByExtension("main.kts") val engine = manager.getEngineByExtension("main.kts")
engine engine
} }
def defaultGroovyRunner(): GroovyScriptEngine =
new GroovyScriptEngine(os.pwd.toString())
def apply( def apply(
scalaRunner: Main = defaultScalaRunner(), scalaRunner: Main = defaultScalaRunner(),
kotlinRunner: ScriptEngine = defaultKotlinRunner() kotlinRunner: KotlinScriptEngine = defaultKotlinRunner(),
groovyRunner: GroovyScriptEngine = defaultGroovyRunner()
// parent: ActorRef[ScriptStoringActor.Command] // parent: ActorRef[ScriptStoringActor.Command]
): Behavior[ScriptActor.Command] = ): Behavior[ScriptActor.Command] =
Behaviors.setup(ctx => Behaviors.setup(ctx =>
new ScriptActor(scalaRunner, kotlinRunner, ctx).receiveMessage new ScriptActor(
scalaRunner,
kotlinRunner,
groovyRunner,
ctx
).receiveMessage
) )
sealed trait ScriptType sealed trait ScriptType
@ -80,20 +87,31 @@ object ScriptActor {
case _ => Left(Error("Failed to run script")) case _ => Left(Error("Failed to run script"))
} }
def runKotlin(path: os.Path, kotlinRunner: ScriptEngine): Either[Error, Any] = def runKotlin(
Try(kotlinRunner.eval(os.read(path))).toEither.leftMap(t => path: os.Path,
Error(t.getMessage()) kotlinRunner: KotlinScriptEngine
) ): Either[Error, Any] =
Either
.catchNonFatal(kotlinRunner.eval(os.read(path)))
.leftMap(t => Error(t.getMessage()))
def runGroovy(path: os.Path): Either[Error, Any] = def runGroovy(
Left(Error("Not implemented yet")) 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( class ScriptActor(
val scalaRunner: Main, val scalaRunner: ammonite.Main,
val kotlinRunner: ScriptEngine, val kotlinRunner: ScriptActor.KotlinScriptEngine,
// parent: ActorRef[ScriptStoringActor.Command], val groovyRunner: GroovyScriptEngine,
context: ActorContext[ScriptActor.Command] context: ActorContext[ScriptActor.Command]
) { ) {
import ScriptActor._ import ScriptActor._
@ -101,57 +119,24 @@ class ScriptActor(
def receiveMessage: Behavior[Command] = def receiveMessage: Behavior[Command] =
Behaviors.receiveMessage { msg => Behaviors.receiveMessage { msg =>
msg match { msg match {
case Compile(path, sender) =>
context.log.debug(s"Received $path")
val res = getScript(path)
sender ! res
Behaviors.same
// case CompileScripts(sender, paths) =>
case CompileAny(path, requester) => case CompileAny(path, requester) =>
context.log.debug(s"Received $path") context.log.debug(s"Received $path")
val res = getScript2(path) val res = getScript(path)
context.log.debug(s"result = $res") context.log.debug(s"result = $res")
requester ! res requester ! res
// parent ! ScriptStoringActor.Put(path, res)
Behaviors.same Behaviors.same
} }
} }
def getScript(path: os.Path) = { def getScript(path: os.Path): Either[Error, Any] =
val res = determineScriptType(path) match { determineScriptType(path) match {
case Right(ScalaType) => runScala(path, scalaRunner) case Right(ScalaType) =>
case Right(KotlinType) => runKotlin(path, kotlinRunner) runScala(path, scalaRunner).flatMap(ensureReturnedObjectNotNull)
case Right(GroovyType) => runGroovy(path) case Right(KotlinType) =>
runKotlin(path, kotlinRunner).flatMap(ensureReturnedObjectNotNull)
case Right(GroovyType) =>
runGroovy(path, groovyRunner).flatMap(ensureReturnedObjectNotNull)
case l @ Left(err) => l case l @ Left(err) => l
} }
res match {
case Left(err) => err
case Right(obj) =>
obj match {
case s: MyBaseState => AppStateResult(s)
case _ => Error("Class in script does not match known types")
}
}
}
def getScript2(path: os.Path): Either[Error, Any] = {
val res = determineScriptType(path) match {
case Right(ScalaType) => runScala(path, scalaRunner)
case Right(KotlinType) => runKotlin(path, kotlinRunner)
case Right(GroovyType) => runGroovy(path)
case l @ Left(err) => l
}
// res match {
// case Left(err) => err
// case Right(obj) =>
// obj match {
// case s: MyBaseState => AppStateResult(s)
// case _ => Error("Class in script does not match known types")
// }
// }
res
}
} }

View File

@ -17,9 +17,9 @@ object ScriptCachingActor {
/** /**
* aka script representation * aka script representation
*/ */
type ScriptRepr = Any type ScriptObject = Any
type ScriptsMap = Map[os.Path, ScriptRepr] type ScriptsMap = Map[os.Path, ScriptObject]
type ScriptResult = Either[ScriptActor.Error, ScriptRepr] type ScriptResult = Either[ScriptActor.Error, ScriptObject]
sealed trait Command sealed trait Command
final case class Get( final case class Get(
@ -27,10 +27,11 @@ object ScriptCachingActor {
requester: ActorRef[ScriptResult] requester: ActorRef[ScriptResult]
) extends Command ) extends Command
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
final case class Put(scriptPath: os.Path, script: ScriptRepr) extends Command final case class Put(scriptPath: os.Path, script: ScriptObject)
private final case object NoOp extends Command extends Command
private[scriptsystem] final case object NoOp extends Command
private final case class DelegateToChild( private[scriptsystem] final case class DelegateToChild(
scriptActor: ActorRef[ScriptActor.Command], scriptActor: ActorRef[ScriptActor.Command],
scriptPath: os.Path, scriptPath: os.Path,
requester: ActorRef[ScriptResult] requester: ActorRef[ScriptResult]
@ -41,6 +42,7 @@ object ScriptCachingActor {
scriptActor: ActorRef[ScriptActor.Command] scriptActor: ActorRef[ScriptActor.Command]
) )
final case class State(scriptsMap: ScriptsMap) final case class State(scriptsMap: ScriptsMap)
def apply(state: State = State(Map.empty)): Behavior[Command] = def apply(state: State = State(Map.empty)): Behavior[Command] =
Behaviors.logMessages { Behaviors.logMessages {
Behaviors.setup { ctx => Behaviors.setup { ctx =>
@ -49,58 +51,12 @@ object ScriptCachingActor {
new ScriptCachingActor(Props(ctx, scriptsRouter)).receiveMessage(state) new ScriptCachingActor(Props(ctx, scriptsRouter)).receiveMessage(state)
} }
} }
private def getOrCompileScript(
ctx: ActorContext[Command],
scriptPath: os.Path,
scriptsMap: ScriptsMap,
scriptActor: ActorRef[ScriptActor.Command],
requester: ActorRef[ScriptResult]
) = {
scriptsMap
.get(scriptPath)
.fold {
ctx.log.debug("Delegating to child")
ctx.self ! DelegateToChild(
scriptActor,
scriptPath,
requester
)
} { s =>
ctx.log.debug("Getting script from cache")
requester ! Right(s)
}
}
private def askChildForScriptCompilation(
ctx: ActorContext[Command],
scriptActor: ActorRef[ScriptActor.Command],
scriptPath: os.Path,
requester: ActorRef[ScriptResult]
)(implicit timeout: Timeout) = {
ctx.ask(scriptActor, ScriptActor.CompileAny(scriptPath, _)) {
case Success(value) =>
requester ! value
value.fold(
err => {
ctx.log.error(err.reason)
NoOp
},
res => {
Put(scriptPath, res)
}
)
case Failure(exception) => {
ctx.log.error(exception.getMessage())
NoOp
}
}
}
} }
class ScriptCachingActor(props: ScriptCachingActor.Props) { class ScriptCachingActor(props: ScriptCachingActor.Props) {
import com.softwaremill.quicklens._ import com.softwaremill.quicklens._
import ScriptCachingActor._ import ScriptCachingActor._
import Methods._
def receiveMessage(state: State): Behavior[Command] = def receiveMessage(state: State): Behavior[Command] =
Behaviors.receiveMessage { msg => Behaviors.receiveMessage { msg =>
msg match { msg match {
@ -134,7 +90,7 @@ class ScriptCachingActor(props: ScriptCachingActor.Props) {
props.ctx.log.debug(s"Putting $script at path $scriptPath") props.ctx.log.debug(s"Putting $script at path $scriptPath")
val newState = val newState =
state.modify(_.scriptsMap).using(_ + (scriptPath -> script)) state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
props.ctx.log.debug(newState.toString()) props.ctx.log.trace(newState.toString())
receiveMessage(state = newState) receiveMessage(state = newState)
case NoOp => Behaviors.same case NoOp => Behaviors.same
@ -152,15 +108,54 @@ object ScriptActorPool {
.supervise(ScriptActor()) .supervise(ScriptActor())
.onFailure[Exception](SupervisorStrategy.restart) .onFailure[Exception](SupervisorStrategy.restart)
) )
}
// def apply(
// poolSize: Int, private[scriptsystem] object Methods {
// parent: ActorRef[ScriptStoringActor.Command] import ScriptCachingActor._
// ): PoolRouter[ScriptActor.Command] = def getOrCompileScript(
// Routers.pool(poolSize = poolSize)( ctx: ActorContext[Command],
// // make sure the workers are restarted if they fail scriptPath: os.Path,
// Behaviors scriptsMap: ScriptsMap,
// .supervise(ScriptActor(parent = parent)) scriptActor: ActorRef[ScriptActor.Command],
// .onFailure[Exception](SupervisorStrategy.restart) requester: ActorRef[ScriptResult]
// ) ) = {
scriptsMap
.get(scriptPath)
.fold {
ctx.log.debug("Delegating to child")
ctx.self ! DelegateToChild(
scriptActor,
scriptPath,
requester
)
} { s =>
ctx.log.debug("Getting script from cache")
requester ! Right(s)
}
}
def askChildForScriptCompilation(
ctx: ActorContext[Command],
scriptActor: ActorRef[ScriptActor.Command],
scriptPath: os.Path,
requester: ActorRef[ScriptResult]
)(implicit timeout: Timeout) = {
ctx.ask(scriptActor, ScriptActor.CompileAny(scriptPath, _)) {
case Success(value) =>
requester ! value
value.fold(
err => {
ctx.log.error(err.reason)
NoOp
},
res => {
Put(scriptPath, res)
}
)
case Failure(exception) => {
ctx.log.error(exception.getMessage())
NoOp
}
}
}
} }

View File

@ -0,0 +1,53 @@
package wow.doge.mygame.utils
import java.io.PrintStream
import java.io.OutputStream
import java.io.ByteArrayOutputStream
import scalafx.scene.control.TextArea
import wow.doge.mygame.implicits._
import cats.effect.Resource
import monix.bio.Task
trait JFXConsoleStreamable[T] {
def println(inst: T, text: String): Unit
def print(inst: T, text: String): Unit
}
class JFXConsoleStream[T](
outputStream: OutputStream,
val config: JFXConsoleStream.Config = JFXConsoleStream.Config.default,
control: T
)(implicit
jcs: JFXConsoleStreamable[T]
) extends PrintStream(outputStream, true) {
private lazy val defaultOut = System.out
override def println(text: String): Unit =
if (config.exclusive) {
jcs.println(control, text + "\n")
} else {
defaultOut.println(text)
jcs.println(control, text + "\n")
}
override def print(text: String): Unit = jcs.println(control, text)
}
object JFXConsoleStream {
/**
* for future use
*/
case class Config(exclusive: Boolean = false)
object Config {
lazy val default = Config()
}
def textAreaStream(ta: TextArea) =
Resource.make(
Task(
new JFXConsoleStream(
outputStream = new ByteArrayOutputStream(),
control = ta
)
)
)(s => Task(s.close()))
}

1
test.plugin.json Normal file
View File

@ -0,0 +1 @@
{ "hello1": "world1", "hello2": "world2" }

1
test2.plugin.json Normal file
View File

@ -0,0 +1 @@
{ "hello2": "world3" }