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"
|
||||
// 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 += "JME Bintray" at "https://bintray.com/jmonkeyengine/com.jme3"
|
||||
@ -49,12 +30,8 @@ lazy val root = (project in file(".")).settings(
|
||||
organization := "wow.doge",
|
||||
version := "1.0-SNAPSHOT",
|
||||
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,
|
||||
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-desktop
|
||||
"org.jmonkeyengine" % "jme3-desktop" % jmeVersion,
|
||||
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-lwjgl3
|
||||
"org.jmonkeyengine" % "jme3-lwjgl3" % jmeVersion,
|
||||
"org.jmonkeyengine" % "jme3-effects" % 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.circe" %% "circe-core" % "0.13.0",
|
||||
"io.circe" %% "circe-generic" % "0.13.0",
|
||||
"com.softwaremill.sttp.client" %% "core" % "2.2.5",
|
||||
"com.softwaremill.sttp.client" %% "monix" % "2.2.5",
|
||||
"com.softwaremill.sttp.client" %% "circe" % "2.2.5",
|
||||
"com.softwaremill.sttp.client" %% "async-http-client-backend-monix" % "2.2.5",
|
||||
"com.softwaremill.sttp.client3" %% "core" % "3.0.0",
|
||||
"com.softwaremill.sttp.client3" %% "monix" % "3.0.0",
|
||||
"com.softwaremill.sttp.client3" %% "circe" % "3.0.0",
|
||||
"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-json" % "0.9.1",
|
||||
"com.softwaremill.macwire" %% "util" % "2.3.7",
|
||||
"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.softwaremill.quicklens" %% "quicklens" % "1.6.1",
|
||||
"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",
|
||||
"com.beachape" %% "enumeratum-circe" % "1.6.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",
|
||||
"org.recast4j" % "recast" % "1.2.5",
|
||||
"org.recast4j" % "detour" % "1.2.5",
|
||||
@ -118,8 +92,6 @@ lazy val root = (project in file(".")).settings(
|
||||
"-Xlint",
|
||||
"-Ywarn-numeric-widen",
|
||||
"-Ymacro-annotations",
|
||||
// "-Xlint:byname-implicit",
|
||||
// "utf-8", // Specify character encoding used by source files.
|
||||
//silence warnings for by-name implicits
|
||||
"-Wconf:cat=lint-byname-implicit:s",
|
||||
//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)"""
|
||||
|
||||
// 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(
|
||||
"org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full
|
||||
|
@ -1,6 +1,40 @@
|
||||
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
|
||||
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
|
||||
).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
|
||||
jmeScheduler <- jMESchedulerResource
|
||||
// backend <- Resource.make(toIO(HttpClientMonixBackend()))(backend =>
|
||||
// toIO(backend.close())
|
||||
// )
|
||||
actorSystem <- actorSystemResource(logger, schedulers.async)
|
||||
_ <- Resource.liftF(
|
||||
new MainApp(
|
||||
|
@ -1,19 +1,22 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import cats.syntax.eq._
|
||||
import com.jme3.app.state.AppStateManager
|
||||
import com.jme3.asset.plugins.ZipLocator
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.material.Material
|
||||
import com.jme3.material.MaterialDef
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.renderer.Camera
|
||||
import com.jme3.renderer.RenderManager
|
||||
@ -27,6 +30,8 @@ import monix.bio.Fiber
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.eval.Coeval
|
||||
import monix.reactive.Observable
|
||||
import scalafx.scene.control.TextArea
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
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.LauncherResult
|
||||
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.GameEventBus
|
||||
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.scriptsystem.ScriptInitMode
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.utils.GenericConsoleStream
|
||||
import wow.doge.mygame.utils.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.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(
|
||||
logger: Logger[Task],
|
||||
@ -84,53 +86,66 @@ class MainApp(
|
||||
tickEventBus: GameEventBus[TickEvent]
|
||||
)
|
||||
|
||||
def gameInit: Resource[Task, Fiber[Throwable, Unit]] =
|
||||
wire[GameAppResource].resource.evalMap {
|
||||
case gameApp -> gameAppFib =>
|
||||
for {
|
||||
playerEventBus <- eventsModule.playerEventBusTask
|
||||
mainEventBus <- eventsModule.mainEventBusTask
|
||||
tickEventBus <- eventsModule.tickEventBusTask
|
||||
obs <- playerEventBus.askL[Observable[PlayerMovementEvent]](
|
||||
def eval(gameApp: GameApp, fib: Fiber[Nothing, Unit]) =
|
||||
for {
|
||||
// g <- UIO.pure(gameApp)
|
||||
playerEventBus <- eventsModule.playerEventBus
|
||||
mainEventBus <- eventsModule.mainEventBus
|
||||
tickEventBus <- eventsModule.tickEventBus
|
||||
obs <-
|
||||
playerEventBus
|
||||
.askL[Observable[PlayerMovementEvent]](
|
||||
ObservableSubscription(_)
|
||||
)
|
||||
_ <- IOUtils.toIO(
|
||||
.onErrorHandleWith {
|
||||
case ex: TimeoutException =>
|
||||
IO.raiseError(AppError.TimeoutError(ex.getMessage()))
|
||||
}
|
||||
_ <-
|
||||
IOUtils
|
||||
.toIO(
|
||||
obs
|
||||
.doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void)
|
||||
.completedL
|
||||
.startAndForget
|
||||
)
|
||||
inputManager <- gameApp.inputManager
|
||||
assetManager <- UIO.pure(gameApp.assetManager)
|
||||
stateManager <- gameApp.stateManager
|
||||
camera <- gameApp.camera
|
||||
rootNode <- UIO.pure(gameApp.rootNode)
|
||||
enqueueR <- UIO(gameApp.enqueue _)
|
||||
viewPort <- gameApp.viewPort
|
||||
physicsSpace <- UIO.pure(gameApp.physicsSpace)
|
||||
_ <- logger.info("before")
|
||||
// jfxUI <- gameApp.jfxUI
|
||||
gameAppActor <- gameApp.spawnGameActor(
|
||||
GameAppActor.Props(tickEventBus).behavior,
|
||||
"gameAppActor"
|
||||
)
|
||||
_ <- gameAppActor !! GameAppActor.Start
|
||||
consoleTextArea <- Task(new TextArea {
|
||||
text = "hello \n"
|
||||
editable = false
|
||||
wrapText = true
|
||||
// maxHeight = 150
|
||||
// maxWidth = 300
|
||||
})
|
||||
// _ <- Task(consoleStream := consoleTextArea)
|
||||
// _ <- Task(jfxUI += consoleTextArea)
|
||||
_ <- logger.info("after")
|
||||
_ <- logger.info("Initializing console stream")
|
||||
_ <-
|
||||
wire[MainAppDelegate]
|
||||
.init()
|
||||
.executeOn(gameApp.scheduler)
|
||||
} yield gameAppFib
|
||||
.hideErrors
|
||||
inputManager <- gameApp.inputManager
|
||||
assetManager <- UIO.pure(gameApp.assetManager)
|
||||
camera <- gameApp.camera
|
||||
rootNode <- UIO.pure(gameApp.rootNode)
|
||||
enqueueR <- UIO(gameApp.enqueue _)
|
||||
viewPort <- gameApp.viewPort
|
||||
physicsSpace <- UIO.pure(gameApp.physicsSpace)
|
||||
_ <- logger.infoU("before")
|
||||
// jfxUI <- gameApp.jfxUI
|
||||
gameAppActor <- gameApp.spawnGameActor(
|
||||
GameAppActor.Props(tickEventBus).behavior,
|
||||
"gameAppActor"
|
||||
)
|
||||
_ <- gameAppActor !! GameAppActor.Start
|
||||
consoleTextArea <- UIO(new TextArea {
|
||||
text = "hello \n"
|
||||
editable = false
|
||||
wrapText = true
|
||||
// maxHeight = 150
|
||||
// maxWidth = 300
|
||||
})
|
||||
// _ <- Task(consoleStream := consoleTextArea)
|
||||
// _ <- Task(jfxUI += consoleTextArea)
|
||||
_ <- logger.infoU("after")
|
||||
_ <- logger.infoU("Initializing console stream")
|
||||
_ <-
|
||||
wire[MainAppDelegate]
|
||||
.init()
|
||||
.executeOn(gameApp.scheduler)
|
||||
} 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 {
|
||||
@ -141,21 +156,24 @@ class MainApp(
|
||||
// } yield ()
|
||||
|
||||
val program = for {
|
||||
scriptSystem <- scriptSystemInit
|
||||
launchSignal <- Deferred[Task, Launcher.LauncherResult]
|
||||
// scriptSystem <- scriptSystemInit
|
||||
launchSignal <- Deferred[Task, Launcher.LauncherResult].hideErrors
|
||||
launcher <- new Launcher.Props(schedulers, launchSignal).create
|
||||
launchResult <- launcher.init.use(_ => launchSignal.get)
|
||||
launchResult <- launcher.init.use(_ => launchSignal.get).hideErrors
|
||||
_ <-
|
||||
/**
|
||||
* User chose to quit
|
||||
*/
|
||||
if (launchResult === LauncherResult.Exit)
|
||||
logger.info("Exiting")
|
||||
logger.infoU("Exiting")
|
||||
/**
|
||||
* User chose launch. Wait for game window to close
|
||||
*/
|
||||
else
|
||||
gameInit.use(_.join)
|
||||
gameInit.use {
|
||||
case Right(fib) => fib.join >> Task.unit
|
||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||
}.hideErrors
|
||||
|
||||
} yield ()
|
||||
}
|
||||
@ -170,12 +188,11 @@ class MainAppDelegate(
|
||||
tickEventBus: GameEventBus[TickEvent],
|
||||
inputManager: InputManager,
|
||||
assetManager: AssetManager,
|
||||
stateManager: AppStateManager,
|
||||
physicsSpace: PhysicsSpace[Task],
|
||||
physicsSpace: PhysicsSpace,
|
||||
camera: Camera,
|
||||
viewPort: ViewPort,
|
||||
enqueueR: Function1[() => Unit, Unit],
|
||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode
|
||||
rootNode: AppNode2 @@ GameAppTags.RootNode
|
||||
)(implicit
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
timeout: Timeout,
|
||||
@ -185,31 +202,28 @@ class MainAppDelegate(
|
||||
def init(
|
||||
// appScheduler: monix.execution.Scheduler
|
||||
// consoleStream: GenericConsoleStream[TextArea]
|
||||
) =
|
||||
): IO[AppError, Unit] =
|
||||
for {
|
||||
_ <- loggerL.info("Initializing Systems")
|
||||
_ <- loggerL.infoU("Initializing Systems")
|
||||
_ <- assetManager.registerLocator(
|
||||
os.rel / "assets" / "town.zip",
|
||||
classOf[ZipLocator]
|
||||
)
|
||||
_ <- loggerL.info("test")
|
||||
_ <- loggerL.infoU("test")
|
||||
// _ <- Task(consoleStream.println("text"))
|
||||
_ <- DefaultGameLevel(assetManager, viewPort)
|
||||
.flatMap(_.addToGame(rootNode, physicsSpace).hideErrors)
|
||||
.onErrorHandleWith(e => loggerL.error(e.toString))
|
||||
level <- DefaultGameLevel(assetManager, viewPort)
|
||||
_ <- level.addToGame(rootNode, physicsSpace)
|
||||
_ <- createPlayerController()
|
||||
.onErrorHandleWith(e => loggerL.error(e.toString))
|
||||
// .onErrorRestart(3)
|
||||
_ <- wire[GameInputHandler.Props].begin
|
||||
// .onErrorRestart(3)
|
||||
johnActor <- createTestNpc("John")
|
||||
.onErrorHandleWith(e => IO.raiseError(new Throwable(e.toString)))
|
||||
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
||||
|
||||
// _ <-
|
||||
// johnActor
|
||||
// .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
||||
// .delayExecution(2.seconds)
|
||||
_ <-
|
||||
johnActor
|
||||
.tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
||||
.delayExecution(2.seconds)
|
||||
// _ <-
|
||||
// IOUtils
|
||||
// .toIO(
|
||||
@ -224,8 +238,8 @@ class MainAppDelegate(
|
||||
|
||||
def createPlayerController(
|
||||
// appScheduler: monix.execution.Scheduler
|
||||
): IO[PlayerController.Error, ActorRef[PlayerActorSupervisor.Command]] = {
|
||||
val playerPos = ImVector3f.ZERO
|
||||
): IO[AppError, ActorRef[PlayerActorSupervisor.Command]] = {
|
||||
val playerPos = ImVector3f.Zero
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
val playerPhysicsControl =
|
||||
PlayerController.Defaults.defaultPlayerPhysicsControl
|
||||
@ -235,7 +249,7 @@ class MainAppDelegate(
|
||||
assetManager
|
||||
.loadModelAs[Node](modelPath)
|
||||
.map(_.withRotate(0, FastMath.PI, 0))
|
||||
.mapError(PlayerController.CouldNotCreatePlayerModel)
|
||||
.mapError(AppError.AssetManagerError)
|
||||
playerNode <- UIO(
|
||||
PlayerController.Defaults
|
||||
.defaultPlayerNode(
|
||||
@ -261,7 +275,7 @@ class MainAppDelegate(
|
||||
def createTestNpc(
|
||||
// appScheduler: monix.execution.Scheduler,
|
||||
npcName: String
|
||||
) = {
|
||||
): IO[AppError, ActorRef[NpcActorSupervisor.Command]] = {
|
||||
val initialPos = ImVector3f(50, 5, 0)
|
||||
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
// (1f, 2.1f, 10f)
|
||||
@ -284,10 +298,13 @@ class MainAppDelegate(
|
||||
s"${npcName}-npcActorSupervisor"
|
||||
)
|
||||
|
||||
for {
|
||||
materialDef <- assetManager.loadAssetAs[MaterialDef](
|
||||
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
)
|
||||
(for {
|
||||
materialDef <-
|
||||
assetManager
|
||||
.loadAssetAs[MaterialDef](
|
||||
os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
)
|
||||
.mapError(AppError.AssetManagerError)
|
||||
material = new Material(materialDef)
|
||||
_ = material.setColor("Color", ColorRGBA.Blue)
|
||||
mesh = PlayerController.Defaults.defaultMesh.withMaterial(material)
|
||||
@ -297,13 +314,13 @@ class MainAppDelegate(
|
||||
npcPhysicsControl,
|
||||
npcName
|
||||
)
|
||||
npcActor <- npcActorTask.hideErrors
|
||||
_ <- (for {
|
||||
_ <- physicsSpace += npcPhysicsControl
|
||||
_ <- physicsSpace += npcNode
|
||||
_ <- rootNode += npcNode
|
||||
} yield ()).hideErrors
|
||||
} yield npcActor
|
||||
} yield ()).mapError(AppError.AppNodeError)
|
||||
npcActor <- npcActorTask
|
||||
} yield npcActor)
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package wow.doge.mygame
|
||||
import akka.actor.BootstrapSetup
|
||||
import akka.actor.typed.ActorSystem
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import cats.effect.Resource
|
||||
import io.odin.Logger
|
||||
import monix.bio.Task
|
||||
import wow.doge.mygame.executors.ExecutorsModule
|
||||
import akka.actor.BootstrapSetup
|
||||
import monix.execution.Scheduler
|
||||
import wow.doge.mygame.executors.ExecutorsModule
|
||||
|
||||
trait MainModule extends ExecutorsModule {
|
||||
|
||||
|
@ -10,7 +10,6 @@ import akka.actor.typed.scaladsl.AskPattern._
|
||||
import akka.util.Timeout
|
||||
import cats.effect.Resource
|
||||
import cats.effect.concurrent.Deferred
|
||||
import com.jme3.app.state.AppStateManager
|
||||
import com.jme3.bullet.BulletAppState
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.scene.Node
|
||||
@ -20,6 +19,7 @@ import com.softwaremill.tagging._
|
||||
import com.typesafe.scalalogging.{Logger => SLogger}
|
||||
import io.odin.Logger
|
||||
import monix.bio.Fiber
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.catnap.ConcurrentChannel
|
||||
@ -28,14 +28,16 @@ import monix.eval.Coeval
|
||||
import monix.execution.CancelableFuture
|
||||
import monix.execution.CancelablePromise
|
||||
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.Schedulers
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
import wow.doge.mygame.game.subsystems.ui.JFxUI
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
import wow.doge.mygame.utils.wrappers.jme._
|
||||
import wow.doge.mygame.Dispatchers
|
||||
|
||||
object GameAppTags {
|
||||
sealed trait RootNode
|
||||
@ -49,18 +51,17 @@ class GameApp private[game] (
|
||||
gameActor: ActorRef[TestGameActor.Command],
|
||||
gameSpawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||
) {
|
||||
def stateManager: Task[AppStateManager] = Task(app.getStateManager())
|
||||
def inputManager: Task[InputManager] = Task(app.getInputManager())
|
||||
def inputManager: UIO[InputManager] = UIO(app.getInputManager())
|
||||
def assetManager = new AssetManager(app.getAssetManager())
|
||||
def guiNode = Task(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
|
||||
val guiNode2 = AppNode[Task](app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
||||
def guiNode = UIO(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
|
||||
val guiNode2 = AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
||||
def flyCam = Option(app.getFlyByCamera())
|
||||
def camera = Task(app.getCamera())
|
||||
def viewPort = Task(app.getViewPort())
|
||||
// def rootNode = Task(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
||||
def camera = UIO(app.getCamera())
|
||||
def viewPort = UIO(app.getViewPort())
|
||||
// def rootNode = UIO(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
||||
val rootNode =
|
||||
AppNode[Task](app.getRootNode()).taggedWith[GameAppTags.RootNode]
|
||||
val physicsSpace = new PhysicsSpace[Task](app.bulletAppState.physicsSpace)
|
||||
AppNode2(app.getRootNode()).taggedWith[GameAppTags.RootNode]
|
||||
val physicsSpace = new PhysicsSpace(app.bulletAppState.physicsSpace)
|
||||
def enqueue(cb: () => Unit) = app.enqueueR(() => cb())
|
||||
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
|
||||
|
||||
@ -89,13 +90,14 @@ class GameAppResource(
|
||||
scheduler: akka.actor.typed.Scheduler,
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||
) {
|
||||
def resource: Resource[Task, (GameApp, Fiber[Throwable, Unit])] =
|
||||
def resource
|
||||
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
|
||||
Resource.make {
|
||||
lazy val bullet = new BulletAppState
|
||||
for {
|
||||
app <- Task(new SimpleAppExt(schedulers, bullet))
|
||||
(for {
|
||||
app <- UIO(new SimpleAppExt(schedulers, bullet))
|
||||
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
|
||||
_ <- Task {
|
||||
_ <- UIO {
|
||||
val settings = new AppSettings(true)
|
||||
settings.setVSync(true)
|
||||
|
||||
@ -107,23 +109,27 @@ class GameAppResource(
|
||||
app.setSettings(settings)
|
||||
}
|
||||
|
||||
fib <- Task(app.start).executeOn(jmeThread).start
|
||||
_ <- Task.deferFuture(app.started)
|
||||
fib <- UIO(app.start).executeOn(jmeThread).start
|
||||
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
|
||||
testGameActor <- AkkaUtils.spawnActorL(
|
||||
new TestGameActor.Props().create,
|
||||
"testGameActor",
|
||||
Dispatchers.jmeDispatcher
|
||||
)
|
||||
sp <- testGameActor.askL(TestGameActor.GetSpawnProtocol(_))
|
||||
gameApp <- Task(new GameApp(logger, app, testGameActor, sp))
|
||||
_ <- Task {
|
||||
sp <-
|
||||
testGameActor
|
||||
.askL(TestGameActor.GetSpawnProtocol(_))
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp))
|
||||
_ <- UIO {
|
||||
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
|
||||
app.cancelToken = Some(fut)
|
||||
}
|
||||
} yield (gameApp, fib)
|
||||
} yield (gameApp, fib)).attempt
|
||||
} {
|
||||
case (gameApp, fib) =>
|
||||
case Right(gameApp -> fib) =>
|
||||
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.scaladsl.Behaviors
|
||||
import wow.doge.mygame.game.TickGenerator.Send
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent.PhysicsTick
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
|
||||
object GameAppActor {
|
||||
|
||||
|
@ -8,14 +8,13 @@ import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.implicits._
|
||||
object PlayerActorSupervisor {
|
||||
sealed trait Command
|
||||
final case class Props(
|
||||
|
@ -4,7 +4,6 @@ import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.util.Timeout
|
||||
import com.jme3.asset.AssetManager
|
||||
import com.jme3.bullet.BulletAppState
|
||||
import com.jme3.bullet.control.BetterCharacterControl
|
||||
import com.jme3.math.FastMath
|
||||
@ -16,8 +15,9 @@ import com.jme3.scene.Spatial
|
||||
import com.jme3.scene.shape.Box
|
||||
import com.softwaremill.tagging._
|
||||
import io.odin.Logger
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.AppError
|
||||
import wow.doge.mygame.game.GameAppTags
|
||||
import wow.doge.mygame.game.SimpleAppExt
|
||||
import wow.doge.mygame.implicits._
|
||||
@ -36,18 +36,15 @@ object PlayerControllerTags {
|
||||
|
||||
object PlayerController {
|
||||
sealed trait Error
|
||||
case class CouldNotCreatePlayerNode(reason: String) extends Error
|
||||
case class CouldNotCreatePlayerModel(
|
||||
reason: wow.doge.mygame.utils.wrappers.jme.AssetManager.Error
|
||||
) extends Error
|
||||
case class CouldNotCreatePlayerModel(reason: AssetManager.Error) extends Error
|
||||
|
||||
class Props(
|
||||
enqueueR: Function1[() => Unit, Unit],
|
||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||
rootNode: AppNode2 @@ GameAppTags.RootNode,
|
||||
loggerL: Logger[Task],
|
||||
// physicsSpace: com.jme3.bullet.PhysicsSpace,
|
||||
physicsSpace: PhysicsSpace[Task],
|
||||
initialPlayerPos: ImVector3f = ImVector3f.ZERO,
|
||||
physicsSpace: PhysicsSpace,
|
||||
initialPlayerPos: ImVector3f = ImVector3f.Zero,
|
||||
playerEventBus: GameEventBus[PlayerEvent],
|
||||
playerPhysicsControl: BetterCharacterControl,
|
||||
// appScheduler: monix.execution.Scheduler,
|
||||
@ -78,16 +75,18 @@ object PlayerController {
|
||||
cameraActorBeh
|
||||
).behavior
|
||||
}
|
||||
val create: UIO[ActorRef[PlayerActorSupervisor.Command]] =
|
||||
val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
|
||||
(for {
|
||||
playerActor <-
|
||||
AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
|
||||
_ <- rootNode += playerNode
|
||||
_ <- physicsSpace += playerNode
|
||||
_ <- physicsSpace += playerPhysicsControl
|
||||
_ = cameraPivotNode += cameraNode
|
||||
_ <- rootNode += cameraPivotNode
|
||||
} yield playerActor).hideErrors
|
||||
_ <- (for {
|
||||
_ <- rootNode += playerNode
|
||||
_ <- physicsSpace += playerNode
|
||||
_ <- physicsSpace += playerPhysicsControl
|
||||
_ = cameraPivotNode += cameraNode
|
||||
_ <- rootNode += cameraPivotNode
|
||||
} yield ()).mapError(AppError.AppNodeError)
|
||||
} yield playerActor)
|
||||
|
||||
}
|
||||
|
||||
@ -96,7 +95,7 @@ object PlayerController {
|
||||
modelPath: os.RelPath,
|
||||
cam: Camera
|
||||
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
|
||||
val playerPos = ImVector3f.ZERO
|
||||
val playerPos = ImVector3f.Zero
|
||||
val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||
val playerNode = new Node("PlayerNode")
|
||||
@ -152,7 +151,7 @@ object PlayerController {
|
||||
new CameraNode("CameraNode", cam)
|
||||
// .withControlDir(ControlDirection.SpatialToCamera)
|
||||
.withLocalTranslation(ImVector3f(0, 1.5f, 10))
|
||||
.withLookAt(playerPos, ImVector3f.UNIT_Y)
|
||||
.withLookAt(playerPos, ImVector3f.UnitY)
|
||||
|
||||
def defaultPlayerNode(
|
||||
playerPos: ImVector3f,
|
||||
|
@ -9,7 +9,7 @@ import com.jme3.input.KeyInput
|
||||
import com.jme3.input.MouseInput
|
||||
import com.jme3.input.controls.KeyTrigger
|
||||
import com.jme3.input.controls.MouseAxisTrigger
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.{eval => me}
|
||||
import wow.doge.mygame.implicits._
|
||||
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.PlayerMovementEvent
|
||||
import wow.doge.mygame.utils.IOUtils._
|
||||
import monix.bio.UIO
|
||||
|
||||
object GameInputHandler {
|
||||
|
||||
|
@ -8,7 +8,9 @@ import com.jme3.math.ColorRGBA
|
||||
import com.jme3.math.Vector3f
|
||||
import com.jme3.renderer.ViewPort
|
||||
import com.jme3.scene.Spatial
|
||||
import monix.bio.IO
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.AppError
|
||||
object DefaultGameLevel {
|
||||
|
||||
def apply(
|
||||
@ -51,7 +53,7 @@ object DefaultGameLevel {
|
||||
def apply(
|
||||
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager,
|
||||
viewPort: ViewPort
|
||||
) =
|
||||
): IO[AppError, GameLevel] =
|
||||
// for {
|
||||
// sceneModel <- assetManager.loadModelAs[Node](os.rel / "main.scene")
|
||||
// sceneShape <- UIO(CollisionShapeFactory.createMeshShape(sceneModel))
|
||||
|
@ -8,11 +8,10 @@ import com.jme3.scene.Node
|
||||
import com.jme3.scene.Spatial
|
||||
import com.softwaremill.tagging._
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.AppError
|
||||
import wow.doge.mygame.game.GameAppTags
|
||||
import wow.doge.mygame.utils.wrappers.jme.AppNode
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
||||
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||
|
||||
@ -23,21 +22,20 @@ class GameLevel(
|
||||
val directionalLight: DirectionalLight
|
||||
) {
|
||||
def addToGame(
|
||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||
physicsSpace: PhysicsSpace[Task]
|
||||
) = {
|
||||
for {
|
||||
rootNode: AppNode2 @@ GameAppTags.RootNode,
|
||||
physicsSpace: PhysicsSpace
|
||||
) =
|
||||
(for {
|
||||
_ <- rootNode += model
|
||||
_ <- rootNode += ambientLight
|
||||
_ <- rootNode += directionalLight
|
||||
_ <- physicsSpace += model
|
||||
_ <- physicsSpace += physicsControl
|
||||
} yield ()
|
||||
}
|
||||
} yield ()).mapError(AppError.AppNodeError)
|
||||
def removeFromGame(
|
||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||
physicsSpace: PhysicsSpace[Task]
|
||||
) = {
|
||||
rootNode: AppNode2 @@ GameAppTags.RootNode,
|
||||
physicsSpace: PhysicsSpace
|
||||
) =
|
||||
for {
|
||||
_ <- rootNode -= model
|
||||
_ <- rootNode -= ambientLight
|
||||
@ -45,11 +43,10 @@ class GameLevel(
|
||||
_ <- physicsSpace -= model
|
||||
_ <- physicsSpace -= physicsControl
|
||||
} yield ()
|
||||
}
|
||||
|
||||
def resource(
|
||||
rootNode: AppNode[Task] @@ GameAppTags.RootNode,
|
||||
physicsSpace: PhysicsSpace[Task]
|
||||
rootNode: AppNode2 @@ GameAppTags.RootNode,
|
||||
physicsSpace: PhysicsSpace
|
||||
) =
|
||||
Resource.make(this.addToGame(rootNode, physicsSpace))(_ =>
|
||||
this.removeFromGame(rootNode, physicsSpace)
|
||||
@ -57,10 +54,6 @@ class GameLevel(
|
||||
}
|
||||
|
||||
object GameLevel {
|
||||
sealed trait Error
|
||||
case class AssetLoadError(err: AssetManager.Error) extends Error
|
||||
case class CollisionShapeCreationFailed(err: CollisionShapeFactory.Error)
|
||||
extends Error
|
||||
|
||||
def apply(
|
||||
modelPath: os.RelPath,
|
||||
@ -68,16 +61,16 @@ object GameLevel {
|
||||
dl: DirectionalLight
|
||||
)(implicit
|
||||
assetManager: wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
): IO[Error, GameLevel] =
|
||||
): IO[AppError, GameLevel] =
|
||||
for {
|
||||
sceneModel <-
|
||||
assetManager
|
||||
.loadModelAs[Node](modelPath)
|
||||
.mapError(AssetLoadError)
|
||||
.mapError(AppError.AssetManagerError)
|
||||
sceneShape <-
|
||||
CollisionShapeFactory
|
||||
.createMeshShape(sceneModel)
|
||||
.mapError(CollisionShapeCreationFailed)
|
||||
.mapError(AppError.CollisionShapeCreationFailed)
|
||||
landscape <- UIO(new RigidBodyControl(sceneShape, 0))
|
||||
|
||||
} yield new GameLevel(
|
||||
|
@ -21,9 +21,8 @@ trait CanMove2[-A, F[_]] {
|
||||
|
||||
object Test {
|
||||
val x = new BetterCharacterControl(4, 10, 5)
|
||||
def test[T](x: T)(implicit cm: CanMove2[T, Id]) = {
|
||||
cm.move(x, ImVector3f.ZERO)
|
||||
}
|
||||
def test[T](x: T)(implicit cm: CanMove2[T, Id]) =
|
||||
cm.move(x, ImVector3f.Zero)
|
||||
}
|
||||
|
||||
object CanMove2 {
|
||||
@ -36,7 +35,7 @@ object CanMove2 {
|
||||
): Id[Unit] = {}
|
||||
|
||||
override def location(inst: BetterCharacterControl): Id[ImVector3f] =
|
||||
ImVector3f.ZERO
|
||||
ImVector3f.Zero
|
||||
|
||||
override def jump(inst: BetterCharacterControl): Id[Unit] = ???
|
||||
|
||||
|
@ -129,7 +129,7 @@ class ImMovementActor[T](
|
||||
}
|
||||
object Methods {
|
||||
def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = {
|
||||
val zero = ImVector3f.ZERO
|
||||
val zero = ImVector3f.Zero
|
||||
val dir = cardinalDir
|
||||
val walkDir = {
|
||||
val mutWalkDir = new Vector3f()
|
||||
|
@ -1,14 +1,13 @@
|
||||
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.effect.Bracket
|
||||
import monix.bio.Task
|
||||
import cats.Show
|
||||
import cats.data.Kleisli
|
||||
import cats.effect.Resource
|
||||
import cats.syntax.show._
|
||||
import monix.bio.IO
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
|
||||
trait CatsExtensions {
|
||||
implicit class KleisliCompanionExt(k: Kleisli.type) {
|
||||
|
@ -5,6 +5,7 @@ import cats.effect.concurrent.Deferred
|
||||
import cats.kernel.Eq
|
||||
import javafx.application.Platform
|
||||
import monix.bio.Task
|
||||
import monix.bio.UIO
|
||||
import monix.catnap.CancelableF
|
||||
import monix.execution.CancelablePromise
|
||||
import monix.reactive.Observable
|
||||
@ -30,7 +31,7 @@ object Launcher {
|
||||
val schedulers: Schedulers,
|
||||
val signal: Deferred[Task, LauncherResult]
|
||||
) {
|
||||
val create = Task(new Launcher(this))
|
||||
val create = UIO(new Launcher(this))
|
||||
}
|
||||
}
|
||||
class Launcher private (props: Launcher.Props) {
|
||||
|
@ -1,29 +1,44 @@
|
||||
package wow.doge.mygame.math;
|
||||
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import cats.syntax.eq._
|
||||
|
||||
import math.{abs, pow, sqrt}
|
||||
|
||||
case class ImVector3f(x: Float, y: Float, z: Float)
|
||||
object ImVector3f {
|
||||
val ZERO = ImVector3f(0, 0, 0)
|
||||
val UNIT_X = ImVector3f(1, 0, 0)
|
||||
val UNIT_Y = ImVector3f(0, 1, 0)
|
||||
val UNIT_Z = ImVector3f(0, 0, 1)
|
||||
//format: off
|
||||
val Zero = ImVector3f(0, 0, 0)
|
||||
val UnitX = ImVector3f(1, 0, 0)
|
||||
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) =
|
||||
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] {
|
||||
implicit val show = new Show[ImVector3f] {
|
||||
def format(f: Float) = f.formatted("%.2f")
|
||||
override def show(t: ImVector3f): String =
|
||||
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
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.Random
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
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.SpawnProtocol
|
||||
import scala.util.Random
|
||||
import akka.actor.typed.scaladsl.AskPattern._
|
||||
import monix.execution.Cancelable
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.implicits._
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.event.EventStream
|
||||
import akka.util.Timeout
|
||||
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.utils.AkkaUtils
|
||||
|
||||
/**
|
||||
* A (typed) event bus
|
||||
@ -81,6 +81,7 @@ object EventBus {
|
||||
behavior,
|
||||
s"eventBusObservable-${ct.toString}-${Random.nextLong()}"
|
||||
)
|
||||
.mapError(err => new Exception(err.toString))
|
||||
.tapError {
|
||||
case ex => UIO(sub.onError(ex))
|
||||
}
|
||||
@ -116,6 +117,7 @@ object EventBus {
|
||||
behavior,
|
||||
s"eventBusObservable-${ct.toString}-${math.abs(Random.nextLong())}"
|
||||
)
|
||||
.mapError(err => new Throwable(err.toString))
|
||||
.tapError {
|
||||
case ex => UIO(sub.onError(ex))
|
||||
}
|
||||
|
@ -1,63 +1,75 @@
|
||||
package wow.doge.mygame.subsystems.events
|
||||
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.LogOptions
|
||||
import akka.actor.typed.Props
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
import akka.actor.typed.SupervisorStrategy
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.util.Timeout
|
||||
import com.typesafe.scalalogging.{Logger => SLogger}
|
||||
import monix.bio.IO
|
||||
import org.slf4j.event.Level
|
||||
import wow.doge.mygame.AppError
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.Event
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import scala.reflect.ClassTag
|
||||
import akka.actor.typed.Scheduler
|
||||
|
||||
class EventsModule(
|
||||
scheduler: Scheduler,
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||
) {
|
||||
import EventsModule._
|
||||
implicit val s = scheduler
|
||||
implicit val sp = spawnProtocol
|
||||
implicit val timeout = Timeout(1.second)
|
||||
|
||||
val eventBusLogger = SLogger[EventBus[_]]
|
||||
|
||||
val playerEventBusTask =
|
||||
val playerEventBus: IO[AppError, GameEventBus[PlayerEvent]] =
|
||||
createEventBus[PlayerEvent]("playerEventBus")
|
||||
|
||||
// val playerCameraEventBusTask =
|
||||
// createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
|
||||
|
||||
val tickEventBusTask =
|
||||
val tickEventBus: IO[AppError, GameEventBus[TickEvent]] =
|
||||
createEventBus[TickEvent]("tickEventBus", Level.TRACE)
|
||||
|
||||
val mainEventBusTask = createEventBus[Event]("mainEventBus")
|
||||
val mainEventBus: IO[AppError, GameEventBus[Event]] =
|
||||
createEventBus[Event]("mainEventBus")
|
||||
|
||||
def createEventBus[T: ClassTag](
|
||||
busName: String,
|
||||
logLevel: Level = Level.DEBUG
|
||||
) =
|
||||
spawnProtocol.askL(
|
||||
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||
Behaviors.logMessages(
|
||||
logOptions = LogOptions()
|
||||
.withLevel(logLevel)
|
||||
.withLogger(eventBusLogger.underlying),
|
||||
Behaviors
|
||||
.supervise(EventBus[T]())
|
||||
.onFailure[Exception](SupervisorStrategy.restart)
|
||||
),
|
||||
busName,
|
||||
Props.empty,
|
||||
_
|
||||
spawnProtocol
|
||||
.askL(
|
||||
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||
Behaviors.logMessages(
|
||||
logOptions = LogOptions()
|
||||
.withLevel(logLevel)
|
||||
.withLogger(eventBusLogger.underlying),
|
||||
Behaviors
|
||||
.supervise(EventBus[T]())
|
||||
.onFailure[Exception](SupervisorStrategy.restart)
|
||||
),
|
||||
busName,
|
||||
Props.empty,
|
||||
_
|
||||
)
|
||||
)
|
||||
)
|
||||
.onErrorHandleWith {
|
||||
case ex: TimeoutException =>
|
||||
IO.raiseError(AppError.TimeoutError(ex.getMessage))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object EventsModule {
|
||||
|
@ -4,9 +4,10 @@ import java.nio.file.NoSuchFileException
|
||||
|
||||
import scala.collection.View
|
||||
import scala.collection.immutable.ArraySeq
|
||||
import scala.util.Try
|
||||
|
||||
import cats.Show
|
||||
import cats.implicits._
|
||||
import cats.kernel.Eq
|
||||
import io.circe._
|
||||
import io.circe.generic.JsonCodec
|
||||
import io.circe.generic.semiauto._
|
||||
@ -17,33 +18,45 @@ import monix.reactive.Consumer
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.utils.IOUtils
|
||||
|
||||
import IOUtils.toIO
|
||||
|
||||
@JsonCodec
|
||||
final case class Test1(hello1: String, hello2: String)
|
||||
@JsonCodec
|
||||
final case class Test2(hello1: String)
|
||||
final case class Plugin(name: String, priority: Int)
|
||||
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 {
|
||||
sealed trait Error
|
||||
final case class CouldNotDecode(cause: String) extends Error
|
||||
final case class ParseFailure(cause: String) extends Error
|
||||
final case class FileNotFound(fileName: String) extends Error
|
||||
case object GenericError extends Error
|
||||
final case class ParseFailure(cause: io.circe.ParsingFailure) extends Error
|
||||
final case class DecodingFailure(cause: io.circe.DecodingFailure)
|
||||
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]]] =
|
||||
Try(
|
||||
parse(os.read(dir / "plugins.json"))
|
||||
.map(
|
||||
_.as[ArraySeq[Plugin]]
|
||||
.leftMap(e => CouldNotDecode(e.getMessage()))
|
||||
)
|
||||
.leftMap((e: ParsingFailure) => ParseFailure(e.message))
|
||||
.flatten
|
||||
)
|
||||
// .toValidated
|
||||
def readPluginsList(dir: os.Path): IO[Error, ArraySeq[Plugin]] =
|
||||
IO(parse(os.read(dir / "plugins.json")))
|
||||
.onErrorHandleWith {
|
||||
case _: FileNotFoundException =>
|
||||
IO.raiseError(FileNotFound(dir / "plugins.json"))
|
||||
case _: NoSuchFileException =>
|
||||
IO.raiseError(FileNotFound(dir / "plugins.json"))
|
||||
}
|
||||
.flatMap(files =>
|
||||
IO.fromEither(files)
|
||||
.map(_.as[ArraySeq[Plugin]])
|
||||
.mapError(ParseFailure)
|
||||
)
|
||||
.flatMap(result => IO.fromEither(result).mapError(DecodingFailure))
|
||||
|
||||
def findPluginFiles(dir: os.Path): View[os.Path] =
|
||||
os.list(dir)
|
||||
@ -53,35 +66,102 @@ object ModdingSystem {
|
||||
def findAndReadPluginFiles(
|
||||
dir: os.Path,
|
||||
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
|
||||
.sortBy(_.priority)
|
||||
.view
|
||||
.map(p =>
|
||||
p ->
|
||||
Either
|
||||
.catchNonFatal {
|
||||
val path = dir / os.RelPath(p.name + ".plugin.json")
|
||||
os.read(path)
|
||||
}
|
||||
.leftMap {
|
||||
case _: FileNotFoundException =>
|
||||
FileNotFound(p.name)
|
||||
case _: NoSuchFileException => FileNotFound(p.name)
|
||||
case e => GenericError
|
||||
}
|
||||
)
|
||||
.partitionMap {
|
||||
case (p, either) =>
|
||||
either match {
|
||||
.map { p =>
|
||||
val path = dir / os.RelPath(p.name + ".plugin.json")
|
||||
p -> IO(os.read(path))
|
||||
.onErrorHandleWith {
|
||||
case _: FileNotFoundException => IO.raiseError(FileNotFound(path))
|
||||
case _: NoSuchFileException => IO.raiseError(FileNotFound(path))
|
||||
}
|
||||
// .map(r => p -> r)
|
||||
|
||||
}
|
||||
.map {
|
||||
case (p, io) =>
|
||||
io.attempt.map {
|
||||
case Left(value) => Left(p -> value)
|
||||
case Right(value) => Right(p -> value)
|
||||
}
|
||||
}
|
||||
.to(List)
|
||||
.parSequence
|
||||
|
||||
def readPluginFiles(filePaths: View[os.Path]) =
|
||||
filePaths.map(path => os.read(path))
|
||||
// .partitionMap {
|
||||
// _.map {
|
||||
// case l @ Left(value) => l
|
||||
// case r @ Right(value) => r
|
||||
// }
|
||||
// }
|
||||
// .sequence
|
||||
|
||||
// def readPluginFiles(filePaths: View[os.Path]) =
|
||||
// filePaths.map(path => os.read(path))
|
||||
|
||||
// def readPluginFiles2(filePaths: View[os.Path]) =
|
||||
// filePaths
|
||||
// .map(path =>
|
||||
// IO(os.read(path)).onErrorHandleWith {
|
||||
// case _: FileNotFoundException =>
|
||||
// IO.raiseError(FileNotFound(path))
|
||||
// case _: NoSuchFileException =>
|
||||
// IO.raiseError(FileNotFound(path))
|
||||
// }
|
||||
// )
|
||||
// .to(List)
|
||||
// .parSequence
|
||||
def parsePluginFiles(files: View[(Plugin, String)]) =
|
||||
files
|
||||
.map {
|
||||
@ -92,73 +172,59 @@ object ModdingSystem {
|
||||
case (p, Right(value)) => Right(p -> value)
|
||||
}
|
||||
|
||||
def foldMerge(iterable: Iterable[Json]) =
|
||||
iterable.foldLeft(Json.fromString("empty")) {
|
||||
case (json, io.circe.Json.Null) => json //ignore null values
|
||||
case (json, value) => json.deepMerge(value)
|
||||
}
|
||||
val emptyJson = Json.fromString("empty")
|
||||
|
||||
def mergePluginData(plugins: View[(Plugin, Json)]) =
|
||||
foldMerge(plugins.map {
|
||||
case (p, json) => json
|
||||
})
|
||||
val foldFn: (Json, Json) => Json = {
|
||||
case (json, Json.Null) => json //ignore null values
|
||||
case (json, value) => json.deepMerge(value)
|
||||
}
|
||||
|
||||
def mergePluginDataConsumer =
|
||||
Consumer.foldLeft[Json, Json](Json.fromString("empty")) {
|
||||
case (json, io.circe.Json.Null) => json
|
||||
case (json, that) => json.deepMerge(that)
|
||||
}
|
||||
Consumer.foldLeft[Json, Json](emptyJson)(foldFn)
|
||||
|
||||
def loadBalancedPluginDataMerger =
|
||||
Consumer
|
||||
.loadBalance(parallelism = 2, mergePluginDataConsumer)
|
||||
.map(foldMerge)
|
||||
.map(_.foldLeft(emptyJson)(foldFn))
|
||||
|
||||
// def test =
|
||||
// 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) =
|
||||
def run(wd: os.Path = os.pwd) =
|
||||
for {
|
||||
plugins <- IO.fromTryEither(readPluginsList(wd))
|
||||
(readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins))
|
||||
plugins <- readPluginsList(wd)
|
||||
(readFailures, readSuccesses) <- findAndReadPluginFiles(wd, plugins)
|
||||
(parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
|
||||
// res <- UIO(mergePluginData(parseSuccesses))
|
||||
res <-
|
||||
IOUtils
|
||||
.toIO(
|
||||
Observable
|
||||
.fromIterable(parseSuccesses)
|
||||
.map { case (p, json) => json }
|
||||
.consumeWith(loadBalancedPluginDataMerger)
|
||||
)
|
||||
.hideErrors
|
||||
_ <- UIO {
|
||||
println(s"Read Successes = ${readSuccesses.to(Seq)}")
|
||||
println(s"Read Failures = ${readFailures.to(Seq)}")
|
||||
println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
|
||||
println(s"Parse Failures = ${parseFailures.to(Seq)}")
|
||||
println(show"Merged = $res")
|
||||
}
|
||||
} yield ()
|
||||
res <- UIO.parMap5(
|
||||
UIO(readFailures.to(List)),
|
||||
UIO(readSuccesses.to(List)),
|
||||
UIO(parseFailures.to(List)),
|
||||
UIO(parseSuccesses.to(List)),
|
||||
toIO(
|
||||
Observable
|
||||
.fromIterable(parseSuccesses)
|
||||
.map { case (p, json) => json }
|
||||
.consumeWith(loadBalancedPluginDataMerger)
|
||||
).hideErrors
|
||||
)(Result.apply)
|
||||
} yield res
|
||||
|
||||
// monix.eval.Task.deferAction(implicit s =>
|
||||
// ModdingSystem
|
||||
// .test()
|
||||
// .leftMap(e => new Throwable(e.toString()))
|
||||
// .to[monix.eval.Task]
|
||||
// )
|
||||
def log(res: Result) =
|
||||
UIO {
|
||||
pprint.log(show"Read Successes = ${res.readSuccesses}")
|
||||
pprint.log(show"Read Failures = ${res.readFailures}")
|
||||
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 {
|
||||
scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts"))
|
||||
scriptFiles <- Task(
|
||||
findScriptFiles(os.pwd / "assets" / "scripts")
|
||||
).hideErrors
|
||||
scriptCacheActor <- AkkaUtils.spawnActorL(
|
||||
ScriptCachingActor(),
|
||||
"scriptCachingActor"
|
||||
)
|
||||
} yield (scriptCacheActor)
|
||||
} yield scriptCacheActor
|
||||
|
||||
def findScriptFiles(wd: os.Path) =
|
||||
os.walk
|
||||
|
@ -6,10 +6,8 @@ import akka.actor.typed.Props
|
||||
import akka.actor.typed.Scheduler
|
||||
import akka.actor.typed.SpawnProtocol
|
||||
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.implicits._
|
||||
|
||||
object AkkaUtils {
|
||||
|
||||
@ -44,7 +42,5 @@ object AkkaUtils {
|
||||
_
|
||||
)
|
||||
)
|
||||
// .onErrorHandleWith {
|
||||
// case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
|
||||
// }
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
package wow.doge.mygame.utils
|
||||
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import scala.util.Random
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
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.Behaviors
|
||||
import akka.actor.typed.scaladsl.TimerScheduler
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
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
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import com.jme3.asset.AssetLoadException
|
||||
import com.jme3.asset.AssetLocator
|
||||
import com.jme3.asset.AssetNotFoundException
|
||||
@ -48,14 +50,8 @@ object AssetManager {
|
||||
case class AssetNotFound(message: String) extends Error
|
||||
case class AssetLoadError(message: String) extends Error
|
||||
case object CouldNotCastError extends Error
|
||||
import cats.data.ReaderT
|
||||
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)
|
||||
object Error {
|
||||
implicit val show = Show.fromToString[Error]
|
||||
implicit val eq = Eq.fromUniversalEquals[Error]
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
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.{util => jmebu}
|
||||
import com.jme3.scene.Spatial
|
||||
@ -9,10 +11,17 @@ object CollisionShapeFactory {
|
||||
sealed trait 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] =
|
||||
IO(jmebu.CollisionShapeFactory.createMeshShape(subtree)).onErrorHandleWith {
|
||||
case ex: IllegalArgumentException
|
||||
if (ex.getMessage.startsWith("The spatial must either be a Node")) =>
|
||||
IO.raiseError(WrongArgumentError(ex.getMessage))
|
||||
}
|
||||
IO(jmebu.CollisionShapeFactory.createMeshShape(subtree))
|
||||
.onErrorHandleWith {
|
||||
case ex: IllegalArgumentException
|
||||
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: jmes.Spatial) = nw.remove(n)
|
||||
def -=(wn: Node[F]) = nw.remove(wn)
|
||||
def +=(light: Light) = {
|
||||
def +=(light: Light) =
|
||||
nw.addLight(light)
|
||||
}
|
||||
|
||||
def -=(light: Light) = {
|
||||
def -=(light: Light) =
|
||||
nw.removeLight(light)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,13 +106,11 @@ object NodeWrapper2 {
|
||||
def +=(n: Node2) = nw.add(n)
|
||||
def -=(n: jmes.Spatial) = nw.remove(n)
|
||||
def -=(wn: Node2) = nw.remove(wn)
|
||||
def +=(light: Light) = {
|
||||
def +=(light: Light) =
|
||||
nw.addLight(light)
|
||||
}
|
||||
|
||||
def -=(light: Light) = {
|
||||
def -=(light: Light) =
|
||||
nw.removeLight(light)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +129,7 @@ object Node2 {
|
||||
|
||||
final class AppNode2 private (node: jmes.Node) extends NodeWrapper2(node)
|
||||
object AppNode2 {
|
||||
|
||||
// sealed trait Error extends NodeWrapper2.Error
|
||||
def apply(name: String) = new AppNode2(new jmes.Node(name))
|
||||
def apply(n: jmes.Node) = new AppNode2(n)
|
||||
|
||||
|
@ -1,22 +1,22 @@
|
||||
package wow.doge.mygame.utils.wrappers.jme
|
||||
import cats.effect.Sync
|
||||
import com.jme3.{bullet => jmeb}
|
||||
import com.jme3.{scene => jmes}
|
||||
import monix.bio.UIO
|
||||
import wow.doge.mygame.implicits._
|
||||
|
||||
final class PhysicsSpace[F[_]: Sync](space: jmeb.PhysicsSpace) {
|
||||
def add(anyObject: Any) = Sync[F].delay(space.add(anyObject))
|
||||
final class PhysicsSpace(space: jmeb.PhysicsSpace) {
|
||||
def add(anyObject: Any) = UIO(space.add(anyObject))
|
||||
|
||||
def remove(anyObject: Any) =
|
||||
Sync[F].delay {
|
||||
UIO {
|
||||
space.remove(anyObject)
|
||||
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) =
|
||||
Sync[F].delay {
|
||||
UIO {
|
||||
space.removeAll(spatial)
|
||||
space
|
||||
}
|
||||
@ -25,7 +25,7 @@ final class PhysicsSpace[F[_]: Sync](space: jmeb.PhysicsSpace) {
|
||||
def physicsTickObservable = space.physicsTickObservable()
|
||||
}
|
||||
object PhysicsSpace {
|
||||
implicit final class PhysicsSpaceOps[F[_]](private val space: PhysicsSpace[F])
|
||||
implicit final class PhysicsSpaceOps(private val space: PhysicsSpace)
|
||||
extends AnyVal {
|
||||
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.material.MaterialDef
|
||||
import com.jme3.material.Material
|
||||
import scala.annotation.nowarn
|
||||
|
||||
@nowarn("msg=method get in class LeftProjection is deprecated")
|
||||
@nowarn("msg=method right in class Either is deprecated")
|
||||
@nowarn("msg=method get in class RightProjection is deprecated")
|
||||
class AssetManagerTest extends AnyFunSuite {
|
||||
|
||||
val _assetManager: jmea.AssetManager = new DesktopAssetManager(true)
|
||||
@ -21,7 +25,8 @@ class AssetManagerTest extends AnyFunSuite {
|
||||
test("Test for AssetNotFound error") {
|
||||
val res =
|
||||
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") {
|
||||
@ -30,14 +35,15 @@ class AssetManagerTest extends AnyFunSuite {
|
||||
.loadModelAs[Geometry](modelPath)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
|
||||
assert(res1 === Left(CouldNotCastError))
|
||||
assert(res1.isLeft)
|
||||
assert(res1.left.get eqv CouldNotCastError)
|
||||
|
||||
val res2 = assetManager
|
||||
.loadModelAs[Node](modelPath)
|
||||
.attempt
|
||||
.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") {
|
||||
@ -47,13 +53,14 @@ class AssetManagerTest extends AnyFunSuite {
|
||||
.loadAssetAs[Material](assetPath)
|
||||
.attempt
|
||||
.runSyncUnsafe()
|
||||
|
||||
assert(res1 === Left(CouldNotCastError))
|
||||
assert(res1.isLeft)
|
||||
assert(res1.left.get eqv CouldNotCastError)
|
||||
|
||||
val res2 = assetManager
|
||||
.loadAssetAs[MaterialDef](assetPath)
|
||||
.attempt
|
||||
.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
|
||||
.runSyncUnsafe()
|
||||
|
||||
assert(res.isLeft)
|
||||
|
||||
assert(
|
||||
res === Left(
|
||||
res.left.get eqv
|
||||
CollisionShapeFactory.WrongArgumentError(
|
||||
"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