This commit is contained in:
Rohan Sircar 2021-01-16 18:50:32 +05:30
parent 2e05cb35fe
commit 89fad19d99
34 changed files with 725 additions and 435 deletions

View File

@ -1,23 +1,4 @@
// The simplest possible sbt build file is just one line:
scalaVersion := "2.13.3" scalaVersion := "2.13.3"
// That is, to create a valid sbt build, all you've got to do is define the
// version of Scala you'd like your project to use.
// ============================================================================
// Lines like the above defining `scalaVersion` are called "settings". Settings
// are key/value pairs. In the case of `scalaVersion`, the key is "scalaVersion"
// and the value is "2.13.1"
// It's possible to define many kinds of settings, such as:
// Note, it's not required for you to define these three settings. These are
// mostly only necessary if you intend to publish your library's binaries on a
// place like Sonatype or Bintray.
// Want to use a published library in your project?
// You can define other libraries as dependencies in your build like this:
resolvers += "Jcenter" at "https://jcenter.bintray.com/" resolvers += "Jcenter" at "https://jcenter.bintray.com/"
resolvers += "JME Bintray" at "https://bintray.com/jmonkeyengine/com.jme3" resolvers += "JME Bintray" at "https://bintray.com/jmonkeyengine/com.jme3"
@ -49,12 +30,8 @@ lazy val root = (project in file(".")).settings(
organization := "wow.doge", organization := "wow.doge",
version := "1.0-SNAPSHOT", version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
// "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2",
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-core
"org.jmonkeyengine" % "jme3-core" % jmeVersion, "org.jmonkeyengine" % "jme3-core" % jmeVersion,
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-desktop
"org.jmonkeyengine" % "jme3-desktop" % jmeVersion, "org.jmonkeyengine" % "jme3-desktop" % jmeVersion,
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-lwjgl3
"org.jmonkeyengine" % "jme3-lwjgl3" % jmeVersion, "org.jmonkeyengine" % "jme3-lwjgl3" % jmeVersion,
"org.jmonkeyengine" % "jme3-effects" % jmeVersion, "org.jmonkeyengine" % "jme3-effects" % jmeVersion,
"org.jmonkeyengine" % "jme3-plugins" % jmeVersion, "org.jmonkeyengine" % "jme3-plugins" % jmeVersion,
@ -74,15 +51,15 @@ lazy val root = (project in file(".")).settings(
"io.monix" %% "monix-bio" % "1.1.0", "io.monix" %% "monix-bio" % "1.1.0",
"io.circe" %% "circe-core" % "0.13.0", "io.circe" %% "circe-core" % "0.13.0",
"io.circe" %% "circe-generic" % "0.13.0", "io.circe" %% "circe-generic" % "0.13.0",
"com.softwaremill.sttp.client" %% "core" % "2.2.5", "com.softwaremill.sttp.client3" %% "core" % "3.0.0",
"com.softwaremill.sttp.client" %% "monix" % "2.2.5", "com.softwaremill.sttp.client3" %% "monix" % "3.0.0",
"com.softwaremill.sttp.client" %% "circe" % "2.2.5", "com.softwaremill.sttp.client3" %% "circe" % "3.0.0",
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5", "com.softwaremill.sttp.client3" %% "async-http-client-backend-monix" % "3.0.0",
"com.softwaremill.sttp.client3" %% "httpclient-backend-monix" % "3.0.0",
"com.github.valskalla" %% "odin-monix" % "0.8.1", "com.github.valskalla" %% "odin-monix" % "0.8.1",
"com.github.valskalla" %% "odin-json" % "0.9.1", "com.github.valskalla" %% "odin-json" % "0.9.1",
"com.softwaremill.macwire" %% "util" % "2.3.7", "com.softwaremill.macwire" %% "util" % "2.3.7",
"com.softwaremill.macwire" %% "macros" % "2.3.7" % "provided", "com.softwaremill.macwire" %% "macros" % "2.3.7" % "provided",
// "com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
"com.github.valskalla" %% "odin-slf4j" % "0.8.1", "com.github.valskalla" %% "odin-slf4j" % "0.8.1",
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1", "com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1", "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1",
@ -90,9 +67,6 @@ lazy val root = (project in file(".")).settings(
"io.circe" %% "circe-config" % "0.8.0", "io.circe" %% "circe-config" % "0.8.0",
"com.beachape" %% "enumeratum-circe" % "1.6.1", "com.beachape" %% "enumeratum-circe" % "1.6.1",
"com.lihaoyi" %% "os-lib" % "0.7.1", "com.lihaoyi" %% "os-lib" % "0.7.1",
// "com.jayfella" % "jme-jfx-11" % "1.1.5",
// "com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0",
// "com.github.Oshan96" % "CustomStage" % "v1.3.1",
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2", "com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
"org.recast4j" % "recast" % "1.2.5", "org.recast4j" % "recast" % "1.2.5",
"org.recast4j" % "detour" % "1.2.5", "org.recast4j" % "detour" % "1.2.5",
@ -118,8 +92,6 @@ lazy val root = (project in file(".")).settings(
"-Xlint", "-Xlint",
"-Ywarn-numeric-widen", "-Ywarn-numeric-widen",
"-Ymacro-annotations", "-Ymacro-annotations",
// "-Xlint:byname-implicit",
// "utf-8", // Specify character encoding used by source files.
//silence warnings for by-name implicits //silence warnings for by-name implicits
"-Wconf:cat=lint-byname-implicit:s", "-Wconf:cat=lint-byname-implicit:s",
//give errors on non exhaustive matches //give errors on non exhaustive matches
@ -159,55 +131,6 @@ lazy val root = (project in file(".")).settings(
} }
) )
initialCommands in (console) := """ammonite.Main.main(Array.empty)""" initialCommands in (console) := """ammonite.Main.main(Array.empty)"""
// Here, `libraryDependencies` is a set of dependencies, and by using `+=`,
// we're adding the scala-parser-combinators dependency to the set of dependencies
// that sbt will go and fetch when it starts up.
// Now, in any Scala file, you can import classes, objects, etc., from
// scala-parser-combinators with a regular import.
// TIP: To find the "dependency" that you need to add to the
// `libraryDependencies` set, which in the above example looks like this:
// "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2"
// You can use Scaladex, an index of all known published Scala libraries. There,
// after you find the library you want, you can just copy/paste the dependency
// information that you need into your build file. For example, on the
// scala/scala-parser-combinators Scaladex page,
// https://index.scala-lang.org/scala/scala-parser-combinators, you can copy/paste
// the sbt dependency from the sbt box on the right-hand side of the screen.
// IMPORTANT NOTE: while build files look _kind of_ like regular Scala, it's
// important to note that syntax in *.sbt files doesn't always behave like
// regular Scala. For example, notice in this build file that it's not required
// to put our settings into an enclosing object or class. Always remember that
// sbt is a bit different, semantically, than vanilla Scala.
// ============================================================================
// Most moderately interesting Scala projects don't make use of the very simple
// build file style (called "bare style") used in this build.sbt file. Most
// intermediate Scala projects make use of so-called "multi-project" builds. A
// multi-project build makes it possible to have different folders which sbt can
// be configured differently for. That is, you may wish to have different
// dependencies or different testing frameworks defined for different parts of
// your codebase. Multi-project builds make this possible.
// Here's a quick glimpse of what a multi-project build looks like for this
// build, with only one "subproject" defined, called `root`:
// lazy val root = (project in file(".")).
// settings(
// inThisBuild(List(
// organization := "ch.epfl.scala",
// scalaVersion := "2.13.1"
// )),
// name := "hello-world"
// )
// To learn more about multi-project builds, head over to the official sbt
// documentation at http://www.scala-sbt.org/documentation.html
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1") addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
addCompilerPlugin( addCompilerPlugin(
"org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full "org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full

View File

@ -1,6 +1,40 @@
package wow.doge.mygame package wow.doge.mygame
import java.util.concurrent.TimeoutException
import cats.data.Reader
import monix.bio.IO
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
object AppError { object AppError {
case class TimeoutError(reason: String) extends AppError final case class TimeoutError(reason: String) extends AppError
object TimeoutError {
def reader =
Reader[Throwable, TimeoutError] {
case ex: TimeoutException => TimeoutError(ex.getMessage)
}
def from: PartialFunction[Throwable, IO[TimeoutError, Nothing]] = {
case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
}
}
final case class DummyError(reason: String) extends AppError
object DummyError {
def reader =
Reader[Throwable, DummyError] {
case ex: NullPointerException => DummyError(ex.getMessage)
}
}
final case class AssetManagerError(error: AssetManager.Error) extends AppError
final case class AppNodeError(error: NodeWrapper2.Error) extends AppError
final case class CollisionShapeCreationFailed(
err: CollisionShapeFactory.Error
) extends AppError
def fromThrowable: PartialFunction[Throwable, IO[AppError, Nothing]] = {
case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
}
} }

View File

@ -35,6 +35,9 @@ object Main extends BIOApp with MainModule {
Formatter.json Formatter.json
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000)) ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
jmeScheduler <- jMESchedulerResource jmeScheduler <- jMESchedulerResource
// backend <- Resource.make(toIO(HttpClientMonixBackend()))(backend =>
// toIO(backend.close())
// )
actorSystem <- actorSystemResource(logger, schedulers.async) actorSystem <- actorSystemResource(logger, schedulers.async)
_ <- Resource.liftF( _ <- Resource.liftF(
new MainApp( new MainApp(

View File

@ -1,19 +1,22 @@
package wow.doge.mygame package wow.doge.mygame
import java.util.concurrent.TimeoutException
import scala.concurrent.duration._
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.ActorSystem
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import cats.effect.Resource import cats.effect.Resource
import cats.effect.concurrent.Deferred import cats.effect.concurrent.Deferred
import cats.syntax.eq._ import cats.syntax.eq._
import com.jme3.app.state.AppStateManager
import com.jme3.asset.plugins.ZipLocator import com.jme3.asset.plugins.ZipLocator
import com.jme3.bullet.control.BetterCharacterControl import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.input.InputManager import com.jme3.input.InputManager
import com.jme3.material.Material import com.jme3.material.Material
import com.jme3.material.MaterialDef import com.jme3.material.MaterialDef
import com.jme3.math.ColorRGBA
import com.jme3.math.FastMath import com.jme3.math.FastMath
import com.jme3.renderer.Camera import com.jme3.renderer.Camera
import com.jme3.renderer.RenderManager import com.jme3.renderer.RenderManager
@ -27,6 +30,8 @@ import monix.bio.Fiber
import monix.bio.IO import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.bio.UIO import monix.bio.UIO
import monix.eval.Coeval
import monix.reactive.Observable
import scalafx.scene.control.TextArea import scalafx.scene.control.TextArea
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.game.GameApp import wow.doge.mygame.game.GameApp
@ -45,23 +50,20 @@ import wow.doge.mygame.implicits._
import wow.doge.mygame.launcher.Launcher import wow.doge.mygame.launcher.Launcher
import wow.doge.mygame.launcher.Launcher.LauncherResult import wow.doge.mygame.launcher.Launcher.LauncherResult
import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.math.ImVector3f
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
import wow.doge.mygame.subsystems.events.EventsModule import wow.doge.mygame.subsystems.events.EventsModule
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.GenericConsoleStream import wow.doge.mygame.utils.GenericConsoleStream
import wow.doge.mygame.utils.wrappers.jme.AppNode import wow.doge.mygame.utils.IOUtils
import wow.doge.mygame.utils.wrappers.jme.AppNode2
import wow.doge.mygame.utils.wrappers.jme.AssetManager import wow.doge.mygame.utils.wrappers.jme.AssetManager
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import monix.reactive.Observable
import monix.eval.Coeval
import wow.doge.mygame.utils.IOUtils
import com.jme3.math.ColorRGBA
class MainApp( class MainApp(
logger: Logger[Task], logger: Logger[Task],
@ -84,53 +86,66 @@ class MainApp(
tickEventBus: GameEventBus[TickEvent] tickEventBus: GameEventBus[TickEvent]
) )
def gameInit: Resource[Task, Fiber[Throwable, Unit]] = def eval(gameApp: GameApp, fib: Fiber[Nothing, Unit]) =
wire[GameAppResource].resource.evalMap { for {
case gameApp -> gameAppFib => // g <- UIO.pure(gameApp)
for { playerEventBus <- eventsModule.playerEventBus
playerEventBus <- eventsModule.playerEventBusTask mainEventBus <- eventsModule.mainEventBus
mainEventBus <- eventsModule.mainEventBusTask tickEventBus <- eventsModule.tickEventBus
tickEventBus <- eventsModule.tickEventBusTask obs <-
obs <- playerEventBus.askL[Observable[PlayerMovementEvent]]( playerEventBus
.askL[Observable[PlayerMovementEvent]](
ObservableSubscription(_) ObservableSubscription(_)
) )
_ <- IOUtils.toIO( .onErrorHandleWith {
case ex: TimeoutException =>
IO.raiseError(AppError.TimeoutError(ex.getMessage()))
}
_ <-
IOUtils
.toIO(
obs obs
.doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void) .doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void)
.completedL .completedL
.startAndForget .startAndForget
) )
inputManager <- gameApp.inputManager .hideErrors
assetManager <- UIO.pure(gameApp.assetManager) inputManager <- gameApp.inputManager
stateManager <- gameApp.stateManager assetManager <- UIO.pure(gameApp.assetManager)
camera <- gameApp.camera camera <- gameApp.camera
rootNode <- UIO.pure(gameApp.rootNode) rootNode <- UIO.pure(gameApp.rootNode)
enqueueR <- UIO(gameApp.enqueue _) enqueueR <- UIO(gameApp.enqueue _)
viewPort <- gameApp.viewPort viewPort <- gameApp.viewPort
physicsSpace <- UIO.pure(gameApp.physicsSpace) physicsSpace <- UIO.pure(gameApp.physicsSpace)
_ <- logger.info("before") _ <- logger.infoU("before")
// jfxUI <- gameApp.jfxUI // jfxUI <- gameApp.jfxUI
gameAppActor <- gameApp.spawnGameActor( gameAppActor <- gameApp.spawnGameActor(
GameAppActor.Props(tickEventBus).behavior, GameAppActor.Props(tickEventBus).behavior,
"gameAppActor" "gameAppActor"
) )
_ <- gameAppActor !! GameAppActor.Start _ <- gameAppActor !! GameAppActor.Start
consoleTextArea <- Task(new TextArea { consoleTextArea <- UIO(new TextArea {
text = "hello \n" text = "hello \n"
editable = false editable = false
wrapText = true wrapText = true
// maxHeight = 150 // maxHeight = 150
// maxWidth = 300 // maxWidth = 300
}) })
// _ <- Task(consoleStream := consoleTextArea) // _ <- Task(consoleStream := consoleTextArea)
// _ <- Task(jfxUI += consoleTextArea) // _ <- Task(jfxUI += consoleTextArea)
_ <- logger.info("after") _ <- logger.infoU("after")
_ <- logger.info("Initializing console stream") _ <- logger.infoU("Initializing console stream")
_ <- _ <-
wire[MainAppDelegate] wire[MainAppDelegate]
.init() .init()
.executeOn(gameApp.scheduler) .executeOn(gameApp.scheduler)
} yield gameAppFib } yield fib
def gameInit: Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
wire[GameAppResource].resource.evalMap {
case Right(gameApp -> gameAppFib) =>
eval(gameApp, gameAppFib).attempt
case Left(error) => IO.terminate(new Exception(error.toString))
} }
// val x: Task[Unit] = for { // val x: Task[Unit] = for {
@ -141,21 +156,24 @@ class MainApp(
// } yield () // } yield ()
val program = for { val program = for {
scriptSystem <- scriptSystemInit // scriptSystem <- scriptSystemInit
launchSignal <- Deferred[Task, Launcher.LauncherResult] launchSignal <- Deferred[Task, Launcher.LauncherResult].hideErrors
launcher <- new Launcher.Props(schedulers, launchSignal).create launcher <- new Launcher.Props(schedulers, launchSignal).create
launchResult <- launcher.init.use(_ => launchSignal.get) launchResult <- launcher.init.use(_ => launchSignal.get).hideErrors
_ <- _ <-
/** /**
* User chose to quit * User chose to quit
*/ */
if (launchResult === LauncherResult.Exit) if (launchResult === LauncherResult.Exit)
logger.info("Exiting") logger.infoU("Exiting")
/** /**
* User chose launch. Wait for game window to close * User chose launch. Wait for game window to close
*/ */
else else
gameInit.use(_.join) gameInit.use {
case Right(fib) => fib.join >> Task.unit
case Left(error) => IO.terminate(new Exception(error.toString))
}.hideErrors
} yield () } yield ()
} }
@ -170,12 +188,11 @@ class MainAppDelegate(
tickEventBus: GameEventBus[TickEvent], tickEventBus: GameEventBus[TickEvent],
inputManager: InputManager, inputManager: InputManager,
assetManager: AssetManager, assetManager: AssetManager,
stateManager: AppStateManager, physicsSpace: PhysicsSpace,
physicsSpace: PhysicsSpace[Task],
camera: Camera, camera: Camera,
viewPort: ViewPort, viewPort: ViewPort,
enqueueR: Function1[() => Unit, Unit], enqueueR: Function1[() => Unit, Unit],
rootNode: AppNode[Task] @@ GameAppTags.RootNode rootNode: AppNode2 @@ GameAppTags.RootNode
)(implicit )(implicit
spawnProtocol: ActorRef[SpawnProtocol.Command], spawnProtocol: ActorRef[SpawnProtocol.Command],
timeout: Timeout, timeout: Timeout,
@ -185,31 +202,28 @@ class MainAppDelegate(
def init( def init(
// appScheduler: monix.execution.Scheduler // appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea] // consoleStream: GenericConsoleStream[TextArea]
) = ): IO[AppError, Unit] =
for { for {
_ <- loggerL.info("Initializing Systems") _ <- loggerL.infoU("Initializing Systems")
_ <- assetManager.registerLocator( _ <- assetManager.registerLocator(
os.rel / "assets" / "town.zip", os.rel / "assets" / "town.zip",
classOf[ZipLocator] classOf[ZipLocator]
) )
_ <- loggerL.info("test") _ <- loggerL.infoU("test")
// _ <- Task(consoleStream.println("text")) // _ <- Task(consoleStream.println("text"))
_ <- DefaultGameLevel(assetManager, viewPort) level <- DefaultGameLevel(assetManager, viewPort)
.flatMap(_.addToGame(rootNode, physicsSpace).hideErrors) _ <- level.addToGame(rootNode, physicsSpace)
.onErrorHandleWith(e => loggerL.error(e.toString))
_ <- createPlayerController() _ <- createPlayerController()
.onErrorHandleWith(e => loggerL.error(e.toString))
// .onErrorRestart(3) // .onErrorRestart(3)
_ <- wire[GameInputHandler.Props].begin _ <- wire[GameInputHandler.Props].begin
// .onErrorRestart(3) // .onErrorRestart(3)
johnActor <- createTestNpc("John") johnActor <- createTestNpc("John")
.onErrorHandleWith(e => IO.raiseError(new Throwable(e.toString)))
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
// _ <- _ <-
// johnActor johnActor
// .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10))) .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
// .delayExecution(2.seconds) .delayExecution(2.seconds)
// _ <- // _ <-
// IOUtils // IOUtils
// .toIO( // .toIO(
@ -224,8 +238,8 @@ class MainAppDelegate(
def createPlayerController( def createPlayerController(
// appScheduler: monix.execution.Scheduler // appScheduler: monix.execution.Scheduler
): IO[PlayerController.Error, ActorRef[PlayerActorSupervisor.Command]] = { ): IO[AppError, ActorRef[PlayerActorSupervisor.Command]] = {
val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.Zero
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
val playerPhysicsControl = val playerPhysicsControl =
PlayerController.Defaults.defaultPlayerPhysicsControl PlayerController.Defaults.defaultPlayerPhysicsControl
@ -235,7 +249,7 @@ class MainAppDelegate(
assetManager assetManager
.loadModelAs[Node](modelPath) .loadModelAs[Node](modelPath)
.map(_.withRotate(0, FastMath.PI, 0)) .map(_.withRotate(0, FastMath.PI, 0))
.mapError(PlayerController.CouldNotCreatePlayerModel) .mapError(AppError.AssetManagerError)
playerNode <- UIO( playerNode <- UIO(
PlayerController.Defaults PlayerController.Defaults
.defaultPlayerNode( .defaultPlayerNode(
@ -261,7 +275,7 @@ class MainAppDelegate(
def createTestNpc( def createTestNpc(
// appScheduler: monix.execution.Scheduler, // appScheduler: monix.execution.Scheduler,
npcName: String npcName: String
) = { ): IO[AppError, ActorRef[NpcActorSupervisor.Command]] = {
val initialPos = ImVector3f(50, 5, 0) val initialPos = ImVector3f(50, 5, 0)
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
// (1f, 2.1f, 10f) // (1f, 2.1f, 10f)
@ -284,10 +298,13 @@ class MainAppDelegate(
s"${npcName}-npcActorSupervisor" s"${npcName}-npcActorSupervisor"
) )
for { (for {
materialDef <- assetManager.loadAssetAs[MaterialDef]( materialDef <-
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" assetManager
) .loadAssetAs[MaterialDef](
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
)
.mapError(AppError.AssetManagerError)
material = new Material(materialDef) material = new Material(materialDef)
_ = material.setColor("Color", ColorRGBA.Blue) _ = material.setColor("Color", ColorRGBA.Blue)
mesh = PlayerController.Defaults.defaultMesh.withMaterial(material) mesh = PlayerController.Defaults.defaultMesh.withMaterial(material)
@ -297,13 +314,13 @@ class MainAppDelegate(
npcPhysicsControl, npcPhysicsControl,
npcName npcName
) )
npcActor <- npcActorTask.hideErrors
_ <- (for { _ <- (for {
_ <- physicsSpace += npcPhysicsControl _ <- physicsSpace += npcPhysicsControl
_ <- physicsSpace += npcNode _ <- physicsSpace += npcNode
_ <- rootNode += npcNode _ <- rootNode += npcNode
} yield ()).hideErrors } yield ()).mapError(AppError.AppNodeError)
} yield npcActor npcActor <- npcActorTask
} yield npcActor)
} }

View File

@ -1,12 +1,12 @@
package wow.doge.mygame package wow.doge.mygame
import akka.actor.BootstrapSetup
import akka.actor.typed.ActorSystem import akka.actor.typed.ActorSystem
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import cats.effect.Resource import cats.effect.Resource
import io.odin.Logger import io.odin.Logger
import monix.bio.Task import monix.bio.Task
import wow.doge.mygame.executors.ExecutorsModule
import akka.actor.BootstrapSetup
import monix.execution.Scheduler import monix.execution.Scheduler
import wow.doge.mygame.executors.ExecutorsModule
trait MainModule extends ExecutorsModule { trait MainModule extends ExecutorsModule {

View File

@ -10,7 +10,6 @@ import akka.actor.typed.scaladsl.AskPattern._
import akka.util.Timeout import akka.util.Timeout
import cats.effect.Resource import cats.effect.Resource
import cats.effect.concurrent.Deferred import cats.effect.concurrent.Deferred
import com.jme3.app.state.AppStateManager
import com.jme3.bullet.BulletAppState import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager import com.jme3.input.InputManager
import com.jme3.scene.Node import com.jme3.scene.Node
@ -20,6 +19,7 @@ import com.softwaremill.tagging._
import com.typesafe.scalalogging.{Logger => SLogger} import com.typesafe.scalalogging.{Logger => SLogger}
import io.odin.Logger import io.odin.Logger
import monix.bio.Fiber import monix.bio.Fiber
import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.bio.UIO import monix.bio.UIO
import monix.catnap.ConcurrentChannel import monix.catnap.ConcurrentChannel
@ -28,14 +28,16 @@ import monix.eval.Coeval
import monix.execution.CancelableFuture import monix.execution.CancelableFuture
import monix.execution.CancelablePromise import monix.execution.CancelablePromise
import monix.execution.Scheduler import monix.execution.Scheduler
import wow.doge.mygame.AppError
import wow.doge.mygame.AppError.TimeoutError
import wow.doge.mygame.Dispatchers
import wow.doge.mygame.executors.JMERunner import wow.doge.mygame.executors.JMERunner
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers
import wow.doge.mygame.utils.GenericTimerActor
import wow.doge.mygame.game.subsystems.ui.JFxUI import wow.doge.mygame.game.subsystems.ui.JFxUI
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.AkkaUtils
import wow.doge.mygame.utils.GenericTimerActor
import wow.doge.mygame.utils.wrappers.jme._ import wow.doge.mygame.utils.wrappers.jme._
import wow.doge.mygame.Dispatchers
object GameAppTags { object GameAppTags {
sealed trait RootNode sealed trait RootNode
@ -49,18 +51,17 @@ class GameApp private[game] (
gameActor: ActorRef[TestGameActor.Command], gameActor: ActorRef[TestGameActor.Command],
gameSpawnProtocol: ActorRef[SpawnProtocol.Command] gameSpawnProtocol: ActorRef[SpawnProtocol.Command]
) { ) {
def stateManager: Task[AppStateManager] = Task(app.getStateManager()) def inputManager: UIO[InputManager] = UIO(app.getInputManager())
def inputManager: Task[InputManager] = Task(app.getInputManager())
def assetManager = new AssetManager(app.getAssetManager()) def assetManager = new AssetManager(app.getAssetManager())
def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode]) def guiNode = UIO(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
val guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode] val guiNode2 = AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
def flyCam = Option(app.getFlyByCamera()) def flyCam = Option(app.getFlyByCamera())
def camera = Task(app.getCamera()) def camera = UIO(app.getCamera())
def viewPort = Task(app.getViewPort()) def viewPort = UIO(app.getViewPort())
// def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode]) // def rootNode = UIO(app.getRootNode().taggedWith[GameAppTags.RootNode])
val rootNode = val rootNode =
AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode] AppNode2(app.getRootNode()).taggedWith[GameAppTags.RootNode]
val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace) val physicsSpace = new PhysicsSpace(app.bulletAppState.physicsSpace)
def enqueue(cb: () => Unit) = app.enqueueR(() => cb()) def enqueue(cb: () => Unit) = app.enqueueR(() => cb())
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb) def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
@ -89,13 +90,14 @@ class GameAppResource(
scheduler: akka.actor.typed.Scheduler, scheduler: akka.actor.typed.Scheduler,
spawnProtocol: ActorRef[SpawnProtocol.Command] spawnProtocol: ActorRef[SpawnProtocol.Command]
) { ) {
def resource: Resource[Task, (GameApp, Fiber[Throwable, Unit])] = def resource
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
Resource.make { Resource.make {
lazy val bullet = new BulletAppState lazy val bullet = new BulletAppState
for { (for {
app <- Task(new SimpleAppExt(schedulers, bullet)) app <- UIO(new SimpleAppExt(schedulers, bullet))
_ <- UIO(JMERunner.runner = Some(app.enqueue _)) _ <- UIO(JMERunner.runner = Some(app.enqueue _))
_ <- Task { _ <- UIO {
val settings = new AppSettings(true) val settings = new AppSettings(true)
settings.setVSync(true) settings.setVSync(true)
@ -107,23 +109,27 @@ class GameAppResource(
app.setSettings(settings) app.setSettings(settings)
} }
fib <- Task(app.start).executeOn(jmeThread).start fib <- UIO(app.start).executeOn(jmeThread).start
_ <- Task.deferFuture(app.started) _ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
testGameActor <- AkkaUtils.spawnActorL( testGameActor <- AkkaUtils.spawnActorL(
new TestGameActor.Props().create, new TestGameActor.Props().create,
"testGameActor", "testGameActor",
Dispatchers.jmeDispatcher Dispatchers.jmeDispatcher
) )
sp <- testGameActor.askL(TestGameActor.GetSpawnProtocol(_)) sp <-
gameApp <- Task(new GameApp(logger, app, testGameActor, sp)) testGameActor
_ <- Task { .askL(TestGameActor.GetSpawnProtocol(_))
.onErrorHandleWith(TimeoutError.from)
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp))
_ <- UIO {
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
app.cancelToken = Some(fut) app.cancelToken = Some(fut)
} }
} yield (gameApp, fib) } yield (gameApp, fib)).attempt
} { } {
case (gameApp, fib) => case Right(gameApp -> fib) =>
fib.cancel >> UIO(JMERunner.runner = None) fib.cancel >> UIO(JMERunner.runner = None)
case Left(error) => IO.terminate(new Exception(error.toString))
} }
} }

View File

@ -5,12 +5,12 @@ import scala.concurrent.duration._
import akka.actor.typed.SupervisorStrategy import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.game.TickGenerator.Send import wow.doge.mygame.game.TickGenerator.Send
import wow.doge.mygame.utils.GenericTimerActor
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.PhysicsTick import wow.doge.mygame.subsystems.events.TickEvent.PhysicsTick
import wow.doge.mygame.utils.GenericTimerActor
object GameAppActor { object GameAppActor {

View File

@ -8,14 +8,13 @@ import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.implicits._
object PlayerActorSupervisor { object PlayerActorSupervisor {
sealed trait Command sealed trait Command
final case class Props( final case class Props(

View File

@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import com.jme3.asset.AssetManager
import com.jme3.bullet.BulletAppState import com.jme3.bullet.BulletAppState
import com.jme3.bullet.control.BetterCharacterControl import com.jme3.bullet.control.BetterCharacterControl
import com.jme3.math.FastMath import com.jme3.math.FastMath
@ -16,8 +15,9 @@ import com.jme3.scene.Spatial
import com.jme3.scene.shape.Box import com.jme3.scene.shape.Box
import com.softwaremill.tagging._ import com.softwaremill.tagging._
import io.odin.Logger import io.odin.Logger
import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.bio.UIO import wow.doge.mygame.AppError
import wow.doge.mygame.game.GameAppTags import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.game.SimpleAppExt import wow.doge.mygame.game.SimpleAppExt
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
@ -36,18 +36,15 @@ object PlayerControllerTags {
object PlayerController { object PlayerController {
sealed trait Error sealed trait Error
case class CouldNotCreatePlayerNode(reason: String) extends Error case class CouldNotCreatePlayerModel(reason: AssetManager.Error) extends Error
case class CouldNotCreatePlayerModel(
reason: wow.doge.mygame.utils.wrappers.jme.AssetManager.Error
) extends Error
class Props( class Props(
enqueueR: Function1[() => Unit, Unit], enqueueR: Function1[() => Unit, Unit],
rootNode: AppNode[Task] @@ GameAppTags.RootNode, rootNode: AppNode2 @@ GameAppTags.RootNode,
loggerL: Logger[Task], loggerL: Logger[Task],
// physicsSpace: com.jme3.bullet.PhysicsSpace, // physicsSpace: com.jme3.bullet.PhysicsSpace,
physicsSpace: PhysicsSpace[Task], physicsSpace: PhysicsSpace,
initialPlayerPos: ImVector3f = ImVector3f.ZERO, initialPlayerPos: ImVector3f = ImVector3f.Zero,
playerEventBus: GameEventBus[PlayerEvent], playerEventBus: GameEventBus[PlayerEvent],
playerPhysicsControl: BetterCharacterControl, playerPhysicsControl: BetterCharacterControl,
// appScheduler: monix.execution.Scheduler, // appScheduler: monix.execution.Scheduler,
@ -78,16 +75,18 @@ object PlayerController {
cameraActorBeh cameraActorBeh
).behavior ).behavior
} }
val create: UIO[ActorRef[PlayerActorSupervisor.Command]] = val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
(for { (for {
playerActor <- playerActor <-
AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor") AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
_ <- rootNode += playerNode _ <- (for {
_ <- physicsSpace += playerNode _ <- rootNode += playerNode
_ <- physicsSpace += playerPhysicsControl _ <- physicsSpace += playerNode
_ = cameraPivotNode += cameraNode _ <- physicsSpace += playerPhysicsControl
_ <- rootNode += cameraPivotNode _ = cameraPivotNode += cameraNode
} yield playerActor).hideErrors _ <- rootNode += cameraPivotNode
} yield ()).mapError(AppError.AppNodeError)
} yield playerActor)
} }
@ -96,7 +95,7 @@ object PlayerController {
modelPath: os.RelPath, modelPath: os.RelPath,
cam: Camera cam: Camera
)(assetManager: AssetManager, bulletAppState: BulletAppState) = { )(assetManager: AssetManager, bulletAppState: BulletAppState) = {
val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.Zero
val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
.withJumpForce(ImVector3f(0, 5f, 0)) .withJumpForce(ImVector3f(0, 5f, 0))
val playerNode = new Node("PlayerNode") val playerNode = new Node("PlayerNode")
@ -152,7 +151,7 @@ object PlayerController {
new CameraNode("CameraNode", cam) new CameraNode("CameraNode", cam)
// .withControlDir(ControlDirection.SpatialToCamera) // .withControlDir(ControlDirection.SpatialToCamera)
.withLocalTranslation(ImVector3f(0, 1.5f, 10)) .withLocalTranslation(ImVector3f(0, 1.5f, 10))
.withLookAt(playerPos, ImVector3f.UNIT_Y) .withLookAt(playerPos, ImVector3f.UnitY)
def defaultPlayerNode( def defaultPlayerNode(
playerPos: ImVector3f, playerPos: ImVector3f,

View File

@ -9,7 +9,7 @@ import com.jme3.input.KeyInput
import com.jme3.input.MouseInput import com.jme3.input.MouseInput
import com.jme3.input.controls.KeyTrigger import com.jme3.input.controls.KeyTrigger
import com.jme3.input.controls.MouseAxisTrigger import com.jme3.input.controls.MouseAxisTrigger
import monix.bio.Task import monix.bio.UIO
import monix.{eval => me} import monix.{eval => me}
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventBus
@ -18,7 +18,6 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.utils.IOUtils._ import wow.doge.mygame.utils.IOUtils._
import monix.bio.UIO
object GameInputHandler { object GameInputHandler {

View File

@ -8,7 +8,9 @@ import com.jme3.math.ColorRGBA
import com.jme3.math.Vector3f import com.jme3.math.Vector3f
import com.jme3.renderer.ViewPort import com.jme3.renderer.ViewPort
import com.jme3.scene.Spatial import com.jme3.scene.Spatial
import monix.bio.IO
import monix.bio.UIO import monix.bio.UIO
import wow.doge.mygame.AppError
object DefaultGameLevel { object DefaultGameLevel {
def apply( def apply(
@ -51,7 +53,7 @@ object DefaultGameLevel {
def apply( def apply(
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager, assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager,
viewPort: ViewPort viewPort: ViewPort
) = ): IO[AppError, GameLevel] =
// for { // for {
// sceneModel <- assetManager.loadModelAs[Node](os.rel / "main.scene") // sceneModel <- assetManager.loadModelAs[Node](os.rel / "main.scene")
// sceneShape <- UIO(CollisionShapeFactory.createMeshShape(sceneModel)) // sceneShape <- UIO(CollisionShapeFactory.createMeshShape(sceneModel))

View File

@ -8,11 +8,10 @@ import com.jme3.scene.Node
import com.jme3.scene.Spatial import com.jme3.scene.Spatial
import com.softwaremill.tagging._ import com.softwaremill.tagging._
import monix.bio.IO import monix.bio.IO
import monix.bio.Task
import monix.bio.UIO import monix.bio.UIO
import wow.doge.mygame.AppError
import wow.doge.mygame.game.GameAppTags import wow.doge.mygame.game.GameAppTags
import wow.doge.mygame.utils.wrappers.jme.AppNode import wow.doge.mygame.utils.wrappers.jme.AppNode2
import wow.doge.mygame.utils.wrappers.jme.AssetManager
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
@ -23,21 +22,20 @@ class GameLevel(
val directionalLight: DirectionalLight val directionalLight: DirectionalLight
) { ) {
def addToGame( def addToGame(
rootNode: AppNode[Task] @@ GameAppTags.RootNode, rootNode: AppNode2 @@ GameAppTags.RootNode,
physicsSpace: PhysicsSpace[Task] physicsSpace: PhysicsSpace
) = { ) =
for { (for {
_ <- rootNode += model _ <- rootNode += model
_ <- rootNode += ambientLight _ <- rootNode += ambientLight
_ <- rootNode += directionalLight _ <- rootNode += directionalLight
_ <- physicsSpace += model _ <- physicsSpace += model
_ <- physicsSpace += physicsControl _ <- physicsSpace += physicsControl
} yield () } yield ()).mapError(AppError.AppNodeError)
}
def removeFromGame( def removeFromGame(
rootNode: AppNode[Task] @@ GameAppTags.RootNode, rootNode: AppNode2 @@ GameAppTags.RootNode,
physicsSpace: PhysicsSpace[Task] physicsSpace: PhysicsSpace
) = { ) =
for { for {
_ <- rootNode -= model _ <- rootNode -= model
_ <- rootNode -= ambientLight _ <- rootNode -= ambientLight
@ -45,11 +43,10 @@ class GameLevel(
_ <- physicsSpace -= model _ <- physicsSpace -= model
_ <- physicsSpace -= physicsControl _ <- physicsSpace -= physicsControl
} yield () } yield ()
}
def resource( def resource(
rootNode: AppNode[Task] @@ GameAppTags.RootNode, rootNode: AppNode2 @@ GameAppTags.RootNode,
physicsSpace: PhysicsSpace[Task] physicsSpace: PhysicsSpace
) = ) =
Resource.make(this.addToGame(rootNode, physicsSpace))(_ => Resource.make(this.addToGame(rootNode, physicsSpace))(_ =>
this.removeFromGame(rootNode, physicsSpace) this.removeFromGame(rootNode, physicsSpace)
@ -57,10 +54,6 @@ class GameLevel(
} }
object GameLevel { object GameLevel {
sealed trait Error
case class AssetLoadError(err: AssetManager.Error) extends Error
case class CollisionShapeCreationFailed(err: CollisionShapeFactory.Error)
extends Error
def apply( def apply(
modelPath: os.RelPath, modelPath: os.RelPath,
@ -68,16 +61,16 @@ object GameLevel {
dl: DirectionalLight dl: DirectionalLight
)(implicit )(implicit
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager
): IO[Error, GameLevel] = ): IO[AppError, GameLevel] =
for { for {
sceneModel <- sceneModel <-
assetManager assetManager
.loadModelAs[Node](modelPath) .loadModelAs[Node](modelPath)
.mapError(AssetLoadError) .mapError(AppError.AssetManagerError)
sceneShape <- sceneShape <-
CollisionShapeFactory CollisionShapeFactory
.createMeshShape(sceneModel) .createMeshShape(sceneModel)
.mapError(CollisionShapeCreationFailed) .mapError(AppError.CollisionShapeCreationFailed)
landscape <- UIO(new RigidBodyControl(sceneShape, 0)) landscape <- UIO(new RigidBodyControl(sceneShape, 0))
} yield new GameLevel( } yield new GameLevel(

View File

@ -21,9 +21,8 @@ trait CanMove2[-A, F[_]] {
object Test { object Test {
val x = new BetterCharacterControl(4, 10, 5) val x = new BetterCharacterControl(4, 10, 5)
def test[T](x: T)(implicit cm: CanMove2[T, Id]) = { def test[T](x: T)(implicit cm: CanMove2[T, Id]) =
cm.move(x, ImVector3f.ZERO) cm.move(x, ImVector3f.Zero)
}
} }
object CanMove2 { object CanMove2 {
@ -36,7 +35,7 @@ object CanMove2 {
): Id[Unit] = {} ): Id[Unit] = {}
override def location(inst: BetterCharacterControl): Id[ImVector3f] = override def location(inst: BetterCharacterControl): Id[ImVector3f] =
ImVector3f.ZERO ImVector3f.Zero
override def jump(inst: BetterCharacterControl): Id[Unit] = ??? override def jump(inst: BetterCharacterControl): Id[Unit] = ???

View File

@ -129,7 +129,7 @@ class ImMovementActor[T](
} }
object Methods { object Methods {
def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = { def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = {
val zero = ImVector3f.ZERO val zero = ImVector3f.Zero
val dir = cardinalDir val dir = cardinalDir
val walkDir = { val walkDir = {
val mutWalkDir = new Vector3f() val mutWalkDir = new Vector3f()

View File

@ -1,14 +1,13 @@
package wow.doge.mygame.implicits package wow.doge.mygame.implicits
import cats.data.Kleisli
import monix.bio.IO
import monix.bio.UIO
import cats.effect.Resource
import cats.Functor import cats.Functor
import cats.effect.Bracket
import monix.bio.Task
import cats.Show import cats.Show
import cats.data.Kleisli
import cats.effect.Resource
import cats.syntax.show._ import cats.syntax.show._
import monix.bio.IO
import monix.bio.Task
import monix.bio.UIO
trait CatsExtensions { trait CatsExtensions {
implicit class KleisliCompanionExt(k: Kleisli.type) { implicit class KleisliCompanionExt(k: Kleisli.type) {

View File

@ -5,6 +5,7 @@ import cats.effect.concurrent.Deferred
import cats.kernel.Eq import cats.kernel.Eq
import javafx.application.Platform import javafx.application.Platform
import monix.bio.Task import monix.bio.Task
import monix.bio.UIO
import monix.catnap.CancelableF import monix.catnap.CancelableF
import monix.execution.CancelablePromise import monix.execution.CancelablePromise
import monix.reactive.Observable import monix.reactive.Observable
@ -30,7 +31,7 @@ object Launcher {
val schedulers: Schedulers, val schedulers: Schedulers,
val signal: Deferred[Task, LauncherResult] val signal: Deferred[Task, LauncherResult]
) { ) {
val create = Task(new Launcher(this)) val create = UIO(new Launcher(this))
} }
} }
class Launcher private (props: Launcher.Props) { class Launcher private (props: Launcher.Props) {

View File

@ -1,29 +1,44 @@
package wow.doge.mygame.math; package wow.doge.mygame.math;
import cats.Show import cats.Show
import cats.kernel.Eq
import cats.syntax.eq._
import math.{abs, pow, sqrt} import math.{abs, pow, sqrt}
case class ImVector3f(x: Float, y: Float, z: Float) case class ImVector3f(x: Float, y: Float, z: Float)
object ImVector3f { object ImVector3f {
val ZERO = ImVector3f(0, 0, 0) //format: off
val UNIT_X = ImVector3f(1, 0, 0) val Zero = ImVector3f(0, 0, 0)
val UNIT_Y = ImVector3f(0, 1, 0) val UnitX = ImVector3f(1, 0, 0)
val UNIT_Z = ImVector3f(0, 0, 1) val UnitY = ImVector3f(0, 1, 0)
val UnitZ = ImVector3f(0, 0, 1)
val Unit = ImVector3f(1, 1, 1)
//format: on
val Max = ImVector3f(Float.MaxValue, Float.MaxValue, Float.MaxValue)
val Min = ImVector3f(Float.MinValue, Float.MinValue, Float.MinValue)
def dst(v1: ImVector3f, v2: ImVector3f) = implicit val show = new Show[ImVector3f] {
sqrt(
pow((v1.x - v2.x).toDouble, 2) + pow((v1.y - v2.y).toDouble, 2) + pow(
(v1.z - v2.z).toDouble,
2
)
)
def manhattanDst(v1: ImVector3f, v2: ImVector3f) =
abs(v1.x - v2.x) + abs(v1.y - v2.y) + abs(v1.z - v2.z)
implicit val showForImVector3f = new Show[ImVector3f] {
def format(f: Float) = f.formatted("%.2f") def format(f: Float) = f.formatted("%.2f")
override def show(t: ImVector3f): String = override def show(t: ImVector3f): String =
s"ImVector3f(${format(t.x)},${format(t.y)},${format(t.z)})" s"ImVector3f(${format(t.x)},${format(t.y)},${format(t.z)})"
} }
implicit val eq = Eq.fromUniversalEquals[ImVector3f]
private def squareDiff(f1: Float, f2: Float) =
pow(f1.toDouble - f2.toDouble, 2)
// private def squareDiff2(f1: Float, f2: Float) = pow((f1 - f2).toDouble, 2)
def dst(v1: ImVector3f, v2: ImVector3f): Double =
if (v1 === v2) 0
else {
val total =
squareDiff(v1.x, v2.x) + squareDiff(v1.y, v2.y) +
squareDiff(v1.z, v2.z)
sqrt(total)
}
def manhattanDst(v1: ImVector3f, v2: ImVector3f) =
abs(v1.x - v2.x) + abs(v1.y - v2.y) + abs(v1.z - v2.z)
} }

View File

@ -1,25 +1,25 @@
package wow.doge.mygame.subsystems.events package wow.doge.mygame.subsystems.events
import scala.reflect.ClassTag import scala.reflect.ClassTag
import scala.util.Random
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import akka.event.EventStream
import monix.reactive.Observable
import monix.reactive.OverflowStrategy
import monix.execution.cancelables.SingleAssignCancelable
import monix.execution.Ack
import akka.util.Timeout
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import scala.util.Random
import akka.actor.typed.scaladsl.AskPattern._ import akka.actor.typed.scaladsl.AskPattern._
import monix.execution.Cancelable import akka.actor.typed.scaladsl.Behaviors
import wow.doge.mygame.utils.AkkaUtils import akka.event.EventStream
import wow.doge.mygame.implicits._ import akka.util.Timeout
import monix.bio.UIO import monix.bio.UIO
import monix.execution.Ack
import monix.execution.Cancelable
import monix.execution.cancelables.SingleAssignCancelable
import monix.reactive.Observable
import monix.reactive.OverflowStrategy
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
import wow.doge.mygame.utils.AkkaUtils
/** /**
* A (typed) event bus * A (typed) event bus
@ -81,6 +81,7 @@ object EventBus {
behavior, behavior,
s"eventBusObservable-${ct.toString}-${Random.nextLong()}" s"eventBusObservable-${ct.toString}-${Random.nextLong()}"
) )
.mapError(err => new Exception(err.toString))
.tapError { .tapError {
case ex => UIO(sub.onError(ex)) case ex => UIO(sub.onError(ex))
} }
@ -116,6 +117,7 @@ object EventBus {
behavior, behavior,
s"eventBusObservable-${ct.toString}-${math.abs(Random.nextLong())}" s"eventBusObservable-${ct.toString}-${math.abs(Random.nextLong())}"
) )
.mapError(err => new Throwable(err.toString))
.tapError { .tapError {
case ex => UIO(sub.onError(ex)) case ex => UIO(sub.onError(ex))
} }

View File

@ -1,63 +1,75 @@
package wow.doge.mygame.subsystems.events package wow.doge.mygame.subsystems.events
import java.util.concurrent.TimeoutException
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.reflect.ClassTag
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.LogOptions import akka.actor.typed.LogOptions
import akka.actor.typed.Props import akka.actor.typed.Props
import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.actor.typed.SupervisorStrategy import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout import akka.util.Timeout
import com.typesafe.scalalogging.{Logger => SLogger} import com.typesafe.scalalogging.{Logger => SLogger}
import monix.bio.IO
import org.slf4j.event.Level import org.slf4j.event.Level
import wow.doge.mygame.AppError
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.Event import wow.doge.mygame.subsystems.events.Event
import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventBus
import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent
import scala.reflect.ClassTag
import akka.actor.typed.Scheduler
class EventsModule( class EventsModule(
scheduler: Scheduler, scheduler: Scheduler,
spawnProtocol: ActorRef[SpawnProtocol.Command] spawnProtocol: ActorRef[SpawnProtocol.Command]
) { ) {
import EventsModule._
implicit val s = scheduler implicit val s = scheduler
implicit val sp = spawnProtocol implicit val sp = spawnProtocol
implicit val timeout = Timeout(1.second) implicit val timeout = Timeout(1.second)
val eventBusLogger = SLogger[EventBus[_]] val eventBusLogger = SLogger[EventBus[_]]
val playerEventBusTask = val playerEventBus: IO[AppError, GameEventBus[PlayerEvent]] =
createEventBus[PlayerEvent]("playerEventBus") createEventBus[PlayerEvent]("playerEventBus")
// val playerCameraEventBusTask = // val playerCameraEventBusTask =
// createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG) // createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
val tickEventBusTask = val tickEventBus: IO[AppError, GameEventBus[TickEvent]] =
createEventBus[TickEvent]("tickEventBus", Level.TRACE) createEventBus[TickEvent]("tickEventBus", Level.TRACE)
val mainEventBusTask = createEventBus[Event]("mainEventBus") val mainEventBus: IO[AppError, GameEventBus[Event]] =
createEventBus[Event]("mainEventBus")
def createEventBus[T: ClassTag]( def createEventBus[T: ClassTag](
busName: String, busName: String,
logLevel: Level = Level.DEBUG logLevel: Level = Level.DEBUG
) = ) =
spawnProtocol.askL( spawnProtocol
SpawnProtocol.Spawn[EventBus.Command[T]]( .askL(
Behaviors.logMessages( SpawnProtocol.Spawn[EventBus.Command[T]](
logOptions = LogOptions() Behaviors.logMessages(
.withLevel(logLevel) logOptions = LogOptions()
.withLogger(eventBusLogger.underlying), .withLevel(logLevel)
Behaviors .withLogger(eventBusLogger.underlying),
.supervise(EventBus[T]()) Behaviors
.onFailure[Exception](SupervisorStrategy.restart) .supervise(EventBus[T]())
), .onFailure[Exception](SupervisorStrategy.restart)
busName, ),
Props.empty, busName,
_ Props.empty,
_
)
) )
) .onErrorHandleWith {
case ex: TimeoutException =>
IO.raiseError(AppError.TimeoutError(ex.getMessage))
}
} }
object EventsModule { object EventsModule {

View File

@ -4,9 +4,10 @@ import java.nio.file.NoSuchFileException
import scala.collection.View import scala.collection.View
import scala.collection.immutable.ArraySeq import scala.collection.immutable.ArraySeq
import scala.util.Try
import cats.Show
import cats.implicits._ import cats.implicits._
import cats.kernel.Eq
import io.circe._ import io.circe._
import io.circe.generic.JsonCodec import io.circe.generic.JsonCodec
import io.circe.generic.semiauto._ import io.circe.generic.semiauto._
@ -17,33 +18,45 @@ import monix.reactive.Consumer
import monix.reactive.Observable import monix.reactive.Observable
import wow.doge.mygame.utils.IOUtils import wow.doge.mygame.utils.IOUtils
import IOUtils.toIO
@JsonCodec @JsonCodec
final case class Test1(hello1: String, hello2: String) final case class Test1(hello1: String, hello2: String)
@JsonCodec @JsonCodec
final case class Test2(hello1: String) final case class Test2(hello1: String)
final case class Plugin(name: String, priority: Int) final case class Plugin(name: String, priority: Int)
object Plugin { object Plugin {
implicit val pluginFormat: Decoder[Plugin] = deriveDecoder implicit val decoder: Decoder[Plugin] = deriveDecoder
implicit val show = Show.fromToString[Plugin]
implicit val eq = Eq.fromUniversalEquals[Plugin]
} }
object ModdingSystem { object ModdingSystem {
sealed trait Error sealed trait Error
final case class CouldNotDecode(cause: String) extends Error final case class CouldNotDecode(cause: String) extends Error
final case class ParseFailure(cause: String) extends Error final case class ParseFailure(cause: io.circe.ParsingFailure) extends Error
final case class FileNotFound(fileName: String) extends Error final case class DecodingFailure(cause: io.circe.DecodingFailure)
case object GenericError extends Error extends Error
final case class FileNotFound(path: os.Path) extends Error
object Error {
implicit val show = Show.fromToString[Error]
implicit val eq = Eq.fromUniversalEquals[Error]
}
def readPluginsList(dir: os.Path): Try[Either[Error, ArraySeq[Plugin]]] = def readPluginsList(dir: os.Path): IO[Error, ArraySeq[Plugin]] =
Try( IO(parse(os.read(dir / "plugins.json")))
parse(os.read(dir / "plugins.json")) .onErrorHandleWith {
.map( case _: FileNotFoundException =>
_.as[ArraySeq[Plugin]] IO.raiseError(FileNotFound(dir / "plugins.json"))
.leftMap(e => CouldNotDecode(e.getMessage())) case _: NoSuchFileException =>
) IO.raiseError(FileNotFound(dir / "plugins.json"))
.leftMap((e: ParsingFailure) => ParseFailure(e.message)) }
.flatten .flatMap(files =>
) IO.fromEither(files)
// .toValidated .map(_.as[ArraySeq[Plugin]])
.mapError(ParseFailure)
)
.flatMap(result => IO.fromEither(result).mapError(DecodingFailure))
def findPluginFiles(dir: os.Path): View[os.Path] = def findPluginFiles(dir: os.Path): View[os.Path] =
os.list(dir) os.list(dir)
@ -53,35 +66,102 @@ object ModdingSystem {
def findAndReadPluginFiles( def findAndReadPluginFiles(
dir: os.Path, dir: os.Path,
plugins: ArraySeq[Plugin] plugins: ArraySeq[Plugin]
): (View[(Plugin, Error)], View[(Plugin, String)]) = ): UIO[(View[(Plugin, Error)], View[(Plugin, String)])] =
UIO(
plugins
.sortBy(_.priority)
.view
.map { p =>
val path = dir / os.RelPath(p.name + ".plugin.json")
p ->
Either
.catchNonFatal(os.read(path))
.leftMap {
case _: FileNotFoundException => FileNotFound(path)
case _: NoSuchFileException => FileNotFound(path)
}
}
.partitionMap {
case (p, either) =>
either match {
case Left(value) => Left(p -> value)
case Right(value) => Right(p -> value)
}
}
)
// : (View[(Plugin, Error)], View[(Plugin, String)])
def findAndReadPluginFiles2(
dir: os.Path,
plugins: ArraySeq[Plugin]
) =
// IO.parTraverse(plugins.sortBy(_.priority))(p =>
// IO {
// val path = dir / os.RelPath(p.name + ".plugin.json")
// os.read(path)
// }
// .onErrorHandleWith {
// case _: FileNotFoundException =>
// IO.raiseError(FileNotFound(dir.toString))
// case _: NoSuchFileException =>
// IO.raiseError(FileNotFound(dir.toString))
// }
// .flatMap(r => UIO(p -> r))
// ).map {
// _.partitionMap {
// case (p, either) =>
// either match {
// case Left(value) => Left(p -> value)
// case Right(value) => Right(p -> value)
// }
// }
// }
plugins plugins
.sortBy(_.priority) .sortBy(_.priority)
.view .view
.map(p => .map { p =>
p -> val path = dir / os.RelPath(p.name + ".plugin.json")
Either p -> IO(os.read(path))
.catchNonFatal { .onErrorHandleWith {
val path = dir / os.RelPath(p.name + ".plugin.json") case _: FileNotFoundException => IO.raiseError(FileNotFound(path))
os.read(path) case _: NoSuchFileException => IO.raiseError(FileNotFound(path))
} }
.leftMap { // .map(r => p -> r)
case _: FileNotFoundException =>
FileNotFound(p.name) }
case _: NoSuchFileException => FileNotFound(p.name) .map {
case e => GenericError case (p, io) =>
} io.attempt.map {
)
.partitionMap {
case (p, either) =>
either match {
case Left(value) => Left(p -> value) case Left(value) => Left(p -> value)
case Right(value) => Right(p -> value) case Right(value) => Right(p -> value)
} }
} }
.to(List)
.parSequence
def readPluginFiles(filePaths: View[os.Path]) = // .partitionMap {
filePaths.map(path => os.read(path)) // _.map {
// case l @ Left(value) => l
// case r @ Right(value) => r
// }
// }
// .sequence
// def readPluginFiles(filePaths: View[os.Path]) =
// filePaths.map(path => os.read(path))
// def readPluginFiles2(filePaths: View[os.Path]) =
// filePaths
// .map(path =>
// IO(os.read(path)).onErrorHandleWith {
// case _: FileNotFoundException =>
// IO.raiseError(FileNotFound(path))
// case _: NoSuchFileException =>
// IO.raiseError(FileNotFound(path))
// }
// )
// .to(List)
// .parSequence
def parsePluginFiles(files: View[(Plugin, String)]) = def parsePluginFiles(files: View[(Plugin, String)]) =
files files
.map { .map {
@ -92,73 +172,59 @@ object ModdingSystem {
case (p, Right(value)) => Right(p -> value) case (p, Right(value)) => Right(p -> value)
} }
def foldMerge(iterable: Iterable[Json]) = val emptyJson = Json.fromString("empty")
iterable.foldLeft(Json.fromString("empty")) {
case (json, io.circe.Json.Null) => json //ignore null values
case (json, value) => json.deepMerge(value)
}
def mergePluginData(plugins: View[(Plugin, Json)]) = val foldFn: (Json, Json) => Json = {
foldMerge(plugins.map { case (json, Json.Null) => json //ignore null values
case (p, json) => json case (json, value) => json.deepMerge(value)
}) }
def mergePluginDataConsumer = def mergePluginDataConsumer =
Consumer.foldLeft[Json, Json](Json.fromString("empty")) { Consumer.foldLeft[Json, Json](emptyJson)(foldFn)
case (json, io.circe.Json.Null) => json
case (json, that) => json.deepMerge(that)
}
def loadBalancedPluginDataMerger = def loadBalancedPluginDataMerger =
Consumer Consumer
.loadBalance(parallelism = 2, mergePluginDataConsumer) .loadBalance(parallelism = 2, mergePluginDataConsumer)
.map(foldMerge) .map(_.foldLeft(emptyJson)(foldFn))
// def test = def run(wd: os.Path = os.pwd) =
// for {
// filePaths <- Task(findPluginFiles(os.pwd))
// files <- Task(readPluginFiles(filePaths))
// (failures, successes) <- Task(parsePluginFiles(files))
// merged <- Task(mergePluginData(successes))
// _ <- Task {
// println(s"Successes = ${successes.to(Seq)}")
// println(s"Failure = ${failures.to(Seq)}")
// println(s"Merged = $merged")
// }
// } yield ()
def test(wd: os.Path = os.pwd) =
for { for {
plugins <- IO.fromTryEither(readPluginsList(wd)) plugins <- readPluginsList(wd)
(readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins)) (readFailures, readSuccesses) <- findAndReadPluginFiles(wd, plugins)
(parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses)) (parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
// res <- UIO(mergePluginData(parseSuccesses)) res <- UIO.parMap5(
res <- UIO(readFailures.to(List)),
IOUtils UIO(readSuccesses.to(List)),
.toIO( UIO(parseFailures.to(List)),
Observable UIO(parseSuccesses.to(List)),
.fromIterable(parseSuccesses) toIO(
.map { case (p, json) => json } Observable
.consumeWith(loadBalancedPluginDataMerger) .fromIterable(parseSuccesses)
) .map { case (p, json) => json }
.hideErrors .consumeWith(loadBalancedPluginDataMerger)
_ <- UIO { ).hideErrors
println(s"Read Successes = ${readSuccesses.to(Seq)}") )(Result.apply)
println(s"Read Failures = ${readFailures.to(Seq)}") } yield res
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
println(s"Parse Failures = ${parseFailures.to(Seq)}")
println(show"Merged = $res")
}
} yield ()
// monix.eval.Task.deferAction(implicit s => def log(res: Result) =
// ModdingSystem UIO {
// .test() pprint.log(show"Read Successes = ${res.readSuccesses}")
// .leftMap(e => new Throwable(e.toString())) pprint.log(show"Read Failures = ${res.readFailures}")
// .to[monix.eval.Task] pprint.log(show"Parse Successes = ${res.parseSuccesses}")
// ) pprint.log(show"Parse Failures = ${res.parseFailures}")
pprint.log(show"Merged = ${res.pluginJson}")
}
case class Result(
readFailures: List[(Plugin, Error)],
readSuccesses: List[(Plugin, String)],
parseFailures: List[(Plugin, ParsingFailure)],
parseSuccesses: List[(Plugin, Json)],
pluginJson: Json
)
object Result {
implicit val show = Show.fromToString[Result]
// implicit val eq = Eq.fromUniversalEquals[Error]
}
// def test3(wd: os.Path = os.pwd) = {
// (readPluginsList(os.pwd).toValidatedNec)
// }
} }

View File

@ -27,12 +27,14 @@ class ScriptSystemResource(
) { ) {
val init = for { val init = for {
scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts")) scriptFiles <- Task(
findScriptFiles(os.pwd / "assets" / "scripts")
).hideErrors
scriptCacheActor <- AkkaUtils.spawnActorL( scriptCacheActor <- AkkaUtils.spawnActorL(
ScriptCachingActor(), ScriptCachingActor(),
"scriptCachingActor" "scriptCachingActor"
) )
} yield (scriptCacheActor) } yield scriptCacheActor
def findScriptFiles(wd: os.Path) = def findScriptFiles(wd: os.Path) =
os.walk os.walk

View File

@ -6,10 +6,8 @@ import akka.actor.typed.Props
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.actor.typed.SpawnProtocol import akka.actor.typed.SpawnProtocol
import akka.util.Timeout import akka.util.Timeout
import wow.doge.mygame.implicits._
import java.util.concurrent.TimeoutException
import monix.bio.IO
import wow.doge.mygame.AppError.TimeoutError import wow.doge.mygame.AppError.TimeoutError
import wow.doge.mygame.implicits._
object AkkaUtils { object AkkaUtils {
@ -44,7 +42,5 @@ object AkkaUtils {
_ _
) )
) )
// .onErrorHandleWith { .onErrorHandleWith(TimeoutError.from)
// case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
// }
} }

View File

@ -1,12 +1,13 @@
package wow.doge.mygame.utils package wow.doge.mygame.utils
import akka.actor.typed.scaladsl.Behaviors
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
import scala.util.Random
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.TimerScheduler
import scala.util.Random
import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.TimerScheduler
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
object GenericTimerActor { object GenericTimerActor {

View File

@ -0,0 +1,31 @@
package wow.doge.mygame.utils
import cats.data.Reader
import cats.data.ReaderT
import monix.bio.UIO
object ReaderDemo {
type IoReaderT[S, E, A] = ReaderT[UIO, S, Either[E, A]]
val IoReaderT = ReaderT
val t =
ReaderT[UIO, String, Either[Error, Unit]](s => UIO.unit.attempt)
.run("s")
.rethrow
val r: IoReaderT[String, Error, Unit] = IoReaderT(s => UIO.unit.attempt)
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)))
def total: Reader[Environment, UIO[Unit]] =
for {
x <- fun1.local[Environment](_.str)
y <- fun2.local[Environment](_.num)
} yield UIO.parSequence(List(x, y)).void
val io: UIO[Unit] = total.run(Environment("hello", 50))
}

View File

@ -1,6 +1,8 @@
package wow.doge.mygame.utils.wrappers.jme package wow.doge.mygame.utils.wrappers.jme
import scala.reflect.ClassTag import scala.reflect.ClassTag
import cats.Show
import cats.kernel.Eq
import com.jme3.asset.AssetLoadException import com.jme3.asset.AssetLoadException
import com.jme3.asset.AssetLocator import com.jme3.asset.AssetLocator
import com.jme3.asset.AssetNotFoundException import com.jme3.asset.AssetNotFoundException
@ -48,14 +50,8 @@ object AssetManager {
case class AssetNotFound(message: String) extends Error case class AssetNotFound(message: String) extends Error
case class AssetLoadError(message: String) extends Error case class AssetLoadError(message: String) extends Error
case object CouldNotCastError extends Error case object CouldNotCastError extends Error
import cats.data.ReaderT object Error {
type IoReaderT[S, E, A] = ReaderT[UIO, S, Either[E, A]] implicit val show = Show.fromToString[Error]
val IoReaderT = ReaderT implicit val eq = Eq.fromUniversalEquals[Error]
val t = }
ReaderT[UIO, String, Either[Error, Unit]](s => UIO.unit.attempt)
.run("s")
.rethrow
val r: IoReaderT[String, Error, Unit] = IoReaderT(s => UIO.unit.attempt)
val t2 = r.run("s").rethrow
// Kleisli[IO, String, Unit](s => IO.unit)
} }

View File

@ -1,5 +1,7 @@
package wow.doge.mygame.utils.wrappers.jme package wow.doge.mygame.utils.wrappers.jme
import cats.Show
import cats.kernel.Eq
import com.jme3.bullet.collision.shapes.CollisionShape import com.jme3.bullet.collision.shapes.CollisionShape
import com.jme3.bullet.{util => jmebu} import com.jme3.bullet.{util => jmebu}
import com.jme3.scene.Spatial import com.jme3.scene.Spatial
@ -9,10 +11,17 @@ object CollisionShapeFactory {
sealed trait Error sealed trait Error
case class WrongArgumentError(reason: String) extends Error case class WrongArgumentError(reason: String) extends Error
object Error {
implicit val show = Show.fromToString[Error]
implicit val eq = Eq.fromUniversalEquals[Error]
}
def createMeshShape(subtree: Spatial): IO[Error, CollisionShape] = def createMeshShape(subtree: Spatial): IO[Error, CollisionShape] =
IO(jmebu.CollisionShapeFactory.createMeshShape(subtree)).onErrorHandleWith { IO(jmebu.CollisionShapeFactory.createMeshShape(subtree))
case ex: IllegalArgumentException .onErrorHandleWith {
if (ex.getMessage.startsWith("The spatial must either be a Node")) => case ex: IllegalArgumentException
IO.raiseError(WrongArgumentError(ex.getMessage)) if (ex.getMessage
} .startsWith("The spatial must either be a Node")) =>
IO.raiseError(WrongArgumentError(ex.getMessage))
}
} }

View File

@ -36,13 +36,11 @@ object NodeWrapper {
def +=(n: Node[F]) = nw.add(n) def +=(n: Node[F]) = nw.add(n)
def -=(n: jmes.Spatial) = nw.remove(n) def -=(n: jmes.Spatial) = nw.remove(n)
def -=(wn: Node[F]) = nw.remove(wn) def -=(wn: Node[F]) = nw.remove(wn)
def +=(light: Light) = { def +=(light: Light) =
nw.addLight(light) nw.addLight(light)
}
def -=(light: Light) = { def -=(light: Light) =
nw.removeLight(light) nw.removeLight(light)
}
} }
} }
@ -108,13 +106,11 @@ object NodeWrapper2 {
def +=(n: Node2) = nw.add(n) def +=(n: Node2) = nw.add(n)
def -=(n: jmes.Spatial) = nw.remove(n) def -=(n: jmes.Spatial) = nw.remove(n)
def -=(wn: Node2) = nw.remove(wn) def -=(wn: Node2) = nw.remove(wn)
def +=(light: Light) = { def +=(light: Light) =
nw.addLight(light) nw.addLight(light)
}
def -=(light: Light) = { def -=(light: Light) =
nw.removeLight(light) nw.removeLight(light)
}
} }
} }
@ -133,7 +129,7 @@ object Node2 {
final class AppNode2 private (node: jmes.Node) extends NodeWrapper2(node) final class AppNode2 private (node: jmes.Node) extends NodeWrapper2(node)
object AppNode2 { object AppNode2 {
// sealed trait Error extends NodeWrapper2.Error
def apply(name: String) = new AppNode2(new jmes.Node(name)) def apply(name: String) = new AppNode2(new jmes.Node(name))
def apply(n: jmes.Node) = new AppNode2(n) def apply(n: jmes.Node) = new AppNode2(n)

View File

@ -1,22 +1,22 @@
package wow.doge.mygame.utils.wrappers.jme package wow.doge.mygame.utils.wrappers.jme
import cats.effect.Sync
import com.jme3.{bullet => jmeb} import com.jme3.{bullet => jmeb}
import com.jme3.{scene => jmes} import com.jme3.{scene => jmes}
import monix.bio.UIO
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
final class PhysicsSpace[F[_]: Sync](space: jmeb.PhysicsSpace) { final class PhysicsSpace(space: jmeb.PhysicsSpace) {
def add(anyObject: Any) = Sync[F].delay(space.add(anyObject)) def add(anyObject: Any) = UIO(space.add(anyObject))
def remove(anyObject: Any) = def remove(anyObject: Any) =
Sync[F].delay { UIO {
space.remove(anyObject) space.remove(anyObject)
space space
} }
def addAll(spatial: jmes.Spatial) = Sync[F].delay(space.addAll(spatial)) def addAll(spatial: jmes.Spatial) = UIO(space.addAll(spatial))
def removeAll(spatial: jmes.Spatial) = def removeAll(spatial: jmes.Spatial) =
Sync[F].delay { UIO {
space.removeAll(spatial) space.removeAll(spatial)
space space
} }
@ -25,7 +25,7 @@ final class PhysicsSpace[F[_]: Sync](space: jmeb.PhysicsSpace) {
def physicsTickObservable = space.physicsTickObservable() def physicsTickObservable = space.physicsTickObservable()
} }
object PhysicsSpace { object PhysicsSpace {
implicit final class PhysicsSpaceOps[F[_]](private val space: PhysicsSpace[F]) implicit final class PhysicsSpaceOps(private val space: PhysicsSpace)
extends AnyVal { extends AnyVal {
def +=(anyObject: Any) = space.add(anyObject) def +=(anyObject: Any) = space.add(anyObject)

View File

@ -12,7 +12,11 @@ import wow.doge.mygame.utils.wrappers.jme.AssetManager.CouldNotCastError
import com.jme3.scene.Node import com.jme3.scene.Node
import com.jme3.material.MaterialDef import com.jme3.material.MaterialDef
import com.jme3.material.Material import com.jme3.material.Material
import scala.annotation.nowarn
@nowarn("msg=method get in class LeftProjection is deprecated")
@nowarn("msg=method right in class Either is deprecated")
@nowarn("msg=method get in class RightProjection is deprecated")
class AssetManagerTest extends AnyFunSuite { class AssetManagerTest extends AnyFunSuite {
val _assetManager: jmea.AssetManager = new DesktopAssetManager(true) val _assetManager: jmea.AssetManager = new DesktopAssetManager(true)
@ -21,7 +25,8 @@ class AssetManagerTest extends AnyFunSuite {
test("Test for AssetNotFound error") { test("Test for AssetNotFound error") {
val res = val res =
assetManager.loadModel(os.rel / "doesnotexist").attempt.runSyncUnsafe() assetManager.loadModel(os.rel / "doesnotexist").attempt.runSyncUnsafe()
assert(res === Left(AssetNotFound("doesnotexist"))) assert(res.isLeft)
assert(res.left.get eqv AssetNotFound("doesnotexist"))
} }
test("Test for Model CouldNotCastError") { test("Test for Model CouldNotCastError") {
@ -30,14 +35,15 @@ class AssetManagerTest extends AnyFunSuite {
.loadModelAs[Geometry](modelPath) .loadModelAs[Geometry](modelPath)
.attempt .attempt
.runSyncUnsafe() .runSyncUnsafe()
assert(res1.isLeft)
assert(res1 === Left(CouldNotCastError)) assert(res1.left.get eqv CouldNotCastError)
val res2 = assetManager val res2 = assetManager
.loadModelAs[Node](modelPath) .loadModelAs[Node](modelPath)
.attempt .attempt
.runSyncUnsafe() .runSyncUnsafe()
assert(res2.map(_.getName) === Right("JaimeGeom-ogremesh")) assert(res2.isRight)
assert(res2.map(_.getName).right.get eqv "JaimeGeom-ogremesh")
} }
test("Test for Asset CouldNotCastError") { test("Test for Asset CouldNotCastError") {
@ -47,13 +53,14 @@ class AssetManagerTest extends AnyFunSuite {
.loadAssetAs[Material](assetPath) .loadAssetAs[Material](assetPath)
.attempt .attempt
.runSyncUnsafe() .runSyncUnsafe()
assert(res1.isLeft)
assert(res1 === Left(CouldNotCastError)) assert(res1.left.get eqv CouldNotCastError)
val res2 = assetManager val res2 = assetManager
.loadAssetAs[MaterialDef](assetPath) .loadAssetAs[MaterialDef](assetPath)
.attempt .attempt
.runSyncUnsafe() .runSyncUnsafe()
assert(res2.map(_.getName) === Right("Unshaded")) assert(res2.isRight)
assert(res2.map(_.getName).right.get eqv "Unshaded")
} }
} }

View File

@ -18,12 +18,13 @@ class CollisionShapeFactoryTest extends AnyFunSuite {
.attempt .attempt
.runSyncUnsafe() .runSyncUnsafe()
assert(res.isLeft)
assert( assert(
res === Left( res.left.get eqv
CollisionShapeFactory.WrongArgumentError( CollisionShapeFactory.WrongArgumentError(
"The spatial must either be a Node or a Geometry!" "The spatial must either be a Node or a Geometry!"
) )
)
) )
} }
} }

View File

@ -0,0 +1,51 @@
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._
class ImVector3fTest extends AnyFunSuite with LazyLogging {
test("maxvalue") {
val v1 = ImVector3f.Max
val v2 = ImVector3f.Max
logger.info(ImVector3f.dst(v1, v2).show)
}
test("minvalue") {
val v1 = ImVector3f.Min
val v2 = ImVector3f.Min
logger.info(ImVector3f.dst(v1, v2).show)
}
test("maxvalue and unit") {
val v1 = ImVector3f.Max
val v2 = ImVector3f(1, 1, 1)
assert(ImVector3f.dst(v1, v2) eqv 5.8938631329669654e38)
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
}
test("minvalue and unit") {
val v1 = ImVector3f.Min
val v2 = ImVector3f(1, 1, 1)
assert(ImVector3f.dst(v1, v2) eqv 5.8938631329669654e38)
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
}
test("another") {
{
val v1 = ImVector3f(1, 0, 0)
val v2 = ImVector3f(1, 1, 1)
logger.info(ImVector3f.dst(v1, v2).show)
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
}
{
val v1 = ImVector3f(1, 1, 0)
val v2 = ImVector3f(1, 1, 1)
logger.info(ImVector3f.dst(v1, v2).show)
assert(ImVector3f.dst(v1, v2) eqv ImVector3f.dst(v2, v1))
}
}
}

View File

@ -0,0 +1,27 @@
package wow.doge.mygame
import org.scalatest.funsuite.AnyFunSuite
import wow.doge.mygame.subsystems.moddingsystem.ModdingSystem
import monix.execution.Scheduler.Implicits.global
import io.circe.Printer
import monix.bio.UIO
import cats.syntax.eq._
class ModdingSystemTest extends AnyFunSuite {
val printer = Printer.spaces2
test("main") {
val io = for {
res <- ModdingSystem.run()
_ <- UIO(
assert(
(res.parseSuccesses.length + res.parseFailures.length) eqv res.readSuccesses.length
)
)
_ <- ModdingSystem.log(res)
} yield res
io.attempt.runSyncUnsafe() match {
case Left(value) => pprint.log(value); ()
case Right(value) => ()
}
}
}

View File

@ -0,0 +1,67 @@
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
class ReaderT_Test extends AnyFunSuite {
// type IoReaderT[S, E, A] = ReaderT[UIO, S, Either[E, A]]
// val IoReaderT = ReaderT
// val t =
// ReaderT[UIO, String, Either[Error, Unit]](s => UIO.unit.attempt)
// .run("s")
// .rethrow
// val r: IoReaderT[String, Error, Unit] = IoReaderT(s => UIO.unit.attempt)
// 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)))
// def total: ReaderT[UIO, Environment, Unit] =
// for {
// _ <- fun1.local[Environment](_.str)
// _ <- fun2.local[Environment](_.num)
// } yield ()
// val uio: UIO[Unit] = total.run(Environment("hello", 50))
// uio.runSyncUnsafe()
// }
test("2") {
def fun1: ReaderT[IO[String, ?], String, Unit] =
ReaderT(s => IO(println(s)).onErrorHandleWith(_ => IO.raiseError("wow")))
def fun2: ReaderT[IO[String, ?], Int, Unit] =
ReaderT(num =>
IO(println(num)).onErrorHandleWith(_ => IO.raiseError("whew"))
)
def fun3: ReaderT[IO[String, ?], Environment, Unit] =
for {
env <- ReaderT.ask[IO[String, ?], Environment]
} yield ()
def fun4: ReaderT[IO[String, ?], Environment, Unit] =
for {
env <- ReaderT.ask[IO[String, ?], Environment]
_ <- ReaderT.liftF(
IO(println(env)).onErrorHandleWith(_ => IO.raiseError("wow"))
)
_ <- fun3
} yield ()
def app: ReaderT[IO[String, ?], Environment, Unit] =
for {
_ <- fun1.local[Environment](_.str)
_ <- fun2.local[Environment](_.num)
_ <- fun3
_ <- fun4
} yield ()
val io: IO[String, Unit] = app.run(Environment("hello", 50))
io.attempt.runSyncUnsafe()
}
}

View File

@ -0,0 +1,37 @@
package wow.doge.mygame
import cats.data.Reader
import monix.bio.UIO
import org.scalatest.funsuite.AnyFunSuite
import monix.execution.Scheduler.Implicits.global
class ReaderTest extends AnyFunSuite {
// type IoReaderT[S, E, A] = ReaderT[UIO, S, Either[E, A]]
// val IoReaderT = ReaderT
// val t =
// ReaderT[UIO, String, Either[Error, Unit]](s => UIO.unit.attempt)
// .run("s")
// .rethrow
// val r: IoReaderT[String, Error, Unit] = IoReaderT(s => UIO.unit.attempt)
// 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)))
def total: Reader[Environment, UIO[Unit]] =
for {
x <- fun1.local[Environment](_.str)
y <- fun2.local[Environment](_.num)
} yield UIO.parSequence(List(x, y)).void
val io: UIO[Unit] = total.run(Environment("hello", 50))
test("runTest") {
io.runSyncUnsafe()
}
}