forked from nova/jmonkey-test
Commit 1
This commit is contained in:
parent
45ab129790
commit
3881aac350
0
.attach_pid12833
Normal file
0
.attach_pid12833
Normal file
0
.attach_pid19972
Normal file
0
.attach_pid19972
Normal file
@ -95,7 +95,8 @@ lazy val root = (project in file(".")).settings(
|
||||
// "com.github.Oshan96" % "CustomStage" % "v1.3.1",
|
||||
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
|
||||
"org.recast4j" % "recast" % "1.2.5",
|
||||
"org.recast4j" % "detour" % "1.2.5"
|
||||
"org.recast4j" % "detour" % "1.2.5",
|
||||
"com.lihaoyi" %% "pprint" % "0.6.0"
|
||||
),
|
||||
// Determine OS version of JavaFX binaries
|
||||
|
||||
@ -155,10 +156,6 @@ lazy val root = (project in file(".")).settings(
|
||||
// val oldStrategy = (assemblyMergeStrategy in assembly).value
|
||||
// oldStrategy(x)
|
||||
}
|
||||
// scalaVersion := "2.13.2", // 2.11.12, or 2.13.3
|
||||
// semanticdbEnabled := true, // enable SemanticDB
|
||||
// semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version
|
||||
// semanticdbVersion := "4.3.24",
|
||||
)
|
||||
initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
|
||||
|
||||
|
@ -47,6 +47,10 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
||||
.allocated
|
||||
.unsafeRunSync()
|
||||
|
||||
val mainFileLogger2 = mainFileLogger.contramap(lm =>
|
||||
lm.copy(message = lm.message.map(s => fansi.Str(s).plainText))
|
||||
)
|
||||
|
||||
private lazy val (eventBusFileLogger, release3) =
|
||||
fileLogger[IO](
|
||||
"eventbus.log",
|
||||
@ -72,15 +76,22 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
||||
case s if s.startsWith("com.jayfella.jme.jfx.util.JfxPlatform") =>
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Info)
|
||||
|
||||
// case s
|
||||
// if s.startsWith(
|
||||
// "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"
|
||||
// ) =>
|
||||
case s
|
||||
if s.startsWith(
|
||||
"wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"
|
||||
) =>
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Info)
|
||||
case s
|
||||
if s.startsWith(
|
||||
"wow.doge.mygame.game.entities.NpcMovementActor"
|
||||
) =>
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Trace) |+| mainFileLogger2
|
||||
.withMinimalLevel(Level.Trace)
|
||||
// defaultConsoleLogger.withMinimalLevel( Level.Trace) //selectively turn on trace logging for specific classes
|
||||
case s if s.startsWith("wow.doge.mygame.subsystems.events.EventBus") =>
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| eventBusFileLogger
|
||||
case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger2
|
||||
case _ => //if wildcard case isn't provided, default logger is no-op
|
||||
defaultConsoleLogger.withMinimalLevel(Level.Debug)
|
||||
}
|
||||
|
@ -5,16 +5,16 @@ import scala.concurrent.duration._
|
||||
import _root_.monix.bio.BIOApp
|
||||
import _root_.monix.bio.Task
|
||||
import _root_.monix.bio.UIO
|
||||
import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.Resource
|
||||
import cats.implicits._
|
||||
import com.softwaremill.macwire._
|
||||
import io.odin._
|
||||
import io.odin.json.Formatter
|
||||
import io.odin.syntax._
|
||||
import scalafx.scene.control.TextArea
|
||||
import wow.doge.mygame.game.GameAppResource
|
||||
import wow.doge.mygame.utils.GenericConsoleStream
|
||||
|
||||
object Main extends BIOApp with MainModule {
|
||||
@ -34,25 +34,23 @@ object Main extends BIOApp with MainModule {
|
||||
Formatter.json
|
||||
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
||||
jmeScheduler <- jMESchedulerResource
|
||||
actorSystem <- actorSystemResource(logger)
|
||||
gameApp <- {
|
||||
// new BulletAppState()
|
||||
// bas.setThreadingType(Thr)
|
||||
// gameAppResource(new StatsAppState())
|
||||
wire[GameAppResource].get
|
||||
}
|
||||
implicit0(actorSystem: ActorSystem[SpawnProtocol.Command]) <-
|
||||
actorSystemResource(logger)
|
||||
// gameApp <- {
|
||||
// // new BulletAppState()
|
||||
// // bas.setThreadingType(Thr)
|
||||
// // gameAppResource(new StatsAppState())
|
||||
// wire[GameAppResource].get
|
||||
// }
|
||||
_ <- Resource.liftF(
|
||||
new MainApp(
|
||||
logger,
|
||||
gameApp,
|
||||
actorSystem,
|
||||
// gameApp,
|
||||
// actorSystem,
|
||||
jmeScheduler,
|
||||
schedulers,
|
||||
consoleStream
|
||||
)(
|
||||
timeout,
|
||||
actorSystem.scheduler
|
||||
).program
|
||||
)(actorSystem, timeout, actorSystem.scheduler).program
|
||||
)
|
||||
|
||||
} yield ()
|
||||
@ -63,7 +61,7 @@ object Main extends BIOApp with MainModule {
|
||||
Console.withOut(consoleStream)(
|
||||
appResource(consoleStream)
|
||||
.use(_ => Task.unit >> Task(consoleStream.close()))
|
||||
.onErrorHandle(_.printStackTrace())
|
||||
.onErrorHandleWith(ex => UIO(ex.printStackTrace()))
|
||||
.as(ExitCode.Success)
|
||||
)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import com.jme3.app.state.AppStateManager
|
||||
import com.jme3.asset.AssetManager
|
||||
@ -46,98 +47,80 @@ import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.utils.GenericConsoleStream
|
||||
import wow.doge.mygame.utils.IOUtils
|
||||
import cats.syntax.eq._
|
||||
|
||||
import EventsModule.GameEventBus
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
|
||||
class MainApp(
|
||||
logger: Logger[Task],
|
||||
gameApp: GameApp,
|
||||
implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||
jmeThread: monix.execution.Scheduler,
|
||||
schedulers: Schedulers,
|
||||
consoleStream: GenericConsoleStream[TextArea]
|
||||
)(implicit
|
||||
spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||
@annotation.unused timeout: Timeout,
|
||||
@annotation.unused scheduler: Scheduler
|
||||
) {
|
||||
|
||||
lazy val scriptSystemInit =
|
||||
new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init
|
||||
val scriptSystemInit =
|
||||
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
|
||||
|
||||
def gameInit: Task[Fiber[Throwable, Unit]] =
|
||||
for {
|
||||
eventsModule <- Task(new EventsModule(spawnProtocol))
|
||||
playerEventBus <- eventsModule.playerEventBusTask
|
||||
mainEventBus <- eventsModule.mainEventBusTask
|
||||
tickEventBus <- eventsModule.tickEventBusTask
|
||||
gameAppActor <- AkkaUtils.spawnActorL2(
|
||||
GameAppActor.Props(tickEventBus).behavior,
|
||||
"gameAppActor"
|
||||
)
|
||||
_ <- gameAppActor !! GameAppActor.Start
|
||||
gameAppFib <- gameApp.start.executeOn(jmeThread).start
|
||||
/**
|
||||
* schedule a task to run on the JME thread and wait for it's completion
|
||||
* before proceeding forward, as a signal that the JME thread has been
|
||||
* initialized, otherwise we'll get NPEs trying to access the fields
|
||||
* of the game app
|
||||
*/
|
||||
res <- gameApp.enqueueL(() => "done")
|
||||
_ <- logger.info(s"Result = $res")
|
||||
/**
|
||||
* JME Thread has been initialized at this point. We can now access the
|
||||
* field of the game application
|
||||
*/
|
||||
inputManager <- gameApp.inputManager
|
||||
assetManager <- gameApp.assetManager
|
||||
stateManager <- gameApp.stateManager
|
||||
camera <- gameApp.camera
|
||||
rootNode <- gameApp.rootNode
|
||||
enqueueR <- Task(gameApp.enqueue _)
|
||||
viewPort <- gameApp.viewPort
|
||||
_ <- logger.info("before")
|
||||
// jfxUI <- gameApp.jfxUI
|
||||
consoleTextArea <- Task(new TextArea {
|
||||
text = "hello \n"
|
||||
editable = false
|
||||
wrapText = true
|
||||
// maxHeight = 150
|
||||
// maxWidth = 300
|
||||
})
|
||||
// _ <- Task(consoleStream := consoleTextArea)
|
||||
// _ <- Task(jfxUI += consoleTextArea)
|
||||
_ <- logger.info("after")
|
||||
bulletAppState <- Task(new BulletAppState())
|
||||
_ <- Task(stateManager.attach(bulletAppState))
|
||||
_ <- logger.info("Initializing console stream")
|
||||
_ <- wire[MainAppDelegate].init(gameApp.scheduler)
|
||||
} yield (gameAppFib)
|
||||
val eventsModule = new EventsModule(spawnProtocol)
|
||||
|
||||
lazy val program = for {
|
||||
def gameInit: Resource[Task, Fiber[Throwable, Unit]] =
|
||||
GameApp.resource(logger, jmeThread, schedulers).evalMap {
|
||||
case gameApp -> gameAppFib =>
|
||||
for {
|
||||
playerEventBus <- eventsModule.playerEventBusTask
|
||||
mainEventBus <- eventsModule.mainEventBusTask
|
||||
tickEventBus <- eventsModule.tickEventBusTask
|
||||
gameAppActor <- AkkaUtils.spawnActorL(
|
||||
GameAppActor.Props(tickEventBus).behavior,
|
||||
"gameAppActor"
|
||||
)
|
||||
_ <- gameAppActor !! GameAppActor.Start
|
||||
inputManager <- gameApp.inputManager
|
||||
assetManager <- gameApp.assetManager
|
||||
stateManager <- gameApp.stateManager
|
||||
camera <- gameApp.camera
|
||||
rootNode <- gameApp.rootNode
|
||||
enqueueR <- Task(gameApp.enqueue _)
|
||||
viewPort <- gameApp.viewPort
|
||||
_ <- logger.info("before")
|
||||
// jfxUI <- gameApp.jfxUI
|
||||
consoleTextArea <- Task(new TextArea {
|
||||
text = "hello \n"
|
||||
editable = false
|
||||
wrapText = true
|
||||
// maxHeight = 150
|
||||
// maxWidth = 300
|
||||
})
|
||||
// _ <- Task(consoleStream := consoleTextArea)
|
||||
// _ <- Task(jfxUI += consoleTextArea)
|
||||
_ <- logger.info("after")
|
||||
bulletAppState <- Task(new BulletAppState())
|
||||
_ <- Task(stateManager.attach(bulletAppState))
|
||||
_ <- logger.info("Initializing console stream")
|
||||
_ <- wire[MainAppDelegate].init(gameApp.scheduler)
|
||||
} yield gameAppFib
|
||||
}
|
||||
|
||||
val program = for {
|
||||
scriptSystem <- scriptSystemInit
|
||||
/**
|
||||
* Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI
|
||||
* Without this, we get a "Toolkit already initialized" exception. The launch button
|
||||
* in the launcher completes the signal. The game init process which listens for this
|
||||
* signal can then continue
|
||||
*/
|
||||
launchSignal <- Deferred[Task, Launcher.LauncherResult]
|
||||
launcher <- new Launcher.Props(schedulers, launchSignal).create
|
||||
cancelToken <- launcher.init()
|
||||
launchResult <- launchSignal.get
|
||||
_ <- cancelToken.cancel
|
||||
launchResult <- launcher.init.use(_ => launchSignal.get)
|
||||
_ <-
|
||||
/**
|
||||
* User chose to quit
|
||||
*/
|
||||
if (launchResult == LauncherResult.Exit)
|
||||
if (launchResult === LauncherResult.Exit)
|
||||
logger.info("Exiting")
|
||||
/**
|
||||
* User chose launch. Wait for game window to close
|
||||
*/
|
||||
else
|
||||
gameInit.flatMap(_.join)
|
||||
gameInit.use(_.join)
|
||||
} yield ()
|
||||
}
|
||||
|
||||
@ -146,7 +129,6 @@ class MainApp(
|
||||
*/
|
||||
class MainAppDelegate(
|
||||
gameApp: GameApp,
|
||||
implicit val spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||
loggerL: Logger[Task],
|
||||
playerEventBus: GameEventBus[PlayerEvent],
|
||||
tickEventBus: GameEventBus[TickEvent],
|
||||
@ -159,10 +141,11 @@ class MainAppDelegate(
|
||||
rootNode: Node @@ GameAppTags.RootNode,
|
||||
bulletAppState: BulletAppState
|
||||
)(implicit
|
||||
spawnProtocol: ActorSystem[SpawnProtocol.Command],
|
||||
@annotation.unused timeout: Timeout,
|
||||
@annotation.unused scheduler: Scheduler
|
||||
) {
|
||||
lazy val physicsSpace = bulletAppState.physicsSpace
|
||||
val physicsSpace = bulletAppState.physicsSpace
|
||||
def init(
|
||||
appScheduler: monix.execution.Scheduler
|
||||
// consoleStream: GenericConsoleStream[TextArea]
|
||||
@ -190,19 +173,21 @@ class MainAppDelegate(
|
||||
// johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler)
|
||||
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
||||
|
||||
// _ <- (johnActor !! NpcActorSupervisor.Move(
|
||||
// ImVector3f(-80, 0, 100)
|
||||
// )).executeAsync.delayExecution(2.seconds)
|
||||
_ <-
|
||||
IOUtils
|
||||
.toIO(
|
||||
rootNode
|
||||
.observableBreadthFirst()
|
||||
.doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName())))
|
||||
.completedL
|
||||
)
|
||||
.executeOn(appScheduler)
|
||||
.startAndForget
|
||||
// _ <-
|
||||
// (johnActor !! NpcActorSupervisor.Move(
|
||||
// ImVector3f(-30, 0, 10)
|
||||
// )).executeAsync
|
||||
// .delayExecution(2.seconds)
|
||||
// _ <-
|
||||
// IOUtils
|
||||
// .toIO(
|
||||
// rootNode
|
||||
// .observableBreadthFirst()
|
||||
// .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName())))
|
||||
// .completedL
|
||||
// )
|
||||
// .executeOn(appScheduler)
|
||||
// .startAndForget
|
||||
} yield ()
|
||||
|
||||
def createPlayerController(
|
||||
@ -210,14 +195,14 @@ class MainAppDelegate(
|
||||
): IO[PlayerController.Error, Unit] = {
|
||||
val playerPos = ImVector3f.ZERO
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
lazy val playerPhysicsControl =
|
||||
val playerPhysicsControl =
|
||||
PlayerController.Defaults.defaultPlayerPhysicsControl
|
||||
.taggedWith[PlayerControllerTags.PlayerTag]
|
||||
// lazy val camNode =
|
||||
// PlayerController.Defaults
|
||||
// .defaultCamerNode(camera, playerPos)
|
||||
// .taggedWith[PlayerControllerTags.PlayerCameraNode]
|
||||
lazy val mbPlayerNode = PlayerController.Defaults
|
||||
val mbPlayerNode = PlayerController.Defaults
|
||||
.defaultPlayerNode(
|
||||
assetManager,
|
||||
modelPath,
|
||||
@ -225,7 +210,7 @@ class MainAppDelegate(
|
||||
// camNode
|
||||
playerPhysicsControl
|
||||
)
|
||||
lazy val cameraPivotNode = new Node(EntityIds.CameraPivot.value)
|
||||
val cameraPivotNode = new Node(EntityIds.CameraPivot.value)
|
||||
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
|
||||
|
||||
for {
|
||||
@ -258,11 +243,11 @@ class MainAppDelegate(
|
||||
) =
|
||||
// : IO[PlayerController.Error, Unit] =
|
||||
{
|
||||
val initialPos = ImVector3f(100, 0, 0)
|
||||
val initialPos = ImVector3f(50, 5, 0)
|
||||
// val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
lazy val npcPhysicsControl =
|
||||
new BetterCharacterControl(1f, 2.1f, 10f)
|
||||
// .withJumpForce(ImVector3f(0, 5f, 0))
|
||||
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
// (1f, 2.1f, 10f)
|
||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||
// val npcMovementActor = AkkaUtils.spawnActorL2(
|
||||
// new NpcMovementActor2.Props(
|
||||
// initialPos,
|
||||
@ -271,13 +256,13 @@ class MainAppDelegate(
|
||||
// ).behavior,
|
||||
// s"${npcName}-npcMovementActor"
|
||||
// )
|
||||
lazy val mbNpcNode = PlayerController.Defaults.defaultNpcNode(
|
||||
val mbNpcNode = PlayerController.Defaults.defaultNpcNode(
|
||||
assetManager,
|
||||
initialPos,
|
||||
npcPhysicsControl,
|
||||
npcName
|
||||
)
|
||||
val npcActorTask = AkkaUtils.spawnActorL2(
|
||||
val npcActorTask = AkkaUtils.spawnActorL(
|
||||
NpcActorSupervisor
|
||||
.Props(
|
||||
new NpcMovementActor.Props(
|
||||
@ -302,7 +287,7 @@ class MainAppDelegate(
|
||||
physicsSpace += npcNode
|
||||
rootNode += npcNode
|
||||
}
|
||||
} yield (npcActor)
|
||||
} yield npcActor
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,15 +11,14 @@ trait MainModule extends ExecutorsModule {
|
||||
def actorSystemResource(
|
||||
logger: Logger[Task]
|
||||
): Resource[Task, ActorSystem[SpawnProtocol.Command]] =
|
||||
Resource.make(logger.info("Creating Actor System") >> Task {
|
||||
ActorSystem(
|
||||
SpawnProtocol(),
|
||||
name = "GameActorSystem"
|
||||
Resource.make(
|
||||
logger.info("Creating Actor System") >> Task(
|
||||
ActorSystem(SpawnProtocol(), name = "GameActorSystem")
|
||||
)
|
||||
})(sys =>
|
||||
)(sys =>
|
||||
for {
|
||||
_ <- Task(sys.terminate())
|
||||
_ <- Task.fromFuture(sys.whenTerminated)
|
||||
_ <- Task.deferFuture(sys.whenTerminated)
|
||||
_ <- logger.info("Actor System Terminated")
|
||||
} yield ()
|
||||
)
|
||||
|
@ -1,11 +1,13 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import com.jme3.app.state.AppStateManager
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.scene.Spatial
|
||||
import com.jme3.system.AppSettings
|
||||
import com.softwaremill.tagging._
|
||||
import com.typesafe.scalalogging.{Logger => SLogger}
|
||||
import io.odin.Logger
|
||||
@ -15,7 +17,13 @@ import monix.catnap.ConcurrentChannel
|
||||
import monix.catnap.ConsumerF
|
||||
import monix.catnap.Semaphore
|
||||
import monix.eval.Coeval
|
||||
import monix.execution.CancelablePromise
|
||||
import monix.execution.Scheduler
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.game.subsystems.ui.JFxUI
|
||||
import wow.doge.mygame.implicits._
|
||||
import monix.execution.annotations.UnsafeBecauseImpure
|
||||
import monix.reactive.Observable
|
||||
|
||||
sealed trait Error
|
||||
case object FlyCamNotExists extends Error
|
||||
@ -40,6 +48,8 @@ class GameApp(logger: Logger[Task], val app: SimpleAppExt) {
|
||||
def camera = Task(app.getCamera())
|
||||
def viewPort = Task(app.getViewPort())
|
||||
def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
||||
def rootNode2 =
|
||||
WrappedNode(app.getRootNode()).taggedWith[GameAppTags.RootNode]
|
||||
// def rootNode2 = SynchedObject(app.getRootNode())
|
||||
def addToRootNode = rootNode.flatMap(rn => Task(new AddToNode(rn)))
|
||||
def enqueue(cb: () => Unit) =
|
||||
@ -55,12 +65,57 @@ class GameApp(logger: Logger[Task], val app: SimpleAppExt) {
|
||||
|
||||
}
|
||||
|
||||
class WrappedNode private (node: Node) {
|
||||
|
||||
// def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
|
||||
def children: Observable[Spatial] = node.observableChildren
|
||||
def attachChild(n: Node): Task[Unit] = Task(node.attachChild(node))
|
||||
def add(wn: WrappedNode): Task[Unit] =
|
||||
Task(node.attachChild(wn.unsafeDelegate))
|
||||
|
||||
/**
|
||||
* Get the underlying wrapped value
|
||||
*/
|
||||
@UnsafeBecauseImpure
|
||||
def unsafeDelegate = node
|
||||
}
|
||||
object WrappedNode {
|
||||
|
||||
def apply(name: String) = new WrappedNode(new Node(name))
|
||||
def apply(n: Node) = new WrappedNode(n)
|
||||
implicit class WrappedNodeOps(private val wn: WrappedNode) extends AnyVal {
|
||||
def +=(n: Node) = wn.attachChild(n)
|
||||
def +=(wn: WrappedNode) = wn.add(wn)
|
||||
}
|
||||
}
|
||||
|
||||
object GameApp {
|
||||
|
||||
class WrappedNode(node: Node, lock: Semaphore[Task]) {
|
||||
def resource(
|
||||
logger: Logger[Task],
|
||||
jmeScheduler: Scheduler,
|
||||
schedulers: Schedulers
|
||||
) =
|
||||
Resource.make(
|
||||
for {
|
||||
startSignal <- Task(CancelablePromise[Unit]())
|
||||
app <- Task(new SimpleAppExt(schedulers, startSignal))
|
||||
_ <- Task {
|
||||
val settings = new AppSettings(true)
|
||||
settings.setVSync(true)
|
||||
|
||||
def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
|
||||
}
|
||||
/**
|
||||
* disables the launcher
|
||||
* We'll be making our own launcher anyway
|
||||
*/
|
||||
app.setShowSettings(false)
|
||||
app.setSettings(settings)
|
||||
}
|
||||
gameApp <- Task(new GameApp(logger, app))
|
||||
fib <- gameApp.start.executeOn(jmeScheduler).start
|
||||
_ <- Task.fromCancelablePromise(startSignal)
|
||||
} yield gameApp -> fib
|
||||
)(_._2.cancel)
|
||||
|
||||
/**
|
||||
* Synchronization wrapper for a mutable object
|
||||
|
@ -6,6 +6,7 @@ import akka.actor.typed.SupervisorStrategy
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import wow.doge.mygame.game.TickGenerator.Send
|
||||
import wow.doge.mygame.game.entities.GenericTimerActor
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
@ -28,7 +29,7 @@ object GameAppActor {
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
ctx.log.info("Hello from GameAppActor")
|
||||
ctx.log.infoP("Hello from GameAppActor")
|
||||
val renderTickGenerator =
|
||||
ctx.spawn(
|
||||
Behaviors
|
||||
@ -78,7 +79,7 @@ object TickGenerator {
|
||||
object SubscribingActor {
|
||||
def apply() =
|
||||
Behaviors.receive[PhysicsTick.type] { (ctx, msg) =>
|
||||
ctx.log.debug(s"received event $msg")
|
||||
ctx.log.debugP(s"received event $msg")
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,30 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
import cats.effect.Resource
|
||||
import com.jme3.app.StatsAppState
|
||||
import com.jme3.system.AppSettings
|
||||
import io.odin.Logger
|
||||
import monix.bio.Task
|
||||
import monix.execution.Scheduler
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
class GameAppResource(
|
||||
logger: Logger[Task],
|
||||
jmeScheduler: Scheduler,
|
||||
schedulers: Schedulers
|
||||
) {
|
||||
|
||||
def get: Resource[Task, GameApp] =
|
||||
Resource.make(
|
||||
for {
|
||||
_ <- logger.info("Creating game app")
|
||||
appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState()))
|
||||
app <- Task {
|
||||
val settings = new AppSettings(true)
|
||||
settings.setVSync(true)
|
||||
// class GameAppResource(
|
||||
// logger: Logger[Task],
|
||||
// jmeScheduler: Scheduler,
|
||||
// schedulers: Schedulers
|
||||
// ) {
|
||||
|
||||
/**
|
||||
* disables the launcher
|
||||
* We'll be making our own launcher anyway
|
||||
*/
|
||||
appExt.setShowSettings(false)
|
||||
appExt.setSettings(settings)
|
||||
// JMERunner.runner = app
|
||||
new GameApp(logger, appExt)
|
||||
}
|
||||
} yield (app)
|
||||
)(_ => logger.info("Closing game app"))
|
||||
}
|
||||
// def get: Resource[Task, GameApp] =
|
||||
// Resource.make(
|
||||
// for {
|
||||
// _ <- logger.info("Creating game app")
|
||||
// appExt <- Task(new SimpleAppExt(schedulers, new StatsAppState()))
|
||||
// app <- Task {
|
||||
// val settings = new AppSettings(true)
|
||||
// settings.setVSync(true)
|
||||
|
||||
// /**
|
||||
// * disables the launcher
|
||||
// * We'll be making our own launcher anyway
|
||||
// */
|
||||
// appExt.setShowSettings(false)
|
||||
// appExt.setSettings(settings)
|
||||
// // JMERunner.runner = app
|
||||
// new GameApp(logger, appExt)
|
||||
// }
|
||||
// } yield (app)
|
||||
// )(_ => logger.info("Closing game app"))
|
||||
// }
|
||||
|
@ -6,17 +6,15 @@ import com.jme3.app.SimpleApplication
|
||||
import com.jme3.app.state.AppState
|
||||
import monix.bio.Task
|
||||
import monix.execution.CancelableFuture
|
||||
import monix.execution.CancelablePromise
|
||||
import monix.execution.Scheduler
|
||||
import monix.execution.atomic.Atomic
|
||||
import monix.execution.{CancelablePromise => Promise}
|
||||
import monix.reactive.MulticastStrategy
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.subjects.ConcurrentSubject
|
||||
import wow.doge.mygame.executors.GUIExecutorService
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
|
||||
class SimpleAppExt(
|
||||
schedulers: Schedulers,
|
||||
startSignal: CancelablePromise[Unit],
|
||||
appStates: AppState*
|
||||
) extends SimpleApplication(appStates: _*) {
|
||||
import SimpleAppExt._
|
||||
@ -26,32 +24,26 @@ class SimpleAppExt(
|
||||
*/
|
||||
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
|
||||
|
||||
private val tickSubject =
|
||||
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
|
||||
schedulers.async
|
||||
)
|
||||
// def tickObservable: Observable[Float] = tickSubject
|
||||
|
||||
def tickObservable: Observable[Float] = tickSubject
|
||||
|
||||
override def simpleInitApp(): Unit = {}
|
||||
|
||||
override def simpleUpdate(tpf: Float): Unit = {
|
||||
tickSubject.onNext(tpf)
|
||||
override def simpleInitApp(): Unit = {
|
||||
startSignal.success(())
|
||||
}
|
||||
|
||||
override def simpleUpdate(tpf: Float): Unit = {}
|
||||
|
||||
override def stop(): Unit = {
|
||||
tickSubject.onComplete()
|
||||
super.stop()
|
||||
}
|
||||
|
||||
def enqueueScala[T](cb: () => T): CancelableFuture[T] = {
|
||||
val p = Promise[T]()
|
||||
def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
|
||||
val p = CancelablePromise[T]()
|
||||
taskQueue2.transform(_ :+ MyTask(p, cb))
|
||||
p.future
|
||||
}
|
||||
|
||||
def enqueueL[T](cb: () => T): Task[T] =
|
||||
Task.deferFuture(enqueueScala(cb))
|
||||
Task.deferFuture(enqueueFuture(cb))
|
||||
|
||||
override protected def runQueuedTasks(): Unit = {
|
||||
taskQueue2.transform { current =>
|
||||
@ -73,7 +65,7 @@ class SimpleAppExt(
|
||||
lazy val scheduler = Scheduler(JMEExecutorService)
|
||||
}
|
||||
object SimpleAppExt {
|
||||
private[game] case class MyTask[T](p: Promise[T], cb: () => T)
|
||||
private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T)
|
||||
}
|
||||
|
||||
// val ship = ed.createEntity()
|
||||
|
@ -36,11 +36,11 @@ object TestActor {
|
||||
// )
|
||||
// ) {
|
||||
// case Success(value) =>
|
||||
// ctx.log.debug("Received Value")
|
||||
// ctx.log.debug(value.toString())
|
||||
// ctx.log.debugP("Received Value")
|
||||
// ctx.log.debugP(value.toString())
|
||||
// Done
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debug(s"Received Error ${exception.getMessage()}")
|
||||
// ctx.log.debugP(s"Received Error ${exception.getMessage()}")
|
||||
// Done
|
||||
// }
|
||||
}
|
||||
@ -60,24 +60,24 @@ object TestActor {
|
||||
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
|
||||
// ) {
|
||||
// case Success(value) => {
|
||||
// ctx.log.debug(value.toString())
|
||||
// ctx.log.debugP(value.toString())
|
||||
// ctx.ask(
|
||||
// scriptStorer,
|
||||
// ScriptStoringActor
|
||||
// .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _)
|
||||
// ) {
|
||||
// case Success(value) => {
|
||||
// ctx.log.debug(value.toString())
|
||||
// ctx.log.debugP(value.toString())
|
||||
// Done
|
||||
// }
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debug(exception.getMessage())
|
||||
// ctx.log.debugP(exception.getMessage())
|
||||
// Done
|
||||
// }
|
||||
// Done
|
||||
// }
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debug(exception.getMessage())
|
||||
// ctx.log.debugP(exception.getMessage())
|
||||
// Done
|
||||
// }
|
||||
}
|
||||
|
@ -193,23 +193,23 @@ class MovementActor(
|
||||
// val walkDir = new Vector3f
|
||||
val dir = state.cardinalDir
|
||||
if (dir.up) {
|
||||
ctx.log.debug("up")
|
||||
// ctx.log.debug(Thread.currentThread().getName())
|
||||
ctx.log.debugP("up")
|
||||
// ctx.log.debugP(Thread.currentThread().getName())
|
||||
// walkDir.addLocal(0, 0, -1)
|
||||
walkDir += camDir
|
||||
}
|
||||
if (dir.left) {
|
||||
ctx.log.debug("left")
|
||||
ctx.log.debugP("left")
|
||||
// walkDir.addLocal(-1, 0, 0)
|
||||
walkDir.addLocal(camLeft)
|
||||
}
|
||||
if (dir.right) {
|
||||
ctx.log.debug("right")
|
||||
ctx.log.debugP("right")
|
||||
// walkDir.addLocal(1, 0, 0)
|
||||
walkDir.addLocal(camLeft.negateLocal())
|
||||
}
|
||||
if (dir.down) {
|
||||
ctx.log.debug("down")
|
||||
ctx.log.debugP("down")
|
||||
walkDir.addLocal(camDir.negateLocal())
|
||||
// walkDir.addLocal(0, 0, 1)
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ object NpcActorSupervisor {
|
||||
private case class LogError(err: Throwable) extends Command
|
||||
private case object NoOp extends Command
|
||||
|
||||
private case class MovementFailed(err: Throwable) extends Command
|
||||
|
||||
final case class Props(
|
||||
npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
|
||||
npcName: String,
|
||||
@ -82,7 +84,7 @@ class NpcActorSupervisor(
|
||||
|
||||
def idle(state: State): Behavior[NpcActorSupervisor.Command] =
|
||||
Behaviors.setup { _ =>
|
||||
ctx.log.debug("Inside Idle State")
|
||||
ctx.log.debugP("Inside Idle State")
|
||||
Behaviors.receiveMessage[Command] {
|
||||
case m @ Move(pos) =>
|
||||
ctx.ask(
|
||||
@ -97,7 +99,7 @@ class NpcActorSupervisor(
|
||||
moving(state, move.pos, signal)
|
||||
|
||||
case LogError(err) =>
|
||||
ctx.log.warn(err.getMessage())
|
||||
ctx.log.warnP(err.getMessage())
|
||||
Behaviors.same
|
||||
case _ => Behaviors.unhandled
|
||||
}
|
||||
@ -117,12 +119,16 @@ class NpcActorSupervisor(
|
||||
// )
|
||||
ctx.pipeToSelf(signal) {
|
||||
case Success(value) => DoneMoving
|
||||
case Failure(exception) => LogError(exception)
|
||||
case Failure(exception) => MovementFailed(exception)
|
||||
}
|
||||
Behaviors.receiveMessagePartial[Command] {
|
||||
case LogError(err) =>
|
||||
ctx.log.error(err.getMessage())
|
||||
Behaviors.same
|
||||
case MovementFailed(err) =>
|
||||
ctx.self ! LogError(err)
|
||||
movementTimer ! GenericTimerActor.Stop
|
||||
idle(state)
|
||||
case m @ Move(pos) =>
|
||||
movementTimer ! GenericTimerActor.Stop
|
||||
children.npcMovementActor ! NpcMovementActor.StopMoving
|
||||
@ -132,7 +138,7 @@ class NpcActorSupervisor(
|
||||
NpcMovementActor.MoveTo(pos, _)
|
||||
) {
|
||||
case Success(signal) => InternalMove(m, signal)
|
||||
case Failure(exception) => LogError(exception)
|
||||
case Failure(exception) => MovementFailed(exception)
|
||||
}
|
||||
Behaviors.same
|
||||
case InternalMove(move, signal) =>
|
||||
@ -190,12 +196,6 @@ class NpcMovementActor[T](
|
||||
case AskPosition(replyTo) =>
|
||||
replyTo ! location
|
||||
Behaviors.same
|
||||
case StopMoving =>
|
||||
ctx.log.debug(
|
||||
"Position at Stop = " + location.toString
|
||||
)
|
||||
props.enqueueR(() => cm.stop(props.movable))
|
||||
receive(state)
|
||||
case MoveTo(
|
||||
target: ImVector3f,
|
||||
replyTo: ActorRef[CancelableFuture[DoneMoving.type]]
|
||||
@ -204,7 +204,6 @@ class NpcMovementActor[T](
|
||||
val p = CancelablePromise[DoneMoving.type]()
|
||||
replyTo ! p.future
|
||||
ticking(p, target, state)
|
||||
|
||||
}
|
||||
|
||||
def ticking(
|
||||
@ -214,7 +213,7 @@ class NpcMovementActor[T](
|
||||
): Behavior[NpcMovementActor.Command] =
|
||||
Behaviors.receiveMessagePartial {
|
||||
case StopMoving =>
|
||||
ctx.log.debug(
|
||||
ctx.log.debugP(
|
||||
"Position at Stop = " + location.toString
|
||||
)
|
||||
props.enqueueR(() => cm.stop(props.movable))
|
||||
@ -225,12 +224,11 @@ class NpcMovementActor[T](
|
||||
if (dst <= 10f) {
|
||||
ctx.self ! StopMoving
|
||||
reachDestination.success(DoneMoving)
|
||||
receive(state)
|
||||
} else {
|
||||
ctx.log.trace("Difference = " + dst.toString())
|
||||
ctx.log.trace("Current pos = " + location.toString())
|
||||
Behaviors.same
|
||||
ctx.log.traceP("Difference = " + dst.toString())
|
||||
ctx.log.traceP("Current pos = " + location.toString())
|
||||
}
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ object PlayerActorSupervisor {
|
||||
ctx.log.info("Hello from PlayerActor")
|
||||
|
||||
// spawn children actors
|
||||
lazy val movementActor =
|
||||
val movementActor =
|
||||
ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
|
@ -29,6 +29,7 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import com.softwaremill.macwire._
|
||||
|
||||
object PlayerControllerTags {
|
||||
sealed trait PlayerTag
|
||||
@ -47,7 +48,6 @@ object PlayerController {
|
||||
loggerL: Logger[Task],
|
||||
physicsSpace: PhysicsSpace,
|
||||
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
playerEventBus: GameEventBus[PlayerEvent],
|
||||
playerPhysicsControl: BetterCharacterControl,
|
||||
appScheduler: monix.execution.Scheduler,
|
||||
@ -56,29 +56,34 @@ object PlayerController {
|
||||
cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode,
|
||||
tickEventBus: GameEventBus[TickEvent],
|
||||
camera: Camera
|
||||
)(implicit timeout: Timeout, scheduler: Scheduler) {
|
||||
)(implicit
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
timeout: Timeout,
|
||||
scheduler: Scheduler
|
||||
) {
|
||||
val playerActorBehavior = {
|
||||
val movementActorBeh = new ImMovementActor.Props(
|
||||
enqueueR,
|
||||
playerPhysicsControl,
|
||||
camera
|
||||
).behavior
|
||||
val cameraActorBeh = new PlayerCameraActor.Props(
|
||||
cameraPivotNode,
|
||||
enqueueR,
|
||||
playerNode.getWorldTranslation _
|
||||
).behavior
|
||||
new PlayerActorSupervisor.Props(
|
||||
playerEventBus,
|
||||
tickEventBus,
|
||||
movementActorBeh,
|
||||
cameraActorBeh
|
||||
).behavior(playerPhysicsControl)
|
||||
}
|
||||
val create: IO[Error, Unit] =
|
||||
(for {
|
||||
playerActor <- AkkaUtils.spawnActorL(
|
||||
spawnProtocol,
|
||||
"playerActorSupervisor",
|
||||
new PlayerActorSupervisor.Props(
|
||||
playerEventBus,
|
||||
// playerCameraEventBus,
|
||||
tickEventBus,
|
||||
new ImMovementActor.Props(
|
||||
enqueueR,
|
||||
playerPhysicsControl,
|
||||
camera
|
||||
).behavior,
|
||||
// wireWith(PlayerCameraEventListener.apply _)
|
||||
// PlayerCameraEventListener()
|
||||
new PlayerCameraActor.Props(
|
||||
cameraPivotNode,
|
||||
enqueueR,
|
||||
playerNode.getWorldTranslation _
|
||||
).behavior
|
||||
).behavior(playerPhysicsControl)
|
||||
playerActorBehavior,
|
||||
"playerActorSupervisor"
|
||||
)
|
||||
_ <- Task(rootNode += playerNode)
|
||||
_ <- IO {
|
||||
@ -88,9 +93,7 @@ object PlayerController {
|
||||
cameraPivotNode += cameraNode
|
||||
// playerNode += cameraPivotNode
|
||||
rootNode += cameraPivotNode
|
||||
|
||||
}
|
||||
|
||||
} yield ())
|
||||
.onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage())))
|
||||
.executeOn(appScheduler)
|
||||
@ -101,10 +104,10 @@ object PlayerController {
|
||||
modelPath: os.RelPath,
|
||||
cam: Camera
|
||||
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
|
||||
lazy val playerPos = ImVector3f.ZERO
|
||||
lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
val playerPos = ImVector3f.ZERO
|
||||
val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||
lazy val playerNode = new Node("PlayerNode")
|
||||
val playerNode = new Node("PlayerNode")
|
||||
.withChildren(
|
||||
assetManager
|
||||
.loadModel(modelPath)
|
||||
|
@ -18,10 +18,11 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||
import wow.doge.mygame.utils.IOUtils._
|
||||
import monix.bio.Task
|
||||
|
||||
object GameInputHandler {
|
||||
|
||||
final class Props(
|
||||
class Props(
|
||||
inputManager: InputManager,
|
||||
playerEventBus: GameEventBus[PlayerEvent]
|
||||
// playerCameraEventBus: GameEventBus[PlayerCameraEvent]
|
||||
@ -29,44 +30,44 @@ object GameInputHandler {
|
||||
) {
|
||||
def begin =
|
||||
for {
|
||||
_ <- UIO(setupMovementKeys(inputManager))
|
||||
_ <- Task(setupMovementKeys(inputManager))
|
||||
// _ <- UIO(setupAnalogMovementKeys)
|
||||
_ <- UIO(setupCameraKeys())
|
||||
_ <- Task(setupCameraKeys())
|
||||
_ <- toIO(
|
||||
generateMovementInputEvents(
|
||||
inputManager,
|
||||
playerEventBus
|
||||
).completedL.startAndForget
|
||||
)
|
||||
_ <- toIO(
|
||||
generateAnalogMovementEvents(
|
||||
inputManager,
|
||||
playerEventBus
|
||||
).completedL.startAndForget
|
||||
)
|
||||
_ <- toIO(
|
||||
generateCameraEvents(
|
||||
inputManager,
|
||||
playerEventBus
|
||||
).completedL.startAndForget
|
||||
)
|
||||
_ <- toIO(
|
||||
Ref.of[me.Task, Boolean](false).flatMap(value => cursorToggle(value))
|
||||
)
|
||||
me.Task.parSequence(
|
||||
Seq(
|
||||
generateMovementInputEvents(
|
||||
inputManager,
|
||||
playerEventBus
|
||||
).completedL,
|
||||
// generateAnalogMovementEvents(
|
||||
// inputManager,
|
||||
// playerEventBus
|
||||
// ).completedL,
|
||||
generateCameraEvents(
|
||||
inputManager,
|
||||
playerEventBus
|
||||
).completedL,
|
||||
Ref
|
||||
.of[me.Task, Boolean](false)
|
||||
.flatMap(value => cursorToggle(value))
|
||||
)
|
||||
)
|
||||
).startAndForget
|
||||
} yield ()
|
||||
|
||||
def setupMovementKeys(inputManager: InputManager) =
|
||||
inputManager.withEnumMappings(PlayerMovementInput) {
|
||||
case PlayerMovementInput.WalkRight =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_D))
|
||||
new KeyTrigger(KeyInput.KEY_D) :: Nil
|
||||
case PlayerMovementInput.WalkLeft =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_A))
|
||||
new KeyTrigger(KeyInput.KEY_A) :: Nil
|
||||
case PlayerMovementInput.WalkForward =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_W))
|
||||
new KeyTrigger(KeyInput.KEY_W) :: Nil
|
||||
case PlayerMovementInput.WalkBackward =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_S))
|
||||
new KeyTrigger(KeyInput.KEY_S) :: Nil
|
||||
case PlayerMovementInput.Jump =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_SPACE))
|
||||
new KeyTrigger(KeyInput.KEY_SPACE) :: Nil
|
||||
}
|
||||
|
||||
def setupAnalogMovementKeys() =
|
||||
@ -124,7 +125,6 @@ object GameInputHandler {
|
||||
}
|
||||
)
|
||||
.completedL
|
||||
.startAndForget
|
||||
} yield ()
|
||||
|
||||
}
|
||||
|
@ -14,15 +14,15 @@ object DefaultGameLevel {
|
||||
assetManager: AssetManager,
|
||||
viewPort: ViewPort
|
||||
) = {
|
||||
lazy val sceneModel: Spatial = assetManager.loadModel("main.scene")
|
||||
lazy val sceneShape = CollisionShapeFactory.createMeshShape(
|
||||
val sceneModel: Spatial = assetManager.loadModel("main.scene")
|
||||
val sceneShape = CollisionShapeFactory.createMeshShape(
|
||||
sceneModel.toNode match {
|
||||
case Right(node) => node
|
||||
case Left(ex) =>
|
||||
throw new NotImplementedError("No fallback sceneshape")
|
||||
}
|
||||
)
|
||||
lazy val landscape: RigidBodyControl =
|
||||
val landscape: RigidBodyControl =
|
||||
new RigidBodyControl(sceneShape, 0)
|
||||
|
||||
viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f))
|
||||
|
@ -1,5 +1,6 @@
|
||||
package wow.doge.mygame.game.subsystems.movement
|
||||
|
||||
import cats.Id
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.math.Quaternion
|
||||
@ -8,7 +9,6 @@ import monix.eval.Coeval
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.subsystems.movement.RotateDir
|
||||
|
||||
// experiment to see if it would be useful to use an effect wrapper for a typeclass like this
|
||||
trait CanMove2[-A, F[_]] {
|
||||
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||
@ -19,7 +19,35 @@ trait CanMove2[-A, F[_]] {
|
||||
def rotate(inst: A, rotateDir: RotateDir): F[Unit]
|
||||
}
|
||||
|
||||
object Test {
|
||||
val x = new BetterCharacterControl(4, 10, 5)
|
||||
def test[T](x: T)(implicit cm: CanMove2[T, Id]) = {
|
||||
cm.move(x, ImVector3f.ZERO)
|
||||
}
|
||||
}
|
||||
|
||||
object CanMove2 {
|
||||
implicit val testImpl = new CanMove2[BetterCharacterControl, Id] {
|
||||
|
||||
override def move(
|
||||
inst: BetterCharacterControl,
|
||||
direction: ImVector3f,
|
||||
speedFactor: Float
|
||||
): Id[Unit] = {}
|
||||
|
||||
override def location(inst: BetterCharacterControl): Id[ImVector3f] =
|
||||
ImVector3f.ZERO
|
||||
|
||||
override def jump(inst: BetterCharacterControl): Id[Unit] = ???
|
||||
|
||||
override def stop(inst: BetterCharacterControl): Id[Unit] = ???
|
||||
|
||||
override def rotate(
|
||||
inst: BetterCharacterControl,
|
||||
rotateDir: RotateDir
|
||||
): Id[Unit] = ???
|
||||
|
||||
}
|
||||
implicit val implCanMoveForBetterCharacterControl =
|
||||
new CanMove2[BetterCharacterControl, Coeval] {
|
||||
override def move(
|
||||
|
@ -84,7 +84,7 @@ class ImMovementActor[T](
|
||||
|
||||
case Tick =>
|
||||
val walkDir =
|
||||
getDirection2(state.cardinalDir, ctx.log.trace)
|
||||
getDirection2(state.cardinalDir, ctx.log.traceP)
|
||||
// if (walkDir != ImVector3f.ZERO) {
|
||||
val tmp = walkDir * 25f * (1f / 144)
|
||||
props.enqueueR { () =>
|
||||
@ -94,7 +94,10 @@ class ImMovementActor[T](
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
def getDirection2(cardinalDir: CardinalDirection, debug: String => Unit) = {
|
||||
def getDirection2(
|
||||
cardinalDir: CardinalDirection,
|
||||
debug: sourcecode.Text[String] => sourcecode.Text[String]
|
||||
) = {
|
||||
val camDir =
|
||||
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
|
||||
val camLeft = props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
|
||||
|
@ -1,11 +1,14 @@
|
||||
package wow.doge.mygame.implicits
|
||||
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.beans.{value => jfxbv}
|
||||
import javafx.scene.{input => jfxsi}
|
||||
import javafx.{event => jfxe}
|
||||
import monix.execution.Ack
|
||||
import monix.execution.Cancelable
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.OverflowStrategy
|
||||
import scalafx.beans.property.Property
|
||||
import scalafx.scene.Scene
|
||||
import scalafx.scene.control.ButtonBase
|
||||
|
||||
@ -55,6 +58,35 @@ object JavaFXMonixObservables {
|
||||
}
|
||||
}
|
||||
|
||||
implicit final class BindObs[A, B](private val prop: Property[A, B])
|
||||
extends AnyVal {
|
||||
def <--[T](op: Observable[(ObservableValue[_ <: B], B, B)] => T) = {
|
||||
op(prop.observableChange())
|
||||
}
|
||||
|
||||
def observableChange(): Observable[(ObservableValue[_ <: B], B, B)] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
|
||||
val listener = new jfxbv.ChangeListener[B] {
|
||||
override def changed(
|
||||
observable: ObservableValue[_ <: B],
|
||||
oldValue: B,
|
||||
newValue: B
|
||||
): Unit = {
|
||||
sub.onNext((observable, oldValue, newValue))
|
||||
}
|
||||
}
|
||||
|
||||
prop.addListener(listener)
|
||||
|
||||
c := Cancelable(() => prop.removeListener(listener))
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit final class OnActionObservable(
|
||||
private val button: ButtonBase
|
||||
) extends AnyVal {
|
||||
|
@ -49,6 +49,7 @@ import monix.execution.cancelables.SingleAssignCancelable
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.OverflowStrategy
|
||||
import monix.reactive.observers.Subscriber
|
||||
import org.slf4j.Logger
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.state.MyBaseState
|
||||
|
||||
@ -794,4 +795,67 @@ package object implicits {
|
||||
def +=(node: scalafx.scene.Node) = jfxui.attachChild(node)
|
||||
def -=(node: scalafx.scene.Node) = jfxui.detachChild(node)
|
||||
}
|
||||
|
||||
implicit class AkkaLoggerExt(private val logger: Logger) extends AnyVal {
|
||||
def logP[T](
|
||||
x: sourcecode.Text[T],
|
||||
tag: String = "",
|
||||
width: Int = 100,
|
||||
height: Int = 500,
|
||||
indent: Int = 2,
|
||||
initialOffset: Int = 0
|
||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||
|
||||
// def joinSeq[T](seq: Seq[T], sep: T): Seq[T] = {
|
||||
// seq.flatMap(x => Seq(x, sep)).dropRight(1)
|
||||
// }
|
||||
val tagStrs =
|
||||
if (tag.isEmpty) Seq.empty
|
||||
else Seq(fansi.Color.Cyan(tag), fansi.Str(" "))
|
||||
val prefix = Seq(
|
||||
fansi.Color.Magenta(fileName.value),
|
||||
fansi.Str(":"),
|
||||
fansi.Color.Green(line.value.toString),
|
||||
fansi.Str(" "),
|
||||
fansi.Color.Cyan(x.source),
|
||||
fansi.Str(": ")
|
||||
) ++ tagStrs
|
||||
fansi.Str.join(
|
||||
prefix ++ pprint.tokenize(x.value, width, height, indent).toSeq: _*
|
||||
)
|
||||
// x.value
|
||||
}
|
||||
|
||||
def warnP[T](
|
||||
s: sourcecode.Text[T]
|
||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||
logger.warn(logP(s).render)
|
||||
s
|
||||
}
|
||||
def errorP[T](
|
||||
s: sourcecode.Text[T]
|
||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||
logger.error(logP(s).render)
|
||||
s
|
||||
}
|
||||
def infoP[T](
|
||||
s: sourcecode.Text[T]
|
||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||
logger.info(logP(s).render)
|
||||
s
|
||||
}
|
||||
def debugP[T](
|
||||
s: sourcecode.Text[T]
|
||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||
logger.debug(logP(s).render)
|
||||
s
|
||||
}
|
||||
def traceP[T](
|
||||
s: sourcecode.Text[T]
|
||||
)(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
|
||||
logger.trace(logP(s).render)
|
||||
s
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,31 @@
|
||||
package wow.doge.mygame.launcher
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import cats.effect.concurrent.Deferred
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.value.ObservableValue
|
||||
import monix.bio.Task
|
||||
import monix.catnap.CancelableF
|
||||
import monix.eval.{Task => ETask}
|
||||
import monix.execution.CancelablePromise
|
||||
import monix.reactive.Observable
|
||||
import monix.{eval => me}
|
||||
import scalafx.Includes._
|
||||
import scalafx.application.JFXApp
|
||||
import scalafx.application.JFXApp.PrimaryStage
|
||||
import scalafx.beans.property.StringProperty
|
||||
import scalafx.scene.control.Button
|
||||
import scalafx.stage.StageStyle
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.implicits.JavaFXMonixObservables._
|
||||
import wow.doge.mygame.utils.IOUtils._
|
||||
|
||||
import cats.effect.Resource
|
||||
import cats.kernel.Eq
|
||||
object Launcher {
|
||||
sealed trait LauncherResult
|
||||
object LauncherResult {
|
||||
case object LaunchGame extends LauncherResult
|
||||
case object Exit extends LauncherResult
|
||||
|
||||
implicit val eqForLR = Eq.fromUniversalEquals[LauncherResult]
|
||||
}
|
||||
|
||||
class Props(
|
||||
@ -44,10 +47,23 @@ class Launcher private (props: Launcher.Props) {
|
||||
.observableAction()
|
||||
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))
|
||||
|
||||
def testChangeObs(
|
||||
obs: Observable[(ObservableValue[_ <: String], String, String)]
|
||||
) =
|
||||
obs
|
||||
.doOnNext {
|
||||
case (x, y, z) => monix.eval.Task.unit
|
||||
}
|
||||
// .subscribe()
|
||||
|
||||
private lazy val exitButton = new Button {
|
||||
text = "Exit"
|
||||
// text <-- testChangeObs
|
||||
}
|
||||
|
||||
// exitButton.text.bind
|
||||
StringProperty("") addListener ((_, _, _) => ())
|
||||
|
||||
private lazy val exitAction =
|
||||
exitButton
|
||||
.observableAction()
|
||||
@ -60,22 +76,25 @@ class Launcher private (props: Launcher.Props) {
|
||||
scene = _scene
|
||||
}
|
||||
|
||||
private lazy val internal = new JFXApp {
|
||||
stage = _stage
|
||||
stage.initStyle(StageStyle.Undecorated)
|
||||
// ResizeHelper.addResizeListener(stage)
|
||||
}
|
||||
private def internal(startSignal: CancelablePromise[Unit]) =
|
||||
new JFXApp {
|
||||
stage = _stage
|
||||
stage.initStyle(StageStyle.Undecorated)
|
||||
// ResizeHelper.addResizeListener(stage)
|
||||
startSignal.success(())
|
||||
}
|
||||
|
||||
private lazy val sceneDragObservable = {
|
||||
lazy val mpo = _scene.observableMousePressed()
|
||||
lazy val mdo = _scene.observableMouseDragged()
|
||||
val mpo = _scene.observableMousePressed()
|
||||
val mdo = _scene.observableMouseDragged()
|
||||
|
||||
mpo.mergeMap(pressEvent =>
|
||||
mpo.concatMap(pressEvent =>
|
||||
mdo.doOnNext(dragEvent =>
|
||||
ETask(
|
||||
_stage.setX(dragEvent.screenX - pressEvent.sceneX)
|
||||
) >>
|
||||
ETask(
|
||||
me.Task(pprint.log("emitted")) >>
|
||||
me.Task(
|
||||
_stage.setX(dragEvent.screenX - pressEvent.sceneX)
|
||||
) >>
|
||||
me.Task(
|
||||
_stage.setY(
|
||||
dragEvent.screenY - pressEvent.sceneY
|
||||
)
|
||||
@ -84,21 +103,58 @@ class Launcher private (props: Launcher.Props) {
|
||||
)
|
||||
}
|
||||
|
||||
def init(delay: FiniteDuration = 2000.millis) =
|
||||
for {
|
||||
_ <- Task(Platform.setImplicitExit(false))
|
||||
// import cats.syntax.all._
|
||||
|
||||
fib <- Task(internal.main(Array.empty)).start
|
||||
_ <- Task.sleep(500.millis)
|
||||
sceneDragFib <- toIO(sceneDragObservable.completedL).start
|
||||
fib2 <- toIO(
|
||||
Observable(launchAction, exitAction).merge
|
||||
.doOnNext(_ =>
|
||||
ETask(internal.stage.close()).executeOn(props.schedulers.fx)
|
||||
// def init(delay: FiniteDuration = 2000.millis) =
|
||||
// for {
|
||||
// _ <- Task(Platform.setImplicitExit(false))
|
||||
// x <- (Task.unit.start, Task.unit.start).parTupled
|
||||
// fxAppStartFib <- Task(internal.main(Array.empty)).start
|
||||
// _ <- Task.sleep(500.millis)
|
||||
// sceneDragFib <- toIO(sceneDragObservable.completedL).start
|
||||
// buttonActionsComposedFib <- toIO(
|
||||
// Observable(launchAction, exitAction).merge
|
||||
// .doOnNext(_ =>
|
||||
// me.Task(internal.stage.close()).executeOn(props.schedulers.fx)
|
||||
// )
|
||||
// .completedL
|
||||
// ).start
|
||||
// c <- CancelableF[Task](
|
||||
// fxAppStartFib.cancel >> buttonActionsComposedFib.cancel >> sceneDragFib.cancel
|
||||
// )
|
||||
// } yield (c)
|
||||
|
||||
def init =
|
||||
Resource.make(for {
|
||||
_ <- Task(Platform.setImplicitExit(false))
|
||||
startSignal <- Task(CancelablePromise[Unit]())
|
||||
delegate <- Task(internal(startSignal))
|
||||
combinedFib <-
|
||||
Task
|
||||
.parZip2(
|
||||
Task(delegate.main(Array.empty)),
|
||||
Task.fromCancelablePromise(startSignal) >> toIO(
|
||||
me.Task.parSequence(
|
||||
List(
|
||||
sceneDragObservable.completedL,
|
||||
Observable(launchAction, exitAction).merge
|
||||
.doOnNext(_ =>
|
||||
me.Task(delegate.stage.close())
|
||||
.executeOn(props.schedulers.fx)
|
||||
)
|
||||
.completedL
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.completedL
|
||||
).start
|
||||
c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel)
|
||||
} yield (c)
|
||||
.start
|
||||
c <- CancelableF[Task](
|
||||
// Task(println("Cancelling")) >>
|
||||
// combinedFib.cancel >>
|
||||
// fxAppStartFib.cancel
|
||||
// Task.unit
|
||||
combinedFib.cancel
|
||||
)
|
||||
} yield c)(_.cancel)
|
||||
|
||||
}
|
||||
|
@ -18,22 +18,22 @@ import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
|
||||
class EventsModule(spawnProtocol: ActorSystem[SpawnProtocol.Command]) {
|
||||
implicit lazy val s = spawnProtocol.scheduler
|
||||
implicit val s = spawnProtocol.scheduler
|
||||
|
||||
implicit lazy val timeout = Timeout(1.second)
|
||||
implicit val timeout = Timeout(1.second)
|
||||
|
||||
lazy val eventBusLogger = SLogger[EventBus[_]]
|
||||
val eventBusLogger = SLogger[EventBus[_]]
|
||||
|
||||
lazy val playerEventBusTask =
|
||||
val playerEventBusTask =
|
||||
createEventBus[PlayerEvent]("playerEventBus")
|
||||
|
||||
// lazy val playerCameraEventBusTask =
|
||||
// val playerCameraEventBusTask =
|
||||
// createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
|
||||
|
||||
lazy val tickEventBusTask =
|
||||
val tickEventBusTask =
|
||||
createEventBus[TickEvent]("tickEventBus", Level.TRACE)
|
||||
|
||||
lazy val mainEventBusTask = createEventBus[Event]("mainEventBus")
|
||||
val mainEventBusTask = createEventBus[Event]("mainEventBus")
|
||||
|
||||
def createEventBus[T](busName: String, logLevel: Level = Level.DEBUG) =
|
||||
spawnProtocol.askL(
|
||||
|
@ -41,7 +41,7 @@ object ScriptActor {
|
||||
result: ActorRef[Map[os.Path, Either[Error, Any]]]
|
||||
) extends Command
|
||||
|
||||
lazy val defaultScalaRunner =
|
||||
val defaultScalaRunner =
|
||||
ammonite
|
||||
.Main(
|
||||
storageBackend = new Folder(
|
||||
@ -51,13 +51,13 @@ object ScriptActor {
|
||||
)
|
||||
)
|
||||
|
||||
lazy val defaultKotlinRunner: KotlinScriptEngine = {
|
||||
val defaultKotlinRunner: KotlinScriptEngine = {
|
||||
val manager = new ScriptEngineManager()
|
||||
val engine = manager.getEngineByExtension("main.kts")
|
||||
engine.taggedWith[Kotlin]
|
||||
}
|
||||
|
||||
lazy val defaultGroovyRunner: GroovyScriptEngine =
|
||||
val defaultGroovyRunner: GroovyScriptEngine =
|
||||
new GroovyScriptEngine(os.pwd.toString)
|
||||
|
||||
def apply(
|
||||
|
@ -1,5 +1,6 @@
|
||||
package wow.doge.mygame.scriptsystem
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
|
||||
@ -14,8 +15,9 @@ import akka.actor.typed.scaladsl.Routers
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.state.ScriptActor
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import ScriptActor.ScriptObject
|
||||
|
||||
object ScriptCachingActor {
|
||||
@ -188,10 +190,10 @@ class ScriptCachingActor(
|
||||
Behaviors.same
|
||||
|
||||
case Put(scriptPath, script) =>
|
||||
ctx.log.debug(s"Putting $script at path $scriptPath")
|
||||
ctx.log.debugP(s"Putting $script at path $scriptPath")
|
||||
val newState =
|
||||
state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
|
||||
ctx.log.trace(newState.toString())
|
||||
ctx.log.traceP(newState.toString())
|
||||
receiveMessage(state = newState)
|
||||
|
||||
case NoOp => Behaviors.same
|
||||
@ -224,14 +226,14 @@ private[scriptsystem] object Methods {
|
||||
scriptsMap
|
||||
.get(scriptPath)
|
||||
.fold {
|
||||
ctx.log.debug("Delegating to child")
|
||||
ctx.log.debugP("Delegating to child")
|
||||
ctx.self ! DelegateToChild(
|
||||
scriptActor,
|
||||
scriptPath,
|
||||
requester
|
||||
)
|
||||
} { s =>
|
||||
ctx.log.debug("Getting script from cache")
|
||||
ctx.log.debugP("Getting script from cache")
|
||||
requester ! Right(s)
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import monix.bio.Task
|
||||
import wow.doge.mygame.scriptsystem.ScriptCachingActor
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
@ -20,29 +19,18 @@ object ScriptInitMode {
|
||||
}
|
||||
class ScriptSystemResource(
|
||||
path: os.Path,
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
mode: ScriptInitMode = ScriptInitMode.Lazy
|
||||
)(implicit timeout: Timeout, scheduler: Scheduler) {
|
||||
val make = {
|
||||
// throw new Exception("boom")
|
||||
findScriptFiles(os.pwd / "assets" / "scripts")
|
||||
|
||||
lazy val scriptCacheActor = AkkaUtils.spawnActorL(
|
||||
spawnProtocol,
|
||||
"scriptCachingActor",
|
||||
ScriptCachingActor()
|
||||
)
|
||||
|
||||
Resource.liftF(scriptCacheActor)
|
||||
}
|
||||
// sys.ask(ref => ScriptCachingActor.GetAll(os.pwd/'assets/'scripts/'scala/"hello2.sc",ref, false))
|
||||
)(implicit
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
timeout: Timeout,
|
||||
scheduler: Scheduler
|
||||
) {
|
||||
|
||||
val init = for {
|
||||
scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts"))
|
||||
scriptCacheActor <- AkkaUtils.spawnActorL(
|
||||
spawnProtocol,
|
||||
"scriptCachingActor",
|
||||
ScriptCachingActor()
|
||||
ScriptCachingActor(),
|
||||
"scriptCachingActor"
|
||||
)
|
||||
} yield (scriptCacheActor)
|
||||
|
||||
|
@ -9,7 +9,7 @@ import akka.util.Timeout
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
object AkkaUtils {
|
||||
def spawnActorL[T](
|
||||
def spawnActorOldL[T](
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
actorName: String,
|
||||
behavior: Behavior[T]
|
||||
@ -22,7 +22,7 @@ object AkkaUtils {
|
||||
_
|
||||
)
|
||||
)
|
||||
def spawnActorL2[T](
|
||||
def spawnActorL[T](
|
||||
behavior: Behavior[T],
|
||||
actorName: String
|
||||
)(implicit
|
||||
|
@ -21,7 +21,7 @@ class GenericConsoleStream[T](
|
||||
)(implicit
|
||||
cs: ConsoleStreamable[T]
|
||||
) extends PrintStream(outputStream, true) {
|
||||
private lazy val defaultOut = System.out
|
||||
private val defaultOut = System.out
|
||||
|
||||
def printToStreamable(stble: Option[T], text: String) =
|
||||
stble.foreach(s => cs.println(s, text))
|
||||
@ -57,7 +57,7 @@ object GenericConsoleStream {
|
||||
*/
|
||||
case class Config(exclusive: Boolean = false)
|
||||
object Config {
|
||||
lazy val default = Config()
|
||||
val default = Config()
|
||||
}
|
||||
|
||||
implicit val implJFXConsoleStreamForTextArea =
|
||||
|
Loading…
Reference in New Issue
Block a user