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 |
package wow.doge.mygame.executors |
||||
|
|
||||
import cats.effect.Resource |
import cats.effect.Resource |
||||
import monix.bio.IO |
|
||||
import monix.bio.Task |
import monix.bio.Task |
||||
import monix.bio.UIO |
|
||||
import monix.execution.Scheduler |
import monix.execution.Scheduler |
||||
|
import wow.doge.mygame.types.JmeScheduler |
||||
|
|
||||
trait ExecutorsModule { |
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( |
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 |
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 |
package wow.doge.mygame |
||||
|
|
||||
import wow.doge.mygame.utils.wrappers.jme.AppNode2 |
|
||||
|
import akka.actor.typed.Scheduler |
||||
import com.softwaremill.tagging._ |
import com.softwaremill.tagging._ |
||||
import wow.doge.mygame.game.GameAppTags |
|
||||
import monix.execution.Scheduler |
|
||||
import io.estatico.newtype.macros.newtype |
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 { |
package object types { |
||||
type RootNode = AppNode2 @@ GameAppTags.RootNode |
type RootNode = AppNode2 @@ GameAppTags.RootNode |
||||
type GuiNode = AppNode2 @@ GameAppTags.GuiNode |
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