forked from nova/jmonkey-test
Compare commits
14 Commits
developmen
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
ece29b6b0d | |||
632bcccef3 | |||
8f3c08f271 | |||
dd01b070ff | |||
4ff54c1373 | |||
44f0538b8b | |||
9b484e895b | |||
1b9bb4265f | |||
f0ae3625bf | |||
be9acf81d5 | |||
67201c8f7e | |||
1422d91b14 | |||
92aae68254 | |||
12b232fb3c |
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@ metals.sbt
|
||||
.metals
|
||||
.bloop
|
||||
.ammonite
|
||||
.bsp
|
||||
|
||||
# Scala-IDE specific
|
||||
.scala_dependencies
|
||||
|
8
.scalafix.conf
Normal file
8
.scalafix.conf
Normal file
@ -0,0 +1,8 @@
|
||||
rules = [
|
||||
# ScalalintClasses,
|
||||
# ScalalintImports,
|
||||
# ScalalintPackages,
|
||||
# ScalalintInference,
|
||||
OrganizeImports
|
||||
]
|
||||
# ScalalintClasses.removeEmptyConstructor = false
|
47
build.sbt
Normal file → Executable file
47
build.sbt
Normal file → Executable file
@ -7,7 +7,7 @@ resolvers += "Jitpack" at "https://jitpack.io"
|
||||
resolvers += Resolver.mavenLocal
|
||||
resolvers += Resolver.sonatypeRepo("snapshots")
|
||||
|
||||
lazy val jmeVersion = "3.3.2-stable"
|
||||
val jmeVersion = "3.3.2-stable"
|
||||
|
||||
lazy val osName = System.getProperty("os.name") match {
|
||||
case n if n.startsWith("Linux") => "linux"
|
||||
@ -18,6 +18,7 @@ lazy val osName = System.getProperty("os.name") match {
|
||||
lazy val javaFXModules =
|
||||
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
|
||||
|
||||
testFrameworks += new TestFramework("munit.Framework")
|
||||
lazy val root = (project in file(".")).settings(
|
||||
name := "mygame",
|
||||
organization := "wow.doge",
|
||||
@ -33,6 +34,7 @@ lazy val root = (project in file(".")).settings(
|
||||
"com.simsilica" % "zay-es" % "1.2.1",
|
||||
"org.typelevel" %% "cats-core" % "2.3.0",
|
||||
"com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full,
|
||||
// "com.lihaoyi" % "ammonite" % "2.3.8" % "test" 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 (),
|
||||
@ -42,6 +44,7 @@ lazy val root = (project in file(".")).settings(
|
||||
"org.typelevel" %% "cats-effect" % "2.3.0",
|
||||
"io.monix" %% "monix" % "3.2.2",
|
||||
"io.monix" %% "monix-bio" % "1.1.0",
|
||||
"io.monix" %% "monix-nio" % "0.0.9",
|
||||
"io.circe" %% "circe-core" % "0.13.0",
|
||||
"io.circe" %% "circe-generic" % "0.13.0",
|
||||
"com.softwaremill.sttp.client3" %% "core" % "3.0.0",
|
||||
@ -67,13 +70,24 @@ lazy val root = (project in file(".")).settings(
|
||||
"org.scalatest" %% "scalatest" % "3.2.2" % "test",
|
||||
"org.typelevel" %% "cats-mtl" % "1.1.1",
|
||||
"io.estatico" %% "newtype" % "0.4.4",
|
||||
"io.methvin" %% "directory-watcher-better-files" % "0.14.0"
|
||||
"io.methvin" %% "directory-watcher-better-files" % "0.14.0",
|
||||
"com.github.rohan-sircar" % "scalafx-utils" % "0.15.0-SNAPSHOT",
|
||||
"com.jfoenix" % "jfoenix" % "9.0.10",
|
||||
"org.kordamp.ikonli" % "ikonli-core" % "12.0.0",
|
||||
"org.kordamp.ikonli" % "ikonli-javafx" % "12.0.0",
|
||||
"org.kordamp.ikonli" % "ikonli-fontawesome5-pack" % "12.0.0",
|
||||
"org.kordamp.ikonli" % "ikonli-material-pack" % "12.0.0",
|
||||
"org.kordamp.bootstrapfx" % "bootstrapfx-core" % "0.4.0",
|
||||
"org.scalameta" %% "munit" % "0.7.23" % Test,
|
||||
"de.lolhens" %% "munit-tagless-final" % "0.0.1" % Test,
|
||||
"org.scalameta" %% "munit-scalacheck" % "0.7.23" % Test,
|
||||
"org.scalacheck" %% "scalacheck" % "1.15.3" % Test
|
||||
),
|
||||
// Determine OS version of JavaFX binaries
|
||||
|
||||
// Add JavaFX dependencies
|
||||
libraryDependencies ++= javaFXModules.map(m =>
|
||||
"org.openjfx" % s"javafx-$m" % "14.0.1" classifier osName
|
||||
"org.openjfx" % s"javafx-$m" % "11.0.1" classifier osName
|
||||
),
|
||||
scalacOptions ++= Seq(
|
||||
"-encoding",
|
||||
@ -126,7 +140,8 @@ lazy val root = (project in file(".")).settings(
|
||||
// oldStrategy(x)
|
||||
}
|
||||
)
|
||||
initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
|
||||
// initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
|
||||
ammoniteVersion := "2.2.0"
|
||||
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
|
||||
addCompilerPlugin(
|
||||
"org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full
|
||||
@ -135,7 +150,29 @@ inThisBuild(
|
||||
List(
|
||||
scalaVersion := scalaVersion.value, // 2.11.12, or 2.13.3
|
||||
semanticdbEnabled := true, // enable SemanticDB
|
||||
semanticdbVersion := "4.3.24" // use Scalafix compatible version
|
||||
semanticdbVersion := "4.4.10" // use Scalafix compatible version
|
||||
)
|
||||
)
|
||||
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.4.3"
|
||||
scalafixDependencies in ThisBuild += "org.scalalint" %% "rules" % "0.1.4"
|
||||
|
||||
wartremoverErrors in (Compile, compile) ++=
|
||||
Warts.allBut(
|
||||
Wart.Any,
|
||||
Wart.NonUnitStatements,
|
||||
// Wart.StringPlusAny,
|
||||
Wart.Overloading,
|
||||
Wart.PublicInference,
|
||||
Wart.Nothing,
|
||||
Wart.Var,
|
||||
Wart.DefaultArguments,
|
||||
// Wart.MutableDataStructures,
|
||||
Wart.ImplicitConversion,
|
||||
Wart.ImplicitParameter,
|
||||
Wart.ToString,
|
||||
Wart.Recursion,
|
||||
Wart.While,
|
||||
Wart.ExplicitImplicitTypes,
|
||||
Wart.ListUnapply
|
||||
)
|
||||
// Seq(Wart.FinalCaseClass)
|
||||
|
@ -1 +1 @@
|
||||
sbt.version=1.3.13
|
||||
sbt.version=1.4.7
|
||||
|
@ -1,2 +1,7 @@
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23")
|
||||
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
|
||||
addSbtPlugin(
|
||||
"com.thoughtworks.deeplearning" % "sbt-ammonite-classpath" % "2.0.0"
|
||||
)
|
||||
addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.4.13")
|
||||
|
11
src/main/resources/main.css
Normal file
11
src/main/resources/main.css
Normal file
@ -0,0 +1,11 @@
|
||||
.red-bar > .bar {
|
||||
-fx-background-color: red;
|
||||
}
|
||||
|
||||
.green-bar > .bar {
|
||||
-fx-background-color: green;
|
||||
}
|
||||
|
||||
.yellow-bar > .bar {
|
||||
-fx-background-color: yellow;
|
||||
}
|
@ -60,11 +60,9 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
|
||||
.allocated
|
||||
.unsafeRunSync()
|
||||
|
||||
{
|
||||
ArraySeq(release1, release2, release3).foreach(r =>
|
||||
sys.addShutdownHook(r.unsafeRunSync())
|
||||
)
|
||||
}
|
||||
ArraySeq(release1, release2, release3).foreach(r =>
|
||||
sys.addShutdownHook(r.unsafeRunSync())
|
||||
)
|
||||
|
||||
val loggers: PartialFunction[String, Logger[IO]] = {
|
||||
case "some.external.package.SpecificClass" =>
|
||||
|
@ -9,7 +9,7 @@ import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||
import wow.doge.mygame.utils.wrappers.jme.NodeWrapper2
|
||||
|
||||
sealed trait AppError
|
||||
sealed trait AppError extends Product with Serializable
|
||||
object AppError {
|
||||
final case class TimeoutError(reason: String) extends AppError
|
||||
object TimeoutError {
|
||||
|
@ -2,22 +2,24 @@ package wow.doge.mygame
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import _root_.monix.bio.BIOApp
|
||||
import _root_.monix.bio.Task
|
||||
import _root_.monix.bio.UIO
|
||||
import _root_.monix.execution.Scheduler
|
||||
import akka.util.Timeout
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.Resource
|
||||
import cats.implicits._
|
||||
import io.odin._
|
||||
import io.odin.consoleLogger
|
||||
import io.odin.fileLogger
|
||||
import io.odin.json.Formatter
|
||||
import io.odin.syntax._
|
||||
import monix.bio.BIOApp
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.execution.Scheduler
|
||||
import scalafx.scene.control.TextArea
|
||||
import wow.doge.mygame.ActorSystemResource
|
||||
import wow.doge.mygame.executors.ExecutorsModule
|
||||
import wow.doge.mygame.types.AkkaScheduler
|
||||
import wow.doge.mygame.utils.GenericConsoleStream
|
||||
import io.odin
|
||||
object Main extends BIOApp with ExecutorsModule {
|
||||
import java.util.logging.{Logger => JLogger, Level}
|
||||
JLogger.getLogger("").setLevel(Level.SEVERE)
|
||||
@ -28,14 +30,14 @@ object Main extends BIOApp with ExecutorsModule {
|
||||
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
|
||||
for {
|
||||
logger <-
|
||||
consoleLogger().withAsync(
|
||||
consoleLogger(minLevel = odin.Level.Debug).withAsync(
|
||||
timeWindow = 1.milliseconds,
|
||||
maxBufferSize = Some(100)
|
||||
) |+|
|
||||
fileLogger(
|
||||
"application-log-1.log",
|
||||
Formatter.json
|
||||
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
||||
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(100))
|
||||
jmeScheduler <- jmeSchedulerResource
|
||||
// backend <- Resource.make(toIO(HttpClientMonixBackend()))(backend =>
|
||||
// toIO(backend.close())
|
||||
|
@ -1,5 +1,8 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import scala.annotation.switch
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
@ -8,6 +11,8 @@ import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import cats.syntax.eq._
|
||||
import cats.syntax.show._
|
||||
import com.jayfella.jme.jfx.JavaFxUI
|
||||
import com.jme3.asset.plugins.ZipLocator
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.input.InputManager
|
||||
@ -28,9 +33,15 @@ import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.eval.Coeval
|
||||
import monix.execution.exceptions.DummyException
|
||||
import monix.execution.cancelables.CompositeCancelable
|
||||
import monix.reactive.Observable
|
||||
import monix.{eval => me}
|
||||
import scalafx.scene.control.Label
|
||||
import scalafx.scene.control.TextArea
|
||||
import scalafx.scene.layout.HBox
|
||||
import scalafx.scene.layout.Priority
|
||||
import scalafx.scene.layout.VBox
|
||||
import scalafx.scene.paint.Color
|
||||
import wow.doge.mygame.AppError.TimeoutError
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.game.GameApp
|
||||
@ -40,10 +51,12 @@ import wow.doge.mygame.game.entities.CharacterStats
|
||||
import wow.doge.mygame.game.entities.EntityIds
|
||||
import wow.doge.mygame.game.entities.NpcActorSupervisor
|
||||
import wow.doge.mygame.game.entities.NpcMovementActor
|
||||
import wow.doge.mygame.game.entities.PlayerActorSupervisor
|
||||
import wow.doge.mygame.game.entities.PlayerController
|
||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
||||
import wow.doge.mygame.game.entities.player.PlayerActor
|
||||
import wow.doge.mygame.game.entities.player.PlayerController
|
||||
import wow.doge.mygame.game.entities.player.PlayerMovementReducer
|
||||
import wow.doge.mygame.game.subsystems.input.InputMappings
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput
|
||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.launcher.Launcher
|
||||
@ -59,14 +72,20 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.StatsEvent.DamageEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||
import wow.doge.mygame.types._
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.utils.GenericConsoleStream
|
||||
import wow.doge.mygame.utils.IOUtils
|
||||
import wow.doge.mygame.utils.MonixDirectoryWatcher
|
||||
import wow.doge.mygame.utils.MonixDirectoryWatcher.ModifyEvent
|
||||
import wow.doge.mygame.utils.controls.JFXProgressBar
|
||||
import wow.doge.mygame.utils.wrappers.jme
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||
|
||||
class MainApp(
|
||||
logger: Logger[Task],
|
||||
jmeThread: JmeScheduler,
|
||||
@ -79,8 +98,13 @@ class MainApp(
|
||||
) {
|
||||
implicit val as = scheduler.value
|
||||
|
||||
val scriptSystemInit =
|
||||
new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init
|
||||
val scriptSystemResource: Resource[UIO, ScriptCompiler] =
|
||||
new ScriptSystemResource(
|
||||
os.pwd,
|
||||
logger,
|
||||
ScriptInitMode.Eager,
|
||||
schedulers.io
|
||||
).init2
|
||||
|
||||
val eventsModule = new EventsModule(scheduler, spawnProtocol)
|
||||
|
||||
@ -101,7 +125,9 @@ class MainApp(
|
||||
IOUtils
|
||||
.toIO(
|
||||
obs
|
||||
.doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void)
|
||||
.doOnNextF(pme =>
|
||||
logger.trace(show"Received event $pme").toTask.void
|
||||
)
|
||||
.completedL
|
||||
.startAndForget
|
||||
)
|
||||
@ -114,7 +140,7 @@ class MainApp(
|
||||
viewPort <- gameApp.viewPort
|
||||
physicsSpace <- UIO.pure(gameApp.physicsSpace)
|
||||
_ <- logger.infoU("before")
|
||||
// jfxUI <- gameApp.jfxUI
|
||||
jfxUI <- gameApp.jfxUI.hideErrors
|
||||
consoleTextArea <- UIO(new TextArea {
|
||||
text = "hello \n"
|
||||
editable = false
|
||||
@ -122,6 +148,7 @@ class MainApp(
|
||||
// maxHeight = 150
|
||||
// maxWidth = 300
|
||||
})
|
||||
|
||||
// _ <- Task(consoleStream := consoleTextArea)
|
||||
// _ <- Task(jfxUI += consoleTextArea)
|
||||
_ <- logger.infoU("after")
|
||||
@ -135,11 +162,41 @@ class MainApp(
|
||||
def gameInit(
|
||||
tickEventBus: GameEventBus[TickEvent]
|
||||
): Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
|
||||
wire[GameAppResource].resource.evalMap {
|
||||
case Right(gameApp -> gameAppFib) =>
|
||||
eval(tickEventBus, gameApp, gameAppFib).attempt
|
||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||
}
|
||||
for {
|
||||
r1 <- wire[GameAppResource].resource.evalMap(e =>
|
||||
IO.fromEither(e)
|
||||
.flatMap {
|
||||
case (gameApp -> gameAppFib) =>
|
||||
eval(tickEventBus, gameApp, gameAppFib)
|
||||
}
|
||||
.attempt
|
||||
)
|
||||
dirWatcher <- Resource.liftF(
|
||||
MonixDirectoryWatcher(
|
||||
os.pwd / "assets" / "scripts"
|
||||
).hideErrors
|
||||
)
|
||||
sc <- scriptSystemResource
|
||||
obs = dirWatcher.doOnNext {
|
||||
case ModifyEvent(file, count) =>
|
||||
sc.request(ScriptCompiler.GetScript(os.Path(file.path), _, true))(
|
||||
15.seconds
|
||||
).toTask
|
||||
.void
|
||||
case _ => me.Task.unit
|
||||
}
|
||||
_ <- Resource.make(obs.completedL.toIO.hideErrors.start)(_.cancel)
|
||||
// _ <-
|
||||
// dirWatcher
|
||||
// .doOnNextF(event =>
|
||||
// Coeval(pprint.log(show"Received file event $event")).void
|
||||
// )
|
||||
// .completedL
|
||||
// .executeOn(schedulers.io.value)
|
||||
// .startAndForget
|
||||
// .toIO
|
||||
// .hideErrors
|
||||
} yield r1
|
||||
|
||||
val program = for {
|
||||
// scriptSystem <- scriptSystemInit
|
||||
@ -150,7 +207,7 @@ class MainApp(
|
||||
.use(_ => launchSignal.get)
|
||||
.hideErrors
|
||||
tickEventBus <-
|
||||
eventsModule.tickEventBus.hideErrorsWith(e => DummyException(e.toString))
|
||||
eventsModule.tickEventBus.hideErrorsWith(e => new Exception(e.toString))
|
||||
_ <-
|
||||
/**
|
||||
* User chose to quit
|
||||
@ -185,7 +242,8 @@ class MainAppDelegate(
|
||||
viewPort: ViewPort,
|
||||
enqueueR: Function1[() => Unit, Unit],
|
||||
rootNode: RootNode,
|
||||
schedulers: Schedulers
|
||||
schedulers: Schedulers,
|
||||
jfxUI: JavaFxUI
|
||||
)(implicit
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
timeout: Timeout,
|
||||
@ -203,13 +261,14 @@ class MainAppDelegate(
|
||||
os.rel / "assets" / "town.zip",
|
||||
classOf[ZipLocator]
|
||||
)
|
||||
_ <- loggerL.infoU("test")
|
||||
|
||||
// _ <- Task(consoleStream.println("text"))
|
||||
level <- DefaultGameLevel(assetManager, viewPort)
|
||||
_ <- level.addToGame(rootNode, physicsSpace)
|
||||
playerActor <- createPlayerController()
|
||||
// .onErrorRestart(3)
|
||||
_ <- wire[GameInputHandler.Props].begin
|
||||
// _ <- wire[GameInputHandler.Props].begin
|
||||
_ <- new InputMappings(new jme.InputManager(inputManager)).setup
|
||||
// .onErrorRestart(3)
|
||||
johnActor <- createTestNpc("John")
|
||||
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
||||
@ -231,45 +290,45 @@ class MainAppDelegate(
|
||||
_ <-
|
||||
damageObs
|
||||
.doOnNextF(event =>
|
||||
(loggerL.debug(s"Received Damage Event $event") >>
|
||||
(loggerL.debug(show"Received Damage Event $event") >>
|
||||
(if (event.victimName === "PlayerNode")
|
||||
// playerActor !! PlayerActorSupervisor.TakeDamage(event.amount)
|
||||
playerActor.askL(
|
||||
PlayerActorSupervisor.TakeDamage2(event.amount, _)
|
||||
)
|
||||
playerActor
|
||||
.askL(PlayerActor.TakeDamage(event.amount, _))
|
||||
.void
|
||||
.onErrorHandle { case ex: TimeoutException => () }
|
||||
else IO.unit)).toTask
|
||||
)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
_ <-
|
||||
Observable
|
||||
.interval(1.second)
|
||||
.doOnNextF(_ =>
|
||||
playerActor
|
||||
.askL(PlayerActorSupervisor.GetStatus)
|
||||
.flatMap(s => loggerL.debug(s"Player actor status: $s"))
|
||||
// _ <-
|
||||
// Observable
|
||||
// .interval(1.second)
|
||||
// .doOnNextF(_ =>
|
||||
// playerActor
|
||||
// .askL(PlayerActorSupervisor.GetStatus)
|
||||
// .flatMap(s => loggerL.debug(show"Player actor status: $s"))
|
||||
|
||||
// .flatMap(s =>
|
||||
// if (s == Status.Alive)
|
||||
// playerActor
|
||||
// .askL(PlayerActorSupervisor.CurrentStats )
|
||||
// .flatMap(s => loggerL.debug(s"Got state $s"))
|
||||
// else IO.unit
|
||||
// )
|
||||
.toTask
|
||||
)
|
||||
// .doOnNextF(_ =>
|
||||
// playerActor
|
||||
// .askL(PlayerActorSupervisor.GetStatus )
|
||||
// .flatMap(s => loggerL.debug(s"Player actor status: $s"))
|
||||
// .toTask
|
||||
// )
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
// // .flatMap(s =>
|
||||
// // if (s == Status.Alive)
|
||||
// // playerActor
|
||||
// // .askL(PlayerActorSupervisor.CurrentStats )
|
||||
// // .flatMap(s => loggerL.debug(show"Got state $s"))
|
||||
// // else IO.unit
|
||||
// // )
|
||||
// .toTask
|
||||
// )
|
||||
// // .doOnNextF(_ =>
|
||||
// // playerActor
|
||||
// // .askL(PlayerActorSupervisor.GetStatus )
|
||||
// // .flatMap(s => loggerL.debug(show"Player actor status: $s"))
|
||||
// // .toTask
|
||||
// // )
|
||||
// .completedL
|
||||
// .toIO
|
||||
// .hideErrors
|
||||
// .startAndForget
|
||||
_ <-
|
||||
physicsSpace.collisionObservable
|
||||
// .filter(event =>
|
||||
@ -282,7 +341,7 @@ class MainAppDelegate(
|
||||
// )
|
||||
// .doOnNextF(event =>
|
||||
// loggerL
|
||||
// .debug(s"$event ${event.appliedImpulse()}")
|
||||
// .debug(show"$event ${event.appliedImpulse()}")
|
||||
// .toTask
|
||||
// )
|
||||
.filter(_.nodeA.map(_.getName =!= "main-scene_node").getOrElse(false))
|
||||
@ -295,7 +354,7 @@ class MainAppDelegate(
|
||||
} yield if (nodeB.getName === "John") nodeA else nodeB)
|
||||
_ <- Coeval(
|
||||
victim.foreach { v =>
|
||||
pprint.log(s"emitted event ${v.getName}")
|
||||
pprint.log(show"emitted event ${v.getName}")
|
||||
mainEventBus ! EventBus.Publish(
|
||||
DamageEvent(
|
||||
"John",
|
||||
@ -322,11 +381,104 @@ class MainAppDelegate(
|
||||
// )
|
||||
// .executeOn(appScheduler)
|
||||
// .startAndForget
|
||||
statsObs <-
|
||||
playerActor
|
||||
.askL(PlayerActor.GetStatsObservable(_))
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
.flatten
|
||||
playerHud <-
|
||||
UIO
|
||||
.deferAction(implicit s =>
|
||||
UIO(new VBox {
|
||||
implicit val c = CompositeCancelable()
|
||||
hgrow = Priority.Always
|
||||
spacing = 10
|
||||
style = """-fx-background-color: rgba(0,0,0,0.7);"""
|
||||
stylesheets = Seq((os.rel / "main.css").toString)
|
||||
|
||||
children = List(
|
||||
new HBox {
|
||||
spacing = 5
|
||||
children = List(
|
||||
new Label("Health") { textFill = Color.White },
|
||||
new Label("100") {
|
||||
text <-- statsObs
|
||||
.doOnNextF(i => loggerL.trace(show"Received stats: $i"))
|
||||
.map(_.hp.toInt.toString)
|
||||
textFill = Color.White
|
||||
},
|
||||
new JFXProgressBar {
|
||||
|
||||
progress = 100
|
||||
minHeight = 10
|
||||
progress <-- statsObs
|
||||
.map(_.hp.toInt.toDouble / 100)
|
||||
c += statsObs
|
||||
.scanEval(me.Task.pure("green-bar")) {
|
||||
case (a, b) =>
|
||||
me.Task(styleClass.removeAll(a)) >>
|
||||
me.Task.pure(
|
||||
(b.hp.toInt: @switch) match {
|
||||
case v if v > 80 => "green-bar"
|
||||
case v if v > 20 && v <= 80 => "yellow-bar"
|
||||
case _ => "red-bar"
|
||||
}
|
||||
)
|
||||
}
|
||||
.doOnNext(cls => me.Task(styleClass += cls))
|
||||
.subscribe()
|
||||
}
|
||||
)
|
||||
},
|
||||
new HBox {
|
||||
spacing = 5
|
||||
children = List(
|
||||
new Label("Stamina") {
|
||||
textFill = Color.White
|
||||
|
||||
},
|
||||
new Label("100") {
|
||||
textFill = Color.White
|
||||
text <-- statsObs
|
||||
.doOnNextF(i => loggerL.trace(show"Received stats: $i"))
|
||||
.map(_.stamina.toInt.toString)
|
||||
},
|
||||
new JFXProgressBar {
|
||||
|
||||
progress = 100
|
||||
minHeight = 10
|
||||
progress <-- statsObs.map(_.stamina.toInt.toDouble / 100)
|
||||
|
||||
styleClass ++= Seq("green-bar")
|
||||
|
||||
c += statsObs
|
||||
.scanEval(me.Task.pure("green-bar")) {
|
||||
case (a, b) =>
|
||||
me.Task(styleClass.removeAll(a)) >>
|
||||
me.Task.pure(
|
||||
(b.stamina.toInt: @switch) match {
|
||||
case v if v > 80 => "green-bar"
|
||||
case v if v > 20 && v <= 40 => "yellow-bar"
|
||||
case _ => "red-bar"
|
||||
}
|
||||
)
|
||||
}
|
||||
.doOnNext(cls => me.Task(styleClass += cls))
|
||||
.subscribe()
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
.executeOn(schedulers.fx.value)
|
||||
_ <- UIO(jfxUI += playerHud)
|
||||
} yield ()
|
||||
|
||||
def createPlayerController(
|
||||
// appScheduler: monix.execution.Scheduler
|
||||
): IO[AppError, PlayerActorSupervisor.Ref] = {
|
||||
): IO[AppError, PlayerActor.Ref] = {
|
||||
val playerPos = ImVector3f.Zero
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
// val modelPath = os.rel / "Models" / "Oto" / "Oto.mesh.xml"
|
||||
@ -369,66 +521,44 @@ class MainAppDelegate(
|
||||
.scan(new Quaternion) {
|
||||
case (rotationBuf, action) =>
|
||||
action.binding match {
|
||||
// case PlayerCameraEvent.CameraLeft =>
|
||||
case PlayerCameraInput.CameraRotateLeft =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
// case PlayerCameraEvent.CameraRight =>
|
||||
case PlayerCameraInput.CameraRotateRight =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
// case PlayerCameraEvent.CameraMovedUp =>
|
||||
case PlayerCameraInput.CameraRotateUp =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
// case PlayerCameraEvent.CameraMovedDown =>
|
||||
case PlayerCameraInput.CameraRotateDown =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
}
|
||||
}
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
// _ <-
|
||||
// Observable
|
||||
// .interval(10.millis)
|
||||
// .doOnNextF(_ =>
|
||||
// Coeval {
|
||||
// val location = playerNode.getWorldTranslation()
|
||||
// cameraPivotNode.setLocalTranslation(location)
|
||||
// }
|
||||
// )
|
||||
// .completedL
|
||||
// .toIO
|
||||
// .hideErrors
|
||||
// .startAndForget
|
||||
|
||||
sched <- UIO.pure(schedulers.async)
|
||||
fxSched <- UIO.pure(schedulers.fx)
|
||||
playerActor <- wire[PlayerController.Props].create
|
||||
obs <-
|
||||
playerActor
|
||||
.askL(PlayerActorSupervisor.GetStatsObservable2)
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
playerMovementReducer = new PlayerMovementReducer(playerActor, loggerL)
|
||||
_ <-
|
||||
obs
|
||||
.doOnNext(s => loggerL.debug(s"Got state $s").toTask)
|
||||
inputManager
|
||||
.enumObservableAction(PlayerMovementInput)
|
||||
.sample(1.millis)
|
||||
.scanEval(me.Task.pure(PlayerMovementReducer.State.empty))(
|
||||
playerMovementReducer.value
|
||||
)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
@ -456,7 +586,7 @@ class MainAppDelegate(
|
||||
npcName,
|
||||
initialPos
|
||||
).behavior,
|
||||
actorName = Some(s"${npcName}-npcActorSupervisor")
|
||||
actorName = Some(show"${npcName}-npcActorSupervisor")
|
||||
)
|
||||
|
||||
(for {
|
||||
|
@ -16,11 +16,11 @@ import wow.doge.mygame.implicits._
|
||||
|
||||
object GameActorSystem {
|
||||
sealed trait Command
|
||||
case class GetSpawnProtocol(
|
||||
final case class GetSpawnProtocol(
|
||||
replyTo: ActorRef[ActorRef[SpawnProtocol.Command]]
|
||||
) extends Command
|
||||
|
||||
class Props() {
|
||||
class Props {
|
||||
def create =
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
val systemSpawnProtocol = ctx.spawnN(SpawnProtocol())
|
||||
@ -44,9 +44,9 @@ class GameActorSystem(
|
||||
|
||||
// object EventBusSupervisor {
|
||||
// sealed trait Command
|
||||
// case class GetMainEventBus(replyTo: ActorRef[GameEventBus[Event]])
|
||||
// final case class GetMainEventBus(replyTo: ActorRef[GameEventBus[Event]])
|
||||
// extends Command
|
||||
// case class GetEventBus[T](replyTo: ActorRef[GameEventBus[T]])(implicit
|
||||
// final case class GetEventBus[T](replyTo: ActorRef[GameEventBus[T]])(implicit
|
||||
// classTag: ClassTag[T]
|
||||
// ) extends Command {
|
||||
// def ct = classTag
|
||||
|
@ -47,6 +47,7 @@ object JMEExecutorService extends GUIExecutorService {
|
||||
}
|
||||
|
||||
object JMERunner {
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Null"))
|
||||
var runner: Application = null
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import monix.execution.Scheduler
|
||||
import monix.execution.UncaughtExceptionReporter
|
||||
|
||||
final case class Schedulers(
|
||||
blockingIO: Schedulers.IoScheduler,
|
||||
io: Schedulers.IoScheduler,
|
||||
async: Schedulers.AsyncScheduler,
|
||||
fx: Schedulers.FxScheduler
|
||||
)
|
||||
@ -32,8 +32,8 @@ object Schedulers {
|
||||
)
|
||||
)
|
||||
|
||||
case class AsyncScheduler(value: Scheduler)
|
||||
case class IoScheduler(value: Scheduler)
|
||||
case class FxScheduler(value: Scheduler)
|
||||
final case class AsyncScheduler(value: Scheduler)
|
||||
final case class IoScheduler(value: Scheduler)
|
||||
final case class FxScheduler(value: Scheduler)
|
||||
|
||||
}
|
||||
|
@ -9,22 +9,15 @@ import akka.actor.typed.SpawnProtocol
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import com.jme3.bullet.BulletAppState
|
||||
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
|
||||
import monix.bio.Fiber
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.catnap.ConcurrentChannel
|
||||
import monix.catnap.ConsumerF
|
||||
import monix.eval.Coeval
|
||||
import wow.doge.mygame.AppError
|
||||
import wow.doge.mygame.AppError.TimeoutError
|
||||
import wow.doge.mygame.Dispatchers
|
||||
@ -135,112 +128,3 @@ class GameAppResource(
|
||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||
}
|
||||
}
|
||||
|
||||
object GameApp {}
|
||||
|
||||
object Ops {
|
||||
final class AddToNode[T <: Node](private val node: T) extends AnyVal {
|
||||
|
||||
/**
|
||||
* Pure version
|
||||
*/
|
||||
def apply(spatial: Spatial)(implicit logger: Logger[Task]) =
|
||||
logger.debug(
|
||||
s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}"
|
||||
) >> Task(node.attachChild(spatial))
|
||||
|
||||
/**
|
||||
* Impure version
|
||||
*/
|
||||
def apply(spatial: Spatial)(implicit logger: SLogger) =
|
||||
Coeval {
|
||||
logger.debug(
|
||||
s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}"
|
||||
)
|
||||
node.attachChild(spatial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SpawnSystem {
|
||||
sealed trait Result
|
||||
case object Ok extends Result
|
||||
|
||||
sealed trait Complete
|
||||
case object Complete extends Complete
|
||||
|
||||
sealed trait SpawnRequest
|
||||
final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest
|
||||
|
||||
final case class SpawnRequestWrapper(
|
||||
spawnRequest: SpawnRequest,
|
||||
result: Deferred[Task, Result]
|
||||
)
|
||||
|
||||
def apply(logger: Logger[Task]) =
|
||||
for {
|
||||
spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper]
|
||||
spawnSystem <- Task(new SpawnSystem(logger, spawnChannel))
|
||||
consumer <-
|
||||
spawnChannel.consume
|
||||
.use(consumer => spawnSystem.receive(consumer))
|
||||
.startAndForget
|
||||
} yield (spawnSystem)
|
||||
}
|
||||
|
||||
class SpawnSystem(
|
||||
logger: Logger[Task],
|
||||
spawnChannel: ConcurrentChannel[
|
||||
Task,
|
||||
SpawnSystem.Complete,
|
||||
SpawnSystem.SpawnRequestWrapper
|
||||
]
|
||||
) {
|
||||
import SpawnSystem._
|
||||
|
||||
for {
|
||||
spawnSystem <- SpawnSystem(logger)
|
||||
res <- spawnSystem.request(SpawnSpatial(Task(new Node("Test"))))
|
||||
} yield ()
|
||||
|
||||
// val spawnChannel = ConcurrentChannel[Task].of[Result, SpawnRequest]
|
||||
|
||||
private def receive(
|
||||
consumer: ConsumerF[Task, Complete, SpawnRequestWrapper]
|
||||
): Task[Unit] =
|
||||
consumer.pull.flatMap {
|
||||
case Right(message) =>
|
||||
for {
|
||||
_ <-
|
||||
logger
|
||||
.debug(s"Received spawn request $message")
|
||||
_ <- handleSpawn(message)
|
||||
} yield receive(consumer)
|
||||
case Left(r) =>
|
||||
logger.info("Closing Spawn System")
|
||||
}
|
||||
|
||||
private def handleSpawn(spawnRequestWrapper: SpawnRequestWrapper) =
|
||||
spawnRequestWrapper match {
|
||||
case SpawnRequestWrapper(spawnRequest, result) =>
|
||||
spawnRequest match {
|
||||
case SpawnSpatial(spatialTask) =>
|
||||
spatialTask.flatMap(spatial =>
|
||||
logger.debug(
|
||||
s"Spawning spatial with name ${spatial.getName()}"
|
||||
) >> result
|
||||
.complete(Ok)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def request(spawnRequest: SpawnRequest) =
|
||||
for {
|
||||
d <- Deferred[Task, Result]
|
||||
_ <- spawnChannel.push(SpawnRequestWrapper(spawnRequest, d))
|
||||
res <- d.get
|
||||
} yield (res)
|
||||
|
||||
def stop = spawnChannel.halt(Complete)
|
||||
}
|
||||
|
@ -22,12 +22,13 @@ object GameAppActor {
|
||||
case object Start extends Command
|
||||
case object Pause extends Command
|
||||
case object Ping extends Command
|
||||
case class Stop(stopSignal: ActorRef[CancelableFuture[Unit]]) extends Command
|
||||
case class GetSpawnProtocol(
|
||||
final case class Stop(stopSignal: ActorRef[CancelableFuture[Unit]])
|
||||
extends Command
|
||||
final case class GetSpawnProtocol(
|
||||
replyTo: ActorRef[ActorRef[SpawnProtocol.Command]]
|
||||
) extends Command
|
||||
|
||||
case class Props(tickEventBus: GameEventBus[TickEvent]) {
|
||||
final case class Props(tickEventBus: GameEventBus[TickEvent]) {
|
||||
def behavior =
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Hello from GameAppActor")
|
||||
@ -48,12 +49,12 @@ object GameAppActor {
|
||||
|
||||
val sp = ctx.spawn(SpawnProtocol(), "gameSpawnProtocol")
|
||||
|
||||
ctx.spawn(
|
||||
GenericTimerActor
|
||||
.Props(ctx.self, Ping, 1000.millis)
|
||||
.behavior,
|
||||
"pingTimer"
|
||||
) ! GenericTimerActor.Start
|
||||
// ctx.spawn(
|
||||
// GenericTimerActor
|
||||
// .Props(ctx.self, Ping, 1000.millis)
|
||||
// .behavior,
|
||||
// "pingTimer"
|
||||
// ) ! GenericTimerActor.Start
|
||||
|
||||
val stopPromise = CancelablePromise[Unit]()
|
||||
|
||||
|
@ -1,30 +0,0 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
|
||||
// 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)
|
||||
|
||||
// /**
|
||||
// * 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"))
|
||||
// }
|
@ -1,7 +1,6 @@
|
||||
package wow.doge.mygame.game
|
||||
|
||||
import scala.collection.immutable.Queue
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
import com.jme3.app.SimpleApplication
|
||||
@ -28,6 +27,7 @@ class SimpleAppExt(
|
||||
|
||||
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||
private val terminationSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||
private implicit val ec = schedulers.async.value
|
||||
|
||||
var cancelToken: Option[() => Future[Unit]] = None
|
||||
|
||||
@ -87,7 +87,7 @@ class SimpleAppExt(
|
||||
val scheduler = Scheduler(JMEExecutorService)
|
||||
}
|
||||
object SimpleAppExt {
|
||||
private[game] case class MyTask[T](p: CancelablePromise[T], cb: () => T)
|
||||
private[game] final case class MyTask[T](p: CancelablePromise[T], cb: () => T)
|
||||
}
|
||||
|
||||
// val ship = ed.createEntity()
|
||||
|
@ -40,7 +40,7 @@ object TestActor {
|
||||
// ctx.log.debugP(value.toString())
|
||||
// Done
|
||||
// case Failure(exception) =>
|
||||
// ctx.log.debugP(s"Received Error ${exception.getMessage()}")
|
||||
// ctx.log.debugP(show"Received Error ${exception.getMessage()}")
|
||||
// Done
|
||||
// }
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import com.jme3.app.Application
|
||||
import com.jme3.app.SimpleApplication
|
||||
import com.jme3.app.state.AppState
|
||||
import com.jme3.app.state.BaseAppState
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.scene.Spatial
|
||||
import com.simsilica.es.EntityData
|
||||
import com.simsilica.es.base.DefaultEntityData
|
||||
|
||||
trait MyBaseState extends BaseAppState {
|
||||
|
||||
var simpleApp: SimpleApplication = null
|
||||
implicit val entityData: EntityData = new DefaultEntityData()
|
||||
def stateManager = simpleApp.getStateManager
|
||||
def guiNode = simpleApp.getGuiNode
|
||||
def rootNode = simpleApp.getRootNode
|
||||
def assetManager = simpleApp.getAssetManager
|
||||
def inputManager = simpleApp.getInputManager
|
||||
def cam = simpleApp.getCamera
|
||||
|
||||
override protected final def initialize(app: Application): Unit = {
|
||||
simpleApp = app.asInstanceOf[SimpleApplication]
|
||||
init()
|
||||
// stateManager.getState(classOf[FlyCamAppState]).getCamera().setMoveSpeed(100)
|
||||
}
|
||||
|
||||
protected def init(): Unit
|
||||
protected def stop(): Unit
|
||||
|
||||
override protected def cleanup(app: Application): Unit = {
|
||||
entityData.close()
|
||||
// stop()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
protected def enableStates(classes: Class[_ <: AppState]*) =
|
||||
setEnabledToStates(true, classes: _*)
|
||||
protected def disableStates(classes: Class[_ <: AppState]*) =
|
||||
setEnabledToStates(false, classes: _*)
|
||||
|
||||
protected def setEnabledToStates(
|
||||
enabled: Boolean,
|
||||
classes: Class[_ <: AppState]*
|
||||
) = {
|
||||
for (clazz <- classes) {
|
||||
val st = stateManager.getState(clazz)
|
||||
if (st != null) st.setEnabled(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
protected def removeStates(classes: Class[_ <: AppState]*) = {
|
||||
for (clazz <- classes) {
|
||||
val st = stateManager.getState(clazz)
|
||||
if (st != null) stateManager.detach(st)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,207 +0,0 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import javax.script.ScriptEngine
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.actor.typed.scaladsl.AbstractBehavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import ammonite.Main
|
||||
import ammonite.main.Defaults
|
||||
import ammonite.runtime.Storage.Folder
|
||||
import ammonite.util.Res.Success
|
||||
import com.jme3.app.state.AppState
|
||||
|
||||
class ScriptingEngineState(
|
||||
sse: ScalaScriptingEngine,
|
||||
kse: KotlinScriptingEngine
|
||||
) extends MyBaseState {
|
||||
|
||||
// implicit val actorSystem =
|
||||
// ActorSystem.create(MyActorSystem(), "rootActor")
|
||||
// implicit val timeout: Timeout = Timeout(3.seconds)
|
||||
// val scalaScriptActor: Future[ActorRef[ScalaScriptBehavior.Command]] =
|
||||
// actorSystem.ask(
|
||||
// SpawnProtocol.Spawn(
|
||||
// ScalaScriptBehavior(sse.runner),
|
||||
// name = "ScalaScriptCompilerActor",
|
||||
// Props.empty,
|
||||
// _
|
||||
// )
|
||||
// )
|
||||
override def stop(): Unit = {}
|
||||
|
||||
// override protected def cleanup(app: Application): Unit = {
|
||||
// // actorSystem.terminate()
|
||||
// }
|
||||
|
||||
// override protected def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
||||
|
||||
// }
|
||||
override def init() = {
|
||||
// Future {
|
||||
// while (true) {
|
||||
// // super.update(tpf)
|
||||
// val (res, k) = sse.runner.runScript(
|
||||
// // os.Path(getClass().getResource("/hello.sc").getPath),
|
||||
// os.pwd / "src" / "main" / "resources" / "hello.sc",
|
||||
// Seq.empty
|
||||
// // Seq(("start", None))
|
||||
// // Scripts.groupArgs(List(""))
|
||||
// )
|
||||
// val ms = res.map(_.asInstanceOf[GameScript])
|
||||
// ms.map(_.start())
|
||||
|
||||
// val res2 = kse.engine.eval(
|
||||
// os.read(os.pwd / "src" / "main" / "resources" / "hello.main.kts")
|
||||
// )
|
||||
// // val res2 = engine.eval(getClass().getResource("/hello.main.kts").getPath)
|
||||
// // val invoker = engine.asInstanceOf[Invocable]
|
||||
// // val scr = invoker.getInterface(res2, classOf[GameScript])
|
||||
// val scr = res2.asInstanceOf[GameScript]
|
||||
// scr.start()
|
||||
// Thread.sleep(2000)
|
||||
// }
|
||||
// }
|
||||
// Future {
|
||||
// sse.runner
|
||||
// .runScript(
|
||||
// os.pwd / "src" / "main" / "resources" / "hello2.sc",
|
||||
// Seq.empty
|
||||
// )
|
||||
// ._1
|
||||
// .map(_.asInstanceOf[MyBaseState])
|
||||
// .map(s => stateManager.attach(s))
|
||||
|
||||
// ()
|
||||
|
||||
// }
|
||||
|
||||
// val res = scalaScriptActor
|
||||
// .map(
|
||||
// _.ask(ref =>
|
||||
// ScalaScriptBehavior.Compile(
|
||||
// ref,
|
||||
// os.pwd / "src" / "main" / "resources" / "hello2.sc"
|
||||
// // os.Path(getClass().getResource("/hello2.sc").getPath)
|
||||
// )
|
||||
// )(Timeout(10.seconds), actorSystem.scheduler)
|
||||
// )
|
||||
// .flatten
|
||||
|
||||
// res.foreach(_ match {
|
||||
// case AppStateResult(state) => {
|
||||
// stateManager.attach(state)
|
||||
// }
|
||||
// case wow.doge.mygame.state.ScalaScriptBehavior.Error(reason) =>
|
||||
// println("error")
|
||||
// })
|
||||
|
||||
}
|
||||
|
||||
override def update(tpf: Float): Unit = {}
|
||||
|
||||
override protected def onEnable(): Unit = {}
|
||||
|
||||
override protected def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
object MyActorSystem {
|
||||
def apply(): Behavior[SpawnProtocol.Command] =
|
||||
Behaviors.setup { context =>
|
||||
// Start initial tasks
|
||||
// context.spawn(...)
|
||||
|
||||
SpawnProtocol()
|
||||
}
|
||||
}
|
||||
class ScalaScriptingEngine(
|
||||
val runner: Main = ammonite
|
||||
.Main(
|
||||
// predefCode = """
|
||||
// import coursierapi.MavenRepository
|
||||
|
||||
// interp.repositories.update(
|
||||
// interp.repositories() ::: List(
|
||||
// MavenRepository.of("file://home/rohan/.m2/repository")
|
||||
// )
|
||||
// )
|
||||
|
||||
// @
|
||||
|
||||
// """,
|
||||
defaultPredef = false,
|
||||
storageBackend = new Folder(Defaults.ammoniteHome, isRepl = false)
|
||||
)
|
||||
) {}
|
||||
|
||||
class KotlinScriptingEngine(val engine: ScriptEngine) {
|
||||
// val manager = new ScriptEngineManager()
|
||||
// val engine = manager.getEngineByExtension("main.kts")
|
||||
}
|
||||
|
||||
object ScalaScriptBehavior {
|
||||
sealed trait Result
|
||||
final case class AppStateResult(state: AppState) extends Result
|
||||
final case class Error(reason: String) extends Result
|
||||
|
||||
sealed trait Command
|
||||
final case class Compile(sender: ActorRef[Result], path: os.Path)
|
||||
extends Command
|
||||
// final case class CompileScripts(sender: ActorRef[Result], paths: os.Path*)
|
||||
// extends Command
|
||||
def apply(
|
||||
runner: Main = ammonite
|
||||
.Main(
|
||||
storageBackend = new Folder(
|
||||
// os.pwd / "target"
|
||||
Defaults.ammoniteHome,
|
||||
isRepl = false
|
||||
)
|
||||
)
|
||||
) =
|
||||
Behaviors.setup(ctx => new ScalaScriptActor(runner, ctx))
|
||||
private class ScalaScriptActor(
|
||||
val runner: Main,
|
||||
context: ActorContext[Command]
|
||||
) extends AbstractBehavior[Command](context) {
|
||||
|
||||
override def onMessage(msg: Command): Behavior[Command] = {
|
||||
msg match {
|
||||
case Compile(sender, path) =>
|
||||
context.log.debug(s"Received $path")
|
||||
val res = getScript(path)
|
||||
println(res)
|
||||
sender ! res
|
||||
Behaviors.same
|
||||
// case CompileScripts(sender, paths) =>
|
||||
}
|
||||
}
|
||||
|
||||
def getScript(path: os.Path): Result = {
|
||||
runner
|
||||
.runScript(
|
||||
path,
|
||||
Seq.empty
|
||||
)
|
||||
._1 match {
|
||||
case ammonite.util.Res.Exception(t, msg) => Error(msg)
|
||||
|
||||
case Success(obj) =>
|
||||
obj match {
|
||||
case s: MyBaseState => AppStateResult(s)
|
||||
case _ => Error("Unknown script type")
|
||||
// AppStateResult(s.asInstanceOf[AppState])
|
||||
}
|
||||
|
||||
case _ => Error("Failed to run script")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.material.Material
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.math.Vector3f
|
||||
import com.jme3.scene.Geometry
|
||||
import com.jme3.scene.shape.Box
|
||||
import wow.doge.mygame.components.TestComponent
|
||||
import wow.doge.mygame.implicits._
|
||||
class TestAppState(
|
||||
// private var _entity: Option[EntityData] = Some(new DefaultEntityData())
|
||||
) extends MyBaseState {
|
||||
var geom: Option[Geometry] = None
|
||||
|
||||
// def entity = _entity
|
||||
// override def initialize(app: Application): Unit = {
|
||||
// super.initialize(app)
|
||||
|
||||
// }
|
||||
|
||||
override def init() = {
|
||||
entityData
|
||||
.createEntity()
|
||||
.withComponents(TestComponent())
|
||||
// entityData.setComponents(x, TestComponent())
|
||||
val es = entityData.getEntities(classOf[TestComponent])
|
||||
println(es)
|
||||
val b = new Box(1, 1, 1)
|
||||
geom = Some(new Geometry("Box", b))
|
||||
|
||||
val mat = MyMaterial(
|
||||
assetManager = assetManager,
|
||||
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
)
|
||||
geom.foreach(e => {
|
||||
e.setMaterial(mat)
|
||||
rootNode.attachChild(e)
|
||||
})
|
||||
}
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
geom.foreach(_.rotate(0, 0.5f * tpf, 0))
|
||||
geom.foreach(_.move(new Vector3f(0, 1 * tpf, 0)))
|
||||
}
|
||||
|
||||
// override def cleanup(app: Application): Unit = {
|
||||
// // _entity.map(_.close())
|
||||
// // _entity = None
|
||||
// }
|
||||
|
||||
override def onEnable(): Unit = {}
|
||||
|
||||
override def onDisable(): Unit = {}
|
||||
override def stop(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
object MyMaterial {
|
||||
def apply(
|
||||
color: String = "Color",
|
||||
colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue,
|
||||
assetManager: AssetManager,
|
||||
path: os.RelPath
|
||||
): Material = {
|
||||
val mat =
|
||||
new Material(assetManager, path.toString())
|
||||
mat.setColor(color, colorType)
|
||||
mat
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package wow.doge.mygame.game.controls
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
import cats.syntax.eq._
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.math.Quaternion
|
||||
import com.jme3.math.Vector3f
|
||||
@ -26,6 +27,7 @@ import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||
* @param rotateFn
|
||||
* @param s
|
||||
*/
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Null"))
|
||||
class CameraMovementControl(
|
||||
rotationBuf: Quaternion,
|
||||
obs: Observable[PlayerCameraInput],
|
||||
@ -49,7 +51,7 @@ class CameraMovementControl(
|
||||
}
|
||||
|
||||
override def controlUpdate(tpf: Float): Unit =
|
||||
if (_event != null) {
|
||||
if (_event =!= null) {
|
||||
_event match {
|
||||
case PlayerCameraInput.CameraRotateLeft =>
|
||||
val rot = rotationBuf
|
||||
@ -75,6 +77,8 @@ class CameraMovementControl(
|
||||
x$1: RenderManager,
|
||||
x$2: ViewPort
|
||||
): Unit = {}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
override def setSpatial(spatial: Spatial): Unit = {
|
||||
super.setSpatial(spatial)
|
||||
if (this.spatial != null)
|
||||
|
@ -4,67 +4,68 @@ import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import io.estatico.newtype.macros.newtype
|
||||
import wow.doge.mygame.game.entities.CharacterStats.HealHealth
|
||||
|
||||
case class CharacterStats(hp: CharacterStats.Health, stamina: Int)
|
||||
final case class CharacterStats(
|
||||
hp: CharacterStats.Health,
|
||||
stamina: CharacterStats.Stamina
|
||||
)
|
||||
object CharacterStats {
|
||||
@newtype case class HealHealth(toInt: Int)
|
||||
@newtype case class DamageHealth(toInt: Int)
|
||||
@newtype case class Health(toInt: Int)
|
||||
@newtype final case class HealHealth(toInt: Int)
|
||||
@newtype final case class DamageHealth(toInt: Int)
|
||||
@newtype final case class Health(toInt: Int)
|
||||
object Health {
|
||||
implicit class HealthOps(private val h: Health) extends AnyVal {
|
||||
// def +(v: Int): Health = Health(h.toInt + v)
|
||||
// def -(v: Int): Health = Health(h.toInt - v)
|
||||
// def *(v: Int): Health = Health(h.toInt * v)
|
||||
// def /(v: Int): Health = Health(h.toInt / v)
|
||||
def :+(v: HealHealth): Health = Health(h.toInt + v.toInt)
|
||||
def -(v: DamageHealth): Health = Health(h.toInt - v.toInt)
|
||||
}
|
||||
}
|
||||
@newtype case class HealStamina(toInt: Int)
|
||||
@newtype case class DamageStamina(toInt: Int)
|
||||
@newtype case class Stamina(toInt: Int)
|
||||
@newtype final case class HealStamina(toInt: Int)
|
||||
@newtype final case class DamageStamina(toInt: Int)
|
||||
@newtype final case class Stamina(toInt: Int)
|
||||
object Stamina {
|
||||
implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
||||
// def +(v: Int): Stamina = Stamina(h.toInt + v)
|
||||
// def -(v: Int): Stamina = Stamina(h.toInt - v)
|
||||
// def *(v: Int): Stamina = Stamina(h.toInt * v)
|
||||
// def /(v: Int): Stamina = Stamina(h.toInt / v)
|
||||
def :+(v: HealStamina): Stamina = Stamina(h.toInt + v.toInt)
|
||||
def -(v: DamageStamina): Stamina = Stamina(h.toInt - v.toInt)
|
||||
}
|
||||
}
|
||||
// object Stamina {
|
||||
// implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
||||
// def +(v: Health): Stamina = Stamina(h.toInt + v.toInt)
|
||||
// def -(v: Health): Stamina = Stamina(h.toInt - v.toInt)
|
||||
// def *(v: Health): Stamina = Stamina(h.toInt * v.toInt)
|
||||
// def /(v: Health): Stamina = Stamina(h.toInt / v.toInt)
|
||||
// }
|
||||
// }
|
||||
|
||||
// object Damage {
|
||||
// implicit class DamageOps(private val h: Damage) extends AnyVal {
|
||||
// def +(v: Health): Damage = Damage(h.toInt + v.toInt)
|
||||
// def -(v: Health): Damage = Damage(h.toInt - v.toInt)
|
||||
// def *(v: Health): Damage = Damage(h.toInt * v.toInt)
|
||||
// def /(v: Health): Damage = Damage(h.toInt / v.toInt)
|
||||
// }
|
||||
// }
|
||||
implicit val show = Show.fromToString[CharacterStats]
|
||||
implicit val eq = Eq.fromUniversalEquals[CharacterStats]
|
||||
|
||||
}
|
||||
|
||||
object StatsActor {
|
||||
import CharacterStats._
|
||||
|
||||
sealed trait Status
|
||||
object Status {
|
||||
case object Alive extends Status
|
||||
case object Dead extends Status
|
||||
}
|
||||
|
||||
sealed trait Command
|
||||
// case class TakeDamage(value: Int) extends Command
|
||||
case class TakeDamageResult(
|
||||
final case class TakeDamageResult(
|
||||
value: CharacterStats.DamageHealth,
|
||||
replyTo: ActorRef[(Boolean, CharacterStats)]
|
||||
replyTo: ActorRef[(Status, CharacterStats)]
|
||||
) extends Command
|
||||
case class HealResult(value: HealHealth) extends Command
|
||||
case class CurrentStats(replyTo: ActorRef[CharacterStats]) extends Command
|
||||
final case class ConsumeStaminaResult(
|
||||
value: CharacterStats.DamageStamina,
|
||||
replyTo: ActorRef[(Status, CharacterStats)]
|
||||
) extends Command
|
||||
final case class HealHealthResult(
|
||||
value: HealHealth,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
|
||||
final case class HealStaminaResult(
|
||||
value: HealStamina,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class CurrentStats(replyTo: ActorRef[CharacterStats])
|
||||
extends Command
|
||||
|
||||
class Props(
|
||||
startingHealth: CharacterStats.Health,
|
||||
@ -74,12 +75,12 @@ object StatsActor {
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
new StatsActor(ctx, this)
|
||||
.receive(
|
||||
State(CharacterStats(startingHealth, startingStamina.toInt))
|
||||
State(CharacterStats(startingHealth, startingStamina))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class State(stats: CharacterStats)
|
||||
final case class State(stats: CharacterStats)
|
||||
}
|
||||
class StatsActor(
|
||||
ctx: ActorContext[StatsActor.Command],
|
||||
@ -100,15 +101,36 @@ class StatsActor(
|
||||
// receive(nextState)
|
||||
case TakeDamageResult(value, replyTo) =>
|
||||
val nextState = if ((state.stats.hp - value).toInt <= 0) {
|
||||
replyTo ! true -> state.stats
|
||||
state.modify(_.stats.hp).setTo(Health(0))
|
||||
val s = state.modify(_.stats.hp).setTo(Health(0))
|
||||
replyTo ! Status.Dead -> s.stats
|
||||
s
|
||||
} else {
|
||||
replyTo ! false -> state.stats
|
||||
state.modify(_.stats.hp).using(_ - value)
|
||||
val s = state.modify(_.stats.hp).using(_ - value)
|
||||
replyTo ! Status.Alive -> s.stats
|
||||
s
|
||||
}
|
||||
receive(nextState)
|
||||
case HealResult(value) =>
|
||||
receive(state.modify(_.stats.hp).using(_ :+ value))
|
||||
case ConsumeStaminaResult(value, replyTo) =>
|
||||
val nextState = if ((state.stats.stamina - value).toInt <= 0) {
|
||||
val s = state.modify(_.stats.stamina).setTo(Stamina(0))
|
||||
replyTo ! Status.Alive -> s.stats
|
||||
s
|
||||
} else {
|
||||
val s = state.modify(_.stats.stamina).using(_ - value)
|
||||
replyTo ! Status.Alive -> s.stats
|
||||
s
|
||||
}
|
||||
receive(nextState)
|
||||
case HealHealthResult(value, replyTo) =>
|
||||
val nextState = receive(state.modify(_.stats.hp).using(_ :+ value))
|
||||
replyTo ! state.stats
|
||||
nextState
|
||||
|
||||
case HealStaminaResult(value, replyTo) =>
|
||||
val nextState = receive(state.modify(_.stats.stamina).using(_ :+ value))
|
||||
replyTo ! state.stats
|
||||
nextState
|
||||
|
||||
case CurrentStats(replyTo) =>
|
||||
replyTo ! state.stats
|
||||
Behaviors.same
|
||||
|
105
src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala
Normal file → Executable file
105
src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala
Normal file → Executable file
@ -6,7 +6,6 @@ import scala.util.Success
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
@ -16,17 +15,6 @@ import monix.execution.CancelablePromise
|
||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedDown
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedLeft
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedRight
|
||||
import wow.doge.mygame.subsystems.events.EntityMovementEvent.MovedUp
|
||||
import wow.doge.mygame.subsystems.events.Event
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
|
||||
object NpcActorSupervisor {
|
||||
@ -39,8 +27,8 @@ object NpcActorSupervisor {
|
||||
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
|
||||
) extends Command
|
||||
private case object DoneMoving extends Command
|
||||
private case class LogError(err: Throwable) extends Command
|
||||
private case class MovementFailed(err: Throwable) extends Command
|
||||
private final case class LogError(err: Throwable) extends Command
|
||||
private final case class MovementFailed(err: Throwable) extends Command
|
||||
|
||||
class Props(
|
||||
val npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
|
||||
@ -84,9 +72,9 @@ class NpcActorSupervisor(
|
||||
s"npc-${props.npcName}-NpcActorTimer"
|
||||
)
|
||||
|
||||
def idle(state: State): Behavior[NpcActorSupervisor.Command] =
|
||||
def idle(state: State): Behavior[Command] =
|
||||
Behaviors.setup { _ =>
|
||||
ctx.log.debugP(s"npcActor-${props.npcName}: Entered Idle State")
|
||||
ctx.log.debugP(show"npcActor-${props.npcName}: Entered Idle State")
|
||||
Behaviors.receiveMessage[Command] {
|
||||
case m @ Move(pos) =>
|
||||
ctx.ask(
|
||||
@ -111,9 +99,9 @@ class NpcActorSupervisor(
|
||||
state: State,
|
||||
targetPos: ImVector3f,
|
||||
signal: CancelableFuture[NpcMovementActor.DoneMoving.type]
|
||||
): Behavior[NpcActorSupervisor.Command] =
|
||||
): Behavior[Command] =
|
||||
Behaviors.setup { _ =>
|
||||
ctx.log.debugP(s"npcActor-${props.npcName}: Entered Moving State")
|
||||
ctx.log.debugP(show"npcActor-${props.npcName}: Entered Moving State")
|
||||
movementTimer ! GenericTimerActor.Start
|
||||
ctx.pipeToSelf(signal) {
|
||||
case Success(value) => DoneMoving
|
||||
@ -150,7 +138,7 @@ class NpcActorSupervisor(
|
||||
}
|
||||
|
||||
def logError(err: Throwable) =
|
||||
ctx.log.errorP(s"npcActor-${props.npcName}: " + err.getMessage)
|
||||
ctx.log.errorP(show"npcActor-${props.npcName}: " + err.getMessage)
|
||||
}
|
||||
|
||||
object NpcMovementActor {
|
||||
@ -158,10 +146,10 @@ object NpcMovementActor {
|
||||
case object DoneMoving
|
||||
|
||||
sealed trait Command
|
||||
case class AskPosition(replyTo: ActorRef[ImVector3f]) extends Command
|
||||
final case class AskPosition(replyTo: ActorRef[ImVector3f]) extends Command
|
||||
case object MovementTick extends Command
|
||||
case object StopMoving extends Command
|
||||
case class MoveTo(
|
||||
final case class MoveTo(
|
||||
target: ImVector3f,
|
||||
doneSignal: ActorRef[CancelableFuture[DoneMoving.type]]
|
||||
) extends Command
|
||||
@ -212,7 +200,7 @@ class NpcMovementActor[T](
|
||||
Behaviors.receiveMessagePartial {
|
||||
case StopMoving =>
|
||||
ctx.log.debugP(
|
||||
show"npcActor-${props.npcName}: Position at Stop = " + location
|
||||
show"npcActor-${props.npcName}: Position at Stop = $location"
|
||||
)
|
||||
props.enqueueR(() => cm.stop(props.movable))
|
||||
receive(state)
|
||||
@ -235,76 +223,3 @@ class NpcMovementActor[T](
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
object NpcMovementActorNotUsed {
|
||||
sealed trait Command
|
||||
class Props(
|
||||
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
npcName: String,
|
||||
// movementActor: ActorRef[ImMovementActor.Command],
|
||||
mainEventBus: ActorRef[
|
||||
EventBus.Command[Event]
|
||||
],
|
||||
tickEventBus: GameEventBus[TickEvent]
|
||||
) {
|
||||
|
||||
def behavior: Behavior[Command] =
|
||||
Behaviors.setup { ctx =>
|
||||
val movementActor = ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
s"npc-${npcName}-MovementActorChild"
|
||||
)
|
||||
val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] {
|
||||
case event: EntityMovementEvent =>
|
||||
event match {
|
||||
case MovedLeft(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||
Behaviors.same
|
||||
case MovedUp(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MoveUp(pressed)
|
||||
Behaviors.same
|
||||
case MovedRight(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MoveRight(pressed)
|
||||
Behaviors.same
|
||||
case MovedDown(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MoveDown(pressed)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
val npcMovementEl = ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(npcMovementElBehavior)
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
s"npc-${npcName}-MovementEventHandler"
|
||||
)
|
||||
val renderTickElBehavior =
|
||||
Behaviors.receiveMessage[RenderTick.type] {
|
||||
case RenderTick =>
|
||||
movementActor ! ImMovementActor.Tick
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
val renderTickEl =
|
||||
ctx.spawn(
|
||||
renderTickElBehavior,
|
||||
s"npc-${npcName}-MovementTickListener"
|
||||
)
|
||||
|
||||
mainEventBus ! EventBus.Subscribe(npcMovementEl)
|
||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||
Behaviors.receiveMessage(msg => Behaviors.same)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package wow.doge.mygame.game.entities.character
|
||||
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
|
||||
object CharacterStates {
|
||||
sealed trait AliveSubstate
|
||||
object AliveSubstate {
|
||||
final case class InCombat(substate: CombatSubstate) extends AliveSubstate
|
||||
final case class Moving(substate: MovementSubstate) extends AliveSubstate
|
||||
case object Idle extends AliveSubstate
|
||||
implicit val eq = Eq.fromUniversalEquals[AliveSubstate]
|
||||
implicit val show = Show.fromToString[AliveSubstate]
|
||||
}
|
||||
|
||||
sealed trait CombatSubstate
|
||||
object CombatSubstate {
|
||||
final case class Moving(substate: MovementSubstate) extends CombatSubstate
|
||||
final case class Attacking(victimName: String) extends CombatSubstate
|
||||
}
|
||||
|
||||
sealed trait MovementSubstate
|
||||
case object Walking extends MovementSubstate
|
||||
case object Running extends MovementSubstate
|
||||
|
||||
sealed trait DeadSubstate
|
||||
object DeadSubstate {
|
||||
implicit val eq = Eq.fromUniversalEquals[DeadSubstate]
|
||||
implicit val show = Show.fromToString[DeadSubstate]
|
||||
}
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
package wow.doge.mygame.game.entities.player
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.LogOptions
|
||||
import akka.actor.typed.PostStop
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import monix.bio.UIO
|
||||
import monix.execution.AsyncQueue
|
||||
import monix.reactive.Observable
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.Dispatchers
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||
import wow.doge.mygame.game.entities.CharacterStats
|
||||
import wow.doge.mygame.game.entities.StatsActor
|
||||
import wow.doge.mygame.game.entities.character.CharacterStates._
|
||||
import wow.doge.mygame.game.entities.player.behaviors.IdleBehaviorFactory
|
||||
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.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.MovementDirection
|
||||
|
||||
object PlayerActor {
|
||||
|
||||
type Ref = ActorRef[PlayerActor.Command]
|
||||
|
||||
sealed trait Status
|
||||
object Status {
|
||||
case object Alive extends Status
|
||||
case object Dead extends Status
|
||||
}
|
||||
|
||||
sealed trait Command extends Product with Serializable
|
||||
final case class TakeDamage(
|
||||
value: CharacterStats.DamageHealth,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class ConsumeStamina(
|
||||
value: CharacterStats.DamageStamina,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class HealHealth(
|
||||
value: CharacterStats.HealHealth,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class HealStamina(
|
||||
value: CharacterStats.HealStamina,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class CurrentStats(replyTo: ActorRef[CharacterStats])
|
||||
extends Command
|
||||
final case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
||||
final case class GetStatsObservable(
|
||||
replyTo: ActorRef[UIO[Observable[CharacterStats]]]
|
||||
) extends Command
|
||||
|
||||
final case class Walk(pressed: Boolean, dir: MovementDirection)
|
||||
extends Command
|
||||
case object StopMoving extends Command
|
||||
case object Jump extends Command
|
||||
|
||||
private[player] final case class HandleWalk(
|
||||
b: CharacterStats,
|
||||
pressed: Boolean,
|
||||
direction: MovementDirection
|
||||
) extends Command
|
||||
|
||||
private[player] case object Die extends Command
|
||||
private[player] final case class StatsResponse(
|
||||
response: (StatsActor.Status, CharacterStats),
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
private[player] final case class LogError(ex: Throwable) extends Command
|
||||
class Props(
|
||||
val playerEventBus: GameEventBus[PlayerEvent],
|
||||
val tickEventBus: GameEventBus[TickEvent],
|
||||
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
val statsActorBehavior: Behavior[StatsActor.Command],
|
||||
val scheduler: AsyncScheduler,
|
||||
val fxScheduler: Schedulers.FxScheduler,
|
||||
val statsQueue: AsyncQueue[CharacterStats]
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.DEBUG)
|
||||
.withLogger(Logger[PlayerActor].underlying),
|
||||
Behaviors
|
||||
.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Starting PlayerActor")
|
||||
|
||||
// spawn children actors
|
||||
|
||||
val playerStatsActor = ctx.spawnN(statsActorBehavior)
|
||||
|
||||
val playerMovementActor =
|
||||
ctx.spawnN(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
Dispatchers.jmeDispatcher
|
||||
)
|
||||
|
||||
// val playerMovementEl = ctx.spawnN(
|
||||
// Behaviors
|
||||
// .supervise(
|
||||
// new PlayerMovementEventListener.Props(
|
||||
// ctx.self,
|
||||
// scheduler
|
||||
// ).behavior
|
||||
// )
|
||||
// .onFailure[Exception](
|
||||
// SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
// )
|
||||
// )
|
||||
|
||||
val renderTickEl = {
|
||||
val behavior: Behavior[RenderTick.type] =
|
||||
Behaviors.setup(ctx =>
|
||||
Behaviors
|
||||
.receiveMessage[RenderTick.type] {
|
||||
case RenderTick =>
|
||||
playerMovementActor ! ImMovementActor.Tick
|
||||
// playerCameraActor ! PlayerCameraActor.Tick
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
ctx.spawn(behavior, "playerMovementTickListener")
|
||||
}
|
||||
|
||||
//init listeners
|
||||
// playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||
|
||||
new PlayerActor(
|
||||
ctx,
|
||||
this,
|
||||
Children(playerMovementActor, playerStatsActor)
|
||||
).aliveState(AliveSubstate.Idle)
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
final case class Children(
|
||||
movementActor: ActorRef[ImMovementActor.Command],
|
||||
statsActor: ActorRef[StatsActor.Command]
|
||||
)
|
||||
|
||||
final case class Env(
|
||||
ctx: ActorContext[PlayerActor.Command],
|
||||
props: PlayerActor.Props,
|
||||
children: PlayerActor.Children
|
||||
)
|
||||
}
|
||||
|
||||
class PlayerActor(
|
||||
ctx: ActorContext[PlayerActor.Command],
|
||||
props: PlayerActor.Props,
|
||||
children: PlayerActor.Children
|
||||
) {
|
||||
import PlayerActor._
|
||||
implicit val timeout = Timeout(1.second)
|
||||
|
||||
val env = Env(ctx, props, children)
|
||||
|
||||
val deadState = Behaviors
|
||||
.receiveMessage[Command] {
|
||||
// case TakeDamage(value) =>
|
||||
// children.statsActor ! StatsActor.TakeDamage(value)
|
||||
// // children.movementActor ! ImMovementActor.MovedDown(true)
|
||||
// Behaviors.same
|
||||
// case CurrentStats(replyTo) =>
|
||||
// // ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||
// children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
// Behaviors.same
|
||||
// case Heal(_) =>
|
||||
// Behaviors.same
|
||||
case CurrentStats(replyTo) =>
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
Behaviors.same
|
||||
case GetStatus(replyTo) =>
|
||||
replyTo ! Status.Dead
|
||||
Behaviors.same
|
||||
case _ => Behaviors.unhandled
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
def idleBehavior(
|
||||
nextStateFn: AliveSubstate => Behavior[Command],
|
||||
consumptionMultiplier: Int => Int
|
||||
) =
|
||||
new IdleBehaviorFactory(
|
||||
env,
|
||||
nextStateFn,
|
||||
deadState,
|
||||
consumptionMultiplier
|
||||
).behavior
|
||||
|
||||
def aliveState(substate: AliveSubstate): Behavior[Command] =
|
||||
substate match {
|
||||
case AliveSubstate.InCombat(substate) =>
|
||||
substate match {
|
||||
case CombatSubstate.Moving(substate) =>
|
||||
substate match {
|
||||
case Walking =>
|
||||
Behaviors.receiveMessage[Command](_ => Behaviors.same)
|
||||
case Running =>
|
||||
Behaviors.receiveMessage[Command](_ => Behaviors.same)
|
||||
}
|
||||
case CombatSubstate.Attacking(victimName) =>
|
||||
Behaviors.receiveMessage[Command](_ => Behaviors.same)
|
||||
}
|
||||
|
||||
case AliveSubstate.Moving(Walking) =>
|
||||
ctx.log.debugP("In Walking State")
|
||||
idleBehavior(aliveState, _ * 2).value
|
||||
|
||||
case AliveSubstate.Moving(Running) =>
|
||||
idleBehavior(aliveState, _ * 3).value
|
||||
|
||||
case AliveSubstate.Idle =>
|
||||
ctx.log.debugP("In Idle State")
|
||||
idleBehavior(aliveState, identity).value
|
||||
}
|
||||
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
package wow.doge.mygame.game.entities
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.LogOptions
|
||||
import akka.actor.typed.PostStop
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.OverflowStrategy
|
||||
import monix.reactive.subjects.ConcurrentSubject
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.Dispatchers
|
||||
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||
import wow.doge.mygame.game.entities.StatsActor
|
||||
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.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import monix.eval.Coeval
|
||||
import monix.execution.AsyncQueue
|
||||
object PlayerActorSupervisor {
|
||||
|
||||
type Ref = ActorRef[PlayerActorSupervisor.Command]
|
||||
|
||||
sealed trait Status
|
||||
object Status {
|
||||
case object Alive extends Status
|
||||
case object Dead extends Status
|
||||
}
|
||||
|
||||
sealed trait Command
|
||||
case class TakeDamage(value: CharacterStats.DamageHealth) extends Command
|
||||
case class TakeDamage2(
|
||||
value: CharacterStats.DamageHealth,
|
||||
replyTo: ActorRef[Unit]
|
||||
) extends Command
|
||||
case class Heal(value: CharacterStats.HealHealth) extends Command
|
||||
case class CurrentStats(replyTo: ActorRef[CharacterStats]) extends Command
|
||||
case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
||||
case class GetStatsObservable(replyTo: ActorRef[Observable[CharacterStats]])
|
||||
extends Command
|
||||
case class GetStatsObservable2(replyTo: ActorRef[Observable[CharacterStats]])
|
||||
extends Command
|
||||
|
||||
private case object Die extends Command
|
||||
private case class DamageResponse(response: (Boolean, CharacterStats))
|
||||
extends Command
|
||||
private case class DamageResponse2(
|
||||
response: (Boolean, CharacterStats),
|
||||
replyTo: ActorRef[Unit]
|
||||
) extends Command
|
||||
// private case class InternalTakeDamage(old: Int, value: Int) extends Command
|
||||
private case class LogError(ex: Throwable) extends Command
|
||||
class Props(
|
||||
val playerEventBus: GameEventBus[PlayerEvent],
|
||||
val tickEventBus: GameEventBus[TickEvent],
|
||||
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
val scheduler: AsyncScheduler
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.DEBUG)
|
||||
.withLogger(
|
||||
Logger[PlayerActorSupervisor].underlying
|
||||
),
|
||||
Behaviors
|
||||
.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Starting PlayerActor")
|
||||
|
||||
// spawn children actors
|
||||
val playerMovementActor =
|
||||
ctx.spawnN(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
Dispatchers.jmeDispatcher
|
||||
)
|
||||
|
||||
val playerStatsActor =
|
||||
ctx.spawnN(
|
||||
new StatsActor.Props(
|
||||
CharacterStats.Health(100),
|
||||
CharacterStats.Stamina(100)
|
||||
).behavior
|
||||
)
|
||||
|
||||
val playerMovementEl = ctx.spawnN(
|
||||
Behaviors
|
||||
.supervise(PlayerMovementEventListener(playerMovementActor))
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
)
|
||||
)
|
||||
|
||||
val renderTickEl = {
|
||||
val behavior: Behavior[RenderTick.type] =
|
||||
Behaviors.setup(ctx =>
|
||||
Behaviors
|
||||
.receiveMessage[RenderTick.type] {
|
||||
case RenderTick =>
|
||||
playerMovementActor ! ImMovementActor.Tick
|
||||
// playerCameraActor ! PlayerCameraActor.Tick
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
ctx.spawn(behavior, "playerMovementTickListener")
|
||||
}
|
||||
|
||||
//init listeners
|
||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||
|
||||
new PlayerActorSupervisor(
|
||||
ctx,
|
||||
this,
|
||||
Children(playerMovementActor, playerStatsActor),
|
||||
ConcurrentSubject.publish(
|
||||
OverflowStrategy.DropOldAndSignal(
|
||||
50,
|
||||
dropped => Coeval.pure(None)
|
||||
)
|
||||
)(scheduler.value),
|
||||
AsyncQueue.bounded(10)(scheduler.value)
|
||||
).aliveState
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
case class Children(
|
||||
movementActor: ActorRef[ImMovementActor.Command],
|
||||
statsActor: ActorRef[StatsActor.Command]
|
||||
)
|
||||
}
|
||||
class PlayerActorSupervisor(
|
||||
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||
props: PlayerActorSupervisor.Props,
|
||||
children: PlayerActorSupervisor.Children,
|
||||
statsSubject: ConcurrentSubject[CharacterStats, CharacterStats],
|
||||
statsQueue: AsyncQueue[CharacterStats]
|
||||
) {
|
||||
import PlayerActorSupervisor._
|
||||
implicit val timeout = Timeout(1.second)
|
||||
val aliveState =
|
||||
Behaviors
|
||||
.receiveMessage[Command] {
|
||||
case TakeDamage(value) =>
|
||||
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats(_)) {
|
||||
// case Success(status) => InternalTakeDamage(status.hp, value)
|
||||
// case Failure(ex) => LogError(ex)
|
||||
// }
|
||||
ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) {
|
||||
case Success(response) => DamageResponse(response)
|
||||
case Failure(ex) => LogError(ex)
|
||||
}
|
||||
Behaviors.same
|
||||
case TakeDamage2(value, replyTo) =>
|
||||
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats(_)) {
|
||||
// case Success(status) => InternalTakeDamage(status.hp, value)
|
||||
// case Failure(ex) => LogError(ex)
|
||||
// }
|
||||
ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) {
|
||||
case Success(response) => DamageResponse2(response, replyTo)
|
||||
case Failure(ex) => LogError(ex)
|
||||
}
|
||||
Behaviors.same
|
||||
case CurrentStats(replyTo) =>
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
Behaviors.same
|
||||
case Heal(value) =>
|
||||
children.statsActor ! StatsActor.HealResult(value)
|
||||
Behaviors.same
|
||||
case GetStatus(replyTo) =>
|
||||
replyTo ! Status.Alive
|
||||
Behaviors.same
|
||||
// case _ => Behaviors.unhandled
|
||||
// case InternalTakeDamage(hp, damage) =>
|
||||
// if (hp - damage <= 0) dead
|
||||
// else {
|
||||
// children.statsActor ! StatsActor.TakeDamage(damage)
|
||||
// Behaviors.same
|
||||
// }
|
||||
case GetStatsObservable(replyTo) =>
|
||||
replyTo ! statsSubject
|
||||
Behaviors.same
|
||||
case GetStatsObservable2(replyTo) =>
|
||||
import monix.{eval => me}
|
||||
replyTo ! Observable.repeatEvalF(
|
||||
me.Task.deferFuture(statsQueue.poll())
|
||||
)
|
||||
Behaviors.same
|
||||
case DamageResponse(response) =>
|
||||
response match {
|
||||
case (dead, state) =>
|
||||
if (dead) ctx.self ! Die
|
||||
statsSubject.onNext(state)
|
||||
}
|
||||
Behaviors.same
|
||||
case DamageResponse2(response, replyTo) =>
|
||||
response match {
|
||||
case (dead, stats) =>
|
||||
if (dead) ctx.self ! Die
|
||||
statsQueue
|
||||
.offer(stats)
|
||||
.foreach(_ => replyTo ! ())(props.scheduler.value)
|
||||
}
|
||||
Behaviors.same
|
||||
case Die => deadState
|
||||
case LogError(ex) =>
|
||||
ctx.log.error(ex.getMessage)
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
statsSubject.onComplete()
|
||||
Behaviors.same
|
||||
}
|
||||
val deadState = Behaviors
|
||||
.receiveMessage[Command] {
|
||||
// case TakeDamage(value) =>
|
||||
// children.statsActor ! StatsActor.TakeDamage(value)
|
||||
// // children.movementActor ! ImMovementActor.MovedDown(true)
|
||||
// Behaviors.same
|
||||
// case CurrentStats(replyTo) =>
|
||||
// // ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||
// children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
// Behaviors.same
|
||||
// case Heal(_) =>
|
||||
// Behaviors.same
|
||||
case CurrentStats(replyTo) =>
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
Behaviors.same
|
||||
case GetStatus(replyTo) =>
|
||||
replyTo ! Status.Dead
|
||||
Behaviors.same
|
||||
case _ => Behaviors.unhandled
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
statsSubject.onComplete()
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
package wow.doge.mygame.game.entities.player
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.LogOptions
|
||||
import akka.actor.typed.PostStop
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.Dispatchers
|
||||
import wow.doge.mygame.game.entities.PlayerCameraActor
|
||||
import wow.doge.mygame.game.entities.PlayerCameraEventListener
|
||||
import wow.doge.mygame.game.entities.PlayerMovementEventListener
|
||||
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.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
|
||||
object PlayerActorSupervisor2 {
|
||||
sealed trait Command
|
||||
case object Tick extends Command
|
||||
sealed trait Movement extends Command
|
||||
final case class MoveLeft(pressed: Boolean) extends Movement
|
||||
final case class MoveUp(pressed: Boolean) extends Movement
|
||||
final case class MoveRight(pressed: Boolean) extends Movement
|
||||
final case class MoveDown(pressed: Boolean) extends Movement
|
||||
case object Jump extends Movement
|
||||
sealed trait Camera
|
||||
case object RotateLeft extends Camera
|
||||
case object RotateRight extends Camera
|
||||
case object RotateUp extends Camera
|
||||
case object RotateDown extends Camera
|
||||
class Props(
|
||||
val playerEventBus: GameEventBus[PlayerEvent],
|
||||
val tickEventBus: GameEventBus[TickEvent],
|
||||
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
val playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.TRACE)
|
||||
.withLogger(
|
||||
Logger[PlayerActorSupervisor2].underlying
|
||||
),
|
||||
Behaviors
|
||||
.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Starting PlayerActor")
|
||||
|
||||
// spawn children actors
|
||||
val movementActor =
|
||||
ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
"playerMovementActor",
|
||||
Dispatchers.jmeDispatcher
|
||||
)
|
||||
|
||||
val playerCameraActor =
|
||||
ctx.spawn(
|
||||
playerCameraActorBehavior,
|
||||
"playerCameraActor",
|
||||
Dispatchers.jmeDispatcher
|
||||
)
|
||||
|
||||
val playerCameraEl = ctx.spawn(
|
||||
PlayerCameraEventListener(playerCameraActor),
|
||||
"playerCameraActorEl"
|
||||
)
|
||||
|
||||
val playerMovementEl = ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(PlayerMovementEventListener(movementActor))
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
"playerMovementEventHandler"
|
||||
)
|
||||
|
||||
val renderTickEl = {
|
||||
val behavior: Behavior[RenderTick.type] =
|
||||
Behaviors.setup(ctx =>
|
||||
Behaviors
|
||||
.receiveMessage[RenderTick.type] {
|
||||
case RenderTick =>
|
||||
movementActor ! ImMovementActor.Tick
|
||||
// playerCameraActor ! PlayerCameraActor.Tick
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
ctx.spawn(behavior, "playerMovementTickListener")
|
||||
}
|
||||
|
||||
//init listeners
|
||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||
playerEventBus ! EventBus.Subscribe(playerCameraEl)
|
||||
|
||||
new PlayerActorSupervisor2(
|
||||
ctx,
|
||||
this,
|
||||
Children(movementActor)
|
||||
).receive
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
case class Children(
|
||||
movementActor: ActorRef[ImMovementActor.Command]
|
||||
)
|
||||
}
|
||||
class PlayerActorSupervisor2(
|
||||
ctx: ActorContext[PlayerActorSupervisor2.Command],
|
||||
props: PlayerActorSupervisor2.Props,
|
||||
children: PlayerActorSupervisor2.Children
|
||||
) {
|
||||
import PlayerActorSupervisor2._
|
||||
def receive =
|
||||
Behaviors
|
||||
.receiveMessage[Command] {
|
||||
case m @ MoveDown(pressed) =>
|
||||
// children.movementActor ! m
|
||||
Behaviors.same
|
||||
case _ =>
|
||||
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ object PlayerCameraActor {
|
||||
)
|
||||
}
|
||||
|
||||
case class State()
|
||||
final case class State()
|
||||
object State {
|
||||
val empty = State()
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package wow.doge.mygame.game.entities
|
||||
package wow.doge.mygame.game.entities.player
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.DispatcherSelector
|
||||
@ -13,9 +13,13 @@ import com.softwaremill.tagging._
|
||||
import io.odin.Logger
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.execution.AsyncQueue
|
||||
import wow.doge.mygame.AppError
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||
import wow.doge.mygame.game.GameApp
|
||||
import wow.doge.mygame.game.entities.CharacterStats
|
||||
import wow.doge.mygame.game.entities.StatsActor
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
@ -27,7 +31,8 @@ import wow.doge.mygame.utils.wrappers.jme._
|
||||
|
||||
object PlayerController {
|
||||
sealed trait Error
|
||||
case class CouldNotCreatePlayerModel(reason: AssetManager.Error) extends Error
|
||||
final case class CouldNotCreatePlayerModel(reason: AssetManager.Error)
|
||||
extends Error
|
||||
|
||||
object Tags {
|
||||
sealed trait PlayerNode
|
||||
@ -49,8 +54,8 @@ object PlayerController {
|
||||
initialPlayerPos: ImVector3f,
|
||||
playerEventBus: GameEventBus[PlayerEvent],
|
||||
playerPhysicsControl: BetterCharacterControl,
|
||||
// appScheduler: monix.execution.Scheduler,
|
||||
scheduler: AsyncScheduler,
|
||||
fxScheduler: Schedulers.FxScheduler,
|
||||
playerNode: PlayerNode,
|
||||
cameraNode: PlayerCameraNode,
|
||||
cameraPivotNode: PlayerCameraPivotNode,
|
||||
@ -62,20 +67,26 @@ object PlayerController {
|
||||
enqueueR,
|
||||
camera
|
||||
).behavior(playerPhysicsControl)
|
||||
new PlayerActorSupervisor.Props(
|
||||
val statsActorBeh = new StatsActor.Props(
|
||||
CharacterStats.Health(100),
|
||||
CharacterStats.Stamina(100)
|
||||
).behavior
|
||||
new PlayerActor.Props(
|
||||
playerEventBus,
|
||||
tickEventBus,
|
||||
movementActorBeh,
|
||||
scheduler
|
||||
statsActorBeh,
|
||||
scheduler,
|
||||
fxScheduler,
|
||||
AsyncQueue.bounded(50)(scheduler.value)
|
||||
).behavior
|
||||
}
|
||||
val playerActor =
|
||||
gameApp.spawnGameActor(
|
||||
playerActorBehavior,
|
||||
// Some("playerActorSupervisor"),
|
||||
props = DispatcherSelector.default()
|
||||
)
|
||||
val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
|
||||
val create: IO[AppError, ActorRef[PlayerActor.Command]] =
|
||||
(for {
|
||||
// playerActor <-
|
||||
// // AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
|
||||
|
@ -1,39 +1,167 @@
|
||||
package wow.doge.mygame.game.entities
|
||||
package wow.doge.mygame.game.entities.player
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.LogOptions
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
import cats.syntax.eq._
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import monix.eval.Task
|
||||
import monix.execution.CancelableFuture
|
||||
import monix.reactive.Observable
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.game.entities.CharacterStats
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.MovementDirection
|
||||
|
||||
object PlayerMovementEventListener {
|
||||
final case class State(
|
||||
keysPressed: Int,
|
||||
staminaTimer: CancelableFuture[Unit],
|
||||
staminaRegenTimer: CancelableFuture[Unit]
|
||||
)
|
||||
|
||||
class Props(
|
||||
val playerActor: PlayerActor.Ref,
|
||||
val asyncScheduler: Schedulers.AsyncScheduler
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.setup[PlayerMovementEvent] { ctx =>
|
||||
new PlayerMovementEventListener(ctx, this)
|
||||
.receive(State(0, CancelableFuture.unit, CancelableFuture.unit))
|
||||
}
|
||||
}
|
||||
}
|
||||
class PlayerMovementEventListener(
|
||||
ctx: ActorContext[PlayerMovementEvent],
|
||||
props: PlayerMovementEventListener.Props
|
||||
) {
|
||||
import PlayerMovementEventListener._
|
||||
import PlayerMovementEvent._
|
||||
def apply(movementActor: ActorRef[ImMovementActor.Command]) =
|
||||
import com.softwaremill.quicklens._
|
||||
def receive(state: State): Behavior[PlayerMovementEvent] =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.TRACE)
|
||||
.withLogger(
|
||||
Logger[PlayerMovementEventListener.type].underlying
|
||||
),
|
||||
Behaviors.setup[PlayerMovementEvent](ctx =>
|
||||
Behaviors.setup[PlayerMovementEvent] { ctx =>
|
||||
implicit val timeout = Timeout(1.second)
|
||||
implicit val sched = ctx.system.scheduler
|
||||
|
||||
val staminaTimer =
|
||||
Task.deferAction(implicit s =>
|
||||
Observable
|
||||
.interval(250.millis)
|
||||
.doOnNext(_ => Task(pprint.log("Sending Stamina Consume Item")))
|
||||
.mapEvalF(_ =>
|
||||
props.playerActor
|
||||
.askL(
|
||||
PlayerActor
|
||||
.ConsumeStamina(CharacterStats.DamageStamina(25), _)
|
||||
)
|
||||
)
|
||||
.doOnNext(stats =>
|
||||
if (stats.stamina.toInt === 0)
|
||||
Task(props.playerActor ! PlayerActor.StopMoving)
|
||||
else Task.unit
|
||||
)
|
||||
.takeWhile(_.stamina.toInt >= 0)
|
||||
.completedL
|
||||
)
|
||||
|
||||
val staminaRegenTimer =
|
||||
Task.deferAction(implicit s =>
|
||||
Observable
|
||||
.interval(500.millis)
|
||||
.doOnNext(_ => Task(pprint.log("Sending Stamina Regen Item")))
|
||||
.mapEvalF(_ =>
|
||||
props.playerActor.askL(
|
||||
PlayerActor
|
||||
.HealStamina(CharacterStats.HealStamina(1), _)
|
||||
)
|
||||
)
|
||||
.takeWhile(_.stamina.toInt =!= 100)
|
||||
.delayExecution(1.second)
|
||||
.completedL
|
||||
)
|
||||
|
||||
def handleStamina(pressed: Boolean) = {
|
||||
state.staminaRegenTimer.cancel()
|
||||
if (pressed) {
|
||||
val nextState1 =
|
||||
if (state.keysPressed === 0)
|
||||
state
|
||||
.modify(_.staminaTimer)
|
||||
.setTo(
|
||||
staminaTimer.runToFuture(props.asyncScheduler.value)
|
||||
)
|
||||
else state
|
||||
val nextState2 = nextState1
|
||||
.modify(_.keysPressed)
|
||||
.using(_ + 1)
|
||||
nextState2
|
||||
} else {
|
||||
val nextState1 = state
|
||||
.modify(_.keysPressed)
|
||||
.using(_ - 1)
|
||||
if (nextState1.keysPressed === 0) {
|
||||
nextState1.staminaTimer.cancel()
|
||||
nextState1
|
||||
.modify(_.staminaRegenTimer)
|
||||
.setTo(
|
||||
staminaRegenTimer.runToFuture(props.asyncScheduler.value)
|
||||
)
|
||||
} else
|
||||
nextState1
|
||||
}
|
||||
}
|
||||
|
||||
Behaviors.receiveMessage {
|
||||
case PlayerMovedLeft(pressed) =>
|
||||
movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||
Behaviors.same
|
||||
// props.movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||
// props.playerActor ! PlayerActor.MoveLeft(pressed)
|
||||
props.playerActor ! PlayerActor.Walk(
|
||||
pressed,
|
||||
MovementDirection.Left
|
||||
)
|
||||
receive(handleStamina(pressed))
|
||||
case PlayerMovedRight(pressed) =>
|
||||
movementActor ! ImMovementActor.MoveRight(pressed)
|
||||
Behaviors.same
|
||||
// props.playerActor ! PlayerActor.MoveRight(pressed)
|
||||
props.playerActor ! PlayerActor.Walk(
|
||||
pressed,
|
||||
MovementDirection.Right
|
||||
)
|
||||
receive(handleStamina(pressed))
|
||||
case PlayerMovedForward(pressed) =>
|
||||
movementActor ! ImMovementActor.MoveUp(pressed)
|
||||
Behaviors.same
|
||||
// props.playerActor ! PlayerActor.MoveUp(pressed)
|
||||
props.playerActor ! PlayerActor.Walk(
|
||||
pressed,
|
||||
MovementDirection.Forward
|
||||
)
|
||||
receive(handleStamina(pressed))
|
||||
case PlayerMovedBackward(pressed) =>
|
||||
movementActor ! ImMovementActor.MoveDown(pressed)
|
||||
Behaviors.same
|
||||
// props.playerActor ! PlayerActor.MoveDown(pressed)
|
||||
props.playerActor ! PlayerActor.Walk(
|
||||
pressed,
|
||||
MovementDirection.Backward
|
||||
)
|
||||
receive(handleStamina(pressed))
|
||||
case PlayerJumped =>
|
||||
movementActor ! ImMovementActor.Jump
|
||||
props.playerActor ! PlayerActor.Jump
|
||||
// props.playerActor
|
||||
// ctx.ask(
|
||||
// props.playerActor,
|
||||
// PlayerActor
|
||||
// .ConsumeStamina(CharacterStats.DamageStamina(1), _)
|
||||
// )(_ => ())
|
||||
Behaviors.same
|
||||
// case PlayerTurnedRight =>
|
||||
// movementActor ! ImMovementActor.RotateRight
|
||||
@ -42,35 +170,6 @@ object PlayerMovementEventListener {
|
||||
// movementActor ! ImMovementActor.RotateLeft
|
||||
// Behaviors.same
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
//not used
|
||||
object PlayerCameraEventListener {
|
||||
import PlayerCameraEvent._
|
||||
def apply(playerCameraActor: ActorRef[PlayerCameraActor.Command]) =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.TRACE)
|
||||
.withLogger(
|
||||
Logger[PlayerCameraEventListener.type].underlying
|
||||
),
|
||||
Behaviors.setup[PlayerCameraEvent](ctx =>
|
||||
Behaviors.receiveMessagePartial {
|
||||
case CameraMovedUp =>
|
||||
playerCameraActor ! PlayerCameraActor.RotateUp
|
||||
Behaviors.same
|
||||
case CameraMovedDown =>
|
||||
playerCameraActor ! PlayerCameraActor.RotateDown
|
||||
Behaviors.same
|
||||
case CameraLeft =>
|
||||
playerCameraActor ! PlayerCameraActor.RotateLeft
|
||||
Behaviors.same
|
||||
case CameraRight =>
|
||||
playerCameraActor ! PlayerCameraActor.RotateRight
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
139
src/main/scala/wow/doge/mygame/game/entities/player/PlayerMovementReducer.scala
Executable file
139
src/main/scala/wow/doge/mygame/game/entities/player/PlayerMovementReducer.scala
Executable file
@ -0,0 +1,139 @@
|
||||
package wow.doge.mygame.game.entities.player
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.util.Timeout
|
||||
import cats.syntax.eq._
|
||||
import io.odin.Logger
|
||||
import monix.eval.Fiber
|
||||
import monix.eval.Task
|
||||
import monix.reactive.Observable
|
||||
import monix.{eval => me}
|
||||
import wow.doge.mygame.EnumActionEvent
|
||||
import wow.doge.mygame.game.entities.CharacterStats
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.Jump
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkBackward
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkForward
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkLeft
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkRight
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.types.AkkaScheduler
|
||||
import wow.doge.mygame.utils.MovementDirection
|
||||
|
||||
class PlayerMovementReducer(
|
||||
val playerActor: PlayerActor.Ref,
|
||||
logger: Logger[monix.bio.Task]
|
||||
)(implicit
|
||||
akkaSched: AkkaScheduler
|
||||
) {
|
||||
import PlayerMovementReducer._
|
||||
import com.softwaremill.quicklens._
|
||||
|
||||
implicit val timeout = Timeout(1.second)
|
||||
implicit val sched = akkaSched.value
|
||||
|
||||
def staminaTimer(multiplier: Int) =
|
||||
Task.deferAction(implicit s =>
|
||||
Observable
|
||||
.interval(250.millis)
|
||||
.doOnNextF(_ => logger.trace("Sending Stamina Consume Item"))
|
||||
.mapEvalF(_ =>
|
||||
playerActor
|
||||
.askL(
|
||||
PlayerActor
|
||||
.ConsumeStamina(CharacterStats.DamageStamina(1 * multiplier), _)
|
||||
)
|
||||
)
|
||||
.doOnNext(stats =>
|
||||
if (stats.stamina.toInt === 0)
|
||||
Task(playerActor ! PlayerActor.StopMoving)
|
||||
else Task.unit
|
||||
)
|
||||
.takeWhile(_.stamina.toInt >= 0)
|
||||
.completedL
|
||||
)
|
||||
|
||||
def staminaRegenTimer(multiplier: Int) =
|
||||
Task.deferAction(implicit s =>
|
||||
Observable
|
||||
.interval(500.millis)
|
||||
.doOnNextF(_ => logger.trace("Sending Stamina Regen Item"))
|
||||
.mapEvalF(_ =>
|
||||
playerActor.askL(
|
||||
PlayerActor
|
||||
.HealStamina(CharacterStats.HealStamina(1 * multiplier), _)
|
||||
)
|
||||
)
|
||||
.takeWhile(_.stamina.toInt =!= 100)
|
||||
.delayExecution(1.second)
|
||||
.completedL
|
||||
)
|
||||
|
||||
def handleStamina(
|
||||
state: State,
|
||||
pressed: Boolean,
|
||||
consumptionMultiplier: Int,
|
||||
regenMultiplier: Int
|
||||
): Task[State] =
|
||||
state.staminaRegenTimer.cancel >>
|
||||
(if (pressed) {
|
||||
val nextState1 =
|
||||
if (state.keysPressed === 0)
|
||||
staminaTimer(consumptionMultiplier).start.map(
|
||||
state.modify(_.staminaTimer).setTo
|
||||
)
|
||||
else Task.pure(state)
|
||||
val nextState2 =
|
||||
nextState1.map(_.modify(_.keysPressed).using(_ + 1))
|
||||
|
||||
nextState2
|
||||
} else {
|
||||
val nextState1 = state
|
||||
.modify(_.keysPressed)
|
||||
.using(_ - 1)
|
||||
if (nextState1.keysPressed === 0)
|
||||
nextState1.staminaTimer.cancel >>
|
||||
staminaRegenTimer(regenMultiplier).start.map(
|
||||
nextState1.modify(_.staminaRegenTimer).setTo
|
||||
)
|
||||
else
|
||||
Task.pure(nextState1)
|
||||
})
|
||||
|
||||
def value(
|
||||
state: State,
|
||||
action: EnumActionEvent[PlayerMovementInput]
|
||||
): Task[State] =
|
||||
action match {
|
||||
case EnumActionEvent(WalkForward, pressed, tpf) =>
|
||||
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Forward)
|
||||
handleStamina(state, pressed, 1, 1)
|
||||
case EnumActionEvent(WalkRight, pressed, tpf) =>
|
||||
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Right)
|
||||
handleStamina(state, pressed, 1, 1)
|
||||
case EnumActionEvent(WalkLeft, pressed, tpf) =>
|
||||
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Left)
|
||||
handleStamina(state, pressed, 1, 1)
|
||||
case EnumActionEvent(WalkBackward, pressed, tpf) =>
|
||||
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Backward)
|
||||
handleStamina(state, pressed, 1, 1)
|
||||
case EnumActionEvent(Jump, pressed, tpf) =>
|
||||
if (pressed) playerActor ! PlayerActor.Jump else ()
|
||||
handleStamina(state, pressed, 10, 1)
|
||||
}
|
||||
}
|
||||
object PlayerMovementReducer {
|
||||
final case class State(
|
||||
keysPressed: Int,
|
||||
staminaTimer: Fiber[Unit],
|
||||
staminaRegenTimer: Fiber[Unit]
|
||||
)
|
||||
object State {
|
||||
val empty = State(
|
||||
0,
|
||||
me.Fiber(me.Task.unit, me.Task.unit),
|
||||
me.Fiber(me.Task.unit, me.Task.unit)
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
package wow.doge.mygame.game.entities.player.behaviors
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.PostStop
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
import cats.syntax.show._
|
||||
import monix.bio.UIO
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.game.entities.CharacterStats
|
||||
import wow.doge.mygame.game.entities.StatsActor
|
||||
import wow.doge.mygame.game.entities.character.CharacterStates._
|
||||
import wow.doge.mygame.game.entities.player.PlayerActor
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.MovementDirection
|
||||
|
||||
class IdleBehaviorFactory(
|
||||
env: PlayerActor.Env,
|
||||
nextStateFn: AliveSubstate => Behavior[PlayerActor.Command],
|
||||
deadState: Behavior[PlayerActor.Command],
|
||||
consumptionMultiplier: Int => Int
|
||||
)(implicit timeout: Timeout) {
|
||||
import env._
|
||||
|
||||
implicit val sched = ctx.system.scheduler
|
||||
|
||||
def behavior =
|
||||
IdleBehavior(
|
||||
Behaviors
|
||||
.receiveMessage[PlayerActor.Command] {
|
||||
|
||||
case PlayerActor.HandleWalk(curStats, pressed, direction) =>
|
||||
if (curStats.stamina.toInt > 0) {
|
||||
children.movementActor ! (direction match {
|
||||
case MovementDirection.Forward =>
|
||||
ImMovementActor.MoveUp(pressed)
|
||||
case MovementDirection.Backward =>
|
||||
ImMovementActor.MoveDown(pressed)
|
||||
case MovementDirection.Left => ImMovementActor.MoveLeft(pressed)
|
||||
case MovementDirection.Right =>
|
||||
ImMovementActor.MoveRight(pressed)
|
||||
})
|
||||
if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
|
||||
else nextStateFn(AliveSubstate.Idle)
|
||||
} else {
|
||||
children.movementActor ! ImMovementActor.StopMoving
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
// case PlayerActor.Walk(pressed, Direction.Up) => Behaviors.same
|
||||
// case PlayerActor.Walk(pressed, Direction.Down) => Behaviors.same
|
||||
// case PlayerActor.Walk(pressed, Direction.Left) => Behaviors.same
|
||||
// case PlayerActor.Walk(pressed, Direction.Right) => Behaviors.same
|
||||
|
||||
case PlayerActor.StopMoving =>
|
||||
children.movementActor ! ImMovementActor.StopMoving
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.Walk(pressed, direction) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
for {
|
||||
curStats <- children.statsActor.ask(StatsActor.CurrentStats)
|
||||
_ <- Future.successful(
|
||||
ctx.self ! PlayerActor.HandleWalk(curStats, pressed, direction)
|
||||
)
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.Jump =>
|
||||
children.movementActor ! ImMovementActor.Jump
|
||||
ctx.self.ask(
|
||||
PlayerActor.ConsumeStamina(CharacterStats.DamageStamina(10), _)
|
||||
)
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.TakeDamage(value, replyTo) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
for {
|
||||
res <-
|
||||
children.statsActor.ask(StatsActor.TakeDamageResult(value, _))
|
||||
_ = ctx.self ! PlayerActor.StatsResponse(res, replyTo)
|
||||
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.ConsumeStamina(value, replyTo) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
val newValue =
|
||||
CharacterStats.DamageStamina(consumptionMultiplier(value.toInt))
|
||||
for {
|
||||
response <- children.statsActor.ask(
|
||||
StatsActor.ConsumeStaminaResult(newValue, _)
|
||||
)
|
||||
_ =
|
||||
ctx.self ! PlayerActor
|
||||
.StatsResponse(response, replyTo)
|
||||
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.CurrentStats(replyTo) =>
|
||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.HealHealth(value, replyTo) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
for {
|
||||
response <- children.statsActor.ask(
|
||||
StatsActor.HealHealthResult(value, _)
|
||||
)
|
||||
_ =
|
||||
ctx.self ! PlayerActor
|
||||
.StatsResponse(StatsActor.Status.Alive -> response, replyTo)
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.HealStamina(value, replyTo) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
for {
|
||||
response <- children.statsActor.ask(
|
||||
StatsActor.HealStaminaResult(value, _)
|
||||
)
|
||||
_ =
|
||||
ctx.self ! PlayerActor
|
||||
.StatsResponse(StatsActor.Status.Alive -> response, replyTo)
|
||||
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.GetStatus(replyTo) =>
|
||||
replyTo ! PlayerActor.Status.Alive
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.GetStatsObservable(replyTo) =>
|
||||
import monix.{eval => me}
|
||||
replyTo !
|
||||
UIO(
|
||||
Observable
|
||||
.repeatEvalF(
|
||||
me.Task.deferFuture(props.statsQueue.poll())
|
||||
)
|
||||
.publish(props.fxScheduler.value)
|
||||
.refCount
|
||||
)
|
||||
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.StatsResponse(response, replyTo) =>
|
||||
response match {
|
||||
case (status, stats) =>
|
||||
status match {
|
||||
case StatsActor.Status.Dead => ctx.self ! PlayerActor.Die
|
||||
case StatsActor.Status.Alive => ()
|
||||
}
|
||||
props.statsQueue
|
||||
.offer(stats)
|
||||
.foreach { _ =>
|
||||
pprint.log(show"Published stats $stats")
|
||||
replyTo ! stats
|
||||
}(props.scheduler.value)
|
||||
}
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.Die => deadState
|
||||
|
||||
case PlayerActor.LogError(ex) =>
|
||||
ctx.log.error(ex.getMessage)
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
final case class IdleBehavior(value: Behavior[PlayerActor.Command])
|
@ -11,8 +11,8 @@ import wow.doge.mygame.game.subsystems.ai.gdx.MyIndexedGraph
|
||||
// import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph
|
||||
// import scala.jdk.javaapi.CollectionConverters._
|
||||
|
||||
case class City(x: Float, y: Float, name: String, index: Int)
|
||||
case class Street(fromNode: City, toNode: City, cost: Float)
|
||||
final case class City(x: Float, y: Float, name: String, index: Int)
|
||||
final case class Street(fromNode: City, toNode: City, cost: Float)
|
||||
extends Connection[City] {
|
||||
|
||||
override def getCost(): Float = cost
|
||||
|
@ -18,6 +18,7 @@ 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 wow.doge.mygame.utils.methodName
|
||||
|
||||
object GameInputHandler {
|
||||
|
||||
@ -29,16 +30,16 @@ object GameInputHandler {
|
||||
) {
|
||||
def begin =
|
||||
for {
|
||||
_ <- UIO(setupMovementKeys(inputManager))
|
||||
_ <- UIO(setupMovementKeys)
|
||||
// _ <- UIO(setupAnalogMovementKeys)
|
||||
_ <- UIO(setupCameraKeys())
|
||||
_ <- toIO(
|
||||
me.Task.parSequence(
|
||||
Seq(
|
||||
playerMovementInputEventsGenerator(
|
||||
inputManager,
|
||||
playerEventBus
|
||||
).completedL,
|
||||
// playerMovementInputEventsGenerator(
|
||||
// inputManager,
|
||||
// playerEventBus
|
||||
// ).completedL,
|
||||
// generateAnalogMovementEvents(
|
||||
// inputManager,
|
||||
// playerEventBus
|
||||
@ -55,7 +56,7 @@ object GameInputHandler {
|
||||
).startAndForget
|
||||
} yield ()
|
||||
|
||||
def setupMovementKeys(inputManager: InputManager) =
|
||||
def setupMovementKeys =
|
||||
inputManager.withEnumMappings(PlayerMovementInput) {
|
||||
case PlayerMovementInput.WalkRight =>
|
||||
new KeyTrigger(KeyInput.KEY_D) :: Nil
|
||||
@ -128,9 +129,6 @@ object GameInputHandler {
|
||||
|
||||
}
|
||||
|
||||
def methodName(implicit enclosing: sourcecode.Enclosing) =
|
||||
enclosing.value.split(" ")(0).split("""\.""").last
|
||||
|
||||
def playerMovementInputEventsGenerator(
|
||||
inputManager: InputManager,
|
||||
playerEventBus: GameEventBus[PlayerEvent]
|
||||
@ -144,28 +142,28 @@ object GameInputHandler {
|
||||
case PlayerMovementInput.WalkLeft =>
|
||||
me.Task(
|
||||
playerEventBus ! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedLeft(pressed = action.value),
|
||||
PlayerMovementEvent.PlayerMovedLeft(action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
case PlayerMovementInput.WalkRight =>
|
||||
me.Task(
|
||||
playerEventBus ! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedRight(pressed = action.value),
|
||||
PlayerMovementEvent.PlayerMovedRight(action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
case PlayerMovementInput.WalkForward =>
|
||||
me.Task(
|
||||
playerEventBus ! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedForward(pressed = action.value),
|
||||
PlayerMovementEvent.PlayerMovedForward(action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
case PlayerMovementInput.WalkBackward =>
|
||||
me.Task(
|
||||
playerEventBus ! EventBus.Publish(
|
||||
PlayerMovementEvent.PlayerMovedBackward(pressed = action.value),
|
||||
PlayerMovementEvent.PlayerMovedBackward(action.value),
|
||||
name
|
||||
)
|
||||
)
|
||||
|
@ -1,6 +1,9 @@
|
||||
package wow.doge.mygame.game.subsystems.input
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import enumeratum.EnumEntry._
|
||||
import enumeratum._
|
||||
import io.circe.generic.semiauto._
|
||||
|
||||
sealed trait PlayerMovementInput extends EnumEntry with UpperSnakecase
|
||||
object PlayerMovementInput extends Enum[PlayerMovementInput] {
|
||||
@ -10,6 +13,9 @@ object PlayerMovementInput extends Enum[PlayerMovementInput] {
|
||||
case object WalkLeft extends PlayerMovementInput
|
||||
case object WalkBackward extends PlayerMovementInput
|
||||
case object Jump extends PlayerMovementInput
|
||||
|
||||
implicit val eq = Eq.fromUniversalEquals[PlayerMovementInput]
|
||||
implicit val show = Show.fromToString[PlayerMovementInput]
|
||||
}
|
||||
|
||||
sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
|
||||
@ -17,6 +23,9 @@ object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
|
||||
val values = findValues
|
||||
case object TurnRight extends PlayerAnalogMovementInput
|
||||
case object TurnLeft extends PlayerAnalogMovementInput
|
||||
|
||||
implicit val eq = Eq.fromUniversalEquals[PlayerAnalogMovementInput]
|
||||
implicit val show = Show.fromToString[PlayerAnalogMovementInput]
|
||||
}
|
||||
|
||||
sealed trait PlayerCameraInput extends EnumEntry with UpperSnakecase
|
||||
@ -26,6 +35,10 @@ object PlayerCameraInput extends Enum[PlayerCameraInput] {
|
||||
case object CameraRotateRight extends PlayerCameraInput
|
||||
case object CameraRotateUp extends PlayerCameraInput
|
||||
case object CameraRotateDown extends PlayerCameraInput
|
||||
|
||||
implicit val eq = Eq.fromUniversalEquals[PlayerCameraInput]
|
||||
implicit val show = Show.fromToString[PlayerCameraInput]
|
||||
implicit val codec = deriveCodec[PlayerCameraInput]
|
||||
}
|
||||
|
||||
sealed trait MiscInput extends EnumEntry with UpperSnakecase
|
||||
|
87
src/main/scala/wow/doge/mygame/game/subsystems/input/InputMappings.scala
Executable file
87
src/main/scala/wow/doge/mygame/game/subsystems/input/InputMappings.scala
Executable file
@ -0,0 +1,87 @@
|
||||
package wow.doge.mygame.game.subsystems.input
|
||||
|
||||
import com.jme3.input.KeyInput
|
||||
import com.jme3.input.MouseInput
|
||||
import com.jme3.input.controls.KeyTrigger
|
||||
import com.jme3.input.controls.MouseAxisTrigger
|
||||
import monix.{eval => me}
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.wrappers.jme.InputManager
|
||||
|
||||
class InputMappings(inputManager: InputManager) {
|
||||
|
||||
def setup =
|
||||
for {
|
||||
_ <- setupMovementKeys
|
||||
// _ <- setupAnalogMovementKeys
|
||||
_ <- setupCameraKeys
|
||||
_ <- cursorToggle
|
||||
} yield ()
|
||||
|
||||
def setupMovementKeys =
|
||||
inputManager.withEnumMappings(PlayerMovementInput) {
|
||||
case PlayerMovementInput.WalkRight =>
|
||||
new KeyTrigger(KeyInput.KEY_D) :: Nil
|
||||
case PlayerMovementInput.WalkLeft =>
|
||||
new KeyTrigger(KeyInput.KEY_A) :: Nil
|
||||
case PlayerMovementInput.WalkForward =>
|
||||
new KeyTrigger(KeyInput.KEY_W) :: Nil
|
||||
case PlayerMovementInput.WalkBackward =>
|
||||
new KeyTrigger(KeyInput.KEY_S) :: Nil
|
||||
case PlayerMovementInput.Jump =>
|
||||
new KeyTrigger(KeyInput.KEY_SPACE) :: Nil
|
||||
}
|
||||
|
||||
def setupAnalogMovementKeys =
|
||||
inputManager.withEnumMappings(PlayerAnalogMovementInput) {
|
||||
case PlayerAnalogMovementInput.TurnRight =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_D))
|
||||
case PlayerAnalogMovementInput.TurnLeft =>
|
||||
Seq(new KeyTrigger(KeyInput.KEY_A))
|
||||
}
|
||||
|
||||
def setupCameraKeys =
|
||||
inputManager.withEnumMappings(PlayerCameraInput) {
|
||||
case PlayerCameraInput.CameraRotateLeft =>
|
||||
Seq(
|
||||
new KeyTrigger(KeyInput.KEY_LEFT),
|
||||
new MouseAxisTrigger(MouseInput.AXIS_X, false)
|
||||
)
|
||||
case PlayerCameraInput.CameraRotateRight =>
|
||||
Seq(
|
||||
new KeyTrigger(KeyInput.KEY_RIGHT),
|
||||
new MouseAxisTrigger(MouseInput.AXIS_X, true)
|
||||
)
|
||||
case PlayerCameraInput.CameraRotateUp =>
|
||||
Seq(
|
||||
new KeyTrigger(KeyInput.KEY_UP),
|
||||
new MouseAxisTrigger(MouseInput.AXIS_Y, false)
|
||||
)
|
||||
case PlayerCameraInput.CameraRotateDown =>
|
||||
Seq(
|
||||
new KeyTrigger(KeyInput.KEY_DOWN),
|
||||
new MouseAxisTrigger(MouseInput.AXIS_Y, true)
|
||||
)
|
||||
}
|
||||
|
||||
def cursorToggle =
|
||||
inputManager.withMapping(
|
||||
MiscInput.ToggleCursor,
|
||||
new KeyTrigger(KeyInput.KEY_Z)
|
||||
) >>
|
||||
inputManager
|
||||
.enumEntryObservableAction(MiscInput.ToggleCursor)
|
||||
.doOnNext(action =>
|
||||
action.binding match {
|
||||
case MiscInput.ToggleCursor =>
|
||||
if (action.value)(inputManager.cursorVisible =
|
||||
!inputManager.cursorVisible).toTask
|
||||
else me.Task.unit
|
||||
}
|
||||
)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
|
||||
}
|
@ -1,55 +1,14 @@
|
||||
package wow.doge.mygame.game.subsystems.level
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.bullet.control.RigidBodyControl
|
||||
import com.jme3.bullet.util.CollisionShapeFactory
|
||||
import com.jme3.light.AmbientLight
|
||||
import com.jme3.light.DirectionalLight
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.math.Vector3f
|
||||
import com.jme3.renderer.ViewPort
|
||||
import com.jme3.scene.Spatial
|
||||
import monix.bio.IO
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.AppError
|
||||
object DefaultGameLevel {
|
||||
|
||||
def apply(
|
||||
assetManager: AssetManager,
|
||||
viewPort: ViewPort
|
||||
) = {
|
||||
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")
|
||||
}
|
||||
)
|
||||
val landscape: RigidBodyControl = new RigidBodyControl(sceneShape, 0)
|
||||
|
||||
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)
|
||||
|
||||
val al = new AmbientLight();
|
||||
al.setColor(ColorRGBA.White.mult(1.3f));
|
||||
// app.rootNode.addLight(al);
|
||||
|
||||
val dl = new DirectionalLight();
|
||||
dl.setColor(ColorRGBA.White);
|
||||
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
|
||||
// app.rootNode.addLight(dl);
|
||||
new GameLevel(
|
||||
model = sceneModel,
|
||||
physicsControl = landscape,
|
||||
ambientLight = al,
|
||||
directionalLight = dl
|
||||
)
|
||||
}
|
||||
|
||||
def apply(
|
||||
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager,
|
||||
viewPort: ViewPort
|
||||
|
@ -15,6 +15,8 @@ import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
||||
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||
|
||||
// case class Blah(humbug: String)
|
||||
|
||||
class GameLevel(
|
||||
val model: Spatial,
|
||||
val physicsControl: RigidBodyControl,
|
||||
|
30
src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
Normal file → Executable file
30
src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
Normal file → Executable file
@ -12,11 +12,14 @@ import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
final case class CardinalDirection(
|
||||
left: Boolean = false,
|
||||
right: Boolean = false,
|
||||
up: Boolean = false,
|
||||
down: Boolean = false
|
||||
left: Boolean,
|
||||
right: Boolean,
|
||||
up: Boolean,
|
||||
down: Boolean
|
||||
)
|
||||
object CardinalDirection {
|
||||
val default = CardinalDirection(false, false, false, false)
|
||||
}
|
||||
|
||||
sealed trait RotateDir
|
||||
object RotateDir {
|
||||
@ -33,6 +36,7 @@ object ImMovementActor {
|
||||
final case class MoveUp(pressed: Boolean) extends Movement
|
||||
final case class MoveRight(pressed: Boolean) extends Movement
|
||||
final case class MoveDown(pressed: Boolean) extends Movement
|
||||
case object StopMoving extends Movement
|
||||
case object Jump extends Movement
|
||||
|
||||
class Props(
|
||||
@ -53,7 +57,9 @@ object ImMovementActor {
|
||||
*
|
||||
* @param cardinalDir The four directions the character can move
|
||||
*/
|
||||
final case class State(cardinalDir: CardinalDirection = CardinalDirection())
|
||||
final case class State(
|
||||
cardinalDir: CardinalDirection = CardinalDirection.default
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@ -72,28 +78,32 @@ class ImMovementActor[T](
|
||||
.receiveMessage[Command] {
|
||||
case m: Movement =>
|
||||
m match {
|
||||
case StopMoving =>
|
||||
cm.stop(movable)
|
||||
receive(State(CardinalDirection.default), new Vector3f)
|
||||
|
||||
case MoveLeft(pressed) =>
|
||||
stopIfNotPressed(pressed)
|
||||
receive(
|
||||
state = state.modify(_.cardinalDir.left).setTo(pressed),
|
||||
state.modify(_.cardinalDir.left).setTo(pressed),
|
||||
walkDirBuf
|
||||
)
|
||||
case MoveUp(pressed) =>
|
||||
stopIfNotPressed(pressed)
|
||||
receive(
|
||||
state = state.modify(_.cardinalDir.up).setTo(pressed),
|
||||
state.modify(_.cardinalDir.up).setTo(pressed),
|
||||
walkDirBuf
|
||||
)
|
||||
case MoveRight(pressed) =>
|
||||
stopIfNotPressed(pressed)
|
||||
receive(
|
||||
state = state.modify(_.cardinalDir.right).setTo(pressed),
|
||||
state.modify(_.cardinalDir.right).setTo(pressed),
|
||||
walkDirBuf
|
||||
)
|
||||
case MoveDown(pressed) =>
|
||||
stopIfNotPressed(pressed)
|
||||
receive(
|
||||
state = state.modify(_.cardinalDir.down).setTo(pressed),
|
||||
state.modify(_.cardinalDir.down).setTo(pressed),
|
||||
walkDirBuf
|
||||
)
|
||||
case Jump =>
|
||||
@ -188,7 +198,7 @@ object MovementActor {
|
||||
* @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
|
||||
*/
|
||||
final case class State(
|
||||
cardinalDir: CardinalDirection = CardinalDirection(),
|
||||
cardinalDir: CardinalDirection = CardinalDirection.default,
|
||||
walkDirection: Vector3f = Vector3f.UNIT_X
|
||||
)
|
||||
|
||||
|
@ -11,11 +11,18 @@ import com.simsilica.es.Filters
|
||||
|
||||
class EntityQuery(ed: EntityData) {
|
||||
private var cfilter: Option[ComponentFilter[_ <: EntityComponent]] = None
|
||||
@SuppressWarnings(Array("org.wartremover.warts.MutableDataStructures"))
|
||||
private val buf = collection.mutable.ListBuffer.empty[Class[_]]
|
||||
|
||||
def filter[T <: EntityComponent](field: String, value: Object)(implicit
|
||||
ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
@SuppressWarnings(
|
||||
Array(
|
||||
"org.wartremover.warts.AsInstanceOf",
|
||||
"org.wartremover.warts.Equals"
|
||||
)
|
||||
)
|
||||
val c = ev.runtimeClass.asInstanceOf[Class[T]]
|
||||
cfilter = Try(Filters.fieldEquals(c, field, value)).toOption
|
||||
this
|
||||
@ -24,6 +31,12 @@ class EntityQuery(ed: EntityData) {
|
||||
def filterOr[T <: EntityComponent](operands: ComponentFilter[_ <: T]*)(
|
||||
implicit ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
@SuppressWarnings(
|
||||
Array(
|
||||
"org.wartremover.warts.AsInstanceOf",
|
||||
"org.wartremover.warts.Equals"
|
||||
)
|
||||
)
|
||||
val c = ev.runtimeClass.asInstanceOf[Class[T]]
|
||||
cfilter = Try(Filters.or(c, operands: _*)).toOption
|
||||
this
|
||||
@ -32,6 +45,12 @@ class EntityQuery(ed: EntityData) {
|
||||
def filterAnd[T <: EntityComponent](operands: ComponentFilter[_ <: T]*)(
|
||||
implicit ev: ClassTag[T]
|
||||
): EntityQuery = {
|
||||
@SuppressWarnings(
|
||||
Array(
|
||||
"org.wartremover.warts.AsInstanceOf",
|
||||
"org.wartremover.warts.Equals"
|
||||
)
|
||||
)
|
||||
val c = ev.runtimeClass.asInstanceOf[Class[T]]
|
||||
cfilter = Try(Filters.and(c, operands: _*)).toOption
|
||||
this
|
||||
@ -45,9 +64,8 @@ class EntityQuery(ed: EntityData) {
|
||||
this
|
||||
}
|
||||
|
||||
def components[T <: EntityComponent](lst: Class[T]*): EntitySet = {
|
||||
def components[T <: EntityComponent](lst: Class[T]*): EntitySet =
|
||||
ed.getEntities(lst: _*)
|
||||
}
|
||||
|
||||
def result: EntitySet =
|
||||
cfilter.fold(ed.getEntities(buf.toList: _*)) { filters =>
|
||||
|
@ -1,29 +1,74 @@
|
||||
package wow.doge.mygame.implicits
|
||||
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.beans.{value => jfxbv}
|
||||
import javafx.beans.property.ObjectProperty
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.event.ActionEvent
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.{input => jfxsi}
|
||||
import javafx.{event => jfxe}
|
||||
import monix.bio.Task
|
||||
import monix.eval.Coeval
|
||||
import monix.execution.Ack
|
||||
import monix.execution.Cancelable
|
||||
import monix.execution.Scheduler
|
||||
import monix.execution.cancelables.CompositeCancelable
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.Observer
|
||||
import monix.reactive.OverflowStrategy
|
||||
import monix.tail.Iterant
|
||||
import monix.{eval => me}
|
||||
import org.gerweck.scalafx.util._
|
||||
import scalafx.Includes._
|
||||
import scalafx.beans.property.Property
|
||||
import scalafx.beans.property.ReadOnlyProperty
|
||||
import scalafx.collections.ObservableBuffer
|
||||
import scalafx.event.subscriptions.Subscription
|
||||
import scalafx.scene.Scene
|
||||
import scalafx.scene.control.ButtonBase
|
||||
import scalafx.scene.control.MenuItem
|
||||
|
||||
trait JavaFXMonixObservables {
|
||||
import JavaFXMonixObservables._
|
||||
implicit def extendedScene(scene: Scene) = new SceneExt(scene)
|
||||
implicit def extendedProperty[T, J](
|
||||
propery: Property[T, J]
|
||||
): PropertyExt[T, J] =
|
||||
new PropertyExt(propery)
|
||||
implicit def extendedObjectPropety[A](prop: ObjectProperty[A]) =
|
||||
new ObjectPropertyExt[A](prop)
|
||||
implicit def extendedReadOnlyObjectPropety[T, J](
|
||||
prop: ReadOnlyProperty[T, J]
|
||||
) =
|
||||
new ReadOnlyPropertyExt[T, J](prop)
|
||||
implicit def extendedObservableList[A](
|
||||
list: ObservableBuffer[A]
|
||||
) = new ObservableListExt(list)
|
||||
implicit def extendedStringObservableList(
|
||||
list: ObservableList[String]
|
||||
) = new StringObservableListExt(list)
|
||||
implicit def extendedObjectPropertyObservableList[A](
|
||||
prop: ObjectProperty[ObservableList[A]]
|
||||
) = new ObjectPropertyObservableListExt(prop)
|
||||
implicit def extendedButton(button: ButtonBase) = new ButtonBaseExt(button)
|
||||
implicit def extendedMenuItem(item: MenuItem) = new MenuItemExt(item)
|
||||
|
||||
// implicit val implShowForOsRelPath = Show.fromToString[os.Path]
|
||||
implicit def osRelPath2String(path: os.RelPath): String = path.toString()
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
object JavaFXMonixObservables {
|
||||
|
||||
implicit final class SceneObservables(private val scene: Scene)
|
||||
extends AnyVal {
|
||||
final class SceneExt(private val scene: Scene) extends AnyVal {
|
||||
|
||||
def observableMousePressed(): Observable[jfxsi.MouseEvent] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
|
||||
override def handle(event: jfxsi.MouseEvent): Unit = {
|
||||
override def handle(event: jfxsi.MouseEvent): Unit =
|
||||
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
scene.onMousePressed = l
|
||||
@ -36,17 +81,17 @@ object JavaFXMonixObservables {
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
def observableMouseDragged(): Observable[jfxsi.MouseEvent] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
|
||||
override def handle(event: jfxsi.MouseEvent): Unit = {
|
||||
override def handle(event: jfxsi.MouseEvent): Unit =
|
||||
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
scene.onMouseDragged = l
|
||||
scene.onMouseDragged = l;
|
||||
c := Cancelable(() =>
|
||||
scene.removeEventHandler(
|
||||
jfxsi.MouseEvent.MOUSE_DRAGGED,
|
||||
@ -58,46 +103,224 @@ object JavaFXMonixObservables {
|
||||
}
|
||||
}
|
||||
|
||||
implicit final class BindObs[A, B](private val prop: Property[A, B])
|
||||
final class PropertyExt[T, J](private val prop: Property[T, J])
|
||||
extends AnyVal {
|
||||
def <--[T](op: Observable[(ObservableValue[_ <: B], B, B)] => T) = {
|
||||
op(prop.observableChange())
|
||||
}
|
||||
def -->[J1 >: J](sub: Observer[J1]) =
|
||||
prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
|
||||
|
||||
def observableChange(): Observable[(ObservableValue[_ <: B], B, B)] = {
|
||||
def ==>(op: Property[T, J]) =
|
||||
op <== prop
|
||||
|
||||
def <--(
|
||||
obs: Observable[T]
|
||||
)(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
||||
c += obs.doOnNextF(v => Coeval(prop.value = v)).subscribe()
|
||||
|
||||
def asOption = prop.map(Option(_))
|
||||
|
||||
def observableChange[J1 >: J]: Observable[J1] = {
|
||||
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))
|
||||
}
|
||||
}
|
||||
val canc =
|
||||
prop.onChange((a, b, c1) =>
|
||||
if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
|
||||
)
|
||||
|
||||
prop.addListener(listener)
|
||||
|
||||
c := Cancelable(() => prop.removeListener(listener))
|
||||
c := Cancelable(() => canc.cancel())
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit final class OnActionObservable(
|
||||
private val button: ButtonBase
|
||||
) extends AnyVal {
|
||||
def observableAction(): Observable[jfxe.ActionEvent] = {
|
||||
final class ObjectPropertyExt[A](private val prop: ObjectProperty[A])
|
||||
extends AnyVal {
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Throw"))
|
||||
def -->(sub: Observer[A]) =
|
||||
prop.onChange((a, b, c) =>
|
||||
if (c != null)
|
||||
if (sub.onNext(c) == Ack.Stop) throw new Exception("boom")
|
||||
)
|
||||
|
||||
def ==>(op: Property[A, A]) =
|
||||
prop.onChange((a, b, c) => if (c != null) op() = c)
|
||||
|
||||
def <--(obs: Observable[A])(implicit s: Scheduler) =
|
||||
obs.doOnNextF(v => Coeval(prop() = v)).subscribe()
|
||||
|
||||
def observableChange[J1 >: A]: Observable[J1] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val l = new jfxe.EventHandler[jfxe.ActionEvent] {
|
||||
override def handle(event: jfxe.ActionEvent): Unit = {
|
||||
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
||||
|
||||
val canc = prop.onChange((_, _, c1) =>
|
||||
if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
|
||||
)
|
||||
|
||||
c := Cancelable(() => canc.cancel())
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ObservableListExt[A](
|
||||
private val buffer: ObservableBuffer[A]
|
||||
) extends AnyVal {
|
||||
|
||||
// def -->(sub: Observer[A]) =
|
||||
// buffer.onChange((a, b, c) => if (c != null) sub.onNext(c))
|
||||
|
||||
// def -->(op: Property[A, A]) = {
|
||||
// buffer.onChange((a, b, c) => if (c != null) op() = c)
|
||||
// }
|
||||
|
||||
def <--(
|
||||
obs: Observable[A]
|
||||
)(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
||||
c += obs
|
||||
.doOnNextF(v =>
|
||||
for {
|
||||
_ <- Coeval(
|
||||
println(s"Current Thread: ${Thread.currentThread().getName}")
|
||||
)
|
||||
_ <- Coeval(buffer.clear())
|
||||
_ <- Coeval(buffer += v)
|
||||
} yield ()
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
def observableChange[J1 >: A]: Observable[J1] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
|
||||
implicit val s = sub.scheduler
|
||||
|
||||
val canc =
|
||||
buffer.onChange((buf, _) =>
|
||||
loop(sub, buf.toIterable.iterator, c).runToFuture
|
||||
)
|
||||
|
||||
c := Cancelable(() => canc.cancel())
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
private def loop(
|
||||
sub: Observer[A],
|
||||
it: Iterator[A],
|
||||
c: Cancelable
|
||||
): Task[Unit] =
|
||||
if (it.hasNext) {
|
||||
val next = it.next()
|
||||
Task.deferFuture(sub.onNext(next)).flatMap {
|
||||
case Ack.Continue => loop(sub, it, c)
|
||||
case Ack.Stop => Task(c.cancel())
|
||||
}
|
||||
} else Task.unit
|
||||
}
|
||||
|
||||
final class StringObservableListExt(
|
||||
private val buffer: ObservableList[String]
|
||||
) extends AnyVal {
|
||||
// def ++=[T](that: Seq[T])(implicit S: Show[T]): Unit =
|
||||
// buffer ++= that.map(S.show)
|
||||
// def ++=[T](that: Seq[T])(implicit C: CssPath[T]): Unit =
|
||||
// buffer ++= that.map(C.path)
|
||||
}
|
||||
|
||||
final class ReadOnlyPropertyExt[T, J](
|
||||
private val prop: ReadOnlyProperty[T, J]
|
||||
) extends AnyVal {
|
||||
def -->[J1 >: J](sub: Observer[J1]) =
|
||||
prop.onChange((a, b, c) => if (c != null) sub.onNext(c))
|
||||
|
||||
def ==>(op: Property[T, J]) =
|
||||
op <== prop
|
||||
|
||||
def observableChange[J1 >: J]: Observable[J1] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
|
||||
val canc = prop.onChange((a, b, c1) =>
|
||||
if (c1 != null && sub.onNext(c1) == Ack.Stop) c.cancel()
|
||||
)
|
||||
|
||||
c := Cancelable(() => canc.cancel())
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ObjectPropertyObservableListExt[A](
|
||||
private val prop: ObjectProperty[ObservableList[A]]
|
||||
) extends AnyVal {
|
||||
def <--(obs: Observable[Seq[A]])(implicit s: Scheduler) =
|
||||
obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
|
||||
|
||||
def -->(sub: Observer[A])(implicit s: Scheduler) = {
|
||||
val c = SingleAssignCancelable()
|
||||
val subs: Subscription = prop.onChange((a, b, c1) =>
|
||||
if (c1 != null)
|
||||
Iterant[Task]
|
||||
.fromIterable(c1.toIterable)
|
||||
.consume
|
||||
.use(consume(sub, c, _))
|
||||
.runToFuture
|
||||
)
|
||||
c := Cancelable(() => subs.cancel())
|
||||
}
|
||||
|
||||
private def loop(sub: Observer[A], it: Iterator[A]): Task[Unit] =
|
||||
if (it.hasNext) {
|
||||
val next = it.next()
|
||||
Task.deferFuture(sub.onNext(next)).flatMap {
|
||||
case Ack.Continue => loop(sub, it)
|
||||
case Ack.Stop => Task.unit
|
||||
}
|
||||
} else Task.unit
|
||||
|
||||
private def consume(
|
||||
sub: Observer[A],
|
||||
c: Cancelable,
|
||||
consumer: Iterant.Consumer[Task, A]
|
||||
): Task[Unit] =
|
||||
consumer.pull.flatMap {
|
||||
case Left(value) => Task.unit
|
||||
case Right(value) =>
|
||||
Task.deferFuture(sub.onNext(value)).flatMap {
|
||||
case Ack.Continue => consume(sub, c, consumer)
|
||||
case Ack.Stop => Task(c.cancel())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class ObjectPropertyActionEvent(
|
||||
private val prop: ObjectProperty[EventHandler[ActionEvent]]
|
||||
) extends AnyVal {
|
||||
// def <--(obs: Observable[ActionEvent])(implicit s: Scheduler) = {
|
||||
// obs.doOnNext(v => me.Task(prop() = ObservableBuffer.from(v))).subscribe()
|
||||
// }
|
||||
|
||||
// def -->(sub: Observer[ActionEvent]) =
|
||||
// prop().
|
||||
|
||||
}
|
||||
|
||||
// def emit(prop: ObjectProperty[EventHandler[ActionEvent]]) =
|
||||
|
||||
final class ButtonBaseExt(private val button: ButtonBase) extends AnyVal {
|
||||
def observableAction: Observable[jfxe.ActionEvent] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.DropNew(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val l = new jfxe.EventHandler[jfxe.ActionEvent] {
|
||||
override def handle(event: jfxe.ActionEvent): Unit =
|
||||
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
||||
}
|
||||
|
||||
button.onAction = l
|
||||
@ -111,4 +334,27 @@ object JavaFXMonixObservables {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class MenuItemExt(private val item: MenuItem) extends AnyVal {
|
||||
|
||||
def observableAction: Observable[jfxe.ActionEvent] = {
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
Observable.create(OverflowStrategy.DropNew(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val l = new jfxe.EventHandler[jfxe.ActionEvent] {
|
||||
override def handle(event: jfxe.ActionEvent): Unit =
|
||||
if (sub.onNext(event) == Ack.Stop) c.cancel()
|
||||
}
|
||||
|
||||
item.onAction = l
|
||||
c := Cancelable(() =>
|
||||
item.removeEventHandler(
|
||||
jfxe.ActionEvent.ACTION,
|
||||
l
|
||||
)
|
||||
)
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import akka.actor.typed.Props
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.util.Timeout
|
||||
import cats.Show
|
||||
import com.jayfella.jme.jfx.JavaFxUI
|
||||
import com.jme3.app.Application
|
||||
import com.jme3.app.SimpleApplication
|
||||
@ -61,7 +62,6 @@ 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
|
||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||
|
||||
final case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
|
||||
@ -87,7 +87,7 @@ final case class CollisionEvent(
|
||||
appliedImpulse: Function0[Float]
|
||||
)
|
||||
|
||||
package object implicits {
|
||||
package object implicits extends JavaFXMonixObservables {
|
||||
type PrePhysicsTickEvent = PhysicsTickEvent
|
||||
type PhysicsTickObservable =
|
||||
Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
|
||||
@ -102,6 +102,12 @@ package object implicits {
|
||||
}
|
||||
implicit final class StateManagerExt(private val asm: AppStateManager)
|
||||
extends AnyVal {
|
||||
@SuppressWarnings(
|
||||
Array(
|
||||
"org.wartremover.warts.AsInstanceOf",
|
||||
"org.wartremover.warts.Equals"
|
||||
)
|
||||
)
|
||||
def state[S <: AppState]()(implicit c: ClassTag[S]): S =
|
||||
asm.getState(c.runtimeClass.asInstanceOf[Class[S]])
|
||||
|
||||
@ -121,26 +127,6 @@ package object implicits {
|
||||
def viewPort = sa.getViewPort()
|
||||
def rootNode = sa.getRootNode()
|
||||
|
||||
def observableTick: Observable[Float] =
|
||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
val as = new MyBaseState {
|
||||
|
||||
override def init(): Unit = {}
|
||||
|
||||
override def update(tpf: Float) =
|
||||
if (sub.onNext(tpf) == Ack.Stop)
|
||||
c.cancel()
|
||||
|
||||
override def stop(): Unit = {}
|
||||
|
||||
override def onEnable() = {}
|
||||
override def onDisable() = {}
|
||||
}
|
||||
sa.stateManager.attach(as)
|
||||
c := Cancelable(() => sa.stateManager.detach(as))
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
implicit final class AssetManagerExt(private val am: AssetManager)
|
||||
@ -493,6 +479,7 @@ package object implicits {
|
||||
* @see [[ActionEvent]]
|
||||
* @see [[enumObservableAction]]
|
||||
*/
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def observableAction(mappingNames: String*): Observable[ActionEvent] =
|
||||
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
@ -537,6 +524,7 @@ package object implicits {
|
||||
* @see [[EnumActionEvent]]
|
||||
* @see [[enumAnalogObservable]]
|
||||
*/
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def enumObservableAction[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
): Observable[EnumActionEvent[T]] =
|
||||
@ -563,6 +551,7 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def enumEntryObservableAction[T <: EnumEntry](
|
||||
mappingEnumEntry: T
|
||||
): Observable[EnumActionEvent[T]] =
|
||||
@ -590,6 +579,7 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def analogObservable(mappingNames: String*): Observable[AnalogEvent] =
|
||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
@ -613,6 +603,7 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def enumAnalogObservable[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
): Observable[EnumAnalogEvent[T]] =
|
||||
@ -643,6 +634,7 @@ package object implicits {
|
||||
implicit final class PhysicsSpaceExt(private val space: jmeb.PhysicsSpace)
|
||||
extends AnyVal {
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def collisionObservable(): Observable[CollisionEvent] =
|
||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
@ -670,6 +662,7 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def prePhysicsTickObservable(): Observable[PrePhysicsTickEvent] =
|
||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
@ -699,6 +692,7 @@ package object implicits {
|
||||
c
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def physicsTickObservable(): Observable[PhysicsTickEvent] =
|
||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||
val c = SingleAssignCancelable()
|
||||
@ -905,4 +899,6 @@ package object implicits {
|
||||
) extends AnyVal {
|
||||
def toIO = coeval.to[Task].hideErrors.rethrow
|
||||
}
|
||||
|
||||
implicit val showForOsPath = Show.fromToString[os.Path]
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import scalafx.application.JFXApp.PrimaryStage
|
||||
import scalafx.scene.control.Button
|
||||
import scalafx.stage.StageStyle
|
||||
import wow.doge.mygame.executors.Schedulers.FxScheduler
|
||||
import wow.doge.mygame.implicits.JavaFXMonixObservables._
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.IOUtils._
|
||||
object Launcher {
|
||||
sealed trait LauncherResult
|
||||
@ -43,8 +43,7 @@ class Launcher private (props: Launcher.Props) {
|
||||
}
|
||||
|
||||
private lazy val launchAction =
|
||||
launchButton
|
||||
.observableAction()
|
||||
launchButton.observableAction
|
||||
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))
|
||||
|
||||
private lazy val exitButton = new Button {
|
||||
@ -52,8 +51,7 @@ class Launcher private (props: Launcher.Props) {
|
||||
}
|
||||
|
||||
private lazy val exitAction =
|
||||
exitButton
|
||||
.observableAction()
|
||||
exitButton.observableAction
|
||||
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit)))
|
||||
|
||||
private lazy val _scene =
|
||||
|
@ -6,7 +6,7 @@ import cats.syntax.eq._
|
||||
|
||||
import math.{abs, pow, sqrt}
|
||||
|
||||
case class ImVector3f(x: Float, y: Float, z: Float)
|
||||
final case class ImVector3f(x: Float, y: Float, z: Float)
|
||||
object ImVector3f {
|
||||
//format: off
|
||||
val Zero = ImVector3f(0, 0, 0)
|
||||
|
@ -11,6 +11,7 @@ import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.event.EventStream
|
||||
import akka.util.Timeout
|
||||
import cats.syntax.show._
|
||||
import monix.bio.UIO
|
||||
import monix.execution.Ack
|
||||
import monix.execution.Cancelable
|
||||
@ -58,6 +59,7 @@ object EventBus {
|
||||
new EventBus().eventStreamBehavior(eventStream)
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def observable[A, B <: A](eventBus: ActorRef[EventBus.Command[A]])(implicit
|
||||
timeout: Timeout,
|
||||
scheduler: Scheduler,
|
||||
@ -70,7 +72,7 @@ object EventBus {
|
||||
val c = SingleAssignCancelable()
|
||||
val behavior = Behaviors
|
||||
.receive[B] { (ctx, msg) =>
|
||||
ctx.log.traceP(s"Emitted $msg")
|
||||
ctx.log.traceP(s"Emitted ${msg.toString}")
|
||||
if (sub.onNext(msg) == Ack.Stop) {
|
||||
c.cancel()
|
||||
Behaviors.stopped
|
||||
@ -86,7 +88,7 @@ object EventBus {
|
||||
.spawnActorL(
|
||||
behavior,
|
||||
actorName =
|
||||
Some(s"eventBusObservable-${ct.toString.split("""\.""").last}")
|
||||
Some(show"eventBusObservable-${ct.toString.split("""\.""").last}")
|
||||
)
|
||||
.mapError(err => new Throwable(err.toString))
|
||||
.tapError {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package wow.doge.mygame.subsystems.events
|
||||
|
||||
import cats.Show
|
||||
import wow.doge.mygame.game.entities.CharacterStats
|
||||
|
||||
sealed trait Event
|
||||
@ -27,9 +28,11 @@ object EntityMovementEvent {
|
||||
|
||||
sealed trait StatsEvent extends Event
|
||||
object StatsEvent {
|
||||
case class DamageEvent(
|
||||
final case class DamageEvent(
|
||||
hitBy: String,
|
||||
victimName: String,
|
||||
amount: CharacterStats.DamageHealth
|
||||
) extends StatsEvent
|
||||
|
||||
implicit val show = Show.fromToString[StatsEvent]
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
package wow.doge.mygame.subsystems.events
|
@ -1,5 +1,7 @@
|
||||
package wow.doge.mygame.subsystems.events
|
||||
|
||||
import cats.Show
|
||||
|
||||
sealed trait PlayerEvent
|
||||
|
||||
sealed trait PlayerMovementEvent extends PlayerEvent
|
||||
@ -14,6 +16,7 @@ object PlayerMovementEvent {
|
||||
case object PlayerJumped extends PlayerMovementEvent
|
||||
// case object PlayerTurnedRight extends PlayerMovementEvent
|
||||
// case object PlayerTurnedLeft extends PlayerMovementEvent
|
||||
implicit val show = Show.fromToString[PlayerMovementEvent]
|
||||
}
|
||||
|
||||
sealed trait PlayerCameraEvent extends PlayerEvent
|
||||
|
151
src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala
Normal file → Executable file
151
src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala
Normal file → Executable file
@ -16,10 +16,10 @@ import monix.bio.IO
|
||||
import monix.bio.UIO
|
||||
import monix.reactive.Consumer
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.readAsyncL
|
||||
import wow.doge.mygame.utils.IOUtils
|
||||
|
||||
import IOUtils.toIO
|
||||
|
||||
@JsonCodec
|
||||
final case class Test1(hello1: String, hello2: String)
|
||||
@JsonCodec
|
||||
@ -32,7 +32,7 @@ object Plugin {
|
||||
}
|
||||
|
||||
object ModdingSystem {
|
||||
sealed trait Error
|
||||
sealed trait Error extends Product with Serializable
|
||||
final case class CouldNotDecode(cause: String) extends Error
|
||||
final case class ParseFailure(cause: io.circe.ParsingFailure) extends Error
|
||||
final case class DecodingFailure(cause: io.circe.DecodingFailure)
|
||||
@ -43,125 +43,52 @@ object ModdingSystem {
|
||||
implicit val eq = Eq.fromUniversalEquals[Error]
|
||||
}
|
||||
|
||||
val x: IO[Error, String] =
|
||||
IOUtils.liftErrors(readAsyncL(os.pwd / "plugins.json")) {
|
||||
case _: FileNotFoundException =>
|
||||
IO.raiseError(FileNotFound(os.pwd / "plugins.json"))
|
||||
case _: NoSuchFileException =>
|
||||
IO.raiseError(FileNotFound(os.pwd / "plugins.json"))
|
||||
}
|
||||
|
||||
def readPluginsList(dir: os.Path): IO[Error, ArraySeq[Plugin]] =
|
||||
IO(parse(os.read(dir / "plugins.json")))
|
||||
readAsyncL(dir / "plugins.json")
|
||||
.onErrorHandleWith {
|
||||
case _: FileNotFoundException =>
|
||||
IO.raiseError(FileNotFound(dir / "plugins.json"))
|
||||
case _: NoSuchFileException =>
|
||||
IO.raiseError(FileNotFound(dir / "plugins.json"))
|
||||
case other => IO.terminate(other)
|
||||
}
|
||||
.flatMap(files =>
|
||||
IO.fromEither(files)
|
||||
.map(_.as[ArraySeq[Plugin]])
|
||||
IO.fromEither(parse(files))
|
||||
.mapError(ParseFailure)
|
||||
.map(_.as[ArraySeq[Plugin]])
|
||||
)
|
||||
.flatMap(result => IO.fromEither(result).mapError(DecodingFailure))
|
||||
|
||||
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]
|
||||
): UIO[(View[(Plugin, Error)], View[(Plugin, String)])] =
|
||||
UIO(
|
||||
plugins
|
||||
.sortBy(_.priority)
|
||||
.view
|
||||
.map { p =>
|
||||
val path = dir / os.RelPath(p.name + ".plugin.json")
|
||||
p ->
|
||||
Either
|
||||
.catchNonFatal(os.read(path))
|
||||
.leftMap {
|
||||
case _: FileNotFoundException => FileNotFound(path)
|
||||
case _: NoSuchFileException => FileNotFound(path)
|
||||
}
|
||||
|
||||
}
|
||||
.partitionMap {
|
||||
case (p, either) =>
|
||||
either match {
|
||||
case Left(value) => Left(p -> value)
|
||||
case Right(value) => Right(p -> value)
|
||||
}
|
||||
}
|
||||
)
|
||||
// : (View[(Plugin, Error)], View[(Plugin, String)])
|
||||
def findAndReadPluginFiles2(
|
||||
dir: os.Path,
|
||||
plugins: ArraySeq[Plugin]
|
||||
) =
|
||||
// IO.parTraverse(plugins.sortBy(_.priority))(p =>
|
||||
// IO {
|
||||
// val path = dir / os.RelPath(p.name + ".plugin.json")
|
||||
// os.read(path)
|
||||
// }
|
||||
// .onErrorHandleWith {
|
||||
// case _: FileNotFoundException =>
|
||||
// IO.raiseError(FileNotFound(dir.toString))
|
||||
// case _: NoSuchFileException =>
|
||||
// IO.raiseError(FileNotFound(dir.toString))
|
||||
// }
|
||||
// .flatMap(r => UIO(p -> r))
|
||||
// ).map {
|
||||
// _.partitionMap {
|
||||
// case (p, either) =>
|
||||
// either match {
|
||||
// case Left(value) => Left(p -> value)
|
||||
// case Right(value) => Right(p -> value)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
plugins
|
||||
.sortBy(_.priority)
|
||||
.view
|
||||
.map { p =>
|
||||
val path = dir / os.RelPath(p.name + ".plugin.json")
|
||||
p -> IO(os.read(path))
|
||||
IO
|
||||
.parTraverse(plugins.sortBy(_.priority))(p =>
|
||||
readAsyncL(dir / os.RelPath(p.name + ".plugin.json"))
|
||||
.onErrorHandleWith {
|
||||
case _: FileNotFoundException => IO.raiseError(FileNotFound(path))
|
||||
case _: NoSuchFileException => IO.raiseError(FileNotFound(path))
|
||||
case _: FileNotFoundException =>
|
||||
IO.raiseError(FileNotFound(dir))
|
||||
case _: NoSuchFileException =>
|
||||
IO.raiseError(FileNotFound(dir))
|
||||
case other => IO.terminate(other)
|
||||
}
|
||||
// .map(r => p -> r)
|
||||
.attempt
|
||||
.map(r => p -> r)
|
||||
)
|
||||
.map(_.partitionMap {
|
||||
case p -> Left(value) => Left(p -> value)
|
||||
case p -> Right(value) => Right(p -> value)
|
||||
})
|
||||
|
||||
}
|
||||
.map {
|
||||
case (p, io) =>
|
||||
io.attempt.map {
|
||||
case Left(value) => Left(p -> value)
|
||||
case Right(value) => Right(p -> value)
|
||||
}
|
||||
}
|
||||
.to(List)
|
||||
.parSequence
|
||||
|
||||
// .partitionMap {
|
||||
// _.map {
|
||||
// case l @ Left(value) => l
|
||||
// case r @ Right(value) => r
|
||||
// }
|
||||
// }
|
||||
// .sequence
|
||||
|
||||
// def readPluginFiles(filePaths: View[os.Path]) =
|
||||
// filePaths.map(path => os.read(path))
|
||||
|
||||
// def readPluginFiles2(filePaths: View[os.Path]) =
|
||||
// filePaths
|
||||
// .map(path =>
|
||||
// IO(os.read(path)).onErrorHandleWith {
|
||||
// case _: FileNotFoundException =>
|
||||
// IO.raiseError(FileNotFound(path))
|
||||
// case _: NoSuchFileException =>
|
||||
// IO.raiseError(FileNotFound(path))
|
||||
// }
|
||||
// )
|
||||
// .to(List)
|
||||
// .parSequence
|
||||
def parsePluginFiles(files: View[(Plugin, String)]) =
|
||||
files
|
||||
.map {
|
||||
@ -191,18 +118,18 @@ object ModdingSystem {
|
||||
for {
|
||||
plugins <- readPluginsList(wd)
|
||||
(readFailures, readSuccesses) <- findAndReadPluginFiles(wd, plugins)
|
||||
(parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
|
||||
(parseFailures, parseSuccesses) = parsePluginFiles(readSuccesses.view)
|
||||
res <- UIO.parMap5(
|
||||
UIO(readFailures.to(List)),
|
||||
UIO(readSuccesses.to(List)),
|
||||
UIO.pure(readSuccesses),
|
||||
UIO(parseFailures.to(List)),
|
||||
UIO(parseSuccesses.to(List)),
|
||||
toIO(
|
||||
Observable
|
||||
.fromIterable(parseSuccesses)
|
||||
.map { case (p, json) => json }
|
||||
.consumeWith(loadBalancedPluginDataMerger)
|
||||
).hideErrors
|
||||
Observable
|
||||
.fromIterable(parseSuccesses)
|
||||
.map { case (p, json) => json }
|
||||
.consumeWith(loadBalancedPluginDataMerger)
|
||||
.toIO
|
||||
.hideErrors
|
||||
)(Result.apply)
|
||||
} yield res
|
||||
|
||||
@ -215,7 +142,7 @@ object ModdingSystem {
|
||||
pprint.log(show"Merged = ${res.pluginJson}")
|
||||
}
|
||||
|
||||
case class Result(
|
||||
final case class Result(
|
||||
readFailures: List[(Plugin, Error)],
|
||||
readSuccesses: List[(Plugin, String)],
|
||||
parseFailures: List[(Plugin, ParsingFailure)],
|
||||
|
@ -10,28 +10,33 @@ import ammonite.main.Defaults
|
||||
import ammonite.runtime.Storage.Folder
|
||||
import ammonite.util.Res
|
||||
import ammonite.util.Res.Failure
|
||||
import cats.Show
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import cats.syntax.either._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.show._
|
||||
import com.softwaremill.macwire._
|
||||
import com.softwaremill.tagging._
|
||||
import groovy.util.GroovyScriptEngine
|
||||
import groovy.util.ResourceException
|
||||
import io.odin.Logger
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.catnap.ConcurrentQueue
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.implicits._
|
||||
import monix.{eval => me}
|
||||
import groovy.util.ResourceException
|
||||
import monix.catnap.MVar
|
||||
import monix.reactive.Observable
|
||||
import monix.{eval => me}
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.readAsyncL
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
|
||||
trait Requestable[A] {
|
||||
|
||||
protected def queue: ConcurrentQueue[Task, A]
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.OptionPartial"))
|
||||
def request[T](
|
||||
compileRequest: Deferred[Task, T] => A
|
||||
)(implicit timeout: FiniteDuration) =
|
||||
@ -41,6 +46,19 @@ trait Requestable[A] {
|
||||
_ <- queue.offer(req)
|
||||
res <- d.get.timeout(timeout).map(_.get)
|
||||
} yield res
|
||||
|
||||
// def request[T](
|
||||
// compileRequest: Deferred[Task, T] => A
|
||||
// )(implicit timeout: FiniteDuration): IO[AppError, T] =
|
||||
// for {
|
||||
// d <- Deferred[Task, T].hideErrors
|
||||
// req = compileRequest(d)
|
||||
// _ <- queue.offer(req).hideErrors
|
||||
// res <-
|
||||
// d.get.hideErrors
|
||||
// .timeout(timeout)
|
||||
// .flatMap(o => IO.fromOption(o, AppError.TimeoutError("Timed out")))
|
||||
// } yield res
|
||||
}
|
||||
|
||||
class ScriptCompiler private (
|
||||
@ -52,6 +70,7 @@ class ScriptCompiler private (
|
||||
// def tell(item: Command) = queue.offer(item)
|
||||
|
||||
}
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Any"))
|
||||
object ScriptCompiler {
|
||||
|
||||
sealed trait State
|
||||
@ -67,7 +86,7 @@ object ScriptCompiler {
|
||||
sealed trait KotlinEngineTag
|
||||
type KotlinScriptEngine = ScriptEngine @@ KotlinEngineTag
|
||||
|
||||
sealed trait Error
|
||||
sealed trait Error extends Product with Serializable
|
||||
final case class AmmoniteFailure(error: Res.Failure) extends Error
|
||||
final case class AmmoniteException(error: Res.Exception) extends Error
|
||||
final case class ScriptExceptionError(error: ScriptException) extends Error
|
||||
@ -79,15 +98,14 @@ object ScriptCompiler {
|
||||
final case class SomeError(reason: String) extends Error
|
||||
|
||||
sealed trait Command
|
||||
final case class Get(
|
||||
final case class GetScript(
|
||||
path: os.Path,
|
||||
result: Deferred[Task, ScriptResult],
|
||||
force: Boolean
|
||||
) extends Command
|
||||
final case class GetData(result: Deferred[Task, Data])
|
||||
// final case class GetData(result: Deferred[Task, Data])
|
||||
final case class ObservableData(result: Deferred[Task, Observable[Data]])
|
||||
extends Command
|
||||
// extends Command
|
||||
// final case class CompileAll(paths: Seq[os.Path]) extends Command
|
||||
|
||||
type ScriptsMap = Map[os.Path, Any]
|
||||
@ -117,7 +135,7 @@ object ScriptCompiler {
|
||||
val defaultGroovyRunner: GroovyScriptEngine =
|
||||
new GroovyScriptEngine(os.pwd.toString)
|
||||
|
||||
case class Data(scriptsMap: ScriptsMap)
|
||||
final case class Data(scriptsMap: ScriptsMap)
|
||||
|
||||
class SourceMaker(
|
||||
queue: ConcurrentQueue[Task, Command],
|
||||
@ -137,14 +155,12 @@ object ScriptCompiler {
|
||||
case Idle => IO.pure(Idle -> data)
|
||||
case Active =>
|
||||
command match {
|
||||
case Get(path, result, force) =>
|
||||
case GetScript(path, result, force) =>
|
||||
def getAndUpdate =
|
||||
worker
|
||||
.request(
|
||||
ScriptCompilerWorker.CompileAny(path, _)
|
||||
)(
|
||||
20.seconds
|
||||
)
|
||||
)(20.seconds)
|
||||
.flatTap(result.complete)
|
||||
.hideErrors
|
||||
.rethrow
|
||||
@ -207,6 +223,24 @@ object ScriptCompiler {
|
||||
case _ => Left(SomeError("Failed to run script"))
|
||||
}
|
||||
|
||||
def runScala2(path: os.Path): IO[Error, Any] =
|
||||
for {
|
||||
scriptContent <- readAsyncL(path).hideErrors
|
||||
res <- IO.fromEither(
|
||||
scalaRunner
|
||||
.runCode(scriptContent)
|
||||
._1 match {
|
||||
case e @ Res.Exception(t, msg) => Left(AmmoniteException(e))
|
||||
|
||||
case f @ Failure(msg) => Left(AmmoniteFailure(f))
|
||||
|
||||
case Res.Success(obj) => Right(obj)
|
||||
|
||||
case _ => Left(SomeError("Failed to run script"))
|
||||
}
|
||||
)
|
||||
} yield res
|
||||
|
||||
def runKotlin(path: os.Path): Either[Error, Any] =
|
||||
Either
|
||||
.catchNonFatal(kotlinRunner.eval(os.read(path)))
|
||||
@ -214,6 +248,8 @@ object ScriptCompiler {
|
||||
case ex: ScriptException => ScriptExceptionError(ex)
|
||||
}
|
||||
|
||||
// def runKotlin2(path: os.path): IO[Error,]
|
||||
|
||||
def runGroovy(path: os.Path): Either[Error, Any] =
|
||||
Either
|
||||
.catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString, ""))
|
||||
@ -229,31 +265,31 @@ object ScriptCompiler {
|
||||
class ScriptCompileSource(
|
||||
fns: ScriptCompileFns,
|
||||
logger: Logger[Task],
|
||||
queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest]
|
||||
queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest],
|
||||
ioScheduler: Schedulers.IoScheduler
|
||||
) {
|
||||
import fns._
|
||||
|
||||
val source =
|
||||
Task.deferAction(implicit s =>
|
||||
Task(
|
||||
Observable
|
||||
.repeatEvalF(queue.poll)
|
||||
.doOnNextF(el => logger.debug(s"Got $el"))
|
||||
.doOnNextF(el => logger.debug(show"Got $el"))
|
||||
.mapParallelUnorderedF(4) {
|
||||
case ScriptCompilerWorker.CompileAny(path, result) =>
|
||||
for {
|
||||
mbRes <- Task(
|
||||
runScala(path)
|
||||
.flatMap(ensureReturnedObjectNotNull)
|
||||
// .map(_.taggedWith[ScriptTag])
|
||||
)
|
||||
mbRes <- (logger.debugU("Test") >> Task(
|
||||
fns
|
||||
.runScala(path)
|
||||
.flatMap(fns.ensureReturnedObjectNotNull)
|
||||
// .map(_.taggedWith[ScriptTag])
|
||||
)).executeOn(ioScheduler.value)
|
||||
_ <- result.complete(mbRes)
|
||||
} yield mbRes
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
// override private val
|
||||
final class ScriptCompilerWorker(
|
||||
logger: Logger[Task],
|
||||
_queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest]
|
||||
@ -266,6 +302,10 @@ object ScriptCompiler {
|
||||
object ScriptCompilerWorker {
|
||||
|
||||
sealed trait CompileRequest
|
||||
|
||||
object CompileRequest {
|
||||
implicit val show: Show[CompileRequest] = Show.fromToString
|
||||
}
|
||||
final case class CompileAny(
|
||||
path: os.Path,
|
||||
result: Deferred[Task, ScriptResult]
|
||||
@ -275,7 +315,8 @@ object ScriptCompiler {
|
||||
logger: Logger[Task],
|
||||
scalaRunner: ammonite.Main = defaultScalaRunner,
|
||||
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner,
|
||||
groovyRunner: GroovyScriptEngine = defaultGroovyRunner
|
||||
groovyRunner: GroovyScriptEngine = defaultGroovyRunner,
|
||||
ioScheduler: Schedulers.IoScheduler
|
||||
) = {
|
||||
val acquire = for {
|
||||
queue <- ConcurrentQueue[Task].bounded[CompileRequest](10)
|
||||
@ -287,22 +328,27 @@ object ScriptCompiler {
|
||||
// )
|
||||
} yield worker -> fib
|
||||
Resource
|
||||
.make(acquire) { case worker -> fib => fib.cancel }
|
||||
.make(acquire.hideErrors) { case worker -> fib => fib.cancel }
|
||||
.map(_._1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def apply(logger: Logger[Task]) = {
|
||||
def apply(
|
||||
logger: Logger[Task],
|
||||
ioScheduler: Schedulers.IoScheduler
|
||||
): Resource[UIO, ScriptCompiler] = {
|
||||
def acquire(worker: ScriptCompilerWorker) =
|
||||
for {
|
||||
queue <- ConcurrentQueue.bounded[Task, Command](10)
|
||||
fib <- wire[SourceMaker].get.flatMap(_.completedL.toIO.start)
|
||||
} yield new ScriptCompiler(queue) -> fib
|
||||
|
||||
ScriptCompilerWorker(logger)
|
||||
ScriptCompilerWorker(logger, ioScheduler = ioScheduler)
|
||||
.flatMap(worker =>
|
||||
Resource.make(acquire(worker)) { case (_, fib) => fib.cancel }
|
||||
Resource.make(acquire(worker).hideErrors) {
|
||||
case (_, fib) => fib.cancel
|
||||
}
|
||||
)
|
||||
.map(_._1)
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ import com.softwaremill.tagging._
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import groovy.util.GroovyScriptEngine
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Any"))
|
||||
object ScriptActor {
|
||||
|
||||
/**
|
||||
@ -126,6 +128,7 @@ object ScriptActor {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Any"))
|
||||
class ScriptActor(
|
||||
val scalaRunner: ammonite.Main,
|
||||
val kotlinRunner: ScriptActor.KotlinScriptEngine,
|
||||
@ -137,14 +140,14 @@ class ScriptActor(
|
||||
def receiveMessage: Behavior[Command] =
|
||||
Behaviors.receiveMessage {
|
||||
case CompileAny(path, requester) =>
|
||||
context.log.debug(s"Received $path")
|
||||
context.log.debug(show"Received $path")
|
||||
val res = getScript(path)
|
||||
context.log.debug(s"result = $res")
|
||||
context.log.debug(s"result = ${res.toString}")
|
||||
requester ! res
|
||||
Behaviors.same
|
||||
|
||||
case CompileAll(paths, requester) =>
|
||||
context.log.debug(s"Received $paths")
|
||||
context.log.debug(show"Received $paths")
|
||||
requester ! compileAll(paths)
|
||||
Behaviors.same
|
||||
}
|
||||
@ -166,7 +169,7 @@ class ScriptActor(
|
||||
case l @ Left(err) => l.map(_.taggedWith[ScriptTag])
|
||||
}
|
||||
|
||||
type LOL = Map[os.Path, Either[wow.doge.mygame.state.ScriptActor.Error, Any]]
|
||||
type LOL = Map[os.Path, Either[ScriptActor.Error, Any]]
|
||||
|
||||
def compileAll(
|
||||
paths: Seq[os.Path]
|
||||
@ -176,7 +179,7 @@ class ScriptActor(
|
||||
paths: Seq[os.Path],
|
||||
scriptsMap: Map[
|
||||
os.Path,
|
||||
Either[wow.doge.mygame.state.ScriptActor.Error, Any]
|
||||
Either[ScriptActor.Error, Any]
|
||||
]
|
||||
): LOL =
|
||||
paths match {
|
||||
|
@ -13,6 +13,7 @@ import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.scaladsl.PoolRouter
|
||||
import akka.actor.typed.scaladsl.Routers
|
||||
import akka.util.Timeout
|
||||
import cats.syntax.show._
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.implicits._
|
||||
@ -25,7 +26,7 @@ object ScriptCachingActor {
|
||||
type ScriptsMap = Map[os.Path, ScriptObject]
|
||||
type ScriptResult = Either[ScriptActor.Error, ScriptObject]
|
||||
|
||||
sealed trait Command
|
||||
sealed trait Command extends Product with Serializable
|
||||
|
||||
/**
|
||||
* @param scriptPath path of the script to compile
|
||||
@ -187,7 +188,7 @@ class ScriptCachingActor(
|
||||
Behaviors.same
|
||||
|
||||
case Put(scriptPath, script) =>
|
||||
ctx.log.debugP(s"Putting $script at path $scriptPath")
|
||||
ctx.log.debugP(show"Putting ${script.toString} at path $scriptPath")
|
||||
val newState =
|
||||
state.modify(_.scriptsMap).using(_ + (scriptPath -> script))
|
||||
ctx.log.traceP(newState.toString())
|
||||
|
@ -4,9 +4,14 @@ import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import cats.syntax.eq._
|
||||
import io.odin.Logger
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.scriptsystem.ScriptCachingActor
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
|
||||
/**
|
||||
* Scripts can either be searched and compiled at startup (Eager mode)
|
||||
@ -19,7 +24,9 @@ object ScriptInitMode {
|
||||
}
|
||||
class ScriptSystemResource(
|
||||
path: os.Path,
|
||||
mode: ScriptInitMode = ScriptInitMode.Lazy
|
||||
logger: Logger[Task],
|
||||
mode: ScriptInitMode = ScriptInitMode.Lazy,
|
||||
ioScheduler: Schedulers.IoScheduler
|
||||
)(implicit
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
timeout: Timeout,
|
||||
@ -36,13 +43,17 @@ class ScriptSystemResource(
|
||||
)
|
||||
} yield scriptCacheActor
|
||||
|
||||
val init2: Resource[UIO, ScriptCompiler] = for {
|
||||
sc <- ScriptCompiler(logger, ioScheduler)
|
||||
} yield sc
|
||||
|
||||
def findScriptFiles(wd: os.Path) =
|
||||
os.walk
|
||||
.stream(wd)
|
||||
.filter(p =>
|
||||
os.isFile(p) &&
|
||||
(p.ext == "sc" || (p.baseName + "." + p.ext)
|
||||
.contains(".main.kts") || p.ext == "groovy")
|
||||
(p.ext === "sc" || (p.baseName + "." + p.ext)
|
||||
.contains(".main.kts") || p.ext === "groovy")
|
||||
)
|
||||
.toList
|
||||
|
||||
|
@ -10,6 +10,6 @@ import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
||||
package object types {
|
||||
type RootNode = AppNode2 @@ GameAppTags.RootNode
|
||||
type GuiNode = AppNode2 @@ GameAppTags.GuiNode
|
||||
@newtype case class JmeScheduler(value: SchedulerService)
|
||||
@newtype case class AkkaScheduler(value: Scheduler)
|
||||
@newtype final case class JmeScheduler(value: SchedulerService)
|
||||
@newtype final case class AkkaScheduler(value: Scheduler)
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ object GenericConsoleStream {
|
||||
/**
|
||||
* for future use
|
||||
*/
|
||||
case class Config(exclusive: Boolean = false)
|
||||
final case class Config(exclusive: Boolean = false)
|
||||
object Config {
|
||||
val default = Config()
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.scaladsl.TimerScheduler
|
||||
import cats.syntax.show._
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
object GenericTimerActor {
|
||||
@ -15,9 +16,9 @@ object GenericTimerActor {
|
||||
case object Start extends Command
|
||||
case object Stop extends Command
|
||||
private case object Tick extends Command
|
||||
case class TimerKey(seed: Long)
|
||||
final case class TimerKey(seed: Long)
|
||||
|
||||
case class Props[T](
|
||||
final case class Props[T](
|
||||
target: ActorRef[T],
|
||||
messageToSend: T,
|
||||
timeInterval: FiniteDuration
|
||||
@ -55,7 +56,7 @@ class GenericTimerActor[T](
|
||||
val active: Behavior[Command] =
|
||||
Behaviors.receiveMessage {
|
||||
case Start =>
|
||||
ctx.log.warnP(s"Timer-${timerKey.seed}: Timer already started")
|
||||
ctx.log.warnP(show"Timer-${timerKey.seed}: Timer already started")
|
||||
Behaviors.same
|
||||
case Tick =>
|
||||
props.target ! props.messageToSend
|
||||
|
@ -14,4 +14,15 @@ object IOUtils {
|
||||
def fromCoevalEither[L, R](coeval: Coeval[Either[L, R]]) =
|
||||
coeval.to[Task].hideErrors.rethrow
|
||||
|
||||
def liftErrors[E, T](
|
||||
task: Task[T]
|
||||
)(pf: PartialFunction[Throwable, IO[E, T]]): IO[E, T] = {
|
||||
val _ = 1
|
||||
// val x = task.onErrorRecoverWith()
|
||||
task.onErrorHandleWith(ex => pf.applyOrElse(ex, IO.terminate))
|
||||
}
|
||||
|
||||
// final def mapErrorPartial[E1, B >: A](pf: PartialFunction[E, IO[E1, B]])(implicit ev: E <:< Throwable): IO[E1, B] =
|
||||
// onErrorHandleWith(ex => pf.applyOrElse(ex, IO.terminate))
|
||||
|
||||
}
|
||||
|
@ -1,33 +1,62 @@
|
||||
package wow.doge.mygame.utils
|
||||
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.OverflowStrategy
|
||||
import cats.kernel.Eq
|
||||
import cats.syntax.show._
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import monix.bio.Task
|
||||
import monix.execution.Ack
|
||||
import monix.execution.Cancelable
|
||||
import monix.execution.cancelables.SingleAssignCancelable
|
||||
import monix.execution.Ack
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.OverflowStrategy
|
||||
|
||||
object MonixDirectoryWatcher {
|
||||
import better.files._
|
||||
import io.methvin.better.files._
|
||||
|
||||
private val logger = Logger[MonixDirectoryWatcher.type]
|
||||
|
||||
sealed trait WatchEvent extends Product with Serializable
|
||||
final case class CreateEvent(file: File, count: Int) extends WatchEvent
|
||||
final case class ModifyEvent(file: File, count: Int) extends WatchEvent
|
||||
final case class DeleteEvent(file: File, count: Int) extends WatchEvent
|
||||
object WatchEvent {
|
||||
implicit val eq = Eq.fromUniversalEquals[WatchEvent]
|
||||
|
||||
}
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||
def apply(path: os.Path) =
|
||||
Observable.create[String](OverflowStrategy.DropNew(50)) { sub =>
|
||||
import sub.scheduler
|
||||
Task.deferAction(implicit s =>
|
||||
Task(
|
||||
Observable
|
||||
.create[WatchEvent](OverflowStrategy.DropNew(50)) { sub =>
|
||||
import sub.scheduler
|
||||
|
||||
val c = SingleAssignCancelable()
|
||||
val c = SingleAssignCancelable()
|
||||
|
||||
val myDir = File(path.toString)
|
||||
val watcher = new RecursiveFileMonitor(myDir) {
|
||||
override def onCreate(file: File, count: Int) =
|
||||
println(s"$file got created")
|
||||
override def onModify(file: File, count: Int) =
|
||||
// println(s"$file got modified $count times")
|
||||
if (sub.onNext(file.name) == Ack.Stop) c.cancel()
|
||||
override def onDelete(file: File, count: Int) =
|
||||
println(s"$file got deleted")
|
||||
}
|
||||
watcher.start()(scheduler)
|
||||
c := Cancelable(() => watcher.stop())
|
||||
c
|
||||
val watcher =
|
||||
new RecursiveFileMonitor(
|
||||
File(path.toString),
|
||||
logger = logger.underlying
|
||||
) {
|
||||
override def onCreate(file: File, count: Int) =
|
||||
if (sub.onNext(CreateEvent(file, count)) == Ack.Stop)
|
||||
c.cancel()
|
||||
|
||||
}
|
||||
override def onModify(file: File, count: Int) =
|
||||
if (sub.onNext(ModifyEvent(file, count)) == Ack.Stop)
|
||||
c.cancel()
|
||||
|
||||
override def onDelete(file: File, count: Int) =
|
||||
if (sub.onNext(DeleteEvent(file, count)) == Ack.Stop)
|
||||
c.cancel()
|
||||
}
|
||||
watcher.start()(scheduler)
|
||||
c := Cancelable(() => watcher.stop())
|
||||
c
|
||||
}
|
||||
.publish
|
||||
.refCount
|
||||
)
|
||||
)
|
||||
}
|
||||
|
18
src/main/scala/wow/doge/mygame/utils/MovementDirection.scala
Normal file
18
src/main/scala/wow/doge/mygame/utils/MovementDirection.scala
Normal file
@ -0,0 +1,18 @@
|
||||
package wow.doge.mygame.utils
|
||||
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import enumeratum._
|
||||
|
||||
sealed trait MovementDirection extends EnumEntry
|
||||
|
||||
object MovementDirection extends Enum[MovementDirection] {
|
||||
val values = findValues
|
||||
case object Forward extends MovementDirection
|
||||
case object Backward extends MovementDirection
|
||||
case object Left extends MovementDirection
|
||||
case object Right extends MovementDirection
|
||||
|
||||
implicit val eq = Eq.fromUniversalEquals[MovementDirection]
|
||||
implicit val show = Show.fromToString[MovementDirection]
|
||||
}
|
@ -15,7 +15,7 @@ object ReaderDemo {
|
||||
val t2 = r.run("s").rethrow
|
||||
// Kleisli[IO, String, Unit](s => IO.unit)
|
||||
|
||||
case class Environment(str: String, num: Int)
|
||||
final case class Environment(str: String, num: Int)
|
||||
|
||||
def fun1: Reader[String, UIO[Unit]] = Reader(str => UIO(println(str)))
|
||||
def fun2: Reader[Int, UIO[Unit]] = Reader(num => UIO(println(num)))
|
||||
|
@ -1,6 +1,6 @@
|
||||
package wow.doge.mygame.utils
|
||||
|
||||
case class Display(
|
||||
final case class Display(
|
||||
width: Int,
|
||||
height: Int,
|
||||
title: String,
|
||||
@ -18,4 +18,4 @@ object Display {
|
||||
frameRate = -1
|
||||
)
|
||||
}
|
||||
case class GlobalSettings(display: Display = Display.default)
|
||||
final case class GlobalSettings(display: Display = Display.default)
|
||||
|
@ -6,16 +6,15 @@ import monix.execution.atomic.AtomicAny
|
||||
* Useless
|
||||
*/
|
||||
sealed abstract class Tree[+T]
|
||||
case class Node[T](data: T, children: AtomicAny[LazyList[Tree[T]]])
|
||||
final case class Node[T](data: T, children: AtomicAny[LazyList[Tree[T]]])
|
||||
extends Tree[T] {
|
||||
def add(data: T) = {
|
||||
def add(data: T) =
|
||||
children.transform(children =>
|
||||
Node(data, AtomicAny(LazyList[Tree[T]]())) #:: children
|
||||
)
|
||||
}
|
||||
}
|
||||
// case object Leaf extends Tree[Nothing]
|
||||
case class Data(data: Int)
|
||||
final case class Data(data: Int)
|
||||
|
||||
class TreeManager[T] {
|
||||
// val root: AtomicAny[Tree[T]] = AtomicAny(Leaf)
|
||||
|
@ -0,0 +1,67 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import monix.bio.Task
|
||||
import monix.execution.Cancelable
|
||||
import monix.execution.Scheduler
|
||||
import monix.execution.cancelables.CompositeCancelable
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.Observer
|
||||
import monix.{eval => me}
|
||||
|
||||
final class ActionObservableExecutor[T](private val delegate: Observable[T]) {
|
||||
//format: off
|
||||
def -->(sub: Observer[T])(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
||||
//format: on
|
||||
c += delegate
|
||||
.doOnNext(el => me.Task.deferFuture(sub.onNext(el)).void)
|
||||
.subscribe()
|
||||
|
||||
//format: off
|
||||
def -->(f: T => Task[Unit])(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
||||
//format: on
|
||||
c += delegate.doOnNextF(f).subscribe()
|
||||
|
||||
//format: off
|
||||
def split(lst: (ActionObservableBuilder[T] => Cancelable)*)(implicit s: Scheduler, c: CompositeCancelable): Unit =
|
||||
//format: on
|
||||
c += delegate
|
||||
.publishSelector(conn =>
|
||||
Observable(
|
||||
lst.map(f =>
|
||||
Observable.unit.doOnNext(_ =>
|
||||
me.Task(c += f(new ActionObservableBuilder[T](conn))).void
|
||||
)
|
||||
): _*
|
||||
).merge
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
}
|
||||
|
||||
//format: off
|
||||
final class ActionObservableBuilder[A](private val observableAction: Observable[A]) {
|
||||
//format: on
|
||||
def useEval[T](v: me.Task[T]) =
|
||||
new ActionObservableExecutor[T](observableAction.mapEval(_ => v))
|
||||
|
||||
def useEval[T](cb: A => me.Task[T]) =
|
||||
new ActionObservableExecutor[T](observableAction.mapEval(cb))
|
||||
|
||||
def use = new ActionObservableExecutor(observableAction)
|
||||
|
||||
def useIterableEval[T](cb: A => collection.immutable.Iterable[T]) =
|
||||
new ActionObservableExecutor[T](
|
||||
observableAction.flatMap(a =>
|
||||
Observable.suspend(Observable.fromIterable(cb(a)))
|
||||
)
|
||||
)
|
||||
|
||||
def doOnNext(cb: A => me.Task[Unit]): ActionObservableBuilder[A] =
|
||||
new ActionObservableBuilder(observableAction.doOnNext(cb))
|
||||
|
||||
def mapEval[B](cb: A => me.Task[B]) =
|
||||
new ActionObservableBuilder(observableAction.mapEval(cb))
|
||||
|
||||
def underlying = observableAction
|
||||
|
||||
}
|
47
src/main/scala/wow/doge/mygame/utils/controls/FontIcon.scala
Normal file
47
src/main/scala/wow/doge/mygame/utils/controls/FontIcon.scala
Normal file
@ -0,0 +1,47 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import javafx.{scene => jfxs}
|
||||
import org.kordamp.ikonli.{javafx => ikonlifx}
|
||||
import scalafx.scene.paint.Paint
|
||||
import scalafx.scene.text.Text
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object FontIcon {
|
||||
implicit def sfxText2jfx(v: FontIcon): jfxs.text.Text =
|
||||
if (v != null) v.delegate else null
|
||||
|
||||
}
|
||||
|
||||
// extends Shape(delegate)
|
||||
// with PositionDelegate[ikonlifx.FontIcon]
|
||||
// with SFXDelegate[ikonlifx.FontIcon]
|
||||
|
||||
class FontIcon(override val delegate: ikonlifx.FontIcon = new ikonlifx.FontIcon)
|
||||
extends Text(delegate) {
|
||||
|
||||
// def iconCode_=(v: Ikon) = delegate.setIconCode(v)
|
||||
|
||||
def iconColor = delegate.getIconColor()
|
||||
|
||||
def iconColor_=(color: Paint) = delegate.setIconColor(color)
|
||||
|
||||
def iconSize = delegate.getIconSize()
|
||||
|
||||
def iconSize_=(size: Int) = delegate.setIconSize(size)
|
||||
|
||||
def iconLiteral = delegate.getIconLiteral()
|
||||
|
||||
def iconLiteral_=(literal: IconLiteral) =
|
||||
delegate.setIconLiteral(literal.value)
|
||||
|
||||
def iconLiteral_=(literal: String) = delegate.setIconLiteral(literal)
|
||||
|
||||
}
|
||||
|
||||
sealed abstract class IconLiteral(val value: String)
|
||||
object IconLiteral {
|
||||
// fab-accusoft
|
||||
case object Gmi10k extends IconLiteral("gmi-10k")
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import javafx.{scene => jfxs}
|
||||
import scalafx.Includes._
|
||||
import scalafx.beans.property.ObjectProperty
|
||||
import scalafx.scene.Node
|
||||
import scalafx.scene.control.Button
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
import jfxs.{paint => jfxsp}
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object JFXButton {
|
||||
implicit def sfxButton2jfx(v: JFXButton): jfoenixc.JFXButton =
|
||||
if (v != null) v.delegate else null
|
||||
}
|
||||
|
||||
class JFXButton(
|
||||
override val delegate: jfoenixc.JFXButton = new jfoenixc.JFXButton
|
||||
) extends Button(delegate) {
|
||||
|
||||
/**
|
||||
* Creates a button with the specified text as its label.
|
||||
*/
|
||||
def this(text: String) = this(new jfoenixc.JFXButton(text))
|
||||
|
||||
/**
|
||||
* Creates a button with the specified text and icon for its label.
|
||||
*/
|
||||
def this(text: String, graphic: Node) =
|
||||
this(new jfoenixc.JFXButton(text, graphic))
|
||||
|
||||
def ripplerFill: ObjectProperty[jfxsp.Paint] = delegate.ripplerFillProperty
|
||||
|
||||
def ripplerFill_=(b: jfxsp.Paint): Unit = ripplerFill() = b
|
||||
|
||||
def obsAction = new ActionObservableBuilder(this.observableAction)
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import scalafx.scene.layout.Region
|
||||
import scalafx.scene.layout.StackPane
|
||||
|
||||
class JFXDialog(
|
||||
override val delegate: jfoenixc.JFXDialog = new jfoenixc.JFXDialog
|
||||
) extends StackPane(delegate) {
|
||||
def show() = delegate.show()
|
||||
def show(sp: StackPane) = delegate.show(sp)
|
||||
def content = delegate.getContent()
|
||||
def content_=(r: Region) = delegate.setContent(r)
|
||||
def overlayClose = delegate.overlayCloseProperty()
|
||||
def overlayClose_=(v: Boolean) = delegate.setOverlayClose(v)
|
||||
def cacheContainer = delegate.cacheContainerProperty()
|
||||
def cacheContainer_=(v: Boolean) = delegate.setCacheContainer(v)
|
||||
}
|
||||
|
||||
object JFXDialog {
|
||||
implicit def sfxJfXDialog2Jfx(v: JFXDialog): jfoenixc.JFXDialog = v.delegate
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import javafx.scene.{control => jfxsc}
|
||||
import scalafx.Includes._
|
||||
import scalafx.beans.property.ReadOnlyObjectProperty
|
||||
import scalafx.delegate.SFXDelegate
|
||||
import scalafx.scene.control.IndexedCell
|
||||
import scalafx.scene.control.ListCell
|
||||
import scalafx.scene.control.ListView
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object JFXListCell {
|
||||
implicit def sfxListCell2jfx[T](
|
||||
l: JFXListCell[T]
|
||||
): ListCell[T] =
|
||||
if (l != null) l.delegate else null
|
||||
}
|
||||
|
||||
class JFXListCell[T](
|
||||
override val delegate: jfoenixc.JFXListCell[T] =
|
||||
new jfoenixc.JFXListCell[T] {
|
||||
override def updateItem(
|
||||
item: T,
|
||||
empty: Boolean
|
||||
): Unit = {
|
||||
super.updateItem(item, empty)
|
||||
// setText(null)
|
||||
setText(getText())
|
||||
setGraphic(getGraphic())
|
||||
// setGraphic(null)
|
||||
// remove empty (Trailing cells)
|
||||
// setMouseTransparent(true)
|
||||
// setStyle("-fx-background-color:TRANSPARENT;")
|
||||
}
|
||||
override def makeChildrenTransparent(): Unit = {}
|
||||
}
|
||||
) extends IndexedCell(delegate)
|
||||
with SFXDelegate[jfoenixc.JFXListCell[T]] {
|
||||
|
||||
/**
|
||||
* The ListView associated with this Cell.
|
||||
*/
|
||||
def listView: ReadOnlyObjectProperty[jfxsc.ListView[T]] =
|
||||
delegate.listViewProperty
|
||||
|
||||
/**
|
||||
* Updates the ListView associated with this Cell.
|
||||
*/
|
||||
def updateListView(listView: ListView[T]): Unit =
|
||||
delegate.updateListView(listView)
|
||||
|
||||
// delegate.cell
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import monix.execution.Scheduler
|
||||
import monix.reactive.Observable
|
||||
import scalafx.Includes._
|
||||
import scalafx.collections.ObservableBuffer
|
||||
import scalafx.scene.control.ListView
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object JFXListView {
|
||||
implicit def sfxListView2jfx[T](l: JFXListView[T]): jfoenixc.JFXListView[T] =
|
||||
if (l != null) l.delegate else null
|
||||
}
|
||||
|
||||
// extends Control(delegate)
|
||||
// with SFXDelegate[jfoenixc.JFXListView[T]]
|
||||
|
||||
class JFXListView[T](
|
||||
override val delegate: jfoenixc.JFXListView[T] = new jfoenixc.JFXListView[T]
|
||||
) extends ListView[T] {
|
||||
|
||||
// def items_=(
|
||||
// v: Observable[ObservableBuffer[T]]
|
||||
// )(implicit s: Scheduler): Unit = {
|
||||
// v.foreach { items() = _ }
|
||||
// }
|
||||
|
||||
def items_=(v: Observable[Seq[T]])(implicit s: Scheduler): Unit =
|
||||
v.map(ObservableBuffer.from).foreach(items() = _)
|
||||
|
||||
def depth = delegate.depthProperty()
|
||||
def depth_=(depth: Int) = delegate.setDepth(depth)
|
||||
|
||||
def expanded = delegate.expandedProperty()
|
||||
def expanded_=(v: Boolean) = expanded() = v
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package wow.doge.mygame.utils.controls
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import scalafx.scene.control.ProgressBar
|
||||
|
||||
class JFXProgressBar(
|
||||
override val delegate: jfoenixc.JFXProgressBar = new jfoenixc.JFXProgressBar
|
||||
) extends ProgressBar(delegate) {
|
||||
def secondaryProgress = delegate.getSecondaryProgress()
|
||||
def secondaryProgress_=(v: Double) = delegate.setSecondaryProgress(v)
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import scalafx.scene.Node
|
||||
import scalafx.scene.layout.StackPane
|
||||
import scalafx.scene.paint.Paint
|
||||
|
||||
class JFXRippler(
|
||||
override val delegate: jfoenixc.JFXRippler = new jfoenixc.JFXRippler
|
||||
) extends StackPane(delegate) {
|
||||
import JFXRippler._
|
||||
def control = delegate.getControl()
|
||||
def control_=(v: Node) = delegate.setControl(v)
|
||||
def enabled_=(v: Boolean) = delegate.setEnabled(v)
|
||||
def ripplerPos = delegate.getPosition()
|
||||
def ripplerPos_=(pos: RipplerPos) = delegate.setPosition(pos)
|
||||
def ripplerDisabled = delegate.ripplerDisabledProperty()
|
||||
def ripplerDisabled_=(v: Boolean) = delegate.setRipplerDisabled(v)
|
||||
def ripplerFill = delegate.ripplerFillProperty()
|
||||
def ripplerFill_=(v: Paint) = delegate.setRipplerFill(v)
|
||||
def ripplerRecenter = delegate.ripplerRecenterProperty()
|
||||
def ripplerRecenter_=(v: Boolean) = delegate.setRipplerRecenter(v)
|
||||
def ripplerRadius = delegate.ripplerRadiusProperty()
|
||||
def ripplerRadius_=(v: Int) = delegate.setRipplerRadius(v)
|
||||
}
|
||||
|
||||
object JFXRippler {
|
||||
abstract class RipplerPos(val delegate: jfoenixc.JFXRippler.RipplerPos)
|
||||
case object Front extends RipplerPos(jfoenixc.JFXRippler.RipplerPos.FRONT)
|
||||
case object Back extends RipplerPos(jfoenixc.JFXRippler.RipplerPos.BACK)
|
||||
object RipplerPos {
|
||||
implicit def sfxRipplerPos2jfxRipplerPos(
|
||||
v: RipplerPos
|
||||
): jfoenixc.JFXRippler.RipplerPos = v.delegate
|
||||
}
|
||||
implicit def sfxRippler2jfxRippler(v: JFXRippler): jfoenixc.JFXRippler =
|
||||
v.delegate
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import scalafx.scene.control.ProgressIndicator
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object JFXSpinner {
|
||||
implicit def sfxSpinner2jfx(
|
||||
v: JFXSpinner
|
||||
): jfoenixc.JFXSpinner = if (v != null) v.delegate else null
|
||||
|
||||
}
|
||||
|
||||
// extends Control(delegate)
|
||||
// with SFXDelegate[jfoenixc.JFXSpinner]
|
||||
|
||||
/**
|
||||
* Wraps [[JFoenix JFXSpinner]]
|
||||
*/
|
||||
class JFXSpinner(
|
||||
override val delegate: jfoenixc.JFXSpinner = new jfoenixc.JFXSpinner
|
||||
) extends ProgressIndicator(delegate) {
|
||||
|
||||
def radius = delegate.getRadius()
|
||||
def radius_=(radius: Double) = delegate.setRadius(radius)
|
||||
|
||||
def startingAngle = delegate.startingAngleProperty()
|
||||
def startingAngle_=(angle: Double) = delegate.setStartingAngle(angle)
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import scalafx.Includes._
|
||||
import scalafx.beans.property.BooleanProperty
|
||||
import scalafx.scene.control.TextArea
|
||||
import scalafx.scene.paint.Paint
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object JFXTextArea {
|
||||
implicit def sfxTextArea2jfx(v: JFXTextArea): jfoenixc.JFXTextArea =
|
||||
if (v != null) v.delegate else null
|
||||
}
|
||||
// extends TextInputControl(delegate)
|
||||
// with SFXDelegate[jfoenixc.JFXTextArea]
|
||||
class JFXTextArea(
|
||||
override val delegate: jfoenixc.JFXTextArea = new jfoenixc.JFXTextArea()
|
||||
) extends TextArea(delegate) {
|
||||
|
||||
/**
|
||||
* Creates a TextArea with initial text content.
|
||||
*
|
||||
* @param text - A string for text content.
|
||||
*/
|
||||
def this(text: String) = this(new jfoenixc.JFXTextArea(text))
|
||||
|
||||
def labelFloat = delegate.labelFloatProperty()
|
||||
def labelFloat_=(v: Boolean) = delegate.setLabelFloat(v)
|
||||
|
||||
def focusColor: Paint = delegate.getFocusColor()
|
||||
def focusColor_=(color: Paint) = delegate.setFocusColor(color)
|
||||
|
||||
def unFocusColor = delegate.getUnFocusColor()
|
||||
def unFocusColor_=(color: Paint) = delegate.setUnFocusColor(color)
|
||||
|
||||
def disableAnimation: BooleanProperty = delegate.disableAnimationProperty()
|
||||
|
||||
def disableAnimation_=(disable: Boolean) =
|
||||
delegate.setDisableAnimation(disable)
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import scalafx.Includes._
|
||||
import scalafx.beans.property.BooleanProperty
|
||||
import scalafx.scene.control.TextField
|
||||
import scalafx.scene.paint.Paint
|
||||
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.Null", "org.wartremover.warts.Equals")
|
||||
)
|
||||
object JFXTextField {
|
||||
implicit def sfxTextField2jfx(v: JFXTextField): jfoenixc.JFXTextField =
|
||||
if (v != null) v.delegate else null
|
||||
}
|
||||
|
||||
// TextInputControl(delegate)
|
||||
// with AlignmentDelegate[jfoenixc.JFXTextField]
|
||||
// with SFXDelegate[jfoenixc.JFXTextField] {
|
||||
|
||||
class JFXTextField(
|
||||
override val delegate: jfoenixc.JFXTextField = new jfoenixc.JFXTextField
|
||||
) extends TextField(delegate) {
|
||||
|
||||
def labelFloat = delegate.labelFloatProperty()
|
||||
def labelFloat_=(v: Boolean) = delegate.setLabelFloat(v)
|
||||
|
||||
def focusColor: Paint = delegate.getFocusColor()
|
||||
def focusColor_=(color: Paint) = delegate.setFocusColor(color)
|
||||
|
||||
def unFocusColor = delegate.getUnFocusColor()
|
||||
def unFocusColor_=(color: Paint) = delegate.setUnFocusColor(color)
|
||||
|
||||
def disableAnimation: BooleanProperty = delegate.disableAnimationProperty()
|
||||
|
||||
def disableAnimation_=(disable: Boolean) =
|
||||
delegate.setDisableAnimation(disable)
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject
|
||||
import com.jfoenix.{controls => jfoenixc}
|
||||
import javafx.scene.{control => jfxsc}
|
||||
import scalafx.collections.ObservableBuffer
|
||||
import scalafx.scene.control.TreeItem
|
||||
import scalafx.scene.control.TreeTableView
|
||||
|
||||
class RecursiveTreeItem[G <: RecursiveTreeObject[G]](
|
||||
override val delegate: jfoenixc.RecursiveTreeItem[G] =
|
||||
new jfoenixc.RecursiveTreeItem[G]((item: RecursiveTreeObject[G]) =>
|
||||
item.getChildren()
|
||||
)
|
||||
) extends TreeItem[G](delegate) {
|
||||
def this(value: G) =
|
||||
this(
|
||||
new jfoenixc.RecursiveTreeItem[G](
|
||||
value,
|
||||
(item: RecursiveTreeObject[G]) => item.getChildren()
|
||||
)
|
||||
)
|
||||
def this(items: ObservableBuffer[G]) =
|
||||
this(
|
||||
new jfoenixc.RecursiveTreeItem[G](
|
||||
items,
|
||||
(item: RecursiveTreeObject[G]) => item.getChildren()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
object RecursiveTreeItem {
|
||||
implicit def sfxTreeItem2jfxTreeItem[G <: RecursiveTreeObject[G]](
|
||||
v: RecursiveTreeItem[G]
|
||||
): jfoenixc.RecursiveTreeItem[G] = v.delegate
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
class JFXTreeTableView[S <: RecursiveTreeObject[S]](
|
||||
override val delegate: jfoenixc.JFXTreeTableView[S] = new jfoenixc.JFXTreeTableView[S]
|
||||
) extends TreeTableView(delegate) {
|
||||
|
||||
|
||||
def this(root: TreeItem[S]) = this(new jfoenixc.JFXTreeTableView[S](root))
|
||||
// def this(root: TreeItem[S], items: ObservableBuffer[S]) = this(new jfoenixc.JFXTreeTableView[S](root, items))
|
||||
|
||||
// @formatter:on
|
||||
def currentItemsCount = delegate.currentItemsCountProperty()
|
||||
def predicate = delegate.predicateProperty()
|
||||
// delegate.set
|
||||
|
||||
// override def treeColumn_=(v: TreeTableColumn[S, _]): Unit = ???
|
||||
// delegate.setTreeColumn()
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Null","org.wartremover.warts.Equals"))
|
||||
object JFXTreeTableView {
|
||||
implicit def sfxTreeTableView2jfx[S <: RecursiveTreeObject[S]](
|
||||
v: JFXTreeTableView[S]
|
||||
): jfxsc.TreeTableView[S] = if (v != null) v.delegate else null
|
||||
}
|
||||
// @formatter:on
|
@ -0,0 +1,7 @@
|
||||
package wow.doge.mygame.util.controls
|
||||
|
||||
import scalafx.scene.{control => sfxc}
|
||||
import wow.doge.mygame.implicits._
|
||||
class MenuItem extends sfxc.MenuItem {
|
||||
def obsAction = new ActionObservableBuilder(this.observableAction)
|
||||
}
|
35
src/main/scala/wow/doge/mygame/utils/package.scala
Normal file → Executable file
35
src/main/scala/wow/doge/mygame/utils/package.scala
Normal file → Executable file
@ -1,7 +1,38 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
// import wow.doge.mygame.utils.wrappers.Node
|
||||
import java.nio.file.StandardOpenOption
|
||||
|
||||
import monix.bio.UIO
|
||||
import monix.execution.Scheduler
|
||||
import monix.nio.file.AsyncFileChannelConsumer
|
||||
import monix.nio.text.UTF8Codec.utf8Decode
|
||||
import monix.nio.{file => mnf}
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
package object utils {
|
||||
// type AppNode = Node
|
||||
def methodName(implicit enclosing: sourcecode.Enclosing) =
|
||||
enclosing.value.split(" ")(0).split("""\.""").last
|
||||
|
||||
def readAsync(
|
||||
path: os.Path,
|
||||
chunkSize: Int = 8192
|
||||
) =
|
||||
UIO
|
||||
.deferAction(implicit s => UIO(mnf.readAsync(path.toNIO, chunkSize)))
|
||||
.flatMap(bytes => UIO(bytes.pipeThrough(utf8Decode)))
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.TraversableOps"))
|
||||
def readAsyncL(
|
||||
path: os.Path,
|
||||
chunkSize: Int = 8192
|
||||
) =
|
||||
readAsync(path, chunkSize)
|
||||
.flatMap(_.toListL.toIO)
|
||||
.map(_.head)
|
||||
|
||||
def writeAsync(path: os.Path, flags: Seq[StandardOpenOption] = Seq.empty)(
|
||||
implicit s: Scheduler
|
||||
): AsyncFileChannelConsumer =
|
||||
mnf.appendAsync(path.toNIO, 0, flags)
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ class AssetManager(assetManager: jmea.AssetManager) {
|
||||
case ex: AssetLoadException =>
|
||||
IO.raiseError(AssetLoadError(ex.getMessage))
|
||||
}
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.AsInstanceOf", "org.wartremover.warts.Equals")
|
||||
)
|
||||
def loadModelAs[T <: Spatial](
|
||||
path: os.RelPath
|
||||
)(implicit ct: ClassTag[T]): IO[Error, T] =
|
||||
@ -28,6 +31,9 @@ class AssetManager(assetManager: jmea.AssetManager) {
|
||||
UIO(model.asInstanceOf[T])
|
||||
else IO.raiseError(CouldNotCastError)
|
||||
)
|
||||
@SuppressWarnings(
|
||||
Array("org.wartremover.warts.AsInstanceOf", "org.wartremover.warts.Equals")
|
||||
)
|
||||
def loadAssetAs[T](path: os.RelPath)(implicit ct: ClassTag[T]): IO[Error, T] =
|
||||
IO(assetManager.loadAsset(path.toString))
|
||||
.onErrorHandleWith {
|
||||
@ -46,9 +52,9 @@ class AssetManager(assetManager: jmea.AssetManager) {
|
||||
|
||||
}
|
||||
object AssetManager {
|
||||
sealed trait Error
|
||||
case class AssetNotFound(message: String) extends Error
|
||||
case class AssetLoadError(message: String) extends Error
|
||||
sealed trait Error extends Product with Serializable
|
||||
final case class AssetNotFound(message: String) extends Error
|
||||
final case class AssetLoadError(message: String) extends Error
|
||||
case object CouldNotCastError extends Error
|
||||
object Error {
|
||||
implicit val show = Show.fromToString[Error]
|
||||
|
@ -9,7 +9,7 @@ import monix.bio.IO
|
||||
|
||||
object CollisionShapeFactory {
|
||||
sealed trait Error
|
||||
case class WrongArgumentError(reason: String) extends Error
|
||||
final case class WrongArgumentError(reason: String) extends Error
|
||||
|
||||
object Error {
|
||||
implicit val show = Show.fromToString[Error]
|
||||
|
@ -0,0 +1,126 @@
|
||||
package wow.doge.mygame.utils.wrappers.jme
|
||||
import com.jme3.input.controls.InputListener
|
||||
import com.jme3.input.controls.Trigger
|
||||
import com.jme3.{input => jmei}
|
||||
import enumeratum._
|
||||
import monix.bio.UIO
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.ActionEvent
|
||||
import wow.doge.mygame.AnalogEvent
|
||||
import wow.doge.mygame.EnumActionEvent
|
||||
import wow.doge.mygame.EnumAnalogEvent
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
final class InputManager(val delegate: jmei.InputManager) {
|
||||
|
||||
/**
|
||||
* Create a new mapping to the given triggers.
|
||||
*
|
||||
* <p>
|
||||
* The given mapping will be assigned to the given triggers, when
|
||||
* any of the triggers given raise an event, the listeners
|
||||
* registered to the mappings will receive appropriate events.
|
||||
*
|
||||
* @param mappingName The mapping name to assign.
|
||||
* @param triggers The triggers to which the mapping is to be registered.
|
||||
*/
|
||||
def withMapping(mapping: String, triggers: Trigger*): UIO[Unit] =
|
||||
UIO(delegate.withMapping(mapping, triggers: _*)).void
|
||||
|
||||
def withMapping[T <: EnumEntry](
|
||||
mapping: T,
|
||||
triggers: Trigger*
|
||||
): UIO[Unit] = UIO(delegate.withMapping(mapping, triggers: _*)).void
|
||||
|
||||
def withListener(listener: InputListener, mappings: String*) =
|
||||
UIO(delegate.withListener(listener, mappings: _*))
|
||||
|
||||
/**
|
||||
* Creates new mappings from the values of the given Enum
|
||||
*
|
||||
* <p>
|
||||
* The given mapping will be assigned to the given triggers, when
|
||||
* any of the triggers given raise an event, the listeners
|
||||
* registered to the mappings will receive appropriate events.
|
||||
*
|
||||
* @param mappingName The mapping name to assign.
|
||||
* @param mappingFn Function from enum values to the sequence of trigers.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* {{{
|
||||
*
|
||||
* sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
|
||||
* object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
|
||||
* val values = findValues
|
||||
* case object TurnRight extends PlayerAnalogMovementInput
|
||||
* case object TurnLeft extends PlayerAnalogMovementInput
|
||||
* }
|
||||
*
|
||||
* {
|
||||
* inputManager.withEnumMappings(PlayerAnalogMovementInput) {
|
||||
* case PlayerAnalogMovementInput.TurnRight =>
|
||||
* Seq(new KeyTrigger(KeyInput.KEY_RIGHT))
|
||||
* case PlayerAnalogMovementInput.TurnLeft =>
|
||||
* Seq(new KeyTrigger(KeyInput.KEY_LEFT))
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
*/
|
||||
def withEnumMappings[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
)(mappingFn: T => Seq[Trigger]) =
|
||||
UIO(delegate.withEnumMappings(mappingEnum)(mappingFn))
|
||||
|
||||
/**
|
||||
* Create an observable which emits the given mappings as elements of an observable
|
||||
*
|
||||
* @param mappingNames
|
||||
* @return Observable of action events
|
||||
*
|
||||
* @see [[ActionEvent]]
|
||||
* @see [[enumObservableAction]]
|
||||
*/
|
||||
def observableAction(mappingNames: String*): Observable[ActionEvent] =
|
||||
delegate.observableAction(mappingNames: _*)
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Create an observable which emits the values of the given
|
||||
* enum as elements of an observable
|
||||
*
|
||||
* @param mappingNames
|
||||
* @return Observable of enum values
|
||||
*
|
||||
* @example {{{
|
||||
* inputManager
|
||||
* .enumObservableAction(PlayerMovementInput)
|
||||
* .doOnNext { action =>
|
||||
* action.binding match {
|
||||
* case PlayerMovementInput.WalkLeft => Task {/* your actions */}
|
||||
* }
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* @see [[EnumActionEvent]]
|
||||
* @see [[enumAnalogObservable]]
|
||||
*/
|
||||
def enumObservableAction[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
): Observable[EnumActionEvent[T]] =
|
||||
delegate.enumObservableAction(mappingEnum)
|
||||
|
||||
def enumEntryObservableAction[T <: EnumEntry](
|
||||
mappingEnumEntry: T
|
||||
) = delegate.enumEntryObservableAction(mappingEnumEntry)
|
||||
|
||||
def analogObservable(mappingNames: String*): Observable[AnalogEvent] =
|
||||
delegate.analogObservable(mappingNames: _*)
|
||||
|
||||
def enumAnalogObservable[T <: EnumEntry](
|
||||
mappingEnum: Enum[T]
|
||||
): Observable[EnumAnalogEvent[T]] = delegate.enumAnalogObservable(mappingEnum)
|
||||
|
||||
def cursorVisible_=(value: Boolean) = UIO(delegate.setCursorVisible(value))
|
||||
def cursorVisible = delegate.isCursorVisible
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import akka.actor.typed.ActorSystem
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
|
||||
class ActorTimeoutTest extends AnyFunSuite with BeforeAndAfterAll {
|
||||
import ActorTimeoutTest._
|
||||
@ -30,7 +31,7 @@ class ActorTimeoutTest extends AnyFunSuite with BeforeAndAfterAll {
|
||||
object ActorTimeoutTest {
|
||||
object MyActor {
|
||||
sealed trait Command
|
||||
case class GetInt(replyTo: ActorRef[Int]) extends Command
|
||||
final case class GetInt(replyTo: ActorRef[Int]) extends Command
|
||||
|
||||
class Props() {
|
||||
def create =
|
||||
|
32
src/test/scala/wow/doge/mygame/AmmoniteTest.scala
Normal file
32
src/test/scala/wow/doge/mygame/AmmoniteTest.scala
Normal file
@ -0,0 +1,32 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import monix.bio.Task
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler
|
||||
import monix.bio.UIO
|
||||
import scala.concurrent.Future
|
||||
import monix.execution.Scheduler
|
||||
|
||||
class AmmoniteTest extends munit.TaglessFinalSuite[Task] {
|
||||
|
||||
override protected def toFuture[A](f: Task[A]): Future[A] = {
|
||||
implicit val s = Scheduler.global
|
||||
f.runToFuture
|
||||
}
|
||||
|
||||
val scriptCompileFns = new ScriptCompiler.ScriptCompileFns(
|
||||
ScriptCompiler.defaultScalaRunner,
|
||||
ScriptCompiler.defaultKotlinRunner,
|
||||
ScriptCompiler.defaultGroovyRunner
|
||||
)
|
||||
|
||||
override def afterAll(): Unit = ()
|
||||
|
||||
test("Basic test") {
|
||||
UIO(
|
||||
scriptCompileFns
|
||||
.runScala(
|
||||
os.pwd / "assets" / "scripts" / "scala" / "basicTestScript.sc"
|
||||
)
|
||||
).assertEquals(Right("hello"))
|
||||
}
|
||||
}
|
@ -1,21 +1,17 @@
|
||||
package wow.doge.mygame
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import com.jme3.anim.AnimClip
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import com.jme3.asset.DesktopAssetManager
|
||||
import com.jme3.scene.Spatial
|
||||
import monix.bio.UIO
|
||||
import scala.concurrent.duration._
|
||||
import com.jme3.scene.Node
|
||||
|
||||
import cats.syntax.all._
|
||||
import com.jme3.anim.AnimComposer
|
||||
import com.jme3.anim.SkinningControl
|
||||
import com.jme3.anim.tween.action.BaseAction
|
||||
import com.jme3.anim.tween.Tweens
|
||||
import scala.jdk.javaapi.CollectionConverters._
|
||||
import com.jme3.anim.tween.Tween
|
||||
import com.jme3.anim.tween.action.BaseAction
|
||||
import com.jme3.anim.tween.action.ClipAction
|
||||
import cats.syntax.all._
|
||||
import com.jme3.asset.DesktopAssetManager
|
||||
import com.jme3.scene.Node
|
||||
import monix.bio.UIO
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
|
||||
class AnimTest extends AnyFunSuite {
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
|
@ -1,66 +1,75 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import cats.syntax.eq._
|
||||
import com.jme3.{asset => jmea}
|
||||
import com.jme3.asset.DesktopAssetManager
|
||||
import com.jme3.material.Material
|
||||
import com.jme3.material.MaterialDef
|
||||
import com.jme3.scene.Geometry
|
||||
import com.jme3.scene.Node
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager.AssetNotFound
|
||||
import com.jme3.scene.Geometry
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager.CouldNotCastError
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.material.MaterialDef
|
||||
import com.jme3.material.Material
|
||||
import scala.annotation.nowarn
|
||||
|
||||
@nowarn("msg=method get in class LeftProjection is deprecated")
|
||||
@nowarn("msg=method right in class Either is deprecated")
|
||||
@nowarn("msg=method get in class RightProjection is deprecated")
|
||||
class AssetManagerTest extends AnyFunSuite {
|
||||
class AssetManagerTest extends MonixBioSuite {
|
||||
|
||||
val _assetManager: jmea.AssetManager = new DesktopAssetManager(true)
|
||||
val assetManager = new AssetManager(_assetManager)
|
||||
val fixture = FunFixture[AssetManager](
|
||||
setup = _ => new AssetManager(new DesktopAssetManager(true)),
|
||||
teardown = _ => ()
|
||||
)
|
||||
|
||||
test("Test for AssetNotFound error") {
|
||||
val res =
|
||||
assetManager.loadModel(os.rel / "doesnotexist").attempt.runSyncUnsafe()
|
||||
assert(res.isLeft)
|
||||
assert(res.left.get eqv AssetNotFound("doesnotexist"))
|
||||
override def beforeAll(): Unit = {
|
||||
import java.util.logging.{Logger => JLogger, Level}
|
||||
JLogger.getLogger("").setLevel(Level.SEVERE)
|
||||
}
|
||||
|
||||
test("Test for Model CouldNotCastError") {
|
||||
fixture.test(
|
||||
"AssetManager#loadModel should fail with AssetNotFoundError when asset does not exist"
|
||||
) { assetManager =>
|
||||
assetManager
|
||||
.loadModel(os.rel / "doesnotexist")
|
||||
.attempt
|
||||
.assertEquals(Left(AssetNotFound("doesnotexist")))
|
||||
}
|
||||
|
||||
fixture.test(
|
||||
"AssetManager#loadModelAs should fail with CouldNotCastError when model is not of target type"
|
||||
) { assetManager =>
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
val res1 = assetManager
|
||||
assetManager
|
||||
.loadModelAs[Geometry](modelPath)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
assert(res1.isLeft)
|
||||
assert(res1.left.get eqv CouldNotCastError)
|
||||
.assertEquals(Left(CouldNotCastError))
|
||||
|
||||
val res2 = assetManager
|
||||
}
|
||||
|
||||
fixture.test(
|
||||
"AssetManager#loadModelAs should load com.jme3.Node successfully"
|
||||
) { assetManager =>
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
assetManager
|
||||
.loadModelAs[Node](modelPath)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
assert(res2.isRight)
|
||||
assert(res2.map(_.getName).right.get eqv "JaimeGeom-ogremesh")
|
||||
.map(_.map(_.getName))
|
||||
.assertEquals(Right("JaimeGeom-ogremesh"))
|
||||
}
|
||||
|
||||
test("Test for Asset CouldNotCastError") {
|
||||
fixture.test(
|
||||
"AssetManager#loadAssetAs should fail with CouldNotCastError when asset is not of target type"
|
||||
) { assetManager =>
|
||||
val assetPath = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
|
||||
val res1 = assetManager
|
||||
assetManager
|
||||
.loadAssetAs[Material](assetPath)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
assert(res1.isLeft)
|
||||
assert(res1.left.get eqv CouldNotCastError)
|
||||
.assertEquals(Left(CouldNotCastError))
|
||||
}
|
||||
|
||||
val res2 = assetManager
|
||||
fixture.test(
|
||||
"AssetManager#loadAssetAs should should load asset succesfully"
|
||||
) { assetManager =>
|
||||
val assetPath = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
assetManager
|
||||
.loadAssetAs[MaterialDef](assetPath)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
assert(res2.isRight)
|
||||
assert(res2.map(_.getName).right.get eqv "Unshaded")
|
||||
.map(_.map(_.getName))
|
||||
.assertEquals(Right("Unshaded"))
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,27 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import com.jme3.scene.Spatial
|
||||
import com.jme3.collision.{Collidable, CollisionResults}
|
||||
import com.jme3.bounding.BoundingVolume
|
||||
import com.jme3.scene.Spatial.DFSMode
|
||||
import com.jme3.collision.Collidable
|
||||
import com.jme3.collision.CollisionResults
|
||||
import com.jme3.scene.SceneGraphVisitor
|
||||
import java.util.Queue
|
||||
import com.jme3.scene.Spatial
|
||||
import com.jme3.scene.Spatial.DFSMode
|
||||
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import cats.syntax.eq._
|
||||
|
||||
class CollisionShapeFactoryTest extends AnyFunSuite {
|
||||
import java.util.Queue
|
||||
|
||||
class CollisionShapeFactoryTest extends MonixBioSuite {
|
||||
test("Test for WrongArgumentError") {
|
||||
val res = CollisionShapeFactory
|
||||
CollisionShapeFactory
|
||||
.createMeshShape(new TestSpatial)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
|
||||
assert(res.isLeft)
|
||||
|
||||
assert(
|
||||
res.left.get eqv
|
||||
CollisionShapeFactory.WrongArgumentError(
|
||||
"The spatial must either be a Node or a Geometry!"
|
||||
.assertEquals(
|
||||
Left(
|
||||
CollisionShapeFactory.WrongArgumentError(
|
||||
"The spatial must either be a Node or a Geometry!"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,70 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import cats.effect.{Resource => CResource}
|
||||
import monix.eval.Task
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class FileWatcherTest extends AnyFunSuite {
|
||||
test("1") {
|
||||
import better.files._
|
||||
import io.methvin.better.files._
|
||||
import cats.syntax.show._
|
||||
import monix.bio.IO
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.MonixDirectoryWatcher
|
||||
import scala.concurrent.Future
|
||||
import monix.execution.Scheduler
|
||||
import monix.bio.Task
|
||||
import monix.{eval => me}
|
||||
|
||||
val myDir =
|
||||
File((os.pwd / "assets" / "scripts").toString)
|
||||
val watcher = new RecursiveFileMonitor(myDir) {
|
||||
override def onCreate(file: File, count: Int) =
|
||||
println(s"$file got created")
|
||||
override def onModify(file: File, count: Int) =
|
||||
println(s"$file got modified $count times")
|
||||
override def onDelete(file: File, count: Int) =
|
||||
println(s"$file got deleted")
|
||||
}
|
||||
class FileWatcherTest extends munit.TaglessFinalSuite[Task] {
|
||||
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
CResource
|
||||
.make(Task { watcher.start(); watcher })(w => Task(w.stop()))
|
||||
.use(_ => Task.never)
|
||||
.runSyncUnsafe(10.seconds)
|
||||
override protected def toFuture[A](f: Task[A]): Future[A] = {
|
||||
implicit val s = Scheduler.global
|
||||
f.runToFuture
|
||||
}
|
||||
|
||||
// test("1") {
|
||||
// import better.files._
|
||||
// import io.methvin.better.files._
|
||||
|
||||
// val myDir =
|
||||
// File((os.pwd / "assets" / "scripts").toString)
|
||||
// val watcher = new RecursiveFileMonitor(myDir) {
|
||||
// override def onCreate(file: File, count: Int) =
|
||||
// println(show"$file got created")
|
||||
// override def onModify(file: File, count: Int) =
|
||||
// println(show"$file got modified $count times")
|
||||
// override def onDelete(file: File, count: Int) =
|
||||
// println(show"$file got deleted")
|
||||
// }
|
||||
|
||||
// import monix.execution.Scheduler.Implicits.global
|
||||
// CResource
|
||||
// .make(Task { watcher.start(); watcher })(w => Task(w.stop()))
|
||||
// .use(_ => Task.never)
|
||||
// .runSyncUnsafe(10.seconds)
|
||||
// }
|
||||
test("2") {
|
||||
|
||||
val obsT = MonixDirectoryWatcher(os.pwd / "assets" / "scripts")
|
||||
|
||||
obsT
|
||||
.flatMap(
|
||||
_.takeUntil(Observable.unit.delayExecution(2.seconds))
|
||||
.doOnNext {
|
||||
case MonixDirectoryWatcher.CreateEvent(file, count) =>
|
||||
me.Task(println(show"${file.toString} got created"))
|
||||
case MonixDirectoryWatcher.DeleteEvent(file, count) =>
|
||||
me.Task(println(show"${file.toString} got deleted"))
|
||||
case MonixDirectoryWatcher.ModifyEvent(file, count) =>
|
||||
me.Task(
|
||||
pprint.log(show"${file.toString} got modified $count times")
|
||||
)
|
||||
|
||||
}
|
||||
.completedL
|
||||
.flatMap(_ => me.Task.sleep(3.seconds))
|
||||
.toIO
|
||||
)
|
||||
.onErrorHandleWith {
|
||||
case ex: java.nio.file.ClosedWatchServiceException => IO.unit
|
||||
case ex: java.lang.UnsupportedOperationException => IO.unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,41 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import munit.ScalaCheckSuite
|
||||
import org.scalacheck.Prop._
|
||||
import munit.FunSuite
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import cats.syntax.eq._
|
||||
import cats.syntax.show._
|
||||
import cats.syntax.eq._
|
||||
import org.scalacheck.Gen
|
||||
import org.scalacheck.Arbitrary.arbitrary
|
||||
|
||||
class ImVector3fSuite extends FunSuite with ScalaCheckSuite with LazyLogging {
|
||||
|
||||
val floatGen = for {
|
||||
a <- arbitrary[Float]
|
||||
b <- arbitrary[Float]
|
||||
c <- arbitrary[Float]
|
||||
} yield ImVector3f(a, b, c)
|
||||
|
||||
val intGen = for {
|
||||
a <- arbitrary[Int]
|
||||
b <- arbitrary[Int]
|
||||
c <- arbitrary[Int]
|
||||
} yield ImVector3f(a.toFloat, b.toFloat, c.toFloat)
|
||||
|
||||
property("imvector3f dst float") {
|
||||
forAll(floatGen, floatGen) { (v1: ImVector3f, v2: ImVector3f) =>
|
||||
assertEquals(ImVector3f.dst(v1, v2), ImVector3f.dst(v2, v1))
|
||||
}
|
||||
}
|
||||
|
||||
property("imvector3f dst int") {
|
||||
forAll(intGen, intGen) { (v1: ImVector3f, v2: ImVector3f) =>
|
||||
assertEquals(ImVector3f.dst(v1, v2), ImVector3f.dst(v2, v1))
|
||||
}
|
||||
}
|
||||
|
||||
class ImVector3fTest extends AnyFunSuite with LazyLogging {
|
||||
test("maxvalue") {
|
||||
val v1 = ImVector3f.Max
|
||||
val v2 = ImVector3f.Max
|
||||
@ -33,19 +62,4 @@ class ImVector3fTest extends AnyFunSuite with LazyLogging {
|
||||
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
|
||||
}
|
||||
|
||||
test("another") {
|
||||
{
|
||||
val v1 = ImVector3f(1, 0, 0)
|
||||
val v2 = ImVector3f(1, 1, 1)
|
||||
logger.info(ImVector3f.dst(v1, v2).show)
|
||||
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
|
||||
}
|
||||
{
|
||||
val v1 = ImVector3f(1, 1, 0)
|
||||
val v2 = ImVector3f(1, 1, 1)
|
||||
logger.info(ImVector3f.dst(v1, v2).show)
|
||||
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +1,20 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import wow.doge.mygame.subsystems.moddingsystem.ModdingSystem
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import cats.syntax.eq._
|
||||
import io.circe.Printer
|
||||
import monix.bio.UIO
|
||||
import cats.syntax.eq._
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import wow.doge.mygame.subsystems.moddingsystem.ModdingSystem
|
||||
import monix.bio.Task
|
||||
import scala.concurrent.Future
|
||||
import monix.execution.Scheduler
|
||||
|
||||
class ModdingSystemTest extends AnyFunSuite {
|
||||
class ModdingSystemTest extends munit.TaglessFinalSuite[Task] {
|
||||
|
||||
override protected def toFuture[A](f: Task[A]): Future[A] = {
|
||||
implicit val s = Scheduler.global
|
||||
f.runToFuture
|
||||
}
|
||||
val printer = Printer.spaces2
|
||||
test("main") {
|
||||
val io = for {
|
||||
@ -19,9 +26,6 @@ class ModdingSystemTest extends AnyFunSuite {
|
||||
)
|
||||
_ <- ModdingSystem.log(res)
|
||||
} yield res
|
||||
io.attempt.runSyncUnsafe() match {
|
||||
case Left(value) => pprint.log(value); ()
|
||||
case Right(value) => ()
|
||||
}
|
||||
io.attempt
|
||||
}
|
||||
}
|
||||
|
13
src/test/scala/wow/doge/mygame/MonixBioSuite.scala
Normal file
13
src/test/scala/wow/doge/mygame/MonixBioSuite.scala
Normal file
@ -0,0 +1,13 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
import monix.bio.Task
|
||||
import monix.execution.Scheduler
|
||||
|
||||
trait MonixBioSuite extends munit.TaglessFinalSuite[Task] {
|
||||
override protected def toFuture[A](f: Task[A]): Future[A] = {
|
||||
implicit val s = Scheduler.global
|
||||
f.runToFuture
|
||||
}
|
||||
}
|
@ -1,29 +1,35 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import monix.bio.Task
|
||||
import scala.concurrent.duration._
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler
|
||||
import io.odin.consoleLogger
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
class MonixScriptCompilerTest extends AnyFunSuite {
|
||||
import io.odin.consoleLogger
|
||||
import monix.bio.Task
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import scala.concurrent.Future
|
||||
import monix.execution.Scheduler
|
||||
|
||||
class MonixScriptCompilerTest extends munit.TaglessFinalSuite[Task] {
|
||||
|
||||
override protected def toFuture[A](f: Task[A]): Future[A] = {
|
||||
implicit val s = Scheduler.global
|
||||
f.runToFuture
|
||||
}
|
||||
|
||||
test("some-test") {
|
||||
ScriptCompiler(consoleLogger[Task]())
|
||||
ScriptCompiler(consoleLogger[Task](), Schedulers.default.io)
|
||||
.use(scriptCompiler =>
|
||||
for {
|
||||
// _ <-
|
||||
// scriptCompiler.source
|
||||
// .doOnNextF(el => Task(println(s"Got $el")))
|
||||
// .doOnNextF(el => Task(println(show"Got $el")))
|
||||
// .completedL
|
||||
// .toIO
|
||||
// .hideErrors
|
||||
// .startAndForget
|
||||
response <- scriptCompiler.request(
|
||||
ScriptCompiler.Get(
|
||||
os.pwd / "assets" / "scripts" / "scala" / "hello2.sc",
|
||||
ScriptCompiler.GetScript(
|
||||
os.pwd / "assets" / "scripts" / "scala" / "hello.sc",
|
||||
_,
|
||||
false
|
||||
)
|
||||
@ -39,6 +45,5 @@ class MonixScriptCompilerTest extends AnyFunSuite {
|
||||
// _ <- Task.sleep(8.seconds)
|
||||
} yield ()
|
||||
)
|
||||
.runSyncUnsafe(20.seconds)
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import cats.data.ReaderT
|
||||
import monix.bio.UIO
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import monix.bio.IO
|
||||
import cats.mtl.Ask
|
||||
import cats.mtl.implicits._
|
||||
import cats.effect.LiftIO
|
||||
import monix.bio.Task
|
||||
import cats.data.Kleisli
|
||||
import cats.mtl.Ask
|
||||
import monix.bio.IO
|
||||
import monix.bio.IOLift
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
|
||||
final case class Environment(str: String, num: Int)
|
||||
|
||||
class ReaderT_Test extends AnyFunSuite {
|
||||
|
||||
@ -24,8 +22,6 @@ class ReaderT_Test extends AnyFunSuite {
|
||||
// val t2 = r.run("s").rethrow
|
||||
// Kleisli[IO, String, Unit](s => IO.unit)
|
||||
|
||||
case class Environment(str: String, num: Int)
|
||||
|
||||
// test("runReaderT_Test") {
|
||||
// def fun1: ReaderT[UIO, String, Unit] = ReaderT(str => UIO(println(str)))
|
||||
// def fun2: ReaderT[UIO, Int, Unit] = ReaderT(num => UIO(println(num)))
|
||||
|
@ -2,8 +2,8 @@ package wow.doge.mygame
|
||||
|
||||
import cats.data.Reader
|
||||
import monix.bio.UIO
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import monix.execution.Scheduler.Implicits.global
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
|
||||
class ReaderTest extends AnyFunSuite {
|
||||
|
||||
@ -17,8 +17,6 @@ class ReaderTest extends AnyFunSuite {
|
||||
// val t2 = r.run("s").rethrow
|
||||
// Kleisli[IO, String, Unit](s => IO.unit)
|
||||
|
||||
case class Environment(str: String, num: Int)
|
||||
|
||||
def fun1: Reader[String, UIO[Unit]] = Reader(str => UIO(println(str)))
|
||||
def fun2: Reader[Int, UIO[Unit]] = Reader(num => UIO(println(num)))
|
||||
|
||||
|
41
src/test/scala/wow/doge/mygame/WebsocketTest.scala
Normal file
41
src/test/scala/wow/doge/mygame/WebsocketTest.scala
Normal file
@ -0,0 +1,41 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import sttp.capabilities.monix.MonixStreams
|
||||
import sttp.client3._
|
||||
import sttp.client3.asynchttpclient.monix._
|
||||
|
||||
import monix.eval.Task
|
||||
import monix.reactive.Observable
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.collection.immutable.ArraySeq
|
||||
import monix.reactive.Consumer
|
||||
|
||||
class WebsocketTest {
|
||||
// : Task[Response[Either[String, Observable[Array[Byte]]]]]
|
||||
|
||||
val s = AsyncHttpClientMonixBackend().flatMap { backend =>
|
||||
val response =
|
||||
basicRequest
|
||||
.post(uri"...")
|
||||
.response(asStreamUnsafe(MonixStreams))
|
||||
.readTimeout(Duration.Inf)
|
||||
.send(backend)
|
||||
response.map(_.body.map(_.map(ArraySeq.unsafeWrapArray)))
|
||||
}
|
||||
|
||||
val MB = 1024 * 1024
|
||||
def consumer(contentLength: Long): Consumer[ArraySeq[Byte], Long] =
|
||||
Consumer.foldLeftEval(0L) {
|
||||
case (sum, data) =>
|
||||
val newSum = sum + data.length
|
||||
for {
|
||||
_ <- Task(
|
||||
pprint.log(
|
||||
s"Bytes downloaded = ${newSum * 1f / MB}MB . Percent done = ${(newSum * 100f / contentLength)
|
||||
.formatted("%.2f")}"
|
||||
)
|
||||
)
|
||||
} yield newSum
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user