forked from nova/jmonkey-test
daghgsre
This commit is contained in:
parent
2e05cb35fe
commit
89fad19d99
87
build.sbt
87
build.sbt
@ -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
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -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(
|
||||||
|
@ -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] = ???
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
31
src/main/scala/wow/doge/mygame/utils/ReaderDemo.scala
Normal file
31
src/main/scala/wow/doge/mygame/utils/ReaderDemo.scala
Normal 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))
|
||||||
|
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!"
|
||||||
)
|
)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
src/test/scala/wow/doge/mygame/ImVector3fTest.scala
Normal file
51
src/test/scala/wow/doge/mygame/ImVector3fTest.scala
Normal 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
27
src/test/scala/wow/doge/mygame/ModdingSystemTest.scala
Normal file
27
src/test/scala/wow/doge/mygame/ModdingSystemTest.scala
Normal 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) => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/test/scala/wow/doge/mygame/ReaderT_Test.scala
Normal file
67
src/test/scala/wow/doge/mygame/ReaderT_Test.scala
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/test/scala/wow/doge/mygame/ReaderTest.scala
Normal file
37
src/test/scala/wow/doge/mygame/ReaderTest.scala
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user