Testing out JmonkeyEngine to make a game in Scala with Akka Actors within a pure FP layer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
5.0 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package wow.doge.mygame.subsystems.moddingsystem
  2. import java.io.FileNotFoundException
  3. import java.nio.file.NoSuchFileException
  4. import scala.collection.View
  5. import scala.collection.immutable.ArraySeq
  6. import scala.util.Try
  7. import cats.implicits._
  8. import io.circe._
  9. import io.circe.generic.semiauto._
  10. import io.circe.parser._
  11. import monix.bio.IO
  12. import monix.bio.UIO
  13. import monix.reactive.Consumer
  14. import monix.reactive.Observable
  15. import wow.doge.mygame.utils.IOUtils
  16. import io.circe.generic.JsonCodec
  17. @JsonCodec
  18. final case class Test1(hello1: String, hello2: String)
  19. @JsonCodec
  20. final case class Test2(hello1: String)
  21. final case class Plugin(name: String, priority: Int)
  22. object Plugin {
  23. // @annotation.nowarn(
  24. // "msg=Block result was adapted via implicit conversion"
  25. // )
  26. implicit val pluginFormat: Decoder[Plugin] = deriveDecoder
  27. }
  28. object ModdingSystem {
  29. sealed trait Error extends Serializable with Product
  30. final case class CouldNotDecode(cause: String) extends Error
  31. final case class ParseFailure(cause: String) extends Error
  32. final case class FileNotFound(fileName: String) extends Error
  33. case object GenericError extends Error
  34. def readPluginsList(dir: os.Path): Try[Either[Error, ArraySeq[Plugin]]] =
  35. Try(
  36. parse(os.read(dir / "plugins.json"))
  37. .map(
  38. _.as[ArraySeq[Plugin]]
  39. .leftMap(e => CouldNotDecode(e.getMessage()))
  40. )
  41. .leftMap((e: ParsingFailure) => ParseFailure(e.message))
  42. .flatten
  43. )
  44. // .toValidated
  45. def findPluginFiles(dir: os.Path): View[os.Path] =
  46. os.list(dir)
  47. .view
  48. .filter(f => f.ext == "json" && f.baseName.endsWith("plugin"))
  49. def findAndReadPluginFiles(
  50. dir: os.Path,
  51. plugins: ArraySeq[Plugin]
  52. ) =
  53. plugins
  54. .sortBy(_.priority)
  55. .view
  56. .map(p =>
  57. p ->
  58. Either
  59. .catchNonFatal {
  60. val path = dir / os.RelPath(p.name + ".plugin.json")
  61. os.read(path)
  62. }
  63. .leftMap {
  64. case _: FileNotFoundException =>
  65. FileNotFound(p.name)
  66. case _: NoSuchFileException => FileNotFound(p.name)
  67. case e => GenericError
  68. }
  69. )
  70. .partitionMap {
  71. case (p, either) =>
  72. either match {
  73. case Left(value) => Left(p -> value)
  74. case Right(value) => Right(p -> value)
  75. }
  76. }
  77. def readPluginFiles(filePaths: View[os.Path]) = {
  78. filePaths.map(path => os.read(path))
  79. }
  80. def parsePluginFiles(files: View[(Plugin, String)]) =
  81. files
  82. .map {
  83. case (p, s) => p -> parse(s)
  84. }
  85. .partitionMap {
  86. case (p, Left(value)) => Left(p -> value)
  87. case (p, Right(value)) => Right(p -> value)
  88. }
  89. def foldMerge(iterable: Iterable[Json]) =
  90. iterable.foldLeft(Json.fromString("empty")) {
  91. case (json, io.circe.Json.Null) => json //ignore null values
  92. case (json, value) => json.deepMerge(value)
  93. }
  94. def mergePluginData(plugins: View[(Plugin, Json)]) = {
  95. foldMerge(plugins.map {
  96. case (p, json) => json
  97. })
  98. }
  99. def mergePluginDataConsumer =
  100. Consumer.foldLeft[Json, Json](Json.fromString("empty")) {
  101. case (json, io.circe.Json.Null) => json
  102. case (json, that) => json.deepMerge(that)
  103. }
  104. def loadBalancedPluginDataMerger =
  105. Consumer
  106. .loadBalance(parallelism = 2, mergePluginDataConsumer)
  107. .map(foldMerge)
  108. // def test =
  109. // for {
  110. // filePaths <- Task(findPluginFiles(os.pwd))
  111. // files <- Task(readPluginFiles(filePaths))
  112. // (failures, successes) <- Task(parsePluginFiles(files))
  113. // merged <- Task(mergePluginData(successes))
  114. // _ <- Task {
  115. // println(s"Successes = ${successes.to(Seq)}")
  116. // println(s"Failure = ${failures.to(Seq)}")
  117. // println(s"Merged = $merged")
  118. // }
  119. // } yield ()
  120. def test(wd: os.Path = os.pwd) =
  121. for {
  122. plugins <- IO.fromTryEither(readPluginsList(wd))
  123. (readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins))
  124. (parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
  125. // res <- UIO(mergePluginData(parseSuccesses))
  126. res <-
  127. IOUtils
  128. .toIO(
  129. Observable
  130. .fromIterable(parseSuccesses)
  131. .map { case (p, json) => json }
  132. .consumeWith(loadBalancedPluginDataMerger)
  133. )
  134. .onErrorHandle(e => GenericError)
  135. _ <- UIO {
  136. println(s"Read Successes = ${readSuccesses.to(Seq)}")
  137. println(s"Read Failures = ${readFailures.to(Seq)}")
  138. println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
  139. println(s"Parse Failures = ${parseFailures.to(Seq)}")
  140. println(s"Merged = $res")
  141. }
  142. } yield ()
  143. // monix.eval.Task.deferAction(implicit s =>
  144. // ModdingSystem
  145. // .test()
  146. // .leftMap(e => new Throwable(e.toString()))
  147. // .to[monix.eval.Task]
  148. // )
  149. // def test3(wd: os.Path = os.pwd) = {
  150. // (readPluginsList(os.pwd).toValidatedNec)
  151. // }
  152. }