forked from nova/jmonkey-test
Rohan Sircar
3 years ago
29 changed files with 1011 additions and 408 deletions
-
3build.sbt
-
14src/main/scala/wow/doge/mygame/Main.scala
-
148src/main/scala/wow/doge/mygame/MainApp.scala
-
13src/main/scala/wow/doge/mygame/MainModule.scala
-
81src/main/scala/wow/doge/mygame/actor/GameActorSystem.scala
-
27src/main/scala/wow/doge/mygame/executors/ExecutorsModule.scala
-
10src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala
-
30src/main/scala/wow/doge/mygame/executors/Schedulers.scala
-
91src/main/scala/wow/doge/mygame/game/GameApp.scala
-
149src/main/scala/wow/doge/mygame/game/GameAppActor.scala
-
46src/main/scala/wow/doge/mygame/game/controls/CameraMovementControls.scala
-
116src/main/scala/wow/doge/mygame/game/entities/CharacterStats.scala
-
136src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala
-
5src/main/scala/wow/doge/mygame/game/entities/player/PlayerController.scala
-
1src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala
-
1src/main/scala/wow/doge/mygame/game/subsystems/movement/MovementActor.scala
-
17src/main/scala/wow/doge/mygame/implicits/package.scala
-
8src/main/scala/wow/doge/mygame/launcher/Launcher.scala
-
5src/main/scala/wow/doge/mygame/subsystems/events/EventBus.scala
-
9src/main/scala/wow/doge/mygame/subsystems/events/Events.scala
-
6src/main/scala/wow/doge/mygame/subsystems/events/EventsModule.scala
-
310src/main/scala/wow/doge/mygame/subsystems/scriptsystem/MonixScriptCompiler.scala
-
5src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala
-
4src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptCachingActor.scala
-
14src/main/scala/wow/doge/mygame/types/package.scala
-
33src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala
-
63src/test/scala/wow/doge/mygame/AnimTest.scala
-
30src/test/scala/wow/doge/mygame/FileWatcherTest.scala
-
44src/test/scala/wow/doge/mygame/MonixScriptCompilerTest.scala
@ -0,0 +1,81 @@ |
|||
package wow.doge.mygame.actor |
|||
|
|||
import akka.actor.typed.ActorRef |
|||
import akka.actor.typed.SpawnProtocol |
|||
import akka.actor.typed.scaladsl.ActorContext |
|||
import akka.actor.typed.scaladsl.Behaviors |
|||
import wow.doge.mygame.implicits._ |
|||
// import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus |
|||
// import wow.doge.mygame.subsystems.events.Event |
|||
// import scala.reflect.ClassTag |
|||
// import akka.actor.typed.LogOptions |
|||
// import wow.doge.mygame.subsystems.events.EventBus |
|||
// import scala.concurrent.duration._ |
|||
// import akka.util.Timeout |
|||
// import akka.actor.typed.SupervisorStrategy |
|||
|
|||
object GameActorSystem { |
|||
sealed trait Command |
|||
case class GetSpawnProtocol( |
|||
replyTo: ActorRef[ActorRef[SpawnProtocol.Command]] |
|||
) extends Command |
|||
|
|||
class Props() { |
|||
def create = |
|||
Behaviors.setup[Command] { ctx => |
|||
val systemSpawnProtocol = ctx.spawnN(SpawnProtocol()) |
|||
new GameActorSystem(ctx, this, systemSpawnProtocol).receive |
|||
} |
|||
} |
|||
} |
|||
class GameActorSystem( |
|||
ctx: ActorContext[GameActorSystem.Command], |
|||
props: GameActorSystem.Props, |
|||
sp: ActorRef[SpawnProtocol.Command] |
|||
) { |
|||
import GameActorSystem._ |
|||
def receive = |
|||
Behaviors.receiveMessage[Command] { |
|||
case GetSpawnProtocol(replyTo) => |
|||
replyTo ! sp |
|||
Behaviors.same |
|||
} |
|||
} |
|||
|
|||
// object EventBusSupervisor { |
|||
// sealed trait Command |
|||
// case class GetMainEventBus(replyTo: ActorRef[GameEventBus[Event]]) |
|||
// extends Command |
|||
// case class GetEventBus[T](replyTo: ActorRef[GameEventBus[T]])(implicit |
|||
// classTag: ClassTag[T] |
|||
// ) extends Command { |
|||
// def ct = classTag |
|||
// } |
|||
|
|||
// class Props(val spawnProtocol: ActorRef[SpawnProtocol.Command]) { |
|||
// def create = |
|||
// Behaviors.setup[Command] { ctx => |
|||
// new EventBusSupervisor(ctx, this).receive |
|||
// } |
|||
// } |
|||
// } |
|||
// class EventBusSupervisor( |
|||
// ctx: ActorContext[EventBusSupervisor.Command], |
|||
// props: EventBusSupervisor.Props |
|||
// ) { |
|||
// import EventBusSupervisor._ |
|||
// implicit val timeout = Timeout(1.second) |
|||
// implicit val sp = props.spawnProtocol |
|||
// def receive = |
|||
// Behaviors.receiveMessage[Command] { |
|||
// case g @ GetEventBus(replyTo) => |
|||
// implicit val ct = g.ct |
|||
// Behaviors |
|||
// .supervise(EventBus()) |
|||
// .onFailure[Exception]( |
|||
// SupervisorStrategy.restart.withLimit(2, 100.millis) |
|||
// ) |
|||
// Behaviors.same |
|||
// case _ => Behaviors.same |
|||
// } |
|||
// } |
@ -1,31 +1,22 @@ |
|||
package wow.doge.mygame.executors |
|||
|
|||
import cats.effect.Resource |
|||
import monix.bio.IO |
|||
import monix.bio.Task |
|||
import monix.bio.UIO |
|||
import monix.execution.Scheduler |
|||
import wow.doge.mygame.types.JmeScheduler |
|||
|
|||
trait ExecutorsModule { |
|||
val schedulers = Schedulers() |
|||
val acquire: UIO[Either[Error, Int]] = |
|||
IO.pure(1).onErrorHandleWith(_ => IO.raiseError(Error)).attempt |
|||
// : Resource[IO[Error, Unit], Unit] |
|||
val res = Resource.make(acquire)(_ => IO.unit) |
|||
val x: Task[Either[Error, Unit]] = res.use { |
|||
case Right(value) => Task(Right(println(s"got $value"))) |
|||
case Left(value) => Task(Left(value)) |
|||
} |
|||
val z = x.onErrorHandleWith(ex => UIO(Right(ex.printStackTrace()))) |
|||
val y: IO[Error, Unit] = z >> |
|||
x.hideErrors.rethrow |
|||
|
|||
val jMESchedulerResource = Resource.make( |
|||
val schedulers = Schedulers.default |
|||
|
|||
val jmeSchedulerResource = Resource.make( |
|||
Task( |
|||
Scheduler |
|||
.singleThread(name = "JME-Application-Thread", daemonic = false) |
|||
JmeScheduler( |
|||
Scheduler |
|||
.singleThread(name = "JME-Application-Thread", daemonic = false) |
|||
) |
|||
) |
|||
)(e => Task(e.shutdown())) |
|||
)(s => Task(s.value.shutdown())) |
|||
} |
|||
|
|||
sealed trait Error |
|||
|
@ -0,0 +1,116 @@ |
|||
package wow.doge.mygame.game.entities |
|||
|
|||
import akka.actor.typed.ActorRef |
|||
import akka.actor.typed.Behavior |
|||
import akka.actor.typed.scaladsl.ActorContext |
|||
import akka.actor.typed.scaladsl.Behaviors |
|||
import io.estatico.newtype.macros.newtype |
|||
import wow.doge.mygame.game.entities.CharacterStats.HealHealth |
|||
|
|||
case class CharacterStats(hp: CharacterStats.Health, stamina: Int) |
|||
object CharacterStats { |
|||
@newtype case class HealHealth(toInt: Int) |
|||
@newtype case class DamageHealth(toInt: Int) |
|||
@newtype case class Health(toInt: Int) |
|||
object Health { |
|||
implicit class HealthOps(private val h: Health) extends AnyVal { |
|||
// def +(v: Int): Health = Health(h.toInt + v) |
|||
// def -(v: Int): Health = Health(h.toInt - v) |
|||
// def *(v: Int): Health = Health(h.toInt * v) |
|||
// def /(v: Int): Health = Health(h.toInt / v) |
|||
def :+(v: HealHealth): Health = Health(h.toInt + v.toInt) |
|||
def -(v: DamageHealth): Health = Health(h.toInt - v.toInt) |
|||
} |
|||
} |
|||
@newtype case class HealStamina(toInt: Int) |
|||
@newtype case class DamageStamina(toInt: Int) |
|||
@newtype case class Stamina(toInt: Int) |
|||
object Stamina { |
|||
implicit class StaminaOps(private val h: Stamina) extends AnyVal { |
|||
// def +(v: Int): Stamina = Stamina(h.toInt + v) |
|||
// def -(v: Int): Stamina = Stamina(h.toInt - v) |
|||
// def *(v: Int): Stamina = Stamina(h.toInt * v) |
|||
// def /(v: Int): Stamina = Stamina(h.toInt / v) |
|||
def :+(v: HealStamina): Stamina = Stamina(h.toInt + v.toInt) |
|||
def -(v: DamageStamina): Stamina = Stamina(h.toInt - v.toInt) |
|||
} |
|||
} |
|||
// object Stamina { |
|||
// implicit class StaminaOps(private val h: Stamina) extends AnyVal { |
|||
// def +(v: Health): Stamina = Stamina(h.toInt + v.toInt) |
|||
// def -(v: Health): Stamina = Stamina(h.toInt - v.toInt) |
|||
// def *(v: Health): Stamina = Stamina(h.toInt * v.toInt) |
|||
// def /(v: Health): Stamina = Stamina(h.toInt / v.toInt) |
|||
// } |
|||
// } |
|||
|
|||
// object Damage { |
|||
// implicit class DamageOps(private val h: Damage) extends AnyVal { |
|||
// def +(v: Health): Damage = Damage(h.toInt + v.toInt) |
|||
// def -(v: Health): Damage = Damage(h.toInt - v.toInt) |
|||
// def *(v: Health): Damage = Damage(h.toInt * v.toInt) |
|||
// def /(v: Health): Damage = Damage(h.toInt / v.toInt) |
|||
// } |
|||
// } |
|||
|
|||
} |
|||
|
|||
object StatsActor { |
|||
|
|||
sealed trait Command |
|||
// case class TakeDamage(value: Int) extends Command |
|||
case class TakeDamageResult( |
|||
value: CharacterStats.DamageHealth, |
|||
replyTo: ActorRef[(Boolean, CharacterStats)] |
|||
) extends Command |
|||
case class HealResult(value: HealHealth) extends Command |
|||
case class CurrentStats(replyTo: ActorRef[CharacterStats]) extends Command |
|||
|
|||
class Props( |
|||
startingHealth: CharacterStats.Health, |
|||
startingStamina: CharacterStats.Stamina |
|||
) { |
|||
def behavior = |
|||
Behaviors.setup[Command] { ctx => |
|||
new StatsActor(ctx, this) |
|||
.receive( |
|||
State(CharacterStats(startingHealth, startingStamina.toInt)) |
|||
) |
|||
} |
|||
} |
|||
|
|||
case class State(stats: CharacterStats) |
|||
} |
|||
class StatsActor( |
|||
ctx: ActorContext[StatsActor.Command], |
|||
props: StatsActor.Props |
|||
) { |
|||
import StatsActor._ |
|||
import CharacterStats._ |
|||
import com.softwaremill.quicklens._ |
|||
def receive(state: State): Behavior[Command] = |
|||
Behaviors.receiveMessage[Command] { |
|||
// Todo add min max values |
|||
// case TakeDamage(value) => |
|||
// val nextState = |
|||
// if (state.stats.hp - value <= 0) |
|||
// state.modify(_.stats.hp).setTo(0) |
|||
// else |
|||
// state.modify(_.stats.hp).using(_ - value) |
|||
// receive(nextState) |
|||
case TakeDamageResult(value, replyTo) => |
|||
val nextState = if ((state.stats.hp - value).toInt <= 0) { |
|||
replyTo ! true -> state.stats |
|||
state.modify(_.stats.hp).setTo(Health(0)) |
|||
} else { |
|||
replyTo ! false -> state.stats |
|||
state.modify(_.stats.hp).using(_ - value) |
|||
} |
|||
receive(nextState) |
|||
case HealResult(value) => |
|||
receive(state.modify(_.stats.hp).using(_ :+ value)) |
|||
case CurrentStats(replyTo) => |
|||
replyTo ! state.stats |
|||
Behaviors.same |
|||
} |
|||
} |
@ -0,0 +1,310 @@ |
|||
package wow.doge.mygame.subsystems.scriptsystem |
|||
|
|||
import javax.script.ScriptEngine |
|||
import javax.script.ScriptEngineManager |
|||
import javax.script.ScriptException |
|||
|
|||
import scala.concurrent.duration._ |
|||
|
|||
import ammonite.main.Defaults |
|||
import ammonite.runtime.Storage.Folder |
|||
import ammonite.util.Res |
|||
import ammonite.util.Res.Failure |
|||
import cats.effect.Resource |
|||
import cats.effect.concurrent.Deferred |
|||
import cats.syntax.either._ |
|||
import cats.syntax.flatMap._ |
|||
import com.softwaremill.macwire._ |
|||
import com.softwaremill.tagging._ |
|||
import groovy.util.GroovyScriptEngine |
|||
import io.odin.Logger |
|||
import monix.bio.IO |
|||
import monix.bio.Task |
|||
import monix.bio.UIO |
|||
import monix.catnap.ConcurrentQueue |
|||
import monix.reactive.Observable |
|||
import wow.doge.mygame.implicits._ |
|||
import monix.{eval => me} |
|||
import groovy.util.ResourceException |
|||
import monix.catnap.MVar |
|||
|
|||
trait Requestable[A] { |
|||
|
|||
protected def queue: ConcurrentQueue[Task, A] |
|||
|
|||
def request[T]( |
|||
compileRequest: Deferred[Task, T] => A |
|||
)(implicit timeout: FiniteDuration) = |
|||
for { |
|||
d <- Deferred[Task, T] |
|||
req = compileRequest(d) |
|||
_ <- queue.offer(req) |
|||
res <- d.get.timeout(timeout).map(_.get) |
|||
} yield res |
|||
} |
|||
|
|||
class ScriptCompiler private ( |
|||
_queue: ConcurrentQueue[Task, ScriptCompiler.Command] |
|||
) extends Requestable[ScriptCompiler.Command] { |
|||
|
|||
override protected def queue = _queue |
|||
|
|||
// def tell(item: Command) = queue.offer(item) |
|||
|
|||
} |
|||
object ScriptCompiler { |
|||
|
|||
sealed trait State |
|||
case object Idle extends State |
|||
case object Active extends State |
|||
|
|||
/** |
|||
* script representation |
|||
*/ |
|||
sealed trait ScriptTag |
|||
type ScriptObject = Any @@ ScriptTag |
|||
|
|||
sealed trait KotlinEngineTag |
|||
type KotlinScriptEngine = ScriptEngine @@ KotlinEngineTag |
|||
|
|||
sealed trait Error |
|||
final case class AmmoniteFailure(error: Res.Failure) extends Error |
|||
final case class AmmoniteException(error: Res.Exception) extends Error |
|||
final case class ScriptExceptionError(error: ScriptException) extends Error |
|||
final case class ResourceExceptionError(error: ResourceException) |
|||
extends Error |
|||
final case class GroovyScriptExceptionError( |
|||
error: groovy.util.ScriptException |
|||
) extends Error |
|||
final case class SomeError(reason: String) extends Error |
|||
|
|||
sealed trait Command |
|||
final case class Get( |
|||
path: os.Path, |
|||
result: Deferred[Task, ScriptResult], |
|||
force: Boolean |
|||
) extends Command |
|||
final case class GetData(result: Deferred[Task, Data]) |
|||
final case class ObservableData(result: Deferred[Task, Observable[Data]]) |
|||
extends Command |
|||
// extends Command |
|||
// final case class CompileAll(paths: Seq[os.Path]) extends Command |
|||
|
|||
type ScriptsMap = Map[os.Path, Any] |
|||
type ScriptResult = Either[Error, Any] |
|||
|
|||
sealed trait ScriptType |
|||
case object ScalaType extends ScriptType |
|||
case object KotlinType extends ScriptType |
|||
case object GroovyType extends ScriptType |
|||
|
|||
val defaultScalaRunner = |
|||
ammonite |
|||
.Main( |
|||
storageBackend = new Folder( |
|||
// os.pwd / "target" |
|||
Defaults.ammoniteHome, |
|||
isRepl = false |
|||
) |
|||
) |
|||
|
|||
val defaultKotlinRunner: KotlinScriptEngine = { |
|||
val manager = new ScriptEngineManager() |
|||
val engine = manager.getEngineByExtension("main.kts") |
|||
engine.taggedWith[KotlinEngineTag] |
|||
} |
|||
|
|||
val defaultGroovyRunner: GroovyScriptEngine = |
|||
new GroovyScriptEngine(os.pwd.toString) |
|||
|
|||
case class Data(scriptsMap: ScriptsMap) |
|||
|
|||
class SourceMaker( |
|||
queue: ConcurrentQueue[Task, Command], |
|||
worker: ScriptCompilerWorker |
|||
) { |
|||
import com.softwaremill.quicklens._ |
|||
def get = |
|||
for { |
|||
dataVar <- MVar[Task].of(Data(Map.empty)) |
|||
obs <- Task.deferAction(implicit s => |
|||
Task( |
|||
Observable |
|||
.repeatEvalF(queue.poll) |
|||
.scanEval0(me.Task.pure((Active: State) -> Data(Map.empty))) { |
|||
case state -> data -> command => |
|||
val nextState: IO[Error, (State, Data)] = state match { |
|||
case Idle => IO.pure(Idle -> data) |
|||
case Active => |
|||
command match { |
|||
case Get(path, result, force) => |
|||
def getAndUpdate = |
|||
worker |
|||
.request( |
|||
ScriptCompilerWorker.CompileAny(path, _) |
|||
)( |
|||
20.seconds |
|||
) |
|||
.flatTap(result.complete) |
|||
.hideErrors |
|||
.rethrow |
|||
.flatMap(res => |
|||
UIO(pprint.log(res)) >> |
|||
UIO.pure( |
|||
data |
|||
.modify(_.scriptsMap) |
|||
.using(_ + (path -> res)) |
|||
) |
|||
) |
|||
for { |
|||
nextData <- |
|||
if (force) getAndUpdate |
|||
else |
|||
data.scriptsMap.get(path) match { |
|||
case Some(e) => |
|||
result |
|||
.complete(e.asRight[Error]) |
|||
.hideErrors >> UIO.pure(data) |
|||
case None => getAndUpdate |
|||
} |
|||
} yield Active -> nextData |
|||
case ObservableData(result) => |
|||
result |
|||
.complete(Observable.repeatEvalF(dataVar.take)) |
|||
.hideErrors >> IO.pure(Active -> data) |
|||
} |
|||
|
|||
} |
|||
nextState |
|||
.flatTap { case (_, data) => dataVar.put(data).hideErrors } |
|||
.tapError(err => UIO(pprint.log(err.toString))) |
|||
.attempt |
|||
// .mapFilter(_.toOption) |
|||
.map(_.getOrElse(state -> data)) |
|||
.toTask |
|||
} |
|||
) |
|||
) |
|||
} yield obs |
|||
|
|||
} |
|||
|
|||
class ScriptCompileFns( |
|||
val scalaRunner: ammonite.Main, |
|||
val kotlinRunner: KotlinScriptEngine, |
|||
val groovyRunner: GroovyScriptEngine |
|||
) { |
|||
def runScala(path: os.Path): Either[Error, Any] = |
|||
scalaRunner |
|||
.runScript(path, Seq.empty) |
|||
._1 match { |
|||
case e @ Res.Exception(t, msg) => Left(AmmoniteException(e)) |
|||
|
|||
case f @ Failure(msg) => Left(AmmoniteFailure(f)) |
|||
|
|||
case Res.Success(obj) => Right(obj) |
|||
|
|||
case _ => Left(SomeError("Failed to run script")) |
|||
} |
|||
|
|||
def runKotlin(path: os.Path): Either[Error, Any] = |
|||
Either |
|||
.catchNonFatal(kotlinRunner.eval(os.read(path))) |
|||
.leftMap { |
|||
case ex: ScriptException => ScriptExceptionError(ex) |
|||
} |
|||
|
|||
def runGroovy(path: os.Path): Either[Error, Any] = |
|||
Either |
|||
.catchNonFatal(groovyRunner.run(path.relativeTo(os.pwd).toString, "")) |
|||
.leftMap { |
|||
case ex: ResourceException => ResourceExceptionError(ex) |
|||
case ex: groovy.util.ScriptException => GroovyScriptExceptionError(ex) |
|||
} |
|||
|
|||
def ensureReturnedObjectNotNull(scriptObject: Any): Either[Error, Any] = |
|||
Either.fromOption(Option(scriptObject), SomeError("unknown object")) |
|||
} |
|||
|
|||
class ScriptCompileSource( |
|||
fns: ScriptCompileFns, |
|||
logger: Logger[Task], |
|||
queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest] |
|||
) { |
|||
import fns._ |
|||
|
|||
val source = |
|||
Task.deferAction(implicit s => |
|||
Task( |
|||
Observable |
|||
.repeatEvalF(queue.poll) |
|||
.doOnNextF(el => logger.debug(s"Got $el")) |
|||
.mapParallelUnorderedF(4) { |
|||
case ScriptCompilerWorker.CompileAny(path, result) => |
|||
for { |
|||
mbRes <- Task( |
|||
runScala(path) |
|||
.flatMap(ensureReturnedObjectNotNull) |
|||
// .map(_.taggedWith[ScriptTag]) |
|||
) |
|||
_ <- result.complete(mbRes) |
|||
} yield mbRes |
|||
} |
|||
) |
|||
) |
|||
} |
|||
// override private val |
|||
final class ScriptCompilerWorker( |
|||
logger: Logger[Task], |
|||
_queue: ConcurrentQueue[Task, ScriptCompilerWorker.CompileRequest] |
|||
) extends Requestable[ScriptCompilerWorker.CompileRequest] { |
|||
|
|||
override def queue = _queue |
|||
|
|||
} |
|||
|
|||
object ScriptCompilerWorker { |
|||
|
|||
sealed trait CompileRequest |
|||
final case class CompileAny( |
|||
path: os.Path, |
|||
result: Deferred[Task, ScriptResult] |
|||
) extends CompileRequest |
|||
|
|||
def apply( |
|||
logger: Logger[Task], |
|||
scalaRunner: ammonite.Main = defaultScalaRunner, |
|||
kotlinRunner: KotlinScriptEngine = defaultKotlinRunner, |
|||
groovyRunner: GroovyScriptEngine = defaultGroovyRunner |
|||
) = { |
|||
val acquire = for { |
|||
queue <- ConcurrentQueue[Task].bounded[CompileRequest](10) |
|||
fns <- UIO.pure(wire[ScriptCompileFns]) |
|||
worker = wire[ScriptCompilerWorker] |
|||
fib <- wire[ScriptCompileSource].source.flatMap(_.completedL.toIO.start) |
|||
// resource = Concurrent[Task].background( |
|||
// wire[ScriptCompileSource].source.flatMap(_.completedL.toIO) |
|||
// ) |
|||
} yield worker -> fib |
|||
Resource |
|||
.make(acquire) { case worker -> fib => fib.cancel } |
|||
.map(_._1) |
|||
} |
|||
|
|||
} |
|||
|
|||
def apply(logger: Logger[Task]) = { |
|||
def acquire(worker: ScriptCompilerWorker) = |
|||
for { |
|||
queue <- ConcurrentQueue.bounded[Task, Command](10) |
|||
fib <- wire[SourceMaker].get.flatMap(_.completedL.toIO.start) |
|||
} yield new ScriptCompiler(queue) -> fib |
|||
|
|||
ScriptCompilerWorker(logger) |
|||
.flatMap(worker => |
|||
Resource.make(acquire(worker)) { case (_, fib) => fib.cancel } |
|||
) |
|||
.map(_._1) |
|||
} |
|||
|
|||
} |
@ -1,17 +1,15 @@ |
|||
package wow.doge.mygame |
|||
|
|||
import wow.doge.mygame.utils.wrappers.jme.AppNode2 |
|||
import akka.actor.typed.Scheduler |
|||
import com.softwaremill.tagging._ |
|||
import wow.doge.mygame.game.GameAppTags |
|||
import monix.execution.Scheduler |
|||
import io.estatico.newtype.macros.newtype |
|||
import monix.execution.schedulers.SchedulerService |
|||
import wow.doge.mygame.game.GameAppTags |
|||
import wow.doge.mygame.utils.wrappers.jme.AppNode2 |
|||
|
|||
package object types { |
|||
type RootNode = AppNode2 @@ GameAppTags.RootNode |
|||
type GuiNode = AppNode2 @@ GameAppTags.GuiNode |
|||
@newtype case class AsyncScheduler(value: Scheduler) |
|||
@newtype case class IoScheduler(value: Scheduler) |
|||
@newtype case class FxScheduler(value: Scheduler) |
|||
@newtype case class JmeScheduler(value: Scheduler) |
|||
@newtype case class AkkaScheduler(value: akka.actor.typed.Scheduler) |
|||
@newtype case class JmeScheduler(value: SchedulerService) |
|||
@newtype case class AkkaScheduler(value: Scheduler) |
|||
} |
@ -0,0 +1,33 @@ |
|||
package wow.doge.mygame.utils |
|||
|
|||
import monix.reactive.Observable |
|||
import monix.reactive.OverflowStrategy |
|||
import monix.execution.Cancelable |
|||
import monix.execution.cancelables.SingleAssignCancelable |
|||
import monix.execution.Ack |
|||
|
|||
object MonixDirectoryWatcher { |
|||
import better.files._ |
|||
import io.methvin.better.files._ |
|||
def apply(path: os.Path) = |
|||
Observable.create[String](OverflowStrategy.DropNew(50)) { sub => |
|||
import sub.scheduler |
|||
|
|||
val c = SingleAssignCancelable() |
|||
|
|||
val myDir = File(path.toString) |
|||
val watcher = new RecursiveFileMonitor(myDir) { |
|||
override def onCreate(file: File, count: Int) = |
|||
println(s"$file got created") |
|||
override def onModify(file: File, count: Int) = |
|||
// println(s"$file got modified $count times") |
|||
if (sub.onNext(file.name) == Ack.Stop) c.cancel() |
|||
override def onDelete(file: File, count: Int) = |
|||
println(s"$file got deleted") |
|||
} |
|||
watcher.start()(scheduler) |
|||
c := Cancelable(() => watcher.stop()) |
|||
c |
|||
|
|||
} |
|||
} |
@ -0,0 +1,63 @@ |
|||
package wow.doge.mygame |
|||
import org.scalatest.funsuite.AnyFunSuite |
|||
import com.jme3.anim.AnimClip |
|||
import wow.doge.mygame.implicits._ |
|||
import wow.doge.mygame.utils.wrappers.jme.AssetManager |
|||
import com.jme3.asset.DesktopAssetManager |
|||
import com.jme3.scene.Spatial |
|||
import monix.bio.UIO |
|||
import scala.concurrent.duration._ |
|||
import com.jme3.scene.Node |
|||
import com.jme3.anim.AnimComposer |
|||
import com.jme3.anim.SkinningControl |
|||
import com.jme3.anim.tween.action.BaseAction |
|||
import com.jme3.anim.tween.Tweens |
|||
import scala.jdk.javaapi.CollectionConverters._ |
|||
import com.jme3.anim.tween.Tween |
|||
import com.jme3.anim.tween.action.ClipAction |
|||
import cats.syntax.all._ |
|||
|
|||
class AnimTest extends AnyFunSuite { |
|||
import monix.execution.Scheduler.Implicits.global |
|||
val assetManager = new AssetManager(new DesktopAssetManager(true)) |
|||
|
|||
test("test 1") { |
|||
println((for { |
|||
_ <- UIO.unit |
|||
model <- assetManager.loadModelAs[Node]( |
|||
os.rel / "Models" / "Oto" / "Oto.mesh.xml" |
|||
) |
|||
animcontrol <- UIO(model.getControlMaybe(classOf[AnimComposer])) |
|||
skinningcontrol <- UIO(model.getControlMaybe(classOf[SkinningControl])) |
|||
_ <- UIO(println(animcontrol)) |
|||
_ <- UIO(println(skinningcontrol)) |
|||
_ <- UIO { |
|||
animcontrol.map { ac => |
|||
// ac.actionSequence() |
|||
// ac.makeAction() |
|||
// new BaseAction(Tweens.sequence()) |
|||
// ac.getAnimClips().a |
|||
Option(ac.getAnimClip("hmm")) |
|||
.map(clip => new ClipAction(clip)) |
|||
.map(Tweens.sequence(_)) |
|||
.foreach { t => |
|||
val actions = new BaseAction(t) |
|||
ac.addAction("hmm", actions) |
|||
} |
|||
|
|||
val names = List("Walk", "Jump") |
|||
for { |
|||
clips <- names.traverse(name => |
|||
Option(ac.getAnimClip(name)).map(clip => new ClipAction(clip)) |
|||
) |
|||
tween <- Tweens.sequence(clips: _*).some |
|||
actions <- new BaseAction(tween).some |
|||
_ <- ac.addAction("Sequence 1", actions).some |
|||
} yield () |
|||
|
|||
() |
|||
} |
|||
} |
|||
} yield model).attempt.runSyncUnsafe(10.seconds)) |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
package wow.doge.mygame |
|||
|
|||
import org.scalatest.funsuite.AnyFunSuite |
|||
import cats.effect.{Resource => CResource} |
|||
import monix.eval.Task |
|||
import scala.concurrent.duration._ |
|||
|
|||
class FileWatcherTest extends AnyFunSuite { |
|||
test("1") { |
|||
import better.files._ |
|||
import io.methvin.better.files._ |
|||
|
|||
val myDir = |
|||
File((os.pwd / "assets" / "scripts").toString) |
|||
val watcher = new RecursiveFileMonitor(myDir) { |
|||
override def onCreate(file: File, count: Int) = |
|||
println(s"$file got created") |
|||
override def onModify(file: File, count: Int) = |
|||
println(s"$file got modified $count times") |
|||
override def onDelete(file: File, count: Int) = |
|||
println(s"$file got deleted") |
|||
} |
|||
|
|||
import monix.execution.Scheduler.Implicits.global |
|||
CResource |
|||
.make(Task { watcher.start(); watcher })(w => Task(w.stop())) |
|||
.use(_ => Task.never) |
|||
.runSyncUnsafe(10.seconds) |
|||
} |
|||
} |
@ -0,0 +1,44 @@ |
|||
package wow.doge.mygame |
|||
|
|||
import org.scalatest.funsuite.AnyFunSuite |
|||
import monix.bio.Task |
|||
import scala.concurrent.duration._ |
|||
import monix.execution.Scheduler.Implicits.global |
|||
import wow.doge.mygame.subsystems.scriptsystem.ScriptCompiler |
|||
import io.odin.consoleLogger |
|||
import wow.doge.mygame.implicits._ |
|||
|
|||
class MonixScriptCompilerTest extends AnyFunSuite { |
|||
|
|||
test("some-test") { |
|||
ScriptCompiler(consoleLogger[Task]()) |
|||
.use(scriptCompiler => |
|||
for { |
|||
// _ <- |
|||
// scriptCompiler.source |
|||
// .doOnNextF(el => Task(println(s"Got $el"))) |
|||
// .completedL |
|||
// .toIO |
|||
// .hideErrors |
|||
// .startAndForget |
|||
response <- scriptCompiler.request( |
|||
ScriptCompiler.Get( |
|||
os.pwd / "assets" / "scripts" / "scala" / "hello2.sc", |
|||
_, |
|||
false |
|||
) |
|||
)(20.seconds) |
|||
_ <- Task(pprint.log(response.toString)) |
|||
// _ <- Task.sleep(4.seconds) |
|||
// _ <- scriptCompiler.tell( |
|||
// ScriptCompiler.CompileAny( |
|||
// os.pwd / "assets" / "scripts" / "scala" / "hello2.sc" |
|||
// ) |
|||
// ) |
|||
// _ <- Task.sleep(4.seconds) |
|||
// _ <- Task.sleep(8.seconds) |
|||
} yield () |
|||
) |
|||
.runSyncUnsafe(20.seconds) |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue