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.

164 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
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 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
  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. ): (View[(Plugin, Error)], View[(Plugin, String)]) =
  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. def parsePluginFiles(files: View[(Plugin, String)]) =
  77. files
  78. .map {
  79. case (p, s) => p -> parse(s)
  80. }
  81. .partitionMap {
  82. case (p, Left(value)) => Left(p -> value)
  83. case (p, Right(value)) => Right(p -> value)
  84. }
  85. def foldMerge(iterable: Iterable[Json]) =
  86. iterable.foldLeft(Json.fromString("empty")) {
  87. case (json, io.circe.Json.Null) => json //ignore null values
  88. case (json, value) => json.deepMerge(value)
  89. }
  90. def mergePluginData(plugins: View[(Plugin, Json)]) =
  91. foldMerge(plugins.map {
  92. case (p, json) => json
  93. })
  94. def mergePluginDataConsumer =
  95. Consumer.foldLeft[Json, Json](Json.fromString("empty")) {
  96. case (json, io.circe.Json.Null) => json
  97. case (json, that) => json.deepMerge(that)
  98. }
  99. def loadBalancedPluginDataMerger =
  100. Consumer
  101. .loadBalance(parallelism = 2, mergePluginDataConsumer)
  102. .map(foldMerge)
  103. // def test =
  104. // for {
  105. // filePaths <- Task(findPluginFiles(os.pwd))
  106. // files <- Task(readPluginFiles(filePaths))
  107. // (failures, successes) <- Task(parsePluginFiles(files))
  108. // merged <- Task(mergePluginData(successes))
  109. // _ <- Task {
  110. // println(s"Successes = ${successes.to(Seq)}")
  111. // println(s"Failure = ${failures.to(Seq)}")
  112. // println(s"Merged = $merged")
  113. // }
  114. // } yield ()
  115. def test(wd: os.Path = os.pwd) =
  116. for {
  117. plugins <- IO.fromTryEither(readPluginsList(wd))
  118. (readFailures, readSuccesses) <- UIO(findAndReadPluginFiles(wd, plugins))
  119. (parseFailures, parseSuccesses) <- UIO(parsePluginFiles(readSuccesses))
  120. // res <- UIO(mergePluginData(parseSuccesses))
  121. res <-
  122. IOUtils
  123. .toIO(
  124. Observable
  125. .fromIterable(parseSuccesses)
  126. .map { case (p, json) => json }
  127. .consumeWith(loadBalancedPluginDataMerger)
  128. )
  129. .hideErrors
  130. _ <- UIO {
  131. println(s"Read Successes = ${readSuccesses.to(Seq)}")
  132. println(s"Read Failures = ${readFailures.to(Seq)}")
  133. println(s"Parse Successes = ${parseSuccesses.to(Seq)}")
  134. println(s"Parse Failures = ${parseFailures.to(Seq)}")
  135. println(show"Merged = $res")
  136. }
  137. } yield ()
  138. // monix.eval.Task.deferAction(implicit s =>
  139. // ModdingSystem
  140. // .test()
  141. // .leftMap(e => new Throwable(e.toString()))
  142. // .to[monix.eval.Task]
  143. // )
  144. // def test3(wd: os.Path = os.pwd) = {
  145. // (readPluginsList(os.pwd).toValidatedNec)
  146. // }
  147. }