commit 1f55e08fa518377e1d2b6e9b6b34c5befc4bbe87 Author: Rohan Sircar Date: Sat Nov 7 20:59:57 2020 +0530 first commit diff --git a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala new file mode 100644 index 0000000..319342d --- /dev/null +++ b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/dep/src/ammonite/$file/src/main/resources/dep.scala @@ -0,0 +1,39 @@ + +package ammonite +package $file.src.main.resources +import _root_.ammonite.interp.api.InterpBridge.{ + value => interp +} +import _root_.ammonite.interp.api.InterpBridge.value.{ + exit +} +import _root_.ammonite.interp.api.IvyConstructor.{ + ArtifactIdExt, + GroupIdExt +} +import _root_.ammonite.runtime.tools.{ + browse, + grep, + time, + tail +} +import _root_.ammonite.repl.tools.{ + desugar, + source +} +import _root_.ammonite.main.Router.{ + doc, + main +} +import _root_.ammonite.repl.tools.Util.{ + pathScoptRead +} + + +object dep{ +/**/ /**/ +def $main() = { scala.Iterator[String]() } + override def toString = "dep" + /**/ +} diff --git a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala new file mode 100644 index 0000000..ae97991 --- /dev/null +++ b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello/src/ammonite/$file/src/main/resources/hello.scala @@ -0,0 +1,45 @@ + +package ammonite +package $file.src.main.resources +import _root_.ammonite.interp.api.InterpBridge.{ + value => interp +} +import _root_.ammonite.interp.api.InterpBridge.value.{ + exit +} +import _root_.ammonite.interp.api.IvyConstructor.{ + ArtifactIdExt, + GroupIdExt +} +import _root_.ammonite.runtime.tools.{ + browse, + grep, + time, + tail +} +import _root_.ammonite.repl.tools.{ + desugar, + source +} +import _root_.ammonite.main.Router.{ + doc, + main +} +import _root_.ammonite.repl.tools.Util.{ + pathScoptRead +} +import ammonite.$file.src.main.resources.{ + dep +} + + +object hello{ +/**/ /**/ +def $main() = { scala.Iterator[String]() } + override def toString = "hello" + /**/ +} diff --git a/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala new file mode 100644 index 0000000..52f6a44 --- /dev/null +++ b/.ammonite/scala-2.12.12/amm-2.2.0-4-4bd225e/src/main/resources/hello2/src/ammonite/$file/src/main/resources/hello2.scala @@ -0,0 +1,134 @@ + +package ammonite +package $file.src.main.resources +import _root_.ammonite.interp.api.InterpBridge.{ + value => interp +} +import _root_.ammonite.interp.api.InterpBridge.value.{ + exit +} +import _root_.ammonite.interp.api.IvyConstructor.{ + ArtifactIdExt, + GroupIdExt +} +import _root_.ammonite.runtime.tools.{ + browse, + grep, + time, + tail +} +import _root_.ammonite.repl.tools.{ + desugar, + source +} +import _root_.ammonite.main.Router.{ + doc, + main +} +import _root_.ammonite.repl.tools.Util.{ + pathScoptRead +} + + +object hello2{ +/**/ /**/ +def $main() = { scala.Iterator[String]() } + override def toString = "hello2" + /**/ +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3dcaadb --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +*.class +*.log + +# sbt specific +.cache/ +.history/ +.lib/ +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +metals.sbt +.metals +.bloop + +# Scala-IDE specific +.scala_dependencies +.worksheet + +.idea/ +.vscode +assets/ +*.j3o diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..ba14fb7 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1 @@ +version = "2.6.4" diff --git a/assets.jmp b/assets.jmp new file mode 100644 index 0000000..2f205c3 --- /dev/null +++ b/assets.jmp @@ -0,0 +1,3 @@ +#assets properties +#Fri Oct 23 22:34:43 IST 2020 +assets.folder.name=src/main/resources/assets diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..4facde8 --- /dev/null +++ b/build.sbt @@ -0,0 +1,192 @@ +// 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" + +resolvers += Resolver.mavenLocal +resolvers += Resolver.sonatypeRepo("snapshots") + +lazy val jmeVersion = "3.3.2-stable" + +lazy val osName = System.getProperty("os.name") match { + case n if n.startsWith("Linux") => "linux" + case n if n.startsWith("Mac") => "mac" + case n if n.startsWith("Windows") => "win" + case _ => throw new Exception("Unknown platform!") +} +lazy val javaFXModules = + Seq("base", "controls", "fxml", "graphics", "media", "swing", "web") + +lazy val root = (project in file(".")).settings( + inThisBuild( + List( + scalaVersion := scalaVersion.value, // 2.11.12, or 2.13.3 + semanticdbEnabled := true, // enable SemanticDB + semanticdbVersion := "4.3.24" // use Scalafix compatible version + ) + ), + name := "mygame", + 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, + "org.jmonkeyengine" % "jme3-blender" % jmeVersion, +// https://mvnrepository.com/artifact/com.github.stephengold/Minie + "com.github.stephengold" % "Minie" % "3.0.0", +// https://mvnrepository.com/artifact/com.simsilica/zay-es + "com.simsilica" % "zay-es" % "1.2.1", + "org.typelevel" %% "cats-core" % "2.1.1", + "com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full, + "org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10", + "org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10", + // "wow.doge" % "game" % "1.0-SNAPSHOT", + "org.scalafx" %% "scalafx" % "14-R19", + "com.typesafe.akka" %% "akka-actor-typed" % "2.6.10", + "ch.qos.logback" % "logback-classic" % "1.2.3", + "org.typelevel" %% "cats-core" % "2.1.1", + "org.typelevel" %% "cats-effect" % "2.1.4", + "io.monix" %% "monix" % "3.2.2", + "io.monix" %% "monix-bio" % "1.0.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.github.valskalla" %% "odin-monix" % "0.8.1", + "com.softwaremill.macwire" %% "util" % "2.3.7", + "com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided", + "com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided", + "com.github.valskalla" %% "odin-slf4j" % "0.8.1", + "com.softwaremill.quicklens" %% "quicklens" % "1.6.1" + ), + // Determine OS version of JavaFX binaries + +// Add JavaFX dependencies + libraryDependencies ++= javaFXModules.map(m => + "org.openjfx" % s"javafx-$m" % "14.0.1" classifier osName + ), + scalacOptions ++= Seq( + "-encoding", + "UTF-8", + "-deprecation", + "-feature", + "-unchecked", + "-Xlint", + "-Ywarn-numeric-widen", + "-Ymacro-annotations", + // "utf-8", // Specify character encoding used by source files. + "-explaintypes" // Explain type errors in more detail. + ), + javacOptions ++= Seq("-source", "11", "-target", "11"), + javaOptions ++= Seq("-Xmx2G", "-Xms2G"), + fork := true, + assemblyMergeStrategy in assembly := { + case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first + case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first + case "application.conf" => MergeStrategy.concat + case "unwanted.txt" => MergeStrategy.discard + case x if Assembly.isConfigFile(x) => + MergeStrategy.concat + case PathList("META-INF", xs @ _*) => + (xs map { _.toLowerCase }) match { + case ("manifest.mf" :: Nil) | ("index.list" :: Nil) | + ("dependencies" :: Nil) => + MergeStrategy.discard + case ps @ (x :: xs) + if ps.last.endsWith(".sf") || ps.last.endsWith(".dsa") => + MergeStrategy.discard + case "plexus" :: xs => + MergeStrategy.discard + case "services" :: xs => + MergeStrategy.filterDistinctLines + case ("spring.schemas" :: Nil) | ("spring.handlers" :: Nil) => + MergeStrategy.filterDistinctLines + case _ => MergeStrategy.first // Changed deduplicate to first + } + case PathList(_*) => MergeStrategy.first + // case x => + // val oldStrategy = (assemblyMergeStrategy in assembly).value + // oldStrategy(x) + } + // scalaVersion := "2.13.2", // 2.11.12, or 2.13.3 + // semanticdbEnabled := true, // enable SemanticDB + // semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version + // semanticdbVersion := "4.3.24", +) + +// 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 diff --git a/libbulletjme.so b/libbulletjme.so new file mode 100644 index 0000000..2101e4e Binary files /dev/null and b/libbulletjme.so differ diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..0837f7a --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.13 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..eb7d2e4 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23") diff --git a/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 0000000..12a8a83 --- /dev/null +++ b/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.jetbrains.kotlin.mainKts.jsr223.KotlinJsr223MainKtsScriptEngineFactory \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf new file mode 100644 index 0000000..7a8406a --- /dev/null +++ b/src/main/resources/application.conf @@ -0,0 +1,7 @@ +jme-dispatcher { + type = "Dispatcher" + name = "JME-Thread" + executor = "wow.doge.mygame.executors.JMEThreadExecutorServiceConfigurator" + throughput = 1 +} +akka.jvm-exit-on-fatal-error = on \ No newline at end of file diff --git a/src/main/resources/dep.sc b/src/main/resources/dep.sc new file mode 100644 index 0000000..c5c6b6c --- /dev/null +++ b/src/main/resources/dep.sc @@ -0,0 +1,2 @@ +// println("hello from dep") +class Test(x: Int) diff --git a/src/main/resources/hello.main.kts b/src/main/resources/hello.main.kts new file mode 100644 index 0000000..75d8050 --- /dev/null +++ b/src/main/resources/hello.main.kts @@ -0,0 +1,36 @@ +// @file:Import("src/main/resources/hello2.main.kts") +@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0-M1") +@file:DependsOn("/home/rohan/.m2/repository/wow/doge/game/1.0-SNAPSHOT/game-1.0-SNAPSHOT.jar") +@file:DependsOn("/home/rohan/.ivy2/local/wow.doge/mygame_2.13/1.0-SNAPSHOT/jars/mygame_2.13.jar") + +import wow.doge.game.types.GameScript + +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlin.random.Random + +import wow.doge.mygame.components.Test + + +//println(x * 2) + +println("hello from main script") + +class GameScriptImpl : GameScript { + override fun start() = runBlocking { + for(x in 0 until 5) { + launch { + val del = Random.nextLong(20, 100) + + delay(del) + println("hello from start $x, delay is $del") + } + } + } + override fun stop() {println("hello from stop")} +} + +GameScriptImpl() + +class MyTest : Test() \ No newline at end of file diff --git a/src/main/resources/hello.sc b/src/main/resources/hello.sc new file mode 100644 index 0000000..2b69a50 --- /dev/null +++ b/src/main/resources/hello.sc @@ -0,0 +1,35 @@ +// #!/usr/bin/env amm + +// import coursierapi.MavenRepository + +// interp.repositories.update( +// interp.repositories() ::: List( +// MavenRepository.of("file://home/rohan/.m2/repository") +// ) +// ) + +// @ +// import $repo.`https://jcenter.bintray.com` +// // import $repo.`https://bintray.com/jmonkeyengine/com.jme3` +// import $file.dep +// import $ivy.`org.jmonkeyengine:jme3-core:3.2.4-stable` +// import $ivy.`wow.doge:game:1.0-SNAPSHOT` +// import wow.doge.game.types.GameScript +// import com.jme3.scene.control.Control + +// println("hello from script") + +// class Scr extends GameScript { +// def start(): Unit = println("hello from start") + +// def stop(): Unit = println("hello from stop") +// } + +// // class SomeClass extends Control {} + +// @main +// def main(): GameScript = new Scr() +import $file.dep +import dep.Test + +new Test(1) diff --git a/src/main/resources/hello2.main.kts b/src/main/resources/hello2.main.kts new file mode 100644 index 0000000..6fdb380 --- /dev/null +++ b/src/main/resources/hello2.main.kts @@ -0,0 +1,5 @@ +println("hello from dep") + +class Test(val x: Int) + +var x = 2 \ No newline at end of file diff --git a/src/main/resources/hello2.sc b/src/main/resources/hello2.sc new file mode 100644 index 0000000..34bd691 --- /dev/null +++ b/src/main/resources/hello2.sc @@ -0,0 +1,107 @@ +#!/usr/bin/env amm + +// import coursierapi.MavenRepository + +// interp.repositories.update( +// interp.repositories() ::: List( +// MavenRepository.of("file://home/rohan/.m2/repository") +// ) +// ) + +// @ +import $repo.`https://jcenter.bintray.com` +// import $repo.`https://bintray.com/jmonkeyengine/com.jme3` +// import $file.dep +import $ivy.`org.jmonkeyengine:jme3-core:3.2.4-stable` +// import $ivy.`wow.doge:game:1.0-SNAPSHOT` +import $ivy.`wow.doge::mygame:1.0-SNAPSHOT` +// import wow.doge.game.types.GameScript +import com.jme3.scene.control.Control + +// println("hello from script") + +// class Scr extends GameScript { +// def start(): Unit = println("hello from start") + +// def stop(): Unit = println("hello from stop") +// } + +// // class SomeClass extends Control {} + +// @main +// def main(): GameScript = new Scr() +import com.simsilica.es.base.DefaultEntityData +import com.simsilica.es.EntityData +import com.jme3.app.Application +import wow.doge.mygame.game.Implicits._ +import wow.doge.mygame.components.TestComponent +import com.jme3.scene.shape.Box +import com.jme3.scene.Geometry +import com.jme3.material.Material +import com.jme3.math.ColorRGBA +import com.jme3.asset.AssetManager +import com.jme3.math.Vector3f +import wow.doge.mygame.state._ +class TestAppState( + // private var _entity: Option[EntityData] = Some(new DefaultEntityData()) +) extends MyBaseState { + protected lazy val b = new Box(1, 1, 1) + protected lazy val geom = new Geometry("Box", b) + protected lazy val mat = Material( + assetManager = assetManager, + path = "Common/MatDefs/Misc/Unshaded.j3md" + ) + +// def entity = _entity + // override def initialize(app: Application): Unit = { + // super.initialize(app) + + // } + + override def init() = { + entityData + .createEntity() + .withComponents(TestComponent()) + // entityData.setComponents(x, TestComponent()) + val es = entityData.getEntities(classOf[TestComponent]) + println(es) + geom.setMaterial(mat) + rootNode.attachChild(geom) + + // geom.foreach(e => { + + // }) + } + + override def update(tpf: Float) = { + geom.rotate(0, 0.5f * tpf, 0) + geom.move(new Vector3f(0, 1 * tpf, 0)) + } + + override def cleanup(app: Application): Unit = { + // _entity.map(_.close()) + // _entity = None + } + + override def onEnable(): Unit = {} + + override def onDisable(): Unit = {} + +} + +object Material { + def apply( + color: String = "Color", + colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue, + assetManager: AssetManager, + path: String + ): Material = { + val mat = + new Material(assetManager, path) + mat.setColor(color, colorType) + mat + } +} + +@main +def main(): MyBaseState = new TestAppState() diff --git a/src/main/resources/weapon.json b/src/main/resources/weapon.json new file mode 100644 index 0000000..e01546d --- /dev/null +++ b/src/main/resources/weapon.json @@ -0,0 +1,19 @@ +{ + "record": "Weapon", + "values": { + "baseId": "4F00000062", + "weaponType": "sword", + "name": "Exquisite Steel Sword", + "attack": "40", + "weight": "20" + } +} + +val entity = ed.createEntity() +entity.setComponents( + Record("Weapon"), + WeaponType("Sword"), + Name("Exquisite Steel Sword"), + Attack(40), + Weight(20) +) \ No newline at end of file diff --git a/src/main/scala/com/jme3/animation/package.scala b/src/main/scala/com/jme3/animation/package.scala new file mode 100644 index 0000000..aaf3725 --- /dev/null +++ b/src/main/scala/com/jme3/animation/package.scala @@ -0,0 +1,62 @@ +package com.jme3 + +import com.jme3.input.Action + +package object animation { + + implicit class AnimChannelWrap(val uval: AnimChannel) extends AnyVal { + + /** + * Set the current animation that is played by this AnimChannel. + *

+ * See {@link #setAnim(java.lang.String, float)}. + * The blendTime argument by default is 150 milliseconds. + * + * @param action The action (name) of the animation to play + */ + def setAnim(action: Action): Unit = uval.setAnim(action.name) + + /** + * Set the current animation that is played by this AnimChannel. + *

+ * This resets the time to zero, and optionally blends the animation + * over blendTime seconds with the currently playing animation. + * Notice that this method will reset the control's speed to 1.0. + * + * @param action The action (name) of the animation to play + * @param blendTime The blend time over which to blend the new animation + * with the old one. If zero, then no blending will occur and the new + * animation will be applied instantly. + */ + def setAnim(action: Action, blendTime: Float): Unit = uval.setAnim(action.name, blendTime) + + + } + + implicit class AnimEventListenerWrap(val uval: AnimEventListener) extends AnyVal { + + /** + * Invoked when an animation "cycle" is done. For non-looping animations, + * this event is invoked when the animation is finished playing. For + * looping animations, this even is invoked each time the animation is restarted. + * + * @param control The control to which the listener is assigned. + * @param channel The channel being altered + * @param action The new animation action that is done. + */ + def onAnimCycleDone(control: AnimControl, channel: AnimChannel, action: Action): Unit = + uval.onAnimCycleDone(control, channel, action.name) + + /** + * Invoked when a animation is set to play by the user on the given channel. + * + * @param control The control to which the listener is assigned. + * @param channel The channel being altered + * @param action The new animation action set. + */ + def onAnimChange(control: AnimControl, channel: AnimChannel, action: Action): Unit = + uval.onAnimChange(control, channel, action.name) + } + + +} \ No newline at end of file diff --git a/src/main/scala/com/jme3/app/package.scala b/src/main/scala/com/jme3/app/package.scala new file mode 100644 index 0000000..881b465 --- /dev/null +++ b/src/main/scala/com/jme3/app/package.scala @@ -0,0 +1,16 @@ +package com.jme3 + +/** + * Created by Brandon Barker on 6/19/17. + */ +package object app { + + implicit class SimpleApplicationWrap(val uval: SimpleApplication) extends AnyVal { + + //FIXME: proof of concept, remove later + def testWrap: String = uval.hashCode().toString + + + } + +} diff --git a/src/main/scala/com/jme3/input/Action.scala b/src/main/scala/com/jme3/input/Action.scala new file mode 100644 index 0000000..7c5eead --- /dev/null +++ b/src/main/scala/com/jme3/input/Action.scala @@ -0,0 +1,6 @@ +package com.jme3.input + +/** + * Created by Brandon Barker on 6/19/17. + */ +final case class Action(name: String) extends AnyVal diff --git a/src/main/scala/com/jme3/input/controls/package.scala b/src/main/scala/com/jme3/input/controls/package.scala new file mode 100644 index 0000000..f4f3e17 --- /dev/null +++ b/src/main/scala/com/jme3/input/controls/package.scala @@ -0,0 +1,37 @@ +package com.jme3.input + +/** + * Created by Brandon Barker on 6/19/17. + */ +package object controls { + + implicit class ActionListenerWrap(val uval: ActionListener) extends AnyVal { + + /** + * Called when an input to which this listener is registered to is invoked. + * + * @param action The action (name) of the mapping that was invoked + * @param isPressed True if the action is "pressed", false otherwise + * @param tpf The time per frame value. + */ + def onAction(action: Action, keyPressed: Boolean, tpf: Float): Unit = + uval.onAction(action.name, keyPressed, tpf) + } + + implicit class AnalogListenerWrap(val uval: AnalogListener) extends AnyVal { + + /** + * Called to notify the implementation that an analog event has occurred. + * + * The results of KeyTrigger and MouseButtonTrigger events will have tpf + * == value. + * + * @param action The action (name) of the mapping that was invoked + * @param value Value of the axis, from 0 to 1. + * @param tpf The time per frame value. + */ + def onAnalog(action: Action, value: Float, tpf: Float): Unit = + uval.onAnalog(action.name, value, tpf) + } + +} diff --git a/src/main/scala/com/jme3/input/package.scala b/src/main/scala/com/jme3/input/package.scala new file mode 100644 index 0000000..57a7f4e --- /dev/null +++ b/src/main/scala/com/jme3/input/package.scala @@ -0,0 +1,18 @@ +package com.jme3 + +import com.jme3.input.controls.{InputListener, Trigger} + +/** + * Created by Brandon Barker on 6/21/17. + */ +package object input { + + implicit class InputManagerWrap(val uval: InputManager) extends AnyVal { + def addMapping(action: Action, triggers: Trigger*): Unit = + uval.addMapping(action.name, triggers: _*) + def addListener(listener: InputListener, actions: Action*): Unit = + uval.addListener(listener, actions.map(act => act.name): _*) + + } + +} diff --git a/src/main/scala/com/jme3/material/package.scala b/src/main/scala/com/jme3/material/package.scala new file mode 100644 index 0000000..38e4218 --- /dev/null +++ b/src/main/scala/com/jme3/material/package.scala @@ -0,0 +1,11 @@ +package com.jme3 + +import com.jme3.asset.AssetManager + +package object material { + + object Material { + def apply(contentMan: AssetManager, defName: String): Material = + new Material(contentMan, defName) + } +} \ No newline at end of file diff --git a/src/main/scala/com/jme3/scene/debug/package.scala b/src/main/scala/com/jme3/scene/debug/package.scala new file mode 100644 index 0000000..36eafb9 --- /dev/null +++ b/src/main/scala/com/jme3/scene/debug/package.scala @@ -0,0 +1,19 @@ +package com.jme3.scene + +import com.jme3.animation.Skeleton + +package object debug { + + object SkeletonDebugger { + + /** + * Creates a debugger with no length data. The wires will be a connection between the bones' heads only. + * The points will show the bones' heads only and no dotted line of inter bones connection will be visible. + * @param name + * the name of the debugger's node + * @param skeleton + * the skeleton that will be shown + */ + def apply(name: String, skeleton: Skeleton) = new SkeletonDebugger(name, skeleton) + } +} \ No newline at end of file diff --git a/src/main/scala/com/jme3/scene/package.scala b/src/main/scala/com/jme3/scene/package.scala new file mode 100644 index 0000000..b243318 --- /dev/null +++ b/src/main/scala/com/jme3/scene/package.scala @@ -0,0 +1,56 @@ +package com.jme3 + +import com.jme3.scene.control.Control + +/** + * Created by Brandon Barker on 6/19/17. + */ +package object scene { + + object Geometry { + + /** + * Create a geometry node with mesh data. + * The material of the geometry is null, it cannot + * be rendered until it is set. + * + * @param name The name of this geometry + * @param mesh The mesh data for this geometry + */ + def apply(name: String, mesh: Mesh): Geometry = new Geometry(name, mesh) + + } + + object Node { + + /** + * Constructor instantiates a new Node with a default empty + * list for containing children. + * + * @param name the name of the scene element. This is required for + * identification and comparison purposes. + */ + def apply(name: String): Node = new Node(name) + } + + implicit class NodeWrap(val uval: Node) extends AnyVal { + + def getControlMaybe[T <: Control](controlType: Class[T]): Option[T] = + Option(uval.getControl(controlType)) + + } + + implicit class SpatialWrap(val uval: Spatial) extends AnyVal { + + def toNode: Either[ClassCastException, Node] = + uval match { + case ul: Node => Right(ul) + case ul => + Left( + new ClassCastException(s"Couldn't convert ${ul.getName} to Node") + ) + } + + } + +} diff --git a/src/main/scala/com/jme3/scene/shape/package.scala b/src/main/scala/com/jme3/scene/shape/package.scala new file mode 100644 index 0000000..b6b5c86 --- /dev/null +++ b/src/main/scala/com/jme3/scene/shape/package.scala @@ -0,0 +1,20 @@ +package com.jme3.scene + +package object shape { + + object Box { + + /** + * Creates a new box. + *

+ * The box has a center of 0,0,0 and extends in the out from the center by + * the given amount in each direction. So, for example, a box + * with extent of 0.5 would be the unit cube. + * + * @param xs the size of the box along the x axis, in both directions. + * @param ys the size of the box along the y axis, in both directions. + * @param zs the size of the box along the z axis, in both directions. + */ + def apply(xs: Float, ys: Float, zs: Float): Box = new Box(xs, ys, zs) + } +} \ No newline at end of file diff --git a/src/main/scala/com/jme3/syntax/package.scala b/src/main/scala/com/jme3/syntax/package.scala new file mode 100644 index 0000000..a3532da --- /dev/null +++ b/src/main/scala/com/jme3/syntax/package.scala @@ -0,0 +1,14 @@ +package com.jme3 + + +/** + * Created by Brandon Barker on 6/21/17. + */ +package object syntax { + + @specialized def discard[A](evaluateForSideEffectOnly: A): Unit = { + val _: A = evaluateForSideEffectOnly + () //Return unit to prevent warning due to discarding value + } + +} diff --git a/src/main/scala/message.txt b/src/main/scala/message.txt new file mode 100644 index 0000000..ba3d2d8 --- /dev/null +++ b/src/main/scala/message.txt @@ -0,0 +1,5 @@ +Hey guys, few days ago I discovered I could load objects created in scripts at runtime, and cast them to an interface to call their methods provided +1. both host program and script have access to the same interface via a common library +2. script object implements that interface + +I was thinking, maybe I could implement appstates in scripts to implement game mechanics and attach them to the state manager at runtime, and similarly for components of an ECS. What do you guys think? \ No newline at end of file diff --git a/src/main/scala/wow/doge/mygame/Main.scala b/src/main/scala/wow/doge/mygame/Main.scala new file mode 100644 index 0000000..a395830 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/Main.scala @@ -0,0 +1,127 @@ +package wow.doge.mygame + +import game.GameApp +import com.jme3.app.StatsAppState + +import akka.actor.typed.ActorSystem +import akka.actor.typed.SpawnProtocol +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.Behavior +import akka.util.Timeout +import com.jme3.system.AppSettings +import wow.doge.mygame.game.GameAppActor +import wow.doge.mygame.scriptsystem.ScriptCachingActor +object Main extends App { + import java.util.logging.{Logger, Level} + Logger.getLogger("").setLevel(Level.SEVERE) + + // runner.runCode("""|println("starting scala script engine")""".stripMargin) + val gameApp = new GameApp( + // new EntityDataState(), + // new TestAppState(), + // new PlayerMovementState(), + // new FlyCamAppState(), + new StatsAppState() + ) + val settings = new AppSettings(true) + // settings.setVSync(true) + settings.setFrameRate(144) + gameApp.setSettings(settings) + val actorSystem = ActorSystem(RootActor(gameApp), "rootActor") + // actorSystem.eventStream + // gameApp.start() + println("here 1") + // actorSystem.terminate() + // JMEExecutorService.shutdown() + // println("here 2") + //FIXME remove this + // System.exit(0) +} + +object RootActor { + def apply(app: GameApp): Behavior[SpawnProtocol.Command] = + Behaviors.setup { ctx => + ctx.log.debug("Starting root actor") + val testActor = ctx.spawn(TestActor(), "testActor") + val _ = ctx.spawn(GameAppActor(app), "gameAppActor") + + testActor ! TestActor.Test + SpawnProtocol() + } +} + +object TestActor { + sealed trait Command + case object Test extends Command + private case object Done extends Command + // sealed trait Result + // case object Done extends Result + + import scala.concurrent.duration._ + implicit val timeout = Timeout(15.seconds) + // implicit val scheduler = + + def apply(): Behavior[Command] = + Behaviors.setup { ctx => + ctx.spawn(ScriptCachingActor(), "scriptCacher") + Behaviors.receiveMessage { msg => + msg match { + case Test => + // ctx.ask( + // router, + // ScriptActor.Compile( + // // os.pwd / "some.sc", + // os.pwd / "src" / "main" / "resources" / "hello2.main.kts", + // _ + // ) + // ) { + // case Success(value) => + // ctx.log.debug("Received Value") + // ctx.log.debug(value.toString()) + // Done + // case Failure(exception) => + // ctx.log.debug(s"Received Error ${exception.getMessage()}") + // Done + // } + // val x = scriptStorer + // .askT( + // ScriptStoringActor + // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) + // )(timeout, ctx.system.scheduler) + + // ctx.ask( + // scriptStorer, + // ScriptStoringActor + // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) + // ) { + // case Success(value) => { + // ctx.log.debug(value.toString()) + // ctx.ask( + // scriptStorer, + // ScriptStoringActor + // .Get(os.pwd / "src" / "main" / "resources" / "hello2.sc", _) + // ) { + // case Success(value) => { + // ctx.log.debug(value.toString()) + // Done + // } + // case Failure(exception) => + // ctx.log.debug(exception.getMessage()) + // Done + // } + // Done + // } + // case Failure(exception) => + // ctx.log.debug(exception.getMessage()) + // Done + // } + + Behaviors.same + case Done => Behaviors.same + } + + // SpawnProtocol() + // Behaviors.same + } + } +} diff --git a/src/main/scala/wow/doge/mygame/components/Position.scala b/src/main/scala/wow/doge/mygame/components/Position.scala new file mode 100644 index 0000000..1dbe781 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/components/Position.scala @@ -0,0 +1,6 @@ +package wow.doge.mygame.components + +import com.simsilica.es.EntityComponent; +import wow.doge.mygame.math.ImVector3f + +final case class Position(location: ImVector3f) extends EntityComponent diff --git a/src/main/scala/wow/doge/mygame/components/Tag.scala b/src/main/scala/wow/doge/mygame/components/Tag.scala new file mode 100644 index 0000000..2dbfe58 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/components/Tag.scala @@ -0,0 +1,28 @@ +package wow.doge.mygame.components + +import com.simsilica.es.EntityComponent + +final case class Tag(name: String) extends EntityComponent + +object Tag { + val SpaceShip = "SpaceShip" + val BasicInvader = "BasicInvader" +} +// public class Model implements EntityComponent { +// private final String name; +// public final static String SpaceShip = "SpaceShip"; +// public final static String BasicInvader = "BasicInvader"; + +// public Model(String name) { +// this.name = name; +// } + +// public String getName() { +// return name; +// } + +// @Override +// public String toString() { +// return "Model[" + name + "]"; +// } +// } diff --git a/src/main/scala/wow/doge/mygame/components/Test.java b/src/main/scala/wow/doge/mygame/components/Test.java new file mode 100644 index 0000000..9d3519a --- /dev/null +++ b/src/main/scala/wow/doge/mygame/components/Test.java @@ -0,0 +1,11 @@ +package wow.doge.mygame.components; + +import com.jme3.math.Vector3f; +import wow.doge.mygame.math.ImVector3f; + +//test java final case class instantiation +public class Test { + public void test() { + var pos = Position.apply(ImVector3f.apply(1, 1, 1)); + } +} \ No newline at end of file diff --git a/src/main/scala/wow/doge/mygame/components/TestComponent.scala b/src/main/scala/wow/doge/mygame/components/TestComponent.scala new file mode 100644 index 0000000..1e36b34 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/components/TestComponent.scala @@ -0,0 +1,5 @@ +package wow.doge.mygame.components + +import com.simsilica.es.EntityComponent + +final case class TestComponent() extends EntityComponent diff --git a/src/main/scala/wow/doge/mygame/events/EventBus.scala b/src/main/scala/wow/doge/mygame/events/EventBus.scala new file mode 100644 index 0000000..2bfde95 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/events/EventBus.scala @@ -0,0 +1,139 @@ +package wow.doge.mygame.events + +// import akka.event.ActorEventBus +// import akka.event.ManagedActorClassification +// import akka.event.ActorClassifier +import akka.actor.typed.ActorRef +// import akka.actor.ActorSystem +// import akka.event.EventBus +// import akka.util.Subclassification +// import java.util.concurrent.atomic.AtomicReference +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.Behaviors +import scala.reflect.ClassTag +import akka.event.EventStream + +// private[events] final case class ClassificationMessage(ref: ActorRef, id: Int) + +// class ActorBusImpl(val system: ActorSystem, val busSize: Int) +// extends ActorEventBus +// with ActorClassifier +// with ManagedActorClassification { +// type Event = ClassificationMessage + +// // is used for extracting the classifier from the incoming events +// override protected def classify(event: Event): ActorRef = event.ref + +// // determines the initial size of the index data structure +// // used internally (i.e. the expected number of different classifiers) +// override protected def mapSize: Int = busSize +// } + +// class StartsWithSubclassification extends Subclassification[String] { +// override def isEqual(x: String, y: String): Boolean = +// x == y + +// override def isSubclass(x: String, y: String): Boolean = +// x.startsWith(y) +// } + +// import akka.event.SubchannelClassification + +// final case class MsgEnvelope(topic: String, payload: Any) +// import akka.actor.typed.scaladsl.adapter._ + +// /** +// * Publishes the payload of the MsgEnvelope when the topic of the +// * MsgEnvelope starts with the String specified when subscribing. +// */ +// class SubchannelBusImpl extends EventBus with SubchannelClassification { +// type Event = Any +// type Classifier = Class[_] +// type Subscriber = ActorRef + +// // Subclassification is an object providing `isEqual` and `isSubclass` +// // to be consumed by the other methods of this classifier +// // override protected val subclassification: Subclassification[Classifier] = +// // new StartsWithSubclassification + +// private val initiallySubscribedOrUnsubscriber = +// new AtomicReference[Either[Set[ActorRef], ActorRef]](Left(Set.empty)) + +// override protected implicit val subclassification +// : Subclassification[Classifier] = new Subclassification[Class[_]] { +// def isEqual(x: Class[_], y: Class[_]) = x == y +// def isSubclass(x: Class[_], y: Class[_]) = y.isAssignableFrom(x) +// } + +// // is used for extracting the classifier from the incoming events +// override protected def classify(event: Event): Classifier = event.getClass() + +// // will be invoked for each event for all subscribers which registered +// // themselves for the event’s classifier +// override protected def publish(event: Event, subscriber: Subscriber): Unit = { +// // subscriber ! event.payload +// subscriber ! event +// } +// } + +object EventBus { + sealed trait Command[A] + final case class Publish[A, E <: A](event: E, publisher: ActorRef[_]) + extends Command[A] + + /** + * Subscribe a typed actor to listen for types or subtypes of E + * by sending this command to the [[akka.actor.typed.ActorSystem.eventStream]]. + * + * ==Simple example== + * {{{ + * sealed trait A + * case object A1 extends A + * //listen for all As + * def subscribe(actorSystem: ActorSystem[_], actorRef: ActorRef[A]) = + * actorSystem.eventStream ! EventStream.Subscribe(actorRef) + * //listen for A1s only + * def subscribe(actorSystem: ActorSystem[_], actorRef: ActorRef[A]) = + * actorSystem.eventStream ! EventStream.Subscribe[A1](actorRef) + * }}} + */ + final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit + classTag: ClassTag[E] + ) extends Command[A] { + + def topic: Class[_] = classTag.runtimeClass + } + + /** + * Unsubscribe an actor ref from the event stream + * by sending this command to the [[akka.actor.typed.ActorSystem.eventStream]]. + */ + final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E]) + extends Command[A] + + def apply[A](): Behavior[EventBus.Command[A]] = + Behaviors.setup { ctx => + val eventStream = new EventStream(ctx.system.classicSystem) + new EventBus().eventStreamBehavior(eventStream) + } + +} + +class EventBus[B] { + import akka.actor.typed.scaladsl.adapter._ + + private def eventStreamBehavior( + eventStream: akka.event.EventStream + ): Behavior[EventBus.Command[B]] = + Behaviors.receiveMessage { + case EventBus.Publish(event, publisher) => + eventStream.publish(event) + Behaviors.same + case s @ EventBus.Subscribe(subscriber) => + eventStream.subscribe(subscriber.toClassic, s.topic) + Behaviors.same + case EventBus.Unsubscribe(subscriber) => + eventStream.unsubscribe(subscriber.toClassic) + Behaviors.same + } +} diff --git a/src/main/scala/wow/doge/mygame/events/Events.scala b/src/main/scala/wow/doge/mygame/events/Events.scala new file mode 100644 index 0000000..1544159 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/events/Events.scala @@ -0,0 +1,17 @@ +package wow.doge.mygame.events + +// object Test { + +// Events.BulletFired +// } + +object Events { + sealed trait Event + case object BulletFired extends Event +// type BulletFired = BulletFired.type + case class EventWithData(data: Int) extends Event + + sealed trait Tick extends Event + case object RenderTick extends Tick + case object PhysicsTick extends Tick +} diff --git a/src/main/scala/wow/doge/mygame/events/EventsModule.scala b/src/main/scala/wow/doge/mygame/events/EventsModule.scala new file mode 100644 index 0000000..9b6853e --- /dev/null +++ b/src/main/scala/wow/doge/mygame/events/EventsModule.scala @@ -0,0 +1,3 @@ +package wow.doge.mygame.events + +trait EventsModule {} diff --git a/src/main/scala/wow/doge/mygame/executors/ExecutorsModule.scala b/src/main/scala/wow/doge/mygame/executors/ExecutorsModule.scala new file mode 100644 index 0000000..f1b2e36 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/executors/ExecutorsModule.scala @@ -0,0 +1,5 @@ +package wow.doge.mygame.executors + +trait ExecutorsModule { + lazy val schedulers = new Schedulers() +} diff --git a/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala b/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala new file mode 100644 index 0000000..07faa02 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/executors/GUIExecutor.scala @@ -0,0 +1,103 @@ +package wow.doge.mygame.executors + +import akka.dispatch.{ + DispatcherPrerequisites, + ExecutorServiceFactory, + ExecutorServiceConfigurator +} +import com.typesafe.config.Config +import java.util.concurrent.{ + ExecutorService, + AbstractExecutorService, + ThreadFactory, + TimeUnit +} +import java.util.Collections +import javax.swing.SwingUtilities +import javafx.application.Platform +import monix.execution.Scheduler +import scala.concurrent.ExecutionContext +import java.util.concurrent.Executor +import wow.doge.mygame.Main + +// First we wrap invokeLater/runLater as an ExecutorService +trait GUIExecutorService extends AbstractExecutorService { + def execute(command: Runnable): Unit + + def shutdown(): Unit = () + + def shutdownNow() = Collections.emptyList[Runnable] + + def isShutdown = false + + def isTerminated = false + + def awaitTermination(l: Long, timeUnit: TimeUnit) = true +} + +object JavaFXExecutorService extends GUIExecutorService { + override def execute(command: Runnable) = Platform.runLater(command) +} + +object SwingExecutorService extends GUIExecutorService { + override def execute(command: Runnable) = SwingUtilities.invokeLater(command) +} + +object JMEExecutorService extends GUIExecutorService { + override def execute(command: Runnable) = Main.gameApp.enqueue(command) +} + +class JavaFXEventThreadExecutorServiceConfigurator( + config: Config, + prerequisites: DispatcherPrerequisites +) extends ExecutorServiceConfigurator(config, prerequisites) { + private val f = new ExecutorServiceFactory { + def createExecutorService: ExecutorService = JavaFXExecutorService + } + + def createExecutorServiceFactory( + id: String, + threadFactory: ThreadFactory + ): ExecutorServiceFactory = f +} + +class JMEThreadExecutorServiceConfigurator( + config: Config, + prerequisites: DispatcherPrerequisites +) extends ExecutorServiceConfigurator(config, prerequisites) { + private val f = new ExecutorServiceFactory { + def createExecutorService: ExecutorService = JMEExecutorService + } + + def createExecutorServiceFactory( + id: String, + threadFactory: ThreadFactory + ): ExecutorServiceFactory = f +} + +// Then we create an ExecutorServiceConfigurator so that Akka can use our SwingExecutorService for the dispatchers +class SwingEventThreadExecutorServiceConfigurator( + config: Config, + prerequisites: DispatcherPrerequisites +) extends ExecutorServiceConfigurator(config, prerequisites) { + private val f = new ExecutorServiceFactory { + def createExecutorService: ExecutorService = SwingExecutorService + } + + def createExecutorServiceFactory( + id: String, + threadFactory: ThreadFactory + ): ExecutorServiceFactory = f +} + +object JFXExecutionContexts { + val javaFxExecutionContext: ExecutionContext = + ExecutionContext.fromExecutor(new Executor { + def execute(command: Runnable): Unit = { + Platform.runLater(command) + } + }) + val fxScheduler = + Scheduler(javaFxExecutionContext) + +} diff --git a/src/main/scala/wow/doge/mygame/executors/Schedulers.scala b/src/main/scala/wow/doge/mygame/executors/Schedulers.scala new file mode 100644 index 0000000..a8e817d --- /dev/null +++ b/src/main/scala/wow/doge/mygame/executors/Schedulers.scala @@ -0,0 +1,10 @@ +package wow.doge.mygame.executors + +import monix.execution.Scheduler + +final case class Schedulers( + blockingIO: Scheduler = Scheduler.io(), + cpu: Scheduler = Scheduler.global, + fx: Scheduler = JFXExecutionContexts.fxScheduler, + jme: Option[Scheduler] = None +) diff --git a/src/main/scala/wow/doge/mygame/game/GameApp.scala b/src/main/scala/wow/doge/mygame/game/GameApp.scala new file mode 100644 index 0000000..676ed48 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/GameApp.scala @@ -0,0 +1,109 @@ +package wow.doge.mygame.game + +import com.jme3.app.SimpleApplication + +import com.jme3.app.state.AppState +import akka.actor.typed.ActorRef +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.Behaviors + +object Greeter { + final case class Greet(whom: String, replyTo: ActorRef[Greeted]) + final case class Greeted(whom: String, from: ActorRef[Greet]) + + def apply(): Behavior[Greet] = + Behaviors.receive { (context, message) => + // context.log.info("Hello {}!", message.whom) + //#greeter-send-messages + message.replyTo ! Greeted(message.whom, context.self) + //#greeter-send-messages + Behaviors.same + } +} + +class GameApp( + // actorSystem: ActorSystem[SpawnProtocol.Command], + appStates: AppState* +) extends SimpleApplication(appStates: _*) { + // implicit val timeout = Timeout(10.seconds) + // implicit val scheduler = actorSystem.scheduler + + override def simpleInitApp(): Unit = { + + // val ship = ed.createEntity() + // val mbState = stateManager().state[EntityDataState]().map(_.getEntityData()) + // val mbState = Try( + // stateManager() + // .state[TestAppState]() + // .entity + // ).toOption.flatten.toRight(println("empty")) + // // .flatMap(_.entity) + // val x = mbState.flatMap( + // _.query + // .filter[TestComponent]("name", new Object()) + // // .filterOr[TestEntity]( + // // Filters + // // .fieldEquals(classOf[TestEntity], "", null) + // // ) + // .component[Tag]() + // .component[TestComponent]() + // .result + // .toRight(println("failed")) + // ) + + // rootNode + // .child(geom) + // .child(geom) + // .child(geom) + // .child(geom) + // .child(geom) + // .child(geom) + // .child(geom) + // .child(geom) + // Future(println("hello"))(jmeEC(this)) + // val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask( + // SpawnProtocol.Spawn( + // behavior = Greeter(), + // name = "listener", + // DispatcherSelector.fromConfig("jme-dispatcher"), + // _ + // ) + // ) + + // wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println)) + + } + override def simpleUpdate(tpf: Float): Unit = { + // val rot2 = rot.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1)) + // val rotation = geom.getLocalRotation() + // rotation.add(rot2) + // geom.rotate(rot2) + + // geom.updateModelBound() + // geom.updateGeometricState() + } + + // override def stop(): Unit = { + // actorSystem.terminate() + // super.stop() + // } + +} + +object GameApp { + // def myExec(app: SimpleApplication, command: Runnable) = { + // app.enqueue(command) + // } + // val javaFxExecutionContext: ExecutionContext = + // ExecutionContext.fromExecutor(new Executor { + // def execute(command: Runnable): Unit = { + // Platform.runLater(command) + // } + // }) + // def jmeEC(app: SimpleApplication): ExecutionContext = + // ExecutionContext.fromExecutor(new Executor { + // override def execute(command: Runnable): Unit = app.enqueue(command) + // }) + + // def jmeScheduler(app: SimpleApplication) = Sch +} diff --git a/src/main/scala/wow/doge/mygame/game/GameAppActor.scala b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala new file mode 100644 index 0000000..18a388a --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/GameAppActor.scala @@ -0,0 +1,67 @@ +package wow.doge.mygame.game + +import akka.actor.typed.scaladsl.Behaviors +import wow.doge.mygame.state.MovementActor +import wow.doge.mygame.state.PlayerMovementState2 +import wow.doge.mygame.state.MovementActorTimer +import com.jme3.scene.shape.Box +import com.jme3.scene.Geometry +import wow.doge.mygame.implicits._ +import wow.doge.mygame.events.EventBus +import wow.doge.mygame.events.Events +import wow.doge.mygame.state.ImMovementActor + +object GameAppActor { + + sealed trait Command + def apply(app: GameApp) = + Behaviors.setup[Command] { ctx => + lazy val b = new Box(1, 1, 1) + lazy val geom = new Geometry("Box", b) + val movementActor = + ctx.spawn( + MovementActor(MovementActor.Props(app, geom)), + "movementActor" + // DispatcherSelector.fromConfig("jme-dispatcher") + ) + + val movementActorTimer = ctx.spawn( + MovementActorTimer(movementActor), + "movementActorTimer" + ) + val imMovementActor = ctx.spawn( + ImMovementActor(ImMovementActor.Props(app, geom)), + "imMovementActor" + ) + + val subscribingActor = ctx.spawn(SubscribingActor(), "subscriber-1") + + val eventBus = + ctx.spawn(Behaviors.logMessages(EventBus[Events.Tick]()), "eventBus1") + + eventBus ! EventBus.Subscribe(subscribingActor) + + eventBus ! EventBus.Publish(Events.PhysicsTick, ctx.self) + + app + .getStateManager() + .attach( + new PlayerMovementState2( + movementActor, + movementActorTimer, + imMovementActor, + geom + ) + ) + app.start() + Behaviors.stopped + } +} + +object SubscribingActor { + def apply() = + Behaviors.receive[Events.PhysicsTick.type] { (ctx, msg) => + ctx.log.debug(s"received event $msg") + Behaviors.same + } +} diff --git a/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala b/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala new file mode 100644 index 0000000..af6e8cd --- /dev/null +++ b/src/main/scala/wow/doge/mygame/implicits/EntityQuery.scala @@ -0,0 +1,55 @@ +package wow.doge.mygame.implicits + +import com.simsilica.es.ComponentFilter +import com.simsilica.es.EntityComponent +import com.simsilica.es.EntityData +import com.simsilica.es.EntitySet +import com.simsilica.es.Filters +import scala.reflect.ClassTag +import scala.util.Try + +class EntityQuery(ed: EntityData) { + private var cfilter: Option[ComponentFilter[_ <: EntityComponent]] = None + private val buf = collection.mutable.ListBuffer.empty[Class[_]] + + def filter[T <: EntityComponent](field: String, value: Object)(implicit + ev: ClassTag[T] + ): EntityQuery = { + val c = ev.runtimeClass.asInstanceOf[Class[T]] + cfilter = Try(Filters.fieldEquals(c, field, value)).toOption + this + } + + def filterOr[T <: EntityComponent](operands: ComponentFilter[_ <: T]*)( + implicit ev: ClassTag[T] + ): EntityQuery = { + val c = ev.runtimeClass.asInstanceOf[Class[T]] + cfilter = Try(Filters.or(c, operands: _*)).toOption + this + } + + def filterAnd[T <: EntityComponent](operands: ComponentFilter[_ <: T]*)( + implicit ev: ClassTag[T] + ): EntityQuery = { + val c = ev.runtimeClass.asInstanceOf[Class[T]] + cfilter = Try(Filters.and(c, operands: _*)).toOption + this + } + + def component[T <: EntityComponent]()(implicit + ev: ClassTag[T] + ): EntityQuery = { + val c = ev.runtimeClass + buf += c + this + } + + def components[T <: EntityComponent](lst: Class[T]*): EntitySet = { + ed.getEntities(lst: _*) + } + + def result: EntitySet = + cfilter.fold(ed.getEntities(buf.toList: _*)) { filters => + ed.getEntities(filters, buf.toList: _*) + } +} diff --git a/src/main/scala/wow/doge/mygame/implicits/package.scala b/src/main/scala/wow/doge/mygame/implicits/package.scala new file mode 100644 index 0000000..aa900fd --- /dev/null +++ b/src/main/scala/wow/doge/mygame/implicits/package.scala @@ -0,0 +1,175 @@ +package wow.doge.mygame + +import com.jme3.app.SimpleApplication +import com.jme3.app.state.AppStateManager +import scala.reflect.ClassTag +import com.jme3.app.state.AppState +import com.jme3.scene.Node +import com.jme3.scene.Spatial +import com.simsilica.es.EntityData +import com.simsilica.es.EntityComponent +import com.simsilica.es.EntityId +import akka.actor.typed.ActorRef +import akka.util.Timeout +import akka.actor.typed.Scheduler +import monix.eval.Task +import com.jme3.input.InputManager +import com.jme3.input.controls.Trigger +import com.jme3.input.controls.InputListener +import com.jme3.math.Vector3f +import wow.doge.mygame.math.ImVector3f +import com.jme3.scene.Geometry +import wow.doge.mygame.state.CardinalDirection +import wow.doge.mygame.state.CanMove +import com.jme3.renderer.Camera + +package object implicits { + implicit class StateManagerExt(val sm: AppStateManager) extends AnyVal { + def state[S <: AppState]()(implicit c: ClassTag[S]): S = + sm.getState(c.runtimeClass.asInstanceOf[Class[S]]) + + } + + implicit class SimpleApplicationExt(val sa: SimpleApplication) + extends AnyVal { + def stateManager() = sa.getStateManager() + } + + implicit class NodeExt(val n: Node) extends AnyVal { + def child(s: Spatial): Node = { + n.attachChild(s) + n + } + } + + implicit class EntityDataExt(val ed: EntityData) extends AnyVal { + + def query = new EntityQuery(ed) + + // def entities[T <: EntityComponent](entities: Seq[T]) + } + + implicit class EntityExt(val e: EntityId) extends AnyVal { + def withComponents(classes: EntityComponent*)(implicit + ed: EntityData + ): EntityId = { + ed.setComponents(e, classes: _*) + e + } + } + + implicit class ActorRefExt[Req](val a: ActorRef[Req]) extends AnyVal { + import akka.actor.typed.scaladsl.AskPattern._ + def askT[Res]( + replyTo: ActorRef[Res] => Req + )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = { + Task.deferFuture(a.ask(replyTo)(timeout, scheduler)) + } + } + // def ?[Res](replyTo: ActorRef[Res] => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res] = { + // ask(replyTo)(timeout, scheduler) + // } + + implicit class InputManagerExt(val inputManager: InputManager) + extends AnyVal { + def withMapping(mapping: String, triggers: Trigger*): InputManager = { + inputManager.addMapping(mapping, triggers: _*) + inputManager + } + + def withListener(listener: InputListener, mappings: String*) = { + inputManager.addListener(listener, mappings: _*) + inputManager + } + } + + implicit class Vector3fExt(val v: Vector3f) extends AnyVal { + def +=(that: Vector3f) = v.addLocal(that) + def *=(that: Vector3f) = v.multLocal(that) + def -=(that: Vector3f) = v.subtractLocal(that) + def /=(that: Vector3f) = v.divideLocal(that) + def unary_- = v.negateLocal() + def immutable = ImVector3f(v.x, v.y, v.z) + } + + implicit class ImVector3fExt(val v: ImVector3f) extends AnyVal { + def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z) + def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z) + def *(f: Float): ImVector3f = + // v.copy(v.x * f, v.y * f, v.x * f) + v * ImVector3f(f, f, f) + def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z) + def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z) + def unary_- = v.copy(-v.x, -v.y, -v.z) + // def unary_-(that: ImVector3f) = this.copy(this.x, this.y, this.z) + def mutable = new Vector3f(v.x, v.y, v.z) + } + + // implicit val implVector3fForVector3 = new Vector3[Vector3f] { + // override def +(implicit v: Vector3f, that: com.jme3.math.Vector3f): Unit = + // v += that + + // override def *(implicit v: Vector3f, that: Vector3f): Unit = v *= that + + // override def -(implicit v: Vector3f, that: Vector3f): Unit = v -= that + + // override def /(implicit v: Vector3f, that: Vector3f): Unit = v /= that + + // } + + // implicit val implImVector3fForVector3 = new Vector3[ImVector3f] { + // override def +(implicit v: ImVector3f, that: ImVector3f): Unit = + // v + that + + // override def *(implicit v: ImVector3f, that: ImVector3f): Unit = v * that + + // override def -(implicit v: ImVector3f, that: ImVector3f): Unit = v - that + + // override def /(implicit v: ImVector3f, that: ImVector3f): Unit = v / that + + // } + + // def test[T](v: T)(implicit ev: Vector3[T]) = { + // import ev._ + // ev.+ + // } + + implicit val implCanMoveForGeom = new CanMove[Geometry] { + override def getDirection( + cam: Camera, + cardinalDir: CardinalDirection + ): ImVector3f = { + val camDir = + cam.getDirection().immutable * 0.6f + val camLeft = cam.getLeft().immutable * 0.4f + + val zero = ImVector3f.ZERO + val dir = cardinalDir + val walkDir = { + val mutWalkDir = new Vector3f() + if (dir.left) { + // ctx.log.trace("left") + mutWalkDir += (zero + camLeft).mutable + } + if (dir.right) { + // ctx.log.trace("right") + mutWalkDir += (zero + -camLeft).mutable + } + if (dir.up) { + // ctx.log.trace("up") + mutWalkDir += (zero + camDir).mutable + } + if (dir.down) { + // ctx.log.trace("down") + mutWalkDir += (zero + -camDir).mutable + } + mutWalkDir.immutable + } + walkDir + } + override def move(geom: Geometry, direction: ImVector3f): Unit = { + val v = geom.getLocalTranslation() + geom.setLocalTranslation(v += direction.mutable) + } + } +} diff --git a/src/main/scala/wow/doge/mygame/math/ImVector3f.scala b/src/main/scala/wow/doge/mygame/math/ImVector3f.scala new file mode 100644 index 0000000..e221f4f --- /dev/null +++ b/src/main/scala/wow/doge/mygame/math/ImVector3f.scala @@ -0,0 +1,11 @@ +package wow.doge.mygame.math + +case class ImVector3f(x: Float = 0f, y: Float = 0f, z: Float = 0f) + +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) + +} diff --git a/src/main/scala/wow/doge/mygame/scriptsystem/ScriptActor.scala b/src/main/scala/wow/doge/mygame/scriptsystem/ScriptActor.scala new file mode 100644 index 0000000..cd73b75 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/scriptsystem/ScriptActor.scala @@ -0,0 +1,157 @@ +package wow.doge.mygame.state + +import akka.actor.typed.scaladsl.Behaviors +import ammonite.Main +import akka.actor.typed.ActorRef +import com.jme3.app.state.AppState +import ammonite.runtime.Storage.Folder +import ammonite.main.Defaults +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.ActorContext +import ammonite.util.Res.Success +import javax.script.ScriptEngine +import javax.script.ScriptEngineManager +import scala.util.Try +import cats.implicits._ + +object ScriptActor { + sealed trait Result + final case class AppStateResult(state: AppState) extends Result + final case class Error(reason: String) extends Result + + sealed trait Command + final case class Compile(path: os.Path, sender: ActorRef[Result]) + extends Command + + final case class CompileAny( + path: os.Path, + result: ActorRef[Either[Error, Any]] + ) extends Command + + def defaultScalaRunner() = + ammonite + .Main( + storageBackend = new Folder( + // os.pwd / "target" + Defaults.ammoniteHome, + isRepl = false + ) + ) + + def defaultKotlinRunner(): ScriptEngine = { + val manager = new ScriptEngineManager() + val engine = manager.getEngineByExtension("main.kts") + engine + } + + def apply( + scalaRunner: Main = defaultScalaRunner(), + kotlinRunner: ScriptEngine = defaultKotlinRunner() + // parent: ActorRef[ScriptStoringActor.Command] + ): Behavior[ScriptActor.Command] = + Behaviors.setup(ctx => + new ScriptActor(scalaRunner, kotlinRunner, ctx).receiveMessage + ) + + sealed trait ScriptType + case object ScalaType extends ScriptType + case object KotlinType extends ScriptType + case object GroovyType extends ScriptType + + def determineScriptType(path: os.Path): Either[Error, ScriptType] = + path.toString match { + case s if s.endsWith(".sc") => Right(ScalaType) + case s if s.endsWith(".main.kts") => Right(KotlinType) + case s if s.endsWith(".groovy") => Right(GroovyType) + case _ => Left(Error("Unknown script type")) + } + + def runScala(path: os.Path, scalaRunner: ammonite.Main): Either[Error, Any] = + scalaRunner + .runScript( + path, + Seq.empty + ) + ._1 match { + case ammonite.util.Res.Exception(t, msg) => Left(Error(msg)) + + case Success(obj) => Right(obj) + + case _ => Left(Error("Failed to run script")) + } + + def runKotlin(path: os.Path, kotlinRunner: ScriptEngine): Either[Error, Any] = + Try(kotlinRunner.eval(os.read(path))).toEither.leftMap(t => + Error(t.getMessage()) + ) + + def runGroovy(path: os.Path): Either[Error, Any] = + Left(Error("Not implemented yet")) + +} + +class ScriptActor( + val scalaRunner: Main, + val kotlinRunner: ScriptEngine, + // parent: ActorRef[ScriptStoringActor.Command], + context: ActorContext[ScriptActor.Command] +) { + import ScriptActor._ + + def receiveMessage: Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case Compile(path, sender) => + context.log.debug(s"Received $path") + val res = getScript(path) + sender ! res + Behaviors.same + // case CompileScripts(sender, paths) => + case CompileAny(path, requester) => + context.log.debug(s"Received $path") + val res = getScript2(path) + context.log.debug(s"result = $res") + requester ! res + // parent ! ScriptStoringActor.Put(path, res) + Behaviors.same + } + } + + def getScript(path: os.Path) = { + val res = determineScriptType(path) match { + case Right(ScalaType) => runScala(path, scalaRunner) + case Right(KotlinType) => runKotlin(path, kotlinRunner) + case Right(GroovyType) => runGroovy(path) + case l @ Left(err) => l + } + + res match { + case Left(err) => err + case Right(obj) => + obj match { + case s: MyBaseState => AppStateResult(s) + case _ => Error("Class in script does not match known types") + } + } + } + + def getScript2(path: os.Path): Either[Error, Any] = { + val res = determineScriptType(path) match { + case Right(ScalaType) => runScala(path, scalaRunner) + case Right(KotlinType) => runKotlin(path, kotlinRunner) + case Right(GroovyType) => runGroovy(path) + case l @ Left(err) => l + } + + // res match { + // case Left(err) => err + // case Right(obj) => + // obj match { + // case s: MyBaseState => AppStateResult(s) + // case _ => Error("Class in script does not match known types") + // } + // } + res + } + +} diff --git a/src/main/scala/wow/doge/mygame/scriptsystem/ScriptCachingActor.scala b/src/main/scala/wow/doge/mygame/scriptsystem/ScriptCachingActor.scala new file mode 100644 index 0000000..7c4c85a --- /dev/null +++ b/src/main/scala/wow/doge/mygame/scriptsystem/ScriptCachingActor.scala @@ -0,0 +1,166 @@ +package wow.doge.mygame.scriptsystem + +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.scaladsl.PoolRouter +import akka.actor.typed.scaladsl.Routers +import wow.doge.mygame.state.ScriptActor +import akka.actor.typed.ActorRef +import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.Behavior +import akka.util.Timeout +import scala.util.Success +import scala.util.Failure +import akka.actor.typed.SupervisorStrategy + +object ScriptCachingActor { + + /** + * aka script representation + */ + type ScriptRepr = Any + type ScriptsMap = Map[os.Path, ScriptRepr] + type ScriptResult = Either[ScriptActor.Error, ScriptRepr] + + sealed trait Command + final case class Get( + scriptPath: os.Path, + requester: ActorRef[ScriptResult] + ) extends Command + final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command + final case class Put(scriptPath: os.Path, script: ScriptRepr) extends Command + private final case object NoOp extends Command + + private final case class DelegateToChild( + scriptActor: ActorRef[ScriptActor.Command], + scriptPath: os.Path, + requester: ActorRef[ScriptResult] + ) extends Command + + final case class Props( + ctx: ActorContext[Command], + scriptActor: ActorRef[ScriptActor.Command] + ) + final case class State(scriptsMap: ScriptsMap) + def apply(state: State = State(Map.empty)): Behavior[Command] = + Behaviors.logMessages { + Behaviors.setup { ctx => + val pool = ScriptActorPool(4) + val scriptsRouter = ctx.spawn(pool, "script-actors-pool") + new ScriptCachingActor(Props(ctx, scriptsRouter)).receiveMessage(state) + } + } + + private def getOrCompileScript( + ctx: ActorContext[Command], + scriptPath: os.Path, + scriptsMap: ScriptsMap, + scriptActor: ActorRef[ScriptActor.Command], + requester: ActorRef[ScriptResult] + ) = { + scriptsMap + .get(scriptPath) + .fold { + ctx.log.debug("Delegating to child") + ctx.self ! DelegateToChild( + scriptActor, + scriptPath, + requester + ) + } { s => + ctx.log.debug("Getting script from cache") + requester ! Right(s) + } + } + + private def askChildForScriptCompilation( + ctx: ActorContext[Command], + scriptActor: ActorRef[ScriptActor.Command], + scriptPath: os.Path, + requester: ActorRef[ScriptResult] + )(implicit timeout: Timeout) = { + ctx.ask(scriptActor, ScriptActor.CompileAny(scriptPath, _)) { + case Success(value) => + requester ! value + value.fold( + err => { + ctx.log.error(err.reason) + NoOp + }, + res => { + Put(scriptPath, res) + } + ) + case Failure(exception) => { + ctx.log.error(exception.getMessage()) + NoOp + } + } + } +} + +class ScriptCachingActor(props: ScriptCachingActor.Props) { + import com.softwaremill.quicklens._ + import ScriptCachingActor._ + def receiveMessage(state: State): Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case Get(scriptPath, requester) => + getOrCompileScript( + props.ctx, + scriptPath, + state.scriptsMap, + props.scriptActor, + requester + ) + Behaviors.same + + case DelegateToChild(scriptActor, scriptPath, requester) => + import scala.concurrent.duration._ + implicit val timeout = Timeout(15.seconds) + // child ! ScriptActor.CompileAny(scriptPath, requester) + askChildForScriptCompilation( + props.ctx, + scriptActor, + scriptPath, + requester + ) + Behaviors.same + + case GetMap(requester) => + requester ! state.scriptsMap + Behaviors.same + + case Put(scriptPath, script) => + props.ctx.log.debug(s"Putting $script at path $scriptPath") + val newState = + state.modify(_.scriptsMap).using(_ + (scriptPath -> script)) + props.ctx.log.debug(newState.toString()) + receiveMessage(state = newState) + + case NoOp => Behaviors.same + } + } +} + +object ScriptActorPool { + def apply( + poolSize: Int + ): PoolRouter[ScriptActor.Command] = + Routers.pool(poolSize = poolSize)( + // make sure the workers are restarted if they fail + Behaviors + .supervise(ScriptActor()) + .onFailure[Exception](SupervisorStrategy.restart) + ) + + // def apply( + // poolSize: Int, + // parent: ActorRef[ScriptStoringActor.Command] + // ): PoolRouter[ScriptActor.Command] = + // Routers.pool(poolSize = poolSize)( + // // make sure the workers are restarted if they fail + // Behaviors + // .supervise(ScriptActor(parent = parent)) + // .onFailure[Exception](SupervisorStrategy.restart) + // ) +} diff --git a/src/main/scala/wow/doge/mygame/state/EntityDataState.java b/src/main/scala/wow/doge/mygame/state/EntityDataState.java new file mode 100644 index 0000000..d748e70 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/state/EntityDataState.java @@ -0,0 +1,38 @@ +package wow.doge.mygame.state; + +import com.jme3.app.state.AbstractAppState; +import com.simsilica.es.EntityData; +import com.simsilica.es.base.DefaultEntityData; +import com.jme3.app.state.BaseAppState; +import com.jme3.app.Application; + +public class EntityDataState extends BaseAppState { + private EntityData entityData; + + public EntityDataState() { + this(new DefaultEntityData()); + } + + public EntityDataState(EntityData ed) { + this.entityData = ed; + } + + public EntityData getEntityData() { + return entityData; + } + + public void onEnable() { + } + + public void onDisable() { + } + + // @Override + public void cleanup(Application application) { + entityData.close(); + entityData = null; // cannot be reused + } + + public void initialize(Application application) { + } +} \ No newline at end of file diff --git a/src/main/scala/wow/doge/mygame/state/MovementActor.scala b/src/main/scala/wow/doge/mygame/state/MovementActor.scala new file mode 100644 index 0000000..7b4786d --- /dev/null +++ b/src/main/scala/wow/doge/mygame/state/MovementActor.scala @@ -0,0 +1,86 @@ +package wow.doge.mygame.state + +import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.Behaviors +import com.softwaremill.quicklens._ +import wow.doge.mygame.implicits._ +import com.jme3.renderer.Camera +import wow.doge.mygame.math.ImVector3f + +trait CanMove[T] { + def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f + def move(inst: T, direction: ImVector3f): Unit +} + +object ImMovementActor { + sealed trait Command + // final case class Tick(tpf: Float) extends Command + final case class Tick(tpf: Float) extends Command + + sealed trait Movement extends Command + final case class MovedLeft(pressed: Boolean) extends Movement + final case class MovedUp(pressed: Boolean) extends Movement + final case class MovedRight(pressed: Boolean) extends Movement + final case class MovedDown(pressed: Boolean) extends Movement + + final case class Props[T: CanMove]( + app: com.jme3.app.Application, + movable: T + ) + + /** + * Internal state of the actor + * + * @param cardinalDir Immutable, can be shared as is + * @param walkDirection Immutable + */ + final case class State( + cardinalDir: CardinalDirection = CardinalDirection() + ) + + def apply[T: CanMove](props: Props[T]): Behavior[Command] = + Behaviors.setup(ctx => new ImMovementActor(ctx, props).receive(State())) + +} + +class ImMovementActor[T]( + ctx: ActorContext[ImMovementActor.Command], + props: ImMovementActor.Props[T] +) { + import ImMovementActor._ + + def receive( + state: ImMovementActor.State + )(implicit cm: CanMove[T]): Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case m: Movement => + m match { + case MovedLeft(pressed) => + receive(state = state.modify(_.cardinalDir.left).setTo(pressed)) + case MovedUp(pressed) => + receive(state = state.modify(_.cardinalDir.up).setTo(pressed)) + case MovedRight(pressed) => + receive(state = state.modify(_.cardinalDir.right).setTo(pressed)) + case MovedDown(pressed) => + receive(state = state.modify(_.cardinalDir.down).setTo(pressed)) + } + + case Tick(tpf) => + val walkDir = + cm.getDirection(props.app.getCamera(), state.cardinalDir) + if (walkDir != ImVector3f.ZERO) { + val tmp = walkDir * 2f + props.app.enqueue(new Runnable { + override def run(): Unit = { + cm.move(props.movable, tmp) + } + }) + } + Behaviors.same + // receive(state = state.modify(_.walkDirection).setTo(walkDir)) + + } + } +} diff --git a/src/main/scala/wow/doge/mygame/state/MyBaseState.scala b/src/main/scala/wow/doge/mygame/state/MyBaseState.scala new file mode 100644 index 0000000..743e33e --- /dev/null +++ b/src/main/scala/wow/doge/mygame/state/MyBaseState.scala @@ -0,0 +1,63 @@ +package wow.doge.mygame.state + +import com.jme3.app.Application +import com.jme3.app.SimpleApplication +import com.jme3.app.state.AppState +import com.jme3.scene.Node +import com.jme3.app.state.BaseAppState +import com.simsilica.es.EntityData +import com.simsilica.es.base.DefaultEntityData + +trait MyBaseState extends BaseAppState { + + var simpleApp: SimpleApplication = null + implicit val entityData: EntityData = new DefaultEntityData() + def stateManager = simpleApp.getStateManager + def guiNode = simpleApp.getGuiNode + def rootNode = simpleApp.getRootNode + def assetManager = simpleApp.getAssetManager + def inputManager = simpleApp.getInputManager + def cam = simpleApp.getCamera + + override protected final def initialize(app: Application): Unit = { + simpleApp = app.asInstanceOf[SimpleApplication] + init() + // stateManager.getState(classOf[FlyCamAppState]).getCamera().setMoveSpeed(100) + } + + protected def init(): Unit + + override protected def cleanup(app: Application): Unit = { + entityData.close() + } + + protected def getOrCreateNode(parent: Node, id: String) = + Option(parent.getChild(id)).fold { + val node = new Node(id) + parent.attachChild(node) + node + }(node => node.asInstanceOf[Node]) + + protected def enableStates(classes: Class[_ <: AppState]*) = + setEnabledToStates(true, classes: _*) + protected def disableStates(classes: Class[_ <: AppState]*) = + setEnabledToStates(false, classes: _*) + + protected def setEnabledToStates( + enabled: Boolean, + classes: Class[_ <: AppState]* + ) = { + for (clazz <- classes) { + val st = stateManager.getState(clazz) + if (st != null) st.setEnabled(enabled) + } + } + + protected def removeStates(classes: Class[_ <: AppState]*) = { + for (clazz <- classes) { + val st = stateManager.getState(clazz) + if (st != null) stateManager.detach(st) + } + } + +} diff --git a/src/main/scala/wow/doge/mygame/state/PlayerMovementState2.scala b/src/main/scala/wow/doge/mygame/state/PlayerMovementState2.scala new file mode 100644 index 0000000..663d6da --- /dev/null +++ b/src/main/scala/wow/doge/mygame/state/PlayerMovementState2.scala @@ -0,0 +1,286 @@ +package wow.doge.mygame.state + + +import scala.concurrent.duration.DurationInt + +import com.jme3.input.InputManager +import com.jme3.input.KeyInput +import com.jme3.input.controls.ActionListener +import com.jme3.input.controls.KeyTrigger +import com.jme3.math.Vector3f + +import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.Behavior +import akka.actor.typed.ActorRef +import com.jme3.scene.Geometry +import akka.actor.typed.scaladsl.TimerScheduler + +import wow.doge.mygame.implicits._ + +class PlayerMovementState2( + movementActor: ActorRef[MovementActor.Command], + movementActorTimer: ActorRef[MovementActorTimer.Command], + imMovementActor: ActorRef[ImMovementActor.Command], + geom: Geometry +) extends MyBaseState + with ActionListener { + + protected lazy val mat = MyMaterial( + assetManager = assetManager, + path = "Common/MatDefs/Misc/Unshaded.j3md" + ) + + override protected[state] def onEnable(): Unit = {} + + override protected[state] def onDisable(): Unit = {} + + override protected def init(): Unit = { + + setupKeys(inputManager) + geom.setMaterial(mat) + rootNode.attachChild(geom) + // movementActorTimer ! MovementActorTimer.Start(geom, cam) + // movementActorTimer ! MovementActorTimer.Start + } + +// def system = +// simpleApp.getStateManager.getState(classOf[ActorSystemState]).system +// def player = system.actorSelection("/user/player") //.resolveOne(1.second) + + var lastDir: Vector3f = Vector3f.UNIT_X + + override def update(tpf: Float) = { + // val direction = new Vector3f() + // direction.multLocal(10 * tpf) + // if (direction.length() > 0f) { + // // player ! PlayerMove(direction) + // lastDir = direction.normalize + // } + // if (shoot) { + // shoot = false + // // player ! Shoot(lastDir) + // } + // movementActor ! MovementActor.Tick(tpf, geom, cam) + imMovementActor ! ImMovementActor.Tick(tpf) + // movementActorTimer ! MovementActorTimer.Update(tpf) + } + + def setupKeys(inputManager: InputManager) = { + + inputManager + .withMapping( + "Left", + // new KeyTrigger(KeyInput.KEY_A), + new KeyTrigger(KeyInput.KEY_LEFT) + ) + .withMapping( + "Right", + // new KeyTrigger(KeyInput.KEY_D), + new KeyTrigger(KeyInput.KEY_RIGHT) + ) + inputManager.addMapping( + "Up", + // new KeyTrigger(KeyInput.KEY_W), + new KeyTrigger(KeyInput.KEY_UP) + ) + inputManager.addMapping( + "Down", + // new KeyTrigger(KeyInput.KEY_S), + new KeyTrigger(KeyInput.KEY_DOWN) + ) + inputManager.addMapping( + "Space", + new KeyTrigger(KeyInput.KEY_SPACE), + new KeyTrigger(KeyInput.KEY_H) + ) + inputManager.addMapping( + "Reset", + new KeyTrigger(KeyInput.KEY_R), + new KeyTrigger(KeyInput.KEY_RETURN) + ) + inputManager + .withListener(this, "Left") + .withListener(this, "Right") + inputManager.addListener(this, "Up") + inputManager.addListener(this, "Down") + inputManager.addListener(this, "Space") + inputManager.addListener(this, "Reset") + } + + def onAction(binding: String, value: Boolean, tpf: Float) = + binding match { + case "Left" => imMovementActor ! ImMovementActor.MovedLeft(value) + case "Right" => imMovementActor ! ImMovementActor.MovedRight(value) + case "Up" => imMovementActor ! ImMovementActor.MovedUp(value) + case "Down" => imMovementActor ! ImMovementActor.MovedDown(value) + case "Space" => + case _ => + } + +} + +final case class CardinalDirection( + left: Boolean = false, + right: Boolean = false, + up: Boolean = false, + down: Boolean = false +) + +object MovementActor { + sealed trait Command + // final case class Tick(tpf: Float, geom: Geometry, cam: Camera) extends Command + // final case class Tick(tpf: Float) extends Command + final case object Tick extends Command + + sealed trait Movement extends Command + final case class MovedLeft(pressed: Boolean) extends Movement + final case class MovedUp(pressed: Boolean) extends Movement + final case class MovedRight(pressed: Boolean) extends Movement + final case class MovedDown(pressed: Boolean) extends Movement + + final case class Props(app: com.jme3.app.Application, geom: Geometry) + + /** + * Internal state of the actor + * + * @param cardinalDir Immutable, can be shared as is + * @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor + */ + final case class State( + cardinalDir: CardinalDirection = CardinalDirection(), + walkDirection: Vector3f = Vector3f.UNIT_X + ) + + def apply(props: Props): Behavior[Command] = + Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State())) + +} +class MovementActor( + ctx: ActorContext[MovementActor.Command], + props: MovementActor.Props +) { + import MovementActor._ + import com.softwaremill.quicklens._ + def receive(state: MovementActor.State): Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case m: Movement => + m match { + case MovedLeft(pressed) => + receive(state = state.modify(_.cardinalDir.left).setTo(pressed)) + case MovedUp(pressed) => + receive(state = state.modify(_.cardinalDir.up).setTo(pressed)) + case MovedRight(pressed) => + receive(state = state.modify(_.cardinalDir.right).setTo(pressed)) + case MovedDown(pressed) => + receive(state = state.modify(_.cardinalDir.down).setTo(pressed)) + } + + case Tick => + val camDir = + props.app.getCamera.getDirection().clone().multLocal(0.6f) + val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f) + val walkDir = state.walkDirection.set(0, 0, 0) + // val walkDir = new Vector3f + val dir = state.cardinalDir + if (dir.up) { + ctx.log.debug("up") + // ctx.log.debug(Thread.currentThread().getName()) + // walkDir.addLocal(0, 0, -1) + walkDir += camDir + } + if (dir.left) { + ctx.log.debug("left") + // walkDir.addLocal(-1, 0, 0) + walkDir.addLocal(camLeft) + } + if (dir.right) { + ctx.log.debug("right") + // walkDir.addLocal(1, 0, 0) + walkDir.addLocal(camLeft.negateLocal()) + } + if (dir.down) { + ctx.log.debug("down") + walkDir.addLocal(camDir.negateLocal()) + // walkDir.addLocal(0, 0, 1) + } + // (dir.up, dir.down, dir.left, dir.right) match { + // case (true, false, true, false) => + // case _ => + // } + + walkDir.multLocal(2f) + + // walkDir.multLocal(100f) + // .multLocal(tpf) + + // val v = props.geom.getLocalTranslation() + // props.geom.setLocalTranslation( + // (v += walkDir) + // ) + props.app.enqueue(new Runnable { + override def run(): Unit = { + // geom.setLocalTranslation(walkDir) + + val v = props.geom.getLocalTranslation() + props.geom.setLocalTranslation( + (v += walkDir) + ) + } + }) + Behaviors.same + // receive(state = state.modify(_.walkDirection).setTo(walkDir)) + + } + } +} + +object MovementActorTimer { + sealed trait Command + final case object Start extends Command + final case object Update extends Command + private case object Send extends Command + case object TimerKey + + final case class Props( + timers: TimerScheduler[MovementActorTimer.Command], + target: ActorRef[MovementActor.Command] + ) + final case class State() + def apply(target: ActorRef[MovementActor.Command]) = + Behaviors.withTimers[Command] { timers => + new MovementActorTimer(Props(timers, target)).idle() + } +} +class MovementActorTimer( + props: MovementActorTimer.Props +) { + import MovementActorTimer._ + // import com.softwaremill.quicklens._ + + def idle(): Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case Start => + props.timers.startTimerWithFixedDelay( + Send, + 10.millis + ) + active() + case _ => Behaviors.unhandled + } + } + + def active(): Behavior[Command] = + Behaviors.receiveMessage { msg => + msg match { + case Update => active() + case Send => + props.target ! MovementActor.Tick + Behaviors.same + case _ => Behaviors.unhandled + + } + } +} diff --git a/src/main/scala/wow/doge/mygame/state/ScriptingEngineState.scala b/src/main/scala/wow/doge/mygame/state/ScriptingEngineState.scala new file mode 100644 index 0000000..0083ec4 --- /dev/null +++ b/src/main/scala/wow/doge/mygame/state/ScriptingEngineState.scala @@ -0,0 +1,206 @@ +package wow.doge.mygame.state + +import ammonite.runtime.Storage.Folder +import ammonite.main.Defaults +import ammonite.Main +import javax.script.ScriptEngine +import com.jme3.app.Application +import com.jme3.app.state.AppState +import akka.actor.typed.scaladsl.AbstractBehavior +import akka.actor.typed.scaladsl.ActorContext +import akka.actor.typed.Behavior +import akka.actor.typed.ActorRef +import ammonite.util.Res.Success +import akka.actor.typed.scaladsl.Behaviors +import akka.actor.typed.SpawnProtocol + +class ScriptingEngineState( + sse: ScalaScriptingEngine, + kse: KotlinScriptingEngine +) extends MyBaseState { + + // implicit val actorSystem = + // ActorSystem.create(MyActorSystem(), "rootActor") + // implicit val timeout: Timeout = Timeout(3.seconds) + // val scalaScriptActor: Future[ActorRef[ScalaScriptBehavior.Command]] = + // actorSystem.ask( + // SpawnProtocol.Spawn( + // ScalaScriptBehavior(sse.runner), + // name = "ScalaScriptCompilerActor", + // Props.empty, + // _ + // ) + // ) + + override protected def cleanup(app: Application): Unit = { + // actorSystem.terminate() + } + +// override protected def initialize(app: Application): Unit = { +// super.initialize(app) + +// } + override def init() = { + // Future { + // while (true) { + // // super.update(tpf) + // val (res, k) = sse.runner.runScript( + // // os.Path(getClass().getResource("/hello.sc").getPath), + // os.pwd / "src" / "main" / "resources" / "hello.sc", + // Seq.empty + // // Seq(("start", None)) + // // Scripts.groupArgs(List("")) + // ) + // val ms = res.map(_.asInstanceOf[GameScript]) + // ms.map(_.start()) + + // val res2 = kse.engine.eval( + // os.read(os.pwd / "src" / "main" / "resources" / "hello.main.kts") + // ) + // // val res2 = engine.eval(getClass().getResource("/hello.main.kts").getPath) + // // val invoker = engine.asInstanceOf[Invocable] + // // val scr = invoker.getInterface(res2, classOf[GameScript]) + // val scr = res2.asInstanceOf[GameScript] + // scr.start() + // Thread.sleep(2000) + // } + // } + // Future { + // sse.runner + // .runScript( + // os.pwd / "src" / "main" / "resources" / "hello2.sc", + // Seq.empty + // ) + // ._1 + // .map(_.asInstanceOf[MyBaseState]) + // .map(s => stateManager.attach(s)) + + // () + + // } + + // val res = scalaScriptActor + // .map( + // _.ask(ref => + // ScalaScriptBehavior.Compile( + // ref, + // os.pwd / "src" / "main" / "resources" / "hello2.sc" + // // os.Path(getClass().getResource("/hello2.sc").getPath) + // ) + // )(Timeout(10.seconds), actorSystem.scheduler) + // ) + // .flatten + + // res.foreach(_ match { + // case AppStateResult(state) => { + // stateManager.attach(state) + // } + // case wow.doge.mygame.state.ScalaScriptBehavior.Error(reason) => + // println("error") + // }) + + } + + override def update(tpf: Float): Unit = {} + + override protected def onEnable(): Unit = {} + + override protected def onDisable(): Unit = {} + +} + +object MyActorSystem { + def apply(): Behavior[SpawnProtocol.Command] = + Behaviors.setup { context => + // Start initial tasks + // context.spawn(...) + + SpawnProtocol() + } +} +class ScalaScriptingEngine( + val runner: Main = ammonite + .Main( + // predefCode = """ + // import coursierapi.MavenRepository + + // interp.repositories.update( + // interp.repositories() ::: List( + // MavenRepository.of("file://home/rohan/.m2/repository") + // ) + // ) + + // @ + + // """, + defaultPredef = false, + storageBackend = new Folder(Defaults.ammoniteHome, isRepl = false) + ) +) {} + +class KotlinScriptingEngine(val engine: ScriptEngine) { +// val manager = new ScriptEngineManager() +// val engine = manager.getEngineByExtension("main.kts") +} + +object ScalaScriptBehavior { + sealed trait Result + final case class AppStateResult(state: AppState) extends Result + final case class Error(reason: String) extends Result + + sealed trait Command + final case class Compile(sender: ActorRef[Result], path: os.Path) + extends Command + // final case class CompileScripts(sender: ActorRef[Result], paths: os.Path*) + // extends Command + def apply( + runner: Main = ammonite + .Main( + storageBackend = new Folder( + // os.pwd / "target" + Defaults.ammoniteHome, + isRepl = false + ) + ) + ) = + Behaviors.setup(ctx => new ScalaScriptActor(runner, ctx)) + private class ScalaScriptActor( + val runner: Main, + context: ActorContext[Command] + ) extends AbstractBehavior[Command](context) { + + override def onMessage(msg: Command): Behavior[Command] = { + msg match { + case Compile(sender, path) => + context.log.debug(s"Received $path") + val res = getScript(path) + println(res) + sender ! res + Behaviors.same + // case CompileScripts(sender, paths) => + } + } + + def getScript(path: os.Path): Result = { + runner + .runScript( + path, + Seq.empty + ) + ._1 match { + case ammonite.util.Res.Exception(t, msg) => Error(msg) + + case Success(obj) => + obj match { + case s: MyBaseState => AppStateResult(s) + case _ => Error("Unknown script type") + // AppStateResult(s.asInstanceOf[AppState]) + } + + case _ => Error("Failed to run script") + } + + } + + } +} diff --git a/src/main/scala/wow/doge/mygame/state/TestAppState.scala b/src/main/scala/wow/doge/mygame/state/TestAppState.scala new file mode 100644 index 0000000..b17292d --- /dev/null +++ b/src/main/scala/wow/doge/mygame/state/TestAppState.scala @@ -0,0 +1,71 @@ +package wow.doge.mygame.state + +import com.jme3.app.Application +import wow.doge.mygame.implicits._ +import wow.doge.mygame.components.TestComponent +import com.jme3.scene.shape.Box +import com.jme3.scene.Geometry +import com.jme3.material.Material +import com.jme3.math.ColorRGBA +import com.jme3.asset.AssetManager +import com.jme3.math.Vector3f +class TestAppState( + // private var _entity: Option[EntityData] = Some(new DefaultEntityData()) +) extends MyBaseState { + var geom: Option[Geometry] = None + +// def entity = _entity + // override def initialize(app: Application): Unit = { + // super.initialize(app) + + // } + + override def init() = { + entityData + .createEntity() + .withComponents(TestComponent()) + // entityData.setComponents(x, TestComponent()) + val es = entityData.getEntities(classOf[TestComponent]) + println(es) + val b = new Box(1, 1, 1) + geom = Some(new Geometry("Box", b)) + + val mat = MyMaterial( + assetManager = assetManager, + path = "Common/MatDefs/Misc/Unshaded.j3md" + ) + geom.foreach(e => { + e.setMaterial(mat) + rootNode.attachChild(e) + }) + } + + override def update(tpf: Float) = { + geom.foreach(_.rotate(0, 0.5f * tpf, 0)) + geom.foreach(_.move(new Vector3f(0, 1 * tpf, 0))) + } + + override def cleanup(app: Application): Unit = { + // _entity.map(_.close()) + // _entity = None + } + + override def onEnable(): Unit = {} + + override def onDisable(): Unit = {} + +} + +object MyMaterial { + def apply( + color: String = "Color", + colorType: com.jme3.math.ColorRGBA = ColorRGBA.Blue, + assetManager: AssetManager, + path: String + ): Material = { + val mat = + new Material(assetManager, path) + mat.setColor(color, colorType) + mat + } +}