From 12b232fb3cce9c6259e9e98664ecdd13413a7f54 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Sat, 27 Feb 2021 11:36:32 +0530 Subject: [PATCH] many changes --- .gitignore | 1 + .scalafix.conf | 8 + build.sbt | 46 ++- project/build.properties | 2 +- project/plugins.sbt | 5 + .../org/slf4j/impl/StaticLoggerBuilder.scala | 8 +- src/main/scala/wow/doge/mygame/AppError.scala | 2 +- src/main/scala/wow/doge/mygame/Main.scala | 11 +- src/main/scala/wow/doge/mygame/MainApp.scala | 233 ++++++++----- .../doge/mygame/actor/GameActorSystem.scala | 8 +- .../doge/mygame/executors/GUIExecutor.scala | 1 + .../doge/mygame/executors/Schedulers.scala | 8 +- .../scala/wow/doge/mygame/game/GameApp.scala | 8 +- .../wow/doge/mygame/game/GameAppActor.scala | 7 +- .../wow/doge/mygame/game/GameModule.scala | 30 -- .../wow/doge/mygame/game/SimpleAppExt.scala | 4 +- .../wow/doge/mygame/game/TestActor.scala | 2 +- .../mygame/game/appstates/MyBaseState.scala | 69 ---- .../game/appstates/ScriptingEngineState.scala | 207 ------------ .../mygame/game/appstates/TestAppState.scala | 71 ---- .../controls/CameraMovementControls.scala | 6 +- .../mygame/game/entities/CharacterStats.scala | 58 +++- .../game/entities/NpcActorSupervisor.scala | 25 +- .../player/PlayerActorSupervisor.scala | 87 +++-- .../player/PlayerActorSupervisor2.scala | 2 +- .../entities/player/PlayerCameraActor.scala | 2 +- .../entities/player/PlayerController.scala | 3 +- .../mygame/game/subsystems/ai/GdxAiTest.scala | 4 +- .../game/subsystems/input/InputEnums.scala | 13 + .../subsystems/level/DefaultGameLevel.scala | 41 --- .../game/subsystems/level/GameLevel.scala | 2 + .../doge/mygame/implicits/EntityQuery.scala | 22 +- .../implicits/JavaFXMonixObservables.scala | 312 ++++++++++++++++-- .../wow/doge/mygame/implicits/package.scala | 40 +-- .../wow/doge/mygame/launcher/Launcher.scala | 8 +- .../wow/doge/mygame/math/ImVector3f.scala | 2 +- .../mygame/subsystems/events/EventBus.scala | 6 +- .../mygame/subsystems/events/Events.scala | 5 +- .../events/PlayerCameraEvents.scala | 1 - .../subsystems/events/PlayerEvents.scala | 3 + .../moddingsystem/ModdingSystem.scala | 6 +- .../scriptsystem/MonixScriptCompiler.scala | 40 ++- .../subsystems/scriptsystem/ScriptActor.scala | 9 +- .../scriptsystem/ScriptCachingActor.scala | 5 +- .../scriptsystem/ScriptSystemModule.scala | 13 +- .../scala/wow/doge/mygame/types/package.scala | 4 +- .../mygame/utils/GenericConsoleStream.scala | 2 +- .../doge/mygame/utils/GenericTimerActor.scala | 7 +- .../mygame/utils/MonixDirectoryWatcher.scala | 72 ++-- .../wow/doge/mygame/utils/ReaderDemo.scala | 2 +- .../wow/doge/mygame/utils/Settings.scala | 4 +- .../wow/doge/mygame/utils/TreeTest.scala | 7 +- .../utils/controls/ActionObservable.scala | 67 ++++ .../doge/mygame/utils/controls/FontIcon.scala | 47 +++ .../mygame/utils/controls/JFXButton.scala | 42 +++ .../mygame/utils/controls/JFXDialog.scala | 22 ++ .../mygame/utils/controls/JFXListCell.scala | 57 ++++ .../mygame/utils/controls/JFXListView.scala | 40 +++ .../utils/controls/JFXProgressBar.scala | 11 + .../mygame/utils/controls/JFXRippler.scala | 37 +++ .../mygame/utils/controls/JFXSpinner.scala | 32 ++ .../mygame/utils/controls/JFXTextArea.scala | 43 +++ .../mygame/utils/controls/JFXTextField.scala | 38 +++ .../utils/controls/JFXTreeTableView.scala | 63 ++++ .../doge/mygame/utils/controls/MenuItem.scala | 7 + .../utils/wrappers/jme/AssetManager.scala | 12 +- .../wrappers/jme/CollisionShapeFactory.scala | 2 +- .../wow/doge/mygame/ActorTimeoutTest.scala | 21 +- src/test/scala/wow/doge/mygame/AnimTest.scala | 20 +- .../wow/doge/mygame/AssetManagerTest.scala | 17 +- .../mygame/CollisionShapeFactoryTest.scala | 18 +- .../wow/doge/mygame/FileWatcherTest.scala | 74 +++-- .../wow/doge/mygame/ImVector3fTest.scala | 6 +- .../wow/doge/mygame/ModdingSystemTest.scala | 8 +- .../doge/mygame/MonixScriptCompilerTest.scala | 14 +- .../scala/wow/doge/mygame/ReaderT_Test.scala | 16 +- .../scala/wow/doge/mygame/ReaderTest.scala | 4 +- 77 files changed, 1434 insertions(+), 828 deletions(-) create mode 100644 .scalafix.conf delete mode 100644 src/main/scala/wow/doge/mygame/game/GameModule.scala delete mode 100644 src/main/scala/wow/doge/mygame/game/appstates/MyBaseState.scala delete mode 100644 src/main/scala/wow/doge/mygame/game/appstates/ScriptingEngineState.scala delete mode 100644 src/main/scala/wow/doge/mygame/game/appstates/TestAppState.scala delete mode 100644 src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/ActionObservable.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/FontIcon.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXButton.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXDialog.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXListCell.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXListView.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXProgressBar.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXRippler.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXSpinner.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXTextArea.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXTextField.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/JFXTreeTableView.scala create mode 100644 src/main/scala/wow/doge/mygame/utils/controls/MenuItem.scala diff --git a/.gitignore b/.gitignore index 9f53ee9..8c1f4d7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ metals.sbt .metals .bloop .ammonite +.bsp # Scala-IDE specific .scala_dependencies diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 0000000..58e2889 --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,8 @@ +rules = [ + # ScalalintClasses, + # ScalalintImports, + # ScalalintPackages, + # ScalalintInference, + OrganizeImports +] +# ScalalintClasses.removeEmptyConstructor = false \ No newline at end of file diff --git a/build.sbt b/build.sbt index 83be22c..1f3d5fb 100644 --- a/build.sbt +++ b/build.sbt @@ -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" @@ -67,13 +67,20 @@ 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" ), // 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 +133,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 @@ -139,3 +147,33 @@ inThisBuild( ) ) 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.Nothing, +// Wart.Serializable +// ) + +// wartremoverWarnings in (Compile, compile) ++= Seq(Wart.Any, Wart.Serializable) + +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) diff --git a/project/build.properties b/project/build.properties index 0837f7a..0b2e09c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.13 +sbt.version=1.4.7 diff --git a/project/plugins.sbt b/project/plugins.sbt index eb7d2e4..b19841a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -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") diff --git a/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala b/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala index 06c8137..6ca1024 100644 --- a/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala +++ b/src/main/scala/org/slf4j/impl/StaticLoggerBuilder.scala @@ -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" => diff --git a/src/main/scala/wow/doge/mygame/AppError.scala b/src/main/scala/wow/doge/mygame/AppError.scala index 0abc428..07438ac 100644 --- a/src/main/scala/wow/doge/mygame/AppError.scala +++ b/src/main/scala/wow/doge/mygame/AppError.scala @@ -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 { diff --git a/src/main/scala/wow/doge/mygame/Main.scala b/src/main/scala/wow/doge/mygame/Main.scala index f0d6d56..cc67f38 100644 --- a/src/main/scala/wow/doge/mygame/Main.scala +++ b/src/main/scala/wow/doge/mygame/Main.scala @@ -2,17 +2,18 @@ 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 diff --git a/src/main/scala/wow/doge/mygame/MainApp.scala b/src/main/scala/wow/doge/mygame/MainApp.scala index dffa404..dfb389f 100644 --- a/src/main/scala/wow/doge/mygame/MainApp.scala +++ b/src/main/scala/wow/doge/mygame/MainApp.scala @@ -1,5 +1,7 @@ package wow.doge.mygame +import java.util.concurrent.TimeoutException + import scala.concurrent.duration._ import akka.actor.typed.ActorRef @@ -8,6 +10,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 +32,14 @@ import monix.bio.IO import monix.bio.Task import monix.bio.UIO import monix.eval.Coeval +import monix.execution.cancelables.CompositeCancelable import monix.execution.exceptions.DummyException import monix.reactive.Observable +import scalafx.scene.control.Label import scalafx.scene.control.TextArea +import scalafx.scene.layout.HBox +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 @@ -59,12 +68,16 @@ 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.AssetManager import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace class MainApp( @@ -79,8 +92,8 @@ 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).init2 val eventsModule = new EventsModule(scheduler, spawnProtocol) @@ -101,7 +114,9 @@ class MainApp( IOUtils .toIO( obs - .doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void) + .doOnNextF(pme => + Coeval(pprint.log(show"Received event $pme")).void + ) .completedL .startAndForget ) @@ -114,7 +129,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 +137,7 @@ class MainApp( // maxHeight = 150 // maxWidth = 300 }) + // _ <- Task(consoleStream := consoleTextArea) // _ <- Task(jfxUI += consoleTextArea) _ <- logger.infoU("after") @@ -132,14 +148,48 @@ class MainApp( .executeOn(gameApp.scheduler.value) } yield fib + // val k = new FunctionK[Task, UIO] { + + // override def apply[A](fa: monix.bio.Task[A]): monix.bio.UIO[A] = + // fa.hideErrors + + // } + 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 { + case Right(gameApp -> gameAppFib) => + eval(tickEventBus, gameApp, gameAppFib).attempt + case Left(error) => IO.terminate(new Exception(error.toString)) + } + 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 _ => monix.eval.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 @@ -185,7 +235,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,7 +254,7 @@ class MainAppDelegate( os.rel / "assets" / "town.zip", classOf[ZipLocator] ) - _ <- loggerL.infoU("test") + // _ <- Task(consoleStream.println("text")) level <- DefaultGameLevel(assetManager, viewPort) _ <- level.addToGame(rootNode, physicsSpace) @@ -231,45 +282,44 @@ 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(PlayerActorSupervisor.TakeDamage(event.amount, _)) + .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 +332,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 +345,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,6 +372,63 @@ class MainAppDelegate( // ) // .executeOn(appScheduler) // .startAndForget + statsObs <- + playerActor + .askL(PlayerActorSupervisor.GetStatsObservable(_)) + .onErrorHandleWith(TimeoutError.from) + playerHud <- + UIO + .deferAction(implicit s => + UIO(new VBox { + implicit val c = CompositeCancelable() + spacing = 10 + style = """-fx-background-color: rgba(0,0,0,0.7);""" + children = List( + new HBox { + spacing = 5 + children = List( + new Label("Health") { textFill = Color.White }, + new Label("100") { + text == "hello" + text <-- statsObs + .doOnNextF(i => loggerL.debug(show"Received stats: $i")) + .map(_.hp.toInt.toString) + textFill = Color.White + }, + new JFXProgressBar { + progress = 100 + minHeight = 10 + progress <-- statsObs + .map(_.hp.toInt.toDouble / 100) + } + ) + }, + new HBox { + spacing = 5 + children = List( + new Label("Stamina") { + textFill = Color.White + + }, + new Label("100") { + textFill = Color.White + text <-- statsObs + .doOnNextF(i => loggerL.debug(show"Received stats: $i")) + .map(_.stamina.toInt.toString) + }, + new JFXProgressBar { + progress = 100 + minHeight = 10 + progress <-- statsObs + .map(_.stamina.toInt.toDouble / 100) + } + ) + } + ) + }) + ) + .executeOn(schedulers.fx.value) + _ <- UIO(jfxUI += playerHud) } yield () def createPlayerController( @@ -369,70 +476,34 @@ 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) playerActor <- wire[PlayerController.Props].create - obs <- - playerActor - .askL(PlayerActorSupervisor.GetStatsObservable2) - .onErrorHandleWith(TimeoutError.from) - _ <- - obs - .doOnNext(s => loggerL.debug(s"Got state $s").toTask) - .completedL - .toIO - .hideErrors - .startAndForget } yield playerActor } @@ -456,7 +527,7 @@ class MainAppDelegate( npcName, initialPos ).behavior, - actorName = Some(s"${npcName}-npcActorSupervisor") + actorName = Some(show"${npcName}-npcActorSupervisor") ) (for { diff --git a/src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala b/src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala index eb24612..6aae599 100644 --- a/src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala +++ b/src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala b/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala index b10b419..20798da 100644 --- a/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala +++ b/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala @@ -47,6 +47,7 @@ object JMEExecutorService extends GUIExecutorService { } object JMERunner { + @SuppressWarnings(Array("org.wartremover.warts.Null")) var runner: Application = null } diff --git a/src/main/scala/wow/doge/mygame/executors/Schedulers.scala b/src/main/scala/wow/doge/mygame/executors/Schedulers.scala index a8bee68..8791ee5 100644 --- a/src/main/scala/wow/doge/mygame/executors/Schedulers.scala +++ b/src/main/scala/wow/doge/mygame/executors/Schedulers.scala @@ -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) } diff --git a/src/main/scala/wow/doge/mygame/game/GameApp.scala b/src/main/scala/wow/doge/mygame/game/GameApp.scala index 27d35bb..d2a1d63 100644 --- a/src/main/scala/wow/doge/mygame/game/GameApp.scala +++ b/src/main/scala/wow/doge/mygame/game/GameApp.scala @@ -8,8 +8,10 @@ import akka.actor.typed.Props import akka.actor.typed.SpawnProtocol import akka.actor.typed.scaladsl.AskPattern._ import akka.util.Timeout +import cats.Show import cats.effect.Resource import cats.effect.concurrent.Deferred +import cats.syntax.show._ import com.jme3.bullet.BulletAppState import com.jme3.input.InputManager import com.jme3.scene.Node @@ -177,6 +179,10 @@ object SpawnSystem { result: Deferred[Task, Result] ) + object SpawnRequestWrapper { + implicit val show = Show.fromToString[SpawnRequestWrapper] + } + def apply(logger: Logger[Task]) = for { spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper] @@ -213,7 +219,7 @@ class SpawnSystem( for { _ <- logger - .debug(s"Received spawn request $message") + .debug(show"Received spawn request $message") _ <- handleSpawn(message) } yield receive(consumer) case Left(r) => diff --git a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala index ea705f2..c2e1a43 100644 --- a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala +++ b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala @@ -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") diff --git a/src/main/scala/wow/doge/mygame/game/GameModule.scala b/src/main/scala/wow/doge/mygame/game/GameModule.scala deleted file mode 100644 index 8f717a8..0000000 --- a/src/main/scala/wow/doge/mygame/game/GameModule.scala +++ /dev/null @@ -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")) -// } diff --git a/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala b/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala index 7193802..0091ac5 100644 --- a/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala +++ b/src/main/scala/wow/doge/mygame/game/SimpleAppExt.scala @@ -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() diff --git a/src/main/scala/wow/doge/mygame/game/TestActor.scala b/src/main/scala/wow/doge/mygame/game/TestActor.scala index 70f6208..6a05495 100644 --- a/src/main/scala/wow/doge/mygame/game/TestActor.scala +++ b/src/main/scala/wow/doge/mygame/game/TestActor.scala @@ -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 // } } diff --git a/src/main/scala/wow/doge/mygame/game/appstates/MyBaseState.scala b/src/main/scala/wow/doge/mygame/game/appstates/MyBaseState.scala deleted file mode 100644 index 4d93a3f..0000000 --- a/src/main/scala/wow/doge/mygame/game/appstates/MyBaseState.scala +++ /dev/null @@ -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) - } - } - -} diff --git a/src/main/scala/wow/doge/mygame/game/appstates/ScriptingEngineState.scala b/src/main/scala/wow/doge/mygame/game/appstates/ScriptingEngineState.scala deleted file mode 100644 index c9008eb..0000000 --- a/src/main/scala/wow/doge/mygame/game/appstates/ScriptingEngineState.scala +++ /dev/null @@ -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") - } - - } - - } -} diff --git a/src/main/scala/wow/doge/mygame/game/appstates/TestAppState.scala b/src/main/scala/wow/doge/mygame/game/appstates/TestAppState.scala deleted file mode 100644 index 8f5e804..0000000 --- a/src/main/scala/wow/doge/mygame/game/appstates/TestAppState.scala +++ /dev/null @@ -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 - } -} diff --git a/src/main/scala/wow/doge/mygame/game/controls/CameraMovementControls.scala b/src/main/scala/wow/doge/mygame/game/controls/CameraMovementControls.scala index f2d3527..29656e4 100644 --- a/src/main/scala/wow/doge/mygame/game/controls/CameraMovementControls.scala +++ b/src/main/scala/wow/doge/mygame/game/controls/CameraMovementControls.scala @@ -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) diff --git a/src/main/scala/wow/doge/mygame/game/entities/CharacterStats.scala b/src/main/scala/wow/doge/mygame/game/entities/CharacterStats.scala index 1f79e09..9d843fc 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/CharacterStats.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/CharacterStats.scala @@ -4,14 +4,18 @@ 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 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) @@ -22,9 +26,9 @@ object CharacterStats { 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) @@ -53,18 +57,25 @@ object CharacterStats { // } // } + implicit val show = Show.fromToString[CharacterStats] + } object StatsActor { sealed trait Command -// case class TakeDamage(value: Int) extends Command - case class TakeDamageResult( +// final case class TakeDamage(value: Int) extends Command + final case class TakeDamageResult( value: CharacterStats.DamageHealth, replyTo: ActorRef[(Boolean, 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[(Boolean, CharacterStats)] + ) extends Command + final case class HealResult(value: HealHealth) extends Command + final case class CurrentStats(replyTo: ActorRef[CharacterStats]) + extends Command class Props( startingHealth: CharacterStats.Health, @@ -74,12 +85,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,11 +111,24 @@ 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 ! true -> s.stats + s } else { - replyTo ! false -> state.stats - state.modify(_.stats.hp).using(_ - value) + val s = state.modify(_.stats.hp).using(_ - value) + replyTo ! false -> s.stats + s + } + receive(nextState) + case ConsumeStaminaResult(value, replyTo) => + val nextState = if ((state.stats.stamina - value).toInt <= 0) { + val s = state.modify(_.stats.stamina).setTo(Stamina(0)) + replyTo ! false -> s.stats + s + } else { + val s = state.modify(_.stats.stamina).using(_ - value) + replyTo ! false -> s.stats + s } receive(nextState) case HealResult(value) => diff --git a/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala index 1cc598f..95186c2 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/NpcActorSupervisor.scala @@ -10,6 +10,7 @@ import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import akka.util.Timeout +import cats.syntax.eq._ import cats.syntax.show._ import monix.execution.CancelableFuture import monix.execution.CancelablePromise @@ -39,8 +40,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], @@ -86,7 +87,7 @@ class NpcActorSupervisor( def idle(state: State): Behavior[NpcActorSupervisor.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( @@ -113,7 +114,7 @@ class NpcActorSupervisor( signal: CancelableFuture[NpcMovementActor.DoneMoving.type] ): Behavior[NpcActorSupervisor.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 +151,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 +159,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 +213,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) @@ -262,19 +263,19 @@ object NpcMovementActorNotUsed { case event: EntityMovementEvent => event match { case MovedLeft(name, pressed) => - if (name == npcName) + if (name === npcName) movementActor ! ImMovementActor.MoveLeft(pressed) Behaviors.same case MovedUp(name, pressed) => - if (name == npcName) + if (name === npcName) movementActor ! ImMovementActor.MoveUp(pressed) Behaviors.same case MovedRight(name, pressed) => - if (name == npcName) + if (name === npcName) movementActor ! ImMovementActor.MoveRight(pressed) Behaviors.same case MovedDown(name, pressed) => - if (name == npcName) + if (name === npcName) movementActor ! ImMovementActor.MoveDown(pressed) Behaviors.same } diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala index 788f208..77b71a0 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala @@ -12,7 +12,10 @@ import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import akka.util.Timeout +import cats.syntax.show._ import com.typesafe.scalalogging.Logger +import monix.eval.Coeval +import monix.execution.AsyncQueue import monix.reactive.Observable import monix.reactive.OverflowStrategy import monix.reactive.subjects.ConcurrentSubject @@ -27,8 +30,6 @@ 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] @@ -40,28 +41,29 @@ object PlayerActorSupervisor { } sealed trait Command - case class TakeDamage(value: CharacterStats.DamageHealth) extends Command - case class TakeDamage2( + final case class TakeDamage( 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]]) + final case class ConsumeStamina( + value: CharacterStats.DamageStamina, + replyTo: ActorRef[Unit] + ) extends Command + final case class Heal(value: CharacterStats.HealHealth) 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[Observable[CharacterStats]] + ) extends Command private case object Die extends Command - private case class DamageResponse(response: (Boolean, CharacterStats)) - extends Command - private case class DamageResponse2( + private final case class DamageResponse( 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 + // private final case class InternalTakeDamage(old: Int, value: Int) extends Command + private final case class LogError(ex: Throwable) extends Command class Props( val playerEventBus: GameEventBus[PlayerEvent], val tickEventBus: GameEventBus[TickEvent], @@ -80,6 +82,15 @@ object PlayerActorSupervisor { ctx.log.infoP("Starting PlayerActor") // spawn children actors + + val playerStatsActor = + ctx.spawnN( + new StatsActor.Props( + CharacterStats.Health(100), + CharacterStats.Stamina(100) + ).behavior + ) + val playerMovementActor = ctx.spawnN( Behaviors @@ -90,14 +101,6 @@ object PlayerActorSupervisor { Dispatchers.jmeDispatcher ) - val playerStatsActor = - ctx.spawnN( - new StatsActor.Props( - CharacterStats.Health(100), - CharacterStats.Stamina(100) - ).behavior - ) - val playerMovementEl = ctx.spawnN( Behaviors .supervise(PlayerMovementEventListener(playerMovementActor)) @@ -146,7 +149,7 @@ object PlayerActorSupervisor { } - case class Children( + final case class Children( movementActor: ActorRef[ImMovementActor.Command], statsActor: ActorRef[StatsActor.Command] ) @@ -163,28 +166,27 @@ class PlayerActorSupervisor( val aliveState = Behaviors .receiveMessage[Command] { - case TakeDamage(value) => + case TakeDamage(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) => DamageResponse(response) + case Success(response) => DamageResponse(response, replyTo) 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 ConsumeStamina(value, replyTo) => + ctx.ask( + children.statsActor, + StatsActor.ConsumeStaminaResult(value, _) + ) { + case Success(response) => DamageResponse(response, replyTo) case Failure(ex) => LogError(ex) } Behaviors.same + Behaviors.same case CurrentStats(replyTo) => // ctx.ask(children.statsActor, StatsActor.CurrentStats()) children.statsActor ! StatsActor.CurrentStats(replyTo) @@ -203,28 +205,21 @@ class PlayerActorSupervisor( // 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) => + case DamageResponse(response, replyTo) => response match { case (dead, stats) => if (dead) ctx.self ! Die statsQueue .offer(stats) - .foreach(_ => replyTo ! ())(props.scheduler.value) + .foreach { _ => + pprint.log(show"Published stats $stats") + replyTo ! () + }(props.scheduler.value) } Behaviors.same case Die => deadState diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala index 0bcfa80..f1dac69 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala @@ -121,7 +121,7 @@ object PlayerActorSupervisor2 { } - case class Children( + final case class Children( movementActor: ActorRef[ImMovementActor.Command] ) } diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerCameraActor.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerCameraActor.scala index 2c735c6..51c27bc 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerCameraActor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerCameraActor.scala @@ -37,7 +37,7 @@ object PlayerCameraActor { ) } - case class State() + final case class State() object State { val empty = State() } diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala index 42a20dd..1f99dee 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala @@ -27,7 +27,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 diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala b/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala index 3b097f0..4b6fafa 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/ai/GdxAiTest.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala index ea65998..68bb6b2 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputEnums.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala b/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala index ef3c145..d1fbe30 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/level/DefaultGameLevel.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala b/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala index 9176f4c..b5c6107 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/level/GameLevel.scala @@ -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, diff --git a/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala b/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala index 5120168..7c957af 100644 --- a/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala +++ b/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala @@ -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 => diff --git a/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala b/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala index 75d3ae7..2b67586 100644 --- a/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala +++ b/src/main/scala/wow/doge/mygame/implicits/JavaFXMonixObservables.scala @@ -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 + } + } + } } diff --git a/src/main/scala/wow/doge/mygame/implicits/package.scala b/src/main/scala/wow/doge/mygame/implicits/package.scala index 41d6bed..e984c27 100644 --- a/src/main/scala/wow/doge/mygame/implicits/package.scala +++ b/src/main/scala/wow/doge/mygame/implicits/package.scala @@ -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] } diff --git a/src/main/scala/wow/doge/mygame/launcher/Launcher.scala b/src/main/scala/wow/doge/mygame/launcher/Launcher.scala index f0c0b03..456160d 100644 --- a/src/main/scala/wow/doge/mygame/launcher/Launcher.scala +++ b/src/main/scala/wow/doge/mygame/launcher/Launcher.scala @@ -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 = diff --git a/src/main/scala/wow/doge/mygame/math/ImVector3f.scala b/src/main/scala/wow/doge/mygame/math/ImVector3f.scala index f56ba75..e80d0e3 100644 --- a/src/main/scala/wow/doge/mygame/math/ImVector3f.scala +++ b/src/main/scala/wow/doge/mygame/math/ImVector3f.scala @@ -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) diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala b/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala index 28adf7d..37c958a 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala @@ -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 { diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala b/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala index f54101b..aa75f3e 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/Events.scala @@ -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] } diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala deleted file mode 100644 index 1016f29..0000000 --- a/src/main/scala/wow/doge/mygame/subsystems/events/PlayerCameraEvents.scala +++ /dev/null @@ -1 +0,0 @@ -package wow.doge.mygame.subsystems.events diff --git a/src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala index 951041c..af7d1f2 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/events/PlayerEvents.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala b/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala index bdc7a62..9e94528 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/moddingsystem/ModdingSystem.scala @@ -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) @@ -61,7 +61,7 @@ object ModdingSystem { def findPluginFiles(dir: os.Path): View[os.Path] = os.list(dir) .view - .filter(f => f.ext == "json" && f.baseName.endsWith("plugin")) + .filter(f => f.ext === "json" && f.baseName.endsWith("plugin")) def findAndReadPluginFiles( dir: os.Path, @@ -215,7 +215,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)], diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/MonixScriptCompiler.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/MonixScriptCompiler.scala index 0e5c088..2ae5ed1 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/MonixScriptCompiler.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/MonixScriptCompiler.scala @@ -10,28 +10,31 @@ 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._ 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) = @@ -52,6 +55,7 @@ class ScriptCompiler private ( // def tell(item: Command) = queue.offer(item) } +@SuppressWarnings(Array("org.wartremover.warts.Any")) object ScriptCompiler { sealed trait State @@ -79,15 +83,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 +120,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 +140,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 @@ -238,7 +239,7 @@ object ScriptCompiler { 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 { @@ -253,7 +254,6 @@ object ScriptCompiler { ) ) } -// override private val final class ScriptCompilerWorker( logger: Logger[Task], _queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest] @@ -266,6 +266,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] @@ -287,13 +291,13 @@ 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]): Resource[UIO, ScriptCompiler] = { def acquire(worker: ScriptCompilerWorker) = for { queue <- ConcurrentQueue.bounded[Task, Command](10) @@ -302,7 +306,9 @@ object ScriptCompiler { ScriptCompilerWorker(logger) .flatMap(worker => - Resource.make(acquire(worker)) { case (_, fib) => fib.cancel } + Resource.make(acquire(worker).hideErrors) { + case (_, fib) => fib.cancel + } ) .map(_._1) } diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala index 6ab54b1..78647bf 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala @@ -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 } diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala index f36fc3b..61e05b6 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala @@ -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()) diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala index e1185d2..fed3c58 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptSystemModule.scala @@ -4,7 +4,11 @@ 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 @@ -19,6 +23,7 @@ object ScriptInitMode { } class ScriptSystemResource( path: os.Path, + logger: Logger[Task], mode: ScriptInitMode = ScriptInitMode.Lazy )(implicit spawnProtocol: ActorRef[SpawnProtocol.Command], @@ -36,13 +41,17 @@ class ScriptSystemResource( ) } yield scriptCacheActor + val init2: Resource[UIO, ScriptCompiler] = for { + sc <- ScriptCompiler(logger) + } 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 diff --git a/src/main/scala/wow/doge/mygame/types/package.scala b/src/main/scala/wow/doge/mygame/types/package.scala index 19087b8..47a4982 100644 --- a/src/main/scala/wow/doge/mygame/types/package.scala +++ b/src/main/scala/wow/doge/mygame/types/package.scala @@ -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) } diff --git a/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala b/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala index ca39707..33c4938 100644 --- a/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala +++ b/src/main/scala/wow/doge/mygame/utils/GenericConsoleStream.scala @@ -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() } diff --git a/src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala b/src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala index 51d8857..0318e77 100644 --- a/src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala +++ b/src/main/scala/wow/doge/mygame/utils/GenericTimerActor.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala b/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala index f7365a6..8e35dc2 100644 --- a/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala +++ b/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala @@ -1,33 +1,65 @@ 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) = + // println(show"$file got created") + if (sub.onNext(CreateEvent(file, count)) == Ack.Stop) + c.cancel() + override def onModify(file: File, count: Int) = { + pprint.log(show"${file.toString} got modified $count times") + if (sub.onNext(ModifyEvent(file, count)) == Ack.Stop) + c.cancel() + } + override def onDelete(file: File, count: Int) = + // println(show"$file got deleted") + if (sub.onNext(DeleteEvent(file, count)) == Ack.Stop) + c.cancel() - } + } + watcher.start()(scheduler) + c := Cancelable(() => watcher.stop()) + c + } + .publish + .refCount + ) + ) } diff --git a/src/main/scala/wow/doge/mygame/utils/ReaderDemo.scala b/src/main/scala/wow/doge/mygame/utils/ReaderDemo.scala index 45e9976..3ca039d 100644 --- a/src/main/scala/wow/doge/mygame/utils/ReaderDemo.scala +++ b/src/main/scala/wow/doge/mygame/utils/ReaderDemo.scala @@ -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))) diff --git a/src/main/scala/wow/doge/mygame/utils/Settings.scala b/src/main/scala/wow/doge/mygame/utils/Settings.scala index 4e06f70..ac7b052 100644 --- a/src/main/scala/wow/doge/mygame/utils/Settings.scala +++ b/src/main/scala/wow/doge/mygame/utils/Settings.scala @@ -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) diff --git a/src/main/scala/wow/doge/mygame/utils/TreeTest.scala b/src/main/scala/wow/doge/mygame/utils/TreeTest.scala index 2f112ee..d53a6d6 100644 --- a/src/main/scala/wow/doge/mygame/utils/TreeTest.scala +++ b/src/main/scala/wow/doge/mygame/utils/TreeTest.scala @@ -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) diff --git a/src/main/scala/wow/doge/mygame/utils/controls/ActionObservable.scala b/src/main/scala/wow/doge/mygame/utils/controls/ActionObservable.scala new file mode 100644 index 0000000..c21298a --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/ActionObservable.scala @@ -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 + +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/FontIcon.scala b/src/main/scala/wow/doge/mygame/utils/controls/FontIcon.scala new file mode 100644 index 0000000..1c0d507 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/FontIcon.scala @@ -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") +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXButton.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXButton.scala new file mode 100644 index 0000000..4301f7a --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXButton.scala @@ -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) + +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXDialog.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXDialog.scala new file mode 100644 index 0000000..81060d6 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXDialog.scala @@ -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 +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXListCell.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXListCell.scala new file mode 100644 index 0000000..be97502 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXListCell.scala @@ -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 + +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXListView.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXListView.scala new file mode 100644 index 0000000..f46d527 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXListView.scala @@ -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 + +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXProgressBar.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXProgressBar.scala new file mode 100644 index 0000000..2b962de --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXProgressBar.scala @@ -0,0 +1,11 @@ +package wow.doge.mygame.utils.controls +// import com.jfoenix.controls.JFXProgressBar +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) +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXRippler.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXRippler.scala new file mode 100644 index 0000000..955276f --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXRippler.scala @@ -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 +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXSpinner.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXSpinner.scala new file mode 100644 index 0000000..ec38a50 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXSpinner.scala @@ -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) + +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXTextArea.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXTextArea.scala new file mode 100644 index 0000000..60170ed --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXTextArea.scala @@ -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) + +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXTextField.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXTextField.scala new file mode 100644 index 0000000..b5ec964 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXTextField.scala @@ -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) +} diff --git a/src/main/scala/wow/doge/mygame/utils/controls/JFXTreeTableView.scala b/src/main/scala/wow/doge/mygame/utils/controls/JFXTreeTableView.scala new file mode 100644 index 0000000..a9711d6 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/JFXTreeTableView.scala @@ -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 diff --git a/src/main/scala/wow/doge/mygame/utils/controls/MenuItem.scala b/src/main/scala/wow/doge/mygame/utils/controls/MenuItem.scala new file mode 100644 index 0000000..5f04ee0 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/controls/MenuItem.scala @@ -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) +} diff --git a/src/main/scala/wow/doge/mygame/utils/wrappers/jme/AssetManager.scala b/src/main/scala/wow/doge/mygame/utils/wrappers/jme/AssetManager.scala index f9c6bb9..a3a6648 100644 --- a/src/main/scala/wow/doge/mygame/utils/wrappers/jme/AssetManager.scala +++ b/src/main/scala/wow/doge/mygame/utils/wrappers/jme/AssetManager.scala @@ -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] diff --git a/src/main/scala/wow/doge/mygame/utils/wrappers/jme/CollisionShapeFactory.scala b/src/main/scala/wow/doge/mygame/utils/wrappers/jme/CollisionShapeFactory.scala index e8b88a5..99e9a13 100644 --- a/src/main/scala/wow/doge/mygame/utils/wrappers/jme/CollisionShapeFactory.scala +++ b/src/main/scala/wow/doge/mygame/utils/wrappers/jme/CollisionShapeFactory.scala @@ -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] diff --git a/src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala b/src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala index be68b03..b737ad6 100644 --- a/src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala +++ b/src/test/scala/wow/doge/mygame/ActorTimeoutTest.scala @@ -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 = diff --git a/src/test/scala/wow/doge/mygame/AnimTest.scala b/src/test/scala/wow/doge/mygame/AnimTest.scala index 2ad6811..cb613f1 100644 --- a/src/test/scala/wow/doge/mygame/AnimTest.scala +++ b/src/test/scala/wow/doge/mygame/AnimTest.scala @@ -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 diff --git a/src/test/scala/wow/doge/mygame/AssetManagerTest.scala b/src/test/scala/wow/doge/mygame/AssetManagerTest.scala index 988581b..0fe7d4d 100644 --- a/src/test/scala/wow/doge/mygame/AssetManagerTest.scala +++ b/src/test/scala/wow/doge/mygame/AssetManagerTest.scala @@ -1,18 +1,19 @@ package wow.doge.mygame -import org.scalatest.funsuite.AnyFunSuite -import monix.execution.Scheduler.Implicits.global +import scala.annotation.nowarn + 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 com.jme3.{asset => jmea} +import monix.execution.Scheduler.Implicits.global +import org.scalatest.funsuite.AnyFunSuite 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") diff --git a/src/test/scala/wow/doge/mygame/CollisionShapeFactoryTest.scala b/src/test/scala/wow/doge/mygame/CollisionShapeFactoryTest.scala index 8a7722f..d3ce03c 100644 --- a/src/test/scala/wow/doge/mygame/CollisionShapeFactoryTest.scala +++ b/src/test/scala/wow/doge/mygame/CollisionShapeFactoryTest.scala @@ -1,15 +1,17 @@ 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.scene.SceneGraphVisitor import java.util.Queue -import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory -import monix.execution.Scheduler.Implicits.global + import cats.syntax.eq._ +import com.jme3.bounding.BoundingVolume +import com.jme3.collision.Collidable +import com.jme3.collision.CollisionResults +import com.jme3.scene.SceneGraphVisitor +import com.jme3.scene.Spatial +import com.jme3.scene.Spatial.DFSMode +import monix.execution.Scheduler.Implicits.global +import org.scalatest.funsuite.AnyFunSuite +import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory class CollisionShapeFactoryTest extends AnyFunSuite { test("Test for WrongArgumentError") { diff --git a/src/test/scala/wow/doge/mygame/FileWatcherTest.scala b/src/test/scala/wow/doge/mygame/FileWatcherTest.scala index d55f1d6..016de09 100644 --- a/src/test/scala/wow/doge/mygame/FileWatcherTest.scala +++ b/src/test/scala/wow/doge/mygame/FileWatcherTest.scala @@ -1,30 +1,62 @@ 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.eval.Task +import monix.reactive.Observable +import org.scalatest.funsuite.AnyFunSuite +import wow.doge.mygame.implicits._ +import wow.doge.mygame.utils.MonixDirectoryWatcher - 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 AnyFunSuite { + // 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") import monix.execution.Scheduler.Implicits.global - CResource - .make(Task { watcher.start(); watcher })(w => Task(w.stop())) - .use(_ => Task.never) - .runSyncUnsafe(10.seconds) + obsT + .flatMap( + _.takeUntil(Observable.unit.delayExecution(2.seconds)) + .doOnNext { + case MonixDirectoryWatcher.CreateEvent(file, count) => + Task(println(show"${file.toString} got created")) + case MonixDirectoryWatcher.DeleteEvent(file, count) => + Task(println(show"${file.toString} got deleted")) + case MonixDirectoryWatcher.ModifyEvent(file, count) => + Task(pprint.log(show"${file.toString} got modified $count times")) + + } + .completedL + .flatMap(_ => Task.sleep(3.seconds)) + .toIO + ) + .onErrorHandleWith { + case ex: java.nio.file.ClosedWatchServiceException => IO.unit + case ex: java.lang.UnsupportedOperationException => IO.unit + } + .runSyncUnsafe(16.seconds) } } diff --git a/src/test/scala/wow/doge/mygame/ImVector3fTest.scala b/src/test/scala/wow/doge/mygame/ImVector3fTest.scala index 46853d6..a43bc2a 100644 --- a/src/test/scala/wow/doge/mygame/ImVector3fTest.scala +++ b/src/test/scala/wow/doge/mygame/ImVector3fTest.scala @@ -1,10 +1,10 @@ package wow.doge.mygame -import org.scalatest.funsuite.AnyFunSuite -import wow.doge.mygame.math.ImVector3f -import com.typesafe.scalalogging.LazyLogging import cats.syntax.eq._ import cats.syntax.show._ +import com.typesafe.scalalogging.LazyLogging +import org.scalatest.funsuite.AnyFunSuite +import wow.doge.mygame.math.ImVector3f class ImVector3fTest extends AnyFunSuite with LazyLogging { test("maxvalue") { diff --git a/src/test/scala/wow/doge/mygame/ModdingSystemTest.scala b/src/test/scala/wow/doge/mygame/ModdingSystemTest.scala index b3d19d2..2f882e1 100644 --- a/src/test/scala/wow/doge/mygame/ModdingSystemTest.scala +++ b/src/test/scala/wow/doge/mygame/ModdingSystemTest.scala @@ -1,11 +1,11 @@ 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 monix.execution.Scheduler.Implicits.global +import org.scalatest.funsuite.AnyFunSuite +import wow.doge.mygame.subsystems.moddingsystem.ModdingSystem class ModdingSystemTest extends AnyFunSuite { val printer = Printer.spaces2 diff --git a/src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala b/src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala index ef24806..d5f982b 100644 --- a/src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala +++ b/src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala @@ -1,12 +1,12 @@ 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._ +import monix.bio.Task +import monix.execution.Scheduler.Implicits.global +import org.scalatest.funsuite.AnyFunSuite +import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler class MonixScriptCompilerTest extends AnyFunSuite { @@ -16,13 +16,13 @@ class MonixScriptCompilerTest extends AnyFunSuite { 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( + ScriptCompiler.GetScript( os.pwd / "assets" / "scripts" / "scala" / "hello2.sc", _, false diff --git a/src/test/scala/wow/doge/mygame/ReaderT_Test.scala b/src/test/scala/wow/doge/mygame/ReaderT_Test.scala index dc20690..afd8e42 100644 --- a/src/test/scala/wow/doge/mygame/ReaderT_Test.scala +++ b/src/test/scala/wow/doge/mygame/ReaderT_Test.scala @@ -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))) diff --git a/src/test/scala/wow/doge/mygame/ReaderTest.scala b/src/test/scala/wow/doge/mygame/ReaderTest.scala index 2230c6f..867b9a3 100644 --- a/src/test/scala/wow/doge/mygame/ReaderTest.scala +++ b/src/test/scala/wow/doge/mygame/ReaderTest.scala @@ -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)))