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.

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