forked from nova/jmonkey-test
many changes
This commit is contained in:
parent
1f55e08fa5
commit
b0994bcdbe
@ -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
3
launch.sh
Normal 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
BIN
lib/jme3-testdata.jar
Normal file
Binary file not shown.
4
plugins.json
Normal file
4
plugins.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[
|
||||||
|
{ "name": "test", "priority": 1 },
|
||||||
|
{ "name": "test2", "priority": 2 }
|
||||||
|
]
|
@ -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")
|
||||||
|
3
src/main/resources/hello.groovy
Normal file
3
src/main/resources/hello.groovy
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
println "hello"
|
||||||
|
|
||||||
|
this
|
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env amm
|
#!/usr/bin/env amm
|
||||||
|
// scala 2.13.3
|
||||||
|
|
||||||
// import coursierapi.MavenRepository
|
// import coursierapi.MavenRepository
|
||||||
|
|
||||||
|
59
src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
Normal file
59
src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala
Normal 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
|
||||||
|
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
87
src/main/scala/wow/doge/mygame/MainModule.scala
Normal file
87
src/main/scala/wow/doge/mygame/MainModule.scala
Normal 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
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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 = {}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
// )
|
||||||
|
25
src/main/scala/wow/doge/mygame/game/GameModule.scala
Normal file
25
src/main/scala/wow/doge/mygame/game/GameModule.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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 = {}
|
||||||
|
}
|
84
src/main/scala/wow/doge/mygame/game/TestActor.scala
Normal file
84
src/main/scala/wow/doge/mygame/game/TestActor.scala
Normal 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
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
@ -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))
|
@ -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: _*)
|
@ -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(
|
@ -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)
|
@ -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
|
||||||
}
|
}
|
94
src/main/scala/wow/doge/mygame/game/nodes/PlayerNode.scala
Normal file
94
src/main/scala/wow/doge/mygame/game/nodes/PlayerNode.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
// }
|
||||||
|
;
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
53
src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala
Normal file
53
src/main/scala/wow/doge/mygame/utils/JFXConsoleStream.scala
Normal 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
1
test.plugin.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "hello1": "world1", "hello2": "world2" }
|
1
test2.plugin.json
Normal file
1
test2.plugin.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "hello2": "world3" }
|
Loading…
Reference in New Issue
Block a user