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,
|
||||
"org.jetbrains.kotlin" % "kotlin-main-kts" % "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",
|
||||
"org.scalafx" %% "scalafx" % "14-R19",
|
||||
"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-effect" % "2.1.4",
|
||||
"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" %% "macrosakka" % "2.3.6" % "provided",
|
||||
"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
|
||||
|
||||
@ -102,6 +104,7 @@ lazy val root = (project in file(".")).settings(
|
||||
"-Xlint",
|
||||
"-Ywarn-numeric-widen",
|
||||
"-Ymacro-annotations",
|
||||
"-Xlint:byname-implicit",
|
||||
// "utf-8", // Specify character encoding used by source files.
|
||||
"-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 := "4.3.24",
|
||||
)
|
||||
initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
|
||||
|
||||
// Here, `libraryDependencies` is a set of dependencies, and by using `+=`,
|
||||
// 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
|
||||
// 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("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
|
||||
// scala 2.13.3
|
||||
|
||||
// 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
|
||||
|
||||
import game.GameApp
|
||||
import com.jme3.app.StatsAppState
|
||||
|
||||
import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
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)
|
||||
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 RootActor {
|
||||
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
|
||||
SpawnProtocol()
|
||||
}
|
||||
}
|
||||
|
||||
object TestActor {
|
||||
sealed trait Command
|
||||
case object Test extends Command
|
||||
private case object Done extends Command
|
||||
// sealed trait Result
|
||||
// case object Done extends Result
|
||||
import monix.bio.Task
|
||||
import cats.effect.Resource
|
||||
import io.odin.syntax._
|
||||
|
||||
// import io.odin.monix._
|
||||
import cats.effect.ExitCode
|
||||
import cats.implicits._
|
||||
import com.softwaremill.macwire._
|
||||
import scala.concurrent.duration._
|
||||
implicit val timeout = Timeout(15.seconds)
|
||||
// implicit val scheduler =
|
||||
import monix.bio.BIOApp
|
||||
import monix.bio.UIO
|
||||
import monix.bio.IO
|
||||
import io.odin._
|
||||
import wow.doge.mygame.executors.JMERunner
|
||||
import com.jme3.bullet.BulletAppState
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
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",
|
||||
// _
|
||||
// import wow.doge.mygame.implicits._
|
||||
|
||||
// object Main extends App {
|
||||
// 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()
|
||||
// )
|
||||
// ) {
|
||||
// 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(
|
||||
// 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
|
||||
// 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)
|
||||
// }
|
||||
|
||||
Behaviors.same
|
||||
case Done => Behaviors.same
|
||||
}
|
||||
object Main extends BIOApp with MainModule {
|
||||
import java.util.logging.{Logger => JLogger, Level}
|
||||
JLogger.getLogger("").setLevel(Level.SEVERE)
|
||||
|
||||
// SpawnProtocol()
|
||||
// Behaviors.same
|
||||
}
|
||||
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 _)
|
||||
// _ <- Resource.liftF(
|
||||
// Task(gameApp.start()).asyncBoundary
|
||||
// .executeOn(Scheduler(JMEExecutorService))
|
||||
// )
|
||||
|
||||
_ <- Resource.liftF(gameApp.enqueueT(actorSystem ! RootActor.Start))
|
||||
_ <- Resource.liftF {
|
||||
IO(gameApp.start())
|
||||
.executeOn(jmeScheduler)
|
||||
}
|
||||
// (_ => IO(gameApp.stop(() => actorSystem ! RootActor.Stop)))
|
||||
} yield ()
|
||||
|
||||
// Console.withOut(
|
||||
// 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
|
||||
|
||||
import monix.bio.Task
|
||||
import cats.effect.Resource
|
||||
import monix.execution.Scheduler
|
||||
|
||||
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 scala.concurrent.ExecutionContext
|
||||
import java.util.concurrent.Executor
|
||||
import wow.doge.mygame.Main
|
||||
|
||||
// First we wrap invokeLater/runLater as an ExecutorService
|
||||
trait GUIExecutorService extends AbstractExecutorService {
|
||||
@ -44,7 +43,15 @@ object SwingExecutorService 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(
|
||||
|
@ -4,7 +4,7 @@ import monix.execution.Scheduler
|
||||
|
||||
final case class Schedulers(
|
||||
blockingIO: Scheduler = Scheduler.io(),
|
||||
cpu: Scheduler = Scheduler.global,
|
||||
fx: Scheduler = JFXExecutionContexts.fxScheduler,
|
||||
jme: Option[Scheduler] = None
|
||||
async: Scheduler = Scheduler.global,
|
||||
fx: Scheduler = JFXExecutionContexts.fxScheduler
|
||||
// jme: SchedulerService
|
||||
)
|
||||
|
@ -3,23 +3,16 @@ package wow.doge.mygame.game
|
||||
import com.jme3.app.SimpleApplication
|
||||
|
||||
import com.jme3.app.state.AppState
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
|
||||
object Greeter {
|
||||
final case class Greet(whom: String, replyTo: ActorRef[Greeted])
|
||||
final case class Greeted(whom: String, from: ActorRef[Greet])
|
||||
|
||||
def apply(): Behavior[Greet] =
|
||||
Behaviors.receive { (context, message) =>
|
||||
// context.log.info("Hello {}!", message.whom)
|
||||
//#greeter-send-messages
|
||||
message.replyTo ! Greeted(message.whom, context.self)
|
||||
//#greeter-send-messages
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
import com.jme3.bullet.BulletAppState
|
||||
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape
|
||||
import com.jme3.bullet.control.CharacterControl
|
||||
import com.jme3.bullet.control.RigidBodyControl
|
||||
import com.jme3.bullet.util.CollisionShapeFactory
|
||||
import com.jme3.scene.Spatial
|
||||
import com.jme3.syntax._
|
||||
import com.jme3.asset.plugins.ZipLocator
|
||||
import com.jme3.math.ColorRGBA
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
class GameApp(
|
||||
// actorSystem: ActorSystem[SpawnProtocol.Command],
|
||||
@ -27,8 +20,47 @@ class GameApp(
|
||||
) extends SimpleApplication(appStates: _*) {
|
||||
// implicit val timeout = Timeout(10.seconds)
|
||||
// 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 = {
|
||||
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 mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
|
||||
@ -88,22 +120,23 @@ class GameApp(
|
||||
// super.stop()
|
||||
// }
|
||||
|
||||
// override def start(): Unit = {
|
||||
// monix.eval.Task(super.start()).runToFuture(Scheduler(JMEExecutorService))
|
||||
// }
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
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
|
||||
// override def stop(): Unit = {}
|
||||
}
|
||||
|
@ -1,60 +1,174 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import wow.doge.mygame.state.MovementActor
|
||||
import wow.doge.mygame.state.PlayerMovementState2
|
||||
import wow.doge.mygame.state.MovementActorTimer
|
||||
import com.jme3.scene.shape.Box
|
||||
import wow.doge.mygame.state.PlayerMovementState
|
||||
import com.jme3.scene.Geometry
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
import wow.doge.mygame.events.EventBus
|
||||
import wow.doge.mygame.events.Events
|
||||
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 {
|
||||
import Methods._
|
||||
|
||||
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 =>
|
||||
lazy val b = new Box(1, 1, 1)
|
||||
lazy val geom = new Geometry("Box", b)
|
||||
val movementActor =
|
||||
ctx.spawn(
|
||||
MovementActor(MovementActor.Props(app, geom)),
|
||||
"movementActor"
|
||||
// DispatcherSelector.fromConfig("jme-dispatcher")
|
||||
)
|
||||
|
||||
val movementActorTimer = ctx.spawn(
|
||||
MovementActorTimer(movementActor),
|
||||
"movementActorTimer"
|
||||
)
|
||||
val imMovementActor = ctx.spawn(
|
||||
ImMovementActor(ImMovementActor.Props(app, geom)),
|
||||
"imMovementActor"
|
||||
)
|
||||
ctx.log.info("Hello from GameAppActor")
|
||||
// lazy val b = new Box(1, 1, 1)
|
||||
// lazy val geom = new Geometry("Box", b)
|
||||
// lazy val playerNode = new Node("PlayerNode")
|
||||
// lazy val camNode = new CameraNode("CameraNode", app.getCamera())
|
||||
// lazy val players = createPlayer(geom, app.getCamera())
|
||||
|
||||
// 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 eventBus =
|
||||
val tickEventBus =
|
||||
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
|
||||
.getStateManager()
|
||||
.attach(
|
||||
new PlayerMovementState2(
|
||||
movementActor,
|
||||
movementActorTimer,
|
||||
imMovementActor,
|
||||
geom
|
||||
.getRootNode()
|
||||
.depthFirst(s =>
|
||||
// s match {
|
||||
// case node: Node =>
|
||||
// println("node" + s.getName() + " children " + node.getChildren())
|
||||
// case g: Geometry => println(s.getName())
|
||||
// }
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
// 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.simsilica.es.EntityData;
|
@ -8,9 +8,9 @@ import wow.doge.mygame.implicits._
|
||||
import com.jme3.renderer.Camera
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
|
||||
trait CanMove[T] {
|
||||
trait CanMove[-A] {
|
||||
def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||
def move(inst: T, direction: ImVector3f): Unit
|
||||
def move(inst: A, direction: ImVector3f): Unit
|
||||
}
|
||||
|
||||
object ImMovementActor {
|
||||
@ -40,7 +40,10 @@ object ImMovementActor {
|
||||
)
|
||||
|
||||
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 =
|
||||
cm.getDirection(props.app.getCamera(), state.cardinalDir)
|
||||
if (walkDir != ImVector3f.ZERO) {
|
||||
val tmp = walkDir * 2f
|
||||
props.app.enqueue(new Runnable {
|
||||
override def run(): Unit = {
|
||||
val tmp = walkDir * 25f * tpf
|
||||
// props.app.enqueue(new Runnable {
|
||||
// override def run(): Unit = {
|
||||
// cm.move(props.movable, tmp)
|
||||
// }
|
||||
// })
|
||||
props.app.enqueueF {
|
||||
cm.move(props.movable, tmp)
|
||||
}
|
||||
})
|
||||
}
|
||||
Behaviors.same
|
||||
// receive(state = state.modify(_.walkDirection).setTo(walkDir))
|
@ -7,6 +7,7 @@ import com.jme3.scene.Node
|
||||
import com.jme3.app.state.BaseAppState
|
||||
import com.simsilica.es.EntityData
|
||||
import com.simsilica.es.base.DefaultEntityData
|
||||
import com.jme3.scene.Spatial
|
||||
|
||||
trait MyBaseState extends BaseAppState {
|
||||
|
||||
@ -26,17 +27,22 @@ trait MyBaseState extends BaseAppState {
|
||||
}
|
||||
|
||||
protected def init(): Unit
|
||||
protected def stop(): Unit
|
||||
|
||||
override protected def cleanup(app: Application): Unit = {
|
||||
entityData.close()
|
||||
// stop()
|
||||
}
|
||||
|
||||
protected def getOrCreateNode(parent: Node, id: String) =
|
||||
Option(parent.getChild(id)).fold {
|
||||
val node = new Node(id)
|
||||
protected def getChildOption(parent: Node, id: String) =
|
||||
Option(parent.getChild(id))
|
||||
|
||||
protected def getOrCreateSpatial(parent: Node, id: String): Spatial =
|
||||
Option(parent.getChild(id)).getOrElse {
|
||||
val node: Spatial = new Node(id)
|
||||
parent.attachChild(node)
|
||||
node
|
||||
}(node => node.asInstanceOf[Node])
|
||||
}
|
||||
|
||||
protected def enableStates(classes: Class[_ <: AppState]*) =
|
||||
setEnabledToStates(true, classes: _*)
|
@ -1,6 +1,5 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
import com.jme3.input.InputManager
|
||||
@ -17,55 +16,64 @@ import com.jme3.scene.Geometry
|
||||
import akka.actor.typed.scaladsl.TimerScheduler
|
||||
|
||||
import wow.doge.mygame.implicits._
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.syntax._
|
||||
|
||||
class PlayerMovementState2(
|
||||
movementActor: ActorRef[MovementActor.Command],
|
||||
movementActorTimer: ActorRef[MovementActorTimer.Command],
|
||||
class PlayerMovementState(
|
||||
// movementActor: ActorRef[MovementActor.Command],
|
||||
// movementActorTimer: ActorRef[MovementActorTimer.Command],
|
||||
imMovementActor: ActorRef[ImMovementActor.Command],
|
||||
geom: Geometry
|
||||
// geom: Geometry,
|
||||
// camNode: CameraNode,
|
||||
playerNode: Node
|
||||
// gameAppActor: ActorRef[GameAppActor.Command]
|
||||
) extends MyBaseState
|
||||
with ActionListener {
|
||||
|
||||
protected lazy val mat = MyMaterial(
|
||||
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 = {
|
||||
|
||||
setupKeys(inputManager)
|
||||
geom.setMaterial(mat)
|
||||
rootNode.attachChild(geom)
|
||||
println("playermovementstate " + Thread.currentThread().getName())
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) = {
|
||||
// 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)
|
||||
imMovementActor ! ImMovementActor.Tick(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) = {
|
||||
|
||||
inputManager
|
||||
@ -79,33 +87,32 @@ class PlayerMovementState2(
|
||||
// new KeyTrigger(KeyInput.KEY_D),
|
||||
new KeyTrigger(KeyInput.KEY_RIGHT)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
.withMapping(
|
||||
"Up",
|
||||
// new KeyTrigger(KeyInput.KEY_W),
|
||||
new KeyTrigger(KeyInput.KEY_UP)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
.withMapping(
|
||||
"Down",
|
||||
// new KeyTrigger(KeyInput.KEY_S),
|
||||
new KeyTrigger(KeyInput.KEY_DOWN)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
.withMapping(
|
||||
"Space",
|
||||
new KeyTrigger(KeyInput.KEY_SPACE),
|
||||
new KeyTrigger(KeyInput.KEY_H)
|
||||
)
|
||||
inputManager.addMapping(
|
||||
.withMapping(
|
||||
"Reset",
|
||||
new KeyTrigger(KeyInput.KEY_R),
|
||||
new KeyTrigger(KeyInput.KEY_RETURN)
|
||||
)
|
||||
inputManager
|
||||
.withListener(this, "Left")
|
||||
.withListener(this, "Right")
|
||||
inputManager.addListener(this, "Up")
|
||||
inputManager.addListener(this, "Down")
|
||||
inputManager.addListener(this, "Space")
|
||||
inputManager.addListener(this, "Reset")
|
||||
.withListener(this, "Up")
|
||||
.withListener(this, "Down")
|
||||
.withListener(this, "Space")
|
||||
.withListener(this, "Reset")
|
||||
}
|
||||
|
||||
def onAction(binding: String, value: Boolean, tpf: Float) =
|
||||
@ -118,6 +125,10 @@ class PlayerMovementState2(
|
||||
case _ =>
|
||||
}
|
||||
|
||||
override protected def onEnable(): Unit = {}
|
||||
|
||||
override protected def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
final case class CardinalDirection(
|
@ -4,7 +4,6 @@ import ammonite.runtime.Storage.Folder
|
||||
import ammonite.main.Defaults
|
||||
import ammonite.Main
|
||||
import javax.script.ScriptEngine
|
||||
import com.jme3.app.Application
|
||||
import com.jme3.app.state.AppState
|
||||
import akka.actor.typed.scaladsl.AbstractBehavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
@ -31,10 +30,11 @@ class ScriptingEngineState(
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
override def stop(): Unit = {}
|
||||
|
||||
override protected def cleanup(app: Application): Unit = {
|
||||
// actorSystem.terminate()
|
||||
}
|
||||
// override protected def cleanup(app: Application): Unit = {
|
||||
// // actorSystem.terminate()
|
||||
// }
|
||||
|
||||
// override protected def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
@ -1,6 +1,5 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import com.jme3.app.Application
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.components.TestComponent
|
||||
import com.jme3.scene.shape.Box
|
||||
@ -32,7 +31,7 @@ class TestAppState(
|
||||
|
||||
val mat = MyMaterial(
|
||||
assetManager = assetManager,
|
||||
path = "Common/MatDefs/Misc/Unshaded.j3md"
|
||||
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
)
|
||||
geom.foreach(e => {
|
||||
e.setMaterial(mat)
|
||||
@ -45,14 +44,15 @@ class TestAppState(
|
||||
geom.foreach(_.move(new Vector3f(0, 1 * tpf, 0)))
|
||||
}
|
||||
|
||||
override def cleanup(app: Application): Unit = {
|
||||
// _entity.map(_.close())
|
||||
// _entity = None
|
||||
}
|
||||
// override def cleanup(app: Application): Unit = {
|
||||
// // _entity.map(_.close())
|
||||
// // _entity = None
|
||||
// }
|
||||
|
||||
override def onEnable(): Unit = {}
|
||||
|
||||
override def onDisable(): Unit = {}
|
||||
override def stop(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
@ -61,10 +61,10 @@ object MyMaterial {
|
||||
color: String = "Color",
|
||||
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
|
||||
assetManager: AssetManager,
|
||||
path: String
|
||||
path: os.RelPath
|
||||
): Material = {
|
||||
val mat =
|
||||
new Material(assetManager, path)
|
||||
new Material(assetManager, path.toString())
|
||||
mat.setColor(color, colorType)
|
||||
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.util.Timeout
|
||||
import akka.actor.typed.Scheduler
|
||||
import monix.eval.Task
|
||||
import monix.bio.Task
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.input.controls.Trigger
|
||||
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.CanMove
|
||||
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 {
|
||||
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 {
|
||||
def state[S <: AppState]()(implicit c: ClassTag[S]): S =
|
||||
sm.getState(c.runtimeClass.asInstanceOf[Class[S]])
|
||||
@ -32,14 +100,126 @@ package object implicits {
|
||||
|
||||
implicit class SimpleApplicationExt(val sa: SimpleApplication)
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Attaches the given child
|
||||
*
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
def child(s: Spatial): Node = {
|
||||
n.attachChild(s)
|
||||
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 {
|
||||
@ -81,6 +261,93 @@ package object implicits {
|
||||
inputManager.addListener(listener, mappings: _*)
|
||||
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 {
|
||||
@ -134,14 +401,52 @@ package object implicits {
|
||||
// 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(
|
||||
cam: Camera,
|
||||
cardinalDir: CardinalDirection
|
||||
): ImVector3f = {
|
||||
val camDir =
|
||||
cam.getDirection().immutable * 0.6f
|
||||
val camLeft = cam.getLeft().immutable * 0.4f
|
||||
// val camDir =
|
||||
// cam.getDirection().immutable * 0.6f
|
||||
// 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 dir = cardinalDir
|
||||
@ -149,27 +454,61 @@ package object implicits {
|
||||
val mutWalkDir = new Vector3f()
|
||||
if (dir.left) {
|
||||
// ctx.log.trace("left")
|
||||
mutWalkDir += (zero + camLeft).mutable
|
||||
mutWalkDir += (zero + ImVector3f(-1, 0, 0)).mutable
|
||||
}
|
||||
if (dir.right) {
|
||||
// ctx.log.trace("right")
|
||||
mutWalkDir += (zero + -camLeft).mutable
|
||||
mutWalkDir += (zero + ImVector3f(1, 0, 0)).mutable
|
||||
}
|
||||
if (dir.up) {
|
||||
// ctx.log.trace("up")
|
||||
mutWalkDir += (zero + camDir).mutable
|
||||
mutWalkDir += (zero + ImVector3f(0, 0, -1)).mutable
|
||||
}
|
||||
if (dir.down) {
|
||||
// ctx.log.trace("down")
|
||||
mutWalkDir += (zero + -camDir).mutable
|
||||
mutWalkDir += (zero + ImVector3f(0, 0, 1)).mutable
|
||||
}
|
||||
mutWalkDir.immutable
|
||||
}
|
||||
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)
|
||||
|
||||
|
@ -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 ammonite.Main
|
||||
import akka.actor.typed.ActorRef
|
||||
import com.jme3.app.state.AppState
|
||||
import ammonite.runtime.Storage.Folder
|
||||
import ammonite.main.Defaults
|
||||
import akka.actor.typed.Behavior
|
||||
@ -11,18 +10,17 @@ import akka.actor.typed.scaladsl.ActorContext
|
||||
import ammonite.util.Res.Success
|
||||
import javax.script.ScriptEngine
|
||||
import javax.script.ScriptEngineManager
|
||||
import scala.util.Try
|
||||
import groovy.util.GroovyScriptEngine
|
||||
import cats.implicits._
|
||||
|
||||
object ScriptActor {
|
||||
sealed trait Result
|
||||
final case class AppStateResult(state: AppState) extends Result
|
||||
final case class Error(reason: String) extends Result
|
||||
type Kotlin
|
||||
type MyScriptEngine[T] = ScriptEngine
|
||||
type KotlinScriptEngine = MyScriptEngine[Kotlin]
|
||||
|
||||
final case class Error(reason: String)
|
||||
|
||||
sealed trait Command
|
||||
final case class Compile(path: os.Path, sender: ActorRef[Result])
|
||||
extends Command
|
||||
|
||||
final case class CompileAny(
|
||||
path: os.Path,
|
||||
result: ActorRef[Either[Error, Any]]
|
||||
@ -38,19 +36,28 @@ object ScriptActor {
|
||||
)
|
||||
)
|
||||
|
||||
def defaultKotlinRunner(): ScriptEngine = {
|
||||
def defaultKotlinRunner(): KotlinScriptEngine = {
|
||||
val manager = new ScriptEngineManager()
|
||||
val engine = manager.getEngineByExtension("main.kts")
|
||||
engine
|
||||
}
|
||||
|
||||
def defaultGroovyRunner(): GroovyScriptEngine =
|
||||
new GroovyScriptEngine(os.pwd.toString())
|
||||
|
||||
def apply(
|
||||
scalaRunner: Main = defaultScalaRunner(),
|
||||
kotlinRunner: ScriptEngine = defaultKotlinRunner()
|
||||
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner(),
|
||||
groovyRunner: GroovyScriptEngine = defaultGroovyRunner()
|
||||
// parent: ActorRef[ScriptStoringActor.Command]
|
||||
): Behavior[ScriptActor.Command] =
|
||||
Behaviors.setup(ctx =>
|
||||
new ScriptActor(scalaRunner, kotlinRunner, ctx).receiveMessage
|
||||
new ScriptActor(
|
||||
scalaRunner,
|
||||
kotlinRunner,
|
||||
groovyRunner,
|
||||
ctx
|
||||
).receiveMessage
|
||||
)
|
||||
|
||||
sealed trait ScriptType
|
||||
@ -80,20 +87,31 @@ object ScriptActor {
|
||||
case _ => Left(Error("Failed to run script"))
|
||||
}
|
||||
|
||||
def runKotlin(path: os.Path, kotlinRunner: ScriptEngine): Either[Error, Any] =
|
||||
Try(kotlinRunner.eval(os.read(path))).toEither.leftMap(t =>
|
||||
Error(t.getMessage())
|
||||
)
|
||||
def runKotlin(
|
||||
path: os.Path,
|
||||
kotlinRunner: KotlinScriptEngine
|
||||
): Either[Error, Any] =
|
||||
Either
|
||||
.catchNonFatal(kotlinRunner.eval(os.read(path)))
|
||||
.leftMap(t => Error(t.getMessage()))
|
||||
|
||||
def runGroovy(path: os.Path): Either[Error, Any] =
|
||||
Left(Error("Not implemented yet"))
|
||||
def runGroovy(
|
||||
path: os.Path,
|
||||
groovyRunner: GroovyScriptEngine
|
||||
): Either[Error, Any] =
|
||||
Either
|
||||
.catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString(), ""))
|
||||
.leftMap(t => Error(t.getMessage()))
|
||||
|
||||
def ensureReturnedObjectNotNull(scriptObject: Any): Either[Error, Any] =
|
||||
Either.fromOption(Option(scriptObject), Error("unknown object"))
|
||||
|
||||
}
|
||||
|
||||
class ScriptActor(
|
||||
val scalaRunner: Main,
|
||||
val kotlinRunner: ScriptEngine,
|
||||
// parent: ActorRef[ScriptStoringActor.Command],
|
||||
val scalaRunner: ammonite.Main,
|
||||
val kotlinRunner: ScriptActor.KotlinScriptEngine,
|
||||
val groovyRunner: GroovyScriptEngine,
|
||||
context: ActorContext[ScriptActor.Command]
|
||||
) {
|
||||
import ScriptActor._
|
||||
@ -101,57 +119,24 @@ class ScriptActor(
|
||||
def receiveMessage: Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
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) =>
|
||||
context.log.debug(s"Received $path")
|
||||
val res = getScript2(path)
|
||||
val res = getScript(path)
|
||||
context.log.debug(s"result = $res")
|
||||
requester ! res
|
||||
// parent ! ScriptStoringActor.Put(path, res)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
def getScript(path: os.Path) = {
|
||||
val res = determineScriptType(path) match {
|
||||
case Right(ScalaType) => runScala(path, scalaRunner)
|
||||
case Right(KotlinType) => runKotlin(path, kotlinRunner)
|
||||
case Right(GroovyType) => runGroovy(path)
|
||||
def getScript(path: os.Path): Either[Error, Any] =
|
||||
determineScriptType(path) match {
|
||||
case Right(ScalaType) =>
|
||||
runScala(path, scalaRunner).flatMap(ensureReturnedObjectNotNull)
|
||||
case Right(KotlinType) =>
|
||||
runKotlin(path, kotlinRunner).flatMap(ensureReturnedObjectNotNull)
|
||||
case Right(GroovyType) =>
|
||||
runGroovy(path, groovyRunner).flatMap(ensureReturnedObjectNotNull)
|
||||
case l @ Left(err) => l
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
type ScriptRepr = Any
|
||||
type ScriptsMap = Map[os.Path, ScriptRepr]
|
||||
type ScriptResult = Either[ScriptActor.Error, ScriptRepr]
|
||||
type ScriptObject = Any
|
||||
type ScriptsMap = Map[os.Path, ScriptObject]
|
||||
type ScriptResult = Either[ScriptActor.Error, ScriptObject]
|
||||
|
||||
sealed trait Command
|
||||
final case class Get(
|
||||
@ -27,10 +27,11 @@ object ScriptCachingActor {
|
||||
requester: ActorRef[ScriptResult]
|
||||
) extends Command
|
||||
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
||||
final case class Put(scriptPath: os.Path, script: ScriptRepr) extends Command
|
||||
private final case object NoOp extends Command
|
||||
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
||||
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],
|
||||
scriptPath: os.Path,
|
||||
requester: ActorRef[ScriptResult]
|
||||
@ -41,6 +42,7 @@ object ScriptCachingActor {
|
||||
scriptActor: ActorRef[ScriptActor.Command]
|
||||
)
|
||||
final case class State(scriptsMap: ScriptsMap)
|
||||
|
||||
def apply(state: State = State(Map.empty)): Behavior[Command] =
|
||||
Behaviors.logMessages {
|
||||
Behaviors.setup { ctx =>
|
||||
@ -49,58 +51,12 @@ object ScriptCachingActor {
|
||||
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) {
|
||||
import com.softwaremill.quicklens._
|
||||
import ScriptCachingActor._
|
||||
import Methods._
|
||||
def receiveMessage(state: State): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
@ -134,7 +90,7 @@ class ScriptCachingActor(props: ScriptCachingActor.Props) {
|
||||
props.ctx.log.debug(s"Putting $script at path $scriptPath")
|
||||
val newState =
|
||||
state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
|
||||
props.ctx.log.debug(newState.toString())
|
||||
props.ctx.log.trace(newState.toString())
|
||||
receiveMessage(state = newState)
|
||||
|
||||
case NoOp => Behaviors.same
|
||||
@ -152,15 +108,54 @@ object ScriptActorPool {
|
||||
.supervise(ScriptActor())
|
||||
.onFailure[Exception](SupervisorStrategy.restart)
|
||||
)
|
||||
|
||||
// def apply(
|
||||
// poolSize: Int,
|
||||
// parent: ActorRef[ScriptStoringActor.Command]
|
||||
// ): PoolRouter[ScriptActor.Command] =
|
||||
// Routers.pool(poolSize = poolSize)(
|
||||
// // make sure the workers are restarted if they fail
|
||||
// Behaviors
|
||||
// .supervise(ScriptActor(parent = parent))
|
||||
// .onFailure[Exception](SupervisorStrategy.restart)
|
||||
// )
|
||||
}
|
||||
|
||||
private[scriptsystem] object Methods {
|
||||
import ScriptCachingActor._
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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